2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
13# FILE: cat_endpoints.py
14# CREATION DATE: 08-12-2025
15# LAST Modified: 21:33:16 05-02-2026
17# This is the project in charge of making the connected cat feeder project work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: This is the file containing the endpoints used for the application of the cat feeder.
22# +==== END CatFeeder =================+
24from dataclasses
import dataclass
25from typing
import TYPE_CHECKING, Union, Optional
26from datetime
import datetime, timezone, timedelta
29from display_tty
import Disp, initialise_logger
31from fastapi
import Request, Response
32from ...utils
import CONST
33from ...core
import RuntimeManager, RI
34from ..
import endpoint_helpers
as EN_CONST
35from ...http_codes
import HCI
38 from typing
import List, Dict, Tuple
39 from ...sql
import SQL
40 from ...server_header
import ServerHeaders
41 from ...boilerplates
import BoilerplateIncoming, BoilerplateResponses, BoilerplateNonHTTP
46 """Dataclass to store user information.
57 disp: Disp = initialise_logger(__qualname__,
False)
59 def __init__(self, error: int = 84, success: int = 0, debug: bool =
False) ->
None:
63 self.
disp.update_disp_debug(debug)
64 self.
disp.log_debug(
"Initialising...")
77 "id",
"creation_date",
"edit_date"
84 "BoilerplateIncoming")
86 "BoilerplateResponses")
92 self.
disp.log_debug(
"Initialised")
94 def _user_connected(self, request: Request, title: str =
"_user_connected") -> Union[UserInfo, Response]:
95 """Check if the user is connected and return their information.
98 request (Request): The incoming request parameters.
100 UserInfo: The user information if connected, None otherwise.
111 if isinstance(user_id, Response):
113 if not user_id
or user_id
is None:
121 if not isinstance(usr_data, list)
or len(usr_data) == 0:
124 user_id=usr_data[0][
"id"],
125 username=usr_data[0][
"username"],
126 email=usr_data[0][
"email"],
127 is_admin=usr_data[0][
"admin"],
132 def _parse_dt(self, val: Optional[str]) -> Optional[datetime]:
135 if isinstance(val, datetime):
139 dt = datetime.fromisoformat(val)
141 except (ValueError, TypeError):
143 for fmt
in (
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%dT%H:%M:%S"):
145 return datetime.strptime(val, fmt)
146 except (ValueError, TypeError):
151 """Register a new cat feeder in the database.
154 request (Request): The incoming request parameters.
157 Response: The HTTP response to send back to the user.
159 title =
"put_register_feeder"
161 if isinstance(data, Response):
165 "latitude",
"longitude",
166 "city_locality",
"country",
"mac",
"name"
174 f
"owner={data.user_id} AND mac='{body['mac']}'",
177 if isinstance(present, list)
and len(present) > 0:
181 "Feeder with this MAC address already registered",
188 if not isinstance(cols, list):
197 body[
"city_locality"],
212 title,
"Feeder registered successfully",
"registered", data.token, error=
False
214 return HCI.created(bod)
217 """Patch (partial update) of a cat feeder.
220 request (Request): The incoming request parameters.
223 Response: The HTTP response to send back to the user.
225 title =
"patch_feeder"
227 if isinstance(data, Response):
233 if "id" not in body
and "mac" not in body:
238 "latitude",
"longitude",
239 "city_locality",
"country",
"mac",
"name"
243 if not isinstance(cols, list):
247 update_cols = [c
for c
in cols
if c
in allowed
and c
in body]
251 sql_data = [body[col]
for col
in update_cols]
256 feeder_id = int(body[
"id"])
257 except (ValueError, TypeError):
259 where = f
"id={feeder_id} AND owner={data.user_id}"
261 where = f
"owner={data.user_id} AND mac='{body['mac']}'"
273 title,
"Feeder updated successfully",
"updated", data.token, error=
False
275 return HCI.success(bod)
278 """Get the status of a cat feeder.
281 request (Request): The incoming request parameters.
284 Response: The HTTP response to send back to the user.
288 if isinstance(data, Response):
294 if "id" not in body
and "name" not in body
and "mac" not in body:
295 self.
disp.log_debug(f
"body={body}\n\n\n\n\n")
299 where_parts = [f
"owner={data.user_id}"]
302 feeder_id = int(body[
"id"])
303 where_parts.append(f
"id={feeder_id}")
304 except (ValueError, TypeError):
307 where_parts.append(f
"mac='{body['mac']}'")
309 where_parts.append(f
"name='{body['name']}'")
311 where =
" AND ".join(where_parts)
320 if not isinstance(feeder_rows, list)
or len(feeder_rows) == 0:
321 return HCI.not_found(
330 data_raw: Dict = feeder_rows[0]
331 data_clean = EN_CONST.sanitize_response_data(
332 data_raw, disp=self.
disp
345 """Get the status of a cat feeder.
348 request (Request): The incoming request parameters.
351 Response: The HTTP response to send back to the user.
353 title =
"get_feeder_status"
355 if isinstance(data, Response):
361 if "id" not in body
and "name" not in body
and "mac" not in body:
365 where_parts = [f
"owner={data.user_id}"]
368 feeder_id = int(body[
"id"])
369 where_parts.append(f
"id={feeder_id}")
370 except (ValueError, TypeError):
373 where_parts.append(f
"mac='{body['mac']}'")
375 where_parts.append(f
"name='{body['name']}'")
377 where =
" AND ".join(where_parts)
386 if not isinstance(feeder_rows, list)
or len(feeder_rows) == 0:
387 return HCI.not_found(
396 feeder_id = feeder_rows[0][
"id"]
402 f
"parent_id={feeder_id}",
405 if not isinstance(ip_rows, list)
or len(ip_rows) == 0:
406 return HCI.not_found(
409 "No IP record for this feeder",
416 self.
disp.log_debug(f
"IP rows: {ip_rows}")
420 self.
disp.log_debug(f
"Evaluating row: {r}")
423 f
"Parsed datetime: {dt} from edit_date: {r.get('edit_date')}"
427 if latest_dt
is None or dt > latest_dt:
431 if latest
is None or latest_dt
is None:
435 if datetime.now() - latest_dt > timedelta(hours=1):
438 "Feeder last seen more than 1 hour ago",
445 return HCI.request_timeout(bod)
446 except AttributeError:
447 return HCI.not_found(bod)
449 ip_address = latest.get(
"ip",
"")
455 url = f
"http://{ip_address}"
456 resp = requests.get(url, timeout=5)
457 if 200 <= resp.status_code < 400:
460 "Feeder is reachable",
465 return HCI.success(bod)
468 f
"Feeder responded with status {resp.status_code}",
473 return HCI.bad_gateway(bod)
if hasattr(HCI,
"bad_gateway")
else HCI.internal_server_error(bod)
474 except requests.exceptions.Timeout:
477 "Timed out while contacting feeder",
482 return HCI.request_timeout(bod)
if hasattr(HCI,
"request_timeout")
else HCI.internal_server_error(bod)
483 except (requests.exceptions.ConnectionError, requests.exceptions.RequestException):
486 "Error while contacting feeder",
491 return HCI.internal_server_error(bod)
494 """Update the IP address of the feeder (called by feeder itself)
497 request (Request): The incoming request with MAC and new IP
500 Response: Success/error response
502 title =
"put_feeder_ip"
507 required_fields = [
"mac",
"ip"]
508 for field
in required_fields:
509 if field
not in body:
517 f
"mac='{body['mac']}'",
521 if not isinstance(feeder_rows, list)
or len(feeder_rows) == 0:
522 return HCI.not_found(
525 "Feeder not found with this MAC",
532 feeder_id = feeder_rows[0][
"id"]
537 if not isinstance(cols, list):
546 f
"parent_id={feeder_id}",
550 if isinstance(existing_ip, list)
and len(existing_ip) > 0:
551 self.
disp.log_debug(f
"Existing IP record found: {existing_ip}")
552 self.
disp.log_debug(f
"Updating IP to: {new_ip}")
554 _now = datetime.now()
555 now_str = self.
database_link.datetime_to_string(_now,
False,
True)
560 where=f
"parent_id={feeder_id}"
564 "No existing IP record found, inserting new one.")
565 self.
disp.log_debug(f
"Inserting new IP: {new_ip}")
567 sql_data = [feeder_id, new_ip]
579 f
"Feeder IP updated to {new_ip}",
584 return HCI.success(bod)
587 """Delete a cat feeder from the database.
590 request (Request): The incoming request parameters.
592 Response: The HTTP response to send back to the user.
594 title =
"delete_feeder"
596 if isinstance(data, Response):
599 if "id" not in body
and "mac" not in body:
604 feeder_id_val = int(body[
"id"])
605 except (ValueError, TypeError):
607 where = f
"owner={data.user_id} AND id={feeder_id_val}"
609 where = f
"owner={data.user_id} AND mac='{body['mac']}'"
618 if not isinstance(feeder_rows, list)
or len(feeder_rows) == 0:
619 return HCI.not_found(
622 "Feeder not found or not owned by user",
629 feeder_id = feeder_rows[0][
"id"]
633 f
"id={feeder_id} AND owner={data.user_id}"
639 title,
"Feeder deleted successfully",
"deleted", data.token, error=
False
641 return HCI.success(bod)
644 """Get all feeders for the authenticated user.
647 request (Request): The incoming request parameters.
650 Response: The HTTP response with the list of feeders.
652 title =
"get_feeders"
654 if isinstance(data, Response):
659 "id",
"name",
"mac",
"latitude",
"longitude",
660 "city_locality",
"country",
"creation_date",
"edit_date"
662 f
"owner={data.user_id}",
665 if not isinstance(feeders, list):
667 feeders = EN_CONST.sanitize_response_data(
668 feeders, disp=self.
disp
671 title,
"The feeders have been gathered", feeders, data.token, error=
False
673 return HCI.success(bod)
676 """Register a beacon signal from a cat feeder.
679 request (Request): The incoming request parameters.
681 Response: The HTTP response to send back to the user.
683 title =
"register_beacon"
685 if isinstance(data, Response):
688 elems = [
"mac",
"name"]
692 self.
disp.log_debug(f
"body: {body}")
698 f
"owner={data.user_id} AND (mac='{body['mac']}' OR name='{body['name']}')",
701 self.
disp.log_debug(f
"present: {present}")
702 if isinstance(present, list)
and len(present) > 0:
706 "Beacon with this MAC address or name already registered",
714 self.
disp.log_debug(f
"Column names: {cols}")
715 if not isinstance(cols, list):
718 sql_data = [str(data.user_id), body[
"mac"], body[
"name"]]
719 self.
disp.log_debug(f
"raw sql_data: {sql_data}")
720 self.
disp.log_debug(f
"cols: {cols}")
730 title,
"Beacon registered successfully",
"registered", data.token, error=
False
732 return HCI.created(bod)
735 """Get the status of a beacon signal from a cat feeder.
738 request (Request): The incoming request parameters.
740 Response: The HTTP response to send back to the user.
742 title =
"get_beacon_status"
744 if isinstance(data, Response):
748 if not body
or (
"id" not in body
and "name" not in body
and "mac" not in body):
754 beacon_id = int(body[
"id"])
755 except (ValueError, TypeError):
760 where = f
"owner={data.user_id} AND name='{body['name']}'"
762 where = f
"owner={data.user_id} AND mac='{body['mac']}'"
769 if not isinstance(rows, list)
or len(rows) == 0:
770 return HCI.not_found(
779 beacon_id = rows[0][
"id"]
783 [
"id",
"mac",
"name",
"creation_date",
"edit_date"],
784 f
"id={beacon_id} AND owner={data.user_id}",
787 if not isinstance(beacon_data, list)
or len(beacon_data) == 0:
788 return HCI.not_found(
797 beacon_cleared = EN_CONST.sanitize_response_data(
798 beacon_data[0], disp=self.
disp
802 "Beacon status retrieved successfully",
803 resp={
"beacon": beacon_cleared},
807 return HCI.success(bod)
810 """Update the beacon signal from a cat feeder.
813 request (Request): The incoming request parameters.
815 Response: The HTTP response to send back to the user.
817 title =
"patch_beacon"
819 if isinstance(data, Response):
824 if "id" not in body
and "mac" not in body:
828 allowed = {
"mac",
"name"}
831 if not isinstance(cols, list):
837 if c
in allowed
and c
in body:
838 update_cols.append(c)
843 for col
in update_cols:
844 sql_data.append(body[col])
849 beacon_id = int(body[
"id"])
850 except (ValueError, TypeError):
852 where = f
"id={beacon_id} AND owner={data.user_id}"
854 where = f
"owner={data.user_id} AND mac='{body['mac']}'"
866 title,
"Beacon updated successfully",
"updated", data.token, error=
False
868 return HCI.success(bod)
871 """Delete the beacon signal from a cat feeder.
874 request (Request): The incoming request parameters.
876 Response: The HTTP response to send back to the user.
878 title =
"delete_beacon"
880 if isinstance(data, Response):
888 beacon_id = int(body[
"id"])
889 except (ValueError, TypeError):
896 f
"id={beacon_id} AND owner={data.user_id}",
899 if not isinstance(beacon_exists, list)
or len(beacon_exists) == 0:
900 return HCI.not_found(
903 "Beacon not found or not owned by user",
912 f
"id={beacon_id} AND owner={data.user_id}"
918 title,
"Beacon deleted successfully",
"deleted", data.token, error=
False
920 return HCI.success(bod)
923 """Get all beacons for the authenticated user.
926 request (Request): The incoming request parameters.
929 Response: The HTTP response with the list of beacons.
931 title =
"get_beacons"
933 if isinstance(data, Response):
937 [
"id",
"name",
"mac",
"creation_date",
"edit_date"],
938 f
"owner={data.user_id}",
941 if not isinstance(beacons_raw, list):
944 beacons = EN_CONST.sanitize_response_data(
945 beacons_raw, disp=self.
disp
949 title,
"The beacons have been gathered", beacons, data.token, error=
False
951 return HCI.success(bod)
954 """Get the list of beacon locations from cat feeders.
957 request (Request): The incoming request parameters.
959 Response: The HTTP response to send back to the user.
961 title =
"get_beacon_locations"
963 if isinstance(data, Response):
968 if "name" not in body
and "mac" not in body:
973 where_clause = f
"owner={data.user_id} AND name='{body['name']}'"
975 where_clause = f
"owner={data.user_id} AND mac='{body['mac']}'"
983 if not isinstance(beacon_data, list)
or len(beacon_data) == 0:
984 return HCI.not_found(
994 beacon_id = beacon_data[0][
"id"]
999 [
"feeder",
"creation_date"],
1000 f
"beacon={beacon_id}",
1003 if not isinstance(location_data, list):
1008 for location
in location_data:
1011 [
"name",
"latitude",
"longitude",
"city_locality",
"country"],
1012 f
"id={location['feeder']} AND owner={data.user_id}",
1015 if isinstance(feeder_data, list)
and len(feeder_data) > 0:
1018 "visit_time": location[
"creation_date"],
1019 "feeder": feeder_data[0]
1023 locations_cleaned = EN_CONST.sanitize_response_data(
1024 locations, disp=self.
disp
1028 "Beacon locations retrieved successfully",
1029 {
"locations": locations_cleaned},
1033 return HCI.success(bod)
1036 """Post a new beacon location from a cat feeder.
1039 request (Request): The incoming request parameters.
1041 Response: The HTTP response to send back to the user.
1043 title =
"post_beacon_location"
1047 elems = [
"beacon_mac",
"feeder_mac"]
1049 if elem
not in body:
1056 f
"mac='{body['beacon_mac']}'",
1059 if not isinstance(beacon_data, list)
or len(beacon_data) == 0:
1060 return HCI.not_found(
1074 f
"mac='{body['feeder_mac']}'",
1077 if not isinstance(feeder_data, list)
or len(feeder_data) == 0:
1078 return HCI.not_found(
1088 beacon_id = beacon_data[0][
"id"]
1089 feeder_id = feeder_data[0][
"id"]
1094 if not isinstance(cols, list):
1097 sql_data = [beacon_id, feeder_id]
1108 title,
"Beacon location recorded successfully",
"recorded",
"", error=
False
1110 return HCI.created(bod)
1113 """Get the list of visits recorded by a cat feeder.
1116 request (Request): The incoming request parameters.
1118 Response: The HTTP response to send back to the user.
1120 title =
"get_feeder_visits"
1122 if isinstance(data, Response):
1127 if "name" not in body
and "mac" not in body:
1132 where_clause = f
"owner={data.user_id} AND name='{body['name']}'"
1134 where_clause = f
"owner={data.user_id} AND mac='{body['mac']}'"
1142 if not isinstance(feeder_data, list)
or len(feeder_data) == 0:
1143 return HCI.not_found(
1153 feeder_id = feeder_data[0][
"id"]
1158 [
"beacon",
"creation_date"],
1159 f
"feeder={feeder_id}",
1162 if not isinstance(visit_data, list):
1167 for visit
in visit_data:
1171 f
"id={visit['beacon']}",
1174 if isinstance(beacon_info, list)
and len(beacon_info) > 0:
1176 "visit_time": visit[
"creation_date"],
1177 "beacon": beacon_info[0]
1180 visits = EN_CONST.sanitize_response_data(
1181 visits_raw, disp=self.
disp
1185 "Feeder visits retrieved successfully",
1190 return HCI.success(bod)
1193 """Get the food distribution status from a cat feeder.
1196 request (Request): The incoming request parameters.
1198 Response: The HTTP response to send back to the user.
1200 title =
"get_distribute_food"
1204 if "beacon_mac" not in body:
1211 f
"mac='{body['beacon_mac']}'",
1214 if not isinstance(beacon_data, list)
or len(beacon_data) == 0:
1215 return HCI.not_found(
1225 beacon_id = beacon_data[0][
"id"]
1230 [
"food_eaten",
"food_max",
"food_reset",
1231 "time_reset_hours",
"time_reset_minutes"],
1232 f
"beacon={beacon_id}",
1235 if not isinstance(pet_data, list)
or len(pet_data) == 0:
1236 return HCI.not_found(
1239 "Pet not found for this beacon",
1249 food_reset_time = self.
_parse_dt(pet.get(
"food_reset"))
1250 if food_reset_time
and datetime.now(timezone.utc) >= food_reset_time:
1252 reset_hours = pet.get(
"time_reset_hours", 24)
1253 reset_minutes = pet.get(
"time_reset_minutes", 0)
1254 next_reset = datetime.now(
1255 timezone.utc) + timedelta(hours=reset_hours, minutes=reset_minutes)
1259 [0, next_reset.isoformat()],
1260 [
"food_eaten",
"food_reset"],
1261 where=f
"beacon={beacon_id}"
1263 pet[
"food_eaten"] = 0
1266 can_distribute = pet[
"food_eaten"] < pet[
"food_max"]
1269 "can_distribute": can_distribute,
1270 "food_eaten": pet[
"food_eaten"],
1271 "food_max": pet[
"food_max"]
1274 cleaned_content = EN_CONST.sanitize_response_data(
1275 raw_content, disp=self.
disp
1280 "Food distribution status checked",
1285 return HCI.success(bod)
1288 """Record food distribution to a pet.
1291 request (Request): The incoming request parameters.
1293 Response: The HTTP response to send back to the user.
1295 title =
"post_distribute_food"
1299 elems = [
"beacon_mac",
"feeder_mac"]
1301 if elem
not in body:
1308 f
"mac='{body['beacon_mac']}'",
1311 if not isinstance(beacon_data, list)
or len(beacon_data) == 0:
1312 return HCI.not_found(
1326 f
"mac='{body['feeder_mac']}'",
1329 if not isinstance(feeder_data, list)
or len(feeder_data) == 0:
1330 return HCI.not_found(
1340 beacon_id = beacon_data[0][
"id"]
1346 "food_eaten",
"food_max",
"food_reset",
1347 "time_reset_hours",
"time_reset_minutes"
1349 f
"beacon={beacon_id}",
1352 if not isinstance(pet_data, list)
or len(pet_data) == 0:
1353 return HCI.not_found(
1356 "Pet not found for this beacon",
1366 food_reset_time = self.
_parse_dt(pet.get(
"food_reset"))
1367 if food_reset_time
and datetime.now(timezone.utc) >= food_reset_time:
1369 reset_hours = pet.get(
"time_reset_hours", 24)
1370 reset_minutes = pet.get(
"time_reset_minutes", 0)
1371 next_reset = datetime.now(
1372 timezone.utc) + timedelta(hours=reset_hours, minutes=reset_minutes)
1376 [0, next_reset.isoformat()],
1377 [
"food_eaten",
"food_reset"],
1378 where=f
"beacon={beacon_id}"
1380 pet[
"food_eaten"] = 0
1383 if pet[
"food_eaten"] >= pet[
"food_max"]:
1384 return HCI.forbidden(
1387 "Pet has reached daily food limit",
1395 food_amount = body.get(
"amount", 1)
1396 new_food_eaten = pet[
"food_eaten"] + food_amount
1399 if new_food_eaten > pet[
"food_max"]:
1400 new_food_eaten = pet[
"food_max"]
1407 where=f
"beacon={beacon_id}"
1413 "amount_distributed": food_amount,
1414 "new_total": new_food_eaten,
1415 "remaining": pet[
"food_max"] - new_food_eaten
1417 cleaned_data = EN_CONST.sanitize_response_data(
1418 raw_data, disp=self.
disp
1423 "Food distributed successfully",
1428 return HCI.success(bod)
1431 """Register a visit from a cat to a feeder.
1434 request (Request): The incoming request parameters.
1436 Response: The HTTP response to send back to the user.
1438 title =
"post_feeder_visit"
1443 if "beacon_mac" not in body:
1448 if "feeder_mac" not in body:
1455 f
"mac='{body['beacon_mac']}'",
1458 if not isinstance(beacon_data, list)
or len(beacon_data) == 0:
1459 return HCI.not_found(
1473 f
"mac='{body['feeder_mac']}'",
1476 if not isinstance(feeder_data, list)
or len(feeder_data) == 0:
1477 return HCI.not_found(
1487 beacon_id = beacon_data[0][
"id"]
1488 feeder_id = feeder_data[0][
"id"]
1493 if not isinstance(cols, list):
1496 sql_data = [beacon_id, feeder_id]
1507 title,
"Feeder visit recorded successfully",
"recorded",
"", error=
False
1509 return HCI.created(bod)
1512 """Register a new pet linked to a beacon.
1515 request (Request): The incoming request parameters.
1517 Response: The HTTP response to send back to the user.
1519 title =
"put_register_pet"
1521 if isinstance(data, Response):
1525 if "name" not in body:
1529 if "beacon_id" in body:
1531 beacon_id = int(body[
"beacon_id"])
1532 except (ValueError, TypeError):
1534 elif "beacon_mac" in body:
1539 f
"mac='{body['beacon_mac']}' AND owner={data.user_id}",
1542 if not isinstance(beacon_rows, list)
or len(beacon_rows) == 0:
1543 return HCI.not_found(
1546 "Beacon not found or not owned by user",
1552 beacon_id = beacon_rows[0][
"id"]
1560 f
"id={beacon_id} AND owner={data.user_id}",
1563 if not isinstance(beacon_exists, list)
or len(beacon_exists) == 0:
1564 return HCI.not_found(
1567 "Beacon not found or not owned by user",
1578 f
"beacon={beacon_id}",
1581 if isinstance(present, list)
and len(present) > 0:
1582 return HCI.conflict(
1585 "Pet already registered for this beacon",
1593 if not isinstance(cols, list):
1597 time_reset_hours = body.get(
"time_reset_hours", 24)
1598 time_reset_minutes = body.get(
"time_reset_minutes", 0)
1599 default_reset: datetime = datetime.now(timezone.utc).astimezone()
1600 default_reset = default_reset + timedelta(
1601 hours=time_reset_hours,
1602 minutes=time_reset_minutes
1608 body.get(
"breed",
None),
1609 body.get(
"age",
None),
1610 body.get(
"weight",
None),
1611 body.get(
"microchip_id",
None),
1612 body.get(
"food_eaten", 0),
1613 body.get(
"food_max", 100),
1614 body.get(
"food_reset", default_reset),
1628 title,
"Pet registered successfully",
"registered", data.token, error=
False
1630 return HCI.created(bod)
1633 """Update pet information.
1636 request (Request): The incoming request parameters.
1638 Response: The HTTP response to send back to the user.
1642 if isinstance(data, Response):
1646 if "id" not in body:
1650 pet_id = int(body[
"id"])
1651 except (ValueError, TypeError):
1661 if not isinstance(pet_data, list)
or len(pet_data) == 0:
1662 return HCI.not_found(
1672 beacon_id = pet_data[0][
"beacon"]
1679 if not isinstance(beacon_owner, list)
or len(beacon_owner) == 0
or beacon_owner[0][
"owner"] != data.user_id:
1684 "name",
"food_eaten",
"food_max",
"food_reset",
1685 "time_reset_hours",
"time_reset_minutes"
1689 if not isinstance(cols, list):
1693 update_cols = [c
for c
in cols
if c
in allowed
and c
in body]
1698 for col
in update_cols:
1699 sql_data.append(body[col])
1705 where=f
"id={pet_id}"
1711 title,
"Pet updated successfully",
"updated", data.token, error=
False
1713 return HCI.success(bod)
1715 async def get_pet(self, request: Request) -> Response:
1716 """Get pet information.
1719 request (Request): The incoming request parameters.
1721 Response: The HTTP response to send back to the user.
1725 if isinstance(data, Response):
1729 if "id" not in body:
1733 pet_id = int(body[
"id"])
1734 except (ValueError, TypeError):
1742 "beacon",
"name",
"food_eaten",
"food_max",
"food_reset",
1743 "time_reset_hours",
"time_reset_minutes"
1748 if not isinstance(pet_data, list)
or len(pet_data) == 0:
1749 return HCI.not_found(
1759 self.
disp.log_debug(
1760 f
"Pet data retrieved for pet_id {pet_id}: {pet_data}"
1764 beacon_id = pet_data[0][
"beacon"]
1771 self.
disp.log_debug(
1772 f
"Beacon owner for beacon_id {beacon_id}: {beacon_owner}"
1774 if not isinstance(beacon_owner, list)
or len(beacon_owner) == 0
or beacon_owner[0][
"owner"] != data.user_id:
1779 for k, v
in pet_data[0].items():
1782 resp_raw = [pet_dict]
1784 resp = EN_CONST.sanitize_response_data(
1785 resp_raw, disp=self.
disp
1789 title,
"Pet retrieved successfully", resp, data.token, error=
False
1791 return HCI.success(bod)
1794 """Delete a pet from the database.
1797 request (Request): The incoming request parameters.
1799 Response: The HTTP response to send back to the user.
1801 title =
"delete_pet"
1803 if isinstance(data, Response):
1807 if elem
not in body:
1811 pet_id = int(body[
"id"])
1812 except (ValueError, TypeError):
1822 if not isinstance(pet_data, list)
or len(pet_data) == 0:
1823 return HCI.not_found(
1833 beacon_id = pet_data[0][
"beacon"]
1840 if not isinstance(beacon_owner, list)
or len(beacon_owner) == 0
or beacon_owner[0][
"owner"] != data.user_id:
1851 title,
"Pet deleted successfully",
"deleted", data.token, error=
False
1853 return HCI.success(bod)
1855 async def get_pets(self, request: Request) -> Response:
1856 """Get all pets for the authenticated user.
1859 request (Request): The incoming request parameters.
1862 Response: The HTTP response with the list of pets.
1866 if isinstance(data, Response):
1871 f
"owner={data.user_id}",
1874 if not isinstance(beacons_raw, list):
1876 if len(beacons_raw) == 0:
1878 title,
"The pets have been gathered.", [], data.token, error=
False
1880 return HCI.success(bod)
1882 for i
in enumerate(beacons_raw):
1883 beacons.append(i[1][
"id"])
1885 if not isinstance(columns, list):
1888 for beacon
in beacons:
1895 self.
disp.log_debug(f
"Pet data for beacon {beacon}: {pet}")
1896 if not isinstance(pet, list):
1900 pets_raw.append(pet[0])
1901 self.
disp.log_debug(f
"Raw pets data: {pets_raw}")
1902 pets = EN_CONST.sanitize_response_data(
1903 pets_raw, disp=self.
disp
1906 title,
"The pets have been gathered.", pets, data.token, error=
False
1908 return HCI.success(bod)
Response patch_beacon(self, Request request)
Response post_feeder_visit(self, Request request)
None __init__(self, int error=84, int success=0, bool debug=False)
Response post_distribute_food(self, Request request)
Response get_distribute_food(self, Request request)
Response get_beacon_status(self, Request request)
RuntimeManager boilerplate_responses_initialised
RuntimeManager boilerplate_incoming_initialised
RuntimeManager server_headers_initialised
Union[UserInfo, Response] _user_connected(self, Request request, str title="_user_connected")
Response get_feeder(self, Request request)
Response post_beacon_location(self, Request request)
Response get_feeder_status(self, Request request)
Response delete_feeder(self, Request request)
Response get_pet(self, Request request)
Response get_feeder_visits(self, Request request)
RuntimeManager database_link
Response get_feeders(self, Request request)
Response get_pets(self, Request request)
Response put_register_pet(self, Request request)
Response put_register_beacon(self, Request request)
Response patch_feeder(self, Request request)
Response put_register_feeder(self, Request request)
Response delete_pet(self, Request request)
Response get_beacons(self, Request request)
Optional[datetime] _parse_dt(self, Optional[str] val)
Response delete_beacon(self, Request request)
Response get_beacon_locations(self, Request request)
RuntimeManager boilerplate_non_http_initialised
Response put_feeder_ip(self, Request request)
RuntimeManager runtime_manager
Response patch_pet(self, Request request)