Files
openapi-first/openapi_first/templates/vet_app/routes.py

384 lines
9.9 KiB
Python

"""
Veterinary Clinic route handlers bound via OpenAPI operationId.
Handlers explicitly control HTTP response status codes to ensure runtime
behavior matches the OpenAPI contract. Domain models defined using
Pydantic are used for request and response payloads.
No routing decorators, path definitions, or implicit framework behavior
appear in this module. All routing, HTTP methods, and schemas are defined
in the OpenAPI specification.
"""
from fastapi import Response, HTTPException, UploadFile
from fastapi.responses import StreamingResponse
from sse import subscribe, unsubscribe
from models import (
ParentCreate,
VetCreate,
TreatmentCreate,
PetCreate,
AppointmentCreate,
)
from data import (
list_parents as _list_parents,
get_parent as _get_parent,
create_parent as _create_parent,
update_parent as _update_parent,
delete_parent as _delete_parent,
list_vets as _list_vets,
get_vet as _get_vet,
create_vet as _create_vet,
update_vet as _update_vet,
delete_vet as _delete_vet,
list_treatments as _list_treatments,
get_treatment as _get_treatment,
create_treatment as _create_treatment,
update_treatment as _update_treatment,
delete_treatment as _delete_treatment,
list_pets as _list_pets,
get_pet as _get_pet,
create_pet as _create_pet,
update_pet as _update_pet,
delete_pet as _delete_pet,
list_appointments as _list_appointments,
get_appointment as _get_appointment,
create_appointment as _create_appointment,
update_appointment as _update_appointment,
delete_appointment as _delete_appointment,
)
# ---------------------------------------------------------------------------
# Parents
# ---------------------------------------------------------------------------
def list_parents(limit: int = 20, offset: int = 0):
"""List parents (paginated).
Parameters
----------
limit : int
Maximum number of records to return.
offset : int
Number of records to skip.
Returns
-------
dict
Paginated response with ``total`` and ``items``.
"""
items = _list_parents()
return {"total": len(items), "items": items[offset:offset + limit] if limit else items[offset:]}
def create_parent(payload: ParentCreate, response: Response):
"""Create a parent.
Parameters
----------
payload : ParentCreate
Parent data excluding the ``id`` field.
Returns
-------
Parent
The newly created parent.
"""
parent = _create_parent(payload)
response.status_code = 201
return parent
def get_parent(id: int):
"""Retrieve a single parent by ID.
Parameters
----------
id : int
Identifier of the parent.
Returns
-------
Parent
The requested parent.
Raises
------
HTTPException
404 if the parent does not exist.
"""
try:
return _get_parent(id)
except KeyError:
raise HTTPException(status_code=404, detail="Parent not found")
def update_parent(id: int, payload: ParentCreate):
"""Update an existing parent.
Parameters
----------
id : int
Identifier of the parent.
payload : ParentCreate
Updated parent data.
Returns
-------
Parent
The updated parent.
Raises
------
HTTPException
404 if the parent does not exist.
"""
try:
return _update_parent(id, payload)
except KeyError:
raise HTTPException(status_code=404, detail="Parent not found")
def delete_parent(id: int, response: Response):
"""Delete an existing parent.
Parameters
----------
id : int
Identifier of the parent.
Raises
------
HTTPException
404 if the parent does not exist.
"""
try:
_delete_parent(id)
except KeyError:
raise HTTPException(status_code=404, detail="Parent not found")
response.status_code = 204
# ---------------------------------------------------------------------------
# Vets
# ---------------------------------------------------------------------------
def list_vets(limit: int = 20, offset: int = 0):
"""List vets (paginated)."""
items = _list_vets()
return {"total": len(items), "items": items[offset:offset + limit] if limit else items[offset:]}
def create_vet(payload: VetCreate, response: Response):
"""Create a vet."""
vet = _create_vet(payload)
response.status_code = 201
return vet
def get_vet(id: int):
"""Retrieve a single vet by ID."""
try:
return _get_vet(id)
except KeyError:
raise HTTPException(status_code=404, detail="Vet not found")
def update_vet(id: int, payload: VetCreate):
"""Update an existing vet."""
try:
return _update_vet(id, payload)
except KeyError:
raise HTTPException(status_code=404, detail="Vet not found")
def delete_vet(id: int, response: Response):
"""Delete an existing vet."""
try:
_delete_vet(id)
except KeyError:
raise HTTPException(status_code=404, detail="Vet not found")
response.status_code = 204
# ---------------------------------------------------------------------------
# Treatments
# ---------------------------------------------------------------------------
def list_treatments():
"""List treatments (catalogue).
Returns
-------
list[Treatment]
A list of treatment domain objects.
"""
return _list_treatments()
def create_treatment(payload: TreatmentCreate, response: Response):
"""Add a treatment (admin only)."""
treatment = _create_treatment(payload)
response.status_code = 201
return treatment
def get_treatment(id: int):
"""Retrieve a single treatment by ID."""
try:
return _get_treatment(id)
except KeyError:
raise HTTPException(status_code=404, detail="Treatment not found")
def update_treatment(id: int, payload: TreatmentCreate):
"""Update an existing treatment."""
try:
return _update_treatment(id, payload)
except KeyError:
raise HTTPException(status_code=404, detail="Treatment not found")
def delete_treatment(id: int, response: Response):
"""Delete an existing treatment."""
try:
_delete_treatment(id)
except KeyError:
raise HTTPException(status_code=404, detail="Treatment not found")
response.status_code = 204
# ---------------------------------------------------------------------------
# Pets
# ---------------------------------------------------------------------------
def list_pets(limit: int = 20, offset: int = 0):
"""List pets (paginated)."""
items = _list_pets()
return {"total": len(items), "items": items[offset:offset + limit] if limit else items[offset:]}
def create_pet(payload: PetCreate, response: Response):
"""Create a pet."""
pet = _create_pet(payload)
response.status_code = 201
return pet
def get_pet(id: int):
"""Retrieve a single pet by ID."""
try:
return _get_pet(id)
except KeyError:
raise HTTPException(status_code=404, detail="Pet not found")
def update_pet(id: int, payload: PetCreate):
"""Update an existing pet."""
try:
return _update_pet(id, payload)
except KeyError:
raise HTTPException(status_code=404, detail="Pet not found")
def delete_pet(id: int, response: Response):
"""Delete an existing pet."""
try:
_delete_pet(id)
except KeyError:
raise HTTPException(status_code=404, detail="Pet not found")
response.status_code = 204
def upload_pet_photo(id: int, file: UploadFile):
"""Upload a pet photo.
Parameters
----------
id : int
Identifier of the pet.
file : UploadFile
Image file to upload.
Returns
-------
dict
A confirmation with the pet ID.
"""
_ = file # In a real app, save to disk / object store
return {"id": id, "status": "photo_uploaded"}
# ---------------------------------------------------------------------------
# Appointments
# ---------------------------------------------------------------------------
def list_appointments(limit: int = 20, offset: int = 0, date: str = None, vet: int = None, pet: int = None):
"""List appointments (paginated, filterable)."""
items = _list_appointments()
# Basic in-memory filtering
if date:
items = [a for a in items if a.date.startswith(date)]
if vet is not None:
items = [a for a in items if a.vet.id == vet]
if pet is not None:
items = [a for a in items if a.pet.id == pet]
return {"total": len(items), "items": items[offset:offset + limit] if limit else items[offset:]}
def create_appointment(payload: AppointmentCreate, response: Response):
"""Create an appointment."""
appointment = _create_appointment(payload)
response.status_code = 201
return appointment
def get_appointment(id: int):
"""Retrieve a single appointment by ID."""
try:
return _get_appointment(id)
except KeyError:
raise HTTPException(status_code=404, detail="Appointment not found")
def update_appointment(id: int, payload: AppointmentCreate):
"""Update an existing appointment."""
try:
return _update_appointment(id, payload)
except KeyError:
raise HTTPException(status_code=404, detail="Appointment not found")
def delete_appointment(id: int, response: Response):
"""Delete an existing appointment."""
try:
_delete_appointment(id)
except KeyError:
raise HTTPException(status_code=404, detail="Appointment not found")
response.status_code = 204
async def stream_calls():
"""Stream random animal sounds via SSE."""
q = await subscribe()
async def event_generator():
try:
while True:
data = await q.get()
yield f"data: {data}\n\n"
finally:
unsubscribe(q)
return StreamingResponse(event_generator(), media_type="text/event-stream")