- Added new updates for Phase A and Phase B in `CLAUDE.md`, detailing the completion of Phase A and the introduction of enriched session metrics in the API response for `GET /api/activity`. - Enhanced the README to include references to new documentation files for scalar canon and composite metrics implementation. - Updated `ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` to reflect the current status of phases and added navigation rules for data access. - Improved `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` with links to new implementation concepts for composite metrics. - Refactored the activity router to integrate enriched session metrics into the activity list responses, ensuring a more comprehensive data presentation.
18 KiB
Activity Session Metrics: Composite-Daten (EAV) – Umsetzungskonzept
Stand: 2026-04-16
Status: Normatives Konzept zur nahtlosen Weiterarbeit durch Code-Agenten
Bezieht sich auf: ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md (§2.3–2.4, Phasen D–E), ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md, Issue #53 (Layer-1-Prinzip: Auswertungen nur über data_layer)
1. Ziel und Abgrenzung
1.1 Ziel
- Composite-Messgrößen (strukturierte Werte mit mehreren benannten Slots) werden wie normale Trainingsparameter im Katalog geführt, Kategorie-/Typ-Profilen zugeordnet und pro Session in der EAV-Tabelle persistiert.
- Persistenz: ein JSON-Dokument pro Session und
training_parameter_id(kanonisch JSONB), kompatibel mit der bestehenden „eine Zeile pro Parameter“-Semantik. - Import: CSV liefert typischerweise eine Spalte pro atomarem Slot; das Mapping verweist auf
(Parameter-Key, Slot-Key)(stabile Strings, nicht Spaltenreihenfolge). - Layer 1: liefert für Consumer weiterhin eine konsistente API: Rohdokument und optional aufgelöste Einzelwerte (flach oder namenspaced), ohne dass Charts/Platzhalter direkt JSON parsen müssen.
1.2 Nicht-Ziele (explizit)
- Kein „freies“ JSON-Schema im Admin ohne Archetyp-Bindung (verhindert Datenmüll und nicht validierbare Dokumente).
- Keine Abschwächung bestehender Skalar-Parameter (
integer,float,string,boolean): alle bisherigen Pfade bleiben gültig. - Kein Ersatz für
activity_log-Spine oder Session-Qualitätsblobs (evaluation, …).
1.3 Kompatibilitätsgarantie („keine Regression“)
| Bereich | Maßnahme |
|---|---|
| DB | Nur additive Migrationen; bestehende CHECK-Regeln für Skalare bleiben für Zeilen ohne Composite erhalten bzw. werden zu einer Oder-Verknüpfung erweitert (siehe §4). |
training_parameters |
Neuer data_type-Wert composite zusätzlich zu den vier bestehenden; bestehende CHECK-Constraint muss erweitert werden (Migration). |
activity_session_metrics |
Skalare Zeilen unverändert; Composite-Zeilen nutzen value_json (neu), alle value_* NULL. |
| Layer 1 | resolve_activity_attribute_schema, Merge, Replace: Composite erscheint als ein Schema-Eintrag; Lese-/Schreibpfade erweitern, nicht ersetzen. |
| CSV | Bestehende Map-Ziele auf Skalare/Registry unverändert; neue Zielnotation nur für Composites. |
| Admin | tcp/ttp-UI: gleiche Zuordnung wie heute; Zusatzfelder nur bei data_type === composite. |
1.4 Abgleich mit functional_concept_composite_data.md (fachliches Konzept)
Das fachliche Konzeptpapier (Composite Scalar/Layer-Trennung) und dieses Umsetzungskonzept sind vereinbar, wenn die Rollen klar getrennt bleiben:
| Thema | Fachliches Konzept (functional_concept_composite_data.md) |
Dieses Umsetzungskonzept (technisch) |
|---|---|---|
| Speicher in der DB | Einheitlicher Store; Composite = jsonb mit kleinem Basisschema (v, kind, domain, items, optional basis, meta) |
activity_session_metrics.value_json; CHECK Skalar vs. Composite |
| Technische Container | Genau vier kind-Werte: group_set, distribution_set, sequence_set, model_set |
Layer-1-Validierung muss diese Hülle durchsetzen; kein freies JSON ohne kind/v/items |
| „Archetypen“ | Fachliche Ausprägungen werden in Layer 2a aus L1-Objekten abgeleitet | Benannte Preset-/Validierungsprofile im Code (z. B. Zonenverteilung HF) sind kein zweites Persistenz-Schema: sie legen fest, welches der vier kind-Muster, welches domain, welche Item-Keys/Typen erlaubt sind — inkl. CSV-Slot-Mapping |
| Layer 1 | Validiert, minimal normalisiert, keine Scores/Bewertungen/KI-Texte | Validator + Merge + optional expand_* (technische Flachstellung für Consumer, z. B. param.slot → Skalar) |
| Layer 2 | Diagramme, Kennzahlen, KI-Platzhalter-Formulierung | unverändert; konsumiert L1 (und ggf. L2a) |
Konsequenz für die Registry: Statt „8 freie JSON-Archetypen“ implementiert die Code-Registry Validierungs-Presets, die alle auf die vier technischen kind-Formen abbilden. Die Tabelle in §3 beschreibt weiterhin fachlich benannte MVP-Anker — technisch übersetzen sie sich in (kind, domain, Item-Regeln, v).
Konsequenz für Platzhalter: Roh-JSON aus der DB nicht ungefiltert in Prompts; L2b nutzt L1/L2a-Aufbereitung (wie im fachlichen Konzept).
2. Begriffe
| Begriff | Bedeutung |
|---|---|
| Archetyp | Im Repo versionierte Strukturvorlage (erlaubte Slots, Typen, Pflichtfelder, Validator, Version). 7–8 Stück geplant; Erweiterung nur per Code-Release. |
| Slot | Benanntes Teilfeld innerhalb des Composite-Dokuments, z. B. z1_sec, z2_sec, avg_cadence. |
| Parameter-Instanz | Eine Zeile in training_parameters mit data_type = composite und Metadaten, welcher Archetyp gilt (siehe §5). |
| Dokument | Ein JSON-Objekt, das alle Slots abbildet; gespeichert in activity_session_metrics.value_json. |
3. Archetypen-Katalog (Planungsstand) — fachliche Namen → technische kind-Presets
Die konkrete Slot-Liste und Validierung wird im Code als Registry geführt (z. B. backend/data_layer/activity_composite_archetypes.py). Jedes Preset mappt auf genau eines von group_set | distribution_set | sequence_set | model_set und erfüllt das Basisschema aus functional_concept_composite_data.md §7.
Inhaltlich orientiert an ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md §2.4.
Beispielhafte fachliche MVP-Anker (8 Kandidaten; im Code als Preset-Key + kind/domain abbilden):
archetype_key (stabil) |
Kurzbeschreibung | Typische Slots (Beispiel) |
|---|---|---|
hr_zone_distribution |
Zeit-/Anteil je HF-Zone | z1_sec…z5_sec oder zones[] |
power_zone_distribution |
Leistungszonen | analog |
pace_band_profile |
Pace-Bänder / Histogramm | bucket-Struktur |
interval_block_summary |
Intervallblöcke aggregiert | blocks[] mit Dauer, Ziel, Ist |
event_marker_sequence |
Ereignisse mit Zeitstempel | events[] |
coupling_efficiency_profile |
Kopplungs-/Effizienzmetriken | sportabhängig |
model_parameter_profile |
Modell-/Schwellenparameter | key-value-ähnlich, validiert |
readiness_recovery_snapshot |
optional: kurzes Multi-Signal-Bundle | nur wenn fachlich gewünscht |
Regel: Jeder Archetyp hat version (Integer). Validator lehnt Dokumente mit falscher/fehlender Version ab oder migriert definiert (nur wenn spezifiziert).
4. Datenmodell-Erweiterungen
4.1 training_parameters
Migration (additiv):
CHECK (data_type IN (...))erweitern umcomposite.- Optional eigene Spalte
composite_archetype_keyVARCHAR(64)(NOT NULL wenndata_type = composite, sonst NULL) — oder ausschließlich invalidation_rulesspeichern (siehe unten).
Empfehlung: Spaltecomposite_archetype_key+composite_archetype_version INTfür einfache Admin-Queries und klare Semantik;validation_rulesfür archetyp-spezifische Feinheiten (z. B. erlaubte Zonenanzahl).
Konsistenz-Constraint (DB oder App):
- Wenn
data_type = composite:composite_archetype_keygesetzt,source_fieldtypischerweise NULL (keinactivity_log-Skalar-Shadowing). unitam Parameter: optional für „Anzeige-Einheit“ des Gesamtwerts oder leer; Slots haben Einheiten im Archetyp oder in Slot-Metadaten.
4.2 activity_session_metrics
Migration (additiv):
value_json JSONB NULL
CHECK-Constraint ersetzen/erweitern (Konzept):
- Modus Skalar: genau eine der Spalten
value_num,value_int,value_text,value_boolist NOT NULL;value_jsonIS NULL. - Modus Composite:
value_jsonIS NOT NULL; alle vier Skalar-Spalten IS NULL.
Damit bleibt die bestehende Semantik „eine Zeile = ein Parameter“ erhalten.
Kommentar: Tabelle trägt weiterhin „EAV“; Composites sind keine zusätzlichen Zeilen pro Slot.
4.3 Profil-Zuordnung (tcp / ttp)
Keine Tabellenänderung: training_category_parameter und training_type_parameter verweisen weiter nur auf training_parameter_id. Composite-Parameter verhalten sich wie Skalare in Bezug auf Zuordnung, sort_order, required, ui_group.
required: bedeutet „Dokument muss nach Validator vollständig sein“, nicht „jede CSV-Spalte muss in jeder Zeile vorkommen“.
5. Metadaten pro Composite-Parameter
Minimal in der DB (Beispiel):
| Feld | Zweck |
|---|---|
data_type |
composite |
composite_archetype_key |
Verweis auf Code-Registry |
composite_archetype_version |
Schema-Version |
validation_rules |
optional: Overrides (z. B. max_zones, sport-spezifisch) — nur was der Validator explizit auswertet |
Admin-API: bestehende Endpoints erweitern (Payload-Validierung): bei composite müssen Archetyp + Version gesetzt sein und in der Registry existieren.
6. Layer 1 – Kontrakt (activity_session_metrics.py + Helfer)
6.1 Schema-Auflösung
resolve_activity_attribute_schema liefert pro Composite einen Eintrag wie bei Skalaren, mit:
data_type: "composite"composite_archetype_key,composite_archetype_version(aus DB oder Join)- ggf.
composite_slot_catalog: nur wenn für Admin/UI gewünscht — alternativ separater EndpointGET .../composite-archetypes(read-only) aus Registry, um Bundle-Größe klein zu halten.
6.2 Lesen / Merge
fetch_activity_session_metrics: SELECT inkl.value_json.merge_column_backed_and_eav_metrics: Composites nur aus EAV (value_json), keinactivity_log-Shadowing (außer später explizit im Kanon — Standard: nein).- Ausgabe in
metrics-Liste: ein Eintrag pro Parameter mit z. B.
value: { "_composite": true, "document": { ... } }oder kanonisch getrennt:value_document+valuenull — festlegen beim Implementieren und in API-Doku halten; Empfehlung:value= deserialisiertes Objekt (dict) für Composites, damit Frontend dieselbe Struktur wie Speicher hat.
6.3 „Einzelwerte für Layer 1 / Issue 53“
Neue pure Funktion (kein SQL im Router), z. B.:
expand_composite_metrics_for_session(
schema: list[dict],
metrics: list[dict],
) -> dict[str, Any]
- Input: effektives Schema + gemergte Metriken.
- Output: flaches Dict
slot_path → typisierter Wert, z. B.
hr_zones.z1_sec → 1200, oder namespaced Keystraining_param_key.slot_keyzur Kollisionssicherheit. - Nutzung:
activity_metrics, Chart-Builder, später Platzhalter-Registry (data_layer_function), ohne JSON-Parsing in Layer 2.
Wichtig: Skalare Parameter erscheinen im expandierten Dict mit ihrem parameter_key wie bisher (kein Breaking Change für Consumer, die nur Skalare erwarten).
6.4 Validierung / Schreiben
replace_activity_session_metrics: Payload-Item für Composite:valueist Objekt (dict) oder JSON-String — Server normalisiert zu dict, validiert mit Archetyp-Validator, speichert alsvalue_json.upsert_session_metrics_from_csv_mapped: siehe §7 (Zusammenbau aus Partial-Updates pro Zeile).
Pflicht: Keine Teil-Updates in DB, die ein halbes Dokument hinterlassen, ohne Validierung — außer explizit als „Draft“-Modus spezifiziert (nicht Teil dieses Konzepts).
7. CSV / Universal Import
7.1 Map-Ziel-Notation
Stabiles Muster (Vorschlag, im Import-Modul zentral parsen):
"<parameter_key>.<slot_key>"
Beispiel: my_hr_zones.z1_sec → nach Import-Zusammenfügung in den Parameter my_hr_zones unter Slot z1_sec.
Alternative: explizites Präfix composite: in der Vorlage — nur nötig, wenn Kollisionen mit normalen Keys befürchtet werden; sonst Punkt-Notation reicht.
7.2 Executor-Flow (Konzept)
build_row_after_mappingliefert flache Keys inkl.param.slot.- Nach Schreiben von
activity_log/ Skalar-EAV: Composite-Accumulator proactivity_log_idundparameter_key:- Sammelt alle Slot-Werte aus der Zeile.
- Vor Commit der Zeile (oder am Ende der Datei — pro Zeile empfohlen, damit SAVEPOINT pro Row funktioniert):
- Dokument aus Slots bauen → Validator → Upsert
activity_session_metricsmitvalue_json.
- Dokument aus Slots bauen → Validator → Upsert
Teilbefüllung: Validator entscheidet (Archetyp: optional vs. required Slots). CSV darf nur Teilmengen liefern, wenn Archetyp erlaubt.
7.3 Typkonvertierung
Pro Slot im Archetyp: definierter skalarer Typ (float, int, …). Converter wie bei Skalaren (Executor / zentrale Converter), keine Parallel-Logik in Routern.
8. Admin-UI / Mapping-UX
8.1 Parameter anlegen
- Auswahl Datentyp „Composite“ → Dropdown Archetyp (aus Registry-API), Version readonly oder wählbar gemäß Policy.
- Rest wie Skalar: Name, Kategorie (
training_parameters.category), Aktiv-Flag.
8.2 Profil zuordnen
Unverändert: Kategorie-/Typ-Matrix wie heute.
8.3 Universal-CSV-Vorlage
- Mapping-Ziele: neben bisherigen Keys Slot-Ziele
parameter_key.slot_key. - UI-Gruppierung: optisch Composite-Block (wie in
ACTIVITY_PRODUCTION_ARCHITECTURE§2.5 angedeutet), um Verwechslung mit Spine-Spalten zu vermeiden.
9. API-Oberflächen (Erweiterungen)
| Bereich | Änderung |
|---|---|
GET /api/activity/{id} |
metrics enthält Composite-Werte als Objekt; schema kennzeichnet data_type: composite. |
PUT /api/activity/{id}/metrics |
Eintrag { parameter_key, value: { ... } } für Composites. |
Admin training-parameters |
Create/Update mit Composite-Feldern. |
| Optional | GET /api/admin/composite-archetypes |
Rückwärtskompatibilität: Clients, die nur Skalare senden, unverändert.
10. Frontend (Kurz)
ActivityPage/ Session-Metrik-Editor: fürdata_type === compositestrukturierte Teilfelder aus Slot-Katalog rendern (oder JSON-Editor nur als Entwickler-Fallback — Produkt: strukturierte Felder).- Sortierung/Gruppierung: bestehende
param_category/ui_group/sort_ordergelten unverändert.
11. Tests (pytest)
| Test | Beschreibung |
|---|---|
| Archetyp-Validator | gültige / ungültige Dokumente je Version |
| DB-Constraint | Skalar vs. Composite Ausschluss |
expand_composite_metrics_for_session |
flache Keys, Kollisionen |
| CSV-Zusammenbau | mehrere Spalten → ein value_json |
| Regression | bestehende test_activity_session_metrics.py unverändert grün halten |
12. Rollout-Phasen (operativ)
Stimmt mit ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md überein:
- Phase D – MVP: ein Preset (z. B. HF-Zonen →
distribution_set,domain: heart_rate), Migrationvalue_json+compositedata_type, Validator gegen Basisschema §7, Import 3–5 Spalten →items, GET/PUT, minimale Admin-Anbindung. - Phase E: weitere Presets /
kind-Varianten, Mapping-UX,expand_*für ausgewählte Layer-1-Consumer. - Phase F: Observability, Performance, Doku, Gitea-Issues schließen.
12.1 Empfohlene Reihenfolge: Skalar-Pipeline vs. Composite-Speicherung
Frage: Zuerst Skalar-EAV vollständig bis Platzhalter/Orchestrator abschließen, oder zuerst Composite-Speicherung?
| Option | Vorteil | Risiko |
|---|---|---|
| A: Nur Skalar zuerst (Kanon, L1-Härtung, Platzhalter aus EAV/L1) | Eine klare, end-to-end Referenzpipeline; weniger gleichzeitige Variablen | Composite-Datenstrome verzögern sich |
| B: Composite-Speicher zuerst | JSON landet früh in der DB | Platzhalter/Charts nutzen noch alte Pfade → zwei Wahrheiten (Detail-API vs. KI) bis L1 vereinheitlicht ist |
| C (Empfehlung): Skalar L1 + Platzhalter-Orchestrierung vor Composite-MVP, oder eng parallel mit gemeinsamem L1-Einstieg | get_activity_session_logical_unit / activity_metrics werden kanonisch; Platzhalter lesen dieselbe Schicht; Composite wird additiv (value_json + Validator + später expand_*) |
Erfordert kurze Planungsdisziplin: Composite-MVP ohne sofort alle KI-Platzhalter |
Konkrete Empfehlung
ACTIVITY_PRODUCTIONPhase A–B nicht überspringen: Kanon „eine Semantik / eine Quelle“ + alle relevanten Consumer über Layer 1 (mind. Session-Detail, Listen-Anreicherung, erste Platzhalter-Pfade für Skalare).- Dann Phase D (Composite-MVP): Migration + Speichern/Lesen mit Basisschema (
kind/items/…); L1 liefert dasselbe API-Objekt wie Skalare, nurvalueals strukturiertes Dokument. - Platzhalter für Composite: erst nach L1 liefert stabil
value_jsonund optionalexpand_composite_metrics_*— ein Orchestrator-Endpoint bzw. Resolver-Aufruf, der eine L1-Funktion nutzt, vermeidet doppelte Logik für Skalar vs. Composite.
Kurz: Composite persistieren kann kurz nach stabiler Skalar-Lese-/Merge-API folgen; KI/Platzhalter für Composite sinnvoll gemeinsam mit der erweiterten L1-Ausgabe bauen, nicht gegen eine noch nicht vereinheitlichte Skalar-Pipeline.
13. Checkliste für den nächsten Agenten
- Migration:
value_json, erweiterte CHECKs,training_parameters.data_type+ ggf.composite_archetype_*Spalten. - Registry-Modul: Archetypen + Versionen + Slot-Metadaten + Validator-Einstieg.
activity_session_metrics.py: Fetch/Merge/Replace/Upsert-Integration; keine Regression für Skalare.- Optional:
expand_composite_metrics_for_session+ erste Nutzung in einem Layer-1-Consumer (Tests). - CSV: Parser für
parameter_key.slot_key, Row-Accumulator, Fehler melden wie bestehender Import. - Admin-API + UI: Composite anlegen, tcp/ttp unverändert nutzbar.
- Doku: dieses Dokument mit festgelegter JSON-Beispielstruktur pro MVP-Archetyp ergänzen.
14. Referenzen
functional_concept_composite_data.md– fachliches Schichtenmodell, vier technischekind-Container, Basisschema JSONACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md– Zielbild, Phasen A–FACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md– Ist-Layer-1, APIsUNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md– Executor, Vorlagen- Migration
054_activity_session_metrics_eav.sql– Ist-Constraint Skalar - Migration
013_training_parameters.sql– Ist-data_type-Enum
Version: 1.1 · Abgleich mit fachlichem Konzept (§1.4, §3, §12.1); MVP auf distribution_set o. ä. konkretisieren.