Extract duplicate AppointmentResponse construction to helper

- Created _to_appointment_response() helper function
- Replaced 5 duplicate AppointmentResponse constructions with helper calls
- Helper handles both explicit user_email and eager-loaded user relationship cases
This commit is contained in:
counterweight 2025-12-21 17:49:37 +01:00
parent 6ff3c0a133
commit d24acfd322
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C

View file

@ -22,6 +22,30 @@ from shared_constants import SLOT_DURATION_MINUTES, MIN_ADVANCE_DAYS, MAX_ADVANC
router = APIRouter(prefix="/api/booking", tags=["booking"])
def _to_appointment_response(
appointment: Appointment,
user_email: str | None = None,
) -> AppointmentResponse:
"""Convert an Appointment model to AppointmentResponse schema.
Args:
appointment: The appointment model instance
user_email: Optional user email. If not provided, uses appointment.user.email
"""
email = user_email if user_email is not None else appointment.user.email
return AppointmentResponse(
id=appointment.id,
user_id=appointment.user_id,
user_email=email,
slot_start=appointment.slot_start,
slot_end=appointment.slot_end,
note=appointment.note,
status=appointment.status.value,
created_at=appointment.created_at,
cancelled_at=appointment.cancelled_at,
)
def _get_bookable_date_range() -> tuple[date, date]:
"""Get the valid date range for booking (tomorrow to +30 days)."""
today = date.today()
@ -180,17 +204,7 @@ async def create_booking(
detail="This slot has already been booked. Please select another slot.",
)
return AppointmentResponse(
id=appointment.id,
user_id=appointment.user_id,
user_email=current_user.email,
slot_start=appointment.slot_start,
slot_end=appointment.slot_end,
note=appointment.note,
status=appointment.status.value,
created_at=appointment.created_at,
cancelled_at=appointment.cancelled_at,
)
return _to_appointment_response(appointment, current_user.email)
# =============================================================================
@ -214,17 +228,7 @@ async def get_my_appointments(
appointments = result.scalars().all()
return [
AppointmentResponse(
id=apt.id,
user_id=apt.user_id,
user_email=current_user.email,
slot_start=apt.slot_start,
slot_end=apt.slot_end,
note=apt.note,
status=apt.status.value,
created_at=apt.created_at,
cancelled_at=apt.cancelled_at,
)
_to_appointment_response(apt, current_user.email)
for apt in appointments
]
@ -270,17 +274,7 @@ async def cancel_my_appointment(
await db.commit()
await db.refresh(appointment)
return AppointmentResponse(
id=appointment.id,
user_id=appointment.user_id,
user_email=current_user.email,
slot_start=appointment.slot_start,
slot_end=appointment.slot_end,
note=appointment.note,
status=appointment.status.value,
created_at=appointment.created_at,
cancelled_at=appointment.cancelled_at,
)
return _to_appointment_response(appointment, current_user.email)
# =============================================================================
@ -315,17 +309,7 @@ async def get_all_appointments(
# Build responses using the eager-loaded user relationship
records = [
AppointmentResponse(
id=apt.id,
user_id=apt.user_id,
user_email=apt.user.email, # Uses eager-loaded relationship
slot_start=apt.slot_start,
slot_end=apt.slot_end,
note=apt.note,
status=apt.status.value,
created_at=apt.created_at,
cancelled_at=apt.cancelled_at,
)
_to_appointment_response(apt) # Uses eager-loaded relationship
for apt in appointments
]
@ -375,14 +359,4 @@ async def admin_cancel_appointment(
await db.commit()
await db.refresh(appointment)
return AppointmentResponse(
id=appointment.id,
user_id=appointment.user_id,
user_email=appointment.user.email, # Uses eager-loaded relationship
slot_start=appointment.slot_start,
slot_end=appointment.slot_end,
note=appointment.note,
status=appointment.status.value,
created_at=appointment.created_at,
cancelled_at=appointment.cancelled_at,
)
return _to_appointment_response(appointment) # Uses eager-loaded relationship