# Gewichtetes Fähigkeiten-Scoring (Phase 3) **Stand:** 2026-05-20 **Status:** Variante A (regelbasiert) umgesetzt — **v1.3** (Peer-Kontext getrennt + Listen-Filter) **Modul:** `backend/skill_scoring.py`, Router `backend/routers/skill_profiles.py` ## Ziel Trainer wählen **Schwerpunkt-Fähigkeiten** und finden passende **Bausteine** für die Trainingsplanung: - **Trainingsmodule** — wiederverwendbare Übungsfolgen - **Rahmenprogramme** — Programme mit Zielen und Session-Slots - **Regressionspfade** (Progressionsgraphen) — Übungsketten Das Scoring beantwortet: *Wie stark trainiert dieser Baustein eine Fähigkeit?* und *Wie stark ist er im Vergleich zu anderen **sichtbaren** Bausteinen **desselben Typs**?* ## Fachliche Kernregel: Peer-Kontext (nicht vermischen) | Planungs-Artefakt | Vergleichsgruppe (`universal_percent`) | |-------------------|----------------------------------------| | Trainingsmodul | nur andere **sichtbare Module** | | Rahmenprogramm | nur andere **sichtbare Rahmenprogramme** | | Regressionspfad | nur andere **sichtbare Pfade** | **Nicht** verglichen werden: - Module vs. Rahmenprogramme vs. Pfade (kein Mix) - Artefakte anderer Vereine, auf die der Nutzer keinen Planungszugriff hat **Sichtbarkeit:** `library_content_visibility_sql` — private, vereinsinterne und offizielle Inhalte gemäß Mandant/Rolle, analog zu anderen Bibliothekslisten. ## Datenquellen | Artefakt | Übungen aus | |----------|-------------| | Rahmenprogramm (gesamt) | Alle Blueprint-`training_units` der Slots → `training_unit_section_items` | | Rahmenprogramm (pro Slot) | Blueprint einer Session | | Trainingsmodul | `training_module_items` (nur `item_type = exercise`) | | Progressionsgraph | `from_exercise_id` + `to_exercise_id` je Kante (Vorkommen zählt) | Fähigkeiten je Übung: `exercise_skills` → `skills` (nur `status = active`). ## Gewichtungsformel (v1.1 / v1.2) Pro **Übungsvorkommen** (eine Zeile im Ablauf / Modul / Kanten-Endpunkt): 1. **Basis-Minuten** = `planned_duration_min` der Position, sonst Default (Einheit/Modul: 8 Min, Graph: 10 Min). 2. Pro verknüpfte Fähigkeit der Übung: - `Beitrag = Basis-Minuten × Anzahl Vorkommen × Link-Faktor` - **Link-Faktor** = Intensität × Stufen-Faktor ### Intensität (Nutzeneinschätzung, UI-Feld) | Wert | Faktor | |------|--------| | niedrig | 0,85 | | mittel / leer | 1,0 | | hoch | 1,2 | ### Stufen-Spanne (`required_level` → `target_level`, UI „von/bis“) Kanonische Slugs: basis … optimierung (1–5). Fehlen beide: Faktor 1,0. - **Spanne** = Anzahl Stufen von „von“ bis „bis“ (1–5) - **Mittelpunkt** = durchschnittliche Stufe - Faktor ≈ `(0,92 + 0,04 × Spanne) × (0,95 + 0,025 × Mittelpunkt)` → typisch 0,96–1,20 ### Bewusst nicht im Scoring | Feld | Grund | |------|--------| | `is_primary` | Perspektivabhängig; bleibt in Übungs-UI, fließt nicht ins Profil ein | | `development_contribution` | Legacy-DB-Feld, in UI nicht gepflegt | ## Aggregierte Metriken | Feld | Bedeutung | |------|-----------| | `weight` / `score` | Absolutes **Trainingsgewicht** (gewichtete Minuten) — über alle Fähigkeiten eines Artefakts vergleichbar | | `share_percent` | Anteil am `total_weight` **innerhalb dieses Artefakts** (summiert 100 %) — sekundär | | `by_main_category[]` | Je Unterkategorie `top_skill` (stärkste Fähigkeit nach Gewicht) | | `universal_percent` | Anteil am **Maximum derselben Fähigkeit im Peer-Kontext** (max. 100 %) | | `is_club_best_for_skill` | Stärkster sichtbarer Peer für diese Fähigkeit (★ in UI) | | `club_best` | Referenz-Peer (Titel, Typ, Gewicht) — **Legacy-Name**, fachlich Peer-Best | ### Berechnung `universal_percent` ``` effective_ref = max(max_weight_in_peer_corpus(skill_id), eigenes_gewicht) universal_percent = min(100, weight / effective_ref × 100) ``` Corpus je Typ: `compute_planning_corpus_by_type()` scannt sichtbare Artefakte getrennt nach `framework_program`, `training_module`, `progression_graph`. Discovery-Sortierung nutzt **`match_score`** (= Summe absoluter Gewichte der gewählten Fähigkeiten), nicht den Plan-internen Anteil. Discovery verwendet typ-getrennte Referenz (`fw_ref`, `mod_ref`, `graph_ref`). ## API | Methode | Pfad | Beschreibung | |---------|------|--------------| | GET | `/api/training-framework-programs/{id}/skill-profile` | `overall` + `slots[]` mit je `profile`; `reference_scale` (Peer-Kontext Rahmenprogramme) | | GET | `/api/training-modules/{id}/skill-profile` | `overall`; `reference_scale` (Peer-Kontext Module) | | GET | `/api/exercise-progression-graphs/{id}/skill-profile` | `overall`; `reference_scale` (Peer-Kontext Pfade) | | POST | `/api/skill-profiles/batch-summaries` | Kompakte Profile für Listen; Body: `frameworkProgramIds`, `trainingModuleIds`, …; Response: `summaries`, `reference_scale_by_type`, `club_best_by_skill` | | GET | `/api/skill-discovery/suggestions?skill_ids=1,2,3` | Ranking sichtbarer Artefakte; Query `types`, `limit` | Zugriff: `get_tenant_context` + `library_content_visibility_sql` wie Parent-Artefakt. ### `reference_scale` / `reference_scale_by_type` ```json { "scope": "planning_peer", "artifact_type": "training_module", "artifacts_scanned": 12, "skills_in_corpus": 34, "description": "Prozent = Anteil am stärksten sichtbaren Eintrag unter Trainingsmodulen …" } ``` ## UI ### Bearbeitung (Vollprofil) | Ort | Panel | `artifactType` | |-----|-------|----------------| | Rahmenprogramm bearbeiten | Fähigkeiten-Schwerpunkte (+ Sessions) | `framework_program` | | Trainingsmodul bearbeiten | Fähigkeiten im Modul | `training_module` | | Progressionsgraph | Fähigkeiten entlang des Pfads | `progression_graph` | Anzeige: Top je Kategorie (Editor) oder alle Fähigkeiten (Modal). Hinweise nennen Peer-Kontext explizit (z. B. „72 % Rahmenpr.“). ### Listen & Filter (UX wie Übungsliste) | Liste | Filter | |-------|--------| | Rahmenprogramme (`/planning/framework-programs`) | Suche, Katalog (Fokus/Trainingsart/Zielgruppe), Session-Dauer, **Fähigkeiten** (`SkillTreeMultiSelect`), Mindest-% im Peer-Kontext, Sortierung nach Stärke | | Trainingsmodule (`/planning/training-modules`) | Suche, **Fähigkeiten** (+ Min-%, Sortierung) | - **Filter-Button** mit Badge, entfernbare **Chips**, Einstellungen im **Modal** (`PlanningArtifactFilterModal`) - KPI-Kacheln: Top-Fähigkeit **je Unterkategorie** mit Score + Peer-% - Vollprofil-Modal: `SkillProfileFullModal` mit `displayMode=full` Profil wird nach Speichern neu geladen (`skillProfileTick` in Editoren). ### Discovery **Fähigkeiten-Seite → Planungs-Vorschläge:** Multi-Select + API `/api/skill-discovery/suggestions` (optional Filter `types`). ## Frontend-Module (Auswahl) | Pfad | Rolle | |------|--------| | `frontend/src/components/planning/PlanningArtifactFilterModal.jsx` | Filter-Modal | | `frontend/src/components/planning/PlanningSkillFilterSection.jsx` | Fähigkeiten-Block im Modal | | `frontend/src/utils/planningArtifactFilterChips.js` | Chip-Labels + Entfernen | | `frontend/src/utils/frameworkProgramListHelpers.js` | Client-Filter Rahmenprogramme | | `frontend/src/utils/trainingModuleListHelpers.js` | Client-Filter Module | | `frontend/src/components/skills/SkillProfileCompact.jsx` | KPI-Kacheln in Listen | | `frontend/src/components/SkillTreeMultiSelect.jsx` | Baumauswahl (Portal-Dropdown in Modals) | ## Grenzen / später - Kein DB-Cache (`skill_profile_json`) — on-the-fly; bei >50 Artefakten pro Typ serverseitiger Index/Caching - Entwicklungsziele am Rahmenkopf bleiben Freitext (kein Scoring) - KI-Zusammenfassung (Variante B) nicht Teil von v1.0 - Trainings**einheiten** (Kalender) optional als nächste Erweiterung - Filter-Persistenz („Als Standard speichern“) wie bei Übungen — noch nicht für Planungslisten - Fähigkeiten-Filter im Dialog **Planung → Rahmen übernehmen** — Katalog ja, Skill-Filter optional nachziehen - API-Feldnamen `club_*` / `skillMinClubPercent` — technische Altlast, semantisch Peer-Kontext ## Tests - `backend/tests/test_skill_scoring.py` — Multiplikator, Aggregation, Match-Score, Cap 100 % - **Offen:** dedizierte Tests für `compute_planning_corpus_by_type` (Typ-Trennung) ## Verweise | Dokument | Inhalt | |----------|--------| | `functional/DOMAIN_MODEL.md` | Domänenabschnitt Planungs-Fähigkeiten-Profil | | `docs/FACHLICHE_NUTZERFUNKTIONEN.md` | Nutzerüberblick Listen/Filter | | `docs/HANDOVER.md` | Handover-Abschnitt Phase 3 | | `technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` | Sichtbarkeit / Mandant |