BREAKING CHANGES:
- exercises.py komplett neu gebaut (kein Legacy-Code)
- Legacy-Felder entfernt: age_groups, focus_area, secondary_areas, training_character
- Nur M:N Relations, keine JSONB-Kataloge
Migrations:
- Migration 014: Variant Progression + Search Vector + Legacy DROP
- exercise_variants: progression_level, sequence_order, prerequisite_variant_id
- exercises: search_vector (tsvector für Volltext-Suche)
- DROP age_groups, focus_area, secondary_areas, training_character
- Helper: update_timestamp() Funktion für Triggers
- Migration 016: Saved Exercise Searches
- saved_exercise_searches (profile_id, name, filters JSONB)
- Migration 017: Exercise Blocks + Template Blocks
- exercise_blocks (name, description, goal, is_template)
- exercise_block_items (exercise_id, variant_id, sequence_order, is_placeholder, placeholder_criteria)
Backend (exercises.py v2.0):
- GET /exercises: Volltext-Suche via tsvector, M:N Joins
- GET /exercises/{id}: enrich_exercise_detail() mit allen M:N Relations
- POST /exercises: M:N Relations (focus_areas_multi, training_styles_multi, target_groups_multi, age_groups, skills)
- PUT /exercises: Partial Update + M:N Relations
- DELETE /exercises: Cascade-Check für exercise_block_items
Architecture:
- Issue #53 konform: Import = Feld-Zuordnung, keine fachliche Interpretation
- Helper: enrich_exercise_detail() für vollständige Objekte
- Helper: assign_exercise_relations() für M:N Management (DELETE+INSERT Pattern)
Docs:
- SMW_IMPORTER_GAP_ANALYSIS.md: Vollständige Gap-Analyse + Umsetzungsplan
Version: 0.7.0
Module: exercises 2.0.0
Schema: 20260424002
116 lines
4.5 KiB
PL/PgSQL
116 lines
4.5 KiB
PL/PgSQL
-- Migration 014: Variant Progression System + Search Vector + Legacy Cleanup
|
|
-- Autor: Claude Code
|
|
-- Datum: 2026-04-24
|
|
-- Zweck: Varianten-Progression, Volltext-Suche, Legacy-Spalten entfernen
|
|
|
|
DO $$
|
|
BEGIN
|
|
|
|
-- ============================================================================
|
|
-- HELPER FUNCTION: update_timestamp (für Triggers)
|
|
-- ============================================================================
|
|
|
|
CREATE OR REPLACE FUNCTION update_timestamp()
|
|
RETURNS trigger AS $func$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$func$ LANGUAGE plpgsql;
|
|
|
|
-- ============================================================================
|
|
-- VARIANT PROGRESSION
|
|
-- ============================================================================
|
|
|
|
-- Erweitere exercise_variants Tabelle
|
|
ALTER TABLE exercise_variants
|
|
ADD COLUMN IF NOT EXISTS progression_level INT DEFAULT 1 CHECK (progression_level BETWEEN 1 AND 10),
|
|
ADD COLUMN IF NOT EXISTS sequence_order INT,
|
|
ADD COLUMN IF NOT EXISTS prerequisite_variant_id INT REFERENCES exercise_variants(id) ON DELETE SET NULL;
|
|
|
|
-- Index für Prerequisites
|
|
CREATE INDEX IF NOT EXISTS idx_exercise_variants_prerequisite
|
|
ON exercise_variants(prerequisite_variant_id);
|
|
|
|
-- ============================================================================
|
|
-- SEARCH VECTOR (Volltext-Suche)
|
|
-- ============================================================================
|
|
|
|
-- Füge search_vector zu exercises hinzu
|
|
ALTER TABLE exercises
|
|
ADD COLUMN IF NOT EXISTS search_vector tsvector;
|
|
|
|
-- Index für Volltext-Suche
|
|
CREATE INDEX IF NOT EXISTS idx_exercises_search
|
|
ON exercises USING gin(search_vector);
|
|
|
|
-- Funktion für automatisches Update
|
|
CREATE OR REPLACE FUNCTION update_exercises_search_vector()
|
|
RETURNS trigger AS $func$
|
|
BEGIN
|
|
NEW.search_vector :=
|
|
setweight(to_tsvector('german', COALESCE(NEW.title, '')), 'A') ||
|
|
setweight(to_tsvector('german', COALESCE(NEW.summary, '')), 'B') ||
|
|
setweight(to_tsvector('german', COALESCE(NEW.execution, '')), 'C') ||
|
|
setweight(to_tsvector('german', COALESCE(NEW.trainer_notes, '')), 'D');
|
|
RETURN NEW;
|
|
END;
|
|
$func$ LANGUAGE plpgsql;
|
|
|
|
-- Trigger
|
|
DROP TRIGGER IF EXISTS exercises_search_update ON exercises;
|
|
CREATE TRIGGER exercises_search_update
|
|
BEFORE INSERT OR UPDATE ON exercises
|
|
FOR EACH ROW EXECUTE FUNCTION update_exercises_search_vector();
|
|
|
|
-- Initiales Befüllen (für existierende Zeilen)
|
|
UPDATE exercises SET search_vector = (
|
|
setweight(to_tsvector('german', COALESCE(title, '')), 'A') ||
|
|
setweight(to_tsvector('german', COALESCE(summary, '')), 'B') ||
|
|
setweight(to_tsvector('german', COALESCE(execution, '')), 'C') ||
|
|
setweight(to_tsvector('german', COALESCE(trainer_notes, '')), 'D')
|
|
) WHERE search_vector IS NULL;
|
|
|
|
-- ============================================================================
|
|
-- LEGACY COLUMN CLEANUP
|
|
-- Deprecated Felder aus exercises (ersetzt durch M:N Tabellen in Migration 008+)
|
|
-- ============================================================================
|
|
|
|
-- age_groups JSONB → ersetzt durch exercise_age_groups M:N (seit Migration 008)
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS age_groups;
|
|
|
|
-- focus_area VARCHAR → ersetzt durch exercise_focus_areas M:N (seit Migration 008)
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS focus_area;
|
|
|
|
-- secondary_areas JSONB → ersetzt durch exercise_focus_areas M:N
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS secondary_areas;
|
|
|
|
-- training_character VARCHAR → ersetzt durch exercise_training_characters M:N (seit Migration 012)
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS training_character;
|
|
|
|
-- ============================================================================
|
|
-- ADDITIONAL INDEXES (Performance)
|
|
-- ============================================================================
|
|
|
|
-- Häufige Filter
|
|
CREATE INDEX IF NOT EXISTS idx_exercises_visibility ON exercises(visibility);
|
|
CREATE INDEX IF NOT EXISTS idx_exercises_status ON exercises(status);
|
|
CREATE INDEX IF NOT EXISTS idx_exercises_created_at ON exercises(created_at DESC);
|
|
|
|
-- M:N Relations (falls noch nicht vorhanden)
|
|
CREATE INDEX IF NOT EXISTS idx_exercise_focus_areas_focus
|
|
ON exercise_focus_areas(focus_area_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_exercise_styles_style
|
|
ON exercise_training_styles(style_direction_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_exercise_target_groups_group
|
|
ON exercise_target_groups(target_group_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_exercise_skills_skill
|
|
ON exercise_skills(skill_id);
|
|
|
|
RAISE NOTICE 'Migration 014 completed successfully';
|
|
|
|
END $$;
|