diff --git a/openapi_first/templates/vet_app/data.py b/openapi_first/templates/vet_app/data.py index cb07b97..bd6349f 100644 --- a/openapi_first/templates/vet_app/data.py +++ b/openapi_first/templates/vet_app/data.py @@ -52,7 +52,9 @@ def create_parent(payload: ParentCreate) -> Parent: now = _now() parent = Parent( id=_parents_next_id, - **payload.model_dump(exclude={"id", "metadata"}), + name=payload.name, + email=payload.email, + phone=payload.phone, metadata={"createdOn": now, "updatedOn": now} if payload.metadata else None, ) _parents[_parents_next_id] = parent @@ -64,13 +66,13 @@ def update_parent(parent_id: int, payload: ParentCreate) -> Parent: if parent_id not in _parents: raise KeyError(parent_id) now = _now() - parent = _parents[parent_id] - updated = parent.model_copy( - update={ - **payload.model_dump(exclude={"id", "metadata"}), - "metadata": {"createdOn": parent.metadata.get("createdOn", now), "updatedOn": now} - if parent.metadata else None, - } + current = _parents[parent_id] + updated = Parent( + id=parent_id, + name=payload.name, + email=payload.email, + phone=payload.phone if payload.phone is not None else current.phone, + metadata={"createdOn": current.metadata["createdOn"] if current.metadata else None, "updatedOn": now}, ) _parents[parent_id] = updated return updated @@ -101,7 +103,10 @@ def create_vet(payload: VetCreate) -> Vet: now = _now() vet = Vet( id=_vets_next_id, - **payload.model_dump(exclude={"id", "metadata"}), + name=payload.name, + specialty=payload.specialty, + email=payload.email, + phone=payload.phone, metadata={"createdOn": now, "updatedOn": now} if payload.metadata else None, ) _vets[_vets_next_id] = vet @@ -113,13 +118,14 @@ def update_vet(vet_id: int, payload: VetCreate) -> Vet: if vet_id not in _vets: raise KeyError(vet_id) now = _now() - vet = _vets[vet_id] - updated = vet.model_copy( - update={ - **payload.model_dump(exclude={"id", "metadata"}), - "metadata": {"createdOn": vet.metadata.get("createdOn", now), "updatedOn": now} - if vet.metadata else None, - } + current = _vets[vet_id] + updated = Vet( + id=vet_id, + name=payload.name, + specialty=payload.specialty if payload.specialty is not None else current.specialty, + email=payload.email, + phone=payload.phone if payload.phone is not None else current.phone, + metadata={"createdOn": current.metadata["createdOn"] if current.metadata else None, "updatedOn": now}, ) _vets[vet_id] = updated return updated @@ -150,7 +156,8 @@ def create_treatment(payload: TreatmentCreate) -> Treatment: now = _now() treatment = Treatment( id=_treatments_next_id, - **payload.model_dump(exclude={"id", "metadata"}), + label=payload.label, + description=payload.description, metadata={"createdOn": now, "updatedOn": now} if payload.metadata else None, ) _treatments[_treatments_next_id] = treatment @@ -162,13 +169,12 @@ def update_treatment(treatment_id: int, payload: TreatmentCreate) -> Treatment: if treatment_id not in _treatments: raise KeyError(treatment_id) now = _now() - treatment = _treatments[treatment_id] - updated = treatment.model_copy( - update={ - **payload.model_dump(exclude={"id", "metadata"}), - "metadata": {"createdOn": treatment.metadata.get("createdOn", now), "updatedOn": now} - if treatment.metadata else None, - } + current = _treatments[treatment_id] + updated = Treatment( + id=treatment_id, + label=payload.label, + description=payload.description if payload.description is not None else current.description, + metadata={"createdOn": current.metadata["createdOn"] if current.metadata else None, "updatedOn": now}, ) _treatments[treatment_id] = updated return updated @@ -197,9 +203,16 @@ def get_pet(pet_id: int) -> Pet: def create_pet(payload: PetCreate) -> Pet: global _pets_next_id now = _now() + parents = [_parents[pid] for pid in payload.parent_ids] pet = Pet( id=_pets_next_id, - **payload.model_dump(exclude={"id", "metadata"}), + name=payload.name, + species=payload.species, + age=payload.age, + weight=payload.weight, + birthDate=payload.birthDate, + photo=payload.photo, + parents=parents, metadata={"createdOn": now, "updatedOn": now} if payload.metadata else None, ) _pets[_pets_next_id] = pet @@ -211,13 +224,18 @@ def update_pet(pet_id: int, payload: PetCreate) -> Pet: if pet_id not in _pets: raise KeyError(pet_id) now = _now() - pet = _pets[pet_id] - updated = pet.model_copy( - update={ - **payload.model_dump(exclude={"id", "metadata"}), - "metadata": {"createdOn": pet.metadata.get("createdOn", now), "updatedOn": now} - if pet.metadata else None, - } + parents = [_parents[pid] for pid in payload.parent_ids] + current = _pets[pet_id] + updated = Pet( + id=pet_id, + name=payload.name, + species=payload.species, + age=payload.age if payload.age is not None else current.age, + weight=payload.weight if payload.weight is not None else current.weight, + birthDate=payload.birthDate if payload.birthDate is not None else current.birthDate, + photo=payload.photo if payload.photo is not None else current.photo, + parents=parents, + metadata={"createdOn": current.metadata["createdOn"] if current.metadata else None, "updatedOn": now}, ) _pets[pet_id] = updated return updated @@ -248,7 +266,11 @@ def create_appointment(payload: AppointmentCreate) -> Appointment: now = _now() appointment = Appointment( id=_appointments_next_id, - **payload.model_dump(exclude={"id", "metadata"}), + date=payload.date, + notes=payload.notes, + pet=_pets[payload.pet_id], + vet=_vets[payload.vet_id], + treatment=_treatments[payload.treatment_id], metadata={"createdOn": now, "updatedOn": now} if payload.metadata else None, ) _appointments[_appointments_next_id] = appointment @@ -260,13 +282,15 @@ def update_appointment(appointment_id: int, payload: AppointmentCreate) -> Appoi if appointment_id not in _appointments: raise KeyError(appointment_id) now = _now() - appointment = _appointments[appointment_id] - updated = appointment.model_copy( - update={ - **payload.model_dump(exclude={"id", "metadata"}), - "metadata": {"createdOn": appointment.metadata.get("createdOn", now), "updatedOn": now} - if appointment.metadata else None, - } + current = _appointments[appointment_id] + updated = Appointment( + id=appointment_id, + date=payload.date, + notes=payload.notes if payload.notes is not None else current.notes, + pet=_pets.get(payload.pet_id, current.pet), + vet=_vets.get(payload.vet_id, current.vet), + treatment=_treatments.get(payload.treatment_id, current.treatment), + metadata={"createdOn": current.metadata["createdOn"] if current.metadata else None, "updatedOn": now}, ) _appointments[appointment_id] = updated return updated @@ -304,17 +328,17 @@ def _seed_data(): _treatments[5] = Treatment(id=5, label="Blood Panel", description="Complete blood count and chemistry", metadata=meta) _treatments_next_id = 6 - _pets[1] = Pet(id=1, name="Max", species="dog", age=4, weight=25.5, birthDate=date(2022, 3, 15), parent_ids=[1], metadata=meta) - _pets[2] = Pet(id=2, name="Luna", species="cat", age=2, weight=4.2, birthDate=date(2024, 1, 10), parent_ids=[1, 2], metadata=meta) - _pets[3] = Pet(id=3, name="Charlie", species="dog", age=7, weight=18.0, birthDate=date(2019, 8, 22), parent_ids=[2], metadata=meta) - _pets[4] = Pet(id=4, name="Bella", species="bird", age=1, weight=0.3, birthDate=date(2025, 5, 1), parent_ids=[3], metadata=meta) - _pets[5] = Pet(id=5, name="Rocky", species="dog", age=3, weight=30.0, birthDate=date(2023, 11, 5), parent_ids=[4], metadata=meta) + _pets[1] = Pet(id=1, name="Max", species="dog", age=4, weight=25.5, birthDate=date(2022, 3, 15), parents=[_parents[1]], metadata=meta) + _pets[2] = Pet(id=2, name="Luna", species="cat", age=2, weight=4.2, birthDate=date(2024, 1, 10), parents=[_parents[1], _parents[2]], metadata=meta) + _pets[3] = Pet(id=3, name="Charlie", species="dog", age=7, weight=18.0, birthDate=date(2019, 8, 22), parents=[_parents[2]], metadata=meta) + _pets[4] = Pet(id=4, name="Bella", species="bird", age=1, weight=0.3, birthDate=date(2025, 5, 1), parents=[_parents[3]], metadata=meta) + _pets[5] = Pet(id=5, name="Rocky", species="dog", age=3, weight=30.0, birthDate=date(2023, 11, 5), parents=[_parents[4]], metadata=meta) _pets_next_id = 6 - _appointments[1] = Appointment(id=1, date=datetime(2026, 6, 18, 9, 0, tzinfo=timezone.utc), notes="Annual checkup", pet_id=1, vet_id=1, treatment_id=1, metadata=meta) - _appointments[2] = Appointment(id=2, date=datetime(2026, 6, 18, 10, 30, tzinfo=timezone.utc), notes="Dental cleaning", pet_id=2, vet_id=2, treatment_id=3, metadata=meta) - _appointments[3] = Appointment(id=3, date=datetime(2026, 6, 19, 11, 0, tzinfo=timezone.utc), notes="Vaccination booster", pet_id=3, vet_id=3, treatment_id=2, metadata=meta) - _appointments[4] = Appointment(id=4, date=datetime(2026, 6, 20, 14, 0, tzinfo=timezone.utc), notes="Follow-up after surgery", pet_id=5, vet_id=1, treatment_id=4, metadata=meta) + _appointments[1] = Appointment(id=1, date=datetime(2026, 6, 18, 9, 0, tzinfo=timezone.utc), notes="Annual checkup", pet=_pets[1], vet=_vets[1], treatment=_treatments[1], metadata=meta) + _appointments[2] = Appointment(id=2, date=datetime(2026, 6, 18, 10, 30, tzinfo=timezone.utc), notes="Dental cleaning", pet=_pets[2], vet=_vets[2], treatment=_treatments[3], metadata=meta) + _appointments[3] = Appointment(id=3, date=datetime(2026, 6, 19, 11, 0, tzinfo=timezone.utc), notes="Vaccination booster", pet=_pets[3], vet=_vets[3], treatment=_treatments[2], metadata=meta) + _appointments[4] = Appointment(id=4, date=datetime(2026, 6, 20, 14, 0, tzinfo=timezone.utc), notes="Follow-up after surgery", pet=_pets[5], vet=_vets[1], treatment=_treatments[4], metadata=meta) _appointments_next_id = 5 diff --git a/openapi_first/templates/vet_app/models.py b/openapi_first/templates/vet_app/models.py index 96acb8d..bcaeb25 100644 --- a/openapi_first/templates/vet_app/models.py +++ b/openapi_first/templates/vet_app/models.py @@ -1,19 +1,3 @@ -""" -Pydantic domain models for the Veterinary Clinic example. - -This module defines Pydantic models representing the five domain -entities used by the veterinary clinic service. These models are -referenced by the OpenAPI specification for request and response -schemas. - -The models are declarative and framework-agnostic. They contain no -persistence logic, validation beyond type constraints, or business -behavior. - -This module is not part of the ``openapi_first`` library API surface. -It exists solely to support the example application template. -""" - from datetime import date, datetime from pydantic import BaseModel @@ -75,30 +59,32 @@ class PetBase(BaseModel): weight: float | None = None birthDate: date | None = None photo: str | None = None - parent_ids: list[int] = [] metadata: Metadata | None = None class PetCreate(PetBase): - pass + parent_ids: list[int] = [] class Pet(PetBase): id: int + parents: list[Parent] = [] class AppointmentBase(BaseModel): date: datetime notes: str | None = None - pet_id: int - vet_id: int - treatment_id: int metadata: Metadata | None = None class AppointmentCreate(AppointmentBase): - pass + pet_id: int + vet_id: int + treatment_id: int class Appointment(AppointmentBase): id: int + pet: Pet + vet: Vet + treatment: Treatment diff --git a/openapi_first/templates/vet_app/routes.py b/openapi_first/templates/vet_app/routes.py index 4772da9..2f75307 100644 --- a/openapi_first/templates/vet_app/routes.py +++ b/openapi_first/templates/vet_app/routes.py @@ -326,9 +326,9 @@ def list_appointments(limit: int = 20, offset: int = 0, date: str = None, vet: i 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] + 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] + items = [a for a in items if a.pet.id == pet] return {"total": len(items), "items": items[offset:offset + limit]} diff --git a/openapi_first/templates/vet_app/test_vet_app.py b/openapi_first/templates/vet_app/test_vet_app.py index d6bdbf8..33474ec 100644 --- a/openapi_first/templates/vet_app/test_vet_app.py +++ b/openapi_first/templates/vet_app/test_vet_app.py @@ -140,7 +140,7 @@ def test_full_appointment_lifecycle(): response = client.create_appointment(body=payload) assert response.status_code == 201 appointment = response.json() - assert appointment["pet_id"] == pet["id"] + assert appointment["pet"]["id"] == pet["id"] # Fetch it back get_resp = client.get_appointment(path_params={"id": appointment["id"]})