Commit Graph

109 Commits

Author SHA1 Message Date
b317246bcd docs: Quality-Level Parameter für KI-Analysen notiert (#28)
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Notiert an 3 Stellen:
1. insights.py: TODO-Kommentar im Code
2. ROADMAP.md: Deliverable bei M0.2 (lokal, nicht im Git)
3. Gitea Issue #28: Kommentar mit Spezifikation

Zukünftig:
- GET /api/insights/run/{slug}?quality_level=quality
- 4 Stufen: all, quality, very_good, excellent
- Frontend: Dropdown wie in History.jsx
- Pipeline-Configs können Standard-Level haben

User-Request: Quality-Level-Auswahl für KI-Analysen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 22:06:30 +01:00
9ec774e956 feat: Quality-Filter für KI-Pipeline & History (#24)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 14s
Backend:
- insights.py: KI-Pipeline filtert activity_log nach quality_label
- Nur 'excellent', 'good', 'acceptable' (poor wird ausgeschlossen)
- NULL-Werte erlaubt (für alte Einträge vor Migration 014)

Frontend:
- History.jsx: Toggle "Nur qualitativ hochwertige Aktivitäten"
- Filter wirkt auf Activity-Statistiken, Charts, Listen
- Anzeige: X von Y Activities (wenn gefiltert)

Dokumentation:
- CLAUDE.md: Feature-Roadmap aktualisiert (Phase 0-2)

Closes #24

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 21:59:02 +01:00
6f035e3706 fix: handle decimal values in Apple Health vitals import
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Problem: Import failed with "invalid literal for int() with base 10: '37.95'"
because Apple Health exports HRV and other vitals with decimal values.

Root cause: Code used int() directly on string values with decimals.

Fix:
- Added safe_int(): parses decimals as float first, then rounds to int
- Added safe_float(): robust float parsing with error handling
- Applied to all vital value parsing: RHR, HRV, VO2 Max, SpO2, resp rate

Example: '37.95' → float(37.95) → int(38) ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:50:08 +01:00
6b64cf31c4 fix: return error details in import response for debugging
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Problem: Errors during import were logged but not visible to user.

Changes:
- Backend: Collect error messages and return in response (first 10 errors)
- Frontend: Display error details in import result box
- UI: Red background when errors > 0, shows detailed error messages

Now users can see exactly which rows failed and why.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:47:36 +01:00
4b024e6d0f debug: add detailed error logging with traceback for import failures
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
2026-03-23 16:44:16 +01:00
f506a55d7b fix: support German column names in CSV imports
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Problem: Import expected English column names, but German Apple Health/Omron
exports use German names with units.

Fixed:
- Apple Health: Support both English and German column names
  - "Start" OR "Datum/Uhrzeit"
  - "Resting Heart Rate" OR "Ruhepuls (count/min)"
  - "Heart Rate Variability" OR "Herzfrequenzvariabilität (ms)"
  - "VO2 Max" OR "VO2 max (ml/(kg·min))"
  - "Oxygen Saturation" OR "Blutsauerstoffsättigung (%)"
  - "Respiratory Rate" OR "Atemfrequenz (count/min)"

- Omron: Support column names with/without units
  - "Systolisch (mmHg)" OR "Systolisch"
  - "Diastolisch (mmHg)" OR "Diastolisch"
  - "Puls (bpm)" OR "Puls"
  - "Unregelmäßiger Herzschlag festgestellt" OR "Unregelmäßiger Herzschlag"
  - "Mögliches AFib" OR "Vorhofflimmern"

Added debug logging for both imports to show detected columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:40:49 +01:00
6a7b78c3eb debug: add logging to Apple Health import to diagnose skipped rows
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Logs:
- CSV column names from first row
- Rows skipped due to missing date
- Rows skipped due to no vitals data
- Shows which fields were found/missing

Helps diagnose CSV format mismatches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:38:18 +01:00
7dcab1d7a3 fix: correct import skipped count when manual entries exist
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 14s
Problem: Import reported all entries as "updated" even when skipped
due to WHERE clause (source != 'manual')

Root cause: RETURNING returns NULL when WHERE clause prevents update,
but code counted NULL as "updated" instead of "skipped"

Fix:
- Check if result is None → skipped (WHERE prevented update)
- Check if xmax = 0 → inserted (new row)
- Otherwise → updated (existing row modified)

Affects:
- vitals_baseline.py: Apple Health import
- blood_pressure.py: Omron import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:35:07 +01:00
1866ff9ce6 refactor: vitals architecture - separate baseline vs blood pressure
Some checks failed
Build Test / lint-backend (push) Waiting to run
Build Test / build-frontend (push) Waiting to run
Deploy Development / deploy (push) Has been cancelled
BREAKING CHANGE: vitals_log split into vitals_baseline + blood_pressure_log

**Architektur-Änderung:**
- Baseline-Vitals (langsam veränderlich, 1x täglich morgens)
  → vitals_baseline (RHR, HRV, VO2 Max, SpO2, Atemfrequenz)
- Kontext-abhängige Vitals (mehrfach täglich, situativ)
  → blood_pressure_log (Blutdruck + Kontext-Tagging)

**Migration 015:**
- CREATE TABLE vitals_baseline (once daily, morning measurements)
- CREATE TABLE blood_pressure_log (multiple daily, context-aware)
- Migrate data from vitals_log → new tables
- Rename vitals_log → vitals_log_backup_pre_015 (safety)
- Prepared for future: glucose_log, temperature_log (commented)

**Backend:**
- NEW: routers/vitals_baseline.py (CRUD + Apple Health import)
- NEW: routers/blood_pressure.py (CRUD + Omron import + context)
- UPDATED: main.py (register new routers, remove old vitals)
- UPDATED: insights.py (query new tables, split template vars)

**Frontend:**
- UPDATED: api.js (new endpoints für baseline + BP)
- UPDATED: Analysis.jsx (add {{bp_summary}} variable)

**Nächster Schritt:**
- Frontend: VitalsPage.jsx refactoren (3 Tabs: Morgenmessung, Blutdruck, Import)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:02:40 +01:00
1619091640 fix: add python-dateutil dependency for vitals CSV import
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
ModuleNotFoundError: No module named 'dateutil' beim Server-Start.
Ursache: vitals.py importiert dateutil.parser für Omron-Datumsformatierung,
aber python-dateutil fehlte in requirements.txt.

Fix: python-dateutil==2.9.0 zu requirements.txt hinzugefügt.

Nach dem Update: Docker Container neu bauen auf dem Pi:
  cd /home/lars/docker/bodytrack-dev
  docker compose -f docker-compose.dev-env.yml build --no-cache backend
  docker compose -f docker-compose.dev-env.yml up -d

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:41:30 +01:00
37fd28ec5a feat: add AI evaluation placeholders for v9d Phase 2 modules
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
**Backend (insights.py):**
- Extended _get_profile_data() to fetch sleep, rest_days, vitals
- Added template variables for Sleep Module:
  {{sleep_summary}}, {{sleep_detail}}, {{sleep_avg_duration}}, {{sleep_avg_quality}}
- Added template variables for Rest Days:
  {{rest_days_summary}}, {{rest_days_count}}, {{rest_days_types}}
- Added template variables for Vitals:
  {{vitals_summary}}, {{vitals_detail}}, {{vitals_avg_hr}}, {{vitals_avg_hrv}},
  {{vitals_avg_bp}}, {{vitals_vo2_max}}

**Frontend (Analysis.jsx):**
- Added 12 new template variables to VARS list in PromptEditor
- Enables AI prompt creation for Sleep, Rest Days, and Vitals analysis

All modules now have AI evaluation support for future prompt creation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:30:17 +01:00
548a5a481d feat: add CSV import for Vitals (Omron + Apple Health)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
- Import endpoints for Omron blood pressure CSV (German date format)
- Import endpoints for Apple Health vitals CSV
- Import UI tab in VitalsPage with drag & drop for both sources
- German month mapping for Omron date parsing ("13 März 2026")
- Upsert logic preserves manual entries (source != 'manual')
- Import result feedback (inserted/updated/skipped/errors)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:26:51 +01:00
a55f11bc96 feat: add blood pressure, VO2 max, and SpO2 to vitals stats
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Avg blood pressure (systolic/diastolic) 7d and 30d
- Latest VO2 Max value
- Avg SpO2 7d and 30d
- Backend now provides all metrics expected by frontend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:18:13 +01:00
4f53cfffab feat: extend vitals with blood pressure, VO2 max, SpO2, respiratory rate
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Migration 014:
- blood_pressure_systolic/diastolic (mmHg)
- pulse (bpm) - during BP measurement
- vo2_max (ml/kg/min) - from Apple Watch
- spo2 (%) - blood oxygen saturation
- respiratory_rate (breaths/min)
- irregular_heartbeat, possible_afib (boolean flags from Omron)
- Added 'omron' to source enum

Backend:
- Updated Pydantic models (VitalsEntry, VitalsUpdate)
- Updated all SELECT queries to include new fields
- Updated INSERT/UPDATE with COALESCE for partial updates
- Validation: at least one vital must be provided

Preparation for Omron + Apple Health imports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:14:34 +01:00
4191c52298 feat: implement Vitals module (Ruhepuls + HRV)
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Backend:
- New router: vitals.py with CRUD endpoints
- GET /api/vitals (list)
- GET /api/vitals/by-date/{date}
- POST /api/vitals (upsert)
- PUT /api/vitals/{id}
- DELETE /api/vitals/{id}
- GET /api/vitals/stats (7d/30d averages, trends)
- Registered in main.py

Frontend:
- VitalsPage.jsx with manual entry form
- List with inline editing
- Stats overview (averages, trend indicators)
- Added to CaptureHub (❤️ icon)
- Route /vitals in App.jsx

API:
- Added vitals methods to api.js

v9d Phase 2d - Vitals tracking complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 14:52:09 +01:00
2c73c3df52 fix: convert Decimal to float for JSON serialization in evaluation
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- PostgreSQL returns numeric values as Decimal objects
- psycopg2.Json() cannot serialize Decimal to JSON
- Added convert_decimals() helper function
- Converts activity_data, context, and evaluation_result before saving

Fixes: Batch evaluation errors (31 errors 'Decimal is not JSON serializable')

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:28:07 +01:00
33e27a4f3e feat: add error_details to batch evaluation response
Some checks failed
Build Test / lint-backend (push) Waiting to run
Build Test / build-frontend (push) Waiting to run
Deploy Development / deploy (push) Has been cancelled
- Shows first 10 errors with activity_id, training_type_id, and error message
- Helps debug evaluation failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 13:24:14 +01:00
d7145874cf feat: Training Type Profiles Phase 2.1 - Backend Profile Management (#15)
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Admin endpoints for profile configuration:
- Extended TrainingTypeCreate/Update models with profile field
- Added profile column to all SELECT queries
- Profile templates for Running, Meditation, Strength Training
- Template endpoints: list, get, apply
- Profile stats endpoint (configured/unconfigured count)

New file: profile_templates.py
- TEMPLATE_RUNNING: Endurance-focused with HR zones
- TEMPLATE_MEDITATION: Mental-focused (low HR ≤ instead of ≥)
- TEMPLATE_STRENGTH: Strength-focused

API Endpoints:
- GET /api/admin/training-types/profiles/templates
- GET /api/admin/training-types/profiles/templates/{key}
- POST /api/admin/training-types/{id}/profile/apply-template
- GET /api/admin/training-types/profiles/stats

Next: Frontend Admin-UI (ProfileEditor component)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:50:40 +01:00
ca7d9b2e3f fix: add missing validation_rules in migration 013 (#15)
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
SQL Error: VALUES lists must all be the same length (line 130)
Cause: kcal_per_km row was missing validation_rules JSONB value

Fixed: Added validation_rules '{"min": 0, "max": 1000}'::jsonb

All 16 parameter rows now have correct 10 columns:
key, name_de, name_en, category, data_type, unit, source_field,
validation_rules, description_de, description_en

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 11:01:53 +01:00
edd15dd556 fix: defensive evaluation import to prevent startup crash (#15)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Problem: Backend crashed on startup due to evaluation import failure
Solution: Wrap evaluation_helper import in try/except

Changes:
- Import evaluation_helper with error handling
- Add EVALUATION_AVAILABLE flag
- All evaluation calls now check flag before executing
- System remains functional even if evaluation system unavailable

This prevents backend crashes if:
- Migrations haven't run yet
- Dependencies are missing
- Import errors occur

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 10:59:23 +01:00
e11953736d feat: Training Type Profiles Phase 1.2 - Auto-evaluation (#15)
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Automatic evaluation on activity INSERT/UPDATE:
- create_activity(): Evaluate after manual creation
- update_activity(): Re-evaluate after manual update
- import_activity_csv(): Evaluate after CSV import (INSERT + UPDATE)
- bulk_categorize_activities(): Evaluate after bulk training type assignment

All evaluation calls wrapped in try/except to prevent activity operations
from failing if evaluation encounters an error. Only activities with
training_type_id assigned are evaluated.

Phase 1.2 complete 

## Next Steps (Phase 2):
Admin-UI for training type profile configuration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 10:53:13 +01:00
1b9cd6d5e6 feat: Training Type Profiles - Phase 1.1 Foundation (#15)
All checks were successful
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
## Implemented

### DB-Schema (Migrations)
- Migration 013: training_parameters table (16 standard parameters)
- Migration 014: training_types.profile + activity_log.evaluation columns
- Performance metric calculations (avg_hr_percent, kcal_per_km)

### Backend - Rule Engine
- RuleEvaluator: Generic rule evaluation with 9 operators
  - gte, lte, gt, lt, eq, neq, between, in, not_in
  - Weighted scoring system
  - Pass strategies: all_must_pass, weighted_score, at_least_n

- IntensityZoneEvaluator: HR zone analysis
- TrainingEffectsEvaluator: Abilities development

### Backend - Master Evaluator
- TrainingProfileEvaluator: 7-dimensional evaluation
  1. Minimum Requirements (Quality Gates)
  2. Intensity Zones (HR zones)
  3. Training Effects (Abilities)
  4. Periodization (Frequency & Recovery)
  5. Performance Indicators (KPIs)
  6. Safety (Warnings)
  7. AI Context (simplified for MVP)

- evaluation_helper.py: Utilities for loading + saving
- routers/evaluation.py: API endpoints
  - POST /api/evaluation/activity/{id}
  - POST /api/evaluation/batch
  - GET /api/evaluation/parameters

### Integration
- main.py: Router registration

## TODO (Phase 1.2)
- Auto-evaluation on activity INSERT/UPDATE
- Admin-UI for profile editing
- User-UI for results display

## Testing
-  Syntax checks passed
- 🔲 Runtime testing pending (after auto-evaluation)

Part of Issue #15 - Training Type Profiles System
2026-03-23 10:49:26 +01:00
29770503bf fix: wrap abilities dict with Json() for JSONB insert (#13)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Problem: Creating new training types via Admin UI resulted in
'Internal Server Error' because abilities dict was passed directly
to PostgreSQL JSONB column without Json() wrapper.

Solution:
- Import Json from psycopg2.extras
- Wrap abilities_json with Json() in INSERT
- Wrap data.abilities with Json() in UPDATE

Same issue as rest_days JSONB fix (commit 7d627cf).

Closes #13
2026-03-23 09:13:50 +01:00
f87b93ce2f feat: prevent duplicate rest day types per date (Migration 012)
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Problem: User can create multiple rest days of same type per date
(e.g., 2x Mental Rest on 2026-03-23) - makes no sense.

Solution: UNIQUE constraint on (profile_id, date, focus)

## Migration 012:
- Add focus column (extracted from rest_config JSONB)
- Populate from existing data
- Add NOT NULL constraint
- Add CHECK constraint (valid focus values)
- Add UNIQUE constraint (profile_id, date, focus)
- Add index for performance

## Backend:
- Insert focus column alongside rest_config
- Handle UniqueViolation gracefully
- User-friendly error: "Du hast bereits einen Ruhetag 'Muskelregeneration' für 23.03."

## Benefits:
- DB-level enforcement (clean)
- Fast queries (no JSONB scan)
- Clear error messages
- Prevents: 2x muscle_recovery same day
- Allows: muscle_recovery + mental_rest same day ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 17:36:49 +01:00
f2e2aff17f fix: remove ON CONFLICT clause after constraint removal
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Migration 011 removed UNIQUE constraint (profile_id, date) to allow
multiple rest days per date, but INSERT still used ON CONFLICT.

Error: psycopg2.errors.InvalidColumnReference: there is no unique or
exclusion constraint matching the ON CONFLICT specification

Solution: Remove ON CONFLICT clause, use plain INSERT.
Multiple entries per date now allowed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 17:05:06 +01:00
6916e5b808 feat: multi-dimensional rest days + development routes architecture (v9d → v9e)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
## Changes:

**Frontend:**
- Fix double icon in rest day list (removed icons from FOCUS_LABELS)
- Icon now shows once with proper styling

**Migration 011:**
- Remove UNIQUE constraint (profile_id, date) from rest_days
- Allow multiple rest day types per date
- Use case: Muscle recovery + Mental rest same day

**Architecture: Development Routes**
New document: `.claude/docs/functional/DEVELOPMENT_ROUTES.md`

6 Independent Development Routes:
- 💪 Kraft (Strength): Muscle, power, HIIT
- 🏃 Kondition (Conditioning): Cardio, endurance, VO2max
- 🧘 Mental: Stress, focus, competition readiness
- 🤸 Koordination (Coordination): Balance, agility, technique
- 🧘‍♂️ Mobilität (Mobility): Flexibility, ROM, fascia
- 🎯 Technik (Technique): Sport-specific skills

Each route has:
- Independent rest requirements
- Independent training plans
- Independent progress tracking
- Independent goals & habits

**Future (v9e):**
- Route-based weekly planning
- Multi-route conflict validation
- Auto-rest on poor recovery
- Route balance analysis (KI)

**Future (v9g):**
- Habits per route (route_habits table)
- Streak tracking per route
- Dashboard route-habits widget

**Backlog Updated:**
- v9d: Rest days  (in testing)
- v9e: Development Routes & Weekly Planning (new)
- v9g: Habits per Route (extended)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:51:09 +01:00
7d627cf128 fix: wrap rest_config dict with Json() for psycopg2 JSONB insert
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Error: psycopg2.ProgrammingError: can't adapt type 'dict'
Solution: Import psycopg2.extras.Json and wrap config_dict

Changes:
- Import Json from psycopg2.extras
- Wrap config_dict with Json() in INSERT
- Wrap config_dict with Json() in UPDATE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:38:39 +01:00
b63d15fd02 feat: flexible rest days system with JSONB config (v9d Phase 2a)
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
PROBLEM: Simple full_rest/active_recovery model doesn't support
context-specific rest days (e.g., strength rest but cardio allowed).

SOLUTION: JSONB-based flexible rest day configuration.

## Changes:

**Migration 010:**
- Refactor rest_days.type → rest_config JSONB
- Schema: {focus, rest_from[], allows[], intensity_max}
- Validation function with check constraint
- GIN index for performant JSONB queries

**Backend (routers/rest_days.py):**
- CRUD: list, create (upsert by date), get, update, delete
- Stats: count per week, focus distribution
- Validation: check activity conflicts with rest day config

**Frontend (api.js):**
- 7 new methods: listRestDays, createRestDay, updateRestDay,
  deleteRestDay, getRestDaysStats, validateActivity

**Integration:**
- Router registered in main.py
- Ready for weekly planning validation rules

## Next Steps:
- Frontend UI (RestDaysPage with Quick/Custom mode)
- Activity conflict warnings
- Dashboard widget

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:20:52 +01:00
0278a8e4a6 fix: photo upload date parameter parsing
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Problem: Photos were always getting NULL date instead of form date,
causing frontend to fallback to created timestamp (today).

Root cause: FastAPI requires Form() wrapper for form fields when
mixing with File() parameters. Without it, the date parameter was
treated as query parameter and always received empty string.

Solution:
- Import Form from fastapi
- Change date parameter from str="" to str=Form("")
- Return photo_date instead of date in response (consistency)

Now photos correctly use the date from the upload form and can be
backdated when uploading later.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 14:33:01 +01:00
ef27660fc8 fix: photo upload with empty date string
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Problem:
- Photo upload with empty date parameter (date='')
- PostgreSQL rejects empty string for DATE field
- Error: "invalid input syntax for type date: ''"
- Occurred when saving circumference entry with only photo

Fix:
- Convert empty string to NULL before INSERT
- Check: date if date and date.strip() else None
- NULL is valid for optional date field

Test case:
- Circumference entry with only photo → should work now
- Photo without date → stored with date=NULL ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 14:25:27 +01:00
9aeb0de936 feat: sleep duration excludes awake time (actual sleep only)
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Conceptual change: duration_minutes = actual sleep time (not time in bed)

Backend:
- Plausibility check: deep + rem + light = duration (awake separate)
- Import: duration = deep + rem + light (without awake)
- Updated error message: clarifies awake not counted

Frontend:
- Label: "Schlafdauer (reine Schlafzeit, Minuten)"
- Auto-calculate: bedtime-waketime minus awake_minutes
- Plausibility check: only validates sleep phases (not awake)
- Both NewEntry and Edit mode updated

Rationale:
- Standard in sleep tracking (Apple Health shows "Sleep", not "Time in Bed")
- Clearer semantics: duration = how long you slept
- awake_minutes tracked separately for analysis
- More intuitive for users

Example:
- Time in bed: 22:00 - 06:00 = 480 min (8h)
- Awake phases: 30 min
- Sleep duration: 450 min (7h 30min) ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 14:01:47 +01:00
1644b34d5c fix: manual sleep entry creation + import overwrite protection
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Critical fixes:
1. Added "+ Schlaf erfassen" button back (was missing!)
   - Opens NewEntryForm component inline
   - Default: 450 min (7h 30min), quality 3
   - Collapsible detail view
   - Live plausibility check

2. Fixed import overwriting manual entries
   - Problem: ON CONFLICT WHERE clause didn't prevent updates
   - Solution: Explicit if/else logic
     - If manual entry exists → skip (don't touch)
     - If non-manual entry exists → UPDATE
     - If no entry exists → INSERT
   - Properly counts imported vs skipped

Test results:
 CSV import with drag & drop
 Inline editing
 Segment timeline view with colors
 Source badges (Manual/Apple Health)
 Plausibility check (backend + frontend)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 13:43:02 +01:00
b52c877367 feat: complete sleep module overhaul - app standard compliance
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Backend improvements:
- Plausibility check: phases must sum to duration (±5 min tolerance)
- Auto-calculate wake_count from awake segments in import
- Applied to both create_sleep and update_sleep endpoints

Frontend complete rewrite:
-  Drag & Drop CSV import (like NutritionPage)
-  Inline editing (no scroll to top, edit directly in list)
-  Toast notifications (no more alerts, auto-dismiss 4s)
-  Source badges (Manual/Apple Health/Garmin with colors)
-  Expandable segment timeline view (JSONB sleep_segments)
-  Live plausibility check (shows error if phases ≠ duration)
-  Color-coded sleep phases (deep/rem/light/awake)
-  Show wake_count in list view

Design improvements:
- Stats card on top (7-day avg)
- Import drag zone with visual feedback
- Clean inline edit mode with validation
- Timeline view with phase colors
- Responsive button layout

Confirmed: Kernschlaf (Apple Health) = Leichtschlaf (light_minutes) ✓

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 13:09:34 +01:00
da376a8b18 feat: store full datetime in sleep_segments JSONB
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Enhanced sleep_segments data structure:
- start: ISO datetime (2026-03-21T22:30:00) instead of HH:MM
- end: ISO datetime (2026-03-21T23:15:00) - NEW
- phase: sleep phase type
- duration_min: duration in minutes

Benefits:
- Exact timestamp for each segment (no date ambiguity)
- Can reconstruct complete sleep timeline
- Enables precise cycle analysis
- Handles midnight crossings correctly

Example:
[
  {"phase": "light", "start": "2026-03-21T22:30:00", "end": "2026-03-21T23:15:00", "duration_min": 45},
  {"phase": "deep", "start": "2026-03-21T23:15:00", "end": "2026-03-22T00:30:00", "duration_min": 75}
]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 12:57:20 +01:00
9a9c597187 fix: sleep import groups segments by gap instead of date boundary
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Problem: Segments crossing midnight were split into different nights
- 22:30-23:15 (21.03) → assigned to 21.03
- 00:30-02:45 (22.03) → assigned to 22.03
But both belong to the same night (21/22.03)!

Solution: Gap-based grouping
- Sort segments chronologically
- Group segments with gap < 2 hours
- Night date = wake_time.date() (last segment's end date)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 12:09:25 +01:00
b1a92c01fc feat: Apple Health CSV import for sleep data (v9d Phase 2c)
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 12s
Backend:
- New endpoint POST /api/sleep/import/apple-health
- Parses Apple Health sleep CSV format
- Maps German phase names (Kern→light, REM→rem, Tief→deep, Wach→awake)
- Aggregates segments by night (wake date)
- Stores raw segments in JSONB (sleep_segments)
- Does NOT overwrite manual entries (source='manual')

Frontend:
- Import button in SleepPage with file picker
- Progress indicator during import
- Success/error messages
- Auto-refresh after import

Documentation:
- Added architecture rules reference to CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 11:49:09 +01:00
b65efd3b71 feat: add missing migration 008 (vitals, rest days, sleep_goal_minutes)
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Creates rest_days table for rest day tracking
- Creates vitals_log table for resting HR + HRV
- Creates weekly_goals table for training planning
- Extends profiles with hf_max and sleep_goal_minutes columns
- Extends activity_log with avg_hr and max_hr columns
- Fixes sleep_goal_minutes missing column error in stats endpoint
- Includes stats error handling in SleepWidget

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 10:59:55 +01:00
836bc4294b fix: convert empty strings to None for TIME fields in sleep router
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
PostgreSQL TIME type doesn't accept empty strings.
Converting empty bedtime/wake_time to None before INSERT/UPDATE.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 08:28:44 +01:00
39d676e5c8 fix: migration 009 - change profile_id from VARCHAR(36) to UUID
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Profile IDs are UUID type in the profiles table, not VARCHAR.
This was causing foreign key constraint error on migration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 08:22:58 +01:00
ef81c46bc0 feat: v9d Phase 2b - Sleep Module Core (Schlaf-Modul)
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Add sleep_log table with JSONB sleep_segments (Migration 009)
- Add sleep router with CRUD + stats endpoints (7d avg, 14d debt, trend, phases)
- Add SleepPage with quick/detail entry forms and inline edit
- Add SleepWidget to Dashboard showing last night + 7d average
- Add sleep navigation entry with Moon icon
- Register sleep router in main.py
- Add 9 new API methods in api.js

Phase 2b complete - ready for testing on dev

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 08:17:11 +01:00
829edecbdc feat: learnable activity type mapping system (DB-based, auto-learning)
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Replaces hardcoded mappings with database-driven, self-learning system.

Backend:
- Migration 007: activity_type_mappings table
  - Supports global and user-specific mappings
  - Seeded with 40+ default mappings (German + English)
  - Unique constraint: (activity_type, profile_id)
- Refactored: get_training_type_for_activity() queries DB
  - Priority: user-specific → global → NULL
- Bulk categorization now saves mapping automatically
  - Source: 'bulk' for learned mappings
- admin_activity_mappings.py: Full CRUD endpoints
  - List, Get, Create, Update, Delete
  - Coverage stats endpoint
- CSV import uses DB mappings (no hardcoded logic)

Frontend:
- AdminActivityMappingsPage: Full mapping management UI
  - Coverage stats (% mapped, unmapped count)
  - Filter: All / Global
  - Create/Edit/Delete mappings
  - Tip: System learns from bulk categorization
- Added route + admin link
- API methods: adminList/Get/Create/Update/DeleteActivityMapping

Benefits:
- No code changes needed for new activity types
- System learns from user bulk categorizations
- User-specific mappings override global defaults
- Admin can manage all mappings via UI
- Migration pre-populates 40+ common German/English types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 19:31:58 +01:00
a4bd738e6f fix: Apple Health import - German names + duplicate detection
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Issue 1: Automatic training type mapping didn't work
- Root cause: Only English workout names were mapped
- Solution: Added 20+ German workout type mappings:
  - "Traditionelles Krafttraining" → hypertrophy
  - "Outdoor Spaziergang" → walk
  - "Innenräume Spaziergang" → walk
  - "Matrial Arts" → technique (handles typo)
  - "Cardio Dance" → dance
  - "Geist & Körper" → yoga
  - Plus: Laufen, Gehen, Radfahren, Schwimmen, etc.

Issue 2: Reimporting CSV created duplicates without training types
- Root cause: Import always did INSERT with new UUID, no duplicate check
- Solution: Check if entry exists (profile_id + date + start_time)
  - If exists: UPDATE with new data + training type mapping
  - If new: INSERT as before
- Handles multiple workouts per day (different start times)
- "Skipped" count now includes updated entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 19:16:09 +01:00
eecc00e824 feat: admin CRUD for training types + distribution chart in ActivityPage
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Backend (v9d Phase 1b):
- Migration 006: Add abilities JSONB column + descriptions
- admin_training_types.py: Full CRUD endpoints for training types
  - List, Get, Create, Update, Delete
  - Abilities taxonomy endpoint (5 dimensions: koordinativ, konditionell, kognitiv, psychisch, taktisch)
  - Validation: Cannot delete types in use
- Register admin_training_types router in main.py

Frontend:
- AdminTrainingTypesPage: Full CRUD UI
  - Create/edit form with all fields (category, subcategory, names, icon, descriptions, sort_order)
  - List grouped by category with color coding
  - Delete with usage check
  - Note about abilities mapping coming in v9f
- Add TrainingTypeDistribution to ActivityPage stats tab
- Add admin link in AdminPanel (v9d section)
- Update api.js with admin training types methods

Notes:
- Abilities mapping UI deferred to v9f (flexible prompt system)
- Placeholders (abilities column) in place for future AI analysis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:32:32 +01:00
d164ab932d feat: add extended training types (cardio walk/dance, mind & meditation)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Migration 005: Add cardio subcategories (Gehen, Tanzen)
- Migration 005: Add new category "Geist & Meditation" with 4 subcategories
  (Meditation, Atemarbeit, Achtsamkeit, Visualisierung)
- Update categories endpoint with mind category metadata
- Update Apple Health mapping: dance → dance, add meditation/mindfulness
- 6 new training types total

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:16:07 +01:00
96b0acacd2 feat: automatic training type mapping for Apple Health import and bulk categorization
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
- Add get_training_type_for_apple_health() mapping function (23 workout types)
- CSV import now automatically assigns training_type_id/category/subcategory
- New endpoint: GET /activity/uncategorized (grouped by activity_type)
- New endpoint: POST /activity/bulk-categorize (bulk update training types)
- New component: BulkCategorize with two-level dropdown selection
- ActivityPage: new "Kategorisieren" tab for existing activities
- Update CLAUDE.md: v9d Phase 1b progress

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:08:18 +01:00
410b2ce308 feat(v9d): add training types system + logout button
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Phase 1: Training Types Basis
=============================

Backend:
- Migration 004: training_types table + seed data (24 types)
- New router: /api/training-types (grouped, flat, categories)
- Extend activity_log: training_type_id, training_category, training_subcategory
- Extend ActivityEntry model: support training type fields

Frontend:
- TrainingTypeSelect component (two-level dropdown)
- TrainingTypeDistribution component (pie chart)
- API functions: listTrainingTypes, listTrainingTypesFlat, getTrainingCategories

Quick Win: Logout Button
========================
- Add LogOut icon button in app header
- Confirm dialog before logout
- Redirect to / after logout
- Hover effect: red color on hover

Not yet integrated:
- TrainingTypeSelect not yet in ActivityPage form
- TrainingTypeDistribution not yet in Dashboard
  (will be added in next commit)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 13:05:33 +01:00
1cd93d521e fix: email verification redirect and already-used token message
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
1. Use window.location.href instead of navigate() for reliable redirect
2. Improve backend error message for already-used verification tokens
3. Show user-friendly message when token was already verified
4. Reduce redirect delay from 2s to 1.5s for better UX

Fixes:
- Empty page after email verification
- Generic error when clicking verification link twice

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 12:28:51 +01:00
f843d71d6b feat: resend verification email functionality
All checks were successful
Deploy Development / deploy (push) Successful in 36s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Backend:
- Added POST /api/auth/resend-verification endpoint
- Rate limited to 3/hour to prevent abuse
- Generates new verification token (24h validity)
- Sends new verification email

Frontend:
- Verify.jsx: Added "expired" status with resend flow
- Email input + "Neue Bestätigungs-E-Mail senden" button
- EmailVerificationBanner: Added "Neue E-Mail senden" button
- Shows success/error feedback inline
- api.js: Added resendVerification() helper

User flows:
1. Expired token → Verify page shows resend form
2. Email lost → Dashboard banner has resend button
3. Both flows use same backend endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 10:23:38 +01:00
9fb6e27256 fix: email verification flow and trial system
All checks were successful
Deploy Development / deploy (push) Successful in 35s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Backend fixes:
- Fixed timezone-aware datetime comparison in verify_email endpoint
- Added trial_ends_at (14 days) for new registrations
- All datetime.now() calls now use timezone.utc

Frontend additions:
- Added EmailVerificationBanner component for unverified users
- Banner shows warning before trial banner in Dashboard
- Clear messaging about verification requirement

This fixes the 500 error on email verification and ensures new users
see both verification and trial status correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 10:20:06 +01:00
913b485500 fix: only process numbered migrations (XXX_*.sql pattern)
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Modified run_migrations() to only process files matching pattern: \d{3}_*.sql
This prevents utility scripts (check_features.sql) and manually applied
migrations (v9c_*.sql) from being executed.

Only properly numbered migrations like 003_add_email_verification.sql
will be processed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 10:08:56 +01:00