Erste Version Platzhalter EAV #86
|
|
@ -115,7 +115,9 @@ _Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Sp
|
|||
| `TRAINING_TYPE_PROFILES_TECHNICAL.md` | Trainingsprofile technisch |
|
||||
| `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` | Universal CSV: Registry, Executor, Vorlagen, Agent-Checkliste |
|
||||
| `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` | Session-Metriken EAV, Attributprofile, Layer-1, Prod-Migration |
|
||||
| `ACTIVITY_COMPOSITE_METRICS_IMPLEMENTATION_CONCEPT.md` | Composite-Metriken in EAV (JSONB), Archetypen, CSV-Slots, Layer-1-Expand, Migration/Test-Checkliste |
|
||||
| `ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` | **Zielarchitektur** Aktivität (Spine/EAV/Composites/Import/Layer 1–2) + **Phasenplan A–F** Produktionsreife |
|
||||
| `ACTIVITY_SCALAR_KANON_TABLE.md` | **Skalar-Kanon** Aktivität (eine Semantik → eine Quelle); Phase A |
|
||||
| *(Code)* `backend/data_layer/activity_data_canon.py` | **Kanon** activity CSV-Modul vs. EAV-primär; Legacy-Lesefallback |
|
||||
| `V9D_PHASE2_VITALS_SLEEP.md` | v9d Vitalwerte/Schlaf (Release-Bezug) |
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,317 @@
|
|||
# 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):**
|
||||
|
||||
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):**
|
||||
|
||||
```text
|
||||
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.:
|
||||
|
||||
```text
|
||||
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):
|
||||
|
||||
```text
|
||||
"<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` | Registry export für UI (Keys, Slot-Liste, Version). |
|
||||
|
||||
**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 3–5 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 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**).
|
||||
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 A–F
|
||||
- `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.
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
# Aktivität: Zielarchitektur & Phasenplan (Produktionsreife)
|
||||
|
||||
**Stand:** 2026-04-14
|
||||
**Stand:** 2026-04-16
|
||||
**Status:** Normative Zielrichtung für `activity_log`, EAV, Composites, Import, Layer 1/2.
|
||||
**Ergänzt:** `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` (Ist-Modell, APIs, Tests).
|
||||
**Ergänzt:** `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` (Ist-Modell, APIs, Tests).
|
||||
**Phase A:** abgeschlossen — Kanon-Tabelle [`ACTIVITY_SCALAR_KANON_TABLE.md`](./ACTIVITY_SCALAR_KANON_TABLE.md).
|
||||
**Phase B:** in Arbeit — Consumer-Audit und Lesepfad-Härtung (siehe §4 Phase B).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -39,6 +41,18 @@ KI / UI / Export
|
|||
- **Orchestrator:** Schreibpfad, Konsistenz nach Write (kein zweites „Lesen der Wahrheit“ neben Layer 1; optional nur Post-Write-Hooks).
|
||||
- **Resolver:** für Aktivität **kein** direkter DB-Zugriff; nur Aufruf von Layer 1.
|
||||
|
||||
### 2.1a Navigationsregel: wo nachsehen (ohne Datei-Zwang)
|
||||
|
||||
Die **physische** Aufteilung ist dreigeteilt: **`activity_log`** (Spine + heiße Spalten), **EAV-Skalare** (`activity_session_metrics` + numerische/textuelle `value_*`), **EAV-Composites** (ein Parameter, Nutzlast z. B. JSON/JSONB im EAV-Datensatz). **Fachlich** soll nach außen **eine homogene Session-Sicht** entstehen — Consumer sollen nicht selbst entscheiden, aus welcher Tabelle/Welche Form ein Wert kommt.
|
||||
|
||||
| Thema | Wo nachsehen (Ist; Ziel: Schnittstelle stabil, Datei optional splittbar) |
|
||||
|--------|--------------------------------------------------------------------------|
|
||||
| **Homogene Session lesen** (Merge Spalte + EAV-Skalare + später Composite-Payload) | `data_layer/activity_session_metrics.py` — u. a. `get_activity_session_logical_unit`, `enrich_sessions_with_metrics`, `merge_column_backed_and_eav_metrics` |
|
||||
| **Schreiben / Import / API-Persistenz** | `data_layer/activity_persistence_orchestrator.py` (+ Router) |
|
||||
| **Berechnungen, Aggregationen, Scores** über viele Sessions oder Zeitfenster | `data_layer/activity_metrics.py` — arbeitet auf der **vereinheitlichten** Session-Datenlage (über die Read-Funktionen oben), nicht durch paralleles Mergen der drei Quellen im Caller |
|
||||
|
||||
**Hinweis:** Orchestrator und Read-Merge **müssen nicht** in derselben Datei stehen. Entscheidend ist, dass es **genau eine dokumentierte Read-Fassade** für „Session inkl. aller effektiven Metriken“ gibt und Layer‑1‑Berechnungen **nur** diese Fassade (oder deren Ergebnisstrukturen) nutzen. Eine spätere Umbenennung oder Auslagerung in z. B. `activity_read_gateway.py` ändert die Rolle nicht — nur der **eine Einstieg** muss in dieser Doku und im Code auffindbar bleiben.
|
||||
|
||||
### 2.2 `activity_log` (Spine + heiße Skalare)
|
||||
|
||||
**Maschinenlesbarer Kanon:** `backend/data_layer/activity_data_canon.py` (`ACTIVITY_MODULE_REGISTRY_FIELD_KEYS`, `ACTIVITY_EAV_PRIMARY_PARAMETER_KEYS`, Legacy-Lesefallback für EAV-primäre Parameter).
|
||||
|
|
@ -97,17 +111,17 @@ KI / UI / Export
|
|||
|
||||
Phasen sind **sequentiell** wo „Abhängigkeit“ steht; Teile können parallel (z. B. UI-Polish) laufen, wenn der Kanon steht.
|
||||
|
||||
### Phase A – Kanon & Abschaltplan (Grundlage)
|
||||
### Phase A – Kanon & Abschaltplan (Grundlage) ✅
|
||||
|
||||
**Inhalt:** Schriftliche **Kanon-Tabelle**: pro Messgröße genau eine Quelle (`activity_log` | `eav_scalar` | `eav_composite` | `session_quality`). Liste der Keys, für die **Sync/Spiegelung** endet.
|
||||
|
||||
**Definition of Done:** Review im Team; Referenz in diesem Dokument oder Verweis auf Gitea-Kommentar; keine Code-Änderung zwingend.
|
||||
|
||||
**Erster konkreter Schritt:** Kanon-Tabelle als Checkliste (Spreadsheet oder Gitea-Issue) – **eine Zeile pro Semantik**.
|
||||
**Erledigt (2026-04-16):** [`ACTIVITY_SCALAR_KANON_TABLE.md`](./ACTIVITY_SCALAR_KANON_TABLE.md) — eine Semantik pro Zeile, verlinkt mit `activity_data_canon.py` und Merge-Logik.
|
||||
|
||||
---
|
||||
|
||||
### Phase B – Lesepfad härten (Layer 1)
|
||||
### Phase B – Lesepfad härten (Layer 1) 🔄
|
||||
|
||||
**Inhalt:** Sicherstellen, dass **alle** relevanten Consumer (mind. `activity_metrics` für Platzhalter/Charts, Activity-Detail-API) dieselbe Merge-/Fallback-Logik nutzen; Legacy-Spalten nur noch als dokumentierter Fallback bis Enddatum.
|
||||
|
||||
|
|
@ -115,6 +129,19 @@ Phasen sind **sequentiell** wo „Abhängigkeit“ steht; Teile können parallel
|
|||
|
||||
**Abhängigkeit:** Phase A für „welche Spalten noch Fallback sind“.
|
||||
|
||||
**Audit-Stand (2026-04-16):**
|
||||
|
||||
| Consumer | Nutzt Layer-1-Merge (`enrich_sessions_with_metrics` / `get_activity_session_logical_unit`) | Anmerkung |
|
||||
|----------|---------------------------------------------------------------------------------------------|-----------|
|
||||
| `GET /api/activity/{eid}` | ✅ `get_activity_session_logical_unit` | Referenz-Detail |
|
||||
| `GET /api/activity` (Liste) | ✅ seit 2026-04-16 `enrich_sessions_with_metrics` auf jeder Listen-Antwort | vorher nur Roh-Spalten |
|
||||
| `activity_metrics.get_activity_detail_data` | ✅ | Platzhalter `{{activity_detail}}` |
|
||||
| `activity_metrics.get_training_sessions_recent_weeks_data` | ✅ | KI-Kontext |
|
||||
| `placeholder_resolver` (Aktivität) | ✅ nur `activity_metrics` | kein paralleles SQL |
|
||||
| `get_activity_summary_data` | n. a. | rein aggregiert (`SUM`/`COUNT`), keine Session-EAV |
|
||||
| `routers/charts.py` (A1–A8) | Spalten-Aggregate | bewusst: Dauer/RPE/HF aus **`activity_log`**-Kanon; kein EAV-Join nötig für definierte Charts |
|
||||
| `activity_stats` (`GET /api/activity/stats`) | nur Spalten | Kacheln: `kcal`/`duration` aus Kernspalten |
|
||||
|
||||
---
|
||||
|
||||
### Phase C – Schreibpfad entschlacken
|
||||
|
|
@ -153,19 +180,21 @@ Phasen sind **sequentiell** wo „Abhängigkeit“ steht; Teile können parallel
|
|||
|
||||
## 5. Was zuerst?
|
||||
|
||||
**Sofort (nächster Schritt):** **Phase A – Kanon-Tabelle** (eine Semantik pro Zeile, eine Quelle). Ohne diese Entscheidung riskieren Phase B/C falsche Abschaltungen.
|
||||
**Erledigt:** Phase A — [`ACTIVITY_SCALAR_KANON_TABLE.md`](./ACTIVITY_SCALAR_KANON_TABLE.md).
|
||||
|
||||
Direkt danach: **Phase B** (Lesepfad), dann **Phase C** (Schreibpfad), dann **Phase D** (ein Composite-MVP).
|
||||
**Aktuell:** Phase B fortsetzen (weitere Consumer prüfen: Export, Import-Vorschau, ggf. zukünftige Chart-Metriken aus EAV), dann **Phase C** (Schreibpfad), dann **Phase D** (Composite-MVP).
|
||||
|
||||
---
|
||||
|
||||
## 6. Referenzen
|
||||
|
||||
- `ACTIVITY_SCALAR_KANON_TABLE.md` – **Skalar-Kanon** (Phase A)
|
||||
- `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` – Tabellen, APIs, Tests, Backfill-Hinweise
|
||||
- `ACTIVITY_COMPOSITE_METRICS_IMPLEMENTATION_CONCEPT.md` – Composite-EAV (JSONB), Archetypen, Import-Slots, Layer-1-Expand, Migrations- und Testplan
|
||||
- `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` – Executor, Vorlagen, Typen
|
||||
- `PLACEHOLDER_REGISTRY_FRAMEWORK.md` – Layer-2-Registrierung
|
||||
- `functional/DATA_ARCHITECTURE.md` – fachliche Datenarchitektur (Querschnitt)
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0 · Bei Meilensteinen Phasen A–F hier Status-Zeile ergänzen (Datum + kurz „erledigt/in Arbeit“).
|
||||
**Version:** 1.2 · §2.1a Navigationsregel (Read-Merge vs. Orchestrator-Schreiben vs. `activity_metrics`-Berechnungen).
|
||||
|
|
|
|||
95
.claude/docs/technical/ACTIVITY_SCALAR_KANON_TABLE.md
Normal file
95
.claude/docs/technical/ACTIVITY_SCALAR_KANON_TABLE.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# Aktivität: Skalar-Kanon (eine Semantik → eine Quelle)
|
||||
|
||||
**Stand:** 2026-04-16
|
||||
**Normativer Code:** `backend/data_layer/activity_data_canon.py`
|
||||
**Kontext:** `ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` (Phase A abgeschlossen)
|
||||
|
||||
---
|
||||
|
||||
## 1. Spine & Identität (`activity_log`, nicht EAV)
|
||||
|
||||
Diese Felder sind **keine** `training_parameters`-Skalare. Sie gehören zur Session-Zeile.
|
||||
|
||||
| Semantik | DB / API | Kanonische Quelle | Lesefallback | Sync Spalte↔EAV |
|
||||
|----------|----------|-------------------|--------------|-----------------|
|
||||
| Primärschlüssel | `activity_log.id` | `activity_log` | — | — |
|
||||
| Profil | `profile_id` | `activity_log` | — | — |
|
||||
| Kalendertag | `date` | `activity_log` | — | — |
|
||||
| Start / Ende (Zeit) | `start_time`, `end_time`, `started_at`, `ended_at` | `activity_log` | — | — |
|
||||
| Trainingsart (Freitext/Legacy) | `activity_type` | `activity_log` | — | — |
|
||||
| Referenz Trainingstyp | `training_type_id`, `training_category`, … | `activity_log` (+ `training_types`) | — | — |
|
||||
| Notiz | `notes` | `activity_log` | — | — |
|
||||
| Quelle / Import | `source`, `created`, … | `activity_log` | — | — |
|
||||
| Session-Auswertung | `evaluation`, `quality_label`, `overall_score`, … | `activity_log` (Blob/Ergebnis) | — | Kein EAV-Raster |
|
||||
|
||||
---
|
||||
|
||||
## 2. Kernfelder CSV-Modul `activity` (= „heiße“ Skalare)
|
||||
|
||||
Abgeleitet aus `csv_parser.module_registry.MODULE_DEFINITIONS["activity"].fields` — maschinenlesbar über `ACTIVITY_MODULE_REGISTRY_FIELD_KEYS` in `activity_data_canon.py`.
|
||||
|
||||
| Semantik | Key (Registry/API) | Kanonische Quelle | Lesefallback | Bemerkung |
|
||||
|----------|-------------------|-------------------|--------------|-----------|
|
||||
| Dauer | `duration_min` | **`activity_log`** | — | Aggregates, Listen |
|
||||
| Aktive Energie | `kcal_active` | **`activity_log`** | — | |
|
||||
| Ruhe-Energie | `kcal_resting` | **`activity_log`** | — | |
|
||||
| Distanz | `distance_km` | **`activity_log`** | — | |
|
||||
| Ø HF | `hr_avg` (Parameter oft `avg_hr` in EAV-Schema) | **`activity_log`** | EAV nur wenn `source_field` / Profil-Schema | `merge_column_backed_and_eav_metrics`: Spalte schlägt EAV |
|
||||
| Max-HF | `hr_max` | **`activity_log`** | analog | |
|
||||
| RPE | `rpe` | **`activity_log`** | analog | |
|
||||
|
||||
Schreibpfad: Universal-CSV und API sollen diese Keys auf **`activity_log`** mappen, sofern nicht ausdrücklich ein EAV-primärer Parameter (§3) gewählt ist.
|
||||
|
||||
---
|
||||
|
||||
## 3. EAV-primäre Parameter (erweiterte Skalare)
|
||||
|
||||
`ACTIVITY_EAV_PRIMARY_PARAMETER_KEYS` in `activity_data_canon.py`. **`training_parameters.source_field`** = NULL (nach Kanon / Migration 057): kanonischer Speicher ist **`activity_session_metrics`**.
|
||||
|
||||
| Parameter-Key (`training_parameters.key`) | Legacy-Spalte `activity_log` (nur Lesefallback) | Kanonische Quelle | Lesefallback |
|
||||
|-------------------------------------------|-------------------------------------------------|-------------------|--------------|
|
||||
| `min_hr` | `hr_min` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `pace_min_per_km` | `pace_min_per_km` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `cadence` | `cadence` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `avg_power` | `avg_power` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `elevation_gain` | `elevation_gain` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `temperature_celsius` | `temperature_celsius` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `humidity_percent` | `humidity_percent` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `avg_hr_percent` | `avg_hr_percent` | **EAV** | Spalte, wenn EAV leer |
|
||||
| `kcal_per_km` | `kcal_per_km` | **EAV** | Spalte, wenn EAV leer |
|
||||
|
||||
Merge-Implementierung: `merge_column_backed_and_eav_metrics` in `activity_session_metrics.py`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Profil-/Typ-dynamische Skalare (EAV, nicht in Registry-Kernliste)
|
||||
|
||||
| Semantik | Kanonische Quelle | Lesefallback |
|
||||
|----------|-------------------|--------------|
|
||||
| Admin-definierte Parameter (Attributprofil Kategorie/Typ) | **`activity_session_metrics`** + `training_parameters` | — |
|
||||
| Parameter mit `source_field` → Spalte | **`activity_log`** (Spalte) | EAV ergänzend; Leseregel: Spalte bevorzugt (kein veraltetes EAV) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Composites (Zielbild, noch nicht Kanon-Zeile pro Slot)
|
||||
|
||||
| Semantik | Kanonische Quelle (Ziel) |
|
||||
|----------|---------------------------|
|
||||
| Strukturierte Composite-Dokumente (z. B. Zonen/Bänder) | **EAV** ein Dokument pro Parameter/Session (siehe `ACTIVITY_COMPOSITE_METRICS_IMPLEMENTATION_CONCEPT.md`) |
|
||||
|
||||
Kein dauerhaftes Spiegeln derselben Semantik in `activity_log`-Spalten.
|
||||
|
||||
---
|
||||
|
||||
## 6. Sync & Übergang
|
||||
|
||||
- **Kein** automatischer Dauer-Sync „Spalte → EAV“ für dieselbe Semantik; Lesepfad vereinheitlicht die Sicht (`merge_column_backed_and_eav_metrics`).
|
||||
- Optionale **Backfill**-Migration/Skript (idempotent) nur nach fachlicher Freigabe — siehe EAV-Agent-Guide §6.
|
||||
|
||||
---
|
||||
|
||||
## 7. Referenzen
|
||||
|
||||
- `ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` — Phasen A–F
|
||||
- `ACTIVITY_SESSION_METRICS_EAV_AGENT_GUIDE.md` — APIs, Tests
|
||||
- `activity_data_canon.py` — `ACTIVITY_LOG_PATCHABLE_COLUMNS`, Legacy-Map
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
**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.
|
||||
|
||||
**Composite-Parameter (EAV, JSONB, Archetypen):** detailliertes Umsetzungskonzept für Agenten: [`ACTIVITY_COMPOSITE_METRICS_IMPLEMENTATION_CONCEPT.md`](./ACTIVITY_COMPOSITE_METRICS_IMPLEMENTATION_CONCEPT.md).
|
||||
|
||||
**Kanon (Code):** `backend/data_layer/activity_data_canon.py` (Repo-Root) — CSV-Modul `activity` vs. EAV-primär; Migration **057**.
|
||||
|
||||
---
|
||||
|
|
@ -89,8 +91,8 @@ Router: `backend/routers/admin_training_parameters.py`, `backend/routers/admin_a
|
|||
|
||||
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).
|
||||
- [x] **Phase A:** Kanon-Tabelle (eine Quelle pro Semantik) — [`ACTIVITY_SCALAR_KANON_TABLE.md`](./ACTIVITY_SCALAR_KANON_TABLE.md).
|
||||
- [ ] **Phase B:** Lesepfad Layer 1 härten (Consumer-Audit fortlaufend — siehe `ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` §4 Phase B).
|
||||
- [ ] **Phase C:** Schreibpfad: Doppelhaltung / Sync stufenweise abschalten.
|
||||
- [ ] **Phase D:** Composite-MVP (ein Archetyp E2E).
|
||||
- [ ] **Phase E:** Archetypen ausbauen + CSV-Typkonvertierung vollständig + Mapping-UX.
|
||||
|
|
|
|||
1480
.claude/docs/technical/functional_concept_composite_data.md
Normal file
1480
.claude/docs/technical/functional_concept_composite_data.md
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -123,6 +123,11 @@ frontend/src/
|
|||
- **API:** Admin `/api/admin/training-parameters`, `/api/admin/training-category-parameters`, `/api/admin/training-type-parameters`; Nutzer `GET /api/activity/{id}`, `PUT /api/activity/{id}/metrics`; Platzhalter-Pfad `training_sessions_recent_json` liefert pro Session `session_metrics` (wenn befüllt).
|
||||
- **Frontend:** Admin `/admin/activity-attribute-profiles`; Aktivität → Verlauf → Bearbeiten: Profil-Kennwerte; `api.js` ergänzt.
|
||||
|
||||
### Updates (16.04.2026 - Aktivität Phase A abgeschlossen, Phase B gestartet)
|
||||
|
||||
- **Phase A:** Skalar-Kanon schriftlich fixiert — `.claude/docs/technical/ACTIVITY_SCALAR_KANON_TABLE.md`; `ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md` v1.1; Agent-Guide Checkliste Phase A erledigt.
|
||||
- **Phase B:** `GET /api/activity` (Liste) reichert jede Zeile mit `session_metrics` über `enrich_sessions_with_metrics` an (gleiche Merge-Logik wie Detail); Consumer-Audit-Tabelle in Produktions-Architektur-Dok §4 Phase B.
|
||||
|
||||
### Updates (11.04.2026 - Ernährung: TDEE, Bilanz, Kalorien-Score)
|
||||
|
||||
- **`data_layer/nutrition_metrics.py`:** TDEE für Bilanz: primär **Mifflin–St Jeor BMR × PAL 1,55**, wenn Profil (Größe, Geschlecht, DOB) und Gewicht vorhanden; sonst Fallback **kg × 32,5** (`estimate_tdee_kcal_from_latest_weight`). `get_energy_balance_data` / `calculate_energy_balance_7d` nutzen **tägliche kcal-Summen**. **`_score_calorie_adherence`** (Komponente von `calculate_nutrition_score`) wertet die 7-Tage-Bilanz nach **`profiles.goal_mode`** aus (weight_loss vs. strength/recomposition vs. maintenance/health/endurance).
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ Kanonische Aufteilung activity_log vs. EAV für Aktivitätssessions.
|
|||
- **Alle anderen Attribute:** ``training_parameters`` + Attributprofil (Kategorie/Typ) → EAV;
|
||||
Lesefallback für bekannte Legacy-Spalten siehe unten.
|
||||
|
||||
Normative Doku: .claude/docs/technical/ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md
|
||||
Normative Doku: .claude/docs/technical/ACTIVITY_PRODUCTION_ARCHITECTURE_AND_PHASES.md,
|
||||
ACTIVITY_SCALAR_KANON_TABLE.md
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ from data_layer.activity_persistence_orchestrator import (
|
|||
new_activity_id,
|
||||
)
|
||||
from data_layer.activity_time_normalize import normalize_activity_start
|
||||
from data_layer.activity_session_metrics import enrich_sessions_with_metrics
|
||||
|
||||
router = APIRouter(prefix="/api/activity", tags=["activity"])
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -71,6 +72,12 @@ def _activity_rows_after_list_query(cur):
|
|||
return rows
|
||||
|
||||
|
||||
def _return_activity_list_rows(cur, rows: list) -> list:
|
||||
"""Layer-1: gemergte session_metrics wie Detail-Pfad (Batch)."""
|
||||
enrich_sessions_with_metrics(cur, rows)
|
||||
return rows
|
||||
|
||||
|
||||
# Evaluation import with error handling (Phase 1.2)
|
||||
try:
|
||||
from evaluation_helper import evaluate_and_save_activity
|
||||
|
|
@ -140,7 +147,7 @@ def list_activity(
|
|||
""",
|
||||
(pid, d0, d1, limit),
|
||||
)
|
||||
return _activity_rows_after_list_query(cur)
|
||||
return _return_activity_list_rows(cur, _activity_rows_after_list_query(cur))
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT * FROM activity_log
|
||||
|
|
@ -152,7 +159,9 @@ def list_activity(
|
|||
""",
|
||||
(pid, d0, d1, limit),
|
||||
)
|
||||
return [r2d(r) for r in cur.fetchall()]
|
||||
return _return_activity_list_rows(
|
||||
cur, [r2d(r) for r in cur.fetchall()]
|
||||
)
|
||||
|
||||
if days is not None:
|
||||
if collapse_duplicate_sessions:
|
||||
|
|
@ -173,7 +182,7 @@ def list_activity(
|
|||
""",
|
||||
(pid, days, limit, offset),
|
||||
)
|
||||
return _activity_rows_after_list_query(cur)
|
||||
return _return_activity_list_rows(cur, _activity_rows_after_list_query(cur))
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT * FROM activity_log
|
||||
|
|
@ -203,7 +212,7 @@ def list_activity(
|
|||
""",
|
||||
(pid, limit, offset),
|
||||
)
|
||||
return _activity_rows_after_list_query(cur)
|
||||
return _return_activity_list_rows(cur, _activity_rows_after_list_query(cur))
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT * FROM activity_log
|
||||
|
|
@ -214,7 +223,7 @@ def list_activity(
|
|||
""",
|
||||
(pid, limit, offset),
|
||||
)
|
||||
return [r2d(r) for r in cur.fetchall()]
|
||||
return _return_activity_list_rows(cur, [r2d(r) for r in cur.fetchall()])
|
||||
|
||||
|
||||
@router.post("")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user