JulianTekles commited on
Commit
9f7fcbb
·
verified ·
1 Parent(s): 2a96a04

Update core/tools/build_glucose_plot.py

Browse files
Files changed (1) hide show
  1. core/tools/build_glucose_plot.py +64 -44
core/tools/build_glucose_plot.py CHANGED
@@ -3,11 +3,45 @@ from __future__ import annotations
3
 
4
  import io
5
  from datetime import datetime, timedelta
6
- from typing import List, Dict, Any
 
 
 
 
7
 
8
  from core.tools.nightscout import get_sgv_entries
9
 
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  def build_glucose_plot_png(hours: int = 3) -> bytes:
12
  """
13
  Baut ein PNG mit dem Glukoseverlauf der letzten `hours`.
@@ -22,24 +56,9 @@ def build_glucose_plot_png(hours: int = 3) -> bytes:
22
  180–250 -> gelb
23
  > 250 -> rot
24
  """
 
25
 
26
- # -------------------------------------------------
27
- # LAZY IMPORT (verhindert Crash beim App-Start)
28
- # -------------------------------------------------
29
- try:
30
- import matplotlib
31
- matplotlib.use("Agg") # Headless für HF / Server
32
- import matplotlib.pyplot as plt
33
- except Exception as e:
34
- raise RuntimeError(
35
- "matplotlib ist nicht installiert oder nicht ladbar"
36
- ) from e
37
-
38
- # -------------------------------------------------
39
- # Nightscout Daten
40
- # -------------------------------------------------
41
- entries: List[Dict[str, Any]] = get_sgv_entries(since_hours=hours)
42
-
43
  if not entries:
44
  raise RuntimeError("No Nightscout data available")
45
 
@@ -47,52 +66,53 @@ def build_glucose_plot_png(hours: int = 3) -> bytes:
47
  values: List[int] = []
48
 
49
  for e in entries:
50
- if not e.get("sgv") or not e.get("date"):
 
 
51
  continue
52
- times.append(datetime.utcfromtimestamp(e["date"] / 1000))
53
- values.append(int(e["sgv"]))
 
 
 
 
 
 
 
 
54
 
55
  if not times:
56
- raise RuntimeError("No valid SGV points")
 
 
 
 
 
57
 
58
  # -------------------------------------------------
59
  # Plot
60
  # -------------------------------------------------
61
  fig, ax = plt.subplots(figsize=(8, 4))
62
 
63
- # Zielbereich
64
- ax.axhspan(80, 180, color="#C8E6C9", alpha=0.6, label="Zielbereich 80–180")
65
-
66
- # Warnzonen
67
- ax.axhspan(0, 80, color="#FFCDD2", alpha=0.6)
68
- ax.axhspan(80, 95, color="#FFF9C4", alpha=0.6)
69
- ax.axhspan(180, 250, color="#FFF9C4", alpha=0.6)
70
- ax.axhspan(250, 400, color="#FFCDD2", alpha=0.6)
71
 
72
  # Verlauf
73
- ax.plot(
74
- times,
75
- values,
76
- color="#1565C0",
77
- linewidth=2,
78
- marker="o",
79
- markersize=3,
80
- )
81
 
82
  ax.set_ylim(40, 300)
83
  ax.set_ylabel("mg/dL")
84
  ax.set_title(f"Glukoseverlauf – letzte {hours}h")
85
-
86
- ax.grid(True, alpha=0.3)
87
  fig.autofmt_xdate()
88
 
89
- # -------------------------------------------------
90
- # Export → PNG Bytes
91
- # -------------------------------------------------
92
  buf = io.BytesIO()
93
  plt.tight_layout()
94
  plt.savefig(buf, format="png", dpi=120)
95
  plt.close(fig)
96
-
97
  buf.seek(0)
98
  return buf.read()
 
3
 
4
  import io
5
  from datetime import datetime, timedelta
6
+ from typing import List, Dict, Any, Optional
7
+
8
+ import matplotlib
9
+ matplotlib.use("Agg") # Headless für HF / Server
10
+ import matplotlib.pyplot as plt
11
 
12
  from core.tools.nightscout import get_sgv_entries
13
 
14
 
15
+ def _safe_get_sgv_entries(hours: int) -> List[Dict[str, Any]]:
16
+ """
17
+ Nightscout wrapper: versucht verschiedene Signaturen von get_sgv_entries(),
18
+ damit wir unabhängig von deiner nightscout.py-Version funktionieren.
19
+ """
20
+ # 1) Häufige Varianten (keyword)
21
+ for kwargs in ({"hours": hours}, {"sinceHours": hours}, {"since_hours": hours}, {"hours_back": hours}):
22
+ try:
23
+ entries = get_sgv_entries(**kwargs) # type: ignore
24
+ if isinstance(entries, list):
25
+ return entries
26
+ except TypeError:
27
+ pass
28
+ except Exception:
29
+ # andere Fehler nicht hier verschlucken -> später sichtbar
30
+ break
31
+
32
+ # 2) Häufige Variante (positional)
33
+ try:
34
+ entries = get_sgv_entries(hours) # type: ignore
35
+ if isinstance(entries, list):
36
+ return entries
37
+ except TypeError:
38
+ pass
39
+
40
+ # 3) Fallback: ohne args (liefert evtl. "letzte N" – wir filtern dann selbst)
41
+ entries = get_sgv_entries() # type: ignore
42
+ return entries if isinstance(entries, list) else []
43
+
44
+
45
  def build_glucose_plot_png(hours: int = 3) -> bytes:
46
  """
47
  Baut ein PNG mit dem Glukoseverlauf der letzten `hours`.
 
56
  180–250 -> gelb
57
  > 250 -> rot
58
  """
59
+ since = datetime.utcnow() - timedelta(hours=hours)
60
 
61
+ entries: List[Dict[str, Any]] = _safe_get_sgv_entries(hours=hours)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  if not entries:
63
  raise RuntimeError("No Nightscout data available")
64
 
 
66
  values: List[int] = []
67
 
68
  for e in entries:
69
+ sgv = e.get("sgv")
70
+ ts = e.get("date") # ms epoch
71
+ if sgv is None or ts is None:
72
  continue
73
+ try:
74
+ t = datetime.utcfromtimestamp(float(ts) / 1000.0)
75
+ if t < since:
76
+ continue # nur gewünschter Zeitraum
77
+ v = int(float(sgv))
78
+ except Exception:
79
+ continue
80
+
81
+ times.append(t)
82
+ values.append(v)
83
 
84
  if not times:
85
+ raise RuntimeError("No valid SGV points in selected time window")
86
+
87
+ # Sortieren, falls Nightscout rückwärts liefert
88
+ pairs = sorted(zip(times, values), key=lambda x: x[0])
89
+ times = [p[0] for p in pairs]
90
+ values = [p[1] for p in pairs]
91
 
92
  # -------------------------------------------------
93
  # Plot
94
  # -------------------------------------------------
95
  fig, ax = plt.subplots(figsize=(8, 4))
96
 
97
+ # Zielbereich + Farbzonen
98
+ ax.axhspan(80, 180, alpha=0.35) # grün (default, keine expliziten Farben nötig)
99
+ ax.axhspan(0, 80, alpha=0.25) # rot-zone (über alpha sichtbar)
100
+ ax.axhspan(80, 95, alpha=0.25) # gelb-zone
101
+ ax.axhspan(180, 250, alpha=0.25) # gelb-zone
102
+ ax.axhspan(250, 450, alpha=0.25) # rot-zone
 
 
103
 
104
  # Verlauf
105
+ ax.plot(times, values, linewidth=2, marker="o", markersize=3)
 
 
 
 
 
 
 
106
 
107
  ax.set_ylim(40, 300)
108
  ax.set_ylabel("mg/dL")
109
  ax.set_title(f"Glukoseverlauf – letzte {hours}h")
110
+ ax.grid(True, alpha=0.25)
 
111
  fig.autofmt_xdate()
112
 
 
 
 
113
  buf = io.BytesIO()
114
  plt.tight_layout()
115
  plt.savefig(buf, format="png", dpi=120)
116
  plt.close(fig)
 
117
  buf.seek(0)
118
  return buf.read()