""" Base class for demo configurations. Each demo config file in this folder extends _BaseDemoConfig and provides idempotent load() and remove() methods for setting up / tearing down a complete demo environment (mandates, users, features, test data, etc.). Subclasses MUST also declare ``credentials`` so the SysAdmin who triggers a demo-load gets the initial username / password pair shown in the UI -- this avoids the "where do I find the password?" anti-pattern of having to grep the source code. """ import logging from abc import ABC, abstractmethod from typing import Any, Dict, List logger = logging.getLogger(__name__) class _BaseDemoConfig(ABC): """Abstract base for demo configurations.""" code: str = "" label: str = "" description: str = "" # Each entry describes one bootstrapped login that the demo creates. # Shape: {"role": "Demo-Sachbearbeiter", "username": "pwg.demo", # "email": "pwg.demo@poweron.swiss", "password": "pwg.demo.2026"} # Surfaced via GET /api/admin/demo-config and inside the load() summary # so the AdminDemoConfigPage can display it (no source-code grep needed). credentials: List[Dict[str, str]] = [] @abstractmethod def load(self, db) -> Dict[str, Any]: """Create all demo data (idempotent). Returns summary dict.""" raise NotImplementedError @abstractmethod def remove(self, db) -> Dict[str, Any]: """Remove all demo data. Returns summary dict.""" raise NotImplementedError def toDict(self) -> Dict[str, Any]: return { "code": self.code, "label": self.label, "description": self.description, "credentials": list(self.credentials or []), }