Repository navigation
18 PDF Export Architecture
The record export produces a designed A4 medical dossier for a selected scope. It can come as a PDF summary only, as a PDF with every original report appended, or as a ZIP archive with the PDF, the originals and a manifest (internal code name: BabyCare). Every format is rendered from a single structured MedicalExportModel, so the PDF, the plain-text rendering and the ZIP manifest can never disagree. Statuses are copied from the records, never inferred. Generation happens in memory for each request, the response is sent with no-store, and each export is audited.
- Translate a preset or custom selection into a record scope, using the same resolver as Share With Doctor (
share.resolve). - Arrange the selected records into presentation sections with provenance labels and
Source Rnn · p.Nreferences. - Render a ReportLab body (cover, executive summary, contents with page numbers, sections, vector charts), then append the original documents unchanged with PyMuPDF.
- Link every source reference to its appendix page, link every appendix page back to the summary, add bookmarks and a "Page X of Y" footer.
- Stream the result once. Nothing is written to disk.
| Endpoint | Purpose |
|---|---|
GET /api/export/presets |
Presets, sections and the three modes |
POST /api/patients/{pid}/export/preview |
Title, file name, date range, sections, counts, estimated pages, source list |
POST /api/patients/{pid}/export/download |
PDF (summary, with_sources) or ZIP (archive) |
GET /api/patients/{pid}/export/download?… |
Same request as query parameters, for the mobile download-and-share flow |
GET /api/patients/{pid}/export/debug.txt |
Plain-text rendering of the same model (support use) |
Presets (EXPORT_TYPES): complete, ecoli (condition-specific history resolved from documented culture and episode links), latest_hospitalization, antibiotics, labs, vaccination, daily_care_7d and custom. A custom export takes sections (a subset of summary, timeline, illnesses, hospitalizations, medicines, antibiotics, labs, cultures, vaccinations, growth, daily_care, sources, plus prescriptions and stool_photos), date_from/date_to (validated to be in order), include_sources, include_photos and daily_care_days (1–365). Unknown sections return 422.
flowchart TD
A["Selected record scope<br/>preset or custom sections and dates"] --> B["export_model.spec_for<br/>to share.resolve"]
B --> C["Export model<br/>MedicalExportModel"]
C --> D["Clinician summary<br/>cover, executive summary, contents"]
C --> E["Structured sections<br/>timeline, medicines, labs, vaccinations, daily care"]
E --> F["Source references<br/>Source Rnn and page"]
C --> G["Original-report appendix<br/>divider page plus unchanged pages"]
D --> H["ReportLab body<br/>two or three pagination passes"]
E --> H
F --> H
H --> I["PyMuPDF finish<br/>GOTO links, back links, bookmarks, Page X of Y"]
G --> I
I --> J["Private temporary PDF<br/>in memory, no-store"]
J --> K["User download<br/>web blob or mobile cache file"]
J --> L["Doctor share<br/>ZIP archive or share package"]
build_model() returns a JSON-serialisable dict (schema: babycare-export-1) with patient, meta, sources, summary, timeline, illnesses, hospitalizations, medicines, antibiotics, labs, trends, same_day_conflicts, cultures, susceptibility_matrix, vaccinations, growth, daily_care, vitals and counts.
-
Sources are the documents in the resolved selection, ordered as in the package. Each gets an appendix letter (A, B, … AA) and a reference
R{document id}. - Labs are grouped by category (haematology, inflammation, stool, liver, kidney/electrolytes, nutrition, thyroid, immunology, microbiology, other). Each row keeps its own printed reference interval, and values outside it are marked "outside printed interval". Trends need at least two numeric points with compatible units. Same-day duplicate values are listed as conflicts.
-
Daily care comes from
care.analyticsandcare.care_export(see Daily Health). - Pending items lists every unverified lab, culture, medicine, vaccine or fever row, in its own section "Items pending verification".
| Domain | Labels | Rule |
|---|---|---|
| Medicines |
PRESCRIBED, PRESCRIBED (unverified transcription), SCHEDULED, CONFIRMED GIVEN, STOPPED
|
CONFIRMED GIVEN only when a given medication event exists. A prescription is never shown as given |
| Antibiotic courses |
PRESCRIBED, SCHEDULED, CONFIRMED GIVEN, CONFIRMED GIVEN (hospital statement, no dated chart), UNKNOWN, NOT RECEIVED
|
copied from administration_status
|
| Vaccinations | status from vaccine_status
|
a due date is never shown as a given date (test_vaccination_due_date_is_not_a_given_date) |
| Cultures | S / R / I matrix with a "named in a prescription or course" column | that column does not mean given. Susceptibility is never a medicine |
Labels appear as coloured chips (STATUS_COLOURS). The Medications section opens with a status legend, and every body page carries the footer "statuses are copied from source documents, never inferred".
Cover → Executive clinical summary → Contents → Medical timeline → Hospitalizations → Illness episodes → Medications → Antibiotic history (with a course Gantt: solid = administration documented, dashed = prescribed or scheduled only) → Laboratory results (with trend charts) → Microbiology → Vaccinations → Growth → Daily Health journal (with a care chart) → Items pending verification → Original source documents (index) → Appendix.
-
Fonts: DejaVu Sans and DejaVu Serif (regular, bold, oblique) are bundled in
api/assets/fontsand registered asBCSans/BCSerif, so non-Latin-1 text renders correctly. If the files are missing, the code falls back to the base-14 Helvetica/Times fonts (Latin-1 only). The appendix bands use the same TTF through PyMuPDF. - Pagination: the body is built once to learn its page numbers, then up to three more times until the contents page numbers and appendix page map stop changing.
-
Appendix: each source gets a divider page (
APPENDIX X, title, reference, date, category, pages). PDFs are inserted as vector pages, never rasterised, and images at their original resolution. Header and footer bands sit outside the document area. Unreadable files get a placeholder page that points to the archive export. -
Links: internal
bcsrc:links becomeLINK_GOTOtargets. Every appendix page gets "‹ Back to summary". -
Determinism:
invariant=1, fixed metadata dates andno_new_idmake the output byte-identical for identical input (test_generation_is_in_memory_and_deterministic). -
Archive mode: the ZIP contains
Medical_Record.pdf,Original_Reports/<date_title_ref>.<ext>,manifest.csv(file, appendix, reference, date, category, title, pages, mime, appendix page) andRecord_Summary.txt, all with a fixed ZIP timestamp. File names carry no database ids.
-
Web (
web/exports.js): a "Download / Share" page with preset tiles, section checkboxes, mode choice and preview. The download is fetched as a blob, saved through an object URL, and the URL is revoked after 60 s. -
Mobile (
mobile/ExportScreen.tsx):FileSystem.downloadAsyncinto the app cache directory with the bearer header, then the native share sheet (expo-sharing). The cached file is deleted afterwards.
Only a guardian of the profile can preview or export (own_patient). Other families get 404, and anonymous requests get 401. Each download writes two audit rows: the mode, and a detail row with preset, page count, appended sources and a 12-character SHA-256 prefix of the output. Responses send Cache-Control: no-store, private and nosniff. Stool photographs are included only on explicit request. Storage names and session tokens never appear in the output.
Prescribed ≠ scheduled ≠ given. Culture susceptibility ≠ antibiotic received. Vaccination due date ≠ administered. OCR-extracted rows stay labelled until verified, and every statement keeps a reference to its original document.
api/export_api.py, api/export_model.py, api/export_pdf.py, api/assets/fonts/, api/share.py (resolve, entries_for), api/care.py, web/exports.js, mobile/ExportScreen.tsx, tests/test_export.py.
Share With Doctor Architecture · Medicines and Administration Model · Vaccination Model · Labs, Growth and Longitudinal Health · OCR and Human Verification Workflow · Limitations
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