scheduler / api /appointments.py
umangchaudhry's picture
Upload 31 files
0d04b76 verified
Raw
History Blame Contribute Delete
4.98 kB
"""Appointment routes — list, cancel, and complete."""
from datetime import datetime
from zoneinfo import ZoneInfo
from fastapi import APIRouter, Depends, HTTPException
from core import data_manager, google_services
from core import settings_manager as settings
from api.deps import get_current_session
router = APIRouter(prefix="/api/appointments", tags=["appointments"])
@router.get("")
def list_appointments(status: str | None = None, provider_id: str | None = None, session: dict = Depends(get_current_session)):
appts = data_manager.get_all_appointments(status=status)
if session.get("user_role") == "provider":
# Providers see only their own appointments
appts = [a for a in appts if a.get("provider_id") == session.get("provider_id")]
elif provider_id:
appts = [a for a in appts if a.get("provider_id") == provider_id]
return appts
@router.delete("/{appointment_id}")
def cancel_appointment(appointment_id: str, session: dict = Depends(get_current_session)):
if session.get("user_role") == "provider":
raise HTTPException(status_code=403, detail="Providers cannot cancel appointments")
appt = next((a for a in data_manager.get_all_appointments() if a.get("id") == appointment_id), None)
if not appt:
raise HTTPException(status_code=404, detail="Appointment not found")
cancelled = data_manager.cancel_appointment(appointment_id)
if not cancelled:
raise HTTPException(status_code=500, detail="Cancellation failed")
# Delete Google Calendar event (non-critical)
if appt.get("google_event_id"):
try:
google_services.delete_calendar_event(appt["google_event_id"])
except Exception:
pass
# Send cancellation notifications (non-critical)
try:
provider = data_manager.get_provider_by_id(appt["provider_id"])
patient = data_manager.get_patient_by_id(appt["patient_id"])
appointment_details = {
"patient_name": appt.get("patient_name", ""),
"provider_name": appt.get("provider_name", ""),
"date": appt.get("date", ""),
"start_time": appt.get("start_time", ""),
"end_time": appt.get("end_time", ""),
"address": appt.get("visit_address", ""),
}
google_services.send_cancellation_notification(
provider_email=provider.get("email", "") if provider else "",
patient_email=patient.get("email", "") if patient else "",
appointment_details=appointment_details,
)
except Exception:
pass
data_manager.log_action(
user=session["current_user"],
action="CANCEL",
details=(
f"Cancelled {appt.get('visit_level', '')} visit for "
f"{appt.get('patient_name', '')} with {appt.get('provider_name', '')} "
f"on {appt.get('date', '')} at {appt.get('start_time', '')}"
),
appointment_id=appointment_id,
)
return {"ok": True}
@router.post("/{appointment_id}/complete")
def complete_appointment(appointment_id: str, session: dict = Depends(get_current_session)):
"""Mark an appointment as completed. Provider-only; only allowed once the
appointment's start time has passed (compared in the platform timezone)."""
appt = next((a for a in data_manager.get_all_appointments() if a.get("id") == appointment_id), None)
if not appt:
raise HTTPException(status_code=404, detail="Appointment not found")
if session.get("user_role") == "provider" and session.get("provider_id") != appt.get("provider_id"):
raise HTTPException(status_code=403, detail="Can only complete your own appointments")
if appt.get("status") != "scheduled":
raise HTTPException(status_code=400, detail=f"Cannot complete appointment with status '{appt.get('status')}'")
# Compare in the platform timezone so a UTC server doesn't mis-evaluate.
try:
start_dt = datetime.strptime(f"{appt['date']} {appt['start_time']}", "%Y-%m-%d %H:%M")
except (KeyError, ValueError):
raise HTTPException(status_code=500, detail="Appointment has invalid date/time")
now_local = datetime.now(ZoneInfo(settings.get_timezone())).replace(tzinfo=None)
if now_local < start_dt:
raise HTTPException(status_code=400, detail="Appointment has not started yet")
completed = data_manager.complete_appointment(appointment_id)
if not completed:
raise HTTPException(status_code=500, detail="Failed to mark complete")
data_manager.log_action(
user=session["current_user"],
action="COMPLETE",
details=(
f"Completed {appt.get('visit_level', '')} visit for "
f"{appt.get('patient_name', '')} with {appt.get('provider_name', '')} "
f"on {appt.get('date', '')} at {appt.get('start_time', '')}"
),
appointment_id=appointment_id,
)
return {"ok": True, "appointment": completed}