| """SQLAlchemy models. Owned by Person C. |
| |
| Tables chosen for the Week 1 + Week 2 MVP. Wiki content lives on disk, |
| ChromaDB lives outside Postgres — this layer stores transactional state |
| that needs SQL semantics (joins, audit, time-window queries). |
| """ |
|
|
| from __future__ import annotations |
|
|
| from datetime import datetime |
| from enum import Enum |
|
|
| from sqlalchemy import ( |
| JSON, |
| Boolean, |
| DateTime, |
| Enum as SAEnum, |
| ForeignKey, |
| Integer, |
| String, |
| Text, |
| ) |
| from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship |
|
|
|
|
| class Base(DeclarativeBase): |
| pass |
|
|
|
|
| |
| |
| |
|
|
|
|
| class Patient(Base): |
| __tablename__ = "patients" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| name: Mapped[str] = mapped_column(String(120)) |
| phone: Mapped[str | None] = mapped_column(String(20)) |
| dob: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) |
| abha_id: Mapped[str | None] = mapped_column(String(20), index=True) |
| wiki_page_path: Mapped[str | None] = mapped_column(String(255)) |
| created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow) |
|
|
| appointments: Mapped[list[Appointment]] = relationship(back_populates="patient") |
| messages: Mapped[list[Message]] = relationship(back_populates="patient") |
|
|
|
|
| class Staff(Base): |
| __tablename__ = "staff" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| name: Mapped[str] = mapped_column(String(120)) |
| role: Mapped[str] = mapped_column(String(20)) |
| certifications: Mapped[list[str]] = mapped_column(JSON, default=list) |
| max_hours_per_week: Mapped[int] = mapped_column(Integer, default=48) |
| active: Mapped[bool] = mapped_column(Boolean, default=True) |
|
|
| shifts: Mapped[list[Shift]] = relationship(back_populates="staff") |
|
|
|
|
| |
| |
| |
|
|
|
|
| class BookingStatusEnum(str, Enum): |
| PROPOSED = "proposed" |
| CONFIRMED = "confirmed" |
| CANCELLED = "cancelled" |
| NO_SHOW = "no_show" |
| COMPLETED = "completed" |
| WAITLISTED = "waitlisted" |
|
|
|
|
| class Appointment(Base): |
| __tablename__ = "appointments" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| patient_id: Mapped[str] = mapped_column(ForeignKey("patients.id"), index=True) |
| doctor_id: Mapped[str] = mapped_column(String(32), index=True) |
| slot_iso: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True) |
| duration_minutes: Mapped[int] = mapped_column(Integer, default=15) |
| status: Mapped[BookingStatusEnum] = mapped_column( |
| SAEnum( |
| BookingStatusEnum, |
| name="booking_status", |
| values_callable=lambda enum_cls: [e.value for e in enum_cls],), |
| default=BookingStatusEnum.PROPOSED,) |
|
|
| calendar_event_id: Mapped[str | None] = mapped_column(String(120)) |
| notes: Mapped[str | None] = mapped_column(Text) |
| created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow) |
|
|
| patient: Mapped[Patient] = relationship(back_populates="appointments") |
|
|
|
|
| class Message(Base): |
| __tablename__ = "messages" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| patient_id: Mapped[str | None] = mapped_column(ForeignKey("patients.id"), index=True) |
| appointment_id: Mapped[str | None] = mapped_column(ForeignKey("appointments.id")) |
| channel: Mapped[str] = mapped_column(String(20)) |
| direction: Mapped[str] = mapped_column(String(8)) |
| body: Mapped[str] = mapped_column(Text) |
| approved_by: Mapped[str | None] = mapped_column(String(32)) |
| sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) |
| created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow) |
|
|
| patient: Mapped[Patient | None] = relationship(back_populates="messages") |
|
|
|
|
| class Shift(Base): |
| __tablename__ = "shifts" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| staff_id: Mapped[str] = mapped_column(ForeignKey("staff.id"), index=True) |
| start: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True) |
| end: Mapped[datetime] = mapped_column(DateTime(timezone=True)) |
| role: Mapped[str] = mapped_column(String(20)) |
| is_replacement: Mapped[bool] = mapped_column(Boolean, default=False) |
|
|
| staff: Mapped[Staff] = relationship(back_populates="shifts") |
|
|
|
|
| class Discharge(Base): |
| __tablename__ = "discharges" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| patient_id: Mapped[str] = mapped_column(ForeignKey("patients.id"), index=True) |
| initiated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow) |
| target_complete_by: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) |
| completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) |
| subtasks: Mapped[dict] = mapped_column(JSON, default=dict) |
|
|
|
|
| class SoapDraft(Base): |
| __tablename__ = "soap_drafts" |
|
|
| id: Mapped[str] = mapped_column(String(32), primary_key=True) |
| patient_id: Mapped[str] = mapped_column(ForeignKey("patients.id"), index=True) |
| encounter_ts: Mapped[datetime] = mapped_column(DateTime(timezone=True)) |
| payload: Mapped[dict] = mapped_column(JSON) |
| approved_by: Mapped[str | None] = mapped_column(String(32)) |
| approved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) |
|
|
|
|
| class AuditLog(Base): |
| __tablename__ = "audit_log" |
|
|
| id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) |
| ts: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow, index=True) |
| actor: Mapped[str] = mapped_column(String(64)) |
| action: Mapped[str] = mapped_column(String(64), index=True) |
| target_type: Mapped[str | None] = mapped_column(String(32)) |
| target_id: Mapped[str | None] = mapped_column(String(64)) |
| detail: Mapped[dict] = mapped_column(JSON, default=dict) |
|
|