- build.py: _TEMPLATE_COMPONENTS rewritten around the vet sample (53 kinds —
per-resource slices, jobs, worked appointment_reminder family, tests);
seed tokens dropped from the manifest ('sample: vet' instead)
- registry.py: get_app_component(kind) no longer renames item/example seeds;
prompts and tool docs updated to 'copy a slice and adapt'
- templates/README.md, 01_build_recipe, 40_ui_admin, wiki/03 updated off the
old seed scheme; repo inventory/config now exclude vet (docs move under
standards; react/vet itself is deleted in a later phase)
- regenerated MCP bundle, vendored templates, and site output
136 lines
4.2 KiB
Python
136 lines
4.2 KiB
Python
"""# Summary
|
|
|
|
Appointments route handlers — names must match openapi.yaml operationIds.
|
|
|
|
Notes:
|
|
``pet``, ``vet`` and ``treatment`` are foreign keys expanded to full
|
|
objects on read and normalized from ids on write. The list endpoint
|
|
supports date-prefix and pet/vet id filters.
|
|
"""
|
|
from fastapi import Request, Response
|
|
|
|
from interfaces._common import (
|
|
delete_or_404,
|
|
dump,
|
|
expand_ref,
|
|
paginate,
|
|
read_or_404,
|
|
svc,
|
|
)
|
|
|
|
|
|
async def list_appointments(
|
|
request: Request,
|
|
limit: int = 20,
|
|
offset: int = 0,
|
|
date: str | None = None,
|
|
vet: int | None = None,
|
|
pet: int | None = None,
|
|
) -> dict:
|
|
"""List appointments (paginated, filterable by date/vet/pet).
|
|
|
|
Args:
|
|
request (Request): Incoming request.
|
|
limit (int): Page size. Defaults to 20.
|
|
offset (int): Items to skip. Defaults to 0.
|
|
date (str | None): ISO date prefix to match (e.g. ``2025-06-01``).
|
|
vet (int | None): Filter by embedded vet id.
|
|
pet (int | None): Filter by embedded pet id.
|
|
|
|
Returns:
|
|
dict: ``{"total": int, "items": list[Appointment]}``.
|
|
"""
|
|
docs = await svc(request, "appointment").search({})
|
|
items = []
|
|
for d in docs:
|
|
row = dump(d)
|
|
if date and not str(row.get("date", "")).startswith(date):
|
|
continue
|
|
if vet is not None and (row.get("vet") or {}).get("id") != vet:
|
|
continue
|
|
if pet is not None and (row.get("pet") or {}).get("id") != pet:
|
|
continue
|
|
items.append(row)
|
|
return paginate(items, limit, offset)
|
|
|
|
|
|
async def create_appointment(request: Request, body: dict, response: Response) -> dict:
|
|
"""Create an appointment; FK fields may be bare ids or expanded objects.
|
|
|
|
Args:
|
|
request (Request): Incoming request.
|
|
body (dict): Appointment fields (``id`` ignored; FKs normalized).
|
|
response (Response): Response object; set to 201 on success.
|
|
|
|
Returns:
|
|
dict: The created appointment with FKs expanded.
|
|
|
|
Raises:
|
|
HTTPException: 422 when an FK reference cannot be resolved.
|
|
"""
|
|
response.status_code = 201
|
|
body.pop("id", None)
|
|
body["pet"] = await expand_ref(svc(request, "pet"), body.get("pet"), "pet")
|
|
body["vet"] = await expand_ref(svc(request, "vet"), body.get("vet"), "vet")
|
|
body["treatment"] = await expand_ref(
|
|
svc(request, "treatment"), body.get("treatment"), "treatment"
|
|
)
|
|
doc = await svc(request, "appointment").create(body)
|
|
return dump(doc)
|
|
|
|
|
|
async def get_appointment(request: Request, id: int) -> dict:
|
|
"""Fetch one appointment by id.
|
|
|
|
Args:
|
|
request (Request): Incoming request.
|
|
id (int): Appointment identifier.
|
|
|
|
Returns:
|
|
dict: The appointment document.
|
|
|
|
Raises:
|
|
HTTPException: 404 when the appointment does not exist.
|
|
"""
|
|
return dump(await read_or_404(svc(request, "appointment"), id, "appointment"))
|
|
|
|
|
|
async def update_appointment(request: Request, id: int, body: dict) -> dict:
|
|
"""Replace an appointment by id; FK fields may be ids or objects.
|
|
|
|
Args:
|
|
request (Request): Incoming request.
|
|
id (int): Appointment identifier.
|
|
body (dict): Full replacement payload.
|
|
|
|
Returns:
|
|
dict: The updated appointment with FKs expanded.
|
|
|
|
Raises:
|
|
HTTPException: 404 when the appointment does not exist; 422 on bad FKs.
|
|
"""
|
|
await read_or_404(svc(request, "appointment"), id, "appointment")
|
|
body.pop("id", None)
|
|
body["pet"] = await expand_ref(svc(request, "pet"), body.get("pet"), "pet")
|
|
body["vet"] = await expand_ref(svc(request, "vet"), body.get("vet"), "vet")
|
|
body["treatment"] = await expand_ref(
|
|
svc(request, "treatment"), body.get("treatment"), "treatment"
|
|
)
|
|
doc = await svc(request, "appointment").update(id, body)
|
|
return dump(doc)
|
|
|
|
|
|
async def delete_appointment(request: Request, id: int, response: Response) -> None:
|
|
"""Delete an appointment by id.
|
|
|
|
Args:
|
|
request (Request): Incoming request.
|
|
id (int): Appointment identifier.
|
|
response (Response): Response object; set to 204 on success.
|
|
|
|
Raises:
|
|
HTTPException: 404 when the appointment does not exist.
|
|
"""
|
|
await delete_or_404(svc(request, "appointment"), id, "appointment")
|
|
response.status_code = 204
|