"""The widget templates a Prometheus widget can draw with. Bundled templates ship in plugins/prometheus/widgets/*.yaml, read once and read-only; their key is "bundled:". Templates an admin copies in join them with the template library (#5692). Steward fetches none from anywhere (System 181's ruling). """ from __future__ import annotations from dataclasses import dataclass, field from functools import lru_cache from pathlib import Path from sqlalchemy import select from .models import PrometheusTarget from .template_format import Problem, Template, TemplateError, check BUNDLED = Path(__file__).parent / "widgets" @dataclass class Entry: key: str name: str bundled: bool text: str template: Template | None = None problems: list[Problem] = field(default_factory=list) def _entry(key: str, fallback_name: str, text: str, bundled: bool) -> Entry: try: template = check(text) except TemplateError as exc: return Entry(key, fallback_name, bundled, text, None, exc.problems) return Entry(key, template.name, bundled, text, template) @lru_cache(maxsize=1) def bundled() -> tuple[Entry, ...]: return tuple(_entry(f"bundled:{p.stem}", p.stem, p.read_text(encoding="utf-8"), True) for p in sorted(BUNDLED.glob("*.yaml"))) async def entries(db) -> list[Entry]: """Every template, by name.""" return sorted(bundled(), key=lambda e: e.name.lower()) async def find(db, key: str) -> Entry | None: return next((e for e in await entries(db) if e.key == key), None) # ── The widget's settings ──────────────────────────────────────────────────── async def target_options(db) -> list[dict]: rows = (await db.execute(select(PrometheusTarget.id, PrometheusTarget.name) .order_by(PrometheusTarget.name))).all() return [{"value": tid, "label": name} for tid, name in rows] async def template_options(db) -> list[dict]: return [{"value": e.key, "label": f"{e.name} (built in)" if e.bundled else e.name} for e in await entries(db) if e.template is not None] async def default_title(db, config: dict) -> str | None: """A new widget is named after its template: "Fabled Archives".""" entry = await find(db, str(config.get("template") or "")) return entry.name if entry and entry.template else None