""" 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}