Repository navigation
12 Daily Health and Care Calendar
Daily Health is HealthBook AI's optional caregiver journal (internal code name: BabyCare). It records fluids, diapers, stool, medicines actually given, symptoms, activity and sleep as typed, profile-scoped rows. The same rows feed the day summary, the unified timeline, the Care Calendar (month, week and day views), observational analytics, before/during/after illness comparison and a doctor-ready care export. Everything is computed from what the caregiver recorded. Nothing is inferred clinically.
- Store seven entry kinds with validation, provenance (
source,recorded_by) and an encrypted free-text note. - Place every entry on the profile's local calendar date, computed in the profile's timezone at write time.
- Keep corrections auditable: edits snapshot the previous values, and rows are withdrawn, never deleted.
- Make duplicate or offline re-submissions idempotent through
client_key. - Produce day summaries, timelines, calendar markers, analytics and exports that use counting and co-occurrence language only.
Kind (/care/{kind}) |
Model | Key fields | Notable validation |
|---|---|---|---|
fluid |
FluidEntry |
amount_ml, amount_unknown, custom_amount, fluid_type, duration_minutes
|
oz is converted to ml; you must give an amount, a description or "unknown" (breastmilk may omit it); 0–3000 ml |
diaper |
DiaperEntry |
kind wet/stool/mixed/dry, urine_amount, stool_entry_id
|
a linked stool entry must belong to the same profile |
stool |
StoolEntry |
colour, consistency, amount, mucus/blood observed, watery, parent_concern, AI review fields |
clinical_review_suggested is set for black, red, pale or white colour, or when blood is observed |
medicine |
MedicationAdministration |
medicine_id or name, dose, route, scheduled_for, administration_status
|
status is one of given, skipped, missed, later, unknown |
symptom |
SymptomEntry |
kind, severity, temperature + unit, ended_at, episode links |
a temperature needs a unit |
activity |
ActivityEntry |
kind, ended_at, duration_minutes, camera_observation_id
|
duration is derived from the end time when it is missing |
sleep |
SleepEntry |
night/nap, ended_at, duration, location |
same as activity |
Every kind also rejects times more than 5 minutes in the future, times before birth, and an end time earlier than the start time. All shared columns live in the _CareEntry mixin: at, local_date, note, source (parent, camera_confirmed, import), status, version and client_key.
| Endpoint | Purpose |
|---|---|
POST /api/patients/{pid}/care/{kind} |
Add an entry. If the client_key was already used, the existing row is returned with existing: true
|
PATCH /api/care/{kind}/{eid} |
Correct an entry. The old values go to record_revisions and version goes up by 1 |
POST /api/care/{kind}/{eid}/withdraw |
Set status=withdrawn. The row stays in history |
GET /api/care/{kind}/{eid}/history |
Current row plus previous snapshots |
GET …/care/day, …/care/timeline?filters=
|
Day summary and filtered timeline |
GET …/care/calendar/month, …/care/calendar/week
|
Calendar markers and week grid |
GET …/care/analytics?range=today|7d|30d|custom |
Daily series, totals, statements (custom range is capped at one year) |
GET …/care/compare?kind=episode|admission&ref_id= |
Before/during/after windows (1–30 days, default 7) |
GET …/care/export?preset=24h|3d|7d|illness|custom&format=json|txt |
Doctor export of daily care |
GET/PUT …/care/settings, PUT …/care/day/{day}/note
|
Settings and a per-day note |
POST …/care/medicine/quick, GET …/care/medicines/today
|
GIVEN / SKIP / LATER / MISSED from the schedule card |
flowchart LR
A["Caregiver quick-add<br/>web or mobile"] -->|"POST with client_key"| B["care_api.prepare<br/>validate and compute local_date"]
B --> C{"client_key already used"}
C -->|"yes"| D["Return existing row"]
C -->|"no"| E["Typed entry row<br/>status active"]
E -->|"PATCH"| R["record_revisions snapshot<br/>version plus one"]
E -->|"withdraw"| W["status withdrawn<br/>excluded from views"]
E --> F["care.entries<br/>active rows by local_date"]
F --> G["day_summary"]
F --> H["timeline with filters"]
F --> I["month_markers and week_grid"]
F --> J["daily_series and analytics"]
J --> K["compare_illness<br/>before during after"]
J --> L["care_export<br/>summary_text and sections"]
L --> M["PDF export and doctor share"]
care.entries() returns only status == 'active' rows, so a withdrawn entry disappears from every derived view at once while its history stays intact.
day_summary() returns:
- Fluids: total ml for entries with a known amount, the event count, the number of entries with an unknown amount (kept out of the total), and a breakdown by fluid type.
- Diapers: wet, stool, mixed, dry checks and total changes. For today it also looks back to earlier days to compute minutes since the last wet diaper and the last stool.
- Stool: entries, watery or loose count, how many have photos, review flags and photo metadata.
-
Medicines: due slots from verified prescriptions that have schedule times, matched against administration records. A slot with no record is
no_record, nevergiven. - Symptoms, activity minutes, sleep minutes, and the number of camera suggestions still pending.
- Fixed warnings: "Fluid volumes and diaper counts are what you recorded; they are not a hydration assessment." and "Medicines are counted as given only when you recorded them as given."
timeline() merges every kind for one local day and sorts by time. Filters are a comma list: all, fluid, diaper, stool, medicine, symptom, activity, sleep, clinical and camera. clinical adds confirmed vaccinations, hospital visits, admissions, lab observations and documented fever episodes. camera adds observations that were not dismissed; unconfirmed ones are labelled "Camera suggestion". When a diaper was logged together with a stool entry, it is folded into that stool item ("diaper changed") so it is not counted twice. The legacy MedicationEvent rows also appear as "medication history".
The web client (web/care.js) offers Day, Week and Month views.
| View | Backend | Content |
|---|---|---|
| Month | month_markers(year, month) |
Per-day counts per category: medicine, fever, admission, visit, stool, diaper, vaccine, lab, illness, fluid, symptom, activity, sleep, camera (confirmed only), plus fluid_ml and wet
|
| Week | week_grid(start) |
Seven days, each with its full timeline and a summary subset |
| Day | care/day |
Day summary, day note and timeline including clinical and camera items |
Analytics ranges are Today, Last 7 days, 30 days and Custom. The output includes a per-day series, totals, stool colour and consistency distributions, mucus/blood report counts, medicine adherence ("a given record exists for N of M scheduled slots") and symptom counts. Statements describe co-occurrence only, for example "Loose or watery stool entries were recorded on 4 of 6 recorded days." Tests check that the output never contains "cured", "dehydrat" or causal "caused" wording.
compare_illness() buckets the window before, during and after an illness episode or admission. It lists documented fevers, antibiotic records, labs and visits in the extended window, with the warning that differences "are observations, not evidence that any treatment or event caused them."
care_export() builds a plain-language DAILY CARE SUMMARY with fluids, diapers, stool patterns, "MEDICINES RECORDED AS GIVEN", symptoms, sleep and activity, followed by a day-by-day list. Stool photographs are left out unless include_photos=true, in which case only their ids are listed. The same function feeds the daily_care_7d preset of the PDF Export Architecture and the daily-care section of Share With Doctor. Every export is audited.
CareSettings holds the quick fluid amounts, the default fluid type, review colours and an optional wet_diaper_threshold_per_day. The app never supplies a threshold. SettingsIn rejects a threshold unless threshold_source names who gave it (for example "Dr A, 2026-01-15"). If the day ends below the threshold, the summary shows "Review requested: fewer wet diapers than the threshold you entered." It never says "dehydrated".
mobile/DailyCareScreen.tsx generates a random client_key for every POST. If a POST fails with a network error, the request is stored in expo-secure-store (one key per queued item plus an index), and the screen shows "N entries saved offline, waiting to sync". queueFlush() replays the queue when the screen loads, when the app returns to the foreground, or when the caregiver taps "Sync now". Because of client_key, a replay that already reached the server returns the existing row instead of creating a duplicate. Non-network errors drop the queued item. The web client has no offline queue.
- Scheduled ≠ given: only an explicit GIVEN record counts. A reminder never marks a dose as given (see Medicines).
- Counts are not a hydration, illness or developmental assessment.
- Stool interpretation is observational only, not a diagnosis (see Stool Photo Pipeline).
- Parent-entered rows keep
source=parent. Camera-derived rows keepsource=camera_confirmed.
Every route resolves the profile through own_patient / own_resource. Another family gets 404, and an unauthenticated request gets 401. Notes, custom amounts and locations use EncryptedText. See Family Profile Isolation.
api/care_models.py, api/care.py, api/care_api.py, api/history.py, web/care.js, mobile/DailyCareScreen.tsx, tests/test_care.py.
Medicines and Administration Model · Stool Photo Pipeline · Home Camera Architecture · PDF Export Architecture · Database and Data Model · Illness History and Lifetime Health
Overview
Platform
Identity and access
Medical records
- 10 Document Ingestion
- 11 OCR and Human Verification
- 13 Medicines and Administration
- 14 Vaccination Model
- 15 Labs, Growth, Longitudinal
- 16 Illness and Lifetime Health
Daily care
Output and sharing
Security
Engineering
- 26 Testing and Validation
- 27 Deployment and CI/CD
- 28 Technology Stack
- 29 Design Decisions
- 30 Limitations
- 31 Roadmap