Data Layer:
- get_sleep_duration_data() - avg duration with hours/minutes breakdown
- get_sleep_quality_data() - Deep+REM percentage with phase breakdown
- get_rest_days_data() - total count + breakdown by rest type
Placeholder Layer:
- get_sleep_avg_duration() - refactored to use data layer
- get_sleep_avg_quality() - refactored to use data layer
- get_rest_days_count() - refactored to use data layer
All 3 recovery data functions + 3 placeholder refactors complete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Data Layer:
- get_activity_summary_data() - count, duration, calories, frequency
- get_activity_detail_data() - detailed activity log with all fields
- get_training_type_distribution_data() - category distribution with percentages
Placeholder Layer:
- get_activity_summary() - refactored to use data layer
- get_activity_detail() - refactored to use data layer
- get_trainingstyp_verteilung() - refactored to use data layer
All 3 activity data functions + 3 placeholder refactors complete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Data Layer:
- get_nutrition_average_data() - all macros in one call
- get_nutrition_days_data() - coverage tracking
- get_protein_targets_data() - 1.6g/kg and 2.2g/kg targets
- get_energy_balance_data() - deficit/surplus/maintenance
- get_protein_adequacy_data() - 0-100 score
- get_macro_consistency_data() - 0-100 score
Placeholder Layer:
- get_nutrition_avg() - refactored to use data layer
- get_nutrition_days() - refactored to use data layer
- get_protein_ziel_low() - refactored to use data layer
- get_protein_ziel_high() - refactored to use data layer
All 6 nutrition data functions + 4 placeholder refactors complete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ROOT CAUSE: get_active_goals() SELECT was missing start_date and created_at
IMPACT: Time-based deviation calculation failed silently for all goals
Now returns:
- start_date: Required for accurate time-based progress calculation
- created_at: Fallback when start_date is not set
This fixes:
- Zielgewicht (weight) should now show +7% ahead
- Körperfett should show time deviation
- All goals with target_date now have time-based tracking
- Log when using created_at as fallback for start_date
- Log when skipping due to missing created_at
- Log when skipping due to invalid date range (total_days <= 0)
This will reveal exactly why Körperfett and Zielgewicht are not added.
- Log each goal processing (name, values, dates)
- Log skip reasons (missing values, no target_date)
- Log exceptions during calculation
- Log successful additions with calculated values
This will reveal why Weight goal (+7% ahead) is not showing up.
OLD: Showed 3 goals with lowest progress %
NEW: Calculates expected progress based on elapsed time vs. total time
Shows goals with largest negative deviation (behind schedule)
Example Weight Goal:
- Total time: 98 days (22.02 - 31.05)
- Elapsed: 34 days (35%)
- Actual progress: 41%
- Deviation: +7% (AHEAD, not behind)
Also updated on_track to show goals with positive deviation (ahead of schedule).
Note: Linear progress is a simplification. Real-world progress curves vary
by goal type (weight loss, muscle gain, VO2max, etc). Future: AI-based
projection models for more realistic expectations.
- Start value already showed start_date in parentheses
- Now target value also shows target_date in parentheses
- Consistent UX: both dates visible at their respective values
Root cause: listGoalsGrouped() SELECT was missing g.start_date and g.reached_date
Result: Frontend used grouped goals for editing, so start_date was undefined
This is why target_date worked (it was in SELECT) but start_date didn't.
- Added serialize_dates() helper to convert date objects to strings
- Applied to list_goals and get_goals_grouped endpoints
- Fixes issue where start_date was saved but not visible in frontend
- Python datetime.date objects need explicit .isoformat() conversion
Root cause: FastAPI doesn't auto-serialize all date types consistently
- Log UPDATE SQL and parameters
- Verify saved values after UPDATE
- Show date types in list_goals response
- Track down why start_date not visible in UI
- Rewrote update logic to determine final_start_date/start_value first
- Then append to updates/params arrays (ensures alignment)
- Fixes bug where only start_value was saved but not start_date
User feedback: start_value correctly calculated but start_date not persisted
**Problem 1:** Edit form showed today's date instead of stored start_date
- Cause: Fallback logic `goal.start_date || today` always defaulted to today
- Fix: Load actual date or empty string (no fallback)
- Input field: Remove fallback from value binding
**Problem 2:** Timeline only showed target_date, not start_date
- Added dedicated timeline display below values
- Shows: "📅 15.01.26 → 31.05.26"
- Only appears if at least one date exists
- Start date with calendar icon, target date bold
**Result:**
- Editing goals now preserves the start_date ✓
- Timeline clearly shows start → target dates ✓
- No more accidental overwrites with today's date ✓
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**User Feedback:** "Macht es nicht Sinn, den nächsten verfügbaren Wert
am oder nach dem Startdatum automatisch zu ermitteln und auch das
Startdatum dann automatisch auf den Wert zu setzen?"
**New Logic:**
1. User sets start_date: 2026-01-01
2. System finds FIRST measurement >= 2026-01-01 (e.g., 2026-01-15: 88 kg)
3. System auto-adjusts:
- start_date → 2026-01-15
- start_value → 88 kg
4. User sees: "Start: 88 kg (15.01.26)" ✓
**Benefits:**
- User doesn't need to know exact date of first measurement
- More user-friendly UX
- Automatically finds closest available data
**Implementation:**
- Changed query from "BETWEEN date ±7 days" to "WHERE date >= target_date"
- Returns dict with {'value': float, 'date': date}
- Both create_goal() and update_goal() now adjust start_date automatically
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**Error:**
function pg_catalog.extract(unknown, integer) does not exist
HINT: No function matches the given name and argument types.
**Problem:**
In PostgreSQL, date - date returns INTEGER (days), not INTERVAL.
EXTRACT(EPOCH FROM integer) fails because EPOCH expects timestamp/interval.
**Solution:**
Changed from:
ORDER BY ABS(EXTRACT(EPOCH FROM (date - '2026-01-01')))
To:
ORDER BY ABS(date - '2026-01-01'::date)
This directly uses the day difference (integer) for sorting,
which is exactly what we need to find the closest date.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**Bug:** start_date was not being loaded into edit form or sent in update request
**Fixed:**
1. handleEditGoal() - Added start_date to formData when editing
2. handleSaveGoal() - Added start_date to API payload for both create and update
Now when editing a goal:
- start_date field is populated with existing value
- Changing start_date triggers backend to recalculate start_value
- Update request includes start_date
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added start_date field to goal creation/editing form:
1. New "Startdatum" input field before "Zieldatum"
- Defaults to today
- Hint: "Startwert wird automatisch aus historischen Daten ermittelt"
2. Display start_date in goals list
- Shows next to start_value: "85 kg (01.01.26)"
- Compact format for better readability
3. Updated formData state
- Added start_date with today as default
- API calls automatically include it
User can now:
- Set historical start date (e.g., 3 months ago)
- Backend auto-populates start_value from that date
- See exact start date and value for each goal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**Problem:** Goals created today had start_value = current_value,
showing 0% progress even after months of tracking.
**Solution:**
1. Added start_date and start_value to GoalCreate/GoalUpdate models
2. New function _get_historical_value_for_goal_type():
- Queries source table for value on specific date
- ±7 day window for closest match
- Works with all goal types via goal_type_definitions
3. create_goal() logic:
- If start_date < today → auto-populate from historical data
- If start_date = today → use current value
- User can override start_value manually
4. update_goal() logic:
- Changing start_date recalculates start_value
- Can manually override start_value
**Example:**
- Goal created today with start_date = 3 months ago
- System finds weight on that date (88 kg)
- Current weight: 85.2 kg, Target: 82 kg
- Progress: (85.2 - 88) / (82 - 88) = 47% ✓
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Goal progress placeholders were filtering out all goals because
start_value was missing from the SELECT statement.
Added start_value to both:
- get_active_goals() - for placeholder formatters
- get_goal_by_id() - for consistency
This will fix:
- active_goals_md progress column (was all "-")
- top_3_goals_behind_schedule (was "keine Ziele")
- top_3_goals_on_track (was "keine Ziele")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed 2 critical placeholder issues:
1. focus_areas_weighted_json was empty:
- Query used 'area_key' but column is 'key' in focus_area_definitions
- Changed to SELECT key, not area_key
2. Goal progress placeholders showed "nicht verfügbar":
- progress_pct in goals table is NULL (not auto-calculated)
- Added manual calculation in all 3 formatter functions:
* _format_goals_as_markdown() - shows % in table
* _format_goals_behind() - finds lowest progress
* _format_goals_on_track() - finds >= 50% progress
All placeholders should now return proper values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TypeError: unsupported operand type(s) for -: 'decimal.Decimal' and 'float'
PostgreSQL NUMERIC columns return Decimal objects. Must convert
current_value, target_value, start_value to float before passing
to calculate_goal_progress_pct().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed remaining placeholder calculation issues:
1. body_progress_score returning 0:
- When start_value is NULL, query oldest weight from last 90 days
- Prevents progress = 0% when start equals current
2. focus_areas_weighted_json empty:
- Changed from goal_utils.get_focus_weights_v2() to scores.get_user_focus_weights()
- Now uses same function as focus_area_weights_json
3. Implemented 5 TODO markdown formatting functions:
- _format_goals_as_markdown() - table with progress bars
- _format_focus_areas_as_markdown() - weighted list
- _format_top_focus_areas() - top N by weight
- _format_goals_behind() - lowest progress goals
- _format_goals_on_track() - goals >= 50% progress
All placeholders should now return proper values.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: Two TODO stubs always returned '[]'
Implemented:
- active_goals_json: Calls get_active_goals() from goal_utils
- focus_areas_weighted_json: Builds weighted list with names/categories
Result:
- active_goals_json now shows actual goals
- body_progress_score should calculate correctly
- top_3_goals placeholders will work
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous commit only converted weight values, but missed:
- avg_intake (calories from DB)
- avg_protein (protein_g from DB)
- protein_per_kg calculations in loops
All DB numeric values now converted to float BEFORE arithmetic.
Fixed locations:
- Line 44: avg_intake conversion
- Line 126: avg_protein conversion
- Line 175: protein_per_kg in loop
- Line 213: protein_values list comprehension
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float'
TypeError: unsupported operand type(s) for -: 'float' and 'decimal.Decimal'
PostgreSQL NUMERIC/DECIMAL columns return decimal.Decimal objects,
not float. These cannot be mixed in arithmetic operations.
Fixed 3 locations:
- Line 62: float(weight_row['weight']) * 32.5
- Line 153: float(weight_row['weight']) for protein_per_kg
- Line 202: float(weight_row['avg_weight']) for adequacy calc
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ImportError: cannot import name 'get_goals_by_type' from 'goal_utils'
Changes:
- body_metrics.py: Use get_active_goals() + filter by type_key
- nutrition_metrics.py: Remove unused import (dead code)
Result: Score functions no longer crash on import error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: All 3 score functions returned None because they queried
German focus area keys that don't exist in database (migration 031
uses English keys).
Changes:
- body_progress_score: körpergewicht/körperfett/muskelmasse
→ weight_loss/muscle_gain/body_recomposition
- nutrition_score: ernährung_basis/proteinzufuhr/kalorienbilanz
→ protein_intake/calorie_balance/macro_consistency/meal_timing/hydration
- activity_score: kraftaufbau/cardio/bewegungsumfang/trainingsqualität
→ strength/aerobic_endurance/flexibility/rhythm/coordination (grouped)
Result: Scores now calculate correctly with existing focus area weights.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Calculates average count per week over 30 days
- Use case: Training frequency per week (smoothed)
- Formula: (count in 30 days) / 4.285 weeks
- Documentation: .claude/docs/technical/AGGREGATION_METHODS.md
Fixed remaining sleep_log column name errors in calculate_health_stability_score:
- SELECT: total_sleep_min, deep_min, rem_min → duration_minutes, deep_minutes, rem_minutes
- _score_sleep_quality: Updated dict access to use new column names
This was blocking goal_progress_score from calculating.
Changes:
- scores.py: Fixed sleep_log SELECT query and _score_sleep_quality dict access
This should be the LAST column name bug! All Phase 0b calculations should now work.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Final bug fixes:
1. blood_pressure_log query - changed 'date' column to 'measured_at' (correct column for TIMESTAMP)
2. top_goal_name KeyError - added 'name' to SELECT in get_active_goals()
3. top_goal_name fallback - use goal_type if name is NULL
Changes:
- scores.py: Fixed blood_pressure_log query to use measured_at instead of date
- goal_utils.py: Added 'name' column to get_active_goals() SELECT
- placeholder_resolver.py: Added fallback to goal_type if name is None
These were the last 2 errors showing in logs. All major calculation bugs should now be fixed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>