docs: cleanup debug logs + document goal system enhancements
- 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:
parent
dd395180a3
commit
255d1d61c5
40
CLAUDE.md
40
CLAUDE.md
|
|
@ -82,7 +82,45 @@ frontend/src/
|
|||
**Branch:** develop
|
||||
**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 ✅
|
||||
- ✅ **Migration 031-032:** Vollständiges dynamisches System
|
||||
|
|
|
|||
|
|
@ -824,7 +824,6 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
|
|||
today = date.today()
|
||||
goals_with_deviation = []
|
||||
|
||||
print(f"[DEBUG] _format_goals_behind: Processing {len(goals)} goals")
|
||||
|
||||
for g in goals:
|
||||
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')
|
||||
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
|
||||
if None in [current, target, start]:
|
||||
print(f"[DEBUG] → Skipped: Missing current/target/start")
|
||||
continue
|
||||
|
||||
# Skip if no target_date (can't calculate time-based progress)
|
||||
if not target_date:
|
||||
print(f"[DEBUG] → Skipped: No target_date")
|
||||
continue
|
||||
|
||||
try:
|
||||
|
|
@ -867,9 +863,7 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
|
|||
created_at = g.get('created_at')
|
||||
if created_at:
|
||||
start_dt = date.fromisoformat(str(created_at).split('T')[0])
|
||||
print(f"[DEBUG] → Using created_at as start_date: {start_dt}")
|
||||
else:
|
||||
print(f"[DEBUG] → Skipped: No start_date and no created_at")
|
||||
continue # Can't calculate without start 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
|
||||
|
||||
if total_days <= 0:
|
||||
print(f"[DEBUG] → Skipped: Invalid date range (total_days={total_days})")
|
||||
continue # Invalid date range
|
||||
|
||||
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['_deviation'] = int(deviation)
|
||||
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:
|
||||
print(f"[DEBUG] → Exception: {type(e).__name__}: {e}")
|
||||
continue
|
||||
|
||||
# Also process goals WITHOUT target_date (simple progress)
|
||||
goals_without_date = []
|
||||
print(f"[DEBUG] Processing goals without target_date for simple progress")
|
||||
|
||||
for g in goals:
|
||||
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')
|
||||
|
||||
if None in [current, target, start]:
|
||||
print(f"[DEBUG] Goal '{goal_name}' (no date): Skipped - missing values")
|
||||
continue
|
||||
|
||||
try:
|
||||
|
|
@ -928,16 +917,13 @@ def _format_goals_behind(profile_id: str, n: int = 3) -> str:
|
|||
|
||||
g['_simple_progress'] = int(progress_pct)
|
||||
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:
|
||||
print(f"[DEBUG] Goal '{goal_name}' (no date): Exception - {e}")
|
||||
continue
|
||||
|
||||
# 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_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
|
||||
combined = []
|
||||
|
|
@ -1002,7 +988,6 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
|
|||
today = date.today()
|
||||
goals_with_deviation = []
|
||||
|
||||
print(f"[DEBUG] _format_goals_on_track: Processing {len(goals)} goals")
|
||||
|
||||
for g in goals:
|
||||
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')
|
||||
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
|
||||
if None in [current, target, start]:
|
||||
print(f"[DEBUG] → Skipped: Missing current/target/start")
|
||||
continue
|
||||
|
||||
# Skip if no target_date
|
||||
if not target_date:
|
||||
print(f"[DEBUG] → Skipped: No target_date")
|
||||
continue
|
||||
|
||||
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['_deviation'] = int(deviation)
|
||||
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:
|
||||
print(f"[DEBUG] → Exception: {type(e).__name__}: {e}")
|
||||
continue
|
||||
|
||||
# Also process goals WITHOUT target_date (simple progress)
|
||||
goals_without_date = []
|
||||
print(f"[DEBUG] Processing goals without target_date for simple progress")
|
||||
|
||||
for g in goals:
|
||||
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')
|
||||
|
||||
if None in [current, target, start]:
|
||||
print(f"[DEBUG] Goal '{goal_name}' (no date): Skipped - missing values")
|
||||
continue
|
||||
|
||||
try:
|
||||
|
|
@ -1101,16 +1079,13 @@ def _format_goals_on_track(profile_id: str, n: int = 3) -> str:
|
|||
|
||||
g['_simple_progress'] = int(progress_pct)
|
||||
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:
|
||||
print(f"[DEBUG] Goal '{goal_name}' (no date): Exception - {e}")
|
||||
continue
|
||||
|
||||
# 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_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
|
||||
combined = []
|
||||
|
|
|
|||
|
|
@ -360,12 +360,10 @@ def list_goals(session: dict = Depends(require_auth)):
|
|||
""", (pid,))
|
||||
|
||||
goals = [r2d(row) for row in cur.fetchall()]
|
||||
print(f"[DEBUG] Loaded {len(goals)} goals for profile {pid}")
|
||||
|
||||
# Debug: Show first goal with dates
|
||||
if goals:
|
||||
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
|
||||
for goal in goals:
|
||||
|
|
@ -378,11 +376,6 @@ def list_goals(session: dict = Depends(require_auth)):
|
|||
# Serialize date objects to ISO format strings
|
||||
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
|
||||
|
||||
except Exception as e:
|
||||
|
|
@ -469,14 +462,6 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
|
|||
"""Update existing goal"""
|
||||
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:
|
||||
cur = get_cursor(conn)
|
||||
|
||||
|
|
@ -549,9 +534,7 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
|
|||
goal_row = cur.fetchone()
|
||||
if goal_row:
|
||||
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)
|
||||
print(f"[DEBUG] Historical data result: {historical_data}")
|
||||
|
||||
if historical_data is not None:
|
||||
# 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])
|
||||
|
||||
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))
|
||||
|
||||
|
|
@ -618,7 +599,6 @@ def update_goal(goal_id: str, data: GoalUpdate, session: dict = Depends(require_
|
|||
FROM goals WHERE id = %s
|
||||
""", (goal_id,))
|
||||
saved_goal = cur.fetchone()
|
||||
print(f"[DEBUG] After UPDATE, goal in DB: {r2d(saved_goal)}")
|
||||
|
||||
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
|
||||
config = get_goal_type_config(conn, goal_type)
|
||||
if not config:
|
||||
print(f"[DEBUG] No config found for goal_type: {goal_type}")
|
||||
return None
|
||||
|
||||
source_table = config.get('source_table')
|
||||
source_column = config.get('source_column')
|
||||
|
||||
print(f"[DEBUG] Config: table={source_table}, column={source_column}")
|
||||
|
||||
if not source_table or not source_column:
|
||||
print(f"[DEBUG] Missing source_table or source_column")
|
||||
return None
|
||||
|
||||
# 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)
|
||||
|
||||
print(f"[DEBUG] Query: {query}")
|
||||
print(f"[DEBUG] Params: {params}")
|
||||
|
||||
cur.execute(query, params)
|
||||
|
||||
row = cur.fetchone()
|
||||
print(f"[DEBUG] Query result: {row}")
|
||||
|
||||
if row:
|
||||
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 = {'value': result_value, 'date': result_date}
|
||||
print(f"[DEBUG] Returning: {result}")
|
||||
return result
|
||||
|
||||
print(f"[DEBUG] No data found on or after {target_date}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to get historical value for {goal_type} on {target_date}: {e}")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user