mitai-jinkendo/.claude/docs/functional/ACTIVITY_QUALITY_GATES.md
Lars 7940dc7560 docs: Struktur .claude/docs versionieren, working/, Gitea-Index, Regeln
- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren
- DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues
- .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08)
- Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben
- docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md
- CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht

Made-with: Cursor
2026-04-08 13:01:49 +02:00

423 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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