BREAKING: Replaces single 'primary goal' with weighted multi-goal system Migration 027: - New table: focus_areas (6 dimensions with percentages) - Constraint: Sum must equal 100% - Auto-migration: goal_mode → focus_areas for existing users - Unique constraint: One active focus_areas per profile Backend: - get_focus_weights() V2: Reads from focus_areas table - Fallback: Uses goal_mode if focus_areas not set - New endpoints: GET/PUT /api/goals/focus-areas - Validation: Sum=100, range 0-100 API: - getFocusAreas() - Get current weights - updateFocusAreas(data) - Update weights (upsert) Focus dimensions: 1. weight_loss_pct (Fettabbau) 2. muscle_gain_pct (Muskelaufbau) 3. strength_pct (Kraftsteigerung) 4. endurance_pct (Ausdauer) 5. flexibility_pct (Beweglichkeit) 6. health_pct (Allgemeine Gesundheit) Benefits: - Multiple goals with custom priorities - More flexible than single primary goal - KI can use weighted scores - Ready for Phase 0b placeholder integration UI: Coming in next commit (slider interface)
135 lines
4.4 KiB
SQL
135 lines
4.4 KiB
SQL
-- Migration 027: Focus Areas System (Goal System v2.0)
|
|
-- Date: 2026-03-27
|
|
-- Purpose: Replace single primary goal with weighted multi-goal system
|
|
|
|
-- ============================================================================
|
|
-- Focus Areas Table
|
|
-- ============================================================================
|
|
|
|
CREATE TABLE IF NOT EXISTS focus_areas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
profile_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
|
|
|
-- Six focus dimensions (percentages, sum = 100)
|
|
weight_loss_pct INTEGER DEFAULT 0 CHECK (weight_loss_pct >= 0 AND weight_loss_pct <= 100),
|
|
muscle_gain_pct INTEGER DEFAULT 0 CHECK (muscle_gain_pct >= 0 AND muscle_gain_pct <= 100),
|
|
strength_pct INTEGER DEFAULT 0 CHECK (strength_pct >= 0 AND strength_pct <= 100),
|
|
endurance_pct INTEGER DEFAULT 0 CHECK (endurance_pct >= 0 AND endurance_pct <= 100),
|
|
flexibility_pct INTEGER DEFAULT 0 CHECK (flexibility_pct >= 0 AND flexibility_pct <= 100),
|
|
health_pct INTEGER DEFAULT 0 CHECK (health_pct >= 0 AND health_pct <= 100),
|
|
|
|
-- Status
|
|
active BOOLEAN DEFAULT true,
|
|
|
|
-- Audit
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
CONSTRAINT sum_equals_100 CHECK (
|
|
weight_loss_pct + muscle_gain_pct + strength_pct +
|
|
endurance_pct + flexibility_pct + health_pct = 100
|
|
)
|
|
);
|
|
|
|
-- Only one active focus_areas per profile
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_focus_areas_profile_active
|
|
ON focus_areas(profile_id) WHERE active = true;
|
|
|
|
COMMENT ON TABLE focus_areas IS 'User-defined focus area weights (replaces simple goal_mode). Enables multi-goal prioritization with custom percentages.';
|
|
COMMENT ON COLUMN focus_areas.weight_loss_pct IS 'Focus on fat loss (0-100%)';
|
|
COMMENT ON COLUMN focus_areas.muscle_gain_pct IS 'Focus on muscle growth (0-100%)';
|
|
COMMENT ON COLUMN focus_areas.strength_pct IS 'Focus on strength gains (0-100%)';
|
|
COMMENT ON COLUMN focus_areas.endurance_pct IS 'Focus on aerobic capacity (0-100%)';
|
|
COMMENT ON COLUMN focus_areas.flexibility_pct IS 'Focus on mobility/flexibility (0-100%)';
|
|
COMMENT ON COLUMN focus_areas.health_pct IS 'Focus on general health (0-100%)';
|
|
|
|
-- ============================================================================
|
|
-- Migrate existing goal_mode to focus_areas
|
|
-- ============================================================================
|
|
|
|
-- For each profile with a goal_mode, create initial focus_areas
|
|
INSERT INTO focus_areas (
|
|
profile_id,
|
|
weight_loss_pct, muscle_gain_pct, strength_pct,
|
|
endurance_pct, flexibility_pct, health_pct
|
|
)
|
|
SELECT
|
|
id AS profile_id,
|
|
CASE goal_mode
|
|
WHEN 'weight_loss' THEN 60 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'recomposition' THEN 30 ELSE 0
|
|
END AS weight_loss_pct,
|
|
|
|
CASE goal_mode
|
|
WHEN 'strength' THEN 40 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'recomposition' THEN 30 ELSE 0
|
|
END AS muscle_gain_pct,
|
|
|
|
CASE goal_mode
|
|
WHEN 'strength' THEN 50 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'recomposition' THEN 25 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'weight_loss' THEN 10 ELSE 0
|
|
END AS strength_pct,
|
|
|
|
CASE goal_mode
|
|
WHEN 'endurance' THEN 70 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'recomposition' THEN 10 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'weight_loss' THEN 20 ELSE 0
|
|
END AS endurance_pct,
|
|
|
|
CASE goal_mode
|
|
WHEN 'endurance' THEN 10 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'health' THEN 15 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'recomposition' THEN 5 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'weight_loss' THEN 5 ELSE 0
|
|
END AS flexibility_pct,
|
|
|
|
CASE goal_mode
|
|
WHEN 'health' THEN 50 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'endurance' THEN 20 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'strength' THEN 10 ELSE 0
|
|
END +
|
|
CASE goal_mode
|
|
WHEN 'weight_loss' THEN 5 ELSE 0
|
|
END AS health_pct
|
|
FROM profiles
|
|
WHERE goal_mode IS NOT NULL
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- For profiles without goal_mode, use balanced health focus
|
|
INSERT INTO focus_areas (
|
|
profile_id,
|
|
weight_loss_pct, muscle_gain_pct, strength_pct,
|
|
endurance_pct, flexibility_pct, health_pct
|
|
)
|
|
SELECT
|
|
id AS profile_id,
|
|
0, 0, 10, 20, 15, 55
|
|
FROM profiles
|
|
WHERE goal_mode IS NULL
|
|
AND id NOT IN (SELECT profile_id FROM focus_areas WHERE active = true)
|
|
ON CONFLICT DO NOTHING;
|