""" 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), }