Commit Graph

129 Commits

Author SHA1 Message Date
7f94a41965 feat: batch import/export for prompts (Issue #28 Debug B)
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
Dev→Prod Sync in 2 Klicks: Export → Import

Backend:
- GET /api/prompts/export-all → JSON mit allen Prompts
- POST /api/prompts/import?overwrite=true/false → Import + Create/Update
  - Returns: created, updated, skipped counts
  - Validates JSON structure
  - Handles stages JSON conversion

Frontend AdminPromptsPage:
- Button "📦 Alle exportieren" → downloads all-prompts-{date}.json
- Button "📥 Importieren" → file upload dialog
  - User-Prompt: Überschreiben? Ja/Nein
  - Success-Message mit Statistik (created/updated/skipped)

Frontend api.js:
- exportAllPrompts()
- importPrompts(data, overwrite)

Use Cases:
1. Backup: Prompts als JSON sichern
2. Dev→Prod: Auf dev.mitai entwickeln → exportieren → auf mitai.jinkendo importieren
3. Versionierung: Prompts in Git speichern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:44:08 +01:00
97e57481f9 fix: Analysis page now uses unified prompt executor (Issue #28)
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
BREAKING: Analysis page switched from old /insights/run to new /prompts/execute

Changes:
- Backend: Added save=true parameter to /prompts/execute
  - When enabled, saves final output to ai_insights table
  - Extracts content from pipeline output (last stage)
- Frontend api.js: Added save parameter to executeUnifiedPrompt()
- Frontend Analysis.jsx: Switched from api.runInsight() to api.executeUnifiedPrompt()
  - Transforms new result format to match InsightCard expectations
  - Pipeline outputs properly extracted and displayed

Fixes: PIPELINE_MASTER responses (old template being sent to AI)
The old /insights/run endpoint used raw template field, which for the
legacy "pipeline" prompt was literally "PIPELINE_MASTER". The new
executor properly handles stages and data processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:38:58 +01:00
811ba8b3dc fix: convert Decimal to float before multiplication in protein targets
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
- get_protein_ziel_low: float(weight) * 1.6
- get_protein_ziel_high: float(weight) * 2.2

Fixes TypeError: unsupported operand type(s) for *: 'decimal.Decimal' and 'float'

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:23:50 +01:00
dfaf24d74c fix: correct SQL column names in placeholder_resolver
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
- caliper_summary: use body_fat_pct (not bf_jpl)
- circ_summary: use c_chest, c_waist, c_hip (not brust, taille, huefte)
- get_latest_bf: use body_fat_pct for consistency

Fixes SQL errors when running base prompts that feed pipeline prompts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:10:55 +01:00
0f2b85c6de fix: reconstruct missing placeholders + fix SQL column names
All checks were successful
Deploy Development / deploy (push) Successful in 46s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Added missing placeholders:
- caliper_summary, circ_summary (body measurements)
- goal_weight, goal_bf_pct (goals from profile)
- nutrition_days (count of nutrition entries)
- protein_ziel_low/high (calculated from weight)

Fixed SQL errors:
- protein → protein_g
- fat → fat_g
- carb → carbs_g

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 09:03:35 +01:00
f4d1fd4de1 feat: add activity_detail placeholder for detailed activity logs
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
- New placeholder: {{activity_detail}} returns formatted activity log
- Shows last 20 activities with date, type, duration, kcal, HR
- Makes activity analysis prompts work properly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:20:18 +01:00
ba92d66880 fix: remove {{ }} from placeholder keys before resolution
All checks were successful
Deploy Development / deploy (push) Successful in 46s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 12s
Placeholder resolver returns keys with {{ }} wrappers,
but resolve_placeholders expects clean keys.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:17:22 +01:00
afc70b5a95 fix: integrate placeholder resolver + JSON unwrapping (Issue #28)
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
- Backend: integrate get_placeholder_example_values in execute_prompt_with_data
- Backend: now provides BOTH raw data AND processed placeholders
- Backend: unwrap Markdown-wrapped JSON (```json ... ```)
- Fixes old-style prompts that expect name, weight_trend, caliper_summary

Resolves unresolved placeholders issue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:14:41 +01:00
84dad07e15 fix: show debug info on errors + prompt export function
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
- Frontend: debug viewer now shows even when test fails
- Frontend: export button to download complete prompt config as JSON
- Backend: attach debug info to JSON validation errors
- Backend: include raw output and length in error details

Users can now debug failed prompts and export configs for analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:07:34 +01:00
7f2ba4fbad feat: debug system for prompt execution (Issue #28)
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: debug mode in prompt_executor with placeholder tracking
- Backend: show resolved/unresolved placeholders, final prompts, AI responses
- Frontend: test button in UnifiedPromptModal for saved prompts
- Frontend: debug output viewer with JSON preview
- Frontend: wider placeholder example fields in PlaceholderPicker

Resolves pipeline execution debugging issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 08:01:33 +01:00
7be7266477 feat: unified prompt executor - Phase 2 complete (Issue #28)
All checks were successful
Deploy Development / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Backend:
- prompt_executor.py: Universal executor for base + pipeline prompts
  - Dynamic placeholder resolution
  - JSON output validation
  - Multi-stage parallel execution (sequential impl)
  - Reference and inline prompt support
  - Data loading per module (körper, ernährung, training, schlaf, vitalwerte)

Endpoints:
- POST /api/prompts/execute - Execute unified prompts
- POST /api/prompts/unified - Create unified prompts
- PUT /api/prompts/unified/{id} - Update unified prompts

Frontend:
- api.js: executeUnifiedPrompt, createUnifiedPrompt, updateUnifiedPrompt

Next: Phase 3 - Frontend UI consolidation
2026-03-25 14:52:24 +01:00
33653fdfd4 fix: migration 020 - make template column nullable
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 14s
Issue: template has NOT NULL constraint but pipeline-type prompts
don't use template (they use stages JSONB instead).

Solution: ALTER COLUMN template DROP NOT NULL before inserting
pipeline configs into ai_prompts.
2026-03-25 14:45:53 +01:00
95dcf080e5 fix: migration 020 SQL syntax - correlated subquery issue
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
Fixed Step 3 pipeline_configs migration:
- Simplified JSONB aggregation logic
- Properly scope pc alias in subqueries
- Use UNNEST with FROM clause for array expansion

Previous version had correlation issues with nested subqueries.
2026-03-25 12:58:02 +01:00
2e0838ca08 feat: unified prompt system migration schema (Issue #28 Phase 1)
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
- Migration 020: Add type, stages, output_format columns to ai_prompts
- Migrate existing prompts to 1-stage pipeline format
- Migrate pipeline_configs into ai_prompts as multi-stage pipelines
- Add UnifiedPrompt Pydantic models for new API
- Backup pipeline_configs table (keep during transition)

Schema structure:
- type: 'base' (reusable) or 'pipeline' (multi-stage)
- stages: JSONB array [{stage:1, prompts:[{source, slug, template, output_key, output_format}]}]
- output_format: 'text' or 'json'
- output_schema: JSON validation schema (optional)

Next: Backend executor + Frontend UI consolidation
2026-03-25 10:43:10 +01:00
6627b5eee7 feat: Pipeline-System - Backend Infrastructure (Issue #28, Phase 1)
All checks were successful
Deploy Development / deploy (push) Successful in 43s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 13s
Implementiert konfigurierbare mehrstufige Analysen. Admins können
mehrere Pipeline-Konfigurationen erstellen mit unterschiedlichen
Modulen, Zeiträumen und Prompts.

**Backend:**
- Migration 019: pipeline_configs Tabelle + ai_prompts erweitert
- Pipeline-Config Models: PipelineConfigCreate, PipelineConfigUpdate
- Pipeline-Executor: refactored für config-basierte Ausführung
- CRUD-Endpoints: /api/prompts/pipeline-configs (list, create, update, delete, set-default)
- Reset-to-Default: /api/prompts/{id}/reset-to-default für System-Prompts

**Features:**
- 3 Seed-Configs: "Alltags-Check" (default), "Schlaf & Erholung", "Wettkampf-Analyse"
- Dynamische Platzhalter: {{stage1_<slug>}} für alle Stage-1-Ergebnisse
- Backward-compatible: /api/insights/pipeline ohne config_id nutzt default

**Dateien:**
- backend/migrations/019_pipeline_system.sql
- backend/models.py (PipelineConfigCreate, PipelineConfigUpdate)
- backend/routers/insights.py (analyze_pipeline refactored)
- backend/routers/prompts.py (Pipeline-Config CRUD + Reset-to-Default)

**Nächste Schritte:**
- Frontend: Pipeline-Config Dialog + Admin-UI
- Design: Mobile-Responsive + Icons

Issue #28 Progress: Backend 3/3  | Frontend 0/3 🔲 | Design 0/3 🔲

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 09:42:28 +01:00
5e7ef718e0 fix: placeholder picker improvements + insight display names (Issue #28)
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
Backend:
- get_placeholder_catalog(): grouped placeholders with descriptions
- Returns {category: [{key, description, example}]} format
- Categories: Profil, Körper, Ernährung, Training, Schlaf, Vitalwerte, Zeitraum

Frontend - Placeholder Picker:
- Grouped by category with visual separation
- Search/filter across keys and descriptions
- Hover effects for better UX
- Insert at cursor position (not at end)
- Shows: key + description + example value
- 'Keine Platzhalter gefunden' message when filtered

Frontend - Insight Display Names:
- InsightCard receives prompts array
- Finds matching prompt by scope/slug
- Shows prompt.display_name instead of hardcoded SLUG_LABELS
- History tab also shows display_name in group headers
- Fallback chain: display_name → SLUG_LABELS → scope

User-facing improvements:
✓ Platzhalter zeigen echte Daten statt Zahlen
✓ Durchsuchbar + filterbar
✓ Einfügen an Cursor-Position
✓ Insights zeigen custom Namen (z.B. '🍽️ Meine Ernährung')

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 06:44:22 +01:00
0c4264de44 feat: display_name + placeholder picker for prompts (Issue #28)
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 14s
Migration 018:
- Add display_name column to ai_prompts
- Migrate existing prompts from hardcoded SLUG_LABELS
- Fallback: name if display_name is NULL

Backend:
- PromptCreate/Update models with display_name field
- create/update/duplicate endpoints handle display_name
- Fallback: use name if display_name not provided

Frontend:
- PromptEditModal: display_name input field
- Placeholder picker: button + dropdown with all placeholders
- Shows example values, inserts {{placeholder}} on click
- Analysis.jsx: use display_name instead of SLUG_LABELS

User-facing changes:
- Prompts now show custom display names (e.g. '🍽️ Ernährung')
- Admin can edit display names instead of hardcoded labels
- Template editor has 'Platzhalter einfügen' button
- No more hardcoded SLUG_LABELS in frontend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 06:31:25 +01:00
500de132b9 feat: AI-Prompts flexibilisierung - Backend & Admin UI (Issue #28, Part 1)
Backend complete:
- Migration 017: Add category column to ai_prompts
- placeholder_resolver.py: 20+ placeholders with resolver functions
- Extended routers/prompts.py with CRUD endpoints:
  * POST /api/prompts (create)
  * PUT /api/prompts/:id (update)
  * DELETE /api/prompts/:id (delete)
  * POST /api/prompts/:id/duplicate
  * PUT /api/prompts/reorder
  * POST /api/prompts/preview
  * GET /api/prompts/placeholders
  * POST /api/prompts/generate (KI-assisted generation)
  * POST /api/prompts/:id/optimize (KI analysis)
- Extended models.py with PromptCreate, PromptUpdate, PromptGenerateRequest

Frontend:
- AdminPromptsPage.jsx: Full CRUD UI with category filter, reordering

Meta-Features:
- KI generates prompts from goal description + example data
- KI analyzes and optimizes existing prompts

Next: PromptEditModal, PromptGenerator, api.js integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 15:32:25 +01:00
302948a248 fix: add quality_filter_level to ProfileUpdate model (Issue #31)
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
The frontend was sending quality_filter_level to the backend, but the
Pydantic ProfileUpdate model didn't include this field, so it was
silently ignored. Profile updates never actually saved the filter.

This is why the charts didn't react to filter changes - the backend
database was never updated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 06:44:05 +01:00
04306a7fef feat: global quality filter setting (Issue #31)
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
Implemented global quality_filter_level in user profiles for consistent
data filtering across all views (Dashboard, History, Charts, KI-Pipeline).

Backend changes:
- Migration 016: Add quality_filter_level column to profiles table
- quality_filter.py: Centralized helper functions for SQL filtering
- insights.py: Apply global filter in _get_profile_data()
- activity.py: Apply global filter in list_activity()

Frontend changes:
- SettingsPage.jsx: Add Datenqualität section with 4-level selector
- History.jsx: Use global quality filter from profile context

Filter levels: all, quality (good+excellent+acceptable), very_good
(good+excellent), excellent (only excellent)

Closes #31

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 22:29:49 +01:00
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