Files

63 lines
2.3 KiB
Python

"""
Data processing logic voor het glucose-dashboard.
Bewust gescheiden van app.py: Streamlit-UI-code kun je niet zomaar los
unit-testen (die heeft een draaiende Streamlit-context nodig). Door de
logica (classificeren, samenvatten) in een los module te zetten, kun je die
logica wel gewoon met pytest testen, en blijft app.py alleen de UI-laag.
"""
from __future__ import annotations
import pandas as pd
from nightscout_client import SENSOR_CHANGE_EVENT_TYPES, SITE_CHANGE_EVENT_TYPES
# Streefbereik in mmol/L — pas aan naar je eigen behandeldoelen.
TARGET_LOW = 3.9
TARGET_HIGH = 10.0
def classify_treatments(treatments: pd.DataFrame) -> dict[str, pd.DataFrame]:
"""Splits ruwe Nightscout-treatments op in aparte, tijd-gesorteerde subsets.
Een insuline- en koolhydraat-registratie kunnen dezelfde treatment zijn
(bijv. een "Meal Bolus" met zowel insuline als carbs) en verschijnen dan
in beide subsets.
"""
insulin = treatments[treatments["insulin"].fillna(0) > 0]
carbs = treatments[treatments["carbs"].fillna(0) > 0]
site_changes = treatments[treatments["eventType"].isin(SITE_CHANGE_EVENT_TYPES)]
sensor_changes = treatments[treatments["eventType"].isin(SENSOR_CHANGE_EVENT_TYPES)]
return {
"insulin": insulin.reset_index(drop=True),
"carbs": carbs.reset_index(drop=True),
"site_changes": site_changes.reset_index(drop=True),
"sensor_changes": sensor_changes.reset_index(drop=True),
}
def glucose_status(mmol: float, low: float = TARGET_LOW, high: float = TARGET_HIGH) -> str:
"""Classificeer een glucosewaarde t.o.v. het streefbereik."""
if mmol < low:
return "low"
if mmol > high:
return "high"
return "in_range"
def summarize_time_in_range(
glucose: pd.DataFrame, low: float = TARGET_LOW, high: float = TARGET_HIGH
) -> dict[str, float]:
"""Bereken het percentage metingen onder/in/boven het streefbereik."""
if glucose.empty:
return {"low": 0.0, "in_range": 0.0, "high": 0.0}
statuses = glucose["mmol"].apply(lambda v: glucose_status(v, low, high))
counts = statuses.value_counts(normalize=True) * 100
return {
"low": round(counts.get("low", 0.0), 1),
"in_range": round(counts.get("in_range", 0.0), 1),
"high": round(counts.get("high", 0.0), 1),
}