Fix bolus/basal insulin categorization: split SMB from manual corrections, add basal calc
AndroidAPS records an automatic SMB micro-bolus as eventType "Correction Bolus" with type "SMB", not as its own eventType. bolus_stats() was grouping by eventType alone, so every SMB was silently counted as a manual correction bolus. Verified against production data: 100% of "Correction Bolus" treatments over 90 days were actually type=SMB. Also adds basal_stats(), computing delivered basal directly from Temp Basal treatments (rate x duration) without needing the pump profile. Closes gitea #20.
This commit is contained in:
+20
-10
@@ -176,29 +176,39 @@ def _entries_since(since: datetime, until: datetime | None = None) -> pd.DataFra
|
||||
return df[["timestamp", "mmol"]].sort_values("timestamp").reset_index(drop=True)
|
||||
|
||||
|
||||
_TREATMENT_COLUMNS = ["timestamp", "eventType", "insulin", "carbs", "type", "absolute", "duration"]
|
||||
|
||||
|
||||
def _treatments_since(since: datetime, until: datetime | None = None) -> pd.DataFrame:
|
||||
"""Haal treatments op sinds `since` (en optioneel tot `until`), lokaal gefilterd."""
|
||||
"""Haal treatments op sinds `since` (en optioneel tot `until`), lokaal gefilterd.
|
||||
|
||||
`type` en `absolute`/`duration` komen erbij naast de oorspronkelijke
|
||||
kolommen: AndroidAPS registreert een SMB-microbolus niet als eigen
|
||||
`eventType`, maar als `eventType == "Correction Bolus"` met
|
||||
`type == "SMB"` (zie reporting.bolus_stats() / gitea #20) — en
|
||||
`absolute` (E/uur) + `duration` (minuten) op Temp Basal-treatments maken
|
||||
het mogelijk afgeleverde basaalinsuline te berekenen zonder het
|
||||
pompprofiel te hoeven ophalen (zie reporting.basal_stats()).
|
||||
"""
|
||||
data = _paginated_get("/api/v1/treatments.json", since, until, TREATMENTS_CURSOR_FIELD)
|
||||
|
||||
if not data:
|
||||
return pd.DataFrame(columns=["timestamp", "eventType", "insulin", "carbs"])
|
||||
return pd.DataFrame(columns=_TREATMENT_COLUMNS)
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
df["timestamp"] = pd.to_datetime(df["created_at"], utc=True).dt.tz_localize(None)
|
||||
for col in ("insulin", "carbs"):
|
||||
for col in ("insulin", "carbs", "absolute", "duration"):
|
||||
if col not in df.columns:
|
||||
df[col] = pd.NA
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
if "eventType" not in df.columns:
|
||||
df["eventType"] = ""
|
||||
for col in ("eventType", "type"):
|
||||
if col not in df.columns:
|
||||
df[col] = ""
|
||||
df[col] = df[col].fillna("")
|
||||
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)
|
||||
)
|
||||
return df[_TREATMENT_COLUMNS].sort_values("timestamp").reset_index(drop=True)
|
||||
|
||||
|
||||
def fetch_entries(hours: int = 24) -> pd.DataFrame:
|
||||
|
||||
Reference in New Issue
Block a user