"""A1 — Stage-bound practice exercises (Activity Set).""" from __future__ import annotations from datetime import datetime, timezone from typing import Any, Optional from services.recurring import ( create_recurring_element, list_recurring_for_initiative, update_recurring_element, ) # Starter exercise sets per stage title (Spagat reference path). STAGE_PRACTICE_SETS: dict[str, list[tuple[str, str]]] = { "Stufe 1 — Basis": [ ("Vorbeuge-Dehnung", "Täglich 3×30 Sek. — Hüfte und Beinrückseite"), ("Hüftöffner (Schmetterling)", "Täglich sitzend, leichter Druck"), ], "Stufe 2 — Aufbau": [ ("Seitlicher Spagat — links", "Täglich halten, optional Messwert (Grad)"), ("Seitlicher Spagat — rechts", "Täglich halten, optional Messwert (Grad)"), ("Aktive Vorbeuge", "Täglich 3× kurz halten"), ], "Stufe 3 — Ziel": [ ("Voller Spagat", "Täglich Zielposition — Messwert dokumentieren"), ], } def get_active_maturity_stage( *, tenant_id: str, initiative_id: str ) -> Optional[dict[str, Any]]: from services.roadmap import list_roadmap_items_for_initiative stages = [ item for item in list_roadmap_items_for_initiative( tenant_id=tenant_id, initiative_id=initiative_id ) if item.get("item_type") == "maturity_stage" and item.get("status") == "active" ] if not stages: return None stages.sort(key=lambda s: (s.get("sort_order", 0), s.get("title", ""))) return stages[0] def enforce_single_active_maturity_stage( cur, *, tenant_id: str, initiative_id: str, keep_active_id: str, ) -> None: """At most one maturity_stage may be active.""" cur.execute( """ UPDATE roadmap_items SET status = 'planned', updated_at = NOW() WHERE tenant_id = %s AND initiative_id = %s AND item_type = 'maturity_stage' AND status IN ('active', 'at_risk') AND id != %s """, (tenant_id, initiative_id, keep_active_id), ) def list_practices_for_stage( *, tenant_id: str, initiative_id: str, stage_id: str, ) -> list[dict[str, Any]]: items = list_recurring_for_initiative( tenant_id=tenant_id, initiative_id=initiative_id ) return [ item for item in items if item.get("roadmap_item_id") == stage_id ] def pause_all_practices( *, tenant_id: str, initiative_id: str, user_id: Optional[str] = None, ) -> list[str]: paused: list[str] = [] for item in list_recurring_for_initiative( tenant_id=tenant_id, initiative_id=initiative_id ): if item.get("status") == "active": update_recurring_element( tenant_id=tenant_id, recurring_id=item["id"], status="paused", user_id=user_id, ) paused.append(item["title"]) return paused def seed_practices_for_stage( *, tenant_id: str, initiative_id: str, stage: dict[str, Any], user_id: Optional[str] = None, ) -> list[dict[str, Any]]: """Idempotent: create missing exercises for a stage; activate existing.""" stage_id = stage["id"] stage_title = stage.get("title") or "" defs = STAGE_PRACTICE_SETS.get(stage_title) if not defs: defs = [ ( f"Übung — {stage_title}", f"Tägliche Routine für {stage_title}", ) ] existing = list_practices_for_stage( tenant_id=tenant_id, initiative_id=initiative_id, stage_id=stage_id, ) existing_by_title = {item["title"]: item for item in existing} created: list[dict[str, Any]] = [] now = datetime.now(timezone.utc) for title, description in defs: if title in existing_by_title: item = existing_by_title[title] if item.get("status") != "active": updated = update_recurring_element( tenant_id=tenant_id, recurring_id=item["id"], status="active", next_due_at=now, user_id=user_id, ) if updated: created.append(updated) else: created.append(item) continue row = create_recurring_element( tenant_id=tenant_id, initiative_id=initiative_id, title=title, description=description, status="active", interval_days=1, next_due_at=now, roadmap_item_id=stage_id, user_id=user_id, ) created.append(row) return created def activate_stage_practice_set( *, tenant_id: str, initiative_id: str, stage: dict[str, Any], user_id: Optional[str] = None, ) -> dict[str, Any]: paused = pause_all_practices( tenant_id=tenant_id, initiative_id=initiative_id, user_id=user_id ) exercises = seed_practices_for_stage( tenant_id=tenant_id, initiative_id=initiative_id, stage=stage, user_id=user_id, ) return { "stage_id": stage["id"], "stage_title": stage.get("title"), "paused_practices": paused, "active_practices": [e["title"] for e in exercises], "practice_ids": [e["id"] for e in exercises], }