Skip to content

13 Medicines and Administration Model

Anubha Parashar edited this page Oct 10, 2026 · 1 revision

HealthBook AI (internal code name: BabyCare) stores three different facts about a medicine in three different places: what a clinician wrote, what the app was told to remind about, and what a person actually gave. The data model, API and UI are built so that one can never stand in for another. The app records medication history. It never prescribes, suggests doses or changes treatment.

Prescribed ≠ Scheduled ≠ Given

Concept Stored as Created by What it proves What it does not prove
Prescribed Medicine course (medicines) caregiver entry or a confirmed document review what was written, by whom, on which page that any dose was given
Scheduled schedule_times + start_date/end_date on a verified course, expanded into reminders and daily due slots caregiver verification against the prescription that a reminder or due slot exists that the dose was given; a delivered notification is not a dose
Given (home) MedicationEvent (given/skipped/stopped) or Daily Health MedicationAdministration an explicit caregiver action that the caregiver reported this outcome at this time clinical correctness
Given (hospital) TreatmentAdministration linked to a hospital visit caregiver copying a hospital administration record, with the document attached that the hospital record documents administration n/a

Related rules: appointment/attendance ≠ treatment administered, and a notification/reminder is never proof a dose was given.

Diagram: Three separate medicine concepts and their links

flowchart LR
    subgraph P["Prescribed"]
        DOC["Source document and page"] --> MED["Medicine course, draft"]
        MED -->|"guardian verifies against prescription"| MV["Medicine course, verified"]
        MV -->|"any revision"| MED
    end
    subgraph S["Scheduled"]
        MV -->|"schedule times plus start and end"| REM["Reminders and due slots"]
        REM --> NTF["Notification delivered"]
    end
    subgraph G["Given"]
        EV["MedicationEvent given or skipped or stopped"]
        DC["Daily Health administration record"]
        TA["Hospital TreatmentAdministration"]
    end
    MV -->|"caregiver logs outcome"| EV
    REM -.->|"slot matched, never auto-filled"| DC
    VIS["Hospital visit attended"] -->|"plus attached chart"| TA
    NTF -.->|"is not proof of a dose"| EV
Loading

Medicine courses

POST /api/patients/{pid}/medicines always creates a course with status='draft'. Fields separate the written plan from actual dates:

Group Fields
Written name, generic_name, brand_name, dosage, dose_unit, route, frequency, instructions, prescribing_doctor, indication, prescription_date
Planned start_date, end_date, schedule_times (up to 8 unique HH:MM times, sorted)
Actual actual_start_date, actual_stop_date (never in the future)
Provenance source_document_id, source_page (checked by safe_doc)
course_type prescription, discharge_prescription, inpatient_treatment, historical_prescription

Verification (POST /api/medicines/{id}/confirm) requires checked_against_prescription: true, non-empty instructions, a linked source document, and exact start and end dates if reminder times are set. It sets status='verified' and verified_at.

Revision (PATCH /api/medicines/{id}) re-validates the merged course, stores a MedicineRevision snapshot, increments version, and resets the course to draft with verified_at=null. A revised course stops generating reminders until it is re-verified. Reminder keys include the version (med-{id}-v{version}-...), so old reminders cannot survive a change. Changing the profile's timezone also returns every verified course to draft.

Stop (POST /api/medicines/{id}/stop) records the actual stop date and a reason, writes a stopped event, and sets status='discontinued'.

Medication events

POST /api/medicines/{id}/events:

  • refuses with 409 if the course is not verified ("Cannot record administration against an unverified or revised draft");
  • is idempotent: (medicine_id, idempotency_key) is unique, and a repeated key returns the original event;
  • accepts only naive local clock times in the profile timezone and rejects times more than 5 minutes in the future;
  • with source='copied_from_hospital_document', requires the hospital record to be attached;
  • stores medicine_version so the event is tied to the exact prescription version.

Hospital administration vs attendance

PATCH /api/hospital-visits/{id} changes only the appointment status (planned, attended, postponed, cancelled). In code, attended means visited the facility, not that any medication was infused. A documented dose is a separate TreatmentAdministration (POST /api/hospital-visits/{vid}/administrations). It requires the source document, checked_administration_record: true, a past time after birth, and a globally unique idempotency key.

Daily Health medicine records

The Daily Health journal (api/care.py, api/care_api.py) keeps MedicationAdministration rows with status given, skipped, missed, later or unknown. A row may reference a course or carry only a typed name.

  • Due slots are computed only from verified courses that have schedule_times, within the planned dates and before any actual stop.
  • medicine_day() matches a record to a slot by scheduled_for (within 60 s) or by time (within 3 h). A slot with no record shows no_record, never "given".
  • The schedule card's GIVEN / SKIP / LATER buttons (and MISSED via the API) call POST /api/patients/{pid}/care/medicine/quick. The API docstring states it is "never called automatically". client_key makes repeated taps idempotent.
  • Counts and adherence tables report "a given record exists for N of M scheduled slots". These are observational statements, not adherence judgements.

Medication history view

GET /api/patients/{pid}/medication-history lists every course with its events, a display_period (prescribed, planned and actual dates) and missing-information flags:

Flag Condition
prescription date not documented no prescription or start date
actual start date unknown / actual end date unknown actual dates absent (end ignored if discontinued)
no administration events recorded no given event (non-inpatient courses)
inpatient dates and times not in supplied records course_type='inpatient_treatment'
prescription not yet verified status='draft'
no source document attached no source_document_id

antibiotic_list_match is true when the name text contains a word from a fixed list. The response states this "is not a clinical classification". Courses are grouped by normalised generic name. Antibiotic exposure metrics are a separate model with their own administration_status, described in Illness History and Lifetime Health.

Clinical safety rules

  • Prescribed ≠ Scheduled ≠ Given/Administered.
  • Unverified or revised drafts generate no reminders and accept no administration events.
  • No dose, frequency or schedule is derived from handwriting (dose_and_frequency: not_parsed).
  • AI never prescribes or changes doses.

Implementation

api/main.py (MedicineIn, add/confirm/revise/events, visits), api/workflows.py (stop, administrations, timezone reconciliation), api/reminders.py, api/care.py (due_slots, medicine_day), api/care_api.py (quick_dose), api/care_models.py, api/history_api.py (medication_history, missing_info), web/care.js.

Related pages

Daily Health and Care Calendar · Notifications and Reminder Architecture · OCR and Human Verification Workflow · Illness History and Lifetime Health · Design Decisions

Clone this wiki locally