AndroidAPS records an automatic SMB micro-bolus as eventType "Correction
Bolus" with type "SMB", not as its own eventType. bolus_stats() was
grouping by eventType alone, so every SMB was silently counted as a manual
correction bolus. Verified against production data: 100% of "Correction
Bolus" treatments over 90 days were actually type=SMB.
Also adds basal_stats(), computing delivered basal directly from Temp
Basal treatments (rate x duration) without needing the pump profile.
Closes gitea #20.
Investigated the "only ~4 days of treatments" report via direct
MongoDB access (issue #13): the collection has 123k+ documents, no
capping, no TTL index — the full year of history is in the database.
The actual cause: /api/v1/treatments.json filters/sorts on the string
field `created_at`, not the numeric `date` field /api/v1/entries uses.
Paginating with find[date][$lte] (or no filter at all) silently caps
treatments to an internal ~4-day window even though every document has
a valid `date` field; find[created_at][$lte] correctly walks back
through the full history.
_paginated_get() now takes an explicit cursor_field per endpoint and
always sends the cursor from the first request (never omitted), so
that hidden default window can't trigger. Verified against the real
instance: a 92-day treatments fetch now returns 39,403 rows (was ~700).
_entries_since/_treatments_since requested a single page via `count`,
but Nightscout's /api/v1/entries and /api/v1/treatments ignore a large
`count` and cap the response around ~1150/~700 documents regardless of
how far back the requested period goes. Every report page relying on
fetch_entries_range/fetch_treatments_range (7/14/30/90-day selectors)
was silently only getting the most recent ~3-4 days, no matter the
selected period.
Fixed by paginating with `find[date][$lte]` cursors in a new
_paginated_get(), walking back until the requested `since` boundary is
reached or the server returns a short/empty page (= genuinely no more
data, confirmed by manually paginating against the real instance:
entries go back 50+ days, treatments only ~4 days on this install).
Also added reporting.completeness_warning(), shared by the interactive
pages (via ui_common.render_completeness_warning) and the PDF export,
that distinguishes "a structural gap at the start of the period" (data
wasn't fetched/available) from "scattered gaps below the clinical AGP
coverage threshold" (Danne et al. 2017) — the previous single generic
"X% sensor coverage" message read as a CGM/sensor problem even when
the real cause was this fetch bug.
First increment of the clinical reporting feature (see
docs/requirements-reporting.md and docs/design-reporting.md): moves the
existing live view to pages/1_Live.py, adds a password gate since the
dashboard URL will be shared with the diabetes team, adds
fetch_entries_range/fetch_treatments_range for multi-day report queries,
and adds reporting.py with the core clinical calculations (GMI, %CV,
5-band time-in-range, hypo/hyper event detection per the Battelino et al.
2019 persistence rule). No report pages wired up yet — that's next.
Co-Authored-By: Claude Sonnet 5 <[email protected]>