removed all unnecessary data seed
This commit is contained in:
parent
7613eb583a
commit
894e18bcae
|
|
@ -136,190 +136,6 @@ VALUES
|
|||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Ensure seeded admin has full panel permissions in legacy team_members.permissions JSON.
|
||||
-- RBAC permissions are managed separately, but this keeps seed behavior consistent.
|
||||
UPDATE team_members
|
||||
SET permissions = '["*"]'::jsonb
|
||||
WHERE id = 2 OR email = 'admin@yimaru.com';
|
||||
|
||||
-- ======================================================
|
||||
-- Global Settings (LMS)
|
||||
-- ======================================================
|
||||
INSERT INTO global_settings (key, value)
|
||||
VALUES
|
||||
('platform_name', 'Yimaru LMS'),
|
||||
('default_language', 'en'),
|
||||
('allow_self_signup', 'true'),
|
||||
('otp_expiry_minutes', '5'),
|
||||
('certificate_enabled', 'true'),
|
||||
('max_courses_per_instructor', '50')
|
||||
ON CONFLICT (key) DO NOTHING;
|
||||
-- ======================================================
|
||||
|
||||
-- ======================================================
|
||||
-- Questions - Level A2 (EASY)
|
||||
-- ======================================================
|
||||
|
||||
INSERT INTO questions (id, question_text, question_type, difficulty_level, points, status)
|
||||
VALUES
|
||||
(1, 'What would you say to greet someone before lunchtime?', 'MCQ', 'EASY', 1, 'PUBLISHED'),
|
||||
(2, 'Which question is correct to ask about your routine?', 'MCQ', 'EASY', 1, 'PUBLISHED'),
|
||||
(3, 'She ___ like pizza.', 'MCQ', 'EASY', 1, 'PUBLISHED'),
|
||||
(4, 'I usually go to school and start class ____ eight o''clock.', 'MCQ', 'EASY', 1, 'PUBLISHED'),
|
||||
(5, 'Someone says, "Here is the book you asked for." What is the best response?', 'MCQ', 'EASY', 1, 'PUBLISHED')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO question_options (question_id, option_text, option_order, is_correct)
|
||||
VALUES
|
||||
-- Q1
|
||||
(1, 'Good morning.', 1, TRUE),
|
||||
(1, 'How do you do?', 2, FALSE),
|
||||
(1, 'Good afternoon.', 3, FALSE),
|
||||
(1, 'Goodbye.', 4, FALSE),
|
||||
|
||||
-- Q2
|
||||
(2, 'What time you wake up?', 1, FALSE),
|
||||
(2, 'What time do you wake up?', 2, TRUE),
|
||||
(2, 'What time are you wake up?', 3, FALSE),
|
||||
(2, 'What time waking you?', 4, FALSE),
|
||||
|
||||
-- Q3
|
||||
(3, 'do not', 1, FALSE),
|
||||
(3, 'not', 2, FALSE),
|
||||
(3, 'is not', 3, FALSE),
|
||||
(3, 'does not', 4, TRUE),
|
||||
|
||||
-- Q4
|
||||
(4, 'about', 1, FALSE),
|
||||
(4, 'on', 2, FALSE),
|
||||
(4, 'at', 3, TRUE),
|
||||
(4, 'in', 4, FALSE),
|
||||
|
||||
-- Q5
|
||||
(5, 'Never mind.', 1, FALSE),
|
||||
(5, 'Really?', 2, FALSE),
|
||||
(5, 'What a pity!', 3, FALSE),
|
||||
(5, 'Thank you.', 4, TRUE);
|
||||
|
||||
-- ======================================================
|
||||
-- Questions - Level B1 (MEDIUM)
|
||||
-- ======================================================
|
||||
|
||||
INSERT INTO questions (id, question_text, question_type, difficulty_level, points, status)
|
||||
VALUES
|
||||
(6, 'How do you introduce your friend to another person?', 'MCQ', 'MEDIUM', 1, 'PUBLISHED'),
|
||||
(7, 'How would you ask for the price of an item in a shop?', 'MCQ', 'MEDIUM', 1, 'PUBLISHED'),
|
||||
(8, 'Which sentence correctly gives simple directions?', 'MCQ', 'MEDIUM', 1, 'PUBLISHED'),
|
||||
(9, 'The watch shows 10:50, but the real time is 10:45. What can you say?', 'MCQ', 'MEDIUM', 1, 'PUBLISHED'),
|
||||
(10, 'Which instruction is correct when giving directions?', 'MCQ', 'MEDIUM', 1, 'PUBLISHED')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO question_options (question_id, option_text, option_order, is_correct)
|
||||
VALUES
|
||||
-- Q6
|
||||
(6, 'Hello, my name is Samson.', 1, FALSE),
|
||||
(6, 'Good morning. Nice to meet you.', 2, FALSE),
|
||||
(6, 'Let me introduce myself to my friend.', 3, FALSE),
|
||||
(6, 'This is my friend, Samson.', 4, TRUE),
|
||||
|
||||
-- Q7
|
||||
(7, 'How many are these?', 1, FALSE),
|
||||
(7, 'What is this?', 2, FALSE),
|
||||
(7, 'How much is this?', 3, TRUE),
|
||||
(7, 'Where is the nearest shop?', 4, FALSE),
|
||||
|
||||
-- Q8
|
||||
(8, 'Thank you very much for asking.', 1, FALSE),
|
||||
(8, 'Turn left and walk two blocks.', 2, TRUE),
|
||||
(8, 'Why don''t you eat out.', 3, FALSE),
|
||||
(8, 'Take the bus to the park.', 4, FALSE),
|
||||
|
||||
-- Q9
|
||||
(9, 'My watch is slow.', 1, TRUE),
|
||||
(9, 'My watch is late.', 2, FALSE),
|
||||
(9, 'My watch is fast.', 3, FALSE),
|
||||
(9, 'My watch is early.', 4, FALSE),
|
||||
|
||||
-- Q10
|
||||
(10, 'Turn left.', 1, TRUE),
|
||||
(10, 'Turn on left.', 2, FALSE),
|
||||
(10, 'Turn left side.', 3, FALSE),
|
||||
(10, 'Turn to straight.', 4, FALSE);
|
||||
|
||||
-- ======================================================
|
||||
-- Questions - Level B2 (HARD)
|
||||
-- ======================================================
|
||||
|
||||
INSERT INTO questions (id, question_text, question_type, difficulty_level, points, status)
|
||||
VALUES
|
||||
(11, 'What is the most polite way to ask to speak to someone on the phone?', 'MCQ', 'HARD', 1, 'PUBLISHED'),
|
||||
(12, 'How do you correctly state the age of a person who is 30 years old?', 'MCQ', 'HARD', 1, 'PUBLISHED'),
|
||||
(13, 'When asking for help with a new Yimaru App feature, which option is most appropriate?', 'MCQ', 'HARD', 1, 'PUBLISHED'),
|
||||
(14, 'Which word has the unvoiced "th" sound?', 'MCQ', 'HARD', 1, 'PUBLISHED'),
|
||||
(15, 'Which sentence sounds like a warning, not friendly advice?', 'MCQ', 'HARD', 1, 'PUBLISHED'),
|
||||
(16, 'What does this sentence mean? "I will definitely be there on time."', 'MCQ', 'HARD', 1, 'PUBLISHED')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO question_options (question_id, option_text, option_order, is_correct)
|
||||
VALUES
|
||||
-- Q11
|
||||
(11, 'May I speak to Mr. Tesfaye, please?', 1, TRUE),
|
||||
(11, 'Can I talk to Mr. Tesfaye?', 2, FALSE),
|
||||
(11, 'Is Mr. Tesfaye there?', 3, FALSE),
|
||||
(11, 'I want to talk to Mr. Tesfaye.', 4, FALSE),
|
||||
|
||||
-- Q12
|
||||
(12, 'He is thirty years.', 1, FALSE),
|
||||
(12, 'He has thirty years.', 2, FALSE),
|
||||
(12, 'He has thirty years old.', 3, FALSE),
|
||||
(12, 'He is thirty.', 4, TRUE),
|
||||
|
||||
-- Q13
|
||||
(13, 'Are you familiar with how this feature works?', 1, FALSE),
|
||||
(13, 'Could you walk me through how this feature works?', 2, TRUE),
|
||||
(13, 'I believe I understand how this feature works.', 3, FALSE),
|
||||
(13, 'I''ve tried similar features before.', 4, FALSE),
|
||||
|
||||
-- Q14
|
||||
(14, 'That', 1, FALSE),
|
||||
(14, 'They', 2, FALSE),
|
||||
(14, 'These', 3, FALSE),
|
||||
(14, 'Three', 4, TRUE),
|
||||
|
||||
-- Q15
|
||||
(15, 'You might want to plan your time better.', 1, FALSE),
|
||||
(15, 'If I were you, I''d start earlier.', 2, FALSE),
|
||||
(15, 'You''d better meet the deadline this time.', 3, TRUE),
|
||||
(15, 'Why don''t you try using a planner?', 4, FALSE),
|
||||
|
||||
-- Q16
|
||||
(16, 'The speaker is unsure about arriving.', 1, FALSE),
|
||||
(16, 'The speaker is promising to arrive on time.', 2, TRUE),
|
||||
(16, 'The speaker might arrive late.', 3, FALSE),
|
||||
(16, 'The speaker has already arrived.', 4, FALSE);
|
||||
|
||||
-- ======================================================
|
||||
-- Initial Assessment Question Set
|
||||
-- ======================================================
|
||||
|
||||
INSERT INTO question_sets (id, title, description, set_type, owner_type, status)
|
||||
VALUES
|
||||
(1, 'Initial Assessment', 'Default initial assessment for new users', 'INITIAL_ASSESSMENT', 'STANDALONE', 'PUBLISHED')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO question_set_items (set_id, question_id, display_order)
|
||||
VALUES
|
||||
(1, 1, 1), (1, 2, 2), (1, 3, 3), (1, 4, 4), (1, 5, 5),
|
||||
(1, 6, 6), (1, 7, 7), (1, 8, 8), (1, 9, 9), (1, 10, 10),
|
||||
(1, 11, 11), (1, 12, 12), (1, 13, 13), (1, 14, 14), (1, 15, 15), (1, 16, 16)
|
||||
ON CONFLICT (set_id, question_id) DO NOTHING;
|
||||
|
||||
-- ======================================================
|
||||
-- Course Management seed data removed intentionally.
|
||||
-- Course/category/sub-course/video/practice/question-set fixtures
|
||||
-- are no longer seeded from this baseline script.
|
||||
-- ======================================================
|
||||
|
||||
-- ======================================================
|
||||
-- Team Members / Admin Panel Users (login via /api/v1/team/login)
|
||||
-- Credentials: email + password@123
|
||||
|
|
@ -471,3 +287,8 @@ VALUES
|
|||
CURRENT_TIMESTAMP
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Legacy team_members row may pre-exist; align admin permissions with seed expectations.
|
||||
UPDATE team_members
|
||||
SET permissions = '["*"]'::jsonb
|
||||
WHERE id = 2 OR email = 'admin@yimaru.com';
|
||||
|
|
|
|||
|
|
@ -1,108 +1,25 @@
|
|||
-- ======================================================
|
||||
-- Reset sequences for LMS tables (PostgreSQL)
|
||||
-- ======================================================
|
||||
-- Reset sequences for tables touched by login-only seed (PostgreSQL)
|
||||
|
||||
-- users.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('users', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM users), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- questions.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('questions', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM questions), 1),
|
||||
pg_get_serial_sequence('team_members', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM team_members), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- question_options.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('question_options', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM question_options), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- question_short_answers.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('question_short_answers', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM question_short_answers), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- question_sets.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('question_sets', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM question_sets), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- question_set_items.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('question_set_items', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM question_set_items), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- refresh_tokens.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('refresh_tokens', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM refresh_tokens), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- otps.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('otps', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM otps), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- notifications.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('notifications', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM notifications), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- reported_issues.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('reported_issues', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM reported_issues), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- course_categories.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('course_categories', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM course_categories), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- courses.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('courses', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM courses), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- sub_courses.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('sub_courses', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM sub_courses), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- sub_course_videos.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('sub_course_videos', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM sub_course_videos), 1),
|
||||
true
|
||||
);
|
||||
|
||||
-- question_set_personas.id (BIGSERIAL)
|
||||
SELECT setval(
|
||||
pg_get_serial_sequence('question_set_personas', 'id'),
|
||||
COALESCE((SELECT MAX(id) FROM question_set_personas), 1),
|
||||
true
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,31 +1 @@
|
|||
INSERT INTO activity_logs (actor_id, actor_role, action, resource_type, resource_id, message, metadata, ip_address, user_agent, created_at) VALUES
|
||||
(1, 'SUPER_ADMIN', 'CATEGORY_CREATED', 'CATEGORY', 1, 'Created course category: Mathematics', '{"name": "Mathematics"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '30 days'),
|
||||
(1, 'SUPER_ADMIN', 'CATEGORY_CREATED', 'CATEGORY', 2, 'Created course category: Science', '{"name": "Science"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '29 days'),
|
||||
(1, 'SUPER_ADMIN', 'CATEGORY_CREATED', 'CATEGORY', 3, 'Created course category: Language Arts', '{"name": "Language Arts"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '28 days'),
|
||||
(1, 'SUPER_ADMIN', 'COURSE_CREATED', 'COURSE', 1, 'Created course: Algebra Fundamentals', '{"title": "Algebra Fundamentals", "category_id": 1}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '27 days'),
|
||||
(1, 'SUPER_ADMIN', 'COURSE_CREATED', 'COURSE', 2, 'Created course: Biology 101', '{"title": "Biology 101", "category_id": 2}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '26 days'),
|
||||
(2, 'ADMIN', 'COURSE_CREATED', 'COURSE', 3, 'Created course: English Grammar', '{"title": "English Grammar", "category_id": 3}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '25 days'),
|
||||
(1, 'SUPER_ADMIN', 'SUB_COURSE_CREATED', 'SUB_COURSE', 1, 'Created sub-course: Linear Equations', '{"title": "Linear Equations", "course_id": 1, "level": "BEGINNER"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '24 days'),
|
||||
(1, 'SUPER_ADMIN', 'SUB_COURSE_CREATED', 'SUB_COURSE', 2, 'Created sub-course: Quadratic Equations', '{"title": "Quadratic Equations", "course_id": 1, "level": "INTERMEDIATE"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '23 days'),
|
||||
(2, 'ADMIN', 'SUB_COURSE_CREATED', 'SUB_COURSE', 3, 'Created sub-course: Cell Biology', '{"title": "Cell Biology", "course_id": 2, "level": "BEGINNER"}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '22 days'),
|
||||
(1, 'SUPER_ADMIN', 'VIDEO_CREATED', 'VIDEO', 1, 'Created video: Introduction to Algebra', '{"title": "Introduction to Algebra", "sub_course_id": 1}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '21 days'),
|
||||
(1, 'SUPER_ADMIN', 'VIDEO_UPLOADED', 'VIDEO', 1, 'Uploaded video to Vimeo: Introduction to Algebra', '{"title": "Introduction to Algebra", "vimeo_id": "987654321", "file_size": 52428800}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '21 days'),
|
||||
(1, 'SUPER_ADMIN', 'VIDEO_PUBLISHED', 'VIDEO', 1, 'Published video: Introduction to Algebra', '{"title": "Introduction to Algebra"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '20 days'),
|
||||
(2, 'ADMIN', 'VIDEO_CREATED', 'VIDEO', 2, 'Created video: Solving for X', '{"title": "Solving for X", "sub_course_id": 1}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '19 days'),
|
||||
(2, 'ADMIN', 'VIDEO_UPLOADED', 'VIDEO', 2, 'Uploaded video to Vimeo: Solving for X', '{"title": "Solving for X", "vimeo_id": "987654322", "file_size": 41943040}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '19 days'),
|
||||
(1, 'SUPER_ADMIN', 'COURSE_UPDATED', 'COURSE', 1, 'Updated course: Algebra Fundamentals', '{"title": "Algebra Fundamentals", "changed_fields": ["description", "thumbnail"]}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '18 days'),
|
||||
(1, 'SUPER_ADMIN', 'CATEGORY_UPDATED', 'CATEGORY', 1, 'Updated course category: Mathematics & Statistics', '{"name": "Mathematics & Statistics"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '17 days'),
|
||||
(2, 'ADMIN', 'VIDEO_CREATED', 'VIDEO', 3, 'Created video: Cell Structure Overview', '{"title": "Cell Structure Overview", "sub_course_id": 3}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '15 days'),
|
||||
(2, 'ADMIN', 'VIDEO_UPLOADED', 'VIDEO', 3, 'Uploaded video to Vimeo: Cell Structure Overview', '{"title": "Cell Structure Overview", "vimeo_id": "987654323", "file_size": 73400320}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '15 days'),
|
||||
(2, 'ADMIN', 'VIDEO_PUBLISHED', 'VIDEO', 2, 'Published video: Solving for X', '{"title": "Solving for X"}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '14 days'),
|
||||
(1, 'SUPER_ADMIN', 'SUB_COURSE_UPDATED', 'SUB_COURSE', 2, 'Updated sub-course: Quadratic Equations', '{"title": "Quadratic Equations", "changed_fields": ["description"]}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '12 days'),
|
||||
(2, 'ADMIN', 'VIDEO_UPDATED', 'VIDEO', 3, 'Updated video: Cell Structure Overview', '{"title": "Cell Structure Overview", "changed_fields": ["thumbnail", "resolution"]}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '10 days'),
|
||||
(2, 'ADMIN', 'VIDEO_PUBLISHED', 'VIDEO', 3, 'Published video: Cell Structure Overview', '{"title": "Cell Structure Overview"}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '9 days'),
|
||||
(1, 'SUPER_ADMIN', 'VIDEO_ARCHIVED', 'VIDEO', 4, 'Archived video ID: 4', '{"id": 4}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '7 days'),
|
||||
(1, 'SUPER_ADMIN', 'SETTINGS_UPDATED', 'SETTINGS', NULL, 'Updated global settings', '{"keys": ["site_name", "maintenance_mode"]}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '5 days'),
|
||||
(1, 'SUPER_ADMIN', 'TEAM_MEMBER_CREATED', 'TEAM_MEMBER', 3, 'Created team member: John Doe', '{"name": "John Doe", "role": "instructor"}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '4 days'),
|
||||
(1, 'SUPER_ADMIN', 'COURSE_CREATED', 'COURSE', 4, 'Created course: Advanced Physics', '{"title": "Advanced Physics", "category_id": 2}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '3 days'),
|
||||
(2, 'ADMIN', 'CATEGORY_DELETED', 'CATEGORY', 5, 'Deleted category ID: 5', '{"id": 5}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '2 days'),
|
||||
(1, 'SUPER_ADMIN', 'SUB_COURSE_DELETED', 'SUB_COURSE', 6, 'Deleted sub-course ID: 6', '{"id": 6}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '1 day'),
|
||||
(2, 'ADMIN', 'VIDEO_DELETED', 'VIDEO', 5, 'Deleted video ID: 5', '{"id": 5}', '10.0.0.55', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36', now() - interval '6 hours'),
|
||||
(1, 'SUPER_ADMIN', 'TEAM_MEMBER_UPDATED', 'TEAM_MEMBER', 3, 'Updated team member: John Doe', '{"name": "John Doe", "changed_fields": ["role"]}', '192.168.1.10', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', now() - interval '2 hours');
|
||||
-- Intentionally empty: no demo activity log seed (login-only seed in 001).
|
||||
|
|
|
|||
|
|
@ -1,14 +1 @@
|
|||
INSERT INTO reported_issues (user_id, user_role, subject, description, issue_type, status, metadata, created_at, updated_at) VALUES
|
||||
(10, 'USER', 'Video not loading on mobile', 'When I try to play the Algebra Fundamentals introduction video on my phone, it shows a blank screen with a spinner that never stops.', 'video', 'pending', '{"course": "Algebra Fundamentals", "device": "iPhone 14", "browser": "Safari 17"}', now() - interval '14 days', now() - interval '14 days'),
|
||||
(10, 'USER', 'Payment confirmation not received', 'I subscribed to the premium plan yesterday and the money was deducted from my account, but I have not received any confirmation email or SMS.', 'payment', 'in_progress', '{"plan": "Premium", "amount": 500, "payment_method": "telebirr"}', now() - interval '10 days', now() - interval '8 days'),
|
||||
(10, 'USER', 'Cannot change profile picture', 'I am trying to upload a new profile picture but the upload button does not respond when I click it.', 'account', 'resolved', '{"browser": "Chrome 120", "file_type": "jpg", "file_size_kb": 2048}', now() - interval '20 days', now() - interval '15 days'),
|
||||
(10, 'USER', 'Add dark mode support', 'It would be great if the platform had a dark mode option. Studying at night with the bright white background is hard on the eyes.', 'feature_request', 'pending', '{"platform": "web"}', now() - interval '7 days', now() - interval '7 days'),
|
||||
(10, 'USER', 'Quiz results not saving', 'I completed the Biology 101 quiz but when I go back to check my results, it shows as incomplete.', 'bug', 'in_progress', '{"course": "Biology 101", "quiz_id": 5, "attempts": 3}', now() - interval '5 days', now() - interval '3 days'),
|
||||
(12, 'SUPPORT', 'Course content displays incorrectly on tablets', 'Multiple users have reported that course text overlaps with images on tablet devices in landscape mode.', 'content', 'pending', '{"affected_devices": ["iPad Air", "Samsung Galaxy Tab S9"], "orientation": "landscape"}', now() - interval '12 days', now() - interval '12 days'),
|
||||
(12, 'SUPPORT', 'Login fails after password reset', 'After resetting my password through the forgot password flow, the new password is not accepted for login.', 'login', 'resolved', '{"browser": "Firefox 121", "reset_method": "email"}', now() - interval '25 days', now() - interval '18 days'),
|
||||
(12, 'SUPPORT', 'Slow page load times', 'The course listing page takes over 10 seconds to load, especially when filtering by category.', 'performance', 'in_progress', '{"page": "/courses", "avg_load_time_ms": 12500, "filter": "category=Science"}', now() - interval '9 days', now() - interval '6 days'),
|
||||
(10, 'USER', 'Subscription auto-renewal not working', 'My monthly subscription expired even though I had auto-renewal enabled. I had to manually resubscribe.', 'subscription', 'rejected', '{"plan": "Monthly Basic", "expected_renewal": "2026-01-15"}', now() - interval '30 days', now() - interval '22 days'),
|
||||
(12, 'SUPPORT', 'Screen reader cannot read course navigation', 'The course sidebar navigation is not accessible with screen readers. ARIA labels are missing on several interactive elements.', 'accessibility', 'pending', '{"screen_reader": "NVDA", "browser": "Chrome 120", "affected_elements": ["sidebar nav", "progress bar", "video controls"]}', now() - interval '4 days', now() - interval '4 days'),
|
||||
(10, 'USER', 'Certificate download gives 404 error', 'After completing the English Grammar course, clicking the download certificate button returns a page not found error.', 'course', 'pending', '{"course": "English Grammar", "completion_date": "2026-01-28"}', now() - interval '2 days', now() - interval '2 days'),
|
||||
(10, 'USER', 'Cannot access course after subscription renewal', 'I renewed my subscription but I still cannot access premium courses. It says my subscription is inactive.', 'subscription', 'in_progress', '{"plan": "Premium Annual", "renewal_date": "2026-02-01"}', now() - interval '1 day', now() - interval '12 hours')
|
||||
ON CONFLICT DO NOTHING;
|
||||
-- Intentionally empty: no demo issue-report seed (login-only seed in 001).
|
||||
|
|
|
|||
|
|
@ -1,40 +1 @@
|
|||
INSERT INTO notifications (
|
||||
id, user_id, receiver_type, type, level, channel, title, message, payload, is_read, created_at
|
||||
) VALUES
|
||||
-- Learner notifications (receiver_type=user, user_id=10)
|
||||
(1001, 10, 'user', 'course_created', 'info', 'in_app', 'New Course Available', 'A new course "Algebra Fundamentals" has been added. Check it out!', '{"course_title": "Algebra Fundamentals", "category": "Mathematics"}', false, now() - interval '30 days'),
|
||||
(1002, 10, 'user', 'course_created', 'info', 'in_app', 'New Course Available', 'A new course "English Grammar 101" has been added. Check it out!', '{"course_title": "English Grammar 101", "category": "Language"}', false, now() - interval '25 days'),
|
||||
(1003, 10, 'user', 'sub_course_created', 'info', 'in_app', 'New Content Available', 'A new sub-course "Linear Equations" has been added.', '{"sub_course_title": "Linear Equations", "course": "Algebra Fundamentals"}', false, now() - interval '24 days'),
|
||||
(1004, 10, 'user', 'video_added', 'info', 'in_app', 'New Video Available', 'A new video "Introduction to Variables" has been added.', '{"video_title": "Introduction to Variables", "sub_course": "Linear Equations"}', false, now() - interval '23 days'),
|
||||
(1005, 10, 'user', 'payment_verified', 'success', 'in_app', 'Payment Successful', 'Your payment has been verified successfully. Your subscription is now active.', '{"plan": "Premium Monthly", "amount": 500}', true, now() - interval '20 days'),
|
||||
(1006, 10, 'user', 'subscription_activated', 'success', 'in_app', 'Subscription Activated', 'Your Premium Monthly subscription is now active until March 20, 2026.', '{"plan": "Premium Monthly", "expires": "2026-03-20"}', true, now() - interval '20 days'),
|
||||
(1007, 10, 'user', 'knowledge_level_update', 'info', 'in_app', 'Knowledge Level Updated', 'Your knowledge level has been updated to: Intermediate', '{"previous_level": "Beginner", "new_level": "Intermediate"}', false, now() - interval '15 days'),
|
||||
(1008, 10, 'user', 'issue_status_updated', 'info', 'in_app', 'Issue Status Updated', 'Your issue "Video not loading on mobile" has been updated to: in_progress', '{"issue_id": 1, "subject": "Video not loading on mobile", "status": "in_progress"}', true, now() - interval '12 days'),
|
||||
(1009, 10, 'user', 'issue_status_updated', 'success', 'in_app', 'Issue Status Updated', 'Your issue "Cannot change profile picture" has been updated to: resolved', '{"issue_id": 3, "subject": "Cannot change profile picture", "status": "resolved"}', true, now() - interval '10 days'),
|
||||
(1010, 10, 'user', 'course_enrolled', 'success', 'in_app', 'Course Enrolled', 'You have been enrolled in "Biology 101".', '{"course_title": "Biology 101"}', false, now() - interval '8 days'),
|
||||
(1011, 10, 'user', 'assessment_assigned', 'info', 'in_app', 'New Assessment Available', 'A new assessment is available for "Algebra Fundamentals".', '{"course": "Algebra Fundamentals", "assessment_type": "quiz"}', false, now() - interval '5 days'),
|
||||
(1012, 10, 'user', 'announcement', 'info', 'in_app', 'Platform Maintenance', 'Scheduled maintenance on Feb 15, 2026 from 2:00 AM - 4:00 AM EAT.', '{"scheduled_at": "2026-02-15T02:00:00+03:00", "duration_hours": 2}', false, now() - interval '2 days'),
|
||||
(1013, 10, 'user', 'video_added', 'info', 'in_app', 'New Video Available', 'A new video "Solving Quadratic Equations" has been added.', '{"video_title": "Solving Quadratic Equations", "sub_course": "Quadratics"}', false, now() - interval '1 day'),
|
||||
|
||||
-- Team member notifications (receiver_type=team_member, user_id references team_members.id)
|
||||
(1014, 2, 'team_member', 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Video not loading on mobile" has been reported.', '{"issue_id": 1, "subject": "Video not loading on mobile", "reporter_id": 10}', false, now() - interval '14 days'),
|
||||
(1015, 2, 'team_member', 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Payment confirmation not received" has been reported.', '{"issue_id": 2, "subject": "Payment confirmation not received", "reporter_id": 10}', false, now() - interval '10 days'),
|
||||
(1016, 2, 'team_member', 'issue_created', 'info', 'in_app', 'New Issue Reported', 'A new issue "Quiz results not saving" has been reported.', '{"issue_id": 5, "subject": "Quiz results not saving", "reporter_id": 10}', false, now() - interval '5 days'),
|
||||
(1017, 2, 'team_member', 'user_deleted', 'warning', 'in_app', 'User Deleted', 'User ID 99 has been deleted.', '{"deleted_user_id": 99, "deleted_by": 2}', true, now() - interval '18 days'),
|
||||
(1018, 2, 'team_member', 'admin_created', 'info', 'in_app', 'New Admin Created', 'A new admin account has been created for admin@yimaru.com.', '{"admin_email": "admin@yimaru.com"}', true, now() - interval '28 days'),
|
||||
(1019, 2, 'team_member', 'team_member_created','info', 'in_app', 'New Team Member', 'A new team member has been added.', '{"member_email": "support@yimaru.com", "role": "support"}', true, now() - interval '26 days'),
|
||||
(1020, 2, 'team_member', 'system_alert', 'warning', 'in_app', 'High Error Rate Detected', 'The notification delivery failure rate exceeded 5% in the last hour.', '{"failure_rate": 5.2, "window": "1h"}', false, now() - interval '3 days'),
|
||||
(1021, 3, 'team_member', 'announcement', 'info', 'in_app', 'Weekly Registration Report','15 new students registered this week.', '{"count": 15, "period": "weekly"}', false, now() - interval '1 day')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Scheduled notifications seeds (created_by references users.id)
|
||||
INSERT INTO scheduled_notifications (
|
||||
id, channel, title, message, html, scheduled_at, status, target_user_ids, target_role, target_raw,
|
||||
attempt_count, last_error, processing_started_at, sent_at, cancelled_at, created_by, created_at, updated_at
|
||||
) VALUES
|
||||
(2001, 'push', 'Reminder: Continue Your Lesson', 'Pick up where you left off and continue learning today.', NULL, now() + interval '6 hours', 'pending', ARRAY[10,11], NULL, NULL, 0, NULL, NULL, NULL, NULL, 10, now() - interval '1 day', now() - interval '1 day'),
|
||||
(2002, 'email', 'Weekly Progress Summary', 'Your weekly course progress summary is ready.', '<p>Your weekly course progress summary is ready.</p>', now() + interval '1 day', 'pending', NULL, 'STUDENT', NULL, 0, NULL, NULL, NULL, NULL, 10, now() - interval '1 day', now() - interval '1 day'),
|
||||
(2003, 'sms', 'Platform Maintenance', 'Scheduled maintenance tonight from 02:00 to 04:00 EAT.', NULL, now() - interval '2 days', 'sent', ARRAY[10,12], NULL, NULL, 1, NULL, now() - interval '2 days' - interval '5 minutes', now() - interval '2 days', NULL, 10, now() - interval '3 days', now() - interval '2 days'),
|
||||
(2004, 'email', 'Payment Service Alert', 'Some users may experience delayed payment confirmation.', '<p>Some users may experience delayed payment confirmation.</p>', now() - interval '1 day', 'failed', NULL, 'SUPPORT', NULL, 3, 'SMTP temporary outage', now() - interval '1 day' - interval '15 minutes', NULL, NULL, 10, now() - interval '2 days', now() - interval '1 day'),
|
||||
(2005, 'push', 'Obsolete Campaign', 'This campaign was cancelled by admin.', NULL, now() + interval '2 days', 'cancelled', NULL, NULL, '{"segment":"inactive_users"}'::jsonb, 0, NULL, NULL, NULL, now() - interval '12 hours', 10, now() - interval '1 day', now() - interval '12 hours')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
-- Intentionally empty: no demo notification seed (login-only seed in 001).
|
||||
|
|
|
|||
|
|
@ -1,469 +1,2 @@
|
|||
-- ======================================================
|
||||
-- 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);
|
||||
-- Intentionally empty: course hierarchy is not seeded from SQL.
|
||||
-- Use admin/API or migrations to create content.
|
||||
|
|
|
|||
|
|
@ -1,29 +1 @@
|
|||
-- Seed account deletion request states for admin panel tracking
|
||||
-- Users referenced here are seeded in 001_initial_seed_data.sql (IDs: 10, 11, 12).
|
||||
|
||||
-- Pending deletion request (within grace period)
|
||||
UPDATE users
|
||||
SET
|
||||
deletion_requested_at = now() - interval '2 days',
|
||||
deletion_scheduled_at = now() + interval '13 days',
|
||||
deletion_cancelled_at = NULL,
|
||||
updated_at = now()
|
||||
WHERE id = 10;
|
||||
|
||||
-- Due deletion request (grace period elapsed, awaiting purge worker)
|
||||
UPDATE users
|
||||
SET
|
||||
deletion_requested_at = now() - interval '20 days',
|
||||
deletion_scheduled_at = now() - interval '5 days',
|
||||
deletion_cancelled_at = NULL,
|
||||
updated_at = now()
|
||||
WHERE id = 11;
|
||||
|
||||
-- Cancelled deletion request (request made then cancelled)
|
||||
UPDATE users
|
||||
SET
|
||||
deletion_requested_at = now() - interval '10 days',
|
||||
deletion_scheduled_at = now() + interval '5 days',
|
||||
deletion_cancelled_at = now() - interval '3 days',
|
||||
updated_at = now()
|
||||
WHERE id = 12;
|
||||
-- Intentionally empty: no demo account-deletion seed (login-only seed in 001).
|
||||
|
|
|
|||
|
|
@ -1,67 +1 @@
|
|||
-- Seed TRUE_FALSE and SHORT_ANSWER question types
|
||||
-- Ensures question sets contain non-MCQ questions for end-to-end testing.
|
||||
|
||||
-- ======================================================
|
||||
-- TRUE_FALSE questions (stored in questions + question_options)
|
||||
-- ======================================================
|
||||
INSERT INTO questions (
|
||||
id,
|
||||
question_text,
|
||||
question_type,
|
||||
difficulty_level,
|
||||
points,
|
||||
status,
|
||||
created_at
|
||||
)
|
||||
VALUES
|
||||
(27, 'The Python interpreter executes Python code top-to-bottom.', 'TRUE_FALSE', 'EASY', 1, 'PUBLISHED', CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- question_options for TRUE_FALSE: use two options with exactly one correct
|
||||
INSERT INTO question_options (question_id, option_text, option_order, is_correct)
|
||||
VALUES
|
||||
(27, 'True', 1, TRUE),
|
||||
(27, 'False', 2, FALSE)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ======================================================
|
||||
-- SHORT_ANSWER questions (stored in questions + question_short_answers)
|
||||
-- ======================================================
|
||||
INSERT INTO questions (
|
||||
id,
|
||||
question_text,
|
||||
question_type,
|
||||
difficulty_level,
|
||||
points,
|
||||
status,
|
||||
created_at
|
||||
)
|
||||
VALUES
|
||||
(29, 'What keyword is used in Python to define a function?', 'SHORT_ANSWER', 'EASY', 1, 'PUBLISHED', CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO question_short_answers (question_id, acceptable_answer, match_type)
|
||||
VALUES
|
||||
(29, 'def', 'EXACT')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ======================================================
|
||||
-- Link new questions into existing question sets
|
||||
-- Question Set 1: Initial Assessment (set_id = 1, PUBLISHED)
|
||||
-- Question Set 2: Python Basics Assessment (set_id = 2, PUBLISHED)
|
||||
-- ======================================================
|
||||
INSERT INTO question_set_items (set_id, question_id, display_order)
|
||||
VALUES
|
||||
(1, 27, 17),
|
||||
(1, 29, 18),
|
||||
(2, 27, 3),
|
||||
(2, 29, 4)
|
||||
ON CONFLICT (set_id, question_id) DO NOTHING;
|
||||
|
||||
-- ======================================================
|
||||
-- Reset sequences to avoid ID collisions after seeding
|
||||
-- ======================================================
|
||||
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_short_answers', 'id'), COALESCE((SELECT MAX(id) FROM question_short_answers), 1), true);
|
||||
|
||||
-- Intentionally empty: no demo question seed (login-only seed in 001).
|
||||
|
|
|
|||
|
|
@ -1250,7 +1250,7 @@ const docTemplate = `{
|
|||
},
|
||||
"/api/v1/internal/db/reset-reseed": {
|
||||
"post": {
|
||||
"description": "Dangerous operation: clears and reseeds only course_categories, courses, and sub_courses from seed SQL files.",
|
||||
"description": "Truncates course_categories, courses, and sub_courses. If seed SQL contains INSERTs for those tables (e.g. 007_course_management_seed.sql), they are replayed; otherwise tables are left empty after truncate.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1242,7 +1242,7 @@
|
|||
},
|
||||
"/api/v1/internal/db/reset-reseed": {
|
||||
"post": {
|
||||
"description": "Dangerous operation: clears and reseeds only course_categories, courses, and sub_courses from seed SQL files.",
|
||||
"description": "Truncates course_categories, courses, and sub_courses. If seed SQL contains INSERTs for those tables (e.g. 007_course_management_seed.sql), they are replayed; otherwise tables are left empty after truncate.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -2678,8 +2678,9 @@ paths:
|
|||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: 'Dangerous operation: clears and reseeds only course_categories,
|
||||
courses, and sub_courses from seed SQL files.'
|
||||
description: Truncates course_categories, courses, and sub_courses. If seed
|
||||
SQL contains INSERTs for those tables (e.g. 007_course_management_seed.sql),
|
||||
they are replayed; otherwise tables are left empty after truncate.
|
||||
parameters:
|
||||
- description: Reset token configured in DB_RESET_RESEED_TOKEN
|
||||
in: header
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func resolveSeedDir(seedDir string) (string, error) {
|
|||
|
||||
// ResetAndReseedDatabase godoc
|
||||
// @Summary Reset and reseed database
|
||||
// @Description Dangerous operation: clears and reseeds only course_categories, courses, and sub_courses from seed SQL files.
|
||||
// @Description Truncates course_categories, courses, and sub_courses. If seed SQL contains INSERTs for those tables (e.g. 007_course_management_seed.sql), they are replayed; otherwise tables are left empty after truncate.
|
||||
// @Tags internal
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
|
|
@ -154,18 +154,23 @@ func (h *Handler) ResetAndReseedDatabase(c *fiber.Ctx) error {
|
|||
}
|
||||
}
|
||||
|
||||
missing := 0
|
||||
for _, tableName := range tableNames {
|
||||
if _, ok := statements[tableName]; !ok {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Missing required seed statement",
|
||||
Error: fmt.Sprintf("could not find INSERT INTO %s in seed files", tableName),
|
||||
})
|
||||
missing++
|
||||
}
|
||||
}
|
||||
if missing != 0 && missing != len(tableNames) {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Incomplete course seed SQL",
|
||||
Error: "seed files must define INSERT for all of course_categories, courses, and sub_courses, or none of them (truncate-only)",
|
||||
})
|
||||
}
|
||||
|
||||
var sqlBuilder strings.Builder
|
||||
sqlBuilder.WriteString("BEGIN;\n")
|
||||
sqlBuilder.WriteString("TRUNCATE TABLE sub_courses, courses, course_categories RESTART IDENTITY CASCADE;\n")
|
||||
if missing == 0 {
|
||||
for _, tableName := range tableNames {
|
||||
sqlBuilder.WriteString("\n-- ")
|
||||
sqlBuilder.WriteString(tableName)
|
||||
|
|
@ -175,6 +180,7 @@ func (h *Handler) ResetAndReseedDatabase(c *fiber.Ctx) error {
|
|||
sqlBuilder.WriteString(statements[tableName])
|
||||
sqlBuilder.WriteString("\n")
|
||||
}
|
||||
}
|
||||
sqlBuilder.WriteString("COMMIT;")
|
||||
|
||||
if _, err := h.analyticsDB.ExecRaw(c.Context(), sqlBuilder.String()); err != nil {
|
||||
|
|
@ -184,12 +190,18 @@ func (h *Handler) ResetAndReseedDatabase(c *fiber.Ctx) error {
|
|||
})
|
||||
}
|
||||
|
||||
msg := "Course management hierarchy reset and reseed completed successfully"
|
||||
if missing == len(tableNames) {
|
||||
msg = "Course management hierarchy truncated successfully (no INSERT seed configured; tables empty)"
|
||||
}
|
||||
return c.JSON(domain.Response{
|
||||
Message: "Course management hierarchy reset and reseed completed successfully",
|
||||
Message: msg,
|
||||
Data: map[string]interface{}{
|
||||
"seed_dir": seedDir,
|
||||
"tables": tableNames,
|
||||
"sources": statementSource,
|
||||
"reseeded": missing == 0,
|
||||
"truncate_only": missing == len(tableNames),
|
||||
},
|
||||
Success: true,
|
||||
StatusCode: fiber.StatusOK,
|
||||
|
|
|
|||
4
makefile
4
makefile
|
|
@ -66,10 +66,6 @@ seed_data:
|
|||
sleep 1; \
|
||||
done
|
||||
@for file in db/data/*.sql; do \
|
||||
if [ "$$(basename $$file)" = "007_course_management_seed.sql" ]; then \
|
||||
echo "Skipping $$file (course management seed disabled)"; \
|
||||
continue; \
|
||||
fi; \
|
||||
echo "Seeding $$file..."; \
|
||||
cat $$file | docker exec -i yimaru-backend-postgres-1 psql -U root -d gh; \
|
||||
done
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user