Skip to content

14 Vaccination Model

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

The vaccination model in HealthBook AI (internal code name: BabyCare) records what the family's vaccination card or doctor says. It does not record what a national schedule predicts. Each row separates the date a dose was given from the date a dose is due. A row is not trusted for reminders until a caregiver confirms it against a real card or doctor schedule. Reference schedules exist only to help a caregiver compare; they never become confirmed entries.

Core rule: due date ≠ administered

A due_date is an appointment written on the card. It is never evidence that a vaccine was given. Only given_date describes administration. An undated card line is never treated as given, and the review validator rejects a vaccine row that has no date (see OCR and Human Verification Workflow).

Responsibilities

  • Store vaccine rows with given and due dates, dose label, batch, facility, doctor, source document and page.
  • Gate reminders on confirmation, a real source document and a non-reference source type.
  • Detect possible duplicate doses before saving.
  • Reset confirmation when a material field changes.
  • Provide India UIP and IAP reference charts as reference-only overlays.
  • Expose a status-labelled vaccination chart and a CSV export.

Inputs and outputs

Endpoint Purpose
GET/POST /api/patients/{pid}/vaccines list rows (by due date) / add a row (always confirmed=false)
PATCH /api/vaccines/{id} edit or confirm; snapshot written first
GET /api/patients/{pid}/vaccine-reference?schedule=India_UIP|IAP_private reference chart computed from DOB, with warnings
POST /api/patients/{pid}/vaccine-reference/import create unconfirmed India_UIP_reference drafts
GET /api/patients/{pid}/vaccination-chart rows with status, age at date, scheduled reminders
GET /api/patients/{pid}/vaccination-chart.csv export (formula-injection safe)

Fields

Field Values / rule
name, dose_label required name; dose label up to 70 chars
given_date must be between the profile's DOB and today (profile timezone)
due_date appointment date; no range restriction
confirmed false on create; set only by PATCH or by review confirmation
source_type manual, vaccination_card, doctor_schedule, India_UIP_reference
schedule card, India_UIP, IAP_private
reminder_days lead days, default 7,1,0, up to 10 values
batch_number, facility, doctor, note free text, encrypted
source_document_id, source_page provenance, validated against the document's page count
version incremented on each edit; part of the reminder key

Confirmation and material edits

PATCH /api/vaccines/{id} enforces these rules:

  • Confirming requires a source_document_id, and source_type must not be India_UIP_reference. The error is "Reference-only dates require a real vaccination card or doctor schedule before confirmation".
  • Confirming requires at least one of due_date or given_date.
  • If due_date, given_date, name, dose_label, source_document_id, source_type, source_page or reminder_days changes, confirmed is reset to false, unless the same request also sets confirmed.
  • Changing the profile timezone un-confirms every confirmed row that has no given date, because its reminder times would otherwise silently shift.

Duplicate detection

check_vaccine_duplicate compares the name with all non-word characters removed and case-folded, plus the case-folded dose label. A match returns 409 with the existing rows, unless the request carries duplicate_confirmed: true. Review confirmation reports the same check per row as possible_duplicate.

Next dose as a separate record

When a reviewed card row has both a given date and a next_due_date, confirmation creates two rows: the administered dose, and a new row for the next dose with only a due date and an empty batch number. The next-dose row is confirmed only if the caregiver ticked "activate reminders". The audit records confirmed_next_dose or unconfirmed_next_dose. An appointment is never folded into the administered record.

Reference schedules

api/vaccination.py holds two static age charts:

  • India UIP, attributed to a Government of India annual report;
  • IAP private-clinic milestones, attributed to an IAP ACVIP publication.

reference(dob, schedule) adds weeks, months or years to the DOB and returns reference_date, an optional window end, the source, and the warning "REFERENCE ONLY: schedule varies; not an appointment or catch-up instruction". The API adds that reference dates are not individualised eligibility or catch-up recommendations, and that JE applies only in specific areas. The import endpoint creates unconfirmed India_UIP_reference rows (skipping name and dose pairs that already exist) and says that no reminders will be generated. Reference rows can never schedule reminders, and the chart shows them as reference_only.

Reminders

reminder_items only schedules a vaccine when all of the following hold:

  • confirmed = true;
  • no given_date;
  • a due_date;
  • a source_document_id;
  • source_type is not India_UIP_reference.

There is one reminder per lead day, at 09:00 profile-local time, keyed vaccine-{id}-v{version}-{due}-{lead}d. Delivery never marks a dose as given (see Notifications and Reminder Architecture).

Entry lifecycle

Diagram: Vaccine entry state diagram

flowchart TD
    N["New row, confirmed false"] --> Q{"Given date present"}
    Q -->|"yes"| GP["documented_given_pending_verification"]
    Q -->|"no, due date only"| DP["due_pending_verification"]
    Q -->|"reference import"| RO["reference_only, never confirmable as is"]
    Q -->|"no dates"| IN["incomplete_record"]
    GP -->|"confirm with source document"| GV["documented_given_verified"]
    DP -->|"confirm with source document"| UC["upcoming_confirmed, reminders active"]
    DP -->|"due date passes"| DX["due_date_passed_unverified"]
    UC -->|"due date passes"| CX["confirmed_date_passed, ask provider"]
    UC -->|"material edit"| DP
    GV -->|"material edit"| GP
    UC -->|"caregiver records given date"| GV
Loading

Vaccination chart statuses

vaccine_status() in api/history_api.py:

Status Meaning shown to user
reference_only Reference schedule only
documented_given_verified Documented given (caregiver verified)
documented_given_pending_verification Documented given on card, pending caregiver verification
upcoming_confirmed Upcoming, confirmed appointment
confirmed_date_passed Confirmed date has passed, ask provider
due_pending_verification Due date on card, pending verification
due_date_passed_unverified Card due date has passed, unverified; ask provider
incomplete_record Incomplete record

The chart also returns age_at_date, reminders_scheduled per row, pending due dates grouped by date, and the next confirmed appointment. The simple timeline shows only confirmed given doses. The history view shows given doses with a certainty label and due dates as "Vaccine due on card".

Clinical safety rules

  • Vaccination due date ≠ vaccination administered.
  • Reference schedules are not personalised orders and never activate reminders.
  • OCR extracted ≠ clinically verified: card transcriptions stay pending until confirmed.
  • A reminder is never proof a dose was given.

Implementation

api/main.py (VaccineIn, VaccineUpdate, add, edit, reference routes), api/workflows.py (check_vaccine_duplicate, review confirmation, timezone reconciliation, CSV), api/vaccination.py, api/reminders.py, api/history_api.py (vaccine_status, vaccination_chart), web/history.js.

Related pages

OCR and Human Verification Workflow · Notifications and Reminder Architecture · Medical Document Ingestion · Design Decisions

Clone this wiki locally