"""
flockuri.py
URI method handler classes for the flock branch of the URI tree.
Copyright PR Hardman 2009 - 2023.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details./
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import cherrypy
from ppdb import const
from ppdb.lib import util, memlib, perslib, flocklib, fbklib, transfer, userlib
from ppdb.handlers import uribase
xhrc = const.JSConsts()
#~ DEBUG = True
DEBUG = False
class FlockBase(uribase.UriBase):
""" Provides generic methods for the 'flock' branch of the URI tree.
Note that because there is only one instance of the subclass per
application then instance variables must only be those variables
which are truly per-instance and not per-method.
"""
def __init__(self, config, path_conf=None):
""" Default constructor. """
super().__init__(config, path_conf)
self.disclaimer = config.get('disclaimer', '')
self.flock_sort_params = list(flocklib.FLOCK_SORT_TRANSLATE.keys())
def check_id(self, arg, srchby='flock', one_only=True):
""" Test that the id is good.
Returns a list, empty if no rid was supplied, tuples of flock_no, flock_name if
the rid is good, or a message string if not.
"""
if not arg:
return []
if srchby == 'owner':
flocklist = flocklib.find_flock_by_owner(arg)
else:
flocklist = flocklib.find_flock(arg)
if not flocklist:
return f"Nothing found matching '{arg}'"
if one_only and len(flocklist) > 1:
return f"More than one flock found matching '{arg}'"
return flocklist
@cherrypy.expose
class Flock(FlockBase):
""" Flock root class and methods.
This class is the root of the 'flock' branch of the tree
so any configuration applied here will also apply to all subsidiary
branches unless explicitly overriden. So take care when modifying
the config for this class!
"""
def GET(self, rid=None):
"""
Return the Flock Data page
- If 'rid' is empty' or resolves to a single flock:
- If an 'officer' is logged in the data for the flock(s) specified by
'rid' or data for the most recently created flock if 'rid' is empty.
- If a 'breeder' is logged in the data for one of the breeder's flocks as
specified by 'rid' or the 'first of the breeder's flocks if 'rid' is
empty.
"""
if DEBUG:
print(f"rid: {rid}")
brdr_flocks = []
# This uri is only available to logged in users - no need to check 'login'
# brdr_flocks is a list of flock_no, flock_name tuples
if ('breeder' in cherrypy.request.loginroles and
'officer' not in cherrypy.request.logingroups):
userpers = userlib.get_user_login(cherrypy.request.login)['person_id']
brdr_flocks = [[row[0], row[1]] for row in
flocklib.get_persons_flocks(userpers)]
if not rid:
if brdr_flocks:
rid = brdr_flocks[0][0]
else:
rid = flocklib.last_flock()[0]
# Get a list of tuples of flock numbers matching 'rid'
flock_list = self.check_id(rid)
if DEBUG:
print(f"flock_list: {flock_list}")
if not isinstance(flock_list, list):
# Some sort of error or unexpected case.
raise cherrypy.HTTPError('400 Bad request', flock_list)
flock = flock_list[0]
if brdr_flocks:
if flock not in brdr_flocks:
raise cherrypy.HTTPError('400 Bad request',
"You may only view data for flock(s) that you own")
flock_no = flock[0]
page_data = util.row2dict(flocklib.get_flock_data(flock_no))
page_data['flocks'] = brdr_flocks
page_data["pagetitle"] = f"{page_data['flock_no']} - {page_data['flock_name']}"
page_data["titlebar"] = page_data["pagetitle"]
page_data["curr_owner"] = \
perslib.make_person_string(flocklib.get_flock_owner(flock_no))
page_data["owners"] = []
owners = util.rows2dicts(flocklib.get_flock_owners(flock_no))
for owner in owners:
current = memlib.is_membership_current(owner['member_no'])
# Set the text colour
if current:
owner['colour'] = ''
else:
owner['colour'] = 'grayed'
owner['pers_string'] = perslib.make_person_string(owner)
owner['current'] = current
owner['changed'] = owner['changed'][:19]
page_data['owners'] = owners
page_data["flk_stats"] = fbklib.get_flock_stats(flock_no)
prefixes = util.rows2dicts(flocklib.get_prefixes_by_flock_no(flock_no)[1])
for pfx in prefixes:
pfx['date_assigned'] = pfx['date_assigned'][:19]
page_data["prefixes"] = prefixes
page_data["health_schemes"] = flocklib.get_health_schemes()
page_data["flock_schemes"] = flocklib.get_flock_health(flock_no)
page_info = self.html_appconf_items()
page_info.update({'requestpath': cherrypy.request.path_info,
'resource': 'flock',
'rid': flock_no})
page_info.update(page_data)
template = const.JINJA_ENV.get_template('templates/flockdata.tmpl')
return template.render(page_info)
@cherrypy.tools.json_out()
def PUT(self):
""" Change flock data """
req_body = self.check_request('action', ("memsec",))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
if req_body['action'] == 'health':
print(req_body)
flocklib.update_flock_health(req_body)
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_UPDATED, "data": "Updated"}
if req_body['action'] == 'rename':
cherrypy.response.status = 200
resp = flocklib.update_flock_name(req_body)
if resp["rcode"] != xhrc.XHR_UPDATED:
cherrypy.response.status = 400
return resp
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "Invalid PUT request"}
@cherrypy.tools.json_out()
def POST(self):
""" Create a new flock or a new owner. """
req_body = self.check_request('action', ("memsec",))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
if req_body['action'] == 'flock':
# Create a new flock
breeder = perslib.get_pers_breeder(req_body['owner_id'])
if not breeder[0]:
return {"rcode": xhrc.XHR_WARN,
"data": f"Member {breeder[1]} membership class does not permit "
"flock ownership"}
result = flocklib.add_flock(req_body)
if result["rcode"] == xhrc.XHR_NOERR:
cherrypy.response.status = 201
return {"rcode": xhrc.XHR_CREATED, "data": result["data"]}
# result is an error message
return result
if req_body['action'] == 'owner':
# Add a new owner for a flock
if not 'person_id' in req_body or not req_body['person_id']:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "A new owner person must be specified."}
if not 'date' in req_body or not req_body['date']:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "An owner change date must be specified."}
try:
flocklib.change_owner(req_body)
except (flocklib.ValidationError, util.PPDError, util.PPDWarning) as err:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": str(err)}
cherrypy.response.status = 201
return {"rcode": xhrc.XHR_CREATED, "data": "New owner created"}
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "Invalid POST request"}
@cherrypy.expose
class Data(FlockBase):
""" Methods to handle data requests """
@cherrypy.tools.json_out()
def GET(self, vpath=None, rid=None):
""" Return flock and owner data for the flock owner in 'rid' """
if vpath == 'owner':
# Return owner data
if not rid:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "You must enter a value to search for"}
flock_list = flocklib.find_flock_by_owner(rid)
if not flock_list:
flock_list = f"Nothing found matching '{rid}'"
if not isinstance(flock_list, list):
# Some sort of error or unexpected case.
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": flock_list}
data = util.rows2dicts(flock_list)
for item in data:
item["owner_name"] = perslib.make_person_string(item, forename=False)
item["paid_up"] = memlib.is_membership_current(item['member_no'])
return {"rcode": xhrc.XHR_NOERR, "data": data}
if vpath == 'flock':
# rid is a flock search argument - may contain wildcard
if not rid:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "You must enter a value to search for"}
if ('breeder' in cherrypy.request.loginroles and
'officer' not in cherrypy.request.logingroups):
userpers = userlib.get_user_login(cherrypy.request.login)['person_id']
brdr_flocks = [[row[0], row[1]] for row in
flocklib.get_persons_flocks(userpers)]
else:
brdr_flocks = []
flocklist = flocklib.find_flock(rid)
if not flocklist:
# Some sort of error or unexpected case.
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": f"Nothing found matching '{rid}'"}
if brdr_flocks:
for flock in flocklist:
if flock[0] not in brdr_flocks:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "You can only view data for flock(s) that you own"}
data = util.rows2dicts(flocklib.get_flocks_current_owners(flocklist))
for item in data:
item["owner_name"] = perslib.make_person_string(item, forename=False)
return {"rcode": xhrc.XHR_NOERR, "data": data}
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "Invalid vpath"}
@cherrypy.expose
class Health(FlockBase):
""" Health root class and methods.
This class is the root of the 'flock/health' branch of the tree
so any configuration applied here will also apply to all subsidiary
branches unless explicitly overriden. So take care when modifying
the config for this class!
"""
@cherrypy.tools.json_out()
def GET(self, findval=None):
""" Return the 'Health Schemes' data """
if not 'json' in cherrypy.request.headers.get("Accept", ''):
cherrypy.response.status = 415
return {"rcode": xhrc.XHR_ERROR, "data": "Program Error: Unsupported Media Type"}
if findval:
try:
scheme_id = int(findval)
except ValueError as e:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": f"Invalid scheme_id: {e}"}
data = data = util.row2dict(flocklib.get_health_schemes(scheme_id)[0])
else:
data = util.rows2dicts(flocklib.get_health_schemes())
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_NOERR, "data": data}
@cherrypy.tools.json_out()
def PUT(self):
""" Update an existing health scheme """
req_body = self.check_request('scheme_id', ("memsec", "admin"))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
# Update the specified health scheme
if len(req_body) == 2:
if not 'active' in req_body or not 'scheme_id' in req_body:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "Missing required field(s)"}
flocklib.update_health_scheme(req_body)
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_UPDATED, "data": "Updated"}
if flocklib.is_health_scheme_used(req_body['scheme_id']):
scheme = flocklib.get_health_schemes(req_body['scheme_id'])[0]
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_WARN,
"data": (f"The Health Scheme '{scheme['health_scheme']}' has "
"been assigned to one or more flocks and can only have "
"the 'Active' flag changed")}
flocklib.update_health_scheme(req_body)
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_UPDATED, "data": "Updated"}
@cherrypy.tools.json_out()
def POST(self):
""" Create a new health scheme """
req_body = self.check_request('scheme', ("memsec", "admin"))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
if not 'short_name' in req_body or req_body['short_name'] == "":
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "Missing 'short_name'"}
scheme_id = flocklib.add_health_scheme(req_body)
cherrypy.response.status = 201
return {"rcode": xhrc.XHR_CREATED, "data": scheme_id}
@cherrypy.expose
class Lookup(FlockBase):
""" Flock Lookup class. This class has only the GET method. """
def GET(self):
""" Return the HTML lookup page """
# Set the page_info items
page_info = self.html_appconf_items()
page_info['pagetitle'] = 'Flock Lookup'
page_info['titlebar'] = page_info['pagetitle']
page_info.update({'resource': "flock/lookup",
'loginroles': cherrypy.request.loginroles,
'logingroups': cherrypy.request.logingroups})
template = const.JINJA_ENV.get_template('templates/flocklookup.tmpl')
return template.render(page_info)
@cherrypy.expose
class Maint(FlockBase):
""" A class to handle the 'Flock Maintenance page """
def GET(self):
""" Return the 'Flock Maintenance' page """
page_info = self.html_appconf_items()
roles = cherrypy.request.loginroles
if 'memsec' in roles or 'admin' in roles:
page_info["health_schemes"] = flocklib.get_health_schemes()
page_info.update({'titlebar': "Flock Administration",
'requestpath': cherrypy.request.path_info,
})
template = const.JINJA_ENV.get_template('templates/flockmaint.tmpl')
return template.render(page_info)
@cherrypy.expose
class Nav(FlockBase):
""" Class and method to return the flock number as JSON after applying the navigation
request. This URL is assumed to be called by an AJAX call from the client.
"""
@cherrypy.tools.json_out()
def GET(self, rid=None, oldflk=None):
""" Return the flock located in reponse to the navigation request """
if rid:
if rid == 'first':
flock_no = flocklib.first_flock()[0]
elif rid == 'last':
flock_no = flocklib.last_flock()[0]
elif oldflk and flocklib.exists_flock(oldflk):
if rid == 'fprev':
flock_no = flocklib.prev_flock(oldflk, recs=10)
elif rid == 'prev':
flock_no = flocklib.prev_flock(oldflk)
elif rid == 'next':
flock_no = flocklib.next_flock(oldflk)
elif rid == 'fnext':
flock_no = flocklib.next_flock(oldflk, recs=10)
else:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_WARN,
"data": f"Invalid request {rid}"}
else:
cherrypy.response.status = 400
return {"rcode": "Warning", "data": "Missing 'oldflk' parameter"}
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_NOERR, "data": flock_no}
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": f"Invalid rid {rid} or previous flock no {oldflk}"}
@cherrypy.expose
class Prefixes(FlockBase):
""" Flock Prefixes sub-resource handler """
@cherrypy.tools.json_out()
def GET(self, vpath=None, findval=None,):
""" Return a list of assigned tag prefixes from current to oldest as a JSON object """
if not findval:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": xhrc.NO_FINDVAL}
if vpath == 'flkpfx':
# This is called from JS sheep.update_prefixes - sort prefixes by 'current'
flocklist = self.check_id(findval)
if not isinstance(flocklist, list):
# Some sort of error or unexpected case.
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": flocklist}
if not flocklist:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "A resource id (rid) is required"}
if flocklist[0][0] == 'SSB000':
pfx_data = flocklib.get_island_prefixes()
else:
pfx_data = flocklib.get_flock_prefixes(flocklist[0][0],
sortby="current desc, date_assigned desc")
return {"rcode": xhrc.XHR_NOERR, "data": pfx_data}
if vpath in ('prefix', 'flock', 'year', 'name'):
# Select or sort the prefixes
arg = findval.replace('*', '%')
arg = arg.replace('?', '_')
rows = None
if vpath == 'name':
if findval and findval.lower() == "island":
sortby, rows = flocklib.get_island_prefixes()
else:
sortby, rows = flocklib.get_prefixes_by_flock_name(arg)
elif vpath == 'flock':
sortby, rows = flocklib.get_prefixes_by_flock_no(arg)
elif vpath == 'year':
sortby, rows = flocklib.get_prefixes_by_year(arg)
else:
sortby, rows = flocklib.get_prefixes_by_prefix(arg)
if not rows:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": f"No prefixes found for search on {findval}"}
prefixes = util.rows2lists(rows)
for pfx in prefixes:
pfx[5] = pfx[5][:19]
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_NOERR, "data": (sortby, prefixes)}
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "You must specify a valid vpath"}
@cherrypy.tools.json_out()
def PUT(self):
""" Change current prefix """
req_body = self.check_request('flock_no', ("regsec", "admin"))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
prefixes = util.rows2lists(flocklib.change_current_prefix(req_body)[1])
for pfx in prefixes:
pfx[5] = pfx[5][:19]
cherrypy.response.status = 200
return {"rcode": xhrc.XHR_UPDATED, "data": prefixes}
@cherrypy.tools.json_out()
def POST(self):
""" Add a prefix.
If no flock is specified then allocate it to either the Island flock or
the Unknown flock depending on the first two digits of the prefix
"""
req_body = self.check_request('prefix', ("regsec", "admin"))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
if not 'flock_no' in req_body or not req_body['flock_no']:
if req_body['prefix'][2:5] == '052' or req_body['prefix'][2:4] == '52':
req_body['flock_no'] = flocklib.FLOCK_ISLAND
else:
req_body['flock_no'] = flocklib.FLOCK_UNRECORDED
rtn = flocklib.add_prefix(req_body['flock_no'], req_body['prefix'])
if isinstance(rtn, dict):
cherrypy.response.status = 400
return rtn
prefixes = util.rows2lists(rtn[1])
for pfx in prefixes:
pfx[5] = pfx[5][:19]
cherrypy.response.status = 201
return {"rcode": xhrc.XHR_CREATED, "data": prefixes}
@cherrypy.tools.json_out()
def DELETE(self):
""" Verify the prefix is unused then delete it """
req_body = self.check_request('tag_prefix', ("regsec", "admin"))
if not isinstance(req_body, dict):
# Some sort of error or unexpected case.
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
rows = flocklib. get_prefixes_by_flock_no(req_body['flock_no'])[1]
if not rows:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": f"Flock {req_body['flock_no']} has no assigned prefixes"}
pfx_row = []
for row in rows:
if row[2] == req_body['flock_no'] and row[1] == req_body['tag_prefix']:
pfx_row = row
break;
if not pfx_row:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": f"Prefix {req_body['tag_prefix']} is not assigned to flock "
f"{req_body['flock_no']}"}
if pfx_row[4] == 'Yes':
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_USED,
"data": f"Prefix {req_body['tag_prefix']} is the 'Current' prefix for "
f"flock {req_body['flock_no']} and cannot be deleted while "
"'Current'.<br><br>"
"Make a different prefix 'Current' then retry the Delete"}
if pfx_row[6] == 'Yes':
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_USED,
"data": f"Prefix {req_body['tag_prefix']} has been used on one or "
"more sheep and cannot be deleted"}
rtn = flocklib.delete_prefix(req_body['tag_prefix'], req_body['flock_no'])
if rtn:
prefixes = util.rows2lists(rtn[1])
for pfx in prefixes:
pfx[5] = pfx[5][:19]
return {"rcode": xhrc.XHR_DELETED, "data": prefixes}
return {"rcode": xhrc.XHR_ERROR, "data": req_body}
@cherrypy.expose
class RegFlk(FlockBase):
""" Data for the 'New Flock Session' dialog """
@cherrypy.tools.json_out()
def GET(self, rid=None):
""" GET the dialog data """
# Get data for a flock in which sheep are to be registered
flocklist = self.check_id(rid)
if not isinstance(flocklist, list):
# Some sort of error or unexpected case.
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": flocklist}
if not flocklist:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "A flock number or name is required"}
data = {'flock_no': flocklist[0][0], 'flock_name': flocklist[0][1]}
# Add the flock owner data
owner = flocklib.get_flock_owner(data['flock_no'])
data['owner_id'] = owner['person_id']
data['mem_info'] = util.row2dict(perslib.get_person_member(owner['person_id']))
breeder = perslib.get_pers_breeder(owner['person_id'])
if not breeder[0]:
return {"rcode": xhrc.XHR_WARN,
"data": f"Member {breeder[1]} membership class does not permit "
"sheep registrations"}
data['mem_info']['name'] = perslib.make_person_string(data['mem_info'])
data['mem_info']['active'] = owner['active']
if owner:
data['mem_info']['paidup'] = bool(data['mem_info']['expires'] >=
util.isodate_now())
else:
# Owner = 0 - a breed special flock
data['mem_info']['paidup'] = True
curr_yr = util.isodate_now()[:4]
# javaScript has no simple test for an empty object...
flk_stats = util.row2dict(fbklib.get_flock_stats(data['flock_no'], curr_yr))
if flk_stats:
data['flock_stats'] = flk_stats
else:
data['flock_stats'] = {'pure_ewes': 0, 'crossing_ewes': 0, 'other_females': 0,
'breeding_males': 0, 'other_males': 0}
data['fbk_orders'] = fbklib.get_members_orders(data['mem_info']['member_no'],
curr_yr)
return {"rcode": xhrc.XHR_NOERR, "data": data}
@cherrypy.expose
class Stats(FlockBase):
""" Flock Statistics. This class supports only the GET method """
def GET(self):
""" GET the flock statistics. """
page_info = self.html_appconf_items()
page_info.update({'pagetitle': 'Flock Statistics',
'requestpath': cherrypy.request.path_info})
page_info['titlebar'] = page_info['pagetitle']
template = const.JINJA_ENV.get_template('templates/underconst.tmpl')
return template.render(page_info)
@cherrypy.expose
class Validators(FlockBase):
""" Flock validation methods. Unless there is an error in the vpath or parameters
all validators return HTTP status 200
"""
@cherrypy.tools.json_out()
def GET(self, vpath=None, param=None):
""" Validate the data according to vpath. """
if DEBUG:
print(f"vpath: {vpath}, param: {param}")
if not param:
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "You must specify a flock number or name"}
if vpath == 'ownerchng':
# Check that the owner of the flock in 'param' is allowed to be changed
param = param.upper()
if not flocklib.exists_flock(param):
return {"rcode": xhrc.XHR_WARN,
"data": f"The specified flock {param} does not exist"}
if param[0:3] == 'SSB':
return {"rcode": xhrc.XHR_WARN,
"data": "You can't change the owner of a 'Society' flock"}
return {"rcode": xhrc.XHR_NOERR, "data": xhrc.XHR_NOERR}
if vpath == 'exists':
# Check if a flock name exists and if so is it a synonym of the 'of ' name
sname = param
if sname[:3] == 'of ':
sname = param[3:]
row = flocklib.exists_flock_name(sname)
if not row:
return {"rcode": xhrc.XHR_UNUSED, "data": xhrc.XHR_UNUSED}
owner_id = flocklib.get_flock_owner(row['flock_no'])['person_id']
owner_name = perslib.make_person_string(
perslib.get_person_data(owner_id), False)
data = {'person_id': owner_id, 'person_name': owner_name,
'flock_name': row['flock_name']}
if param != row['flock_name']:
data['synonym'] = 'true'
return {"rcode": xhrc.XHR_EXISTS, "data": data}
if vpath == 'name':
# Check a flock name. parms are new_name[:old_name]
# old_name will not include 'old_name' in any matchesis o
parms = param.split(':')
if len(parms) == 1:
return flocklib.check_flock_name(parms[0])
if len(parms) == 2:
return flocklib.check_flock_name(parms[0], parms[1])
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "Must supply colon separated flock_name and flock_no (may be empty)"}
if vpath == 'prefix':
parms = param.split(':')
if parms[0] not in ('new', 'used'):
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR,
"data": "First parameter must be 'new' or 'used'"}
if parms[0] == 'new':
return flocklib.check_flock_prefix(parms[1], parms[2])
if parms[0] == 'used':
rows = flocklib.is_prefix_used(parms[1], parms[2])
if rows:
return {"rcode": xhrc.XHR_USED,
"data": f"Prefix is used on {len(rows)} sheep originating "
f"in flock {parms[2]} and cannot be deleted from that flock"}
return {"rcode": xhrc.XHR_UNUSED, "data": "Prefix is assigned but unused"}
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "Invalid action parameter"}
if vpath == 'owns':
# Is the person allowed to own flocks, and if so do they already own flock(s)
breeder = perslib.get_pers_breeder(param)
if not breeder[0]:
return {"rcode": xhrc.XHR_WARN,
"data": f"Member {breeder[1]} membership class does not permit "
"flock ownership"}
flocks = flocklib.get_persons_flocks(param)
return {"rcode": xhrc.XHR_NOERR, "data": flocks}
if vpath == 'regns':
# See if any sheep have been registered in the flock
return {"rcode": xhrc.XHR_NOERR,
"data": len(transfer.get_sheep_registered_in_flock(param))}
cherrypy.response.status = 400
return {"rcode": xhrc.XHR_ERROR, "data": "You must specify a valid vpath"}