Files
HealthLog/docs/doctor-report.md
T

3.6 KiB

Doctor Report

The doctor report is a printable PDF summary of a user's tracked vitals, medication compliance, glucose context, and mood over a configurable window (default 90 days, max 365). It is intended as discussion material for a doctor's appointment, not a medical diagnosis.

Two endpoints

The same aggregation logic backs both endpoints — collectDoctorReportData in src/lib/doctor-report-data.ts is the single source of truth — so the JSON payload and the server-rendered PDF are guaranteed to be in sync.

POST /api/doctor-report — JSON payload

Returns the aggregated DoctorReportData envelope. Used by the PWA's client-side renderer (src/lib/doctor-report-pdf.ts, jsPDF + autotable). Best on desktop browsers and on Android, where local generation keeps the PDF off the wire and works offline (cached page).

  • Auth: session required.
  • Rate-limit: 10/h per user.
  • Body: { days?: number } (default 90).
  • Response: 200 application/json with envelope { data, error: null }.
  • Audit action: doctor-report.generate.

POST /api/doctor-report/pdf — server-rendered PDF

Returns the ready-to-download PDF bytes. Used primarily on iOS/Safari, where the client-side jsPDF download UX is unreliable (Safari sometimes opens the PDF in a new tab without preserving the filename or fails the blob save).

  • Auth: session required (401 otherwise).
  • Rate-limit: 10/h per user, shared with the JSON endpoint (429 on excess).
  • Body: { days?: number; locale?: "de" | "en" }. Body is optional — empty bodies are accepted.
  • Locale resolution: explicit body.locale wins, then the Accept-Language header (de* → de, anything else → en), then the hard fallback de.
  • Response: 200 application/pdf with Content-Disposition: attachment; filename="healthlog-report-YYYY-MM-DD.pdf" and Cache-Control: no-store. Body is the raw PDF bytes.
  • Errors: JSON envelope { data: null, error: "..." } for 401 and 429; 500 if rendering fails.
  • Audit action: doctor-report.pdf.generate.

Wellness summary (derived scores)

Since v1.10.0 the report carries a Wellness summary section below the clinical content, holding the server-derived wellness scores (Recovery, Stress, Strain) and the other derived metrics where present. It is kept deliberately separate from the clinical vitals table:

  • Each entry is labelled descriptive, not a clinical assessment, and the scores never enter the vitals table or the clinical aggregations.
  • The Stress score is shown as an HRV-derived proxy, not a sensor-measured stress value.
  • In the FHIR export the same scores are emitted as survey-category Observations (text-only code, no fabricated LOINC) with a per-Observation non-clinical note — so a downstream system never mistakes them for a vital sign or a diagnosis.

Each derived metric states its inputs, method, and the cited standard it is grounded in (the same provenance the in-app explainer shows).

Implementation notes

  • src/lib/doctor-report-pdf-core.ts contains the isomorphic renderer. jsPDF runs unchanged in Node — doc.output("arraybuffer") returns a valid %PDF- byte stream in both environments, so we deliberately avoid headless Chromium (Puppeteer/Playwright) to keep the runtime dependency surface low.
  • src/lib/doctor-report-pdf.ts is a thin client façade that returns the raw jsPDF instance so the existing settings-page download flow (which calls doc.save(...)) keeps working unchanged.
  • Server-side translations come from messages/{de,en}.json via getServerTranslator() (src/lib/i18n/server-translator.ts).