Stacked on #30 (base branch is feature/treatment-overlays) — merge after #30 (and #29), or retarget to main once those land.
pdf_export.py: new GlucoseReportPDF(FPDF) subclass — every section is its own landscape A4 page (Dagoverzicht: one page per day), with a real header (icon, section title, report version, insulin/pump/sensor type) and footer (page number, generation timestamp, Nightscout link) via fpdf2's header()/footer() hooks and alias_nb_pages().
New Insulineprofiel PDF section (mirrors the dashboard page from #29): basal bar chart, ISF/carb-ratio segments, DIA.
Each Dagoverzicht day now draws the same basal/bolus/carb/target overlays as the interactive charts from #30 (matplotlib port: twinx() secondary axis for basal area + carb bars, scatter for sized/labeled bolus dots, step line for the profile target), plus a new stats block per day.
reporting.py: new daily_stats() — average/min/max glucose, total/basal/bolus/SMB insulin, hypo/hyper counts and total durations for one calendar day.
New original SVG icon (assets/icon.svg — droplet + glucose-trend line) embedded in the PDF header via fpdf2's native SVG support.
New optional env vars REPORT_INSULIN_TYPE/REPORT_PUMP_TYPE/REPORT_SENSOR_TYPE (documented in .env.example/README) for the header's device line; all default to "Onbekend ..." if unset.
pages/8_Export.py now threads treatments/profile through to build_report_pdf (previously fetched but discarded).
Test plan
ruff check . passes
pytest tests/ -v passes (139 tests, +17 new: daily_stats in test_reporting.py, page-count/section/config tests in test_pdf_export.py using a black-box /Type /Page counting technique — no pixel comparisons, consistent with this project's existing PDF-testing philosophy)
Generated a real report against the configured Nightscout instance (all 5 sections, 90-day period → 12 pages) and visually inspected every page: cover page, Insulineprofiel, Samenvatting, AGP profiel, 7× Dagoverzicht (glucose+basal+bolus+carbs+target overlay rendering correctly with full legend and daily stats), Hypo-/hyperevents — header/footer/icon/pagination correct on every page
Manually exercised the real Export page in the browser (90-day period, all sections) end-to-end through Streamlit with no errors
## Summary
Stacked on #30 (base branch is `feature/treatment-overlays`) — merge after #30 (and #29), or retarget to `main` once those land.
- `pdf_export.py`: new `GlucoseReportPDF(FPDF)` subclass — every section is its own **landscape A4 page** (Dagoverzicht: one page per day), with a real **header** (icon, section title, report version, insulin/pump/sensor type) and **footer** (page number, generation timestamp, Nightscout link) via fpdf2's `header()`/`footer()` hooks and `alias_nb_pages()`.
- New **Insulineprofiel** PDF section (mirrors the dashboard page from #29): basal bar chart, ISF/carb-ratio segments, DIA.
- Each **Dagoverzicht** day now draws the same basal/bolus/carb/target overlays as the interactive charts from #30 (matplotlib port: `twinx()` secondary axis for basal area + carb bars, scatter for sized/labeled bolus dots, step line for the profile target), plus a new stats block per day.
- `reporting.py`: new `daily_stats()` — average/min/max glucose, total/basal/bolus/SMB insulin, hypo/hyper counts and total durations for one calendar day.
- New original SVG icon (`assets/icon.svg` — droplet + glucose-trend line) embedded in the PDF header via fpdf2's native SVG support.
- New optional env vars `REPORT_INSULIN_TYPE`/`REPORT_PUMP_TYPE`/`REPORT_SENSOR_TYPE` (documented in `.env.example`/README) for the header's device line; all default to "Onbekend ..." if unset.
- `pages/8_Export.py` now threads `treatments`/`profile` through to `build_report_pdf` (previously fetched but discarded).
## Test plan
- [x] `ruff check .` passes
- [x] `pytest tests/ -v` passes (139 tests, +17 new: `daily_stats` in `test_reporting.py`, page-count/section/config tests in `test_pdf_export.py` using a black-box `/Type /Page` counting technique — no pixel comparisons, consistent with this project's existing PDF-testing philosophy)
- [x] Generated a real report against the configured Nightscout instance (all 5 sections, 90-day period → 12 pages) and visually inspected every page: cover page, Insulineprofiel, Samenvatting, AGP profiel, 7× Dagoverzicht (glucose+basal+bolus+carbs+target overlay rendering correctly with full legend and daily stats), Hypo-/hyperevents — header/footer/icon/pagination correct on every page
- [x] Manually exercised the real Export page in the browser (90-day period, all sections) end-to-end through Streamlit with no errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Every report section now gets its own landscape A4 page (or, for
Dagoverzicht, one page per day) via a GlucoseReportPDF(FPDF) subclass
with a real header (section title, report version, insulin/pump/sensor
type from new REPORT_* env vars) and footer (page number, generation
timestamp, Nightscout link) on every page, plus an original SVG icon
in the header — inspired by the LibreView AGP report.
New "Insulineprofiel" PDF section mirrors the dashboard page (basal
bar chart, ISF/carb-ratio segments, DIA). Each Dagoverzicht day now
draws the same basal/bolus/carb/target overlays as the interactive
charts (matplotlib port of the Plotly helpers from #30) plus a new
per-day stats block (reporting.daily_stats: avg/min/max glucose,
total/basal/bolus/SMB insulin, hypo/hyper counts and durations).
Builds on #29 (profile data) and #30 (overlay logic/colors).
willemjan
merged commit a3d89c4d21 into feature/treatment-overlays2026-09-05 20:45:16 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Stacked on #30 (base branch is
feature/treatment-overlays) — merge after #30 (and #29), or retarget tomainonce those land.pdf_export.py: newGlucoseReportPDF(FPDF)subclass — every section is its own landscape A4 page (Dagoverzicht: one page per day), with a real header (icon, section title, report version, insulin/pump/sensor type) and footer (page number, generation timestamp, Nightscout link) via fpdf2'sheader()/footer()hooks andalias_nb_pages().twinx()secondary axis for basal area + carb bars, scatter for sized/labeled bolus dots, step line for the profile target), plus a new stats block per day.reporting.py: newdaily_stats()— average/min/max glucose, total/basal/bolus/SMB insulin, hypo/hyper counts and total durations for one calendar day.assets/icon.svg— droplet + glucose-trend line) embedded in the PDF header via fpdf2's native SVG support.REPORT_INSULIN_TYPE/REPORT_PUMP_TYPE/REPORT_SENSOR_TYPE(documented in.env.example/README) for the header's device line; all default to "Onbekend ..." if unset.pages/8_Export.pynow threadstreatments/profilethrough tobuild_report_pdf(previously fetched but discarded).Test plan
ruff check .passespytest tests/ -vpasses (139 tests, +17 new:daily_statsintest_reporting.py, page-count/section/config tests intest_pdf_export.pyusing a black-box/Type /Pagecounting technique — no pixel comparisons, consistent with this project's existing PDF-testing philosophy)🤖 Generated with Claude Code