Goalsystem V1 #50
729
docs/GOAL_SYSTEM_REDESIGN_v2.md
Normal file
729
docs/GOAL_SYSTEM_REDESIGN_v2.md
Normal file
|
|
@ -0,0 +1,729 @@
|
||||||
|
# Goal System Redesign v2.0
|
||||||
|
|
||||||
|
**Datum:** 26. März 2026
|
||||||
|
**Status:** 📋 KONZEPTION
|
||||||
|
**Anlass:** Fundamentale Design-Probleme in Phase 0a identifiziert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Probleme der aktuellen Implementierung (Phase 0a)
|
||||||
|
|
||||||
|
### 1.1 Primärziel zu simplistisch
|
||||||
|
**Problem:**
|
||||||
|
- Nur EIN Primärziel erlaubt
|
||||||
|
- Binäres System (primär/nicht-primär)
|
||||||
|
- Toggle funktioniert nicht richtig beim Update
|
||||||
|
|
||||||
|
**Realität:**
|
||||||
|
- User hat MEHRERE Ziele gleichzeitig mit unterschiedlichen Prioritäten
|
||||||
|
- Beispiel: 30% Abnehmen, 25% Kraft, 25% Ausdauer, 20% Beweglichkeit
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
→ **Gewichtungssystem** (0-100%, Summe = 100%)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.2 Ein Goal Mode zu simpel
|
||||||
|
**Problem:**
|
||||||
|
- User muss sich für EINEN Modus entscheiden (weight_loss ODER strength)
|
||||||
|
- In Realität: Kombinierte Ziele (Abnehmen + Kraft + Ausdauer gleichzeitig)
|
||||||
|
|
||||||
|
**Realität (User-Zitat):**
|
||||||
|
> "Ich versuche nach einer Operation Kraft und Ausdauer aufzubauen, gleichzeitig Abzunehmen und meine Beweglichkeit und Koordination wieder zu steigern."
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
→ **Multi-Mode mit Gewichtung** statt Single-Mode
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.3 Fehlende Current Values
|
||||||
|
**Problem:**
|
||||||
|
- `lean_mass` current value = "-" (nicht implementiert)
|
||||||
|
- `strength`, `flexibility` haben keine Datenquellen
|
||||||
|
- VO2Max wirft Internal Server Error
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
→ Alle Goal-Typen mit korrekten Datenquellen verbinden
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.4 Abstrakte Zieltypen
|
||||||
|
**Problem:**
|
||||||
|
- "Kraft" - was bedeutet das? Bankdrücken? Kniebeuge? Gesamt?
|
||||||
|
- "Beweglichkeit" - welcher Test? Sit-and-Reach? Hüftbeugung?
|
||||||
|
- Zu unspezifisch für konkrete Messung
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
→ **Konkrete, messbare Zieltypen** mit standardisierten Tests
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.5 Blutdruck als einzelner Wert
|
||||||
|
**Problem:**
|
||||||
|
- BP braucht ZWEI Werte (systolisch/diastolisch)
|
||||||
|
- Aktuelles Schema: nur ein `target_value`
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
→ **Compound Goals** (Ziele mit mehreren Werten)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1.6 Keine Guidance für User
|
||||||
|
**Problem:**
|
||||||
|
- User muss konkrete Zahlen eingeben ohne Kontext
|
||||||
|
- Was ist ein guter VO2Max Wert? Was ist realistisch?
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
→ **Richtwerte, Normen, Beispiele** in UI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Redesign-Konzept v2.0
|
||||||
|
|
||||||
|
### 2.1 Kern-Prinzipien
|
||||||
|
|
||||||
|
**Prinzip 1: Gewichtung statt Priorisierung**
|
||||||
|
- Alle Ziele haben eine Gewichtung (0-100%)
|
||||||
|
- Summe aller Gewichtungen = 100%
|
||||||
|
- KI berücksichtigt Gewichtung in Analysen
|
||||||
|
|
||||||
|
**Prinzip 2: Multi-dimensional statt Singular**
|
||||||
|
- Kein einzelner "Goal Mode"
|
||||||
|
- Stattdessen: Gewichtete Kombination von Fokus-Bereichen
|
||||||
|
- Realitätsnah: User hat mehrere Ziele gleichzeitig
|
||||||
|
|
||||||
|
**Prinzip 3: Konkret statt Abstrakt**
|
||||||
|
- Jedes Ziel hat klare Messbarkeit
|
||||||
|
- Standardisierte Tests wo möglich
|
||||||
|
- Datenquellen eindeutig definiert
|
||||||
|
|
||||||
|
**Prinzip 4: Guidance statt Ratlosigkeit**
|
||||||
|
- Richtwerte für jedes Ziel
|
||||||
|
- Alters-/Geschlechts-spezifische Normen
|
||||||
|
- Beispiele und Erklärungen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Neues Datenmodell
|
||||||
|
|
||||||
|
### 3.1 Fokus-Bereiche (statt Goal Modes)
|
||||||
|
|
||||||
|
**Tabelle: `focus_areas` (NEU)**
|
||||||
|
```sql
|
||||||
|
CREATE TABLE focus_areas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
||||||
|
|
||||||
|
-- Gewichtete Fokus-Bereiche
|
||||||
|
weight_loss_pct INT DEFAULT 0, -- 0-100%
|
||||||
|
muscle_gain_pct INT DEFAULT 0, -- 0-100%
|
||||||
|
endurance_pct INT DEFAULT 0, -- 0-100%
|
||||||
|
strength_pct INT DEFAULT 0, -- 0-100%
|
||||||
|
flexibility_pct INT DEFAULT 0, -- 0-100%
|
||||||
|
health_pct INT DEFAULT 0, -- 0-100% (Erhaltung, kein spezifisches Ziel)
|
||||||
|
|
||||||
|
-- Constraint: Summe muss 100 sein
|
||||||
|
CONSTRAINT sum_equals_100 CHECK (
|
||||||
|
weight_loss_pct + muscle_gain_pct + endurance_pct +
|
||||||
|
strength_pct + flexibility_pct + health_pct = 100
|
||||||
|
),
|
||||||
|
|
||||||
|
active BOOLEAN DEFAULT true,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
|
||||||
|
-- Nur ein aktiver Fokus-Mix pro User
|
||||||
|
UNIQUE(profile_id, active) WHERE active = true
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE focus_areas IS
|
||||||
|
'Weighted focus distribution - replaces single goal_mode.
|
||||||
|
Example: 30% weight loss + 25% strength + 25% endurance + 20% flexibility = 100%';
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel-Daten:**
|
||||||
|
```json
|
||||||
|
// User nach Operation (wie im Feedback beschrieben):
|
||||||
|
{
|
||||||
|
"weight_loss_pct": 30,
|
||||||
|
"muscle_gain_pct": 20,
|
||||||
|
"endurance_pct": 25,
|
||||||
|
"strength_pct": 15,
|
||||||
|
"flexibility_pct": 10,
|
||||||
|
"health_pct": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// User reiner Kraftfokus:
|
||||||
|
{
|
||||||
|
"weight_loss_pct": 0,
|
||||||
|
"muscle_gain_pct": 50,
|
||||||
|
"strength_pct": 40,
|
||||||
|
"endurance_pct": 10,
|
||||||
|
"flexibility_pct": 0,
|
||||||
|
"health_pct": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// User Gewichtsverlust primär:
|
||||||
|
{
|
||||||
|
"weight_loss_pct": 60,
|
||||||
|
"muscle_gain_pct": 0,
|
||||||
|
"endurance_pct": 20,
|
||||||
|
"strength_pct": 10,
|
||||||
|
"flexibility_pct": 5,
|
||||||
|
"health_pct": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Überarbeitete Goal-Typen
|
||||||
|
|
||||||
|
**Tabelle: `goals` (ÜBERARBEITET)**
|
||||||
|
|
||||||
|
**A) Simple Goals (ein Wert):**
|
||||||
|
```sql
|
||||||
|
goal_type:
|
||||||
|
- 'weight' → kg (aus weight_log)
|
||||||
|
- 'body_fat_pct' → % (aus caliper_log)
|
||||||
|
- 'lean_mass' → kg (berechnet: weight - (weight * bf_pct))
|
||||||
|
- 'vo2max' → ml/kg/min (aus vitals_baseline)
|
||||||
|
- 'rhr' → bpm (aus vitals_baseline)
|
||||||
|
- 'hrv' → ms (aus vitals_baseline)
|
||||||
|
```
|
||||||
|
|
||||||
|
**B) Test-based Goals (standardisierte Tests):**
|
||||||
|
```sql
|
||||||
|
goal_type:
|
||||||
|
- 'cooper_test' → Meter (12min Lauf)
|
||||||
|
- 'pushups_max' → Anzahl
|
||||||
|
- 'plank_max' → Sekunden
|
||||||
|
- 'sit_reach' → cm (Beweglichkeit)
|
||||||
|
- 'squat_1rm' → kg (Kraft Unterkörper)
|
||||||
|
- 'bench_1rm' → kg (Kraft Oberkörper)
|
||||||
|
- 'deadlift_1rm' → kg (Kraft Rücken)
|
||||||
|
```
|
||||||
|
|
||||||
|
**C) Compound Goals (mehrere Werte):**
|
||||||
|
```sql
|
||||||
|
goal_type:
|
||||||
|
- 'blood_pressure' → systolic/diastolic (mmHg)
|
||||||
|
→ Braucht: target_value_secondary
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schema-Erweiterung:**
|
||||||
|
```sql
|
||||||
|
ALTER TABLE goals ADD COLUMN goal_weight INT DEFAULT 100;
|
||||||
|
-- Gewichtung dieses Ziels (0-100%)
|
||||||
|
-- Summe aller goal_weight für einen User sollte ~100% sein
|
||||||
|
|
||||||
|
ALTER TABLE goals ADD COLUMN target_value_secondary DECIMAL(10,2);
|
||||||
|
-- Für Compound Goals (z.B. BP diastolisch)
|
||||||
|
|
||||||
|
ALTER TABLE goals ADD COLUMN current_value_secondary DECIMAL(10,2);
|
||||||
|
-- Aktueller Wert für sekundären Target
|
||||||
|
|
||||||
|
ALTER TABLE goals DROP COLUMN is_primary;
|
||||||
|
-- Nicht mehr nötig (wird durch goal_weight ersetzt)
|
||||||
|
|
||||||
|
COMMENT ON COLUMN goals.goal_weight IS
|
||||||
|
'Weight/priority of this goal (0-100%).
|
||||||
|
Higher weight = more important in AI scoring.
|
||||||
|
Sum of all goal_weight should be ~100% per user.';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 Datenquellen-Mapping
|
||||||
|
|
||||||
|
**Korrekte Current-Value Extraktion:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# backend/routers/goals.py - _get_current_value_for_goal_type()
|
||||||
|
|
||||||
|
GOAL_TYPE_SOURCES = {
|
||||||
|
# Simple values from existing tables
|
||||||
|
'weight': {
|
||||||
|
'table': 'weight_log',
|
||||||
|
'column': 'weight',
|
||||||
|
'order': 'date DESC'
|
||||||
|
},
|
||||||
|
'body_fat_pct': {
|
||||||
|
'table': 'caliper_log',
|
||||||
|
'column': 'body_fat_pct',
|
||||||
|
'order': 'date DESC'
|
||||||
|
},
|
||||||
|
'lean_mass': {
|
||||||
|
'calculation': 'weight - (weight * body_fat_pct / 100)',
|
||||||
|
'requires': ['weight_log', 'caliper_log']
|
||||||
|
},
|
||||||
|
'vo2max': {
|
||||||
|
'table': 'vitals_baseline',
|
||||||
|
'column': 'vo2_max',
|
||||||
|
'order': 'date DESC'
|
||||||
|
},
|
||||||
|
'rhr': {
|
||||||
|
'table': 'vitals_baseline',
|
||||||
|
'column': 'resting_hr',
|
||||||
|
'order': 'date DESC'
|
||||||
|
},
|
||||||
|
'hrv': {
|
||||||
|
'table': 'vitals_baseline',
|
||||||
|
'column': 'hrv',
|
||||||
|
'order': 'date DESC'
|
||||||
|
},
|
||||||
|
|
||||||
|
# Test-based values from fitness_tests
|
||||||
|
'cooper_test': {
|
||||||
|
'table': 'fitness_tests',
|
||||||
|
'filter': "test_type = 'cooper_12min'",
|
||||||
|
'column': 'result_value',
|
||||||
|
'order': 'test_date DESC'
|
||||||
|
},
|
||||||
|
'pushups_max': {
|
||||||
|
'table': 'fitness_tests',
|
||||||
|
'filter': "test_type = 'pushups_max'",
|
||||||
|
'column': 'result_value',
|
||||||
|
'order': 'test_date DESC'
|
||||||
|
},
|
||||||
|
# ... weitere Tests
|
||||||
|
|
||||||
|
# Compound goals
|
||||||
|
'blood_pressure': {
|
||||||
|
'table': 'blood_pressure_log',
|
||||||
|
'columns': ['systolic', 'diastolic'], # Beide Werte
|
||||||
|
'order': 'measured_at DESC'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. UI/UX Redesign
|
||||||
|
|
||||||
|
### 4.1 Fokus-Bereiche Konfigurator
|
||||||
|
|
||||||
|
**Statt 5 einzelne Cards → Slider-Interface:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ 🎯 Mein Trainings-Fokus │
|
||||||
|
├─────────────────────────────────────────────────────┤
|
||||||
|
│ Verschiebe die Regler um deine Prioritäten zu │
|
||||||
|
│ setzen. Die Summe muss 100% ergeben. │
|
||||||
|
│ │
|
||||||
|
│ 📉 Gewichtsverlust [====] 30% │
|
||||||
|
│ Schwerpunkt auf Kaloriendefizit & Fettabbau │
|
||||||
|
│ │
|
||||||
|
│ 💪 Muskelaufbau [===] 20% │
|
||||||
|
│ Magermasse steigern, Körperkomposition │
|
||||||
|
│ │
|
||||||
|
│ 🏃 Ausdauer [====] 25% │
|
||||||
|
│ VO2Max, aerobe Kapazität, Pace │
|
||||||
|
│ │
|
||||||
|
│ 🏋️ Maximalkraft [==] 15% │
|
||||||
|
│ 1RM Steigerung, progressive Belastung │
|
||||||
|
│ │
|
||||||
|
│ 🤸 Beweglichkeit [=] 10% │
|
||||||
|
│ Mobilität, Flexibilität, Koordination │
|
||||||
|
│ │
|
||||||
|
│ ❤️ Allgemeine Gesundheit [ ] 0% │
|
||||||
|
│ Erhaltung, präventiv │
|
||||||
|
│ │
|
||||||
|
│ ────────────────────────────────────────────────── │
|
||||||
|
│ Gesamt: 100% ✓ │
|
||||||
|
│ │
|
||||||
|
│ [Speichern] [Zurücksetzen] │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Technisch:**
|
||||||
|
- HTML Range Slider (0-100)
|
||||||
|
- Live-Update der Summe
|
||||||
|
- Validierung: Summe muss 100% sein
|
||||||
|
- Auto-Adjust: Wenn User einen Slider erhöht, andere proportional reduzieren
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.2 Ziele mit Gewichtung
|
||||||
|
|
||||||
|
**Goal-List mit Gewichtungs-Indikator:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ 🎯 Konkrete Ziele │
|
||||||
|
├─────────────────────────────────────────────────────┤
|
||||||
|
│ ┌───────────────────────────────────────────────┐ │
|
||||||
|
│ │ ⚖️ Zielgewicht: 82 kg [30%]│ │
|
||||||
|
│ │ Start: 95 kg → Aktuell: 89 kg → Ziel: 82 kg │ │
|
||||||
|
│ │ ████████████░░░░░░░░░░ 65% │ │
|
||||||
|
│ │ ✓ Voraussichtlich: 15.05.2026 (on track) │ │
|
||||||
|
│ │ [✏️] [🗑️] │ │
|
||||||
|
│ └───────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌───────────────────────────────────────────────┐ │
|
||||||
|
│ │ 💪 Magermasse: 72 kg [20%]│ │
|
||||||
|
│ │ Start: 68 kg → Aktuell: 70.5 kg → Ziel: 72 kg│ │
|
||||||
|
│ │ ██████████░░░░░░░░░░░░ 63% │ │
|
||||||
|
│ │ ⚠ Prognose: 20.06.2026 (5 Tage später) │ │
|
||||||
|
│ │ [✏️] [🗑️] │ │
|
||||||
|
│ └───────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ [+ Neues Ziel] │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
Summe Gewichtungen: 50% (noch 50% verfügbar)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Änderungen:**
|
||||||
|
- Gewichtung in `[30%]` Badge angezeigt
|
||||||
|
- Summe unten angezeigt
|
||||||
|
- Warnung wenn Summe > 100%
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.3 Ziel-Editor mit Guidance
|
||||||
|
|
||||||
|
**Beispiel: VO2Max Ziel erstellen:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Neues Ziel erstellen │
|
||||||
|
├─────────────────────────────────────────────────────┤
|
||||||
|
│ Zieltyp │
|
||||||
|
│ [VO2 Max ▼] │
|
||||||
|
│ │
|
||||||
|
│ ℹ️ VO2 Max (ml/kg/min) - Maximale Sauerstoffauf- │
|
||||||
|
│ nahme. Misst die aerobe Leistungsfähigkeit. │
|
||||||
|
│ │
|
||||||
|
│ 📊 Richtwerte (Männer, 35 Jahre): │
|
||||||
|
│ Sehr gut: > 48 ml/kg/min │
|
||||||
|
│ Gut: 44-48 ml/kg/min │
|
||||||
|
│ Durchschn.: 40-44 ml/kg/min │
|
||||||
|
│ Unterdurch.: 35-40 ml/kg/min │
|
||||||
|
│ │
|
||||||
|
│ 🎯 Zielwert │
|
||||||
|
│ ┌──────────┬──────────┐ │
|
||||||
|
│ │ [ 46 ] │ ml/kg/min│ │
|
||||||
|
│ └──────────┴──────────┘ │
|
||||||
|
│ Dein aktueller Wert: 42 ml/kg/min (Durchschnitt) │
|
||||||
|
│ → Ziel liegt in "Gut"-Bereich ✓ │
|
||||||
|
│ │
|
||||||
|
│ 📅 Zieldatum (optional) │
|
||||||
|
│ [2026-06-30] │
|
||||||
|
│ │
|
||||||
|
│ ⚖️ Gewichtung │
|
||||||
|
│ [==== ] 25% │
|
||||||
|
│ Wie wichtig ist dir dieses Ziel? │
|
||||||
|
│ │
|
||||||
|
│ 💡 Name (optional) │
|
||||||
|
│ [Ausdauer für Bergwandern ] │
|
||||||
|
│ │
|
||||||
|
│ [Ziel erstellen] [Abbrechen] │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Info-Box mit Erklärung
|
||||||
|
- Alters-/geschlechtsspezifische Richtwerte
|
||||||
|
- Live-Feedback zum eingegebenen Wert
|
||||||
|
- Aktueller Wert automatisch geladen
|
||||||
|
- Gewichtungs-Slider mit Live-Preview
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4.4 Compound Goals (Blutdruck)
|
||||||
|
|
||||||
|
**Spezial-UI für Blutdruck:**
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ Zieltyp: Blutdruck │
|
||||||
|
├─────────────────────────────────────────────────────┤
|
||||||
|
│ 🎯 Zielwerte │
|
||||||
|
│ │
|
||||||
|
│ Systolisch (oberer Wert) │
|
||||||
|
│ [ 120 ] mmHg │
|
||||||
|
│ │
|
||||||
|
│ Diastolisch (unterer Wert) │
|
||||||
|
│ [ 80 ] mmHg │
|
||||||
|
│ │
|
||||||
|
│ ℹ️ WHO/ISH Klassifikation: │
|
||||||
|
│ Optimal: < 120/80 mmHg │
|
||||||
|
│ Normal: 120-129 / 80-84 mmHg │
|
||||||
|
│ Hoch-norm.: 130-139 / 85-89 mmHg │
|
||||||
|
│ Hypertonie: ≥ 140/90 mmHg │
|
||||||
|
│ │
|
||||||
|
│ Dein aktueller Wert: 135/88 mmHg (Hoch-normal) │
|
||||||
|
│ Dein Ziel: 120/80 mmHg (Optimal) ✓ │
|
||||||
|
│ │
|
||||||
|
│ [Ziel erstellen] [Abbrechen] │
|
||||||
|
└─────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Scoring-System mit Gewichtung
|
||||||
|
|
||||||
|
### 5.1 Score-Berechnung v2.0
|
||||||
|
|
||||||
|
**Aktuell (Phase 0a):**
|
||||||
|
```python
|
||||||
|
# Feste Gewichtung per goal_mode
|
||||||
|
SCORE_WEIGHTS = {
|
||||||
|
"strength": {
|
||||||
|
"body_progress": 0.35,
|
||||||
|
"nutrition": 0.30,
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Neu (v2.0):**
|
||||||
|
```python
|
||||||
|
def calculate_weighted_score(profile_id):
|
||||||
|
"""
|
||||||
|
Berechnet Score basierend auf:
|
||||||
|
1. Focus Areas (Multi-dimensional statt single mode)
|
||||||
|
2. Goal Weights (individuelle Ziel-Gewichtungen)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. Hole Focus Areas
|
||||||
|
focus = get_focus_areas(profile_id)
|
||||||
|
# → {weight_loss: 30%, muscle_gain: 20%, endurance: 25%, ...}
|
||||||
|
|
||||||
|
# 2. Hole alle Ziele mit Gewichtung
|
||||||
|
goals = get_goals_with_weights(profile_id)
|
||||||
|
# → [{type: 'weight', weight: 30%}, {type: 'lean_mass', weight: 20%}, ...]
|
||||||
|
|
||||||
|
# 3. Berechne Basis-Scores
|
||||||
|
base_scores = {
|
||||||
|
'body_composition': calculate_body_score(profile_id),
|
||||||
|
'nutrition': calculate_nutrition_score(profile_id),
|
||||||
|
'training': calculate_training_score(profile_id),
|
||||||
|
'recovery': calculate_recovery_score(profile_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. Gewichte Scores nach Focus Areas
|
||||||
|
weighted_score = 0
|
||||||
|
|
||||||
|
# Weight Loss Focus → Body Composition + Nutrition wichtiger
|
||||||
|
if focus['weight_loss_pct'] > 0:
|
||||||
|
weighted_score += (
|
||||||
|
base_scores['body_composition'] * 0.4 +
|
||||||
|
base_scores['nutrition'] * 0.4 +
|
||||||
|
base_scores['training'] * 0.1 +
|
||||||
|
base_scores['recovery'] * 0.1
|
||||||
|
) * (focus['weight_loss_pct'] / 100)
|
||||||
|
|
||||||
|
# Muscle Gain Focus → Body + Nutrition + Training
|
||||||
|
if focus['muscle_gain_pct'] > 0:
|
||||||
|
weighted_score += (
|
||||||
|
base_scores['body_composition'] * 0.35 +
|
||||||
|
base_scores['nutrition'] * 0.35 +
|
||||||
|
base_scores['training'] * 0.25 +
|
||||||
|
base_scores['recovery'] * 0.05
|
||||||
|
) * (focus['muscle_gain_pct'] / 100)
|
||||||
|
|
||||||
|
# Endurance Focus → Training + Recovery
|
||||||
|
if focus['endurance_pct'] > 0:
|
||||||
|
weighted_score += (
|
||||||
|
base_scores['training'] * 0.50 +
|
||||||
|
base_scores['recovery'] * 0.30 +
|
||||||
|
base_scores['body_composition'] * 0.10 +
|
||||||
|
base_scores['nutrition'] * 0.10
|
||||||
|
) * (focus['endurance_pct'] / 100)
|
||||||
|
|
||||||
|
# ... weitere Focus Areas
|
||||||
|
|
||||||
|
return {
|
||||||
|
'overall_score': round(weighted_score, 1),
|
||||||
|
'base_scores': base_scores,
|
||||||
|
'focus_weights': focus,
|
||||||
|
'goal_weights': [g['weight'] for g in goals]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```python
|
||||||
|
User: 30% Weight Loss + 25% Endurance + 20% Muscle Gain + 25% Strength
|
||||||
|
|
||||||
|
Base Scores:
|
||||||
|
- Body Composition: 75/100
|
||||||
|
- Nutrition: 80/100
|
||||||
|
- Training: 70/100
|
||||||
|
- Recovery: 65/100
|
||||||
|
|
||||||
|
Calculation:
|
||||||
|
Weight Loss (30%):
|
||||||
|
= (75*0.4 + 80*0.4 + 70*0.1 + 65*0.1) * 0.30
|
||||||
|
= 69.5 * 0.30 = 20.85
|
||||||
|
|
||||||
|
Endurance (25%):
|
||||||
|
= (70*0.50 + 65*0.30 + 75*0.10 + 80*0.10) * 0.25
|
||||||
|
= 69.0 * 0.25 = 17.25
|
||||||
|
|
||||||
|
Muscle Gain (20%):
|
||||||
|
= (75*0.35 + 80*0.35 + 70*0.25 + 65*0.05) * 0.20
|
||||||
|
= 74.0 * 0.20 = 14.80
|
||||||
|
|
||||||
|
Strength (25%):
|
||||||
|
= (70*0.40 + 80*0.30 + 75*0.20 + 65*0.10) * 0.25
|
||||||
|
= 72.5 * 0.25 = 18.13
|
||||||
|
|
||||||
|
Overall Score = 20.85 + 17.25 + 14.80 + 18.13 = 71.03/100
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Migration-Strategie
|
||||||
|
|
||||||
|
### 6.1 Daten-Migration von Phase 0a
|
||||||
|
|
||||||
|
**Bestehende Daten:**
|
||||||
|
- `profiles.goal_mode` (single mode)
|
||||||
|
- `goals` mit `is_primary`
|
||||||
|
|
||||||
|
**Migrations-Logik:**
|
||||||
|
```sql
|
||||||
|
-- Migration 023: Goal System Redesign v2.0
|
||||||
|
|
||||||
|
-- 1. Erstelle focus_areas Tabelle
|
||||||
|
CREATE TABLE focus_areas (...);
|
||||||
|
|
||||||
|
-- 2. Migriere bestehende goal_mode → focus_areas
|
||||||
|
INSERT INTO focus_areas (profile_id, weight_loss_pct, muscle_gain_pct, ...)
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
CASE goal_mode
|
||||||
|
WHEN 'weight_loss' THEN 70 -- 70% Weight Loss + 15% Health + 15% Endurance
|
||||||
|
WHEN 'strength' THEN 0
|
||||||
|
-- ...
|
||||||
|
END as weight_loss_pct,
|
||||||
|
CASE goal_mode
|
||||||
|
WHEN 'strength' THEN 60
|
||||||
|
WHEN 'recomposition' THEN 30
|
||||||
|
-- ...
|
||||||
|
END as muscle_gain_pct,
|
||||||
|
-- ... weitere
|
||||||
|
FROM profiles
|
||||||
|
WHERE goal_mode IS NOT NULL;
|
||||||
|
|
||||||
|
-- 3. Erweitere goals Tabelle
|
||||||
|
ALTER TABLE goals ADD COLUMN goal_weight INT DEFAULT 100;
|
||||||
|
ALTER TABLE goals ADD COLUMN target_value_secondary DECIMAL(10,2);
|
||||||
|
ALTER TABLE goals ADD COLUMN current_value_secondary DECIMAL(10,2);
|
||||||
|
|
||||||
|
-- 4. Migriere is_primary → goal_weight
|
||||||
|
UPDATE goals SET goal_weight = 100 WHERE is_primary = true;
|
||||||
|
UPDATE goals SET goal_weight = 50 WHERE is_primary = false;
|
||||||
|
|
||||||
|
-- 5. Cleanup (später)
|
||||||
|
-- ALTER TABLE profiles DROP COLUMN goal_mode; -- nach Verifikation
|
||||||
|
-- ALTER TABLE goals DROP COLUMN is_primary; -- nach Verifikation
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Implementierungs-Phasen
|
||||||
|
|
||||||
|
### Phase 1: Konzeption ✅ (DIESES DOKUMENT)
|
||||||
|
**Dauer:** -
|
||||||
|
**Ziel:** Vollständiges Redesign-Konzept
|
||||||
|
|
||||||
|
### Phase 2: Backend Redesign (6-8h)
|
||||||
|
- Migration 023 erstellen
|
||||||
|
- `focus_areas` Tabelle + CRUD
|
||||||
|
- `goals` erweitern (weight, secondary values)
|
||||||
|
- Datenquellen-Mapping korrigieren (lean_mass, VO2Max fix, etc.)
|
||||||
|
- Scoring-System v2.0 implementieren
|
||||||
|
|
||||||
|
### Phase 3: Frontend Redesign (8-10h)
|
||||||
|
- Fokus-Bereiche Slider-UI
|
||||||
|
- Ziel-Editor mit Guidance (Richtwerte, Normen)
|
||||||
|
- Gewichtungs-System in Goal-Liste
|
||||||
|
- Compound Goals UI (Blutdruck zwei Werte)
|
||||||
|
- Neue Goal-Typen (Tests) integrieren
|
||||||
|
|
||||||
|
### Phase 4: Testing & Refinement (2-3h)
|
||||||
|
- Migration testen (Phase 0a → v2.0)
|
||||||
|
- Scoring-Logik verifizieren
|
||||||
|
- UI/UX Testing
|
||||||
|
- Edge Cases (Summe ≠ 100%, keine Ziele, etc.)
|
||||||
|
|
||||||
|
**Total: 16-21h**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Offene Fragen / Entscheidungen
|
||||||
|
|
||||||
|
### 8.1 Focus Areas vs Goals Weight
|
||||||
|
**Frage:** Brauchen wir BEIDE Gewichtungssysteme?
|
||||||
|
- Focus Areas (Weight Loss 30%, Strength 25%, ...)
|
||||||
|
- Goal Weights (Ziel "82kg" = 30%, Ziel "VO2Max 46" = 25%, ...)
|
||||||
|
|
||||||
|
**Option A:** NUR Focus Areas
|
||||||
|
- Einfacher
|
||||||
|
- Weniger Redundanz
|
||||||
|
- Aber: Weniger granular
|
||||||
|
|
||||||
|
**Option B:** BEIDE Systeme
|
||||||
|
- Focus Areas = Strategisch (Richtung)
|
||||||
|
- Goal Weights = Taktisch (konkrete Prioritäten)
|
||||||
|
- Komplexer, aber flexibler
|
||||||
|
|
||||||
|
**Empfehlung:** Option B - beide Systeme ergänzen sich
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8.2 Konkrete vs Abstrakte Tests
|
||||||
|
**Frage:** Wie konkret sollen Strength-Goals sein?
|
||||||
|
|
||||||
|
**Option A:** Sehr konkret
|
||||||
|
- `bench_press_1rm`, `squat_1rm`, `deadlift_1rm`
|
||||||
|
- Vorteil: Präzise, messbar
|
||||||
|
- Nachteil: Viele Goal-Typen
|
||||||
|
|
||||||
|
**Option B:** Abstrakt mit Kontext
|
||||||
|
- `strength` mit Sub-Type (Bench/Squat/Deadlift)
|
||||||
|
- Vorteil: Flexibler
|
||||||
|
- Nachteil: Komplizierteres Schema
|
||||||
|
|
||||||
|
**Empfehlung:** Option A - konkrete Typen, dafür klare Messbarkeit
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8.3 Auto-Update von Current Values
|
||||||
|
**Frage:** Wie oft sollen current_value aktualisiert werden?
|
||||||
|
|
||||||
|
**Option A:** On-Demand (beim Laden der Goals-Seite)
|
||||||
|
- Vorteil: Keine Background-Jobs
|
||||||
|
- Nachteil: Kann verzögert sein
|
||||||
|
|
||||||
|
**Option B:** Trigger-basiert (bei neuem Messwert)
|
||||||
|
- Vorteil: Immer aktuell
|
||||||
|
- Nachteil: Mehr Komplexität
|
||||||
|
|
||||||
|
**Empfehlung:** Option A für MVP, Option B später
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Nächste Schritte
|
||||||
|
|
||||||
|
### User-Feedback einholen:
|
||||||
|
1. ✅ Löst das Redesign alle genannten Probleme?
|
||||||
|
2. ✅ Ist die Fokus-Bereiche UI verständlich?
|
||||||
|
3. ✅ Sind die konkreten Goal-Typen sinnvoll?
|
||||||
|
4. ✅ Brauchen wir beide Gewichtungssysteme?
|
||||||
|
5. ✅ Fehlt noch etwas?
|
||||||
|
|
||||||
|
### Nach Freigabe:
|
||||||
|
1. Migration 023 schreiben
|
||||||
|
2. Backend implementieren
|
||||||
|
3. Frontend implementieren
|
||||||
|
4. Testing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Erstellt:** 26. März 2026
|
||||||
|
**Status:** 📋 WARTET AUF FEEDBACK
|
||||||
|
**Nächster Schritt:** User-Review & Freigabe
|
||||||
Loading…
Reference in New Issue
Block a user