shinkan-jinkendo/.claude/docs/technical/TRAINING_FRAMEWORK_SPEC.md
Lars b054c642a3
Some checks failed
Deploy Development / deploy (push) Successful in 33s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / playwright-tests (push) Failing after 40s
chore: update training framework specifications and versioning
- Incremented version to 0.8.8 and updated database schema version to 20260505035.
- Added new entity `training_framework_programs` to manage training frameworks, including goals and slots.
- Enhanced `training_plan_templates` with a visibility attribute and backfilled existing data.
- Updated API to support CRUD operations for training frameworks, ensuring proper authorization similar to existing planning libraries.
- Revised documentation in DOMAIN_MODEL.md, TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md, and TRAINING_FRAMEWORK_SPEC.md to reflect these changes.
2026-05-05 08:41:43 +02:00

186 lines
13 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Trainingsrahmenprogramm — Technische Spezifikation
**Status:** Zwischenstand dokumentiert · **Stand:** 2026-05-05
**Bindendes Fachkonzept / Entscheide:** `.claude/docs/functional/TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` (CURR‑001 bis CURR‑013)
**Relevant für nächsten Schritt:** CURR‑002 **(2)** Trainingsplanung / Rahmen über mehrere Einheiten — der hier dokumentierte **Progressionsgraph Stufe 1** ist bewusst **unterstützend**, keine Pflicht für Slot-Zuordnungen (**CURR‑013**).
---
## 1. Abgrenzung zu anderen Dokumenten
| Dokument | Rolle · warum **nicht** hier hineinmischen |
|----------|--------------------------------------------|
| `EXERCISES_DATABASE_FINAL.md`, `EXERCISES_ARCHITECTURE.md`, `EXERCISES_API_SPEC.md` | **Übungskatalog** inkl. Varianten-Progression **innerhalb einer Übung** (Migration 014). Kanten **zwischen** Übungen siehe **§3**. |
| `DATABASE_SCHEMA.md` | **Nachgeordnete** Übersicht: Migrationshistorie und Tabellenliste; Detail-DDL primär **hier §2–§3** + SQL unter `backend/migrations/`. |
| `functional/DOMAIN_MODEL.md` | Fachliche Begriffe; Kurzverweis auf Progressionsgraph ergänzt. |
| `TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` | **Was** und **warum** (Modus A/B, Governance, CURR‑Tabelle). |
**Konsequenz:** Diese Datei bleibt der **technische Arbeitspool** für Rahmenprogramm Stufe 1–2. Abschnitt **§4** beschreibt explizit den **aktuellen Produktfreigabe-Umfang** und **bekannte Lücken** (damit Trainingsplanung weiter gebaut werden kann ohne falscher Erwartung an „Alternative‑Pakete“ in der UI).
---
## 2. Rahmenprogramm (CURR‑002 Stufe 2) — Checkliste & technische Ausarbeitung
### 2.0 Technische Entscheidung: eine Tabelle + `plan_mode` (Modus A | B)
**Entscheid:** **Eine** Hauptentität `training_framework_programs` mit **`plan_mode` ∈ {`concrete`, `library`}** statt zweier getrennter Tabellentypen.
**Begründung:** Gleiche Lebenszyklus‑ und CRUD‑Form (Header, Ziele, Slots, Übungen); die fachliche Unterscheidung A/B lässt sich mit **CHECK**- und API‑Regeln ausdrücken (`library` ⇒ `group_id IS NULL`, keine `training_unit_id` an Slots), ohne doppelte Router/Joins. Zwei physische Typen würden ohne Mehrwert Polymorphismus in der API erzwingen oder eine künstliche Supertyp‑Tabelle nach sich ziehen.
**Abgrenzung Konzept §6:** Dokumentiert hier; Funktionskonzept kann auf diesen Abschnitt verweisen.
### 2.1 Checkliste (Abhak-Stand)
- [x] **Entität(en):** eigene Bibliotheks-Entität `training_framework_programs` (**CURR‑009**); `training_plan_templates` unverändert **eine‑Einheit‑Mikrovorlage** (**C5**).
- [x] **Modus A vs. B:** ein Datensatz + `plan_mode` + Nullables (**§2.0**); `library` erzwingt `group_id` NULL; **`training_plan_template_id` pro Slot** — **deferred** (Technical: MVP ohne Spalte, bis Nutzen geklärt, **CURR‑010**).
- [x] **Zielliste:** `training_framework_goals`, API erzwingt **≥ 1** Ziel beim Anlegen/Ersetzen (**CURR‑011**).
- [x] **Slots:** `training_framework_slots` mit **`sort_order`**, optional **Titel/Notizen**; Übungen über **`training_framework_slot_exercises`** (Sortierung **`order_index`**, optional **`exercise_variant_id`**).
- [x] **Progressionsgraph:** Stufe 1 besteht (**§3–§4**); **kein Pflichtbezug** pro Slot (**CURR‑013**).
- [x] **Konkretkontakt Modus A:** optional **`training_unit_id`** pro Slot; bei gesetzter Rahmen-**`group_id`** muss die Einheit zur gleichen Gruppe gehören. **Live‑Writes** zurück in die Bibliotheksvorlage: **nicht** vorgesehen (**CURR‑006**).
- [x] **Instanziierung (Modus B):** Persistenz der Vorlage (**MVP**); Bulk-Anlage von **`training_units`** aus dem Rahmen — **Ausbauschritt**/zweiter PR (**CURR‑012** C4a/b).
- [x] **Governance neue Objekte:** `visibility`, `club_id`, `created_by` wie Progressionsgraph (**CURR‑005**). **`training_plan_templates.visibility`** nachgezogen in derselben Migration **035** mit Backfill **`club`** (**CURR‑007**, **CURR‑008**; frühe Installationen).
- [x] **REST Rahmenprogramm:** `/api/training-framework-programs` (**§2.3**); Progressions‑API weiter **§3.3**.
### 2.2 DDL‑Skizze (Migration **035**, Kurzüberblick)
```sql
-- Header
training_framework_programs (
id, title NOT NULL, description,
plan_mode NOT NULL CHECK (IN 'concrete','library'),
group_id FK training_groups NULL,
planned_period_start, planned_period_end NULL, -- reine Meta-/UI‑Hilfe
visibility NOT NULL, club_id, created_by,
CHECK ((plan_mode = 'library' AND group_id IS NULL) OR plan_mode = 'concrete'),
timestamps + update_trigger
)
-- Ziele (≥1 über API beim Speichern)
training_framework_goals (
id, framework_program_id FK CASCADE, sort_order UNIQUE per framework,
title, notes
)
-- Slots
training_framework_slots (
id, framework_program_id FK CASCADE, sort_order UNIQUE per framework,
title, notes, training_unit_id FK training_units SET NULL
)
-- Stückliste pro Slot (Übung → FK CASCADE beim Löschen der Übung)
training_framework_slot_exercises (
id, slot_id FK CASCADE,
exercise_id FK exercises CASCADE,
exercise_variant_id FK exercise_variants NULL,
order_index UNIQUE per slot
)
```
**Löschkaskaden:** löschen eines **Rahmens** ⇒ Ziele + Slots ⇒ Slot‑Übungen (alles **`ON DELETE CASCADE`** vom Rahmen über Slots). löschen eines **`training_unit`** ⇒ FK am Slot **`SET NULL`**. löschen einer **Übung** ⇒ Zeilen in **`training_framework_slot_exercises`** entfallen (`CASCADE`).
**Deferred (nicht im MVP):** `training_plan_templates.id` FK je Slot (**CURR‑010** Optional).
### 2.3 REST‑Überblick (`router` `training_framework_programs`)
| Methode | Pfad | Zweck |
|---------|------|--------|
| GET | `/training-framework-programs` | Liste; Admin/Superadmin alle, sonst eigene (`created_by`); Aggregation `goals_count`, `slots_count` |
| GET | `/training-framework-programs/{id}` | Detail inkl. `goals[]`, `slots[]` mit jeweils `exercises[]` (inkl. Titel‑Joins) |
| POST | `/training-framework-programs` | Neu; **Pflicht:** `title`, **`plan_mode`**, **`goals`** (≥ 1 Eintrag mit `title`); optional `slots` |
| PUT | `/training-framework-programs/{id}` | Header‑Felder; optional volles Ersetzen von **`goals`** und/oder **`slots`** wie bei Vorlagen‑Sektionen |
| DELETE | `/training-framework-programs/{id}` | Rahmen löschen (**CASCADE** Kinder) |
**AuthZ:** Schreibzugriff nur mit **`_has_planning_role`** (wie `training_plan_templates`); Lesen/Ändern/Löschen: **Admin/Superadmin** oder **Ersteller** (`created_by`).
**Payload‑Hinweise (JSON):**
- `goals`: `[{ sort_order?, title, notes? }, …]` — **`sort_order`** default Reihenfolge im Array
- `slots`: `[{ sort_order?, title?, notes?, training_unit_id?, exercises: [{ exercise_id, exercise_variant_id?, order_index? }] }, …]`
- Bei **`library`:** **`training_unit_id`** an Slots → **400**; nach Wechsel auf **`library`** werden bestehende Slot-Verknüpfungen zu **`training_units`** geleert.
**Minimal‑UI:** im Lieferumfang dieser Iteration nicht enthalten (**OpenAPI `/docs`** / Postman); siehe Funktionskonzept §6.
---
## 3. Progressionsgraph Übung → Übung (implementierter Stand)
### 3.1 Abgrenzung
- **Zwischen Übungen:** gerichtete Kanten auf Ebene **`exercises`** mit optionalen Endpunkten auf konkreten **`exercise_variants`** (Knoten = „Übung“ oder „Übung · Variante“). Migrationen **032–034**.
- **Innerhalb einer Übung:** Reihenfolge / Progressionsmetadaten der Varianten unverändert über **`exercise_variants`** (Migration **014**) — nicht duplizieren.
AuthZ analog **`training_plan_templates`**: Graph nur für **Admin/Superadmin** oder **Ersteller** (`created_by`); Anlegen neuer Graphen mit **`_has_planning_role`**.
### 3.2 Migrationen & Schema (Kurz)
| Mig. | Inhalt |
|------|--------|
| **032** | `exercise_progression_graphs` (Name, Beschreibung, **`visibility`**, **`club_id`**, **`created_by`**); `exercise_progression_edges` (`graph_id`, von/nach Übung, `edge_type` VARCHAR Default `next_exercise`). FK CASCADE zu Graph und Übungen. |
| **033** | `exercise_progression_edges.notes` (freier Text / „Entwicklungsziel“ pro Kante). |
| **034** | `from_exercise_variant_id`, `to_exercise_variant_id` (nullable, FK `exercise_variants`, CASCADE). CHECK: gleiche Übung nur mit **zwei verschiedenen Varianten**. UNIQUE-Index über Graph + Endpunkte inkl. `COALESCE(variant_id,0)` + `edge_type`. |
Kantentypen in Produktnutzung: **`next_exercise`** (Nachfolger), **`sibling`** (Schwester / gleiche „Entwicklungslage“, semantisch oft Paar — weiterhin eine gerichtete Kante in DB).
Listenqueries liefern Join‑Felder **`from_exercise_title`**, **`to_exercise_title`**, **`from_variant_name`**, **`to_variant_name`**.
### 3.3 REST (`/api`, Router `exercise_progression_graphs.py`)
| Methode | Pfad | Zweck |
|---------|------|--------|
| GET | `/exercise-progression-graphs` | Liste (+ `edges_count`); Admin sieht alle, sonst nur eigene. |
| GET | `/exercise-progression-graphs/{id}` | Detail; `?include_edges=true` |
| POST | `/exercise-progression-graphs` | Graph anlegen |
| PUT | `/exercise-progression-graphs/{id}` | Metadaten |
| DELETE | `/exercise-progression-graphs/{id}` | Graph + Kanten |
| GET | `/exercise-progression-graphs/{id}/edges` | Kanten; Query optional `from_exercise_id`, `to_exercise_id` |
| POST | `/exercise-progression-graphs/{id}/edges` | Einzelkante; Duplikat/Constraint → **409** |
| POST | `/exercise-progression-graphs/{id}/edges/sequence` | **Bulk:** `{ steps: [{ exercise_id, variant_id? }, …], segment_notes?: [...] }` — nur **`next_exercise`**, Transaktion alle oder keine Zeile |
| PUT | `/exercise-progression-graphs/{id}/edges/{edge_id}` | z. B. **`notes`** |
| DELETE | `/exercise-progression-graphs/{id}/edges/{edge_id}` | eine Kante |
| POST | `/exercise-progression-graphs/{id}/edges/delete-batch` | `{ edge_ids: [...] }` — z. B. gesamte sichtbare „Reihe“ löschen |
### 3.4 Frontend (Stand Code)
- **`ExercisesListPage`:** Tabs **Liste** · **Progressionsgraphen** → **`ExerciseProgressionGraphPanel`**.
- **`ExerciseFormPage`** (nur Edit): eingeklappter Block **Progressionsgraph** mit Kontext „diese Übung“ + Filter „nur betroffene Kanten“.
- Panel‑Funktionen: **Sequenz‑Editor** (mehrere Schritte → ein Bulk‑Speichern), zusammengefasste **Reihen‑Lesart** für `next_exercise`, eigene Liste für **Schwestern**, Einzelkantenbereich, Tab **Alle Kanten (Tabelle)**.
- API‑Client: `frontend/src/utils/api.js` (`createExerciseProgressionSequence`, `deleteExerciseProgressionEdgesBatch`, …).
---
## 4. Zwischenstand für Produkt / Trainingsplanung (bewusste Grenzen)
**Freigabe:** Der beschriebene Stand gilt als **ausreichend**, um mit dem **Trainingsplanungsmodul** und später Rahmen/Slots (**CURR‑002 (2)**) weiterzuarbeiten — ohne dass Pflicht zur Pflege komplexer Graph‑Strukturen entsteht (**CURR‑013**).
**Was gut nutzbar ist**
- Lineare **Reihen** mehrerer Übungen (bzw. Varianten‑Knoten) über **Sequenz‑API** bzw. Sequenz‑UI.
- **Nachfolger‑Lesart** als zusammenhängende Kette in der Übersicht.
- **Schwester‑Kanten** als eigene Liste (Alternative gleicher „Stufe“ zwischen zwei Knoten).
- **Einzelkanten** für Sonderfälle und Verzweigungen.
**Was noch nicht „ein Knopf“ ist (bekannt)**
- **Parallele gleichwertige Alternativen** („Paket B bestehend aus mehreren Übungen als echte Alternative zu Paket A“) sind **nicht** als erste‑Klass‑UX modelliert: mehrere Nachfolger aus einem Knoten sind technisch möglich (mehrere `next_exercise`‑Kanten), aber **keine** dedizierte Gruppe „Alternativ‑Set“. Pflege kann mehrzeilig und koplastisch wirken.
- **Visualisierung echter Bäume** (Join‑Points, mehrere ausgehende Pfeile in einem Bild) ist nur eingeschränkt über Reihen‑Zusammenfassung + Tabelle abbildbar.
**Nächste sinnvolle Ausbaustufen** (Backlog Graph‑UX, nicht Blocker Planung)
- Semantik **`alternative_group_id`** oder **Hyperkanten** (ein UX‑Schritt legt mehrere Kanten mit gemeinsamer Gruppe an).
- Komfort beim Pflegen **symmetrischer Schwestern** (ein Klick für zwei Richtungen / Dedupe).
- Karten-/Baum‑Layout statt nur Zeilen.
Details weiterhin Diskussionsgrundlage in `TRAINING_CURRICULUM_AND_GOVERNANCE_CONCEPT.md` §6 (Kantentypen).
---
## 5. Changelog (Dokument)
| Datum | Änderung |
|-------|----------|
| 2026-05-05 | **CURR‑002 (2):** §2 Rahmenprogramm — Entscheid **eine Tabelle + `plan_mode`**, DDL‑Skizze, REST‑Überblick; Migration **035**; `training_plan_templates.visibility`. |
| 2026-04-30 | **Zwischen-Doku:** §3 auf Migrationen 032–034 + API **sequence/delete-batch** + Frontend erweitert; **§4** Produktfreigabe vs. Lücken (parallele Alternativen); Changelog §5. |
| 2026-04-30 | §3: erste Fassung Migration 032 + REST‑Basis (CURR‑002 (1)). |
| 2026-04-28 | Erstanlage Stub mit Checkliste. |