Commit Graph

94 Commits

Author SHA1 Message Date
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
22651647cb fix: add automatic migration system to db_init.py
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 13s
Added migration tracking and execution to db_init.py:
- Created schema_migrations table to track applied migrations
- Added run_migrations() to automatically apply pending SQL files
- Migrations from backend/migrations/*.sql are now applied on startup

This fixes the missing email verification columns (migration 003).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 10:07:37 +01:00
c1562a27f4 feat: add self-registration with email verification
Backend:
- New endpoint: POST /api/auth/register
- New endpoint: GET /api/auth/verify/{token}
- Migration: Add email_verified, verification_token, verification_expires
- Helper: send_email() for reusable SMTP
- Validation: email format, password length (min 8), name
- Auto-login after verification (returns session token)
- Rate limit: 3 registrations per hour per IP

Features:
- Verification token valid for 24h
- Existing users marked as verified (grandfather clause)
- SMTP configured via .env (SMTP_HOST, SMTP_USER, SMTP_PASS)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 09:53:11 +01:00
770a49b5f3 fix: update version string to v9c
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 13s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 08:59:24 +01:00
02ca9772d6 feat: add manual nutrition entry form with auto-detect
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
Features:
- Manual entry form above data list
- Date picker with auto-load existing entries
- Upsert logic: creates new or updates existing entry
- Smart button text: "Hinzufügen" vs "Aktualisieren"
- Prevents duplicate entries per day
- Feature enforcement for nutrition_entries

Backend:
- POST /nutrition - Create or update entry (upsert)
- GET /nutrition/by-date/{date} - Load entry by date
- Auto-detects existing entry and switches to UPDATE mode
- Increments usage counter only on INSERT

Frontend:
- EntryForm component with date picker + macros inputs
- Auto-loads data when date changes
- Shows info message when entry exists
- Success/error feedback
- Disabled state while loading/saving

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 08:37:01 +01:00
0f072f4735 feat: add nutrition entry editing and import history
All checks were successful
Deploy Development / deploy (push) Successful in 33s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Features:
- Import history panel showing all CSV imports with date, count, and range
- Edit/delete functionality for nutrition entries (inline editing)
- New backend endpoints: GET /import-history, PUT /{id}, DELETE /{id}

UI Changes:
- Import history displayed under import panel
- "Daten" tab now has edit/delete buttons per entry
- Inline form for editing macros (kcal, protein, fat, carbs)
- Confirmation dialog for deletion

Backend:
- nutrition.py: Added import_history, update_nutrition, delete_nutrition endpoints
- Groups imports by created date to show history

Frontend:
- NutritionPage: New DataTab and ImportHistory components
- api.js: Added nutritionImportHistory, updateNutrition, deleteNutrition

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 08:26:47 +01:00
4d9c59ccf7 fix: [BUG-001] TypeError in nutrition_weekly endpoint
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 12s
Problem:
- /api/nutrition/weekly crashed with 500 Internal Server Error
- TypeError: strptime() argument 1 must be str, not datetime.date

Root Cause:
- d['date'] from PostgreSQL is already datetime.date object
- datetime.strptime() expects string input
- Line 156: wk=datetime.strptime(d['date'],'%Y-%m-%d').strftime('%Y-W%V')

Solution:
- Added type check before strptime()
- If date already has strftime method → use directly
- Else → parse as string first
- Works with both datetime.date objects and strings

Tested:
- /nutrition page loads without error
- Weekly aggregation works correctly
- Chart displays nutrition data

Closes: BUG-001

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 07:58:37 +01:00
4b8e6755dc feat: complete Phase 4 enforcement for all features (backend)
Alle 11 Features blockieren jetzt bei Limit-Überschreitung:

Batch 1 (bereits erledigt):
- weight_entries, circumference_entries, caliper_entries

Batch 2:
- activity_entries
- nutrition_entries (CSV import)
- photos

Batch 3:
- ai_calls (einzelne Analysen)
- ai_pipeline (3-stufige Gesamtanalyse)
- data_export (CSV, JSON, ZIP)
- data_import (ZIP)

Entfernt: Alte check_ai_limit() Calls (ersetzt durch neue Feature-Limits)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 07:40:37 +01:00
cf522190c6 fix: correct indentation in auth.py _check_impl function
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 12s
Behebt IndentationError in Zeile 204 der _check_impl() Funktion.
Die Funktion wurde beim Connection-Pool-Fix erstellt, hatte aber
inkonsistente Einrückungen (8 statt 4 Spaces nach der ersten Zeile).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 07:06:53 +01:00
329daaef1c fix: prevent connection pool exhaustion in features/usage
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 12s
- Add optional conn parameter to get_effective_tier()
- Add optional conn parameter to check_feature_access()
- Pass existing connection in features.py loop
- Prevents opening 20+ connections simultaneously
- Fixes "connection pool exhausted" error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 07:02:42 +01:00
cbcb6a2a34 feat: Phase 4 Batch 1 - enable enforcement for data entries
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
- Weight, Circumference, Caliper now BLOCK on limit exceeded
- Raise HTTPException(403) with user-friendly message
- Show used/limit and suggest contacting admin
- Phase 2 → Phase 4 transition

Phase 4: Enforcement (Batch 1/3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 06:57:05 +01:00
d10f605d66 feat: add GET /api/features/usage endpoint (Phase 3)
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
- Add user-facing usage overview endpoint
- Returns all features with usage, limits, reset info
- Fully dynamic - automatically includes new features
- Phase 3: Frontend Display preparation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 06:32:43 +01:00
32d53b447d fix: pipeline typo and add features diagnostic script
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
- Fix NameError in insights.py pipeline endpoint (access -> access_calls)
- Add check_features.py diagnostic script for debugging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 22:32:09 +01:00
1298bd235f feat: add structured JSON logging for all feature usage (Phase 2)
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 12s
- Create feature_logger.py with JSON logging infrastructure
- Add log_feature_usage() calls to all 9 routers after check_feature_access()
- Logs written to /app/logs/feature-usage.log
- Tracks all usage (not just violations) for future analysis
- Phase 2: Non-blocking monitoring complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 22:18:12 +01:00
ddcd2f4350 feat: v9c Phase 2 - Backend Non-Blocking Logging (12 Endpoints)
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
PHASE 2: Backend Non-Blocking Logging - KOMPLETT

Instrumentierte Endpoints (12):
- Data: weight, circumference, caliper, nutrition, activity, photos (6)
- AI: insights/run/{slug}, insights/pipeline (2)
- Export: csv, json, zip (3)
- Import: zip (1)

Pattern implementiert:
- check_feature_access() VOR Operation (non-blocking)
- [FEATURE-LIMIT] Logging wenn Limit überschritten
- increment_feature_usage() NACH Operation
- Alte Permission-Checks bleiben aktiv

Features geprüft:
- weight_entries, circumference_entries, caliper_entries
- nutrition_entries, activity_entries, photos
- ai_calls, ai_pipeline
- data_export, data_import

Monitoring: 1-2 Wochen Log-Only-Phase
Logs zeigen: Wie oft würde blockiert werden?
Nächste Phase: Frontend Display (Usage-Counter)

Phase 1 (Cleanup) + Phase 2 (Logging) vollständig!

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 21:59:33 +01:00
73bea5ee86 feat: v9c Phase 1 - Feature consolidation & cleanup migration
All checks were successful
Deploy Development / deploy (push) Successful in 33s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
PHASE 1: Cleanup & Analyse
- Feature-Konsolidierung: export_csv/json/zip → data_export (1 Feature)
- Umbenennung: csv_import → data_import
- Auto-Migration bei Container-Start (apply_v9c_migration.py)
- Diagnose-Script (check_features.sql)

Lessons Learned angewendet:
- Ein Feature für Export, nicht drei
- Migration ist idempotent (kann mehrfach laufen)
- Zeigt BEFORE/AFTER State im Log

Finaler Feature-Katalog (10 statt 13):
- Data: weight, circumference, caliper, nutrition, activity, photos
- AI: ai_calls, ai_pipeline
- Export/Import: data_export, data_import

Tier Limits:
- FREE: 30 data entries, 0 AI/export/import
- BASIC: unlimited data, 3 AI/month, 5 export/month, 3 import/month
- PREMIUM/SELFHOSTED: unlimited

Migration läuft automatisch auf dev UND prod beim Container-Start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 18:57:39 +01:00