diff --git a/docs/GOAL_SYSTEM_PRIORITY_ANALYSIS.md b/docs/GOAL_SYSTEM_PRIORITY_ANALYSIS.md new file mode 100644 index 0000000..e9cac76 --- /dev/null +++ b/docs/GOAL_SYSTEM_PRIORITY_ANALYSIS.md @@ -0,0 +1,538 @@ +# Zielesystem: Prioritäts-Analyse + +**Datum:** 26. März 2026 +**Frage:** Zielesystem vor oder nach Platzhaltern/Charts? +**Antwort:** **Minimales Zielesystem VOR Platzhaltern, volles System parallel** + +--- + +## 1. Kritische Erkenntnis aus Fachkonzept + +### Zitat Fachkonzept (Zeile 20-28): +> **Wichtig ist, dass das System zielabhängig interpretiert:** +> - Gewichtsreduktion +> - Muskel-/Kraftaufbau +> - Konditions-/Ausdaueraufbau +> - Körperrekomposition +> - allgemeine Gesundheit +> +> **Dasselbe Rohsignal kann je nach Ziel anders bewertet werden.** +> Ein Kaloriendefizit ist z. B. bei Gewichtsreduktion oft positiv, +> bei Kraftaufbau aber potenziell hinderlich. + +### Konsequenz +❌ **Charts OHNE Zielesystem = falsche Interpretationen** +✅ **Charts MIT Zielesystem = korrekte, zielspezifische Aussagen** + +--- + +## 2. Abhängigkeits-Matrix + +### Was hängt vom Zielesystem ab? + +| Komponente | Zielabhängig? | Beispiel | +|------------|---------------|----------| +| **Rohdaten-Charts** | ❌ Nein | Gewichtsverlauf, Umfänge-Trend | +| **Score-Gewichtung** | ✅ JA | Body Progress Score: 30% bei weight_loss, 20% bei strength | +| **Interpretationen** | ✅ JA | Kaloriendefizit: "gut" bei weight_loss, "kritisch" bei strength | +| **Hinweise** | ✅ JA | "Gewicht stagniert" → bei weight_loss: Warnung, bei strength: egal | +| **Platzhalter (Berechnungen)** | ⚠️ TEILWEISE | Trends: Nein, Scores: JA | +| **KI-Prompts** | ✅ JA | Analyse-Kontext ändert sich komplett | + +### Fachkonzept: Score-Gewichtung (Zeile 185-216) + +```yaml +score_weights: + weight_loss: + body_progress: 0.30 # Körper wichtig + nutrition: 0.25 + activity: 0.20 + recovery: 0.15 + health_risk: 0.10 + + strength: + body_progress: 0.20 + nutrition: 0.25 + activity: 0.30 # Training wichtiger + recovery: 0.20 + health_risk: 0.05 # Weniger kritisch + + endurance: + body_progress: 0.10 # Körper unwichtiger + activity: 0.35 # Training am wichtigsten + recovery: 0.25 # Recovery sehr wichtig +``` + +### Beispiel: Body Progress Score + +**OHNE Zielesystem:** +```python +def calculate_body_progress_score(): + # Generisch, für niemanden wirklich passend + fm_delta_score = calculate_fm_change() # -5kg + lbm_delta_score = calculate_lbm_change() # -2kg + return (fm_delta_score + lbm_delta_score) / 2 + # Score: 50/100 (FM gut runter, aber LBM auch runter) +``` + +**MIT Zielesystem:** +```python +def calculate_body_progress_score(goal_mode): + fm_delta_score = calculate_fm_change() # -5kg + lbm_delta_score = calculate_lbm_change() # -2kg + + if goal_mode == "weight_loss": + # FM runter: sehr gut, LBM runter: tolerierbar wenn nicht zu viel + return 0.70 * fm_delta_score + 0.30 * lbm_delta_score + # Score: 78/100 (FM wichtiger, LBM-Verlust weniger kritisch) + + elif goal_mode == "strength": + # FM runter: ok, LBM runter: SEHR SCHLECHT + return 0.30 * fm_delta_score + 0.70 * lbm_delta_score + # Score: 32/100 (LBM-Verlust ist Hauptproblem!) + + elif goal_mode == "recomposition": + # FM runter: gut, LBM runter: schlecht + return 0.50 * fm_delta_score + 0.50 * lbm_delta_score + # Score: 50/100 (ausgewogen bewertet) +``` + +**Ergebnis:** +- Gleiche Daten (-5kg FM, -2kg LBM) +- ABER: 78/100 bei weight_loss, 32/100 bei strength +- **Ohne Ziel: völlig falsche Bewertung!** + +--- + +## 3. Ziel-Erkennung aus Daten + +### Fachkonzept erwähnt dies NICHT explizit, aber logisch ableitbar: + +**Pattern-Erkennung:** +```python +def suggest_goal_from_data(profile_id): + """Schlägt Ziel basierend auf Daten-Mustern vor.""" + + # Analyse der letzten 28 Tage + training_types = get_training_distribution_28d(profile_id) + nutrition = get_nutrition_pattern_28d(profile_id) + body_changes = get_body_changes_28d(profile_id) + + # Pattern 1: Viel Kraft + viel Protein + LBM steigt + if (training_types['strength'] > 60% and + nutrition['protein_g_per_kg'] > 1.8 and + body_changes['lbm_trend'] > 0): + return { + 'suggested_goal': 'strength', + 'confidence': 'high', + 'reasoning': 'Krafttraining dominant + hohe Proteinzufuhr + Muskelaufbau erkennbar' + } + + # Pattern 2: Viel Cardio + Kaloriendefizit + Gewicht sinkt + if (training_types['endurance'] > 50% and + nutrition['kcal_balance_avg'] < -300 and + body_changes['weight_trend'] < 0): + return { + 'suggested_goal': 'weight_loss', + 'confidence': 'high', + 'reasoning': 'Ausdauertraining + Kaloriendefizit + Gewichtsverlust' + } + + # Pattern 3: Mixed Training + Protein hoch + Gewicht stabil + Rekomposition + if (training_types['mixed'] == True and + nutrition['protein_g_per_kg'] > 1.6 and + abs(body_changes['weight_trend']) < 0.05 and + body_changes['fm_trend'] < 0 and + body_changes['lbm_trend'] > 0): + return { + 'suggested_goal': 'recomposition', + 'confidence': 'medium', + 'reasoning': 'Gemischtes Training + Rekomposition sichtbar (FM↓, LBM↑)' + } + + # Default: Nicht genug Muster erkennbar + return { + 'suggested_goal': 'health', + 'confidence': 'low', + 'reasoning': 'Keine klaren Muster erkennbar, gesundheitsorientiertes Training angenommen' + } +``` + +### Voraussetzungen für Ziel-Erkennung: +1. ✅ Mindestens 21-28 Tage Daten +2. ✅ Training-Type Distribution +3. ✅ Ernährungs-Pattern +4. ✅ Körper-Trends (FM, LBM, Gewicht) +5. ✅ Berechnet → **braucht Platzhalter!** + +**ABER:** Ziel-Erkennung ist **nachgelagert**, nicht Voraussetzung. + +--- + +## 4. Empfohlene Implementierungs-Strategie + +### Hybrid-Ansatz: Minimal-Ziele SOFORT, Voll-System parallel + +## Phase 0a: Minimal-Zielesystem (2-3h) ⭐ **START HIER** + +### Ziel +User kann manuell Ziel setzen, System nutzt es für Berechnungen. + +### Implementierung + +**1. DB-Schema erweitern:** +```sql +-- Migration 023 +ALTER TABLE profiles ADD COLUMN goal_mode VARCHAR(50) DEFAULT 'health'; +ALTER TABLE profiles ADD COLUMN goal_weight DECIMAL(5,2); +ALTER TABLE profiles ADD COLUMN goal_bf_pct DECIMAL(4,1); +ALTER TABLE profiles ADD COLUMN goal_set_date DATE; +ALTER TABLE profiles ADD COLUMN goal_target_date DATE; + +COMMENT ON COLUMN profiles.goal_mode IS + 'Primary goal: weight_loss, strength, endurance, recomposition, health'; +``` + +**2. Goal-Mode Konstanten:** +```python +# backend/goals.py (NEU) +GOAL_MODES = { + 'weight_loss': { + 'label': 'Gewichtsreduktion', + 'description': 'Fettabbau bei Erhalt der Magermasse', + 'score_weights': { + 'body_progress': 0.30, + 'nutrition': 0.25, + 'activity': 0.20, + 'recovery': 0.15, + 'health_risk': 0.10 + }, + 'focus_areas': ['fettmasse', 'gewichtstrend', 'kalorienbilanz', 'protein_sicherung'] + }, + 'strength': { + 'label': 'Kraftaufbau', + 'description': 'Muskelaufbau und Kraftsteigerung', + 'score_weights': { + 'body_progress': 0.20, + 'nutrition': 0.25, + 'activity': 0.30, + 'recovery': 0.20, + 'health_risk': 0.05 + }, + 'focus_areas': ['trainingsqualitaet', 'protein', 'lbm', 'recovery'] + }, + 'endurance': { + 'label': 'Ausdaueraufbau', + 'description': 'Kondition und VO2max verbessern', + 'score_weights': { + 'body_progress': 0.10, + 'nutrition': 0.20, + 'activity': 0.35, + 'recovery': 0.25, + 'health_risk': 0.10 + }, + 'focus_areas': ['trainingsvolumen', 'intensitaetsverteilung', 'vo2max', 'recovery'] + }, + 'recomposition': { + 'label': 'Körperrekomposition', + 'description': 'Fettabbau bei gleichzeitigem Muskelaufbau', + 'score_weights': { + 'body_progress': 0.30, + 'nutrition': 0.25, + 'activity': 0.25, + 'recovery': 0.15, + 'health_risk': 0.05 + }, + 'focus_areas': ['lbm', 'fettmasse', 'protein', 'trainingsqualitaet'] + }, + 'health': { + 'label': 'Allgemeine Gesundheit', + 'description': 'Ausgeglichenes Gesundheits- und Fitnesstraining', + 'score_weights': { + 'body_progress': 0.20, + 'nutrition': 0.20, + 'activity': 0.20, + 'recovery': 0.20, + 'health_risk': 0.20 + }, + 'focus_areas': ['bewegung', 'blutdruck', 'schlaf', 'gewicht', 'regelmaessigkeit'] + } +} +``` + +**3. API-Endpoint:** +```python +# routers/goals.py (NEU) +from fastapi import APIRouter, Depends +from auth import require_auth +from goals import GOAL_MODES + +router = APIRouter(prefix="/api/goals", tags=["goals"]) + +@router.get("/modes") +def get_goal_modes(): + """Return all available goal modes with descriptions.""" + return GOAL_MODES + +@router.get("/current") +def get_current_goal(session: dict = Depends(require_auth)): + """Get user's current goal settings.""" + profile_id = session['profile_id'] + with get_db() as conn: + cur = get_cursor(conn) + cur.execute( + """SELECT goal_mode, goal_weight, goal_bf_pct, + goal_set_date, goal_target_date + FROM profiles WHERE id=%s""", + (profile_id,) + ) + row = r2d(cur.fetchone()) + return { + **row, + 'mode_config': GOAL_MODES.get(row['goal_mode'], GOAL_MODES['health']) + } + +@router.post("/set") +def set_goal( + goal_mode: str, + goal_weight: Optional[float] = None, + goal_bf_pct: Optional[float] = None, + target_date: Optional[str] = None, + session: dict = Depends(require_auth) +): + """Set user's goal.""" + if goal_mode not in GOAL_MODES: + raise HTTPException(400, f"Invalid goal_mode. Must be one of: {list(GOAL_MODES.keys())}") + + profile_id = session['profile_id'] + with get_db() as conn: + cur = get_cursor(conn) + cur.execute( + """UPDATE profiles + SET goal_mode=%s, goal_weight=%s, goal_bf_pct=%s, + goal_set_date=CURRENT_DATE, goal_target_date=%s + WHERE id=%s""", + (goal_mode, goal_weight, goal_bf_pct, target_date, profile_id) + ) + conn.commit() + + return {"success": True, "goal_mode": goal_mode} +``` + +**4. Frontend UI (Settings.jsx):** +```jsx +// Minimal Goal Selector +function GoalSettings() { + const [goalModes, setGoalModes] = useState({}) + const [currentGoal, setCurrentGoal] = useState(null) + const [selectedMode, setSelectedMode] = useState('health') + + useEffect(() => { + loadGoalModes() + loadCurrentGoal() + }, []) + + const loadGoalModes = async () => { + const modes = await api.getGoalModes() + setGoalModes(modes) + } + + const loadCurrentGoal = async () => { + const goal = await api.getCurrentGoal() + setCurrentGoal(goal) + setSelectedMode(goal.goal_mode || 'health') + } + + const saveGoal = async () => { + await api.setGoal({ + goal_mode: selectedMode, + goal_weight: goalWeight, + goal_bf_pct: goalBfPct, + target_date: targetDate + }) + loadCurrentGoal() + } + + return ( +
+

🎯 Trainingsziel

+ +
+ + +

+ {goalModes[selectedMode]?.description} +

+
+ + {(selectedMode === 'weight_loss' || selectedMode === 'recomposition') && ( +
+ + +
+ )} + + +
+ ) +} +``` + +### Aufwand: 2-3h +- 1h: DB + Backend +- 1h: Frontend UI +- 0.5h: Testing + +--- + +## Phase 0b: Goal-Aware Platzhalter (16-20h) + +**Alle 84 Platzhalter implementieren, ABER:** +- Score-Berechnungen nutzen `goal_mode` von Anfang an +- Beispiel: + +```python +def get_body_progress_score(profile_id: str) -> str: + """Body Progress Score (0-100, goal-dependent).""" + profile = get_profile_data(profile_id) + goal_mode = profile.get('goal_mode', 'health') + + # Hole Gewichte aus goals.GOAL_MODES + weights = GOAL_MODES[goal_mode]['score_weights'] + + # Berechne Sub-Scores + fm_score = calculate_fm_progress(profile_id) + lbm_score = calculate_lbm_progress(profile_id) + weight_score = calculate_weight_progress(profile_id, goal_mode) + + # Gewichte nach Ziel + if goal_mode == 'weight_loss': + total = (0.50 * fm_score + 0.30 * weight_score + 0.20 * lbm_score) + elif goal_mode == 'strength': + total = (0.60 * lbm_score + 0.30 * fm_score + 0.10 * weight_score) + elif goal_mode == 'recomposition': + total = (0.45 * fm_score + 0.45 * lbm_score + 0.10 * weight_score) + else: # health, endurance + total = (0.40 * weight_score + 0.30 * fm_score + 0.30 * lbm_score) + + return f"{int(total)}/100" +``` + +**Resultat:** +- Charts bekommen von Anfang an **korrekte** Scores +- Keine Umarbeitung nötig später +- System ist "smart" ab Tag 1 + +--- + +## Phase 2+: Vollständiges Zielesystem (6-8h) + +**Features:** +1. **Ziel-Erkennung aus Daten** + - Pattern-Analyse (wie oben) + - Vorschlag mit Confidence + - "Passt dein Ziel noch?" Check + +2. **Sekundäre Ziele** + - `goal_mode` = primary + - `secondary_goals[]` = weitere Schwerpunkte + - Gewichtung: 70% primary, 30% secondary + +3. **Ziel-Progression Tracking** + - Fortschritt zum Ziel (%) + - Geschätzte Erreichung (Datum) + - Anpassungs-Vorschläge + +4. **Goal-Aware Charts** + - Priorisierung nach goal_relevance + - Dashboard zeigt ziel-spezifische Charts zuerst + +5. **Goal-Aware KI** + - Prompt-Kontext enthält goal_mode + - KI interpretiert zielspezifisch + +--- + +## 5. Entscheidungs-Matrix + +### Option A: Zielesystem komplett ZUERST +**Aufwand:** 10-12h +**Pro:** +- Alles konsistent von Anfang an +- Keine Umarbeitung +**Contra:** +- Verzögert Platzhalter-Start +- Ziel-Erkennung braucht Platzhalter (Henne-Ei) + +### Option B: Platzhalter ZUERST, dann Ziele +**Aufwand:** 16-20h + später Rework +**Pro:** +- Schneller Start +**Contra:** +- ALLE Scores falsch gewichtet +- Komplette Umarbeitung nötig +- User sehen falsche Werte + +### Option C: HYBRID ⭐ **EMPFOHLEN** +**Aufwand:** 2-3h (Minimal-Ziele) + 16-20h (Goal-Aware Platzhalter) + später 6-8h (Voll-System) +**Pro:** +- ✅ Beste aus beiden Welten +- ✅ Korrekte Scores von Anfang an +- ✅ Keine Umarbeitung +- ✅ Ziel-Erkennung später als Enhancement +**Contra:** +- Keinen signifikanten Nachteil + +--- + +## 6. Empfehlung + +### JA, Zielesystem VOR Platzhaltern – aber minimal! + +**Reihenfolge:** + +1. **Phase 0a (2-3h):** Minimal-Zielesystem + - DB: goal_mode field + - API: Get/Set Goal + - UI: Goal Selector (Settings) + - Default: "health" + +2. **Phase 0b (16-20h):** Goal-Aware Platzhalter + - 84 Platzhalter implementieren + - Scores nutzen goal_mode + - Berechnungen goal-abhängig + +3. **Phase 1 (12-16h):** Charts + - Nutzen goal-aware Platzhalter + - Zeigen korrekte Interpretationen + +4. **Phase 2+ (6-8h):** Vollständiges Zielesystem + - Ziel-Erkennung + - Sekundäre Ziele + - Goal Progression Tracking + +--- + +## 7. Fazit + +**Deine Intuition war 100% richtig!** + +✅ **Ohne Zielesystem:** +- Charts zeigen falsche Interpretationen +- Scores sind generisch und für niemanden passend +- System bleibt "dummer Datensammler" + +✅ **Mit Zielesystem:** +- Charts interpretieren zielspezifisch +- Scores sind individuell gewichtet +- System wird "intelligenter Coach" + +**Nächster Schritt:** Phase 0a implementieren (2-3h), dann Phase 0b mit goal-aware Platzhaltern. + +**Soll ich mit Phase 0a (Minimal-Zielesystem) starten?**