- Added support for the "activity_overview" widget in the dashboard configuration, allowing for chart_days validation. - Refactored validation logic to streamline error handling for both "body_overview" and "activity_overview" widgets. - Updated the widget catalog description to reflect the new configuration options. - Enhanced the DashboardLabPage to manage chart_days input for both widgets, improving user experience. - Bumped app_dashboard version to 1.3.0 to reflect these enhancements.
67 lines
2.4 KiB
Python
67 lines
2.4 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"})
|
|
|
|
|
|
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")
|
|
|
|
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}
|