# 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
