123 lines
6.2 KiB
Markdown
123 lines
6.2 KiB
Markdown
|
|
# 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)
|
|||
|
|
|
|||
|
|
- [ ] Admin-UI: Matrix Kategorie / Trainingstyp ↔ Parameter.
|
|||
|
|
- [ ] `/activity` Frontend: dynamische Felder aus `GET /api/activity/{id}`.
|
|||
|
|
- [ ] 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.
|