fix: map default CEFR courses to Beginner/Intermediate/Advanced programs

Seed A1-A2, B1-B2, and C1-C2 only on their matching programs; add migration
000050 for existing databases. Document mapping in domain.

Made-with: Cursor
This commit is contained in:
Yared Yemane 2026-04-24 01:14:50 -07:00
parent 5b53929d92
commit 33d34f0dd2
4 changed files with 66 additions and 12 deletions

View File

@ -1,5 +1,5 @@
-- Default CEFR-style course names per program (custom courses can still be created via the API with any name). -- Default CEFR-style courses per seeded program: Beginner→A1,A2; Intermediate→B1,B2; Advanced→C1,C2.
-- Matches hierarchy note on courses: CEFR labels A1..C2, plus ad-hoc names allowed. -- Custom courses can still be created via the API with any name.
INSERT INTO courses (program_id, name, description, thumbnail) INSERT INTO courses (program_id, name, description, thumbnail)
SELECT SELECT
p.id, p.id,
@ -7,12 +7,13 @@ SELECT
'Default CEFR level course (system seed).', 'Default CEFR level course (system seed).',
NULL NULL
FROM programs AS p FROM programs AS p
CROSS JOIN ( INNER JOIN (
VALUES VALUES
('A1'), ('Beginner', 'A1'),
('A2'), ('Beginner', 'A2'),
('B1'), ('Intermediate', 'B1'),
('B2'), ('Intermediate', 'B2'),
('C1'), ('Advanced', 'C1'),
('C2') ('Advanced', 'C2')
) AS v (name); ) AS v (program_name, name)
ON p.name = v.program_name;

View File

@ -0,0 +1 @@
-- Data cleanup is not reversed; restoring the old cross-product seed would be ambiguous.

View File

@ -0,0 +1,45 @@
-- Align default seeded courses with program: Beginner→A1,A2; Intermediate→B1,B2; Advanced→C1,C2.
-- Only touches rows with the system seed description; custom courses are unchanged.
-- Removing a course cascades to modules, lessons, and related LMS progress (see FKs on those tables).
DELETE FROM courses AS c
USING programs AS p
WHERE c.program_id = p.id
AND c.description = 'Default CEFR level course (system seed).'
AND (
(
p.name = 'Beginner'
AND c.name IN ('B1', 'B2', 'C1', 'C2')
)
OR (
p.name = 'Intermediate'
AND c.name IN ('A1', 'A2', 'C1', 'C2')
)
OR (
p.name = 'Advanced'
AND c.name IN ('A1', 'A2', 'B1', 'B2')
)
);
INSERT INTO courses (program_id, name, description, thumbnail)
SELECT
p.id,
v.name,
'Default CEFR level course (system seed).',
NULL
FROM programs AS p
INNER JOIN (
VALUES
('Beginner', 'A1'),
('Beginner', 'A2'),
('Intermediate', 'B1'),
('Intermediate', 'B2'),
('Advanced', 'C1'),
('Advanced', 'C2')
) AS v (program_name, name)
ON p.name = v.program_name
WHERE
NOT EXISTS (
SELECT 1 FROM courses AS e
WHERE e.program_id = p.id AND e.name = v.name
);

View File

@ -2,8 +2,15 @@ package domain
import "time" import "time"
// DefaultCEFRCourseNames are the standard course names seeded for each program (migration 000048). // DefaultCEFRCoursesByProgramName maps seeded program names to default course names
// Creating a course via the API may use any of these or a custom name. // (migrations 000048 and 000050). The API may still create courses with any name.
var DefaultCEFRCoursesByProgramName = map[string][]string{
"Beginner": {"A1", "A2"},
"Intermediate": {"B1", "B2"},
"Advanced": {"C1", "C2"},
}
// DefaultCEFRCourseNames is every CEFR label used in DefaultCEFRCoursesByProgramName.
var DefaultCEFRCourseNames = []string{"A1", "A2", "B1", "B2", "C1", "C2"} var DefaultCEFRCourseNames = []string{"A1", "A2", "B1", "B2", "C1", "C2"}
// Course belongs to a Program. // Course belongs to a Program.