Phase 7: serve the vet sample from the template tools and docs
- 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
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
"""# Summary
|
||||
|
||||
Appointments interfaces."""
|
||||
135
standards/mcp/templates/app/interfaces/appointments/routes.py
Normal file
135
standards/mcp/templates/app/interfaces/appointments/routes.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""# 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
|
||||
@@ -1,90 +0,0 @@
|
||||
"""# Summary
|
||||
|
||||
Item route handlers — names must match openapi.yaml operationIds.
|
||||
"""
|
||||
from fastapi import Request, Response
|
||||
|
||||
from interfaces._common import delete_or_404, dump, paginate, read_or_404, svc
|
||||
|
||||
|
||||
async def list_items(request: Request, limit: int = 20, offset: int = 0) -> dict:
|
||||
"""List items (paginated). ``limit=0`` returns all remaining items.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
limit (int): Page size. Defaults to 20.
|
||||
offset (int): Items to skip. Defaults to 0.
|
||||
|
||||
Returns:
|
||||
dict: ``{"total": int, "items": list[Item]}``.
|
||||
"""
|
||||
docs = await svc(request, "item").search({})
|
||||
return paginate([dump(d) for d in docs], limit, offset)
|
||||
|
||||
|
||||
async def create_item(request: Request, body: dict, response: Response) -> dict:
|
||||
"""Create an item.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
body (dict): Item fields (``id`` ignored).
|
||||
response (Response): Response object; set to 201 on success.
|
||||
|
||||
Returns:
|
||||
dict: The created item.
|
||||
"""
|
||||
response.status_code = 201
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "item").create(body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def get_item(request: Request, id: int) -> dict:
|
||||
"""Fetch one item by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Item identifier.
|
||||
|
||||
Returns:
|
||||
dict: The item document.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the item does not exist.
|
||||
"""
|
||||
return dump(await read_or_404(svc(request, "item"), id, "item"))
|
||||
|
||||
|
||||
async def update_item(request: Request, id: int, body: dict) -> dict:
|
||||
"""Replace an item by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Item identifier.
|
||||
body (dict): Full replacement payload.
|
||||
|
||||
Returns:
|
||||
dict: The updated item.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the item does not exist.
|
||||
"""
|
||||
await read_or_404(svc(request, "item"), id, "item")
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "item").update(id, body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def delete_item(request: Request, id: int, response: Response) -> None:
|
||||
"""Delete an item by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Item identifier.
|
||||
response (Response): Response object; set to 204 on success.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the item does not exist.
|
||||
"""
|
||||
await delete_or_404(svc(request, "item"), id, "item")
|
||||
response.status_code = 204
|
||||
@@ -1,25 +1,29 @@
|
||||
"""# Summary
|
||||
|
||||
Job route handlers — names must match openapi.yaml operationIds.
|
||||
Jobs route handlers — names must match openapi.yaml operationIds.
|
||||
"""
|
||||
from fastapi import Request, Response
|
||||
|
||||
from interfaces._common import delete_or_404, dump, paginate, read_or_404, svc
|
||||
|
||||
|
||||
async def list_jobs(request: Request, limit: int = 20, offset: int = 0) -> dict:
|
||||
"""List jobs (paginated). ``limit=0`` returns all remaining items.
|
||||
async def list_jobs(request: Request, limit: int = 20, offset: int = 0, status: str | None = None) -> dict:
|
||||
"""List jobs (paginated, optionally filtered by status).
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
limit (int): Page size. Defaults to 20.
|
||||
offset (int): Items to skip. Defaults to 0.
|
||||
status (str | None): Filter by status (pending/processing/done/failed).
|
||||
|
||||
Returns:
|
||||
dict: ``{"total": int, "items": list[Job]}``.
|
||||
"""
|
||||
docs = await svc(request, "job").search({})
|
||||
return paginate([dump(d) for d in docs], limit, offset)
|
||||
items = [dump(d) for d in docs]
|
||||
if status:
|
||||
items = [row for row in items if row.get("status") == status]
|
||||
return paginate(items, limit, offset)
|
||||
|
||||
|
||||
async def create_job(request: Request, body: dict, response: Response) -> dict:
|
||||
@@ -5,6 +5,10 @@ OpenAPI-first router resolves each operation to a handler by name, so this modul
|
||||
re-exports every route handler from the subpackages into one flat namespace.
|
||||
"""
|
||||
# ruff: noqa: F403
|
||||
from interfaces.item.routes import *
|
||||
from interfaces.job.routes import *
|
||||
from interfaces.appointments.routes import *
|
||||
from interfaces.jobs.routes import *
|
||||
from interfaces.parents.routes import *
|
||||
from interfaces.pets.routes import *
|
||||
from interfaces.system.routes import *
|
||||
from interfaces.treatments.routes import *
|
||||
from interfaces.vets.routes import *
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""# Summary
|
||||
|
||||
Parents interfaces."""
|
||||
90
standards/mcp/templates/app/interfaces/parents/routes.py
Normal file
90
standards/mcp/templates/app/interfaces/parents/routes.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""# Summary
|
||||
|
||||
Parents route handlers — names must match openapi.yaml operationIds.
|
||||
"""
|
||||
from fastapi import Request, Response
|
||||
|
||||
from interfaces._common import delete_or_404, dump, paginate, read_or_404, svc
|
||||
|
||||
|
||||
async def list_parents(request: Request, limit: int = 20, offset: int = 0) -> dict:
|
||||
"""List parents (paginated). ``limit=0`` returns all remaining items.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
limit (int): Page size. Defaults to 20.
|
||||
offset (int): Items to skip. Defaults to 0.
|
||||
|
||||
Returns:
|
||||
dict: ``{"total": int, "items": list[Parent]}``.
|
||||
"""
|
||||
docs = await svc(request, "parent").search({})
|
||||
return paginate([dump(d) for d in docs], limit, offset)
|
||||
|
||||
|
||||
async def create_parent(request: Request, body: dict, response: Response) -> dict:
|
||||
"""Create a parent.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
body (dict): Parent fields (``id`` ignored).
|
||||
response (Response): Response object; set to 201 on success.
|
||||
|
||||
Returns:
|
||||
dict: The created parent.
|
||||
"""
|
||||
response.status_code = 201
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "parent").create(body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def get_parent(request: Request, id: int) -> dict:
|
||||
"""Fetch one parent by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Parent identifier.
|
||||
|
||||
Returns:
|
||||
dict: The parent document.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the parent does not exist.
|
||||
"""
|
||||
return dump(await read_or_404(svc(request, "parent"), id, "parent"))
|
||||
|
||||
|
||||
async def update_parent(request: Request, id: int, body: dict) -> dict:
|
||||
"""Replace a parent by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Parent identifier.
|
||||
body (dict): Full replacement payload.
|
||||
|
||||
Returns:
|
||||
dict: The updated parent.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the parent does not exist.
|
||||
"""
|
||||
await read_or_404(svc(request, "parent"), id, "parent")
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "parent").update(id, body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def delete_parent(request: Request, id: int, response: Response) -> None:
|
||||
"""Delete a parent by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Parent identifier.
|
||||
response (Response): Response object; set to 204 on success.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the parent does not exist.
|
||||
"""
|
||||
await delete_or_404(svc(request, "parent"), id, "parent")
|
||||
response.status_code = 204
|
||||
3
standards/mcp/templates/app/interfaces/pets/__init__.py
Normal file
3
standards/mcp/templates/app/interfaces/pets/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""# Summary
|
||||
|
||||
Pets interfaces."""
|
||||
172
standards/mcp/templates/app/interfaces/pets/routes.py
Normal file
172
standards/mcp/templates/app/interfaces/pets/routes.py
Normal file
@@ -0,0 +1,172 @@
|
||||
"""# Summary
|
||||
|
||||
Pets route handlers — names must match openapi.yaml operationIds.
|
||||
|
||||
Notes:
|
||||
``parents`` is a list foreign key expanded to full objects on read and
|
||||
normalized from ids on write. ``upload_pet_photo`` stores the raw bytes
|
||||
under ``settings.uploads_dir``; ``stream_actions`` returns an SSE stream
|
||||
scoped to the pet's species.
|
||||
"""
|
||||
import os
|
||||
from collections.abc import AsyncIterator
|
||||
|
||||
from fastapi import Request, Response, UploadFile
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from config import settings
|
||||
from infra.adapters import actions_stream
|
||||
from interfaces._common import (
|
||||
delete_or_404,
|
||||
dump,
|
||||
expand_ref_list,
|
||||
paginate,
|
||||
read_or_404,
|
||||
svc,
|
||||
)
|
||||
|
||||
|
||||
async def list_pets(request: Request, limit: int = 20, offset: int = 0) -> dict:
|
||||
"""List pets (paginated). ``limit=0`` returns all remaining items.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
limit (int): Page size. Defaults to 20.
|
||||
offset (int): Items to skip. Defaults to 0.
|
||||
|
||||
Returns:
|
||||
dict: ``{"total": int, "items": list[Pet]}``.
|
||||
"""
|
||||
docs = await svc(request, "pet").search({})
|
||||
return paginate([dump(d) for d in docs], limit, offset)
|
||||
|
||||
|
||||
async def create_pet(request: Request, body: dict, response: Response) -> dict:
|
||||
"""Create a pet; parents may be bare ids or expanded objects.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
body (dict): Pet fields (``id`` ignored; ``parents`` normalized).
|
||||
response (Response): Response object; set to 201 on success.
|
||||
|
||||
Returns:
|
||||
dict: The created pet with parents expanded.
|
||||
|
||||
Raises:
|
||||
HTTPException: 422 when a parent reference cannot be resolved.
|
||||
"""
|
||||
response.status_code = 201
|
||||
body.pop("id", None)
|
||||
body["parents"] = await expand_ref_list(
|
||||
svc(request, "parent"), body.get("parents"), "parent"
|
||||
)
|
||||
doc = await svc(request, "pet").create(body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def get_pet(request: Request, id: int) -> dict:
|
||||
"""Fetch one pet by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Pet identifier.
|
||||
|
||||
Returns:
|
||||
dict: The pet document.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the pet does not exist.
|
||||
"""
|
||||
return dump(await read_or_404(svc(request, "pet"), id, "pet"))
|
||||
|
||||
|
||||
async def update_pet(request: Request, id: int, body: dict) -> dict:
|
||||
"""Replace a pet by id; parents may be bare ids or expanded objects.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Pet identifier.
|
||||
body (dict): Full replacement payload.
|
||||
|
||||
Returns:
|
||||
dict: The updated pet with parents expanded.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the pet does not exist; 422 on a bad parent ref.
|
||||
"""
|
||||
await read_or_404(svc(request, "pet"), id, "pet")
|
||||
body.pop("id", None)
|
||||
body["parents"] = await expand_ref_list(
|
||||
svc(request, "parent"), body.get("parents"), "parent"
|
||||
)
|
||||
doc = await svc(request, "pet").update(id, body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def delete_pet(request: Request, id: int, response: Response) -> None:
|
||||
"""Delete a pet by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Pet identifier.
|
||||
response (Response): Response object; set to 204 on success.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the pet does not exist.
|
||||
"""
|
||||
await delete_or_404(svc(request, "pet"), id, "pet")
|
||||
response.status_code = 204
|
||||
|
||||
|
||||
async def upload_pet_photo(request: Request, id: int, file: UploadFile) -> dict:
|
||||
"""Store an uploaded photo for a pet and remember its path.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Pet identifier.
|
||||
file (UploadFile): Multipart upload carrying the photo bytes.
|
||||
|
||||
Returns:
|
||||
dict: ``{"id": int, "status": "photo_uploaded", "path": str}``.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the pet does not exist.
|
||||
"""
|
||||
pet = await read_or_404(svc(request, "pet"), id, "pet")
|
||||
os.makedirs(settings.uploads_dir, exist_ok=True)
|
||||
safe_name = os.path.basename(file.filename or "photo")
|
||||
path = os.path.join(settings.uploads_dir, f"{id}_{safe_name}")
|
||||
content = await file.read()
|
||||
with open(path, "wb") as fh:
|
||||
fh.write(content)
|
||||
await svc(request, "pet").update(id, {"photo": path, "parents": pet.parents})
|
||||
return {"id": id, "status": "photo_uploaded", "path": path}
|
||||
|
||||
|
||||
async def stream_actions(request: Request, id: int) -> StreamingResponse:
|
||||
"""Stream random, species-appropriate pet actions over SSE.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Pet identifier whose species scopes the stream.
|
||||
|
||||
Returns:
|
||||
StreamingResponse: ``text/event-stream`` emitting ``data: {action}``.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the pet does not exist.
|
||||
"""
|
||||
pet = await read_or_404(svc(request, "pet"), id, "pet")
|
||||
|
||||
async def event_gen() -> AsyncIterator[str]:
|
||||
queue = await actions_stream.subscribe(id, pet.species)
|
||||
try:
|
||||
while True:
|
||||
if await request.is_disconnected():
|
||||
break
|
||||
data = await queue.get()
|
||||
yield f"data: {data}\n\n"
|
||||
finally:
|
||||
actions_stream.unsubscribe(id, queue)
|
||||
|
||||
return StreamingResponse(event_gen(), media_type="text/event-stream")
|
||||
@@ -8,6 +8,6 @@ async def health() -> dict:
|
||||
"""Liveness probe — public, unauthenticated.
|
||||
|
||||
Returns:
|
||||
dict: ``{"status": "ok", "service": "app-template"}``.
|
||||
dict: ``{"status": "ok", "service": "vet-server"}``.
|
||||
"""
|
||||
return {"status": "ok", "service": "app-template"}
|
||||
return {"status": "ok", "service": "vet-server"}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""# Summary
|
||||
|
||||
Treatments interfaces."""
|
||||
92
standards/mcp/templates/app/interfaces/treatments/routes.py
Normal file
92
standards/mcp/templates/app/interfaces/treatments/routes.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""# Summary
|
||||
|
||||
Treatments route handlers — names must match openapi.yaml operationIds.
|
||||
|
||||
Notes:
|
||||
``list_treatments`` returns a plain array (the catalogue is small and
|
||||
unpaginated by contract).
|
||||
"""
|
||||
from fastapi import Request, Response
|
||||
|
||||
from interfaces._common import delete_or_404, dump, read_or_404, svc
|
||||
|
||||
|
||||
async def list_treatments(request: Request) -> list[dict]:
|
||||
"""List treatments (catalogue, unpaginated).
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
|
||||
Returns:
|
||||
list[dict]: All treatment documents.
|
||||
"""
|
||||
docs = await svc(request, "treatment").search({})
|
||||
return [dump(d) for d in docs]
|
||||
|
||||
|
||||
async def create_treatment(request: Request, body: dict, response: Response) -> dict:
|
||||
"""Add a treatment (admin only).
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
body (dict): Treatment fields (``id`` ignored).
|
||||
response (Response): Response object; set to 201 on success.
|
||||
|
||||
Returns:
|
||||
dict: The created treatment.
|
||||
"""
|
||||
response.status_code = 201
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "treatment").create(body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def get_treatment(request: Request, id: int) -> dict:
|
||||
"""Fetch one treatment by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Treatment identifier.
|
||||
|
||||
Returns:
|
||||
dict: The treatment document.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the treatment does not exist.
|
||||
"""
|
||||
return dump(await read_or_404(svc(request, "treatment"), id, "treatment"))
|
||||
|
||||
|
||||
async def update_treatment(request: Request, id: int, body: dict) -> dict:
|
||||
"""Replace a treatment by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Treatment identifier.
|
||||
body (dict): Full replacement payload.
|
||||
|
||||
Returns:
|
||||
dict: The updated treatment.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the treatment does not exist.
|
||||
"""
|
||||
await read_or_404(svc(request, "treatment"), id, "treatment")
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "treatment").update(id, body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def delete_treatment(request: Request, id: int, response: Response) -> None:
|
||||
"""Delete a treatment by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Treatment identifier.
|
||||
response (Response): Response object; set to 204 on success.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the treatment does not exist.
|
||||
"""
|
||||
await delete_or_404(svc(request, "treatment"), id, "treatment")
|
||||
response.status_code = 204
|
||||
3
standards/mcp/templates/app/interfaces/vets/__init__.py
Normal file
3
standards/mcp/templates/app/interfaces/vets/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""# Summary
|
||||
|
||||
Vets interfaces."""
|
||||
90
standards/mcp/templates/app/interfaces/vets/routes.py
Normal file
90
standards/mcp/templates/app/interfaces/vets/routes.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""# Summary
|
||||
|
||||
Vets route handlers — names must match openapi.yaml operationIds.
|
||||
"""
|
||||
from fastapi import Request, Response
|
||||
|
||||
from interfaces._common import delete_or_404, dump, paginate, read_or_404, svc
|
||||
|
||||
|
||||
async def list_vets(request: Request, limit: int = 20, offset: int = 0) -> dict:
|
||||
"""List vets (paginated). ``limit=0`` returns all remaining items.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
limit (int): Page size. Defaults to 20.
|
||||
offset (int): Items to skip. Defaults to 0.
|
||||
|
||||
Returns:
|
||||
dict: ``{"total": int, "items": list[Vet]}``.
|
||||
"""
|
||||
docs = await svc(request, "vet").search({})
|
||||
return paginate([dump(d) for d in docs], limit, offset)
|
||||
|
||||
|
||||
async def create_vet(request: Request, body: dict, response: Response) -> dict:
|
||||
"""Create a vet.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
body (dict): Vet fields (``id`` ignored).
|
||||
response (Response): Response object; set to 201 on success.
|
||||
|
||||
Returns:
|
||||
dict: The created vet.
|
||||
"""
|
||||
response.status_code = 201
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "vet").create(body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def get_vet(request: Request, id: int) -> dict:
|
||||
"""Fetch one vet by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Vet identifier.
|
||||
|
||||
Returns:
|
||||
dict: The vet document.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the vet does not exist.
|
||||
"""
|
||||
return dump(await read_or_404(svc(request, "vet"), id, "vet"))
|
||||
|
||||
|
||||
async def update_vet(request: Request, id: int, body: dict) -> dict:
|
||||
"""Replace a vet by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Vet identifier.
|
||||
body (dict): Full replacement payload.
|
||||
|
||||
Returns:
|
||||
dict: The updated vet.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the vet does not exist.
|
||||
"""
|
||||
await read_or_404(svc(request, "vet"), id, "vet")
|
||||
body.pop("id", None)
|
||||
doc = await svc(request, "vet").update(id, body)
|
||||
return dump(doc)
|
||||
|
||||
|
||||
async def delete_vet(request: Request, id: int, response: Response) -> None:
|
||||
"""Delete a vet by id.
|
||||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
id (int): Vet identifier.
|
||||
response (Response): Response object; set to 204 on success.
|
||||
|
||||
Raises:
|
||||
HTTPException: 404 when the vet does not exist.
|
||||
"""
|
||||
await delete_or_404(svc(request, "vet"), id, "vet")
|
||||
response.status_code = 204
|
||||
Reference in New Issue
Block a user