mitai-jinkendo/backend/dashboard_layout_schema.py
Lars f6c5f96768
All checks were successful
Deploy Development / deploy (push) Successful in 46s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
feat: Enhance Dashboard-Lab with widget catalog integration and layout updates
- Integrated a new API endpoint for fetching the widget catalog in the Dashboard-Lab.
- Updated the dashboard layout schema to utilize the widget catalog for dynamic widget management.
- Refactored DashboardLabPage and PilotVizPage to leverage the new widget rendering system.
- Removed deprecated widget metadata from the frontend, streamlining the widget management process.
- Bumped app_dashboard version to 1.1.0 to reflect the new features and improvements.
2026-04-07 11:47:16 +02:00

86 lines
2.5 KiB
Python

"""
Dashboard-Layout v1 (Nutzer-Lab): Validierung und Standard-Layout.
Erlaubte Widget-IDs und Standard-Reihenfolge: widget_catalog.WIDGET_CATALOG
"""
from __future__ import annotations
from typing import Any, Literal
from pydantic import BaseModel, Field, model_validator
from widget_catalog import ALLOWED_WIDGET_IDS, WIDGET_CATALOG
# Abwärtskompatibel (Tests importieren weiterhin aus diesem Modul)
__all__ = [
"ALLOWED_WIDGET_IDS",
"DashboardLayoutPayload",
"DashboardWidgetEntry",
"coalesce_effective_layout",
"default_layout_dict",
]
def default_layout_dict() -> dict[str, Any]:
return {
"version": 1,
"widgets": [{"id": e["id"], "enabled": True} for e in WIDGET_CATALOG],
}
class DashboardWidgetEntry(BaseModel):
id: str = Field(min_length=1, max_length=64)
enabled: bool = True
class DashboardLayoutPayload(BaseModel):
version: Literal[1] = 1
widgets: list[DashboardWidgetEntry] = Field(min_length=1, max_length=32)
@model_validator(mode="after")
def _validate_widgets(self) -> DashboardLayoutPayload:
ids = [w.id for w in self.widgets]
if len(ids) != len(set(ids)):
raise ValueError("Doppelte widget id")
bad = [i for i in ids if i not in ALLOWED_WIDGET_IDS]
if bad:
raise ValueError(f"Unbekannte Widget-IDs: {bad}")
if not any(w.enabled for w in self.widgets):
raise ValueError("Mindestens ein Widget muss aktiv sein")
return self
def to_stored_dict(self) -> dict[str, Any]:
return {
"version": self.version,
"widgets": [{"id": w.id, "enabled": w.enabled} for w in self.widgets],
}
def coalesce_effective_layout(raw: Any) -> tuple[bool, dict[str, Any]]:
"""
Returns (has_custom, effective_layout).
has_custom=True nur wenn DB-Wert vorhanden und gültig (v1).
"""
if raw is None:
return False, default_layout_dict()
parsed_obj: Any = raw
if isinstance(raw, str):
import json
try:
parsed_obj = json.loads(raw)
except json.JSONDecodeError:
return False, default_layout_dict()
if not isinstance(parsed_obj, dict):
return False, default_layout_dict()
try:
parsed = DashboardLayoutPayload.model_validate(
{
"version": parsed_obj.get("version", 1),
"widgets": parsed_obj.get("widgets", []),
}
)
return True, parsed.to_stored_dict()
except Exception:
return False, default_layout_dict()