Yimaru-BackEnd/db/migrations/000003_simplify_courses.up.sql

146 lines
5.0 KiB
SQL

-- Migration: Simplify course hierarchy
-- OLD: Course Category → Course → Program → Level → Module → (Video, Practice)
-- NEW: Course Category → Course → Sub-course (with level) → (Video, Practice)
-- Step 1: Create new tables
CREATE TABLE IF NOT EXISTS sub_courses (
id BIGSERIAL PRIMARY KEY,
course_id BIGINT NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
thumbnail TEXT,
display_order INT NOT NULL DEFAULT 0,
level VARCHAR(50) NOT NULL, -- BEGINNER, INTERMEDIATE, ADVANCED
is_active BOOLEAN NOT NULL DEFAULT TRUE,
CHECK (level IN ('BEGINNER', 'INTERMEDIATE', 'ADVANCED'))
);
CREATE INDEX IF NOT EXISTS idx_sub_courses_course_id ON sub_courses(course_id);
CREATE TABLE IF NOT EXISTS sub_course_videos (
id BIGSERIAL PRIMARY KEY,
sub_course_id BIGINT NOT NULL REFERENCES sub_courses(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
description TEXT,
video_url TEXT NOT NULL,
duration INT NOT NULL, -- seconds
resolution VARCHAR(20), -- "720p", "1080p"
is_published BOOLEAN NOT NULL DEFAULT FALSE,
publish_date TIMESTAMPTZ,
visibility VARCHAR(50), -- public, private, unlisted
instructor_id VARCHAR(100),
thumbnail TEXT,
display_order INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE INDEX IF NOT EXISTS idx_sub_course_videos_sub_course_id ON sub_course_videos(sub_course_id);
-- Step 2: Add sub_course_id to practices (nullable during migration)
ALTER TABLE practices ADD COLUMN IF NOT EXISTS sub_course_id BIGINT NULL;
CREATE INDEX IF NOT EXISTS idx_practices_sub_course_id ON practices(sub_course_id);
-- Step 3: Migrate data from old structure to new structure
-- Insert sub-courses from (program, level) combinations
INSERT INTO sub_courses (course_id, title, description, thumbnail, display_order, level, is_active)
SELECT
p.course_id,
(p.title || ' - ' || l.title) as title,
COALESCE(l.description, p.description) as description,
p.thumbnail,
(p.display_order * 100 + l.level_index) as display_order,
CASE l.level_index
WHEN 1 THEN 'BEGINNER'
WHEN 2 THEN 'INTERMEDIATE'
WHEN 3 THEN 'ADVANCED'
ELSE 'BEGINNER'
END as level,
(l.is_active AND p.is_active) as is_active
FROM levels l
JOIN programs p ON p.id = l.program_id;
-- Create temporary mapping table for migration
CREATE TEMP TABLE level_to_sub_course AS
SELECT
l.id as level_id,
sc.id as sub_course_id
FROM levels l
JOIN programs p ON p.id = l.program_id
JOIN sub_courses sc
ON sc.course_id = p.course_id
AND sc.title = (p.title || ' - ' || l.title);
-- Create temporary mapping for modules to sub-courses
CREATE TEMP TABLE module_to_sub_course AS
SELECT
m.id as module_id,
lsc.sub_course_id
FROM modules m
JOIN level_to_sub_course lsc ON lsc.level_id = m.level_id;
-- Migrate videos from module_videos to sub_course_videos
INSERT INTO sub_course_videos (
sub_course_id, title, description, video_url, duration, resolution,
is_published, publish_date, visibility, instructor_id, thumbnail, display_order, is_active
)
SELECT
msc.sub_course_id,
mv.title,
mv.description,
mv.video_url,
mv.duration,
mv.resolution,
mv.is_published,
mv.publish_date,
mv.visibility,
mv.instructor_id,
mv.thumbnail,
(m.display_order * 100 + mv.id) as display_order,
mv.is_active
FROM module_videos mv
JOIN modules m ON m.id = mv.module_id
JOIN module_to_sub_course msc ON msc.module_id = m.id;
-- Migrate practices owned by LEVEL
UPDATE practices pr
SET sub_course_id = lsc.sub_course_id
FROM level_to_sub_course lsc
WHERE pr.owner_type = 'LEVEL'
AND pr.owner_id = lsc.level_id;
-- Migrate practices owned by MODULE
UPDATE practices pr
SET sub_course_id = msc.sub_course_id
FROM module_to_sub_course msc
WHERE pr.owner_type = 'MODULE'
AND pr.owner_id = msc.module_id;
-- Step 4: Enforce integrity on practices
ALTER TABLE practices
ADD CONSTRAINT practices_sub_course_id_fkey
FOREIGN KEY (sub_course_id) REFERENCES sub_courses(id) ON DELETE CASCADE;
-- Make sub_course_id NOT NULL (only if there's data to migrate)
-- If practices table has rows without sub_course_id after migration, this will fail
-- ALTER TABLE practices ALTER COLUMN sub_course_id SET NOT NULL;
-- Step 5: Drop old columns from practices
ALTER TABLE practices DROP CONSTRAINT IF EXISTS practices_owner_type_check;
ALTER TABLE practices DROP COLUMN IF EXISTS owner_type;
ALTER TABLE practices DROP COLUMN IF EXISTS owner_id;
-- Step 6: Drop old indexes
DROP INDEX IF EXISTS idx_videos_module_id;
DROP INDEX IF EXISTS idx_modules_level_id;
DROP INDEX IF EXISTS idx_levels_program_id;
DROP INDEX IF EXISTS idx_programs_course_id;
DROP INDEX IF EXISTS idx_practices_owner;
-- Step 7: Drop old tables (CASCADE to remove FK references)
DROP TABLE IF EXISTS module_videos CASCADE;
DROP TABLE IF EXISTS modules CASCADE;
DROP TABLE IF EXISTS levels CASCADE;
DROP TABLE IF EXISTS programs CASCADE;