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
+38
View File
@@ -8,10 +8,19 @@ worden per pagina.
from __future__ import annotations
from datetime import date, datetime, timedelta, timezone
import streamlit as st
from nightscout_client import fetch_entries_range, fetch_treatments_range
MMOL_HOVERTEMPLATE = "%{x|%d-%m %H:%M} · %{y:.1f} mmol/L<extra></extra>"
# Periode-opties voor de rapportagepagina's (FR1): elke pagina kiest zijn eigen
# standaard (90 dagen voor statistiek/TIR, 14 dagen voor recente
# glucosegrafieken) via `default_label`.
PERIOD_OPTIONS_DAYS = {"14 dagen": 14, "30 dagen": 30, "90 dagen": 90}
# Kleuren per rol, licht/donker — categorale kleuren voor identiteit
# (glucose/insuline/carbs), status-kleuren (vast, nooit hergebruikt) voor
# te laag/te hoog.
@@ -47,6 +56,13 @@ PALETTE = {
}
def hex_to_rgba(hex_color: str, alpha: float) -> str:
"""Zet een '#rrggbb'-kleur om naar een 'rgba(r,g,b,a)'-string voor Plotly-fills."""
hex_color = hex_color.lstrip("#")
r, g, b = (int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
return f"rgba({r},{g},{b},{alpha})"
def get_colors() -> dict:
"""Geef de kleurenset terug die past bij het actieve Streamlit-thema."""
theme_type = "light"
@@ -55,3 +71,25 @@ def get_colors() -> dict:
except AttributeError:
pass
return PALETTE["dark" if theme_type == "dark" else "light"]
def select_period_days(default_label: str) -> tuple[date, date, int]:
"""Periodekiezer voor rapportagepagina's; geeft (start, eind, aantal_dagen) terug."""
labels = list(PERIOD_OPTIONS_DAYS.keys())
label = st.radio("Periode", labels, index=labels.index(default_label), horizontal=True)
days = PERIOD_OPTIONS_DAYS[label]
end = datetime.now(tz=timezone.utc).date()
start = end - timedelta(days=days - 1)
return start, end, days
@st.cache_data(ttl=3600, show_spinner="Data ophalen van Nightscout...")
def load_range_data(start: date, end: date):
"""Gecachete Nightscout-fetch voor een vaste periode.
Lange TTL (in tegenstelling tot de 60s van de live-pagina): historische
data van een afgeronde periode verandert niet.
"""
glucose = fetch_entries_range(start, end)
treatments = fetch_treatments_range(start, end)
return glucose, treatments