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

14 KiB
Raw Permalink Blame 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:

  1. Admin-Presets für gängige Trainingstypen (Laufen, Krafttraining, etc.)
  2. User-spezifische Anpassung (z.B. für Reha-Patienten andere Schwellwerte)
  3. 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 + 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)

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?

  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:
    -- 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

  1. Frontend: Badge/Warning in Activity-Liste
  2. Frontend: Details-Ansicht (welche Regeln failed?)
  3. Frontend: User-Override (Als gültig/ungültig markieren)

Phase 3: Integration

  1. KI-Pipeline: Filter für hochwertige Aktivitäten
  2. Charts: Nur hochwertige zählen
  3. Batch-Job: Bestehende Aktivitäten evaluieren

Phase 4: Polish

  1. Admin-UI: Presets für gängige Trainingstypen
  2. Filter-Optionen in Activity-Liste
  3. 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