mitai-jinkendo/.claude/docs/functional/ACTIVITY_QUALITY_GATES.md

423 lines
14 KiB
Markdown
Raw Normal View History

# Activity Quality Gates – Fachliches Konzept
**Issue:** #15
**Status:** Design Phase
**Erstellt:** 2026-03-23
---
## Problem-Statement
### Aktuelles Problem:
Apple Health und andere Tracker importieren **alle** Workouts, unabhängig von ihrer Qualität:
- 5-Minuten-Spaziergang → "Outdoor Run"
- 10-Minuten-Krafttraining ohne echte Belastung
- Versehentlich gestartete Workouts
- Aufwärm-Sessions ohne echtes Training
**Folgen:**
- KI-Analysen werden verfälscht ("Du trainierst täglich!" - aber nur 5min)
- Statistiken zeigen unrealistische Trainingsfrequenz
- Korrelationen (Training ↔ Erholung) werden unbrauchbar
- User verliert Vertrauen in die Auswertungen
---
## Fachliche Anforderungen
### Must-Have:
1. **Pro Trainingstyp** unterschiedliche Qualitätskriterien
2. **Automatische Bewertung** beim Import und bei manueller Eingabe
3. **Nicht-destruktiv** - keine Daten löschen, nur markieren
4. **Transparenz** - User sieht warum eine Aktivität als minderwertig gilt
5. **Opt-Out möglich** - User kann Quality Gates pro Aktivität überschreiben
### Nice-to-Have:
6. **Admin-Presets** für gängige Trainingstypen (Laufen, Krafttraining, etc.)
7. **User-spezifische Anpassung** (z.B. für Reha-Patienten andere Schwellwerte)
8. **Historische Nachbearbeitung** - bestehende Aktivitäten neu bewerten
---
## Lösungsansätze (Evaluation)
### Ansatz A: Quality Flag (Boolean)
```sql
ALTER TABLE activity_log ADD COLUMN is_valid BOOLEAN DEFAULT true;
```
**Pro:**
- Einfach zu implementieren
- Schnelle Queries (`WHERE is_valid = true`)
- Binäre Entscheidung: gültig oder nicht
**Contra:**
- ❌ Keine Abstufungen (was ist mit "grenzwertig"?)
- ❌ Kein Grund dokumentiert (warum ungültig?)
- ❌ Schwer erweiterbar
**Bewertung:** ⭐⭐☆☆☆ (zu simpel)
---
### Ansatz B: Quality Score (0-100)
```sql
ALTER TABLE activity_log ADD COLUMN quality_score INTEGER DEFAULT 100;
```
**Pro:**
- Abstufungen möglich (100 = perfekt, 0 = wertlos)
- Filterbar: `WHERE quality_score >= 70`
- Flexibel für zukünftige Erweiterungen
**Contra:**
- ❌ Score-Berechnung komplex (wie gewichten?)
- ❌ Kein Grund dokumentiert
- ❌ Schwellwert (70? 80?) willkürlich
**Bewertung:** ⭐⭐⭐☆☆ (besser, aber intransparent)
---
### Ansatz C: Validation Result (JSONB) ⭐ **EMPFEHLUNG**
```sql
ALTER TABLE activity_log ADD COLUMN quality_check JSONB DEFAULT NULL;
-- Beispiel-Daten:
{
"evaluated_at": "2026-03-23T10:30:00Z",
"passed": false,
"score": 45,
"reasons": [
{"rule": "duration_min", "expected": 15, "actual": 8, "passed": false},
{"rule": "avg_hr_min", "expected": 100, "actual": 95, "passed": false},
{"rule": "max_hr_min", "expected": 120, "actual": 125, "passed": true}
],
"override": null // User kann auf "valid" oder "invalid" setzen
}
```
**Pro:**
- ✅ Transparent: Jede Regel einzeln nachvollziehbar
- ✅ Erweiterbar: Neue Regeln einfach hinzufügbar
- ✅ Override-Mechanismus eingebaut
- ✅ Historisch: Wann wurde evaluiert?
- ✅ Flexibel: Score + Boolean + Details
**Contra:**
- Mehr Speicherplatz (aber marginal bei JSONB)
- Komplexere Queries (aber PostgreSQL JSONB ist schnell)
**Bewertung:** ⭐⭐⭐⭐⭐ **BESTE LÖSUNG**
---
## Empfohlene Lösung: Ansatz C (Validation Result)
### Datenmodell
#### 1. Training Types (Regel-Definition)
```sql
ALTER TABLE training_types ADD COLUMN quality_rules JSONB DEFAULT NULL;
-- Beispiel: Laufen
UPDATE training_types SET quality_rules = '{
"enabled": true,
"rules": {
"duration_min": {"min": 15, "weight": 3},
"avg_hr_min": {"min": 100, "weight": 2},
"max_hr_min": {"min": 120, "weight": 1},
"distance_km": {"min": 1.0, "weight": 1}
},
"pass_threshold": 0.6,
"description": "Mindestens 15min, Durchschnittspuls > 100"
}'::jsonb WHERE name_de = 'Laufen';
-- Beispiel: Krafttraining
UPDATE training_types SET quality_rules = '{
"enabled": true,
"rules": {
"duration_min": {"min": 20, "weight": 5},
"avg_hr_min": {"min": 90, "weight": 1}
},
"pass_threshold": 0.8,
"description": "Mindestens 20 Minuten"
}'::jsonb WHERE name_de = 'Krafttraining';
```
**Erklärung:**
- `enabled`: Quality Gates aktiv für diesen Typ?
- `rules`: Dictionary der Regeln mit Schwellwerten + Gewichtung
- `weight`: Wichtigkeit der Regel (1-5)
- `pass_threshold`: Mindest-Score (0.0-1.0) für "bestanden"
- `description`: User-freundliche Erklärung
#### 2. Activity Log (Validierungs-Ergebnis)
```sql
ALTER TABLE activity_log ADD COLUMN quality_check JSONB DEFAULT NULL;
```
**Struktur siehe Ansatz C oben.**
---
## Validierungs-Logik
### Backend-Funktion: `validate_activity_quality()`
```python
def validate_activity_quality(activity: dict, training_type: dict) -> dict:
"""
Evaluiert eine Aktivität gegen die Quality Rules des Trainingstyps.
Returns:
{
"evaluated_at": ISO timestamp,
"passed": bool,
"score": float (0.0-1.0),
"reasons": [
{"rule": str, "expected": value, "actual": value, "passed": bool}
],
"override": null | "valid" | "invalid"
}
"""
rules = training_type.get('quality_rules', {})
if not rules or not rules.get('enabled'):
return None # Keine Quality Gates aktiv
results = []
total_weight = 0
passed_weight = 0
for rule_name, rule_config in rules['rules'].items():
weight = rule_config.get('weight', 1)
total_weight += weight
actual_value = activity.get(rule_name)
expected_min = rule_config.get('min')
passed = actual_value is not None and actual_value >= expected_min
if passed:
passed_weight += weight
results.append({
"rule": rule_name,
"expected": expected_min,
"actual": actual_value,
"passed": passed
})
score = passed_weight / total_weight if total_weight > 0 else 1.0
passed = score >= rules.get('pass_threshold', 0.6)
return {
"evaluated_at": datetime.now().isoformat(),
"passed": passed,
"score": round(score, 2),
"reasons": results,
"override": None
}
```
### Wann wird validiert?
1. **Beim Import** (CSV, API)
- Automatisch nach INSERT
- Spalte `quality_check` wird befüllt
2. **Beim manuellen Anlegen**
- Automatisch nach INSERT
- Spalte `quality_check` wird befüllt
3. **Beim Ändern der Quality Rules** (Admin)
- Optionaler Batch-Job: Alle Aktivitäten neu evaluieren
4. **Beim User-Override**
- User setzt `quality_check.override = "valid"` oder `"invalid"`
---
## User Experience
### 1. Activity-Liste (User-Ansicht)
```
┌─────────────────────────────────────────────────────────┐
│ Aktivitäten (März 2026) [Filter ▼] │
├─────────────────────────────────────────────────────────┤
│ │
│ 🏃 Laufen - 23. März 2026 │
│ 45 Minuten · Ø 142 bpm · 5.2 km │
│ ✅ Hochwertige Aktivität │
│ │
│ 🏃 Laufen - 22. März 2026 ⚠️ │
│ 8 Minuten · Ø 95 bpm · 0.8 km │
│ ⚠️ Niedrige Qualität - wird nicht gezählt │
│ [Details anzeigen ▼] │
│ │
│ → Dauer zu kurz (8 min < 15 min erforderlich) ❌ │
│ → Durchschnittspuls zu niedrig (95 < 100) ❌ │
│ → Maximalpuls OK (125 ≥ 120) ✅ │
│ │
│ [Als gültig markieren] [Als ungültig markieren] │
│ │
└─────────────────────────────────────────────────────────┘
```
**Filter-Optionen:**
- ☑ Hochwertige Aktivitäten
- ☑ Minderwertige Aktivitäten
- ☐ Nur in Statistiken gezählt
- ☐ Nur manuell überschrieben
### 2. Admin-UI (Quality Rules konfigurieren)
```
┌─────────────────────────────────────────────────────────┐
│ Trainingstyp bearbeiten: Laufen 🏃 │
├─────────────────────────────────────────────────────────┤
│ │
│ Kategorie: Cardio │
│ Name (DE): Laufen │
│ Name (EN): Running │
│ │
│ ━━━ Quality Gates ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ ☑ Quality Gates aktiviert │
│ │
│ Mindest-Schwellwerte: │
│ │
│ • Dauer (Minuten) [15] min Gewicht: ●●●○○ │
│ • Ø Herzfrequenz [100] bpm Gewicht: ●●○○○ │
│ • Max. Herzfrequenz [120] bpm Gewicht: ●○○○○ │
│ • Distanz [1.0] km Gewicht: ●○○○○ │
│ │
│ Erfolgs-Schwelle: [60]% (gewichteter Score) │
│ │
│ Beschreibung (User-sichtbar): │
│ "Mindestens 15 Minuten, Durchschnittspuls > 100" │
│ │
│ [Speichern] [Abbrechen] [Alle Aktivitäten neu prüfen]│
│ │
└─────────────────────────────────────────────────────────┘
```
### 3. Dashboard / Statistiken
**Transparenz:**
```
📊 Training (März 2026)
15 Aktivitäten erfasst
12 hochwertig ✅
3 minderwertig ⚠️ (werden nicht gezählt)
[Details zu minderwertigen Aktivitäten anzeigen]
```
---
## Betroffene Bereiche (Impact Analysis)
### 1. Aktivitäten-Listen
- **Query-Änderung:**
```sql
-- Alt:
SELECT * FROM activity_log WHERE profile_id = %s
-- Neu (nur hochwertige):
SELECT * FROM activity_log
WHERE profile_id = %s
AND (quality_check IS NULL
OR quality_check->>'passed' = 'true'
OR quality_check->>'override' = 'valid')
```
### 2. KI-Pipeline
- **insights.py:** Filter bei Daten-Aggregation
- Nur hochwertige Aktivitäten für KI-Prompts
### 3. Charts / Statistiken
- **Training Type Distribution:** Nur hochwertige zählen
- **Korrelations-Charts:** Separate Kurven für "alle" vs "hochwertig"?
### 4. Activity Import (CSV)
- Nach INSERT: `validate_activity_quality()` aufrufen
- Quality Check speichern
---
## Offene Fragen (für Entscheidung)
### Frage 1: Rückwirkende Evaluierung?
**Option A:** Nur neue Aktivitäten evaluieren
**Option B:** Alle bestehenden Aktivitäten nachträglich evaluieren
**Empfehlung:** Option B mit Batch-Job (einmalig beim Rollout)
### Frage 2: Standard-Verhalten bei fehlenden Quality Rules?
**Option A:** Alle Aktivitäten gelten als hochwertig (NULL = valid)
**Option B:** Alle Aktivitäten gelten als minderwertig (NULL = invalid)
**Empfehlung:** Option A (konservativ, keine Daten-Verlust-Angst)
### Frage 3: User-Override-Berechtigung?
**Option A:** Jeder User kann eigene Aktivitäten überschreiben
**Option B:** Nur Admin kann überschreiben
**Empfehlung:** Option A (User kennt seinen Kontext am besten)
### Frage 4: Wie viele Default-Rules?
**Option A:** Nur für Top 5 Trainingstypen (Laufen, Krafttraining, Radfahren, Schwimmen, HIIT)
**Option B:** Für alle 29 Trainingstypen
**Empfehlung:** Option A (Rest kann Admin/User nachpflegen)
### Frage 5: Quality Check in Liste anzeigen?
**Option A:** Immer sichtbar (Badge/Icon)
**Option B:** Nur bei minderwertigen Aktivitäten
**Empfehlung:** Option B (weniger visuelles Rauschen)
---
## Implementierungs-Reihenfolge (Vorschlag)
### Phase 1: Foundation (MVP)
1. DB-Migration: `quality_rules` + `quality_check` Spalten
2. Backend: `validate_activity_quality()` Funktion
3. Backend: Validierung beim INSERT (activity.py)
4. Admin-UI: Quality Rules konfigurieren (basic)
### Phase 2: User Experience
5. Frontend: Badge/Warning in Activity-Liste
6. Frontend: Details-Ansicht (welche Regeln failed?)
7. Frontend: User-Override (Als gültig/ungültig markieren)
### Phase 3: Integration
8. KI-Pipeline: Filter für hochwertige Aktivitäten
9. Charts: Nur hochwertige zählen
10. Batch-Job: Bestehende Aktivitäten evaluieren
### Phase 4: Polish
11. Admin-UI: Presets für gängige Trainingstypen
12. Filter-Optionen in Activity-Liste
13. Dashboard: Statistik hochwertig vs. minderwertig
**Geschätzter Aufwand:** 4-6 Stunden (mit Tests)
---
## Nächste Schritte
1. **Entscheidung:** Offene Fragen klären
2. **Technisches Design:** DB-Schema + API-Endpoints spezifizieren
3. **Implementation:** Phase 1 starten
4. **Testing:** Mit echten Apple Health Daten testen
---
**Erstellt:** 2026-03-23
**Review:** Pending User-Feedback