-- Migration 025: Reifegradmodell-Kontext als M:N (Fokusbereich, Stilrichtung, Zielgruppe) -- + Bootstrap: ein Modell aus allen importierten Skills (Wiki / Migration 023) -- Datum: 2026-04-27 -- ============================================================================ -- JUNCTION TABLES -- ============================================================================ CREATE TABLE IF NOT EXISTS maturity_model_focus_areas ( id SERIAL PRIMARY KEY, maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE, focus_area_id INT NOT NULL REFERENCES focus_areas(id) ON DELETE CASCADE, is_primary BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT NOW(), UNIQUE (maturity_model_id, focus_area_id) ); CREATE INDEX IF NOT EXISTS idx_mmfa_model ON maturity_model_focus_areas(maturity_model_id); CREATE INDEX IF NOT EXISTS idx_mmfa_focus ON maturity_model_focus_areas(focus_area_id); CREATE TABLE IF NOT EXISTS maturity_model_style_directions ( id SERIAL PRIMARY KEY, maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE, style_direction_id INT NOT NULL REFERENCES style_directions(id) ON DELETE CASCADE, is_primary BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT NOW(), UNIQUE (maturity_model_id, style_direction_id) ); CREATE INDEX IF NOT EXISTS idx_mmsd_model ON maturity_model_style_directions(maturity_model_id); CREATE INDEX IF NOT EXISTS idx_mmsd_style ON maturity_model_style_directions(style_direction_id); CREATE TABLE IF NOT EXISTS maturity_model_target_groups ( id SERIAL PRIMARY KEY, maturity_model_id INT NOT NULL REFERENCES maturity_models(id) ON DELETE CASCADE, target_group_id INT NOT NULL REFERENCES target_groups(id) ON DELETE CASCADE, is_primary BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT NOW(), UNIQUE (maturity_model_id, target_group_id) ); CREATE INDEX IF NOT EXISTS idx_mmtg_model ON maturity_model_target_groups(maturity_model_id); CREATE INDEX IF NOT EXISTS idx_mmtg_tg ON maturity_model_target_groups(target_group_id); -- ============================================================================ -- Daten aus 1:1-FKs übernehmen (falls Spalten noch existieren), dann FK-Spalten entfernen -- ============================================================================ DO $$ BEGIN IF EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'maturity_models' AND column_name = 'focus_area_id' ) THEN INSERT INTO maturity_model_focus_areas (maturity_model_id, focus_area_id, is_primary) SELECT id, focus_area_id, true FROM maturity_models WHERE focus_area_id IS NOT NULL ON CONFLICT (maturity_model_id, focus_area_id) DO NOTHING; END IF; IF EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'maturity_models' AND column_name = 'style_direction_id' ) THEN INSERT INTO maturity_model_style_directions (maturity_model_id, style_direction_id, is_primary) SELECT id, style_direction_id, true FROM maturity_models WHERE style_direction_id IS NOT NULL ON CONFLICT (maturity_model_id, style_direction_id) DO NOTHING; END IF; IF EXISTS ( SELECT 1 FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'maturity_models' AND column_name = 'target_group_id' ) THEN INSERT INTO maturity_model_target_groups (maturity_model_id, target_group_id, is_primary) SELECT id, target_group_id, true FROM maturity_models WHERE target_group_id IS NOT NULL ON CONFLICT (maturity_model_id, target_group_id) DO NOTHING; END IF; END $$; ALTER TABLE maturity_models DROP CONSTRAINT IF EXISTS maturity_models_context_name_unique; ALTER TABLE maturity_models DROP COLUMN IF EXISTS focus_area_id; ALTER TABLE maturity_models DROP COLUMN IF EXISTS style_direction_id; ALTER TABLE maturity_models DROP COLUMN IF EXISTS target_group_id; CREATE UNIQUE INDEX IF NOT EXISTS idx_maturity_models_wiki_import ON maturity_models(import_source, import_id) WHERE import_id IS NOT NULL; -- ============================================================================ -- Bootstrap: Standard-Modell für alle Skills aus der Wiki-Matrix (Migration 023) -- ============================================================================ DO $$ DECLARE mid INT; n_skills INT; BEGIN SELECT COUNT(*) INTO n_skills FROM skills; IF n_skills = 0 THEN RAISE NOTICE 'Bootstrap: keine Skills – übersprungen'; RETURN; END IF; SELECT id INTO mid FROM maturity_models WHERE import_id = 'faehigkeitsmatrix_karatetrainer' LIMIT 1; IF mid IS NOT NULL THEN RAISE NOTICE 'Bootstrap: Modell bereits vorhanden (id=%)', mid; RETURN; END IF; INSERT INTO maturity_models (name, description, level_count, status, import_source, import_id, version) VALUES ( 'Fähigkeitsmatrix (Wiki Import)', 'Alle Fähigkeiten aus der Wiki-Matrix (Karatetrainer). Haupt- und Untergruppen kommen aus skill_main_categories / skill_categories.', 5, 'active', 'wiki_matrix', 'faehigkeitsmatrix_karatetrainer', '1.0' ) RETURNING id INTO mid; INSERT INTO maturity_model_focus_areas (maturity_model_id, focus_area_id, is_primary) SELECT mid, fa.id, (fa.name = 'Karate') FROM focus_areas fa WHERE fa.name IN ('Karate', 'Selbstverteidigung', 'Gewaltschutz') ON CONFLICT (maturity_model_id, focus_area_id) DO NOTHING; INSERT INTO model_levels (maturity_model_id, level_number, name, description, sort_order) VALUES (mid, 1, 'Einsteiger', NULL, 1), (mid, 2, 'Grundlagen', NULL, 2), (mid, 3, 'Aufbau', NULL, 3), (mid, 4, 'Fortgeschritten', NULL, 4), (mid, 5, 'Experte', NULL, 5); INSERT INTO model_skills (maturity_model_id, skill_id, sort_order, relevance) SELECT mid, s.id, ROW_NUMBER() OVER ( ORDER BY mc.sort_order NULLS LAST, sc.sort_order NULLS LAST, s.name ), NULL FROM skills s LEFT JOIN skill_categories sc ON s.category_id = sc.id LEFT JOIN skill_main_categories mc ON s.main_category_id = mc.id; INSERT INTO model_skill_levels (maturity_model_id, skill_id, level_number, description, observable_criteria) SELECT mid, sld.skill_id, sld.level, sld.description, NULL FROM skill_level_definitions sld ON CONFLICT (maturity_model_id, skill_id, level_number) DO NOTHING; RAISE NOTICE 'Bootstrap: Reifegradmodell id=% angelegt (% Skills)', mid, n_skills; END $$;