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]>
96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
"""
|
|
Gedeelde UI-hulpmiddelen voor app.py en alle pages/*.py.
|
|
|
|
Elke Streamlit-pagina draait als los script, dus gedeelde constanten/functies
|
|
(kleurenpalet, thema-detectie) horen hier thuis in plaats van gedupliceerd te
|
|
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.
|
|
PALETTE = {
|
|
"light": {
|
|
"glucose": "#2a78d6",
|
|
"insulin": "#1baf7a",
|
|
"carbs": "#eb6834",
|
|
"low": "#d03b3b",
|
|
"very_low": "#d03b3b",
|
|
"high": "#fab219",
|
|
"very_high": "#fab219",
|
|
"band": "rgba(12,163,12,0.08)",
|
|
"grid": "#e1e0d9",
|
|
"muted": "#898781",
|
|
"text": "#52514e",
|
|
"good": "#0ca30c",
|
|
},
|
|
"dark": {
|
|
"glucose": "#3987e5",
|
|
"insulin": "#199e70",
|
|
"carbs": "#d95926",
|
|
"low": "#d03b3b",
|
|
"very_low": "#d03b3b",
|
|
"high": "#fab219",
|
|
"very_high": "#fab219",
|
|
"band": "rgba(12,163,12,0.15)",
|
|
"grid": "#2c2c2a",
|
|
"muted": "#898781",
|
|
"text": "#c3c2b7",
|
|
"good": "#0ca30c",
|
|
},
|
|
}
|
|
|
|
|
|
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"
|
|
try:
|
|
theme_type = st.context.theme.type or "light"
|
|
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
|