ranjithkumar111's picture
Upload 10 files
1bfbac9 verified
Raw
History Blame Contribute Delete
4.32 kB
# File: src/booking_api.py
# Purpose: Mock hospital booking API (FastAPI) that processes and confirms appointments
import json
import uuid
import random
from datetime import datetime
from pathlib import Path
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from config import AVAILABLE_SLOTS, DEPARTMENTS, BOOKING_LOG_PATH
app = FastAPI(title="Hospital Booking API", version="1.0")
# In-memory store simulating a database
_booked_slots: dict[str, list[str]] = {} # { "department::date": [slot1, slot2] }
class BookingRequest(BaseModel):
patient_name: str
department: str
date: str # YYYY-MM-DD
slot: str
class BookingResponse(BaseModel):
success: bool
appointment_id: str | None = None
reason: str | None = None
patient_name: str | None = None
department: str | None = None
date: str | None = None
slot: str | None = None
doctor: str | None = None
def _log_booking(record: dict):
"""Append booking attempt to JSONL log for audit trail."""
BOOKING_LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(BOOKING_LOG_PATH, "a") as f:
f.write(json.dumps(record) + "\n")
@app.post("/book", response_model=BookingResponse)
def book_appointment(req: BookingRequest) -> BookingResponse:
"""
Book an appointment. Returns a confirmed appointment_id on success
or an error reason on failure.
This endpoint simulates real constraints:
- Slot must be valid.
- Department must be valid.
- Slots can be taken (random simulation for demo).
"""
# Validate department
if req.department not in DEPARTMENTS:
result = BookingResponse(success=False, reason=f"Unknown department: {req.department}")
_log_booking({"timestamp": datetime.now().isoformat(), **result.dict()})
return result
# Validate slot
if req.slot not in AVAILABLE_SLOTS:
result = BookingResponse(success=False, reason=f"Invalid time slot: {req.slot}")
_log_booking({"timestamp": datetime.now().isoformat(), **result.dict()})
return result
# Check slot availability (80% success rate in simulation)
key = f"{req.department}::{req.date}"
taken_slots = _booked_slots.get(key, [])
if req.slot in taken_slots or random.random() < 0.20:
result = BookingResponse(
success=False,
reason="Slot unavailable. Please choose another time."
)
_log_booking({
"timestamp": datetime.now().isoformat(),
"patient": req.patient_name,
**result.dict()
})
return result
# Confirm booking
appointment_id = f"APT-{uuid.uuid4().hex[:6].upper()}"
doctor = f"Dr. Sample_{random.randint(1, 5)}"
# Persist to in-memory store
_booked_slots.setdefault(key, []).append(req.slot)
result = BookingResponse(
success=True,
appointment_id=appointment_id,
patient_name=req.patient_name,
department=req.department,
date=req.date,
slot=req.slot,
doctor=doctor,
)
_log_booking({
"timestamp": datetime.now().isoformat(),
**result.dict()
})
print(f"[BookingAPI] Confirmed: {appointment_id} for {req.patient_name}")
return result
@app.get("/slots")
def get_available_slots(department: str, date: str) -> dict:
"""Return remaining open slots for a department on a given date."""
key = f"{department}::{date}"
taken = _booked_slots.get(key, [])
open_slots = [s for s in AVAILABLE_SLOTS if s not in taken]
return {"department": department, "date": date, "available_slots": open_slots}
@app.get("/health")
def health():
return {"status": "ok"}
if __name__ == "__main__":
import importlib
try:
uvicorn = importlib.import_module("uvicorn")
except ImportError as exc:
raise RuntimeError(
"uvicorn is required to run the booking API directly. "
"Install it with 'pip install uvicorn'."
) from exc
uvicorn.run("src.booking_api:app", host="0.0.0.0", port=8000, reload=True)