docs: cleanup debug logs + document goal system enhancements
All checks were successful
Deploy Development / deploy (push) Successful in 53s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s

- Removed all debug print statements from placeholder_resolver.py
- Removed debug print statements from goals.py (list_goals, update_goal)
- Updated CLAUDE.md with Phase 0a completion details:
  * Auto-population of start_date/start_value from historical data
  * Time-based tracking (behind schedule = time-deviated)
  * Hybrid goal display (with/without target_date)
  * Timeline visualization in goal lists
  * 7 bug fixes documented
- Created memory file for future sessions (feedback_goal_system.md)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-28 17:32:13 +01:00
parent dd395180a3
commit 255d1d61c5
3 changed files with 39 additions and 54 deletions

View File

@ -82,7 +82,45 @@ frontend/src/
**Branch:** develop **Branch:** develop
**Nächster Schritt:** Testing → Prod Deploy → Code Splitting → Phase 0b (120+ Platzhalter) **Nächster Schritt:** Testing → Prod Deploy → Code Splitting → Phase 0b (120+ Platzhalter)
### Letzte Updates (27.03.2026 - Dynamic Focus Areas v2.0 Complete) 🆕 ### Updates (28.03.2026 - Goal System Enhancement Complete) 🆕
#### Auto-Population & Time-Based Tracking ✅
- ✅ **Auto-Population von start_date/start_value:**
- Automatische Ermittlung aus erster historischer Messung (on/after Startdatum)
- Windowing-Logik: Findet nächste verfügbare Messung am oder nach gewähltem Datum
- Auto-Adjustment: Startdatum wird auf tatsächliches Messdatum gesetzt
- Funktioniert für alle Goal-Typen (weight, body_fat, lean_mass, vo2max, strength, bp, rhr)
- ✅ **Time-Based Tracking (Behind Schedule):**
- Linear Progress Model: expected = (elapsed_days / total_days) × 100
- Deviation Calculation: actual_progress - expected_progress
- Negativ = behind schedule, Positiv = ahead of schedule
- User-Feedback: "Warum 'behind schedule'?" → Zeitbasierte Abweichung implementiert
- ✅ **Hybrid Goal Display:**
- Goals MIT target_date: Zeit-basierte Abweichung (±% voraus/zurück)
- Goals OHNE target_date: Einfacher Fortschritt (% erreicht)
- Kombinierte Sortierung für aussagekräftige Rankings
- Platzhalter: `{{top_3_goals_behind_schedule}}`, `{{top_3_goals_on_track}}`
- ✅ **Timeline Visualization:**
- Start → Ziel Datumsanzeige in Ziellisten
- Format: "Start: 92.0 kg (22.02.26) → Ziel: 85.0 kg (31.05.26)"
- Fortschrittsbalken mit Prozentanzeige
#### Bug Fixes (28.03.2026) ✅
- ✅ **PostgreSQL Date Arithmetic:** ORDER BY ABS(date - %s::date) statt EXTRACT(EPOCH)
- ✅ **JSON Date Serialization:** serialize_dates() für Python date → ISO strings
- ✅ **start_date nicht gespeichert:** update_goal() Logik komplett überarbeitet
- ✅ **start_date fehlte in SELECT:** get_active_goals() + get_goals_grouped() ergänzt
- ✅ **Edit-Form Datum-Fallback:** goal.start_date || '' statt || today
- ✅ **Behind Schedule Logik:** Von "lowest progress" zu "time-based deviation"
- ✅ **Fehlende created_at:** Backup-Datum für Goals ohne start_date
#### Betroffene Dateien:
- `backend/routers/goals.py`: serialize_dates(), _get_historical_value_for_goal_type(), create_goal(), update_goal(), list_goals(), get_goals_grouped()
- `backend/goal_utils.py`: get_active_goals() SELECT ergänzt (start_date, created_at)
- `backend/placeholder_resolver.py`: _format_goals_behind(), _format_goals_on_track() komplett überarbeitet (hybrid logic)
- `frontend/src/pages/GoalsPage.jsx`: Timeline-Display, handleEditGoal() fix
### Letzte Updates (27.03.2026 - Dynamic Focus Areas v2.0 Complete)
#### Dynamic Focus Areas v2.0 System ✅ #### Dynamic Focus Areas v2.0 System ✅
- ✅ **Migration 031-032:** Vollständiges dynamisches System - ✅ **Migration 031-032:** Vollständiges dynamisches System

View File

@ -824,7 +824,6 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
today = date.today() today = date.today()
goals_with_deviation = [] goals_with_deviation = []
print(f"[DEBUG] _format_goals_behind: Processing {len(goals)} goals")
for g in goals: for g in goals:
goal_name = g.get('name') or g.get('goal_type', 'Unknown') goal_name = g.get('name') or g.get('goal_type', 'Unknown')
@ -834,16 +833,13 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
start_date = g.get('start_date') start_date = g.get('start_date')
target_date = g.get('target_date') target_date = g.get('target_date')
print(f"[DEBUG] Goal '{goal_name}': current={current}, target={target}, start={start}, start_date={start_date}, target_date={target_date}")
# Skip if missing required values # Skip if missing required values
if None in [current, target, start]: if None in [current, target, start]:
print(f"[DEBUG] → Skipped: Missing current/target/start")
continue continue
# Skip if no target_date (can't calculate time-based progress) # Skip if no target_date (can't calculate time-based progress)
if not target_date: if not target_date:
print(f"[DEBUG] → Skipped: No target_date")
continue continue
try: try:
@ -867,9 +863,7 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
created_at = g.get('created_at') created_at = g.get('created_at')
if created_at: if created_at:
start_dt = date.fromisoformat(str(created_at).split('T')[0]) start_dt = date.fromisoformat(str(created_at).split('T')[0])
print(f"[DEBUG] → Using created_at as start_date: {start_dt}")
else: else:
print(f"[DEBUG] → Skipped: No start_date and no created_at")
continue # Can't calculate without start date continue # Can't calculate without start date
target_dt = target_date if isinstance(target_date, date) else date.fromisoformat(str(target_date)) target_dt = target_date if isinstance(target_date, date) else date.fromisoformat(str(target_date))
@ -879,7 +873,6 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
elapsed_days = (today - start_dt).days elapsed_days = (today - start_dt).days
if total_days <= 0: if total_days <= 0:
print(f"[DEBUG] → Skipped: Invalid date range (total_days={total_days})")
continue # Invalid date range continue # Invalid date range
expected_progress_pct = (elapsed_days / total_days) * 100 expected_progress_pct = (elapsed_days / total_days) * 100
@ -892,15 +885,12 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
g['_expected_progress'] = int(expected_progress_pct) g['_expected_progress'] = int(expected_progress_pct)
g['_deviation'] = int(deviation) g['_deviation'] = int(deviation)
goals_with_deviation.append(g) goals_with_deviation.append(g)
print(f"[DEBUG] → Added: actual={int(actual_progress_pct)}%, expected={int(expected_progress_pct)}%, deviation={int(deviation)}%")
except (ValueError, ZeroDivisionError, TypeError) as e: except (ValueError, ZeroDivisionError, TypeError) as e:
print(f"[DEBUG] → Exception: {type(e).__name__}: {e}")
continue continue
# Also process goals WITHOUT target_date (simple progress) # Also process goals WITHOUT target_date (simple progress)
goals_without_date = [] goals_without_date = []
print(f"[DEBUG] Processing goals without target_date for simple progress")
for g in goals: for g in goals:
if g.get('target_date'): if g.get('target_date'):
@ -912,7 +902,6 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
start = g.get('start_value') start = g.get('start_value')
if None in [current, target, start]: if None in [current, target, start]:
print(f"[DEBUG] Goal '{goal_name}' (no date): Skipped - missing values")
continue continue
try: try:
@ -928,16 +917,13 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
g['_simple_progress'] = int(progress_pct) g['_simple_progress'] = int(progress_pct)
goals_without_date.append(g) goals_without_date.append(g)
print(f"[DEBUG] Goal '{goal_name}' (no date): Added with {int(progress_pct)}% progress")
except (ValueError, ZeroDivisionError, TypeError) as e: except (ValueError, ZeroDivisionError, TypeError) as e:
print(f"[DEBUG] Goal '{goal_name}' (no date): Exception - {e}")
continue continue
# Combine: Goals with negative deviation + Goals without date with low progress # Combine: Goals with negative deviation + Goals without date with low progress
behind_with_date = [g for g in goals_with_deviation if g['_deviation'] < 0] behind_with_date = [g for g in goals_with_deviation if g['_deviation'] < 0]
behind_without_date = [g for g in goals_without_date if g['_simple_progress'] < 50] behind_without_date = [g for g in goals_without_date if g['_simple_progress'] < 50]
print(f"[DEBUG] Behind with date: {len(behind_with_date)}, Behind without date: {len(behind_without_date)}")
# Create combined list with sort keys # Create combined list with sort keys
combined = [] combined = []
@ -1002,7 +988,6 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
today = date.today() today = date.today()
goals_with_deviation = [] goals_with_deviation = []
print(f"[DEBUG] _format_goals_on_track: Processing {len(goals)} goals")
for g in goals: for g in goals:
goal_name = g.get('name') or g.get('goal_type', 'Unknown') goal_name = g.get('name') or g.get('goal_type', 'Unknown')
@ -1012,16 +997,13 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
start_date = g.get('start_date') start_date = g.get('start_date')
target_date = g.get('target_date') target_date = g.get('target_date')
print(f"[DEBUG] Goal '{goal_name}': current={current}, target={target}, start={start}, start_date={start_date}, target_date={target_date}")
# Skip if missing required values # Skip if missing required values
if None in [current, target, start]: if None in [current, target, start]:
print(f"[DEBUG] → Skipped: Missing current/target/start")
continue continue
# Skip if no target_date # Skip if no target_date
if not target_date: if not target_date:
print(f"[DEBUG] → Skipped: No target_date")
continue continue
try: try:
@ -1065,15 +1047,12 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
g['_expected_progress'] = int(expected_progress_pct) g['_expected_progress'] = int(expected_progress_pct)
g['_deviation'] = int(deviation) g['_deviation'] = int(deviation)
goals_with_deviation.append(g) goals_with_deviation.append(g)
print(f"[DEBUG] → Added: actual={int(actual_progress_pct)}%, expected={int(expected_progress_pct)}%, deviation={int(deviation)}%")
except (ValueError, ZeroDivisionError, TypeError) as e: except (ValueError, ZeroDivisionError, TypeError) as e:
print(f"[DEBUG] → Exception: {type(e).__name__}: {e}")
continue continue
# Also process goals WITHOUT target_date (simple progress) # Also process goals WITHOUT target_date (simple progress)
goals_without_date = [] goals_without_date = []
print(f"[DEBUG] Processing goals without target_date for simple progress")
for g in goals: for g in goals:
if g.get('target_date'): if g.get('target_date'):
@ -1085,7 +1064,6 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
start = g.get('start_value') start = g.get('start_value')
if None in [current, target, start]: if None in [current, target, start]:
print(f"[DEBUG] Goal '{goal_name}' (no date): Skipped - missing values")
continue continue
try: try:
@ -1101,16 +1079,13 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
g['_simple_progress'] = int(progress_pct) g['_simple_progress'] = int(progress_pct)
goals_without_date.append(g) goals_without_date.append(g)
print(f"[DEBUG] Goal '{goal_name}' (no date): Added with {int(progress_pct)}% progress")
except (ValueError, ZeroDivisionError, TypeError) as e: except (ValueError, ZeroDivisionError, TypeError) as e:
print(f"[DEBUG] Goal '{goal_name}' (no date): Exception - {e}")
continue continue
# Combine: Goals with positive deviation + Goals without date with high progress # Combine: Goals with positive deviation + Goals without date with high progress
ahead_with_date = [g for g in goals_with_deviation if g['_deviation'] >= 0] ahead_with_date = [g for g in goals_with_deviation if g['_deviation'] >= 0]
ahead_without_date = [g for g in goals_without_date if g['_simple_progress'] >= 50] ahead_without_date = [g for g in goals_without_date if g['_simple_progress'] >= 50]
print(f"[DEBUG] Ahead with date: {len(ahead_with_date)}, Ahead without date: {len(ahead_without_date)}")
# Create combined list with sort keys # Create combined list with sort keys
combined = [] combined = []

View File

@ -360,12 +360,10 @@ def list_goals(session: dict = Depends(require_auth)):
""", (pid,)) """, (pid,))
goals = [r2d(row) for row in cur.fetchall()] goals = [r2d(row) for row in cur.fetchall()]
print(f"[DEBUG] Loaded {len(goals)} goals for profile {pid}")
# Debug: Show first goal with dates # Debug: Show first goal with dates
if goals: if goals:
first = goals[0] first = goals[0]
print(f"[DEBUG] First goal dates: start_date={first.get('start_date')} (type: {type(first.get('start_date'))}), target_date={first.get('target_date')} (type: {type(first.get('target_date'))})")
# Update current values for each goal # Update current values for each goal
for goal in goals: for goal in goals:
@ -378,11 +376,6 @@ def list_goals(session: dict = Depends(require_auth)):
# Serialize date objects to ISO format strings # Serialize date objects to ISO format strings
goals = serialize_dates(goals) goals = serialize_dates(goals)
# Debug: Show ALL goals with their dates
print(f"[DEBUG] Returning {len(goals)} goals after serialization:")
for g in goals:
print(f" Goal {g.get('id')}: goal_type={g.get('goal_type')}, start_date={g.get('start_date')}, target_date={g.get('target_date')}")
return goals return goals
except Exception as e: except Exception as e:
@ -469,14 +462,6 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
"""Update existing goal""" """Update existing goal"""
pid = session['profile_id'] pid = session['profile_id']
# Debug logging
print(f"[DEBUG] update_goal called with:")
print(f" goal_id: {goal_id}")
print(f" start_date: {data.start_date} (type: {type(data.start_date)})")
print(f" start_value: {data.start_value}")
print(f" target_date: {data.target_date}")
print(f" target_value: {data.target_value}")
with get_db() as conn: with get_db() as conn:
cur = get_cursor(conn) cur = get_cursor(conn)
@ -549,9 +534,7 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
goal_row = cur.fetchone() goal_row = cur.fetchone()
if goal_row: if goal_row:
goal_type = goal_row['goal_type'] goal_type = goal_row['goal_type']
print(f"[DEBUG] Looking up historical value for {goal_type} on or after {requested_date}")
historical_data = _get_historical_value_for_goal_type(conn, pid, goal_type, requested_date) historical_data = _get_historical_value_for_goal_type(conn, pid, goal_type, requested_date)
print(f"[DEBUG] Historical data result: {historical_data}")
if historical_data is not None: if historical_data is not None:
# Use actual measurement date and value # Use actual measurement date and value
@ -607,8 +590,6 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
params.extend([goal_id, pid]) params.extend([goal_id, pid])
update_sql = f"UPDATE goals SET {', '.join(updates)} WHERE id = %s AND profile_id = %s" update_sql = f"UPDATE goals SET {', '.join(updates)} WHERE id = %s AND profile_id = %s"
print(f"[DEBUG] UPDATE SQL: {update_sql}")
print(f"[DEBUG] UPDATE params: {params}")
cur.execute(update_sql, tuple(params)) cur.execute(update_sql, tuple(params))
@ -618,7 +599,6 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
FROM goals WHERE id = %s FROM goals WHERE id = %s
""", (goal_id,)) """, (goal_id,))
saved_goal = cur.fetchone() saved_goal = cur.fetchone()
print(f"[DEBUG] After UPDATE, goal in DB: {r2d(saved_goal)}")
return {"message": "Ziel aktualisiert"} return {"message": "Ziel aktualisiert"}
@ -768,16 +748,13 @@ def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, t
# Get goal type configuration # Get goal type configuration
config = get_goal_type_config(conn, goal_type) config = get_goal_type_config(conn, goal_type)
if not config: if not config:
print(f"[DEBUG] No config found for goal_type: {goal_type}")
return None return None
source_table = config.get('source_table') source_table = config.get('source_table')
source_column = config.get('source_column') source_column = config.get('source_column')
print(f"[DEBUG] Config: table={source_table}, column={source_column}")
if not source_table or not source_column: if not source_table or not source_column:
print(f"[DEBUG] Missing source_table or source_column")
return None return None
# Query for value closest to target_date (±7 days window) # Query for value closest to target_date (±7 days window)
@ -803,13 +780,10 @@ def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, t
""" """
params = (profile_id, target_date) params = (profile_id, target_date)
print(f"[DEBUG] Query: {query}")
print(f"[DEBUG] Params: {params}")
cur.execute(query, params) cur.execute(query, params)
row = cur.fetchone() row = cur.fetchone()
print(f"[DEBUG] Query result: {row}")
if row: if row:
value = row[source_column] value = row[source_column]
@ -827,10 +801,8 @@ def _get_historical_value_for_goal_type(conn, profile_id: str, goal_type: str, t
result_date = measurement_date result_date = measurement_date
result = {'value': result_value, 'date': result_date} result = {'value': result_value, 'date': result_date}
print(f"[DEBUG] Returning: {result}")
return result return result
print(f"[DEBUG] No data found on or after {target_date}")
return None return None
except Exception as e: except Exception as e:
print(f"[ERROR] Failed to get historical value for {goal_type} on {target_date}: {e}") print(f"[ERROR] Failed to get historical value for {goal_type} on {target_date}: {e}")