Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class Settings(BaseSettings):
PrescriptionCreate,
PrescriptionUpdate,
PrescriptionResponse,
DoseStatus,
MedicationDose,
MedicationDoseCreate,
MedicationDoseResponse,
)
from .risk_calculator import calculate_health_risk
from .health_summary import build_health_summary
Expand Down Expand Up @@ -183,6 +187,7 @@ async def lifespan(app: FastAPI):
Questionnaire,
HealthRiskAssessment,
Prescription,
MedicationDose,
],
)
print("Database connection established.")
Expand Down Expand Up @@ -811,3 +816,90 @@ async def archive_prescription(
return SuccessResponse(
data=PrescriptionResponse.model_validate(prescription)
)



@app.post(
"/api/v1/prescriptions/{prescription_id}/doses",
response_model=SuccessResponse[MedicationDoseResponse],
)
async def record_medication_dose(
prescription_id: uuid.UUID,
dose_data: MedicationDoseCreate,
current_user: Annotated[User, Depends(require_health_consent)],
):
prescription = await Prescription.find_one(
Prescription.id == prescription_id,
Prescription.user_id == current_user.id,
Prescription.archived == False,
)

if not prescription:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Prescription not found",
)

existing_dose = await MedicationDose.find_one(
MedicationDose.user_id == current_user.id,
MedicationDose.prescription_id == prescription.id,
MedicationDose.scheduled_date == dose_data.scheduled_date,
)

if existing_dose:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="A dose record already exists for this scheduled date",
)
dose = MedicationDose(
user_id=current_user.id,
prescription_id=prescription.id,
scheduled_date=dose_data.scheduled_date,
status=dose_data.status,
)

try:
await dose.insert()
except DuplicateKeyError:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="A dose record already exists for this scheduled date",
)

return SuccessResponse(
data=MedicationDoseResponse.model_validate(dose)
)


@app.get(
"/api/v1/prescriptions/{prescription_id}/doses",
response_model=SuccessResponse[list[MedicationDoseResponse]],
)
async def get_medication_doses(
prescription_id: uuid.UUID,
current_user: Annotated[User, Depends(require_health_consent)],
):
prescription = await Prescription.find_one(
Prescription.id == prescription_id,
Prescription.user_id == current_user.id,
Prescription.archived == False,
)

if not prescription:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Prescription not found",
)

doses = await MedicationDose.find(
MedicationDose.user_id == current_user.id,
MedicationDose.prescription_id == prescription.id,
).sort(-MedicationDose.recorded_at).to_list()

return SuccessResponse(
data=[
MedicationDoseResponse.model_validate(dose)
for dose in doses
]
)

47 changes: 47 additions & 0 deletions backend/src/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from enum import Enum
from datetime import date, datetime, timezone
from typing import Optional, Annotated, Generic, TypeVar
from pymongo import IndexModel

# --- Consent (must match frontend CURRENT_CONSENT_VERSION) ---
CURRENT_CONSENT_VERSION = "v1.0"
Expand Down Expand Up @@ -451,3 +452,49 @@ class PrescriptionResponse(BaseModel):

class Config:
from_attributes = True



class DoseStatus(str, Enum):
taken = "taken"
skipped = "skipped"


class MedicationDose(Document):
id: uuid.UUID = Field(default_factory=uuid.uuid4)
user_id: uuid.UUID
prescription_id: uuid.UUID
scheduled_date: date
status: DoseStatus
recorded_at: datetime = Field(
default_factory=lambda: datetime.now(timezone.utc)
)

class Settings:
name = "medication_doses"
indexes = [
IndexModel(
[
("user_id", 1),
("prescription_id", 1),
("scheduled_date", 1),
],
unique=True,
)
]


class MedicationDoseCreate(BaseModel):
scheduled_date: date
status: DoseStatus


class MedicationDoseResponse(BaseModel):
id: uuid.UUID
prescription_id: uuid.UUID
scheduled_date: date
status: DoseStatus
recorded_at: datetime

class Config:
from_attributes = True
Loading
Loading