Replace rename-based lesson migration with additive sub_module_lessons creation, preserve sub_module_practices as its own model, and enforce QUIZ/PRACTICE filtering in hierarchy reads to prevent cross-mixing. Made-with: Cursor
198 lines
4.5 KiB
SQL
198 lines
4.5 KiB
SQL
-- name: GetCoursesWithHierarchy :many
|
|
SELECT
|
|
cc.id AS category_id,
|
|
cc.name AS category_name,
|
|
csc.id AS sub_category_id,
|
|
csc.name AS sub_category_name,
|
|
c.id AS course_id,
|
|
c.title AS course_title
|
|
FROM course_categories cc
|
|
LEFT JOIN course_sub_categories csc ON csc.category_id = cc.id AND csc.is_active = TRUE
|
|
LEFT JOIN courses c ON c.sub_category_id = csc.id AND c.is_active = TRUE
|
|
WHERE cc.is_active = TRUE
|
|
ORDER BY cc.id, csc.display_order, csc.id, c.id;
|
|
|
|
-- name: GetLevelsByCourseID :many
|
|
SELECT *
|
|
FROM levels
|
|
WHERE course_id = $1
|
|
AND is_active = TRUE
|
|
ORDER BY display_order ASC, id ASC;
|
|
|
|
-- name: GetModulesByLevelID :many
|
|
SELECT *
|
|
FROM modules
|
|
WHERE level_id = $1
|
|
AND is_active = TRUE
|
|
ORDER BY display_order ASC, id ASC;
|
|
|
|
-- name: GetSubModulesByModuleID :many
|
|
SELECT *
|
|
FROM sub_modules
|
|
WHERE module_id = $1
|
|
AND is_active = TRUE
|
|
ORDER BY display_order ASC, id ASC;
|
|
|
|
-- name: GetSubModuleVideos :many
|
|
SELECT *
|
|
FROM sub_module_videos
|
|
WHERE sub_module_id = $1
|
|
AND status != 'ARCHIVED'
|
|
ORDER BY display_order ASC, id ASC;
|
|
|
|
-- name: GetSubModuleLessons :many
|
|
SELECT
|
|
smp.id,
|
|
smp.sub_module_id,
|
|
smp.question_set_id,
|
|
smp.intro_video_url,
|
|
smp.display_order,
|
|
smp.is_active,
|
|
qs.title,
|
|
qs.description,
|
|
qs.status,
|
|
qs.set_type,
|
|
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
|
|
FROM sub_module_lessons smp
|
|
JOIN question_sets qs ON qs.id = smp.question_set_id
|
|
WHERE smp.sub_module_id = $1
|
|
AND smp.is_active = TRUE
|
|
AND qs.set_type = 'QUIZ'
|
|
ORDER BY smp.display_order ASC, smp.id ASC;
|
|
|
|
-- name: GetSubModulePractices :many
|
|
SELECT
|
|
smp.id,
|
|
smp.sub_module_id,
|
|
smp.title,
|
|
smp.description,
|
|
smp.thumbnail,
|
|
smp.intro_video_url,
|
|
smp.question_set_id,
|
|
smp.display_order,
|
|
smp.is_active,
|
|
qs.status,
|
|
qs.set_type,
|
|
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
|
|
FROM sub_module_practices smp
|
|
JOIN question_sets qs ON qs.id = smp.question_set_id
|
|
WHERE smp.sub_module_id = $1
|
|
AND smp.is_active = TRUE
|
|
AND qs.set_type = 'PRACTICE'
|
|
ORDER BY smp.display_order ASC, smp.id ASC;
|
|
|
|
-- name: GetFullHierarchyByCourseID :many
|
|
SELECT
|
|
c.id AS course_id,
|
|
c.title AS course_title,
|
|
l.id AS level_id,
|
|
l.cefr_level,
|
|
m.id AS module_id,
|
|
m.title AS module_title,
|
|
sm.id AS sub_module_id,
|
|
sm.title AS sub_module_title
|
|
FROM courses c
|
|
LEFT JOIN levels l ON l.course_id = c.id AND l.is_active = TRUE
|
|
LEFT JOIN modules m ON m.level_id = l.id AND m.is_active = TRUE
|
|
LEFT JOIN sub_modules sm ON sm.module_id = m.id AND sm.is_active = TRUE
|
|
WHERE c.id = $1
|
|
ORDER BY l.display_order, l.id, m.display_order, m.id, sm.display_order, sm.id;
|
|
|
|
-- name: CreateCourseSubCategory :one
|
|
INSERT INTO course_sub_categories (
|
|
category_id,
|
|
name,
|
|
description,
|
|
display_order,
|
|
is_active
|
|
)
|
|
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE))
|
|
RETURNING *;
|
|
|
|
-- name: CreateLevel :one
|
|
INSERT INTO levels (
|
|
course_id,
|
|
cefr_level,
|
|
display_order,
|
|
is_active
|
|
)
|
|
VALUES ($1, $2, COALESCE($3, 0), COALESCE($4, TRUE))
|
|
RETURNING *;
|
|
|
|
-- name: CreateModule :one
|
|
INSERT INTO modules (
|
|
level_id,
|
|
title,
|
|
description,
|
|
display_order,
|
|
is_active
|
|
)
|
|
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE))
|
|
RETURNING *;
|
|
|
|
-- name: CreateSubModule :one
|
|
INSERT INTO sub_modules (
|
|
module_id,
|
|
title,
|
|
description,
|
|
display_order,
|
|
is_active
|
|
)
|
|
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE))
|
|
RETURNING *;
|
|
|
|
-- name: CreateSubModuleVideo :one
|
|
INSERT INTO sub_module_videos (
|
|
sub_module_id,
|
|
title,
|
|
description,
|
|
video_url,
|
|
duration,
|
|
resolution,
|
|
is_published,
|
|
publish_date,
|
|
visibility,
|
|
instructor_id,
|
|
thumbnail,
|
|
display_order,
|
|
status,
|
|
vimeo_id,
|
|
vimeo_embed_url,
|
|
vimeo_player_html,
|
|
vimeo_status,
|
|
video_host_provider
|
|
)
|
|
VALUES (
|
|
$1, $2, $3, $4, $5, $6,
|
|
COALESCE($7, FALSE), $8, $9, $10, $11,
|
|
COALESCE($12, 0), COALESCE($13, 'DRAFT'),
|
|
$14, $15, $16, $17, COALESCE($18, 'DIRECT')
|
|
)
|
|
RETURNING *;
|
|
|
|
-- name: AttachQuestionSetLessonToSubModule :one
|
|
INSERT INTO sub_module_lessons (
|
|
sub_module_id,
|
|
question_set_id,
|
|
intro_video_url,
|
|
display_order,
|
|
is_active
|
|
)
|
|
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, TRUE))
|
|
RETURNING *;
|
|
|
|
-- name: CreateSubModulePractice :one
|
|
INSERT INTO sub_module_practices (
|
|
sub_module_id,
|
|
title,
|
|
description,
|
|
thumbnail,
|
|
intro_video_url,
|
|
question_set_id,
|
|
display_order,
|
|
is_active
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 0), COALESCE($8, TRUE))
|
|
RETURNING *;
|
|
|