146 lines
5.0 KiB
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;
|