add Samenvatting and AGP Profiel pages (FR2, FR3, FR9)

Wires reporting.py's stats into two new pages: a summary panel (data
completeness, GMI, %CV, 5-band time-in-range vs. consensus targets) and
the standard AGP percentile chart (5th-95th and 25th-75th bands plus
median, aggregated by time-of-day across the selected period).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-07-26 20:09:25 +02:00
co-authored by Claude Sonnet 5
parent 3ef9a7666a
commit 1207ff536f
6 changed files with 277 additions and 0 deletions
+24
View File
@@ -202,3 +202,27 @@ def summarize_events(events: pd.DataFrame, period_days: float) -> dict[str, floa
"total_minutes": round(total_minutes, 1),
"events_per_week": round(len(events) / period_days * 7, 1),
}
AGP_PERCENTILE_COLUMNS = ["minute_of_day", "p5", "p25", "p50", "p75", "p95"]
def agp_percentile_bands(glucose: pd.DataFrame, bin_minutes: int = 30) -> pd.DataFrame:
"""Aggregeer glucosewaarden per tijdstip-van-de-dag naar percentielbanden.
Groepeert alle metingen in de periode naar hun tijd-van-de-dag (in bins
van `bin_minutes`), en berekent per bin de 5e/25e/50e/75e/95e percentiel
— de standaard AGP-percentielgrafiek over een samengesteld 24-uursbeeld
(Danne et al. 2017, Diabetes Care 40(12):1631-1640).
"""
if glucose.empty:
return pd.DataFrame(columns=AGP_PERCENTILE_COLUMNS)
df = glucose.copy()
minute_of_day = df["timestamp"].dt.hour * 60 + df["timestamp"].dt.minute
df["bin"] = (minute_of_day // bin_minutes) * bin_minutes
grouped = df.groupby("bin")["mmol"].quantile([0.05, 0.25, 0.5, 0.75, 0.95]).unstack()
grouped.columns = ["p5", "p25", "p50", "p75", "p95"]
grouped = grouped.reset_index().rename(columns={"bin": "minute_of_day"})
return grouped[AGP_PERCENTILE_COLUMNS].sort_values("minute_of_day").reset_index(drop=True)