mitai-jinkendo/.claude/docs/technical/ACTIVITY_COMPOSITE_METRICS_IMPLEMENTATION_CONCEPT.md
Lars fa3e66fb31
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 18s
feat: Update activity documentation and enhance API responses with session metrics
- 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.
2026-04-17 12:55:12 +02:00

18 KiB
Raw Permalink Blame History

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.32.4, Phasen DE), 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). 78 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_secz5_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):

  1. CHECK (data_type IN (...)) erweitern um composite.
  2. Optional eigene Spalte composite_archetype_key VARCHAR(64) (NOT NULL wenn data_type = composite, sonst NULL) — oder ausschließlich in validation_rules speichern (siehe unten).
    Empfehlung: Spalte composite_archetype_key + composite_archetype_version INT für einfache Admin-Queries und klare Semantik; validation_rules für archetyp-spezifische Feinheiten (z.B. erlaubte Zonenanzahl).

Konsistenz-Constraint (DB oder App):

  • Wenn data_type = composite: composite_archetype_key gesetzt, source_field typischerweise NULL (kein activity_log-Skalar-Shadowing).
  • unit am 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_bool ist NOT NULL; value_json IS NULL.
  • Modus Composite: value_json IS 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 Endpoint GET .../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), kein activity_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 + value null — 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 Keys training_param_key.slot_key zur 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: value ist Objekt (dict) oder JSON-String — Server normalisiert zu dict, validiert mit Archetyp-Validator, speichert als value_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)

  1. build_row_after_mapping liefert flache Keys inkl. param.slot.
  2. Nach Schreiben von activity_log / Skalar-EAV: Composite-Accumulator pro activity_log_id und parameter_key:
    • Sammelt alle Slot-Werte aus der Zeile.
  3. 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_metrics mit value_json.

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ür data_type === composite strukturierte Teilfelder aus Slot-Katalog rendern (oder JSON-Editor nur als Entwickler-Fallback — Produkt: strukturierte Felder).
  • Sortierung/Gruppierung: bestehende param_category / ui_group / sort_order gelten 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:

  1. Phase D MVP: ein Preset (z.B. HF-Zonen → distribution_set, domain: heart_rate), Migration value_json + composite data_type, Validator gegen Basisschema §7, Import 35 Spalten → items, GET/PUT, minimale Admin-Anbindung.
  2. Phase E: weitere Presets / kind-Varianten, Mapping-UX, expand_* für ausgewählte Layer-1-Consumer.
  3. 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

  1. ACTIVITY_PRODUCTION Phase AB nicht überspringen: Kanon „eine Semantik / eine Quelle“ + alle relevanten Consumer über Layer 1 (mind. Session-Detail, Listen-Anreicherung, erste Platzhalter-Pfade für Skalare).
  2. Dann Phase D (Composite-MVP): Migration + Speichern/Lesen mit Basisschema (kind/items/…); L1 liefert dasselbe API-Objekt wie Skalare, nur value als strukturiertes Dokument.
  3. Platzhalter für Composite: erst nach L1 liefert stabil value_json und optional expand_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 technische kind-Container, Basisschema JSON
  • ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md Zielbild, Phasen AF
  • ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md Ist-Layer-1, APIs
  • UNIVERSAL_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.