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
**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>
**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>
This file was replaced by the refactored vitals system:
- vitals_baseline.py (morning measurements)
- blood_pressure.py (BP tracking with context)
Migration 015 completed the split in v9d Phase 2d.
File was no longer imported in main.py.
Cleanup result: -684 lines of dead code
Bug 1 Final Fix:
- Changed all placeholders from $1, $2, $3 to %s
- psycopg2 expects Python-style %s, converts to $N internally
- Using $N directly causes 'there is no parameter $1' error
- Removed param_idx counter (not needed with %s)
Root cause: Mixing PostgreSQL native syntax with psycopg2 driver
This is THE fix that will finally work!
Bug 3 Fix: filter_conditions was missing from SELECT statement in
list_goal_type_definitions(), preventing edit form from loading
existing filter JSON.
- Added filter_conditions to line 1087
- Now edit form correctly populates filter textarea
**Bug:** POST /api/vitals/baseline threw UndefinedParameter
**Cause:** Dynamic SQL generation had desynchronized column names and placeholders
**Fix:** Rewrote to use synchronized insert_cols, insert_placeholders, update_fields arrays
- Track param_idx correctly (start at 3 after pid and date)
- Build INSERT columns and placeholders in parallel
- Cleaner, more maintainable code
- Fixes Ruhepuls entry error
**Bug 1: Focus contributions not saved**
- GoalsPage: Added focus_contributions to data object (line 232)
- Was missing from API payload, causing loss of focus area assignments
**Bug 2: Filter focus areas in goal form**
- Only show focus areas user has weighted (weight > 0)
- Cleaner UX, avoids confusion with non-prioritized areas
- Filters focusAreasGrouped by userFocusWeights
**Bug 3: Vitals RHR entry - Internal Server Error**
- Fixed: Endpoint tried to INSERT into vitals_log (renamed in Migration 015)
- Now uses vitals_baseline table (correct post-migration table)
- Removed BP fields from baseline endpoint (use /blood-pressure instead)
- Backward compatible return format
All fixes tested and ready for production.
**Migration 032:**
- user_focus_area_weights table (profile_id, focus_area_id, weight)
- Migrates legacy 6 preferences to dynamic weights
**Backend (focus_areas.py):**
- GET /user-preferences: Returns dynamic focus weights with percentages
- PUT /user-preferences: Saves user weights (dict: focus_area_id → weight)
- Auto-calculates percentages from relative weights
- Graceful fallback if Migration 032 not applied
**Frontend (GoalsPage.jsx):**
- REMOVED: Goal Mode cards (obsolete)
- REMOVED: 6 hardcoded legacy focus sliders
- NEW: Dynamic focus area cards (weight > 0 only)
- NEW: Edit mode with sliders for all 26 areas (grouped by category)
- Clean responsive design
**How it works:**
1. Admin defines focus areas in /admin/focus-areas (26 default)
2. User sets weights for areas they care about (0-100 relative)
3. System calculates percentages automatically
4. Cards show only weighted areas
5. Goals assign to 1-n focus areas (existing functionality)
- get_focus_areas now tries user_focus_preferences first (Migration 031)
- Falls back to old focus_areas table if Migration 031 not applied
- get_goals_grouped wraps focus_contributions loading in try/catch
- Graceful degradation until migrations run
- Wrap focus_contributions loading in try/catch
- If tables don't exist (migration not run), continue without them
- Backward compatible with pre-migration state
- Logs warning but doesn't crash
- Changed prefix from '/focus-areas' to '/api/focus-areas'
- Consistent with all other routers (goals, prompts, etc.)
- Fixes 404 Not Found on /admin/focus-areas page
**Backend Safeguards:**
- get_goals_grouped: Added source_table, source_column, direction to SELECT
- create_goal_progress: Check source_table before allowing manual entry
- Returns HTTP 400 if user tries to log progress for automatic goals (weight, activity, etc.)
**Prevents:**
- Data confusion: Manual entries in goal_progress_log for weight/activity/etc.
- Dual tracking: Same data in multiple tables
- User error: Wrong data entry location
**Result:**
- Frontend filter (!goal.source_table) now works correctly
- CustomGoalsPage shows ONLY custom goals (flexibility, strength, etc.)
- Clear error message if manual entry attempted via API
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implemented progress tracking system for all goals.
**Backend:**
- Migration 030: goal_progress_log table with unique constraint per day
- Trigger: Auto-update goal.current_value from latest progress
- Endpoints: GET/POST/DELETE /api/goals/{id}/progress
- Pydantic Models: GoalProgressCreate, GoalProgressUpdate
**Features:**
- Manual progress tracking for custom goals (flexibility, strength, etc.)
- Full history with date, value, note
- current_value always reflects latest progress entry
- One entry per day per goal (unique constraint)
- Cascade delete when goal is deleted
**API:**
- GET /api/goals/{goal_id}/progress - List all entries
- POST /api/goals/{goal_id}/progress - Log new progress
- DELETE /api/goals/{goal_id}/progress/{progress_id} - Delete entry
**Next:** Frontend UI (progress button, modal, history list)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
goals table doesn't have is_active column.
Removed AND g.is_active = true from WHERE clause.
Fixes: psycopg2.errors.UndefinedColumn: column g.is_active does not exist
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed SQL error: column g.linear_projection does not exist
Replaced with: g.on_track, g.projection_date (actual columns)
This was causing Internal Server Error on /api/goals/grouped
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BREAKING: Replaces single 'primary goal' with weighted multi-goal system
Migration 027:
- New table: focus_areas (6 dimensions with percentages)
- Constraint: Sum must equal 100%
- Auto-migration: goal_mode → focus_areas for existing users
- Unique constraint: One active focus_areas per profile
Backend:
- get_focus_weights() V2: Reads from focus_areas table
- Fallback: Uses goal_mode if focus_areas not set
- New endpoints: GET/PUT /api/goals/focus-areas
- Validation: Sum=100, range 0-100
API:
- getFocusAreas() - Get current weights
- updateFocusAreas(data) - Update weights (upsert)
Focus dimensions:
1. weight_loss_pct (Fettabbau)
2. muscle_gain_pct (Muskelaufbau)
3. strength_pct (Kraftsteigerung)
4. endurance_pct (Ausdauer)
5. flexibility_pct (Beweglichkeit)
6. health_pct (Allgemeine Gesundheit)
Benefits:
- Multiple goals with custom priorities
- More flexible than single primary goal
- KI can use weighted scores
- Ready for Phase 0b placeholder integration
UI: Coming in next commit (slider interface)
NEW FEATURE: Filter conditions for goal types
Enables counting/aggregating specific subsets of data.
Example use case: Count only strength training sessions per week
- Create goal type with filter: {"training_type": "strength"}
- count_7d now counts only strength training, not all activities
Implementation:
- Migration 026: filter_conditions JSONB column
- Backend: Dynamic WHERE clause building from JSON filters
- Supports single value: {"training_type": "strength"}
- Supports multiple values: {"training_type": ["strength", "hiit"]}
- Works with all 8 aggregation methods (count, avg, sum, min, max)
- Frontend: JSON textarea with example + validation
- Pydantic models: filter_conditions field added
Technical details:
- SQL injection safe (parameterized queries)
- Graceful degradation (invalid JSON ignored with warning)
- Backward compatible (NULL filters = no filtering)
Answers user question: 'Kann ich Trainingstypen wie Krafttraining separat zählen?'
Answer: YES! 🎯
Admin can now easily create custom goal types:
- New endpoint /api/goals/schema-info with table/column metadata
- 9 tables documented (weight, caliper, activity, nutrition, sleep, vitals, BP, rest_days, circumference)
- Table dropdown with descriptions (e.g., 'activity_log - Trainingseinheiten')
- Column dropdown dependent on selected table
- All columns documented in German with data types
- Fields optional (for complex calculation formulas)
UX improvements:
- No need to guess table/column names
- Clear descriptions for each field
- Type-safe selection (no typos)
- Cascading dropdowns (column depends on table)
Closes user feedback: 'Admin weiß nicht welche Tabellen/Spalten verfügbar sind'
- try-catch around entire endpoint
- try-catch for each goal progress update
- Detailed error logging with traceback
- Continue processing other goals if one fails
- Clear error message to frontend
This will show exact error location in logs.
Goal type definitions are global system entities, not user-specific.
System types seeded in migration cannot have created_by FK.
Changes:
- Remove created_by/updated_by columns from goal_type_definitions
- Update CREATE/UPDATE endpoints to not use these fields
- Migration now runs cleanly on container start
- No manual intervention needed for production deployment
Removed faulty EXISTS check that was causing "0" error.
Added debug logging and better error messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Check if goal_type_definitions table exists
- Detailed error messages
- Fallback if goalTypes is empty
- Prevent form opening without types
Helps debugging Migration 024 issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Based on test feedback - 3 issues addressed:
1. Primary Toggle (Frontend Debug):
- Add console.log in handleSaveGoal
- Shows what data is sent to backend
- Helps debug if checkbox state is correct
2. Lean Mass Display (Backend Debug):
- Add error handling in lean_mass calculation
- Log why calculation fails (missing weight/bf data)
- Try-catch for value conversion errors
3. BP/Strength/Flexibility Warning (UI):
- Yellow warning box for incomplete goal types
- BP: "benötigt 2 Werte (geplant für v2.0)"
- Strength/Flexibility: "Keine Datenquelle"
- Transparent about limitations
Next: User re-tests with debug output to identify root cause.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The migration system tracks migrations via filename automatically.
Removed manual DO block that used wrong column name (version vs filename).
Also removed unused json import from goals.py.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend:
- Add ALL stage outputs to metadata (not just referenced ones)
- Format JSON with indent for readability
- Description: 'Zwischenergebnis aus Stage X'
Frontend:
- Stage raw values shown in collapsible <details> element
- JSON formatted in <pre> tag with syntax highlighting
- 'JSON anzeigen ▼' summary for better UX
Fixes: Stage X - Rohdaten now shows intermediate results