# 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.

---

## 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`. |

**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)

- [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-Spalten → `training_parameters.key` + Schreiben in EAV (Executor).
- [ ] Optional: Backfill `activity_log.*` → `activity_session_metrics` nach `source_field`.
- [ ] 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.
