add reporting foundation: multipage nav, auth gate, date-range fetch
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]>
This commit is contained in:
+62
-20
@@ -18,7 +18,7 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import date, datetime, time, timedelta, timezone
|
||||
|
||||
import pandas as pd
|
||||
import requests
|
||||
@@ -75,18 +75,14 @@ def _get(path: str, params: dict) -> list:
|
||||
return response.json()
|
||||
|
||||
|
||||
def fetch_entries(hours: int = 24) -> pd.DataFrame:
|
||||
"""Haal glucosemetingen (SGV) op van de laatste `hours` uur, in mmol/L.
|
||||
def _entries_since(since: datetime, count: int, until: datetime | None = None) -> pd.DataFrame:
|
||||
"""Haal entries op sinds `since` (en optioneel tot `until`), lokaal gefilterd.
|
||||
|
||||
Geeft een DataFrame terug met kolommen 'timestamp' en 'mmol', oplopend
|
||||
gesorteerd op tijd.
|
||||
Nightscout geeft resultaten terug in aflopende volgorde; vraag ruim genoeg
|
||||
entries op (CGM meet doorgaans elke 5 min) en filter lokaal op tijd. Dit is
|
||||
robuuster dan server-side find[]-query's, die niet op elke Nightscout-versie
|
||||
identiek werken.
|
||||
"""
|
||||
since = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||
# Nightscout geeft resultaten terug in aflopende volgorde; vraag ruim
|
||||
# genoeg entries op (CGM meet doorgaans elke 5 min) en filter lokaal op
|
||||
# tijd. Dit is robuuster dan server-side find[]-query's, die niet op elke
|
||||
# Nightscout-versie identiek werken.
|
||||
count = max(300, hours * 15)
|
||||
data = _get("/api/v1/entries/sgv.json", {"count": count})
|
||||
|
||||
if not data:
|
||||
@@ -96,18 +92,13 @@ def fetch_entries(hours: int = 24) -> pd.DataFrame:
|
||||
df["timestamp"] = pd.to_datetime(df["date"], unit="ms", utc=True).dt.tz_localize(None)
|
||||
df["mmol"] = df["sgv"].astype(float).apply(mgdl_to_mmol)
|
||||
df = df[df["timestamp"] >= since.replace(tzinfo=None)]
|
||||
if until is not None:
|
||||
df = df[df["timestamp"] <= until.replace(tzinfo=None)]
|
||||
return df[["timestamp", "mmol"]].sort_values("timestamp").reset_index(drop=True)
|
||||
|
||||
|
||||
def fetch_treatments(hours: int = 24) -> pd.DataFrame:
|
||||
"""Haal behandelingen op van de laatste `hours` uur: insuline, koolhydraten
|
||||
en pomp-/sensorwissels.
|
||||
|
||||
Geeft een DataFrame terug met kolommen 'timestamp', 'eventType',
|
||||
'insulin' en 'carbs' (de laatste twee NaN waar niet van toepassing).
|
||||
"""
|
||||
since = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||
count = max(200, hours * 10)
|
||||
def _treatments_since(since: datetime, count: int, until: datetime | None = None) -> pd.DataFrame:
|
||||
"""Haal treatments op sinds `since` (en optioneel tot `until`), lokaal gefilterd."""
|
||||
data = _get("/api/v1/treatments.json", {"count": count})
|
||||
|
||||
if not data:
|
||||
@@ -122,8 +113,59 @@ def fetch_treatments(hours: int = 24) -> pd.DataFrame:
|
||||
if "eventType" not in df.columns:
|
||||
df["eventType"] = ""
|
||||
df = df[df["timestamp"] >= since.replace(tzinfo=None)]
|
||||
if until is not None:
|
||||
df = df[df["timestamp"] <= until.replace(tzinfo=None)]
|
||||
return (
|
||||
df[["timestamp", "eventType", "insulin", "carbs"]]
|
||||
.sort_values("timestamp")
|
||||
.reset_index(drop=True)
|
||||
)
|
||||
|
||||
|
||||
def fetch_entries(hours: int = 24) -> pd.DataFrame:
|
||||
"""Haal glucosemetingen (SGV) op van de laatste `hours` uur, in mmol/L.
|
||||
|
||||
Geeft een DataFrame terug met kolommen 'timestamp' en 'mmol', oplopend
|
||||
gesorteerd op tijd.
|
||||
"""
|
||||
since = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||
count = max(300, hours * 15)
|
||||
return _entries_since(since, count)
|
||||
|
||||
|
||||
def fetch_treatments(hours: int = 24) -> pd.DataFrame:
|
||||
"""Haal behandelingen op van de laatste `hours` uur: insuline, koolhydraten
|
||||
en pomp-/sensorwissels.
|
||||
|
||||
Geeft een DataFrame terug met kolommen 'timestamp', 'eventType',
|
||||
'insulin' en 'carbs' (de laatste twee NaN waar niet van toepassing).
|
||||
"""
|
||||
since = datetime.now(timezone.utc) - timedelta(hours=hours)
|
||||
count = max(200, hours * 10)
|
||||
return _treatments_since(since, count)
|
||||
|
||||
|
||||
def fetch_entries_range(start_date: date, end_date: date) -> pd.DataFrame:
|
||||
"""Haal glucosemetingen op tussen start_date en end_date (beide inclusief).
|
||||
|
||||
Bedoeld voor meerdaagse rapportages (bijv. 90 dagen) waarbij het
|
||||
uren-schalende `fetch_entries` niet praktisch is. Zelfde kolommen als
|
||||
`fetch_entries`.
|
||||
"""
|
||||
since = datetime.combine(start_date, time.min, tzinfo=timezone.utc)
|
||||
until = datetime.combine(end_date, time.max, tzinfo=timezone.utc)
|
||||
days = (end_date - start_date).days + 1
|
||||
count = min(days * 300, 100_000) # ruime marge boven 5-min-interval
|
||||
return _entries_since(since, count, until)
|
||||
|
||||
|
||||
def fetch_treatments_range(start_date: date, end_date: date) -> pd.DataFrame:
|
||||
"""Haal behandelingen op tussen start_date en end_date (beide inclusief).
|
||||
|
||||
Zelfde kolommen als `fetch_treatments`.
|
||||
"""
|
||||
since = datetime.combine(start_date, time.min, tzinfo=timezone.utc)
|
||||
until = datetime.combine(end_date, time.max, tzinfo=timezone.utc)
|
||||
days = (end_date - start_date).days + 1
|
||||
count = min(days * 50, 50_000)
|
||||
return _treatments_since(since, count, until)
|
||||
|
||||
Reference in New Issue
Block a user