mitai-jinkendo/backend/dashboard_widget_config.py
Lars de99856a28
Some checks failed
Deploy Development / deploy (push) Failing after 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
feat: Extend widget configuration for KPI board and enhance validation
- Added support for the "kpi_board" widget in the dashboard configuration, allowing for chart_days validation.
- Updated the widget catalog description to reflect the new configuration options for KPI tiles.
- Enhanced the DashboardLabPage to manage chart_days input for the KPI board, improving user experience.
- Introduced normalization functions for KPI kcal window days to maintain consistent behavior.
- Bumped app_dashboard version to 1.4.0 to reflect these enhancements.
2026-04-07 12:37:04 +02:00

69 lines
2.5 KiB
Python

"""
Pro-Widget-Konfiguration im Dashboard-Layout (v1).
Nur ausgewählte Widget-IDs dürfen nicht-leere config haben; bekannte Keys werden validiert.
"""
from __future__ import annotations
import json
import math
from typing import Any
MAX_WIDGET_CONFIG_JSON_BYTES = 1024
WIDGETS_ALLOWING_CONFIG: frozenset[str] = frozenset({"body_overview", "activity_overview", "kpi_board"})
def _config_json_size_bytes(config: dict[str, Any]) -> int:
return len(json.dumps(config, sort_keys=True, ensure_ascii=False).encode("utf-8"))
def validate_widget_entry_config(widget_id: str, raw: Any) -> dict[str, Any]:
if raw is None:
return {}
if not isinstance(raw, dict):
raise ValueError(f"Widget {widget_id}: config muss ein Objekt sein")
if _config_json_size_bytes(raw) > MAX_WIDGET_CONFIG_JSON_BYTES:
raise ValueError(f"Widget {widget_id}: config zu groß (max. {MAX_WIDGET_CONFIG_JSON_BYTES} Byte JSON)")
if not raw:
return {}
if widget_id not in WIDGETS_ALLOWING_CONFIG:
raise ValueError(f"Widget {widget_id}: keine Konfiguration unterstützt")
if widget_id == "body_overview":
return _validate_chart_days_only(raw, label="body_overview")
if widget_id == "activity_overview":
return _validate_chart_days_only(raw, label="activity_overview")
if widget_id == "kpi_board":
return _validate_chart_days_only(raw, label="kpi_board")
raise ValueError(f"Widget {widget_id}: keine Konfiguration unterstützt")
def _parse_chart_days(v: Any, label: str) -> int:
if isinstance(v, bool):
raise ValueError(f"{label}: chart_days muss ganze Zahl sein")
if isinstance(v, float):
if not math.isfinite(v):
raise ValueError(f"{label}: chart_days muss ganze Zahl sein")
if abs(v - round(v)) > 1e-9:
raise ValueError(f"{label}: chart_days muss ganze Zahl sein")
return int(round(v))
if isinstance(v, int):
return v
raise ValueError(f"{label}: chart_days muss ganze Zahl sein")
def _validate_chart_days_only(raw: dict[str, Any], *, label: str) -> dict[str, Any]:
allowed = frozenset({"chart_days"})
unknown = set(raw) - allowed
if unknown:
raise ValueError(f"{label}: unbekannte config-Felder: {sorted(unknown)}")
if "chart_days" not in raw:
return {}
v = _parse_chart_days(raw["chart_days"], label)
if v < 7 or v > 90:
raise ValueError(f"{label}: chart_days muss zwischen 7 und 90 liegen")
return {"chart_days": v}