mitai-jinkendo/backend/migrations/022_goal_system.sql
Lars 337667fc07
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
feat: Phase 0a - Minimal Goal System (Strategic + Tactical)
- Strategic Layer: Goal modes (weight_loss, strength, endurance, recomposition, health)
- Tactical Layer: Concrete goal targets with progress tracking
- Training phases (manual + auto-detection framework)
- Fitness tests (standardized performance tracking)

Backend:
- Migration 022: goal_mode in profiles, goals, training_phases, fitness_tests tables
- New router: routers/goals.py with full CRUD for goals, phases, tests
- API endpoints: /api/goals/* (mode, list, create, update, delete)

Frontend:
- GoalsPage: Goal mode selector + goal management UI
- Dashboard: Goals preview card with link
- API integration: goal mode, CRUD operations, progress calculation

Basis for 120+ placeholders and goal-aware analyses (Phase 0b)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 16:20:35 +01:00

148 lines
6.9 KiB
SQL

-- Migration 022: Goal System (Strategic + Tactical)
-- Date: 2026-03-26
-- Purpose: Two-level goal architecture for AI-driven coaching
-- ============================================================================
-- STRATEGIC LAYER: Goal Modes
-- ============================================================================
-- Add goal_mode to profiles (strategic training direction)
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS goal_mode VARCHAR(50) DEFAULT 'health';
COMMENT ON COLUMN profiles.goal_mode IS
'Strategic goal mode: weight_loss, strength, endurance, recomposition, health.
Determines score weights and interpretation context for all analyses.';
-- ============================================================================
-- TACTICAL LAYER: Concrete Goal Targets
-- ============================================================================
CREATE TABLE IF NOT EXISTS goals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
-- Goal Classification
goal_type VARCHAR(50) NOT NULL, -- weight, body_fat, lean_mass, vo2max, strength, flexibility, bp, rhr
is_primary BOOLEAN DEFAULT false,
status VARCHAR(20) DEFAULT 'active', -- draft, active, reached, abandoned, expired
-- Target Values
target_value DECIMAL(10,2),
current_value DECIMAL(10,2),
start_value DECIMAL(10,2),
unit VARCHAR(20), -- kg, %, ml/kg/min, bpm, mmHg, cm, reps
-- Timeline
start_date DATE DEFAULT CURRENT_DATE,
target_date DATE,
reached_date DATE,
-- Metadata
name VARCHAR(100), -- e.g., "Sommerfigur 2026"
description TEXT,
-- Progress Tracking
progress_pct DECIMAL(5,2), -- Auto-calculated: (current - start) / (target - start) * 100
projection_date DATE, -- Prognose wann Ziel erreicht wird
on_track BOOLEAN, -- true wenn Prognose <= target_date
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_goals_profile ON goals(profile_id);
CREATE INDEX IF NOT EXISTS idx_goals_status ON goals(profile_id, status);
CREATE INDEX IF NOT EXISTS idx_goals_primary ON goals(profile_id, is_primary) WHERE is_primary = true;
COMMENT ON TABLE goals IS 'Concrete user goals (tactical targets)';
COMMENT ON COLUMN goals.goal_type IS 'Type of goal: weight, body_fat, lean_mass, vo2max, strength, flexibility, bp, rhr';
COMMENT ON COLUMN goals.is_primary IS 'Primary goal gets highest priority in scoring and charts';
COMMENT ON COLUMN goals.status IS 'draft = not yet started, active = in progress, reached = successfully completed, abandoned = given up, expired = deadline passed';
COMMENT ON COLUMN goals.progress_pct IS 'Percentage progress: (current_value - start_value) / (target_value - start_value) * 100';
COMMENT ON COLUMN goals.projection_date IS 'Projected date when goal will be reached based on current trend';
COMMENT ON COLUMN goals.on_track IS 'true if projection_date <= target_date (goal reachable on time)';
-- ============================================================================
-- TRAINING PHASES (Auto-Detection)
-- ============================================================================
CREATE TABLE IF NOT EXISTS training_phases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
-- Phase Classification
phase_type VARCHAR(50) NOT NULL, -- calorie_deficit, calorie_surplus, deload, maintenance, periodization
detected_automatically BOOLEAN DEFAULT false,
confidence_score DECIMAL(3,2), -- 0.00 - 1.00 (Wie sicher ist die Erkennung?)
status VARCHAR(20) DEFAULT 'suggested', -- suggested, accepted, active, completed, rejected
-- Timeframe
start_date DATE NOT NULL,
end_date DATE,
duration_days INT,
-- Detection Criteria (JSONB für Flexibilität)
detection_params JSONB, -- { "avg_calories": 1800, "weight_trend": -0.3, ... }
-- User Notes
notes TEXT,
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_training_phases_profile ON training_phases(profile_id);
CREATE INDEX IF NOT EXISTS idx_training_phases_status ON training_phases(profile_id, status);
CREATE INDEX IF NOT EXISTS idx_training_phases_dates ON training_phases(profile_id, start_date, end_date);
COMMENT ON TABLE training_phases IS 'Training phases detected from data patterns or manually defined';
COMMENT ON COLUMN training_phases.phase_type IS 'calorie_deficit, calorie_surplus, deload, maintenance, periodization';
COMMENT ON COLUMN training_phases.detected_automatically IS 'true if AI detected this phase from data patterns';
COMMENT ON COLUMN training_phases.confidence_score IS 'AI confidence in detection (0.0 - 1.0)';
COMMENT ON COLUMN training_phases.status IS 'suggested = AI proposed, accepted = user confirmed, active = currently running, completed = finished, rejected = user dismissed';
COMMENT ON COLUMN training_phases.detection_params IS 'JSON with detection criteria: avg_calories, weight_trend, activity_volume, etc.';
-- ============================================================================
-- FITNESS TESTS (Standardized Performance Tests)
-- ============================================================================
CREATE TABLE IF NOT EXISTS fitness_tests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
-- Test Type
test_type VARCHAR(50) NOT NULL, -- cooper_12min, step_test, pushups_max, plank_max, flexibility_sit_reach, vo2max_est, strength_1rm_squat, strength_1rm_bench
result_value DECIMAL(10,2) NOT NULL,
result_unit VARCHAR(20) NOT NULL, -- meters, bpm, reps, seconds, cm, ml/kg/min, kg
-- Test Metadata
test_date DATE NOT NULL,
test_conditions TEXT, -- Optional: Notizen zu Bedingungen
norm_category VARCHAR(30), -- sehr gut, gut, durchschnitt, unterdurchschnitt, schlecht
-- Timestamps
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_fitness_tests_profile ON fitness_tests(profile_id);
CREATE INDEX IF NOT EXISTS idx_fitness_tests_type ON fitness_tests(profile_id, test_type);
CREATE INDEX IF NOT EXISTS idx_fitness_tests_date ON fitness_tests(profile_id, test_date);
COMMENT ON TABLE fitness_tests IS 'Standardized fitness tests (Cooper, step test, strength tests, etc.)';
COMMENT ON COLUMN fitness_tests.test_type IS 'cooper_12min, step_test, pushups_max, plank_max, flexibility_sit_reach, vo2max_est, strength_1rm_squat, strength_1rm_bench';
COMMENT ON COLUMN fitness_tests.norm_category IS 'Performance category based on age/gender norms';
-- ============================================================================
-- VERSION UPDATE
-- ============================================================================
-- Track migration
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM schema_migrations WHERE version = '022') THEN
INSERT INTO schema_migrations (version, applied_at) VALUES ('022', NOW());
END IF;
END $$;