- .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
14 KiB
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:
- Pro Trainingstyp unterschiedliche Qualitätskriterien
- Automatische Bewertung beim Import und bei manueller Eingabe
- Nicht-destruktiv - keine Daten löschen, nur markieren
- Transparenz - User sieht warum eine Aktivität als minderwertig gilt
- Opt-Out möglich - User kann Quality Gates pro Aktivität überschreiben
Nice-to-Have:
- Admin-Presets für gängige Trainingstypen (Laufen, Krafttraining, etc.)
- User-spezifische Anpassung (z.B. für Reha-Patienten andere Schwellwerte)
- Historische Nachbearbeitung - bestehende Aktivitäten neu bewerten
Lösungsansätze (Evaluation)
Ansatz A: Quality Flag (Boolean)
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)
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
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)
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 + Gewichtungweight: 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)
ALTER TABLE activity_log ADD COLUMN quality_check JSONB DEFAULT NULL;
Struktur siehe Ansatz C oben.
Validierungs-Logik
Backend-Funktion: validate_activity_quality()
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?
-
Beim Import (CSV, API)
- Automatisch nach INSERT
- Spalte
quality_checkwird befüllt
-
Beim manuellen Anlegen
- Automatisch nach INSERT
- Spalte
quality_checkwird befüllt
-
Beim Ändern der Quality Rules (Admin)
- Optionaler Batch-Job: Alle Aktivitäten neu evaluieren
-
Beim User-Override
- User setzt
quality_check.override = "valid"oder"invalid"
- User setzt
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:
-- 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)
- DB-Migration:
quality_rules+quality_checkSpalten - Backend:
validate_activity_quality()Funktion - Backend: Validierung beim INSERT (activity.py)
- Admin-UI: Quality Rules konfigurieren (basic)
Phase 2: User Experience
- Frontend: Badge/Warning in Activity-Liste
- Frontend: Details-Ansicht (welche Regeln failed?)
- Frontend: User-Override (Als gültig/ungültig markieren)
Phase 3: Integration
- KI-Pipeline: Filter für hochwertige Aktivitäten
- Charts: Nur hochwertige zählen
- Batch-Job: Bestehende Aktivitäten evaluieren
Phase 4: Polish
- Admin-UI: Presets für gängige Trainingstypen
- Filter-Optionen in Activity-Liste
- Dashboard: Statistik hochwertig vs. minderwertig
Geschätzter Aufwand: 4-6 Stunden (mit Tests)
Nächste Schritte
- Entscheidung: Offene Fragen klären
- Technisches Design: DB-Schema + API-Endpoints spezifizieren
- Implementation: Phase 1 starten
- Testing: Mit echten Apple Health Daten testen
Erstellt: 2026-03-23 Review: Pending User-Feedback