- Implemented a new SQL migration for wiki import tracking tables. - Created an import router for handling MediaWiki imports of exercises, skills, and methods. - Developed a Semantic MediaWiki API client for direct API interactions. - Added a mapper to convert SMW properties to local database fields. - Introduced background tasks for asynchronous import processing. - Implemented logging and error handling for import operations. - Added endpoints for previewing imports, checking import status, and managing import references.
455 lines
16 KiB
Markdown
455 lines
16 KiB
Markdown
# Fähigkeitsmatrix / Reifegradmodell – Specification
|
||
|
||
**Version:** 1.0
|
||
**Datum:** 2026-04-24
|
||
**Status:** DRAFT
|
||
**Autor:** Claude Code
|
||
**Basis:** shinkan_anforderungsdokument_entwurf.md §8 + DOMAIN_MODEL.md
|
||
|
||
---
|
||
|
||
## 1. Konzept
|
||
|
||
### 1.1 Was ist die Fähigkeitsmatrix?
|
||
|
||
Die Fähigkeitsmatrix verbindet **globale Fähigkeiten** (Skills) mit **kontextspezifischen
|
||
Reifegradmodellen**. Sie beantwortet pro Kontext die Frage:
|
||
|
||
> „Was muss ein Schüler auf Stufe X dieser Fähigkeit können, und welche Übungen trainieren ihn von Stufe A auf Stufe B?"
|
||
|
||
**Drei Ebenen:**
|
||
|
||
```
|
||
1. Globale Fähigkeit (skill)
|
||
"Distanzgefühl" – gilt überall, für alle Stile und Zielgruppen
|
||
|
||
2. Reifegradmodell (maturity_model)
|
||
"Karate / Shotokan / Breitensport" – definiert WIE VIELE Stufen es gibt
|
||
und wie die Stufen heißen
|
||
|
||
3. Modell-Fähigkeitsstufe (model_skill_level)
|
||
"Stufe 3 von Distanzgefühl im Shotokan-Breitensport-Modell" –
|
||
beschreibt KONKRET was auf Stufe 3 erwartet wird
|
||
```
|
||
|
||
### 1.2 Warum kontextabhängig?
|
||
|
||
Dieselbe Fähigkeit (z.B. „Distanzgefühl") hat in verschiedenen Kontexten
|
||
unterschiedliche Bedeutungen:
|
||
|
||
| Kontext | Stufe 3 Bedeutung |
|
||
|---------|-------------------|
|
||
| Karate / Shotokan / Breitensport | Distanzkontrolle in einfachen Partnerübungen |
|
||
| Karate / Shotokan / Leistungssport | Stabile Distanz im freien Kumite |
|
||
| Selbstverteidigung / Erwachsene | Sicherheitsabstand in Alltagssituationen einschätzen |
|
||
|
||
Ein Modell hat **variable Stufenanzahl** (3-7, nicht fest 5):
|
||
|
||
```
|
||
Shotokan Breitensport → 5 Stufen (Einsteiger bis Experte)
|
||
Kinder-Karate → 4 Stufen (Kinder-gerecht vereinfacht)
|
||
Leistungssport → 7 Stufen (feiner granuliert)
|
||
```
|
||
|
||
### 1.3 Abgrenzung zu exercise_skills
|
||
|
||
```
|
||
exercise_skills: Übung trainiert Fähigkeit mit Intensität und
|
||
suggested required/target Stufe
|
||
→ kontextunabhängig (gilt für alle Modelle)
|
||
|
||
model_skill_levels: Für dieses Modell bedeutet "Stufe 3" von Fähigkeit X:
|
||
"[konkrete Beschreibung]"
|
||
→ kontextspezifisch
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Datenmodell
|
||
|
||
### 2.1 Entity-Übersicht
|
||
|
||
```
|
||
focus_areas (global, existiert)
|
||
└─ style_directions (existiert, früher training_styles)
|
||
└─ target_groups (M:N, existiert)
|
||
|
||
maturity_models (NEU)
|
||
├─ focus_area_id FK (optional, NULL = gilt für alle Fokusbereiche)
|
||
├─ style_direction_id FK (optional)
|
||
├─ target_group_id FK (optional)
|
||
└─ level_count (INT) – wie viele Stufen hat das Modell?
|
||
|
||
model_levels (NEU) – Stufen-Definitionen
|
||
├─ maturity_model_id FK
|
||
├─ level_number (1 bis level_count)
|
||
├─ name (z.B. "Einsteiger", "Grundlagen", ...)
|
||
└─ description
|
||
|
||
model_skill_levels (NEU) – Was bedeutet Stufe X für Fähigkeit Y im Modell Z?
|
||
├─ maturity_model_id FK
|
||
├─ skill_id FK
|
||
├─ level_number (muss in maturity_model.level_count liegen)
|
||
├─ description (Pflicht-Text: was soll der Lernende können?)
|
||
├─ observable_criteria (Beobachtungskriterien für Trainer)
|
||
└─ example_exercises JSONB (Empfohlene Übungs-Typen, keine FK)
|
||
```
|
||
|
||
### 2.2 Vollständige Migration (019_maturity_models.sql)
|
||
|
||
```sql
|
||
-- Migration 019: Fähigkeitsmatrix / Reifegradmodelle
|
||
-- Autor: Claude Code
|
||
-- Datum: 2026-04-24
|
||
|
||
DO $$
|
||
BEGIN
|
||
|
||
-- ============================================================================
|
||
-- MATURITY MODELS (Reifegradmodelle)
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS maturity_models (
|
||
id SERIAL PRIMARY KEY,
|
||
name VARCHAR(200) NOT NULL,
|
||
description TEXT,
|
||
|
||
-- Kontext-Bindung (alle optional – NULL bedeutet "gilt allgemein")
|
||
-- Je mehr gesetzt, desto spezifischer das Modell
|
||
focus_area_id INT REFERENCES focus_areas(id) ON DELETE RESTRICT,
|
||
style_direction_id INT REFERENCES style_directions(id) ON DELETE RESTRICT,
|
||
target_group_id INT REFERENCES target_groups(id) ON DELETE RESTRICT,
|
||
|
||
-- Stufenanzahl (flexibel, nicht fest auf 5)
|
||
level_count INT NOT NULL DEFAULT 5 CHECK (level_count BETWEEN 3 AND 10),
|
||
|
||
-- Sichtbarkeit & Freigabe
|
||
-- 'draft': in Bearbeitung | 'active': in Nutzung | 'archived': nicht mehr aktiv
|
||
status VARCHAR(20) DEFAULT 'draft' CHECK (status IN ('draft', 'active', 'archived')),
|
||
|
||
-- Versionierung
|
||
version VARCHAR(20) DEFAULT '1.0',
|
||
|
||
-- Ownership
|
||
created_by INT REFERENCES profiles(id) ON DELETE SET NULL,
|
||
club_id INT REFERENCES clubs(id) ON DELETE SET NULL,
|
||
|
||
-- Import-Tracking (für Semantic MediaWiki Import)
|
||
import_source VARCHAR(50),
|
||
import_id VARCHAR(200),
|
||
|
||
-- Timestamps
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW(),
|
||
|
||
-- Constraint: Name + Kontext eindeutig
|
||
UNIQUE(name, focus_area_id, style_direction_id, target_group_id)
|
||
);
|
||
|
||
-- ============================================================================
|
||
-- MODEL LEVELS (Stufendefinitionen pro Modell)
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS model_levels (
|
||
id SERIAL PRIMARY KEY,
|
||
maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE,
|
||
level_number INT NOT NULL CHECK (level_number >= 1),
|
||
name VARCHAR(100) NOT NULL, -- z.B. "Einsteiger", "Grundlagen", "Aufbau"
|
||
description TEXT, -- Was zeichnet diese Stufe generell aus?
|
||
sort_order INT NOT NULL,
|
||
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
|
||
UNIQUE(maturity_model_id, level_number)
|
||
);
|
||
|
||
-- ============================================================================
|
||
-- MODEL SKILL LEVELS
|
||
-- Was bedeutet "Stufe X der Fähigkeit Y im Modell Z"?
|
||
-- ============================================================================
|
||
|
||
CREATE TABLE IF NOT EXISTS model_skill_levels (
|
||
id SERIAL PRIMARY KEY,
|
||
maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE,
|
||
skill_id INT NOT NULL REFERENCES skills(id) ON DELETE RESTRICT,
|
||
level_number INT NOT NULL CHECK (level_number >= 1),
|
||
|
||
-- Was wird auf dieser Stufe erwartet? (Pflichtfeld)
|
||
description TEXT NOT NULL,
|
||
|
||
-- Konkrete Beobachtungskriterien für Trainer
|
||
-- z.B. "Kann Distanz in ruhigen Partnerübungen halten (3/3 Versuchen)"
|
||
observable_criteria TEXT,
|
||
|
||
-- Empfohlene Übungs-Typen (keine FK, nur Hinweise)
|
||
-- z.B. {"types": ["Grundübung", "Partnerübung"], "max_duration_min": 15}
|
||
example_exercise_hints JSONB,
|
||
|
||
-- KI-generiert?
|
||
ai_generated BOOLEAN DEFAULT false,
|
||
|
||
created_at TIMESTAMP DEFAULT NOW(),
|
||
updated_at TIMESTAMP DEFAULT NOW(),
|
||
|
||
UNIQUE(maturity_model_id, skill_id, level_number),
|
||
|
||
-- level_number muss im Bereich des Modells liegen
|
||
-- (wird per Trigger oder Backend-Validierung geprüft)
|
||
CONSTRAINT ck_level_positive CHECK (level_number > 0)
|
||
);
|
||
|
||
-- ============================================================================
|
||
-- SKILL CATALOG: Felder für Matrix-Nutzung (falls noch nicht vorhanden)
|
||
-- ============================================================================
|
||
|
||
-- Fähigkeiten können einem bestimmten Fokusbereich "zugehören" (primär)
|
||
-- bleiben aber global nutzbar (secondary assignments über M:N)
|
||
ALTER TABLE skills
|
||
ADD COLUMN IF NOT EXISTS primary_focus_area_id INT REFERENCES focus_areas(id) ON DELETE SET NULL,
|
||
ADD COLUMN IF NOT EXISTS is_cross_domain BOOLEAN DEFAULT false; -- gilt für alle Fokusbereiche
|
||
|
||
-- ============================================================================
|
||
-- INDEXES
|
||
-- ============================================================================
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_maturity_models_focus ON maturity_models(focus_area_id);
|
||
CREATE INDEX IF NOT EXISTS idx_maturity_models_style ON maturity_models(style_direction_id);
|
||
CREATE INDEX IF NOT EXISTS idx_maturity_models_target ON maturity_models(target_group_id);
|
||
CREATE INDEX IF NOT EXISTS idx_maturity_models_status ON maturity_models(status);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_model_levels_model ON model_levels(maturity_model_id);
|
||
|
||
CREATE INDEX IF NOT EXISTS idx_model_skill_levels_model ON model_skill_levels(maturity_model_id);
|
||
CREATE INDEX IF NOT EXISTS idx_model_skill_levels_skill ON model_skill_levels(skill_id);
|
||
CREATE INDEX IF NOT EXISTS idx_model_skill_levels_combo ON model_skill_levels(maturity_model_id, skill_id);
|
||
|
||
-- ============================================================================
|
||
-- TRIGGERS
|
||
-- ============================================================================
|
||
|
||
DROP TRIGGER IF EXISTS maturity_models_update ON maturity_models;
|
||
CREATE TRIGGER maturity_models_update
|
||
BEFORE UPDATE ON maturity_models
|
||
FOR EACH ROW EXECUTE FUNCTION update_timestamp();
|
||
|
||
DROP TRIGGER IF EXISTS model_skill_levels_update ON model_skill_levels;
|
||
CREATE TRIGGER model_skill_levels_update
|
||
BEFORE UPDATE ON model_skill_levels
|
||
FOR EACH ROW EXECUTE FUNCTION update_timestamp();
|
||
|
||
RAISE NOTICE 'Migration 019 completed successfully (Maturity Models / Fähigkeitsmatrix)';
|
||
|
||
END $$;
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Beispiel-Daten
|
||
|
||
### 3.1 Beispiel-Modell: Karate Shotokan Breitensport (5 Stufen)
|
||
|
||
```sql
|
||
INSERT INTO maturity_models (name, focus_area_id, style_direction_id, target_group_id, level_count, status)
|
||
VALUES ('Karate Shotokan Breitensport', 1, 1, 1, 5, 'active');
|
||
|
||
-- Stufennamen
|
||
INSERT INTO model_levels (maturity_model_id, level_number, name, description, sort_order) VALUES
|
||
(1, 1, 'Einsteiger', 'Erste Begegnung – keine Vorkenntnisse erforderlich', 1),
|
||
(1, 2, 'Grundlagen', 'Grundprinzipien bekannt, in ruhigen Situationen anwendbar', 2),
|
||
(1, 3, 'Aufbau', 'Semi-intuitiver Einsatz, mit gelegentlicher Korrektur', 3),
|
||
(1, 4, 'Fortgeschritten', 'Intuitiver Einsatz auch unter Druck', 4),
|
||
(1, 5, 'Experte', 'Vollständig automatisiert, stabile Leistung auf Spitzenniveau', 5);
|
||
|
||
-- Fähigkeitsstufen: Distanzgefühl im Shotokan-Breitensport-Modell
|
||
INSERT INTO model_skill_levels (maturity_model_id, skill_id, level_number, description, observable_criteria)
|
||
VALUES
|
||
(1, 10, 1, 'Versteht das Konzept "Distanz" und kann erklären warum es wichtig ist.',
|
||
'Kann auf Frage benennen, was Maai bedeutet.'),
|
||
(1, 10, 2, 'Hält in langsamen, geführten Partnerübungen die Angriffsdistanz.',
|
||
'In 3 von 3 langsamen Wiederholungen korrekte Distanz eingenommen.'),
|
||
(1, 10, 3, 'Kontrolliert Distanz in mittlerer Geschwindigkeit bei Standardtechniken.',
|
||
'Trainer bewertet Distanzkontrolle mit "gut" in Kihon-Ippon-Kumite.'),
|
||
(1, 10, 4, 'Stabiler Einsatz im Jiyu-Ippon-Kumite, selten vom Partner überrascht.',
|
||
'In freiem Jiyu-Kumite hält Schüler Linie in >70% der Aktionen.'),
|
||
(1, 10, 5, 'Feingefühl für minimale Distanzveränderungen, taktischer Einsatz im Shiai.',
|
||
'Wettkampferfolge durch bewusstes Distanzmanagement nachweisbar.');
|
||
```
|
||
|
||
---
|
||
|
||
## 4. API-Endpoints
|
||
|
||
### 4.1 Übersicht
|
||
|
||
| Method | Endpoint | Beschreibung |
|
||
|--------|----------|--------------|
|
||
| GET | `/maturity-models` | Liste aller Modelle (mit Filter) |
|
||
| GET | `/maturity-models/{id}` | Modell-Detail mit Stufen + Skills |
|
||
| POST | `/maturity-models` | Neues Modell erstellen (Admin) |
|
||
| PUT | `/maturity-models/{id}` | Modell bearbeiten (Admin) |
|
||
| DELETE | `/maturity-models/{id}` | Modell löschen (Admin) |
|
||
| GET | `/maturity-models/{id}/skills` | Alle Fähigkeiten + Stufen dieses Modells |
|
||
| PUT | `/maturity-models/{id}/skills/{skill_id}/levels` | Stufen-Beschreibungen setzen (Admin) |
|
||
| GET | `/maturity-models/resolve` | Bestes Modell für Kontext finden |
|
||
|
||
### 4.2 `GET /maturity-models/resolve`
|
||
|
||
Findet das spezifischste Modell für einen gegebenen Kontext.
|
||
Wird von der KI und vom Frontend verwendet.
|
||
|
||
**Query Parameters:**
|
||
- `focus_area_id` (int, optional)
|
||
- `style_direction_id` (int, optional)
|
||
- `target_group_id` (int, optional)
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"model": {
|
||
"id": 1,
|
||
"name": "Karate Shotokan Breitensport",
|
||
"level_count": 5,
|
||
"match_specificity": "full"
|
||
},
|
||
"fallback_used": false
|
||
}
|
||
```
|
||
|
||
**Auflösungslogik (Most Specific First):**
|
||
1. Alle drei passen (focus + style + target) → `match_specificity: "full"`
|
||
2. focus + style passen → `match_specificity: "style"`
|
||
3. Nur focus passt → `match_specificity: "focus"`
|
||
4. Allgemeines Modell (alle FK = NULL) → `match_specificity: "generic"`
|
||
5. Kein Modell vorhanden → `match_specificity: null`, 404
|
||
|
||
---
|
||
|
||
## 5. Verbindung zu Übungen
|
||
|
||
### 5.1 Wie exercise_skills das Modell referenziert
|
||
|
||
`exercise_skills` speichert `required_level` und `target_level` als **Stufen-Namen**
|
||
(einsteiger/grundlagen/aufbau/fortgeschritten/experte), die modellunabhängig sind.
|
||
|
||
Das Frontend kann bei Bedarf die konkrete Modell-Beschreibung nachladen:
|
||
|
||
```
|
||
exercise_skills.required_level = "grundlagen"
|
||
→ GET /maturity-models/resolve?focus_area_id=1
|
||
→ Model: Karate Shotokan Breitensport (level_count=5)
|
||
→ model_levels: level_number=2, name="Grundlagen"
|
||
→ model_skill_levels für skill_id=10: "Hält in ruhigen Partnerübungen Distanz"
|
||
```
|
||
|
||
### 5.2 Darstellung in der Übungsdetail-Ansicht
|
||
|
||
```
|
||
Fähigkeiten
|
||
─────────────────────────────────────────────
|
||
★ Distanzgefühl (primär)
|
||
Voraussetzung: Grundlagen → Ziel: Aufbau
|
||
Intensität: Hoch
|
||
────────────────────────────────
|
||
Grundlagen: "Hält in ruhigen Partnerübungen
|
||
Distanz"
|
||
Aufbau: "Kontrolle in mittlerer
|
||
Geschwindigkeit"
|
||
[Kontext: Shotokan Breitensport ▼]
|
||
─────────────────────────────────────────────
|
||
◦ Reaktionsschnelligkeit (sekundär)
|
||
Voraussetzung: Einsteiger → Ziel: Grundlagen
|
||
Intensität: Mittel
|
||
```
|
||
|
||
**Kontext-Dropdown:** Trainer kann Modell wechseln um kontextspezifische Beschreibungen zu sehen.
|
||
|
||
---
|
||
|
||
## 6. KI-Unterstützung für Modell-Pflege
|
||
|
||
Die KI kann `model_skill_levels`-Beschreibungen vorschlagen.
|
||
|
||
**Prompt-Placeholder:**
|
||
- `{{skill_name}}` – Name der Fähigkeit
|
||
- `{{model_name}}` – Name des Reifegradmodells
|
||
- `{{level_count}}` – Anzahl der Stufen
|
||
- `{{level_names}}` – Namen der Stufen (z.B. "Einsteiger, Grundlagen, Aufbau...")
|
||
- `{{focus_area}}` – Fokusbereich
|
||
- `{{target_group}}` – Zielgruppe
|
||
|
||
**Endpoint:** `POST /maturity-models/{id}/ai/suggest-skill-levels`
|
||
|
||
```json
|
||
// Request
|
||
{
|
||
"skill_id": 10,
|
||
"regenerate_existing": false
|
||
}
|
||
|
||
// Response
|
||
{
|
||
"skill": {"id": 10, "name": "Distanzgefühl"},
|
||
"suggestions": [
|
||
{
|
||
"level_number": 1,
|
||
"level_name": "Einsteiger",
|
||
"description": "...",
|
||
"observable_criteria": "...",
|
||
"ai_generated": true
|
||
},
|
||
...
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Semantic MediaWiki Import
|
||
|
||
Die bestehende Fähigkeitsmatrix im SMW enthält bereits Stufen-Beschreibungen.
|
||
|
||
**Erweiterung der MEDIAWIKI_IMPORT_SPEC.md:**
|
||
|
||
| Wiki-Feld | Ziel-Tabelle | Transformation |
|
||
|-----------|-------------|----------------|
|
||
| SMW Property: `Stufe 1 Beschreibung` | `model_skill_levels.description` (level=1) | Direkt |
|
||
| SMW Property: `Stufe N Beschreibung` | `model_skill_levels.description` (level=N) | Direkt |
|
||
| SMW Property: `Modell` | `maturity_models.name` | Lookup/Create |
|
||
| SMW Property: `Fokusbereich` | `maturity_models.focus_area_id` | Name → ID |
|
||
|
||
**Import-Endpoint:** `POST /import/mediawiki/execute` mit `import_type: "maturity_model"`
|
||
|
||
---
|
||
|
||
## 8. Admin-Workflow
|
||
|
||
### 8.1 Modell anlegen (Admin)
|
||
|
||
```
|
||
1. Admin wählt: Fokusbereich + Stil + Zielgruppe
|
||
2. Admin legt Stufenanzahl + Stufennamen fest
|
||
3. Admin weist Fähigkeiten dem Modell zu (Pflicht-/Optional-Skills)
|
||
4. Für jede Fähigkeit × Stufe: Beschreibung eingeben
|
||
→ Optional: KI-Vorschlag generieren
|
||
5. Modell auf "active" stellen
|
||
```
|
||
|
||
### 8.2 SMW-Migration-Workflow
|
||
|
||
```
|
||
1. SMW-Import: Bestehende Modelle und Beschreibungen importieren
|
||
2. Review: Trainer prüft importierte Beschreibungen
|
||
3. KI-Ergänzung: Für fehlende Stufen KI-Vorschläge generieren
|
||
4. Freigabe: status → 'active'
|
||
```
|
||
|
||
---
|
||
|
||
**Version:** 1.0
|
||
**Datum:** 2026-04-24
|
||
**Status:** DRAFT
|
||
**Nächste Schritte:**
|
||
- SMW-Struktur analysieren (User legt Daten ins Repo)
|
||
- Migration 019 implementieren
|
||
- Admin-UI für Modellpflege spezifizieren (UI_COMPONENTS_SPEC.md ergänzen)
|