470 lines
23 KiB
SQL
470 lines
23 KiB
SQL
-- ======================================================
|
|
-- Complete Course Management Seed Data
|
|
-- Covers: categories, courses, sub-courses, videos,
|
|
-- question sets, questions, options, prerequisites,
|
|
-- and user progress for admin panel integration
|
|
-- ======================================================
|
|
|
|
-- ======================================================
|
|
-- Course Categories (supplement existing 3 categories)
|
|
-- Existing: 1=Programming, 2=Data Science, 3=Web Development
|
|
-- ======================================================
|
|
INSERT INTO course_categories (id, name, is_active, created_at) VALUES
|
|
(4, 'Mobile Development', TRUE, CURRENT_TIMESTAMP),
|
|
(5, 'DevOps & Cloud', TRUE, CURRENT_TIMESTAMP),
|
|
(6, 'Cybersecurity', FALSE, CURRENT_TIMESTAMP)
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- ======================================================
|
|
-- Courses (supplement existing 7 courses)
|
|
-- Existing: 1-7 in categories 1-3
|
|
-- ======================================================
|
|
INSERT INTO courses (id, category_id, title, description, thumbnail, intro_video_url, is_active) VALUES
|
|
(8, 4, 'Flutter App Development', 'Build cross-platform mobile apps with Flutter and Dart', 'https://example.com/thumbnails/flutter.jpg', 'https://example.com/intro/flutter.mp4', TRUE),
|
|
(9, 4, 'React Native Essentials', 'Create native mobile apps using React Native', 'https://example.com/thumbnails/react-native.jpg', NULL, TRUE),
|
|
(10, 5, 'Docker & Kubernetes', 'Container orchestration and deployment strategies', 'https://example.com/thumbnails/docker-k8s.jpg', 'https://example.com/intro/docker.mp4', TRUE),
|
|
(11, 5, 'CI/CD Pipeline Mastery', 'Automate your build, test, and deployment workflows', 'https://example.com/thumbnails/cicd.jpg', NULL, FALSE),
|
|
(12, 6, 'Ethical Hacking Fundamentals', 'Learn penetration testing and security analysis', 'https://example.com/thumbnails/ethical-hacking.jpg', NULL, FALSE)
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- ======================================================
|
|
-- Sub-courses (supplement existing 17 sub-courses: IDs 1-17)
|
|
-- ======================================================
|
|
INSERT INTO sub_courses (id, course_id, title, description, thumbnail, display_order, level, sub_level, is_active) VALUES
|
|
-- Flutter sub-courses (course 8) — IDs 18-21
|
|
(18, 8, 'Dart Language Basics', 'Learn Dart programming language fundamentals', NULL, 1, 'BEGINNER', 'A1', TRUE),
|
|
(19, 8, 'Flutter UI Widgets', 'Build beautiful UIs with Flutter widgets', NULL, 2, 'BEGINNER', 'A2', TRUE),
|
|
(20, 8, 'State Management', 'Manage app state with Provider and Riverpod', NULL, 3, 'INTERMEDIATE', 'B1', TRUE),
|
|
(21, 8, 'Flutter Networking & APIs', 'HTTP requests, REST APIs, and data persistence', NULL, 4, 'ADVANCED', 'C1', TRUE),
|
|
|
|
-- React Native sub-courses (course 9) — IDs 22-24
|
|
(22, 9, 'React Native Setup', 'Environment setup and first app', NULL, 1, 'BEGINNER', 'A1', TRUE),
|
|
(23, 9, 'Navigation & Routing', 'React Navigation and screen management', NULL, 2, 'INTERMEDIATE', 'B1', TRUE),
|
|
(24, 9, 'Native Modules', 'Bridge native code with React Native', NULL, 3, 'ADVANCED', 'C1', TRUE),
|
|
|
|
-- Docker & Kubernetes sub-courses (course 10) — IDs 25-27
|
|
(25, 10, 'Docker Fundamentals', 'Containers, images, and Dockerfiles', NULL, 1, 'BEGINNER', 'A1', TRUE),
|
|
(26, 10, 'Docker Compose', 'Multi-container applications', NULL, 2, 'INTERMEDIATE', 'B1', TRUE),
|
|
(27, 10, 'Kubernetes Basics', 'Pods, services, and deployments', NULL, 3, 'ADVANCED', 'C1', TRUE),
|
|
|
|
-- CI/CD sub-courses (course 11) — IDs 28-29
|
|
(28, 11, 'Git Workflows', 'Branching strategies and pull requests', NULL, 1, 'BEGINNER', 'A1', TRUE),
|
|
(29, 11, 'GitHub Actions', 'Automate workflows with GitHub Actions', NULL, 2, 'INTERMEDIATE', 'B1', TRUE),
|
|
|
|
-- Cybersecurity sub-courses (course 12) — IDs 30-31
|
|
(30, 12, 'Network Security Basics', 'Firewalls, VPNs, and network protocols', NULL, 1, 'BEGINNER', 'A1', TRUE),
|
|
(31, 12, 'Penetration Testing', 'Tools and techniques for pen testing', NULL, 2, 'ADVANCED', 'C1', TRUE)
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- ======================================================
|
|
-- Sub-course Videos (supplement existing 5 videos: IDs 1-5)
|
|
-- ======================================================
|
|
INSERT INTO sub_course_videos (
|
|
id, sub_course_id, title, description, video_url,
|
|
duration, resolution, visibility, display_order, status,
|
|
video_host_provider, vimeo_id, vimeo_embed_url, vimeo_status
|
|
) VALUES
|
|
-- Dart Language Basics videos (sub_course 18)
|
|
(6, 18, 'Introduction to Dart', 'Overview of Dart programming language', 'https://example.com/dart-intro.mp4', 720, '1080p', 'public', 1, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
(7, 18, 'Variables and Data Types', 'Dart variables, constants, and types', 'https://example.com/dart-variables.mp4', 900, '1080p', 'public', 2, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
(8, 18, 'Control Flow in Dart', 'If/else, loops, and switch statements', 'https://example.com/dart-control.mp4', 1100, '720p', 'public', 3, 'DRAFT', 'DIRECT', NULL, NULL, NULL),
|
|
|
|
-- Flutter UI Widgets videos (sub_course 19)
|
|
(9, 19, 'Widget Tree Basics', 'Understanding the Flutter widget tree', 'https://player.vimeo.com/video/100000001', 1500, '1080p', 'public', 1, 'PUBLISHED', 'VIMEO', '100000001', 'https://player.vimeo.com/video/100000001', 'available'),
|
|
(10, 19, 'Layout Widgets', 'Row, Column, Stack, and Container widgets', 'https://player.vimeo.com/video/100000002', 1800, '1080p', 'public', 2, 'PUBLISHED', 'VIMEO', '100000002', 'https://player.vimeo.com/video/100000002', 'available'),
|
|
(11, 19, 'Custom Widgets', 'Building reusable custom widgets', 'https://player.vimeo.com/video/100000003', 2100, '1080p', 'public', 3, 'DRAFT', 'VIMEO', '100000003', 'https://player.vimeo.com/video/100000003', 'transcoding'),
|
|
|
|
-- State Management videos (sub_course 20)
|
|
(12, 20, 'setState and Stateful Widgets', 'Managing local state in Flutter', 'https://example.com/flutter-setstate.mp4', 1200, '1080p', 'public', 1, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
(13, 20, 'Provider Pattern', 'Global state management with Provider', 'https://example.com/flutter-provider.mp4', 1600, '1080p', 'public', 2, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
|
|
-- Docker Fundamentals videos (sub_course 25)
|
|
(14, 25, 'What is Docker?', 'Introduction to containerization', 'https://example.com/docker-intro.mp4', 600, '1080p', 'public', 1, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
(15, 25, 'Building Docker Images', 'Writing Dockerfiles and building images', 'https://example.com/docker-images.mp4', 1400, '1080p', 'public', 2, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
|
|
-- Docker Compose videos (sub_course 26)
|
|
(16, 26, 'Docker Compose Basics', 'Defining multi-container applications', 'https://example.com/compose-basics.mp4', 1300, '1080p', 'public', 1, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
|
|
-- React Native Setup videos (sub_course 22)
|
|
(17, 22, 'Setting Up React Native', 'Installing React Native CLI and Expo', 'https://example.com/rn-setup.mp4', 900, '1080p', 'public', 1, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL),
|
|
(18, 22, 'Your First React Native App', 'Creating and running a basic app', 'https://example.com/rn-first-app.mp4', 1100, '1080p', 'public', 2, 'PUBLISHED', 'DIRECT', NULL, NULL, NULL)
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- ======================================================
|
|
-- Question Options for existing practice questions (17-20)
|
|
-- These were missing from the initial seed
|
|
-- ======================================================
|
|
INSERT INTO question_options (question_id, option_text, option_order, is_correct) VALUES
|
|
-- Q17: What is the correct way to print "Hello World" in Python?
|
|
(17, 'print("Hello World")', 1, TRUE),
|
|
(17, 'echo "Hello World"', 2, FALSE),
|
|
(17, 'console.log("Hello World")', 3, FALSE),
|
|
(17, 'System.out.println("Hello World")', 4, FALSE),
|
|
|
|
-- Q18: Which is a valid Python variable name?
|
|
(18, '2name', 1, FALSE),
|
|
(18, 'my_name', 2, TRUE),
|
|
(18, 'my-name', 3, FALSE),
|
|
(18, 'class', 4, FALSE),
|
|
|
|
-- Q19: How do you convert "123" to an integer?
|
|
(19, 'int("123")', 1, TRUE),
|
|
(19, 'integer("123")', 2, FALSE),
|
|
(19, 'str(123)', 3, FALSE),
|
|
(19, 'toInt("123")', 4, FALSE),
|
|
|
|
-- Q20: How many times does range(3) loop run?
|
|
(20, '2', 1, FALSE),
|
|
(20, '3', 2, TRUE),
|
|
(20, '4', 3, FALSE),
|
|
(20, '1', 4, FALSE);
|
|
|
|
-- ======================================================
|
|
-- Additional Practice Questions for new sub-courses
|
|
-- ======================================================
|
|
INSERT INTO questions (id, question_text, question_type, tips, status) VALUES
|
|
(21, 'What keyword is used to declare a variable in Dart?', 'MCQ', 'Dart uses var, final, or const', 'PUBLISHED'),
|
|
(22, 'Which widget is the root of every Flutter app?', 'MCQ', 'Think about the main() function', 'PUBLISHED'),
|
|
(23, 'What is a StatefulWidget?', 'MCQ', 'Consider mutable state', 'PUBLISHED'),
|
|
(24, 'What command creates a Docker container from an image?', 'MCQ', 'Think about docker run', 'PUBLISHED'),
|
|
(25, 'What file defines a Docker Compose application?', 'MCQ', 'It is a YAML file', 'PUBLISHED'),
|
|
(26, 'Which tool is used to create a new React Native project?', 'MCQ', 'Consider npx or expo', 'PUBLISHED')
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
INSERT INTO question_options (question_id, option_text, option_order, is_correct) VALUES
|
|
-- Q21: Dart variable declaration
|
|
(21, 'var', 1, TRUE),
|
|
(21, 'let', 2, FALSE),
|
|
(21, 'dim', 3, FALSE),
|
|
(21, 'define', 4, FALSE),
|
|
|
|
-- Q22: Root Flutter widget
|
|
(22, 'MaterialApp', 1, TRUE),
|
|
(22, 'Container', 2, FALSE),
|
|
(22, 'Scaffold', 3, FALSE),
|
|
(22, 'AppBar', 4, FALSE),
|
|
|
|
-- Q23: StatefulWidget
|
|
(23, 'A widget that can change its state during its lifetime', 1, TRUE),
|
|
(23, 'A widget that never changes', 2, FALSE),
|
|
(23, 'A widget for static content only', 3, FALSE),
|
|
(23, 'A widget that cannot have children', 4, FALSE),
|
|
|
|
-- Q24: Docker container creation
|
|
(24, 'docker run', 1, TRUE),
|
|
(24, 'docker create', 2, FALSE),
|
|
(24, 'docker start', 3, FALSE),
|
|
(24, 'docker build', 4, FALSE),
|
|
|
|
-- Q25: Docker Compose file
|
|
(25, 'docker-compose.yml', 1, TRUE),
|
|
(25, 'Dockerfile', 2, FALSE),
|
|
(25, 'docker.json', 3, FALSE),
|
|
(25, 'compose.xml', 4, FALSE),
|
|
|
|
-- Q26: React Native project creation
|
|
(26, 'npx react-native init', 1, TRUE),
|
|
(26, 'npm create react-native', 2, FALSE),
|
|
(26, 'react-native new', 3, FALSE),
|
|
(26, 'rn init', 4, FALSE);
|
|
|
|
-- ======================================================
|
|
-- Question Sets for new sub-courses
|
|
-- ======================================================
|
|
INSERT INTO question_sets (id, title, description, set_type, owner_type, owner_id, persona, status) VALUES
|
|
(5, 'Dart Basics Quiz', 'Test your Dart fundamentals', 'PRACTICE', 'SUB_COURSE', 18, 'beginner', 'PUBLISHED'),
|
|
(6, 'Flutter Widgets Assessment', 'Assess Flutter widget knowledge', 'PRACTICE', 'SUB_COURSE', 19, 'beginner', 'PUBLISHED'),
|
|
(7, 'State Management Quiz', 'Test state management concepts', 'PRACTICE', 'SUB_COURSE', 20, 'intermediate', 'DRAFT'),
|
|
(8, 'Docker Fundamentals Quiz', 'Test Docker basics', 'PRACTICE', 'SUB_COURSE', 25, 'beginner', 'PUBLISHED'),
|
|
(9, 'Docker Compose Assessment', 'Assess Docker Compose skills', 'PRACTICE', 'SUB_COURSE', 26, 'intermediate', 'PUBLISHED'),
|
|
(10, 'React Native Setup Quiz', 'Test React Native setup knowledge', 'PRACTICE', 'SUB_COURSE', 22, 'beginner', 'PUBLISHED')
|
|
ON CONFLICT (id) DO NOTHING;
|
|
|
|
-- Ensure every sub-course has at least one practice set
|
|
INSERT INTO question_sets (title, description, set_type, owner_type, owner_id, status)
|
|
SELECT
|
|
sc.title || ' Practice',
|
|
'Default practice set for ' || sc.title,
|
|
'PRACTICE',
|
|
'SUB_COURSE',
|
|
sc.id,
|
|
'DRAFT'
|
|
FROM sub_courses sc
|
|
WHERE NOT EXISTS (
|
|
SELECT 1
|
|
FROM question_sets qs
|
|
WHERE qs.owner_type = 'SUB_COURSE'
|
|
AND qs.owner_id = sc.id
|
|
AND qs.set_type = 'PRACTICE'
|
|
AND qs.status != 'ARCHIVED'
|
|
);
|
|
|
|
-- Ensure every sub-course has one initial assessment set
|
|
INSERT INTO question_sets (title, description, set_type, owner_type, owner_id, status)
|
|
SELECT
|
|
sc.title || ' Entry Assessment',
|
|
'Initial assessment used before learners start ' || sc.title,
|
|
'INITIAL_ASSESSMENT',
|
|
'SUB_COURSE',
|
|
sc.id,
|
|
'DRAFT'
|
|
FROM sub_courses sc
|
|
WHERE NOT EXISTS (
|
|
SELECT 1
|
|
FROM question_sets qs
|
|
WHERE qs.owner_type = 'SUB_COURSE'
|
|
AND qs.owner_id = sc.id
|
|
AND qs.set_type = 'INITIAL_ASSESSMENT'
|
|
AND qs.status != 'ARCHIVED'
|
|
);
|
|
|
|
-- Link questions to question sets
|
|
INSERT INTO question_set_items (set_id, question_id, display_order) VALUES
|
|
(5, 21, 1),
|
|
(6, 22, 1),
|
|
(7, 23, 1),
|
|
(8, 24, 1),
|
|
(9, 25, 1),
|
|
(10, 26, 1)
|
|
ON CONFLICT (set_id, question_id) DO NOTHING;
|
|
|
|
-- Link personas to question sets
|
|
INSERT INTO question_set_personas (question_set_id, user_id, display_order) VALUES
|
|
(5, 10, 1), (5, 11, 2),
|
|
(6, 10, 1), (6, 12, 2),
|
|
(8, 11, 1),
|
|
(10, 10, 1)
|
|
ON CONFLICT (question_set_id, user_id) DO NOTHING;
|
|
|
|
-- ======================================================
|
|
-- Sub-course Prerequisites
|
|
-- Defines the learning path / dependency graph
|
|
-- ======================================================
|
|
INSERT INTO sub_course_prerequisites (sub_course_id, prerequisite_sub_course_id) VALUES
|
|
-- Python course (IDs 1-5): linear progression
|
|
-- "Python Basics - Data Types" requires "Python Basics - Getting Started"
|
|
(2, 1),
|
|
-- "Python Intermediate - Functions" requires "Python Basics - Data Types"
|
|
(3, 2),
|
|
-- "Python Intermediate - Collections" requires "Python Intermediate - Functions"
|
|
(4, 3),
|
|
-- "Python Advanced - Best Practices" requires "Python Intermediate - Collections"
|
|
(5, 4),
|
|
|
|
-- JavaScript course (IDs 6-7): linear
|
|
-- "DOM Manipulation Basics" requires "JavaScript Fundamentals"
|
|
(7, 6),
|
|
|
|
-- Java course (IDs 8-9): linear
|
|
-- "Spring Framework Intro" requires "Java Core Concepts"
|
|
(9, 8),
|
|
|
|
-- Data Science course (IDs 10-11): linear
|
|
-- "Advanced Data Analysis" requires "Data Analysis Fundamentals"
|
|
(11, 10),
|
|
|
|
-- ML course (IDs 12-13): linear
|
|
-- "ML Algorithms" requires "ML Basics"
|
|
(13, 12),
|
|
|
|
-- Full Stack course (IDs 14-15): linear
|
|
-- "Backend Development" requires "Frontend Fundamentals"
|
|
(15, 14),
|
|
|
|
-- React course (IDs 16-17): linear
|
|
-- "React Advanced Patterns" requires "React Basics"
|
|
(17, 16),
|
|
|
|
-- Flutter course (IDs 18-21): structured path
|
|
-- "Flutter UI Widgets" requires "Dart Language Basics"
|
|
(19, 18),
|
|
-- "State Management" requires "Flutter UI Widgets"
|
|
(20, 19),
|
|
-- "Flutter Networking & APIs" requires "State Management"
|
|
(21, 20),
|
|
|
|
-- React Native course (IDs 22-24): linear
|
|
-- "Navigation & Routing" requires "React Native Setup"
|
|
(23, 22),
|
|
-- "Native Modules" requires "Navigation & Routing"
|
|
(24, 23),
|
|
|
|
-- Docker & Kubernetes course (IDs 25-27): structured
|
|
-- "Docker Compose" requires "Docker Fundamentals"
|
|
(26, 25),
|
|
-- "Kubernetes Basics" requires "Docker Compose"
|
|
(27, 26),
|
|
|
|
-- CI/CD course (IDs 28-29): linear
|
|
-- "GitHub Actions" requires "Git Workflows"
|
|
(29, 28),
|
|
|
|
-- Cybersecurity course (IDs 30-31): linear
|
|
-- "Penetration Testing" requires "Network Security Basics"
|
|
(31, 30)
|
|
ON CONFLICT (sub_course_id, prerequisite_sub_course_id) DO NOTHING;
|
|
|
|
-- ======================================================
|
|
-- Completion-driven progress seed (auto-aggregate model)
|
|
-- Seed video/practice completion records, then derive sub-course progress
|
|
-- ======================================================
|
|
|
|
-- Video completions
|
|
INSERT INTO user_sub_course_video_progress (user_id, sub_course_id, video_id, completed_at, updated_at)
|
|
SELECT 10, v.sub_course_id, v.id, CURRENT_TIMESTAMP - INTERVAL '20 days', CURRENT_TIMESTAMP - INTERVAL '20 days'
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id IN (1, 2, 18)
|
|
AND v.status = 'PUBLISHED'
|
|
ON CONFLICT (user_id, video_id) DO NOTHING;
|
|
|
|
INSERT INTO user_sub_course_video_progress (user_id, sub_course_id, video_id, completed_at, updated_at)
|
|
SELECT 10, v.sub_course_id, v.id, CURRENT_TIMESTAMP - INTERVAL '8 days', CURRENT_TIMESTAMP - INTERVAL '8 days'
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id = 19
|
|
AND v.status = 'PUBLISHED'
|
|
AND v.display_order = 1
|
|
ON CONFLICT (user_id, video_id) DO NOTHING;
|
|
|
|
INSERT INTO user_sub_course_video_progress (user_id, sub_course_id, video_id, completed_at, updated_at)
|
|
SELECT 11, v.sub_course_id, v.id, CURRENT_TIMESTAMP - INTERVAL '25 days', CURRENT_TIMESTAMP - INTERVAL '25 days'
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id IN (1, 2, 25)
|
|
AND v.status = 'PUBLISHED'
|
|
ON CONFLICT (user_id, video_id) DO NOTHING;
|
|
|
|
INSERT INTO user_sub_course_video_progress (user_id, sub_course_id, video_id, completed_at, updated_at)
|
|
SELECT 11, v.sub_course_id, v.id, CURRENT_TIMESTAMP - INTERVAL '3 days', CURRENT_TIMESTAMP - INTERVAL '3 days'
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id = 26
|
|
AND v.status = 'PUBLISHED'
|
|
ON CONFLICT (user_id, video_id) DO NOTHING;
|
|
|
|
INSERT INTO user_sub_course_video_progress (user_id, sub_course_id, video_id, completed_at, updated_at)
|
|
SELECT 12, v.sub_course_id, v.id, CURRENT_TIMESTAMP - INTERVAL '7 days', CURRENT_TIMESTAMP - INTERVAL '7 days'
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id = 22
|
|
AND v.status = 'PUBLISHED'
|
|
ON CONFLICT (user_id, video_id) DO NOTHING;
|
|
|
|
INSERT INTO user_sub_course_video_progress (user_id, sub_course_id, video_id, completed_at, updated_at)
|
|
SELECT 12, v.sub_course_id, v.id, CURRENT_TIMESTAMP - INTERVAL '3 days', CURRENT_TIMESTAMP - INTERVAL '3 days'
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id = 18
|
|
AND v.status = 'PUBLISHED'
|
|
AND v.display_order = 1
|
|
ON CONFLICT (user_id, video_id) DO NOTHING;
|
|
|
|
-- Practice completions
|
|
INSERT INTO user_practice_progress (user_id, sub_course_id, question_set_id, completed_at, updated_at)
|
|
SELECT 10, qs.owner_id::BIGINT, qs.id, CURRENT_TIMESTAMP - INTERVAL '18 days', CURRENT_TIMESTAMP - INTERVAL '18 days'
|
|
FROM question_sets qs
|
|
WHERE qs.owner_type = 'SUB_COURSE'
|
|
AND qs.set_type = 'PRACTICE'
|
|
AND qs.status = 'PUBLISHED'
|
|
AND qs.owner_id IN (1, 2, 18)
|
|
ON CONFLICT (user_id, question_set_id) DO NOTHING;
|
|
|
|
INSERT INTO user_practice_progress (user_id, sub_course_id, question_set_id, completed_at, updated_at)
|
|
SELECT 11, qs.owner_id::BIGINT, qs.id, CURRENT_TIMESTAMP - INTERVAL '10 days', CURRENT_TIMESTAMP - INTERVAL '10 days'
|
|
FROM question_sets qs
|
|
WHERE qs.owner_type = 'SUB_COURSE'
|
|
AND qs.set_type = 'PRACTICE'
|
|
AND qs.status = 'PUBLISHED'
|
|
AND qs.owner_id IN (1, 2, 25)
|
|
ON CONFLICT (user_id, question_set_id) DO NOTHING;
|
|
|
|
INSERT INTO user_practice_progress (user_id, sub_course_id, question_set_id, completed_at, updated_at)
|
|
SELECT 12, qs.owner_id::BIGINT, qs.id, CURRENT_TIMESTAMP - INTERVAL '7 days', CURRENT_TIMESTAMP - INTERVAL '7 days'
|
|
FROM question_sets qs
|
|
WHERE qs.owner_type = 'SUB_COURSE'
|
|
AND qs.set_type = 'PRACTICE'
|
|
AND qs.status = 'PUBLISHED'
|
|
AND qs.owner_id IN (22)
|
|
ON CONFLICT (user_id, question_set_id) DO NOTHING;
|
|
|
|
-- Derive sub-course progress from completion tables (same model as runtime auto-aggregate)
|
|
WITH target_pairs AS (
|
|
SELECT DISTINCT user_id, sub_course_id
|
|
FROM user_sub_course_video_progress
|
|
WHERE user_id IN (10, 11, 12)
|
|
UNION
|
|
SELECT DISTINCT user_id, sub_course_id
|
|
FROM user_practice_progress
|
|
WHERE user_id IN (10, 11, 12)
|
|
),
|
|
stats AS (
|
|
SELECT
|
|
tp.user_id,
|
|
tp.sub_course_id,
|
|
(SELECT COUNT(*)::INT
|
|
FROM sub_course_videos v
|
|
WHERE v.sub_course_id = tp.sub_course_id
|
|
AND v.status = 'PUBLISHED')
|
|
+
|
|
(SELECT COUNT(*)::INT
|
|
FROM question_sets qs
|
|
WHERE qs.owner_type = 'SUB_COURSE'
|
|
AND qs.owner_id = tp.sub_course_id
|
|
AND qs.set_type = 'PRACTICE'
|
|
AND qs.status = 'PUBLISHED') AS total_items,
|
|
(SELECT COUNT(*)::INT
|
|
FROM user_sub_course_video_progress uv
|
|
JOIN sub_course_videos v ON v.id = uv.video_id
|
|
WHERE uv.user_id = tp.user_id
|
|
AND uv.sub_course_id = tp.sub_course_id
|
|
AND uv.completed_at IS NOT NULL
|
|
AND v.status = 'PUBLISHED')
|
|
+
|
|
(SELECT COUNT(*)::INT
|
|
FROM user_practice_progress up
|
|
JOIN question_sets qs ON qs.id = up.question_set_id
|
|
WHERE up.user_id = tp.user_id
|
|
AND up.sub_course_id = tp.sub_course_id
|
|
AND up.completed_at IS NOT NULL
|
|
AND qs.owner_type = 'SUB_COURSE'
|
|
AND qs.owner_id = tp.sub_course_id
|
|
AND qs.set_type = 'PRACTICE'
|
|
AND qs.status = 'PUBLISHED') AS completed_items
|
|
FROM target_pairs tp
|
|
)
|
|
INSERT INTO user_sub_course_progress (user_id, sub_course_id, status, progress_percentage, started_at, completed_at, updated_at)
|
|
SELECT
|
|
user_id,
|
|
sub_course_id,
|
|
CASE
|
|
WHEN total_items > 0 AND completed_items >= total_items THEN 'COMPLETED'
|
|
WHEN completed_items > 0 THEN 'IN_PROGRESS'
|
|
ELSE 'NOT_STARTED'
|
|
END AS status,
|
|
CASE
|
|
WHEN total_items = 0 THEN 0
|
|
ELSE ROUND((completed_items::NUMERIC * 100.0) / total_items::NUMERIC)::SMALLINT
|
|
END AS progress_percentage,
|
|
CASE WHEN completed_items > 0 THEN CURRENT_TIMESTAMP - INTERVAL '10 days' ELSE NULL END AS started_at,
|
|
CASE WHEN total_items > 0 AND completed_items >= total_items THEN CURRENT_TIMESTAMP - INTERVAL '3 days' ELSE NULL END AS completed_at,
|
|
CURRENT_TIMESTAMP AS updated_at
|
|
FROM stats
|
|
ON CONFLICT (user_id, sub_course_id) DO UPDATE SET
|
|
status = EXCLUDED.status,
|
|
progress_percentage = EXCLUDED.progress_percentage,
|
|
started_at = COALESCE(user_sub_course_progress.started_at, EXCLUDED.started_at),
|
|
completed_at = EXCLUDED.completed_at,
|
|
updated_at = EXCLUDED.updated_at;
|
|
|
|
-- ======================================================
|
|
-- Reset sequences to avoid ID conflicts after seeding
|
|
-- ======================================================
|
|
SELECT setval(pg_get_serial_sequence('course_categories', 'id'), COALESCE((SELECT MAX(id) FROM course_categories), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('courses', 'id'), COALESCE((SELECT MAX(id) FROM courses), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('sub_courses', 'id'), COALESCE((SELECT MAX(id) FROM sub_courses), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('sub_course_videos', 'id'), COALESCE((SELECT MAX(id) FROM sub_course_videos), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('questions', 'id'), COALESCE((SELECT MAX(id) FROM questions), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('question_options', 'id'), COALESCE((SELECT MAX(id) FROM question_options), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('question_sets', 'id'), COALESCE((SELECT MAX(id) FROM question_sets), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('question_set_items', 'id'), COALESCE((SELECT MAX(id) FROM question_set_items), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('question_set_personas', 'id'), COALESCE((SELECT MAX(id) FROM question_set_personas), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('sub_course_prerequisites', 'id'), COALESCE((SELECT MAX(id) FROM sub_course_prerequisites), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('user_sub_course_progress', 'id'), COALESCE((SELECT MAX(id) FROM user_sub_course_progress), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('user_sub_course_video_progress', 'id'), COALESCE((SELECT MAX(id) FROM user_sub_course_video_progress), 1), true);
|
|
SELECT setval(pg_get_serial_sequence('user_practice_progress', 'id'), COALESCE((SELECT MAX(id) FROM user_practice_progress), 1), true);
|