# Activity Session Metrics (EAV) – Umsetzungs- & Agent-Guide **Stand:** 2026-04-14 **Status:** Kern-Backend (Migration 054, Layer 1, Admin- & Nutzer-API) umgesetzt; Admin-UI & CSV-Mapping folgen. **Ziel:** Sportspezifische **Attributprofile** (Kategorie + optional Trainingstyp-Override) administrierbar; Messwerte pro Session in **EAV**; **alle Auswertungen** sollen künftig über **Layer 1** (`data_layer`) laufen. **Zielarchitektur, Phasenplan (Produktionsreife):** [`ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md`](./ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md) – Kanon `activity_log`/EAV, Composites, Import, Layer 1/2, Reihenfolge A–F. **Kanon (Code):** `backend/data_layer/activity_data_canon.py` (Repo-Root) — CSV-Modul `activity` vs. EAV-primär; Migration **057**. --- ## 0. CSV-Import & Doppel-EAV (Kanon) - Vor Schreibzugriff: **`apply_activity_mapped_column_aliases`** kopiert Werte von `training_parameters.key` auf `source_field`-Spalte, wenn die Spalte leer ist (z. B. `avg_hr` → `hr_avg`). - **`activity_csv_registry_updates_from_mapped`** ist die **einzige** Quelle für `activity_log`-Kernspalten aus dem Mapping (Keys = `module_registry.activity.fields`); der Executor **liest** keine parallelen `mapped.get("hr_avg")`-Pfade mehr. - Plausible Zahlen: **`min`/`max`** in den Feld-Specs der Registry (keine HF-speziellen Key-Listen im Executor). - **`upsert_session_metrics_from_csv_mapped`** schreibt **keine** EAV-Zeilen für Parameter mit gesetztem **`source_field`** (kanonisch `activity_log`). - **Migration 058:** Entfernt bestehende redundante EAV-Zeilen für alle Parameter mit `source_field`. ## 1. Produktions-Migrationen (Pflicht) - **Nur additive Änderungen** bis zur Stabilisierung: neue Tabellen/Spalten **nullable**, kein `DROP COLUMN` / `DELETE` von Altbestand in derselben Story. - Neue Migrationen: **`backend/migrations/054_*.sql`** (nächste freie Nummer nach 053 einhalten). - **Prod-Checkliste vor Deploy:** 1. Backup / Snapshot der DB. 2. Migration auf **Kopie** der Prod-DB laufen lassen; Container-Start (`db_init`) verifizieren. 3. Stichprobe: `activity_log`-Zeilen unverändert; neue Tabellen leer oder nur Seed. - **Datenhaltung:** Bestehende Spalten in `activity_log` bleiben **Quelle für Alt-Daten**; EAV (`activity_session_metrics`) ist der **kanonische Ort für konfigurierte Session-Metriken**, sobald geschrieben. Backfill Altspalten → EAV ist **separater Schritt** (siehe §6). --- ## 2. Datenmodell (Ist nach Migration 054) | Tabelle | Zweck | |---------|--------| | `training_parameters` | Katalog messbarer Größen (`key`, `data_type`, `unit`, `validation_rules`, …) – bereits Migration 013; Admin-API ergänzt. | | `training_category_parameter` | Welche Parameter für welche **`training_types.category`** (z. B. `cardio`) gelten: `sort_order`, `required`, `ui_group`. | | `training_type_parameter` | Zusatzparameter oder **Overrides** pro **`training_types.id`**: `sort_order`, `required`, `ui_group` (NULL = von Kategorie erben). | | `activity_session_metrics` | EAV: `(activity_log_id, training_parameter_id)` eindeutig; genau eine Wertspalte `value_num` / `value_int` / `value_text` / `value_bool`. | | `activity_log` | **Neu:** `started_at`, `ended_at` (`TIMESTAMPTZ`, nullable) – für spätere Dedupe/Intervalle; **kein** Pflichtfeld in v1. | **Merge-Logik effektives Schema** (Layer 1, eine Funktion): 1. Kategorie ermitteln: aus Zeile `training_category` oder aus `training_types.category` via `training_type_id`. 2. Basis = alle Zeilen `training_category_parameter` für diese Kategorie, Join auf `training_parameters` (aktiv). 3. Für jeden Eintrag in `training_type_parameter` zum gewählten Typ: gleiche `training_parameter_id` → Overrides anwenden; nur im Typ vorhanden → anhängen. 4. Sortierung: `sort_order` aufsteigend, dann `key`. --- ## 3. Layer 1 – Kanonische Module | Modul | Pfad | Aufgabe | |-------|------|---------| | Session-Metriken & Schema | `backend/data_layer/activity_session_metrics.py` | `resolve_activity_attribute_schema`, `fetch_activity_session_metrics`, `replace_activity_session_metrics`, `get_activity_session_logical_unit`, `enrich_sessions_with_metrics`, `merge_column_backed_and_eav_metrics`. | **Spalten vs. EAV (Lesepfad):** `merge_column_backed_and_eav_metrics` / `get_activity_session_logical_unit` / `enrich_sessions_with_metrics` werten Parameter mit `source_field` **primär aus `activity_log`** aus; EAV ist Fallback (z. B. Legacy) oder für Parameter ohne Spalte. **Kein** automatischer Spalte→EAV-Schreib-Sync mehr in `run_activity_post_write_hooks` / Import-Hooks (vermeidet Doppelhaltung). **Regeln für Agenten:** - **Keine** zweite Implementierung derselben Merge- oder Validierungslogik in Routern. - Platzhalter / Charts, die Session-Details brauchen: **nur** diese Layer-1-Helfer erweitern oder aufrufen (z. B. `activity_metrics.get_training_sessions_recent_weeks_data` nutzt `enrich_sessions_with_metrics`). - Router: `get_db`, `get_cursor`, Auth; Business-Validierung delegieren an `activity_session_metrics`. --- ## 4. API (Ist / geplant) ### Admin (`require_admin`) | Methode | Pfad | Beschreibung | |---------|------|--------------| | GET/POST | `/api/admin/training-parameters` | Katalog lesen / Parameter anlegen | | PUT/DELETE | `/api/admin/training-parameters/{id}` | Aktualisieren / Soft-deaktivieren (`is_active`) | | GET | `/api/admin/training-category-parameters?category=` | Zuordnungen Kategorie | | POST | `/api/admin/training-category-parameters` | Zuordnung anlegen | | DELETE | `/api/admin/training-category-parameters/{id}` | Zuordnung entfernen | | GET | `/api/admin/training-type-parameters?training_type_id=` | Zuordnungen Typ | | POST | `/api/admin/training-type-parameters` | Zuordnung anlegen | | DELETE | `/api/admin/training-type-parameters/{id}` | Zuordnung entfernen | Router: `backend/routers/admin_training_parameters.py`, `backend/routers/admin_activity_attribute_profiles.py`. ### Nutzer (`require_auth`) | Methode | Pfad | Beschreibung | |---------|------|--------------| | GET | `/api/activity/{eid}` | Session-Kopf + `schema` + `metrics` (Layer 1) | | PUT | `/api/activity/{eid}/metrics` | **Voller Ersatz** der EAV-Metriken für diese Session (Liste `{parameter_key, value}`) | `ActivityEntry` unverändert für bestehende Create/Update-Routen; optionale Erweiterung um `started_at`/`ended_at` in späterem Schritt. --- ## 5. Agent-Checkliste (nächste Iterationen) Siehe **Phasen A–F** in [`ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md`](./ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md). Kurz: - [ ] **Phase A:** Kanon-Tabelle (eine Quelle pro Semantik). - [ ] **Phase B:** Lesepfad Layer 1 härten (Consumer-Audit). - [ ] **Phase C:** Schreibpfad: Doppelhaltung / Sync stufenweise abschalten. - [ ] **Phase D:** Composite-MVP (ein Archetyp E2E). - [ ] **Phase E:** Archetypen ausbauen + CSV-Typkonvertierung vollständig + Mapping-UX. - [ ] **Phase F:** Härtung Prod (Indizes, Observability, Doku). Legacy-Punkte: - [x] Admin-UI: `frontend/src/pages/AdminActivityAttributeProfilesPage.jsx`, Route `/admin/activity-attribute-profiles`, Admin-Nav-Gruppe „Trainingstypen“. - [x] `/activity` Frontend: Bearbeiten lädt `GET /api/activity/{id}`, dynamische Felder + `PUT /api/activity/{id}/metrics`. - [ ] Universal CSV: Mapping inkl. EAV/Composite-Ziele + Executor (fortlaufend). - [ ] Optional: Backfill / Abschluss `source_field`-Pfad nach Kanon (Phase A/C). - [ ] Dedupe Polar/Apple: nach stabilen `started_at`/`ended_at` + Policy (eigenes Issue). --- ## 6. Backfill (nicht in Migration 054) Separates Skript oder Migration **055+**, wenn fachlich freigegeben: - Pro aktivem `training_parameter` mit gesetztem `source_field`: Wert aus `activity_log` lesen, in EAV schreiben, wenn noch keine Zeile existiert. - Idempotent (`ON CONFLICT DO NOTHING` oder Upsert-Regel dokumentieren). --- ## 7. Automatische Tests (pytest, ohne DB) Aus **`backend/`**: ```bash python -m pytest tests/test_activity_session_metrics.py -v ``` Abdeckung: reine Merge-Logik (`merge_parameter_schema_rows`), Validierung (`_validate_single_value`), `resolve_activity_attribute_schema` mit Mock-Cursor, `enrich_sessions_with_metrics` mit Mock-Cursor. --- ## 8. Referenzen - Migration 013: `training_parameters` - Migration 004/014: `training_types`, `activity_log`-Erweiterungen - Pattern Admin-Katalog: `routers/admin_reference_value_types.py` - Platzhalter Session-JSON: `data_layer/activity_metrics.py` → `get_training_sessions_recent_weeks_data` --- **Version:** 1.1 · Bei Schema- oder API-Änderungen dieses Dokument und ggf. `CLAUDE.md` Kurzverweis aktualisieren.