"""Operating Context read model — AP2.3a.""" from __future__ import annotations from typing import Any, Optional from psycopg2.extras import RealDictCursor from db import get_connection from entity_archetypes.registry import get_ui_profile_json, resolve_ui_profile from services.initiatives import get_initiative from services.steering_context import ensure_steering_context, get_steering_context from steering.methods.registry import get_method def _load_ui_profile_from_db(archetype_key: str) -> Optional[dict[str, Any]]: conn = get_connection() try: with conn.cursor(cursor_factory=RealDictCursor) as cur: cur.execute( """ SELECT ui_profile_json FROM entity_archetypes WHERE archetype_key = %s """, (archetype_key,), ) row = cur.fetchone() if not row: return None profile = row.get("ui_profile_json") if isinstance(profile, dict) and profile: return dict(profile) return None except Exception: return None finally: conn.close() def _resolve_ui_profile(archetype_key: str) -> dict[str, Any]: db_profile = _load_ui_profile_from_db(archetype_key) if db_profile: return db_profile seed_profile = get_ui_profile_json(archetype_key) if seed_profile: return seed_profile return resolve_ui_profile(archetype_key) def _resolve_data_slices( *, ui_profile: dict[str, Any], method_key: str, ) -> list[str]: """Schnittmenge Archetyp-Default ∩ Method-Slices (Phase E erweitert).""" archetype_slices = ui_profile.get("dataSlices") or [] method = get_method(method_key) if method and getattr(method, "data_slices", None): method_slices = set(method.data_slices) return [s for s in archetype_slices if s in method_slices] return list(archetype_slices) def _method_capabilities(method_key: str) -> dict[str, Any]: method = get_method(method_key) if not method: return { "lifecycle_steps": [], "next_action_strategy_key": "default", } return { "lifecycle_steps": list(method.default_lifecycle_steps), "next_action_strategy_key": method.next_action_strategy_key, } def get_operating_context( *, tenant_id: str, initiative_id: str ) -> Optional[dict[str, Any]]: initiative = get_initiative(tenant_id=tenant_id, initiative_id=initiative_id) if not initiative: return None steering = get_steering_context(tenant_id=tenant_id, initiative_id=initiative_id) if not steering: steering = ensure_steering_context( tenant_id=tenant_id, initiative_id=initiative_id ) archetype_key = initiative["archetype_key"] ui_profile = _resolve_ui_profile(archetype_key) method_key = steering["method_key"] metadata = steering.get("lifecycle_metadata") or {} if isinstance(metadata, str): metadata = {} method_profile_key = metadata.get("method_profile_key") data_slices = _resolve_data_slices(ui_profile=ui_profile, method_key=method_key) return { "initiative_id": initiative_id, "archetype_key": archetype_key, "method_key": method_key, "method_profile_key": method_profile_key, "ui_profile": ui_profile, "data_slices": data_slices, "method_capabilities": _method_capabilities(method_key), }