fix: Grafana-Zeitfenster deckt Projektionshorizont ab (Szenario-Vergleich sichtbar)
Das Szenario-Vergleich-Panel zeichnet die Projektion (Zukunft, horizon_days ab heute). Kein Panel nutzt $__timeFilter, daher begrenzt allein das Dashboard-Zeitfenster die Anzeige. Mit time.to='now' wurde die gesamte Kurve ab morgen abgeschnitten (nur ~4 Tage <= heute sichtbar, Y-Achse auf deren schmalen Bereich fixiert). Fix: time.to='now+19M' deckt den 548-Tage-Horizont ab. Regressionstest tests/test_grafana_dashboard.py haelt die Invariante fest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
"tags": ["finanzen"],
|
||||
"time": {
|
||||
"from": "now-1y",
|
||||
"to": "now"
|
||||
"to": "now+19M"
|
||||
},
|
||||
"refresh": "",
|
||||
"panels": [
|
||||
|
||||
51
finance/tests/test_grafana_dashboard.py
Normal file
51
finance/tests/test_grafana_dashboard.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Regressionstests fuer das provisionierte Grafana-Dashboard finanzen.json.
|
||||
|
||||
Hintergrund: Kein Panel nutzt $__timeFilter, jedes liefert alle Zeilen und
|
||||
Grafana beschneidet auf das Dashboard-Zeitfenster. Das Szenario-Vergleich-Panel
|
||||
zeichnet die Projektion (Zukunft, horizon_days ab heute). Steht 'to' auf 'now',
|
||||
wird die gesamte Kurve ab morgen abgeschnitten (nur die ~4 Tage <= heute
|
||||
sichtbar, Y-Achse auf deren schmalen Bereich fixiert). Dieser Test haelt fest,
|
||||
dass 'to' weit genug in die Zukunft reicht.
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
DASHBOARD = Path(__file__).resolve().parent.parent / "grafana" / "dashboards" / "finanzen.json"
|
||||
|
||||
_UNIT_DAYS = {"y": 365, "M": 30, "w": 7, "d": 1, "h": 1 / 24}
|
||||
|
||||
|
||||
def _relative_to_days(expr: str) -> float:
|
||||
"""Grafana-Relativausdruck ('now', 'now+19M', 'now-1y') -> vorzeichen-
|
||||
behafteter Tages-Offset relativ zu 'now' (Zukunft positiv)."""
|
||||
expr = expr.strip()
|
||||
if expr == "now":
|
||||
return 0.0
|
||||
m = re.fullmatch(r"now([+-])(\d+)([yMwdh])", expr)
|
||||
if not m:
|
||||
raise ValueError(f"Unerwarteter Grafana-Zeitausdruck: {expr!r}")
|
||||
sign = 1 if m.group(1) == "+" else -1
|
||||
return sign * int(m.group(2)) * _UNIT_DAYS[m.group(3)]
|
||||
|
||||
|
||||
def _load() -> dict:
|
||||
return json.loads(DASHBOARD.read_text())
|
||||
|
||||
|
||||
def test_dashboard_has_szenario_panel():
|
||||
titles = [p.get("title") for p in _load().get("panels", [])]
|
||||
assert "Szenario-Vergleich" in titles
|
||||
|
||||
|
||||
def test_time_window_covers_projection_horizon():
|
||||
d = _load()
|
||||
horizon = get_settings().horizon_days
|
||||
to_days = _relative_to_days(d["time"]["to"])
|
||||
assert to_days >= horizon, (
|
||||
f"time.to reicht nur {to_days} Tage in die Zukunft, deckt aber den "
|
||||
f"Projektionshorizont von {horizon} Tagen nicht ab -> Projektionskurve "
|
||||
f"wird abgeschnitten"
|
||||
)
|
||||
Reference in New Issue
Block a user