- 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.
186 lines
13 KiB
Markdown
186 lines
13 KiB
Markdown
# 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. |
|