course management and course data seed implementations

This commit is contained in:
Yared Yemane 2026-01-24 06:29:42 -08:00
parent aae54e928a
commit 64c25699e9
48 changed files with 9854 additions and 4750 deletions

View File

@ -287,3 +287,93 @@ VALUES
(16, 'The speaker is promising to arrive on time.', 2, TRUE), (16, 'The speaker is promising to arrive on time.', 2, TRUE),
(16, 'The speaker might arrive late.', 3, FALSE), (16, 'The speaker might arrive late.', 3, FALSE),
(16, 'The speaker has already arrived.', 4, FALSE); (16, 'The speaker has already arrived.', 4, FALSE);
-- ======================================================
-- Course Management Seed Data
-- ======================================================
-- Course Categories
INSERT INTO course_categories (name, is_active, created_at) VALUES
('Programming', TRUE, CURRENT_TIMESTAMP),
('Data Science', TRUE, CURRENT_TIMESTAMP),
('Web Development', TRUE, CURRENT_TIMESTAMP);
-- Courses
INSERT INTO courses (category_id, title, description, is_active) VALUES
(1, 'Python Programming Fundamentals', 'Learn Python from basics to advanced concepts', TRUE),
(1, 'JavaScript for Beginners', 'Master JavaScript programming language', TRUE),
(1, 'Advanced Java Development', 'Deep dive into Java enterprise development', TRUE),
(2, 'Data Analysis with Python', 'Learn data manipulation and analysis using pandas', TRUE),
(2, 'Machine Learning Basics', 'Introduction to machine learning algorithms', TRUE),
(3, 'Full Stack Web Development', 'Complete guide to modern web development', TRUE),
(3, 'React.js Masterclass', 'Build dynamic user interfaces with React', TRUE);
-- Programs
INSERT INTO programs (course_id, title, description, thumbnail, display_order, is_active) VALUES
(1, 'Python Basics', 'Fundamental concepts of Python programming', NULL, 1, TRUE),
(1, 'Python Intermediate', 'Object-oriented programming and data structures', NULL, 2, TRUE),
(1, 'Python Advanced', 'Advanced Python concepts and best practices', NULL, 3, TRUE),
(2, 'JavaScript Fundamentals', 'Core JavaScript concepts and syntax', NULL, 1, TRUE),
(2, 'DOM Manipulation', 'Working with the Document Object Model', NULL, 2, TRUE),
(3, 'Java Core Concepts', 'Essential Java programming principles', NULL, 1, TRUE),
(3, 'Spring Framework', 'Building enterprise applications with Spring', NULL, 2, TRUE);
-- Levels
INSERT INTO levels (program_id, title, description, level_index, is_active) VALUES
(1, 'Getting Started', 'Introduction to Python and basic syntax', 1, TRUE),
(1, 'Data Types & Variables', 'Understanding Python data types and variables', 2, TRUE),
(1, 'Control Flow', 'Conditional statements and loops', 3, TRUE),
(2, 'Functions', 'Writing and using functions in Python', 1, TRUE),
(2, 'Lists & Dictionaries', 'Working with Python collections', 2, TRUE),
(2, 'File Operations', 'Reading and writing files', 3, TRUE);
-- Modules
INSERT INTO modules (level_id, title, content, display_order, is_active) VALUES
(1, 'Installing Python', 'Setting up Python development environment', 1, TRUE),
(1, 'Your First Python Program', 'Writing and running your first Python script', 2, TRUE),
(2, 'Numbers and Strings', 'Working with numeric and text data types', 1, TRUE),
(2, 'Variables and Assignment', 'Understanding variables and assignment operators', 2, TRUE),
(3, 'Conditional Statements', 'Using if, elif, and else statements', 1, TRUE),
(3, 'Loops in Python', 'For and while loops with examples', 2, TRUE);
-- Module Videos
INSERT INTO module_videos (
module_id,
title,
description,
video_url,
duration,
resolution,
visibility,
is_active
) VALUES
(1, 'Python Installation Guide', 'Installing Python', 'https://example.com/python-install.mp4', 900, '1080p', 'public', TRUE),
(2, 'Hello World in Python', 'First Python program', 'https://example.com/python-hello.mp4', 1200, '1080p', 'public', TRUE),
(3, 'Numbers and Math', 'Numeric types in Python', 'https://example.com/python-numbers.mp4', 1500, '720p', 'public', TRUE);
-- Practices
INSERT INTO practices (
owner_type,
owner_id,
title,
description,
persona,
is_active
) VALUES
('LEVEL', 1, 'Python Basics Assessment', 'Test Python basics', 'beginner', TRUE),
('LEVEL', 2, 'Data Types Practice', 'Practice Python data types', 'beginner', TRUE),
('MODULE', 3, 'Control Flow Quiz', 'Assess control flow knowledge', 'beginner', TRUE);
-- Practice Questions
INSERT INTO practice_questions (
practice_id,
question,
sample_answer,
tips,
type
) VALUES
(1, 'What is the correct way to print "Hello World" in Python?', 'print("Hello World")', 'Use print()', 'MCQ'),
(1, 'Which is a valid Python variable name?', 'my_variable', 'Variables cannot start with numbers', 'MCQ'),
(2, 'How do you convert "123" to an integer?', 'int("123")', 'Use int()', 'MCQ'),
(3, 'How many times does range(3) loop run?', '3', 'Starts from zero', 'MCQ');

View File

@ -78,3 +78,59 @@ SELECT setval(
COALESCE((SELECT MAX(id) FROM reported_issues), 1), COALESCE((SELECT MAX(id) FROM reported_issues), 1),
true 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
);
-- programs.id (BIGSERIAL)
SELECT setval(
pg_get_serial_sequence('programs', 'id'),
COALESCE((SELECT MAX(id) FROM programs), 1),
true
);
-- levels.id (BIGSERIAL)
SELECT setval(
pg_get_serial_sequence('levels', 'id'),
COALESCE((SELECT MAX(id) FROM levels), 1),
true
);
-- modules.id (BIGSERIAL)
SELECT setval(
pg_get_serial_sequence('modules', 'id'),
COALESCE((SELECT MAX(id) FROM modules), 1),
true
);
-- module_videos.id (BIGSERIAL)
SELECT setval(
pg_get_serial_sequence('module_videos', 'id'),
COALESCE((SELECT MAX(id) FROM module_videos), 1),
true
);
-- practices.id (BIGSERIAL)
SELECT setval(
pg_get_serial_sequence('practices', 'id'),
COALESCE((SELECT MAX(id) FROM practices), 1),
true
);
-- practice_questions.id (BIGSERIAL)
SELECT setval(
pg_get_serial_sequence('practice_questions', 'id'),
COALESCE((SELECT MAX(id) FROM practice_questions), 1),
true
);

View File

@ -3,50 +3,37 @@ INSERT INTO course_categories (
name, name,
is_active is_active
) )
VALUES ( VALUES ($1, COALESCE($2, true))
$1, -- name RETURNING *;
$2 -- is_active
)
RETURNING
id,
name,
is_active,
created_at;
-- name: GetCourseCategoryByID :one -- name: GetCourseCategoryByID :one
SELECT SELECT *
id,
name,
is_active,
created_at
FROM course_categories FROM course_categories
WHERE id = $1; WHERE id = $1;
-- name: ListActiveCourseCategories :many
-- name: GetAllCourseCategories :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
name, name,
is_active, is_active,
created_at created_at
FROM course_categories FROM course_categories
WHERE is_active = TRUE ORDER BY created_at DESC
ORDER BY created_at DESC; LIMIT sqlc.narg('limit')::INT
OFFSET sqlc.narg('offset')::INT;
-- name: UpdateCourseCategory :one
-- name: UpdateCourseCategory :exec
UPDATE course_categories UPDATE course_categories
SET SET
name = $2, name = COALESCE($1, name),
is_active = $3 is_active = COALESCE($2, is_active)
WHERE id = $1 WHERE id = $3;
RETURNING
id,
name,
is_active,
created_at;
-- name: DeactivateCourseCategory :exec
UPDATE course_categories -- name: DeleteCourseCategory :exec
SET is_active = FALSE DELETE FROM course_categories
WHERE id = $1; WHERE id = $1;

View File

@ -7,14 +7,37 @@ INSERT INTO programs (
display_order, display_order,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, $4, COALESCE($5, 0), COALESCE($6, true))
$1, -- course_id RETURNING *;
$2, -- title
$3, -- description
$4, -- thumbnail -- name: GetProgramsByCourse :many
$5, -- display_order SELECT
$6 -- is_active COUNT(*) OVER () AS total_count,
) id,
course_id,
title,
description,
thumbnail,
display_order,
is_active
FROM programs
WHERE course_id = $1
ORDER BY display_order ASC;
-- name: UpdateProgramPartial :exec
UPDATE programs
SET
title = COALESCE($1, title),
description = COALESCE($2, description),
thumbnail = COALESCE($3, thumbnail),
display_order = COALESCE($4, display_order),
is_active = COALESCE($5, is_active)
WHERE id = $6;
-- name: DeleteProgram :one
DELETE FROM programs
WHERE id = $1
RETURNING RETURNING
id, id,
course_id, course_id,
@ -24,6 +47,7 @@ RETURNING
display_order, display_order,
is_active; is_active;
-- name: GetProgramByID :one -- name: GetProgramByID :one
SELECT SELECT
id, id,
@ -63,7 +87,7 @@ FROM programs
WHERE is_active = TRUE WHERE is_active = TRUE
ORDER BY display_order ASC; ORDER BY display_order ASC;
-- name: UpdateProgram :one -- name: UpdateProgramFull :one
UPDATE programs UPDATE programs
SET SET
course_id = $2, course_id = $2,
@ -82,6 +106,7 @@ RETURNING
display_order, display_order,
is_active; is_active;
-- name: DeactivateProgram :exec -- name: DeactivateProgram :exec
UPDATE programs UPDATE programs
SET is_active = FALSE SET is_active = FALSE

View File

@ -5,31 +5,19 @@ INSERT INTO courses (
description, description,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, COALESCE($4, true))
$1, -- category_id RETURNING *;
$2, -- title
$3, -- description
$4 -- is_active
)
RETURNING
id,
category_id,
title,
description,
is_active;
-- name: GetCourseByID :one -- name: GetCourseByID :one
SELECT SELECT *
id,
category_id,
title,
description,
is_active
FROM courses FROM courses
WHERE id = $1; WHERE id = $1;
-- name: ListCoursesByCategory :many
-- name: GetCoursesByCategory :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
category_id, category_id,
title, title,
@ -37,37 +25,20 @@ SELECT
is_active is_active
FROM courses FROM courses
WHERE category_id = $1 WHERE category_id = $1
AND is_active = TRUE ORDER BY id DESC
ORDER BY id DESC; LIMIT sqlc.narg('limit')::INT
OFFSET sqlc.narg('offset')::INT;
-- name: ListActiveCourses :many
SELECT
id,
category_id,
title,
description,
is_active
FROM courses
WHERE is_active = TRUE
ORDER BY id DESC;
-- name: UpdateCourse :one -- name: UpdateCourse :exec
UPDATE courses UPDATE courses
SET SET
category_id = $2, title = COALESCE($1, title),
title = $3, description = COALESCE($2, description),
description = $4, is_active = COALESCE($3, is_active)
is_active = $5 WHERE id = $4;
WHERE id = $1
RETURNING
id,
category_id,
title,
description,
is_active;
-- name: DeactivateCourse :exec
UPDATE courses -- name: DeleteCourse :exec
SET is_active = FALSE DELETE FROM courses
WHERE id = $1; WHERE id = $1;

View File

@ -0,0 +1,16 @@
-- name: GetFullLearningTree :many
SELECT
c.id AS course_id,
c.title AS course_title,
p.id AS program_id,
p.title AS program_title,
l.id AS level_id,
l.title AS level_title,
m.id AS module_id,
m.title AS module_title
FROM courses c
JOIN programs p ON p.course_id = c.id
JOIN levels l ON l.program_id = p.id
LEFT JOIN modules m ON m.level_id = l.id
WHERE c.is_active = true
ORDER BY p.display_order, l.level_index, m.display_order;

View File

@ -6,34 +6,13 @@ INSERT INTO modules (
display_order, display_order,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, true))
$1, -- level_id RETURNING *;
$2, -- title
$3, -- content
$4, -- display_order
$5 -- is_active
)
RETURNING
id,
level_id,
title,
content,
display_order,
is_active;
-- name: GetModuleByID :one
SELECT
id,
level_id,
title,
content,
display_order,
is_active
FROM modules
WHERE id = $1;
-- name: ListModulesByLevel :many -- name: GetModulesByLevel :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
level_id, level_id,
title, title,
@ -42,26 +21,19 @@ SELECT
is_active is_active
FROM modules FROM modules
WHERE level_id = $1 WHERE level_id = $1
AND is_active = TRUE ORDER BY display_order ASC;
ORDER BY display_order ASC, id ASC;
-- name: UpdateModule :one
-- name: UpdateModule :exec
UPDATE modules UPDATE modules
SET SET
title = $2, title = COALESCE($1, title),
content = $3, content = COALESCE($2, content),
display_order = $4, display_order = COALESCE($3, display_order),
is_active = $5 is_active = COALESCE($4, is_active)
WHERE id = $1 WHERE id = $5;
RETURNING
id,
level_id,
title,
content,
display_order,
is_active;
-- name: DeactivateModule :exec
UPDATE modules -- name: DeleteModule :exec
SET is_active = FALSE DELETE FROM modules
WHERE id = $1; WHERE id = $1;

View File

@ -6,136 +6,50 @@ INSERT INTO module_videos (
video_url, video_url,
duration, duration,
resolution, resolution,
is_published,
publish_date,
visibility,
instructor_id, instructor_id,
thumbnail, thumbnail,
visibility,
is_active is_active
) )
VALUES ( VALUES (
$1, -- module_id $1, $2, $3, $4, $5, $6,
$2, -- title $7, $8, $9,
$3, -- description COALESCE($10, true)
$4, -- video_url
$5, -- duration
$6, -- resolution
$7, -- is_published
$8, -- publish_date
$9, -- visibility
$10, -- instructor_id
$11, -- thumbnail
$12 -- is_active
) )
RETURNING RETURNING *;
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active;
-- name: GetModuleVideoByID :one
SELECT
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active
FROM module_videos
WHERE id = $1;
-- name: ListPublishedVideosByModule :many -- name: PublishModuleVideo :exec
SELECT
id,
module_id,
title,
description,
video_url,
duration,
resolution,
publish_date,
visibility,
instructor_id,
thumbnail
FROM module_videos
WHERE module_id = $1
AND is_active = TRUE
AND is_published = TRUE
ORDER BY publish_date ASC, id ASC;
-- name: ListAllVideosByModule :many
SELECT
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active
FROM module_videos
WHERE module_id = $1
ORDER BY id ASC;
-- name: UpdateModuleVideo :one
UPDATE module_videos UPDATE module_videos
SET SET
title = $2, is_published = true,
description = $3, publish_date = CURRENT_TIMESTAMP
video_url = $4,
duration = $5,
resolution = $6,
is_published = $7,
publish_date = $8,
visibility = $9,
instructor_id = $10,
thumbnail = $11,
is_active = $12
WHERE id = $1
RETURNING
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active;
-- name: DeactivateModuleVideo :exec
UPDATE module_videos
SET is_active = FALSE
WHERE id = $1; WHERE id = $1;
-- name: GetPublishedVideosByModule :many
SELECT *
FROM module_videos
WHERE module_id = $1
AND is_published = true
AND is_active = true
ORDER BY publish_date ASC;
-- name: UpdateModuleVideo :exec
UPDATE module_videos
SET
title = COALESCE($1, title),
description = COALESCE($2, description),
video_url = COALESCE($3, video_url),
duration = COALESCE($4, duration),
resolution = COALESCE($5, resolution),
visibility = COALESCE($6, visibility),
thumbnail = COALESCE($7, thumbnail),
is_active = COALESCE($8, is_active)
WHERE id = $9;
-- name: DeleteModuleVideo :exec
DELETE FROM module_videos
WHERE id = $1;

View File

@ -8,71 +8,26 @@ INSERT INTO practice_questions (
tips, tips,
type type
) )
VALUES ( VALUES ($1, $2, $3, $4, $5, $6, $7)
$1, -- practice_id RETURNING *;
$2, -- question
$3, -- question_voice_prompt
$4, -- sample_answer_voice_prompt
$5, -- sample_answer
$6, -- tips
$7 -- type (MCQ, TRUE_FALSE, SHORT)
)
RETURNING
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type;
-- name: GetPracticeQuestionByID :one
SELECT
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
FROM practice_questions
WHERE id = $1;
-- name: ListPracticeQuestions :many -- name: GetQuestionsByPractice :many
SELECT SELECT *
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
FROM practice_questions FROM practice_questions
WHERE practice_id = $1 WHERE practice_id = $1
ORDER BY id ASC; ORDER BY id ASC;
-- name: UpdatePracticeQuestion :one
-- name: UpdatePracticeQuestion :exec
UPDATE practice_questions UPDATE practice_questions
SET SET
question = $2, question = COALESCE($1, question),
question_voice_prompt = $3, sample_answer = COALESCE($2, sample_answer),
sample_answer_voice_prompt = $4, tips = COALESCE($3, tips),
sample_answer = $5, type = COALESCE($4, type)
tips = $6, WHERE id = $5;
type = $7
WHERE id = $1
RETURNING
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type;
-- name: DeletePracticeQuestion :exec -- name: DeletePracticeQuestion :exec
DELETE FROM practice_questions DELETE FROM practice_questions

View File

@ -8,74 +8,29 @@ INSERT INTO practices (
persona, persona,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, true))
$1, -- owner_type (LEVEL | MODULE) RETURNING *;
$2, -- owner_id
$3, -- title
$4, -- description
$5, -- banner_image
$6, -- persona
$7 -- is_active
)
RETURNING
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active;
-- name: GetPracticeByID :one
SELECT
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active
FROM practices
WHERE id = $1;
-- name: ListPracticesByOwner :many -- name: GetPracticesByOwner :many
SELECT SELECT *
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active
FROM practices FROM practices
WHERE owner_type = $1 WHERE owner_type = $1
AND owner_id = $2 AND owner_id = $2
AND is_active = TRUE AND is_active = true;
ORDER BY id ASC;
-- name: UpdatePractice :one
-- name: UpdatePractice :exec
UPDATE practices UPDATE practices
SET SET
title = $2, title = COALESCE($1, title),
description = $3, description = COALESCE($2, description),
banner_image = $4, banner_image = COALESCE($3, banner_image),
persona = $5, persona = COALESCE($4, persona),
is_active = $6 is_active = COALESCE($5, is_active)
WHERE id = $1 WHERE id = $6;
RETURNING
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active;
-- name: DeactivatePractice :exec
UPDATE practices -- name: DeletePractice :exec
SET is_active = FALSE DELETE FROM practices
WHERE id = $1; WHERE id = $1;

View File

@ -4,48 +4,15 @@ INSERT INTO levels (
title, title,
description, description,
level_index, level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, $4, COALESCE($5, true))
$1, -- program_id RETURNING *;
$2, -- title
$3, -- description
$4, -- level_index
$5, -- number_of_modules
$6, -- number_of_practices
$7, -- number_of_videos
$8 -- is_active
)
RETURNING
id,
program_id,
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active;
-- name: GetLevelByID :one
SELECT
id,
program_id,
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active
FROM levels
WHERE id = $1;
-- name: ListLevelsByProgram :many -- name: GetLevelsByProgram :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
program_id, program_id,
title, title,
@ -57,32 +24,37 @@ SELECT
is_active is_active
FROM levels FROM levels
WHERE program_id = $1 WHERE program_id = $1
AND is_active = TRUE
ORDER BY level_index ASC; ORDER BY level_index ASC;
-- name: UpdateLevel :one
-- name: UpdateLevel :exec
UPDATE levels UPDATE levels
SET SET
title = $2, title = COALESCE($1, title),
description = $3, description = COALESCE($2, description),
level_index = $4, level_index = COALESCE($3, level_index),
number_of_modules = $5, is_active = COALESCE($4, is_active)
number_of_practices = $6, WHERE id = $5;
number_of_videos = $7,
is_active = $8
WHERE id = $1
RETURNING
id,
program_id,
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active;
-- name: DeactivateLevel :exec
-- name: IncrementLevelModuleCount :exec
UPDATE levels UPDATE levels
SET is_active = FALSE SET number_of_modules = number_of_modules + 1
WHERE id = $1;
-- name: IncrementLevelPracticeCount :exec
UPDATE levels
SET number_of_practices = number_of_practices + 1
WHERE id = $1;
-- name: IncrementLevelVideoCount :exec
UPDATE levels
SET number_of_videos = number_of_videos + 1
WHERE id = $1;
-- name: DeleteLevel :exec
DELETE FROM levels
WHERE id = $1; WHERE id = $1;

View File

@ -26,7 +26,7 @@ services:
image: dpage/pgadmin4:latest image: dpage/pgadmin4:latest
restart: always restart: always
ports: ports:
- "5050:80" - "5051:80"
environment: environment:
PGADMIN_DEFAULT_EMAIL: admin@local.dev PGADMIN_DEFAULT_EMAIL: admin@local.dev
PGADMIN_DEFAULT_PASSWORD: admin PGADMIN_DEFAULT_PASSWORD: admin

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,8 @@ package dbgen
import ( import (
"context" "context"
"github.com/jackc/pgx/v5/pgtype"
) )
const CreateCourseCategory = `-- name: CreateCourseCategory :one const CreateCourseCategory = `-- name: CreateCourseCategory :one
@ -14,24 +16,17 @@ INSERT INTO course_categories (
name, name,
is_active is_active
) )
VALUES ( VALUES ($1, COALESCE($2, true))
$1, -- name RETURNING id, name, is_active, created_at
$2 -- is_active
)
RETURNING
id,
name,
is_active,
created_at
` `
type CreateCourseCategoryParams struct { type CreateCourseCategoryParams struct {
Name string `json:"name"` Name string `json:"name"`
IsActive bool `json:"is_active"` Column2 interface{} `json:"column_2"`
} }
func (q *Queries) CreateCourseCategory(ctx context.Context, arg CreateCourseCategoryParams) (CourseCategory, error) { func (q *Queries) CreateCourseCategory(ctx context.Context, arg CreateCourseCategoryParams) (CourseCategory, error) {
row := q.db.QueryRow(ctx, CreateCourseCategory, arg.Name, arg.IsActive) row := q.db.QueryRow(ctx, CreateCourseCategory, arg.Name, arg.Column2)
var i CourseCategory var i CourseCategory
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
@ -42,24 +37,71 @@ func (q *Queries) CreateCourseCategory(ctx context.Context, arg CreateCourseCate
return i, err return i, err
} }
const DeactivateCourseCategory = `-- name: DeactivateCourseCategory :exec const DeleteCourseCategory = `-- name: DeleteCourseCategory :exec
UPDATE course_categories DELETE FROM course_categories
SET is_active = FALSE
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) DeactivateCourseCategory(ctx context.Context, id int64) error { func (q *Queries) DeleteCourseCategory(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateCourseCategory, id) _, err := q.db.Exec(ctx, DeleteCourseCategory, id)
return err return err
} }
const GetCourseCategoryByID = `-- name: GetCourseCategoryByID :one const GetAllCourseCategories = `-- name: GetAllCourseCategories :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
name, name,
is_active, is_active,
created_at created_at
FROM course_categories FROM course_categories
ORDER BY created_at DESC
LIMIT $2::INT
OFFSET $1::INT
`
type GetAllCourseCategoriesParams struct {
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetAllCourseCategoriesRow struct {
TotalCount int64 `json:"total_count"`
ID int64 `json:"id"`
Name string `json:"name"`
IsActive bool `json:"is_active"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}
func (q *Queries) GetAllCourseCategories(ctx context.Context, arg GetAllCourseCategoriesParams) ([]GetAllCourseCategoriesRow, error) {
rows, err := q.db.Query(ctx, GetAllCourseCategories, arg.Offset, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetAllCourseCategoriesRow
for rows.Next() {
var i GetAllCourseCategoriesRow
if err := rows.Scan(
&i.TotalCount,
&i.ID,
&i.Name,
&i.IsActive,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetCourseCategoryByID = `-- name: GetCourseCategoryByID :one
SELECT id, name, is_active, created_at
FROM course_categories
WHERE id = $1 WHERE id = $1
` `
@ -75,69 +117,21 @@ func (q *Queries) GetCourseCategoryByID(ctx context.Context, id int64) (CourseCa
return i, err return i, err
} }
const ListActiveCourseCategories = `-- name: ListActiveCourseCategories :many const UpdateCourseCategory = `-- name: UpdateCourseCategory :exec
SELECT
id,
name,
is_active,
created_at
FROM course_categories
WHERE is_active = TRUE
ORDER BY created_at DESC
`
func (q *Queries) ListActiveCourseCategories(ctx context.Context) ([]CourseCategory, error) {
rows, err := q.db.Query(ctx, ListActiveCourseCategories)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CourseCategory
for rows.Next() {
var i CourseCategory
if err := rows.Scan(
&i.ID,
&i.Name,
&i.IsActive,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateCourseCategory = `-- name: UpdateCourseCategory :one
UPDATE course_categories UPDATE course_categories
SET SET
name = $2, name = COALESCE($1, name),
is_active = $3 is_active = COALESCE($2, is_active)
WHERE id = $1 WHERE id = $3
RETURNING
id,
name,
is_active,
created_at
` `
type UpdateCourseCategoryParams struct { type UpdateCourseCategoryParams struct {
ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdateCourseCategory(ctx context.Context, arg UpdateCourseCategoryParams) (CourseCategory, error) { func (q *Queries) UpdateCourseCategory(ctx context.Context, arg UpdateCourseCategoryParams) error {
row := q.db.QueryRow(ctx, UpdateCourseCategory, arg.ID, arg.Name, arg.IsActive) _, err := q.db.Exec(ctx, UpdateCourseCategory, arg.Name, arg.IsActive, arg.ID)
var i CourseCategory return err
err := row.Scan(
&i.ID,
&i.Name,
&i.IsActive,
&i.CreatedAt,
)
return i, err
} }

View File

@ -20,22 +20,8 @@ INSERT INTO programs (
display_order, display_order,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, $4, COALESCE($5, 0), COALESCE($6, true))
$1, -- course_id RETURNING id, course_id, title, description, thumbnail, display_order, is_active
$2, -- title
$3, -- description
$4, -- thumbnail
$5, -- display_order
$6 -- is_active
)
RETURNING
id,
course_id,
title,
description,
thumbnail,
display_order,
is_active
` `
type CreateProgramParams struct { type CreateProgramParams struct {
@ -43,8 +29,8 @@ type CreateProgramParams struct {
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
Thumbnail pgtype.Text `json:"thumbnail"` Thumbnail pgtype.Text `json:"thumbnail"`
DisplayOrder int32 `json:"display_order"` Column5 interface{} `json:"column_5"`
IsActive bool `json:"is_active"` Column6 interface{} `json:"column_6"`
} }
func (q *Queries) CreateProgram(ctx context.Context, arg CreateProgramParams) (Program, error) { func (q *Queries) CreateProgram(ctx context.Context, arg CreateProgramParams) (Program, error) {
@ -53,8 +39,8 @@ func (q *Queries) CreateProgram(ctx context.Context, arg CreateProgramParams) (P
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.Thumbnail, arg.Thumbnail,
arg.DisplayOrder, arg.Column5,
arg.IsActive, arg.Column6,
) )
var i Program var i Program
err := row.Scan( err := row.Scan(
@ -80,6 +66,34 @@ func (q *Queries) DeactivateProgram(ctx context.Context, id int64) error {
return err return err
} }
const DeleteProgram = `-- name: DeleteProgram :one
DELETE FROM programs
WHERE id = $1
RETURNING
id,
course_id,
title,
description,
thumbnail,
display_order,
is_active
`
func (q *Queries) DeleteProgram(ctx context.Context, id int64) (Program, error) {
row := q.db.QueryRow(ctx, DeleteProgram, id)
var i Program
err := row.Scan(
&i.ID,
&i.CourseID,
&i.Title,
&i.Description,
&i.Thumbnail,
&i.DisplayOrder,
&i.IsActive,
)
return i, err
}
const GetProgramByID = `-- name: GetProgramByID :one const GetProgramByID = `-- name: GetProgramByID :one
SELECT SELECT
id, id,
@ -108,6 +122,61 @@ func (q *Queries) GetProgramByID(ctx context.Context, id int64) (Program, error)
return i, err return i, err
} }
const GetProgramsByCourse = `-- name: GetProgramsByCourse :many
SELECT
COUNT(*) OVER () AS total_count,
id,
course_id,
title,
description,
thumbnail,
display_order,
is_active
FROM programs
WHERE course_id = $1
ORDER BY display_order ASC
`
type GetProgramsByCourseRow struct {
TotalCount int64 `json:"total_count"`
ID int64 `json:"id"`
CourseID int64 `json:"course_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Thumbnail pgtype.Text `json:"thumbnail"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
}
func (q *Queries) GetProgramsByCourse(ctx context.Context, courseID int64) ([]GetProgramsByCourseRow, error) {
rows, err := q.db.Query(ctx, GetProgramsByCourse, courseID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetProgramsByCourseRow
for rows.Next() {
var i GetProgramsByCourseRow
if err := rows.Scan(
&i.TotalCount,
&i.ID,
&i.CourseID,
&i.Title,
&i.Description,
&i.Thumbnail,
&i.DisplayOrder,
&i.IsActive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const ListActivePrograms = `-- name: ListActivePrograms :many const ListActivePrograms = `-- name: ListActivePrograms :many
SELECT SELECT
id, id,
@ -193,7 +262,7 @@ func (q *Queries) ListProgramsByCourse(ctx context.Context, courseID int64) ([]P
return items, nil return items, nil
} }
const UpdateProgram = `-- name: UpdateProgram :one const UpdateProgramFull = `-- name: UpdateProgramFull :one
UPDATE programs UPDATE programs
SET SET
course_id = $2, course_id = $2,
@ -213,7 +282,7 @@ RETURNING
is_active is_active
` `
type UpdateProgramParams struct { type UpdateProgramFullParams struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CourseID int64 `json:"course_id"` CourseID int64 `json:"course_id"`
Title string `json:"title"` Title string `json:"title"`
@ -223,8 +292,8 @@ type UpdateProgramParams struct {
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
} }
func (q *Queries) UpdateProgram(ctx context.Context, arg UpdateProgramParams) (Program, error) { func (q *Queries) UpdateProgramFull(ctx context.Context, arg UpdateProgramFullParams) (Program, error) {
row := q.db.QueryRow(ctx, UpdateProgram, row := q.db.QueryRow(ctx, UpdateProgramFull,
arg.ID, arg.ID,
arg.CourseID, arg.CourseID,
arg.Title, arg.Title,
@ -245,3 +314,35 @@ func (q *Queries) UpdateProgram(ctx context.Context, arg UpdateProgramParams) (P
) )
return i, err return i, err
} }
const UpdateProgramPartial = `-- name: UpdateProgramPartial :exec
UPDATE programs
SET
title = COALESCE($1, title),
description = COALESCE($2, description),
thumbnail = COALESCE($3, thumbnail),
display_order = COALESCE($4, display_order),
is_active = COALESCE($5, is_active)
WHERE id = $6
`
type UpdateProgramPartialParams struct {
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Thumbnail pgtype.Text `json:"thumbnail"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateProgramPartial(ctx context.Context, arg UpdateProgramPartialParams) error {
_, err := q.db.Exec(ctx, UpdateProgramPartial,
arg.Title,
arg.Description,
arg.Thumbnail,
arg.DisplayOrder,
arg.IsActive,
arg.ID,
)
return err
}

View File

@ -18,25 +18,15 @@ INSERT INTO courses (
description, description,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, COALESCE($4, true))
$1, -- category_id RETURNING id, category_id, title, description, is_active
$2, -- title
$3, -- description
$4 -- is_active
)
RETURNING
id,
category_id,
title,
description,
is_active
` `
type CreateCourseParams struct { type CreateCourseParams struct {
CategoryID int64 `json:"category_id"` CategoryID int64 `json:"category_id"`
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
IsActive bool `json:"is_active"` Column4 interface{} `json:"column_4"`
} }
func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Course, error) { func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Course, error) {
@ -44,7 +34,7 @@ func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Cou
arg.CategoryID, arg.CategoryID,
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.IsActive, arg.Column4,
) )
var i Course var i Course
err := row.Scan( err := row.Scan(
@ -57,24 +47,18 @@ func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Cou
return i, err return i, err
} }
const DeactivateCourse = `-- name: DeactivateCourse :exec const DeleteCourse = `-- name: DeleteCourse :exec
UPDATE courses DELETE FROM courses
SET is_active = FALSE
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) DeactivateCourse(ctx context.Context, id int64) error { func (q *Queries) DeleteCourse(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateCourse, id) _, err := q.db.Exec(ctx, DeleteCourse, id)
return err return err
} }
const GetCourseByID = `-- name: GetCourseByID :one const GetCourseByID = `-- name: GetCourseByID :one
SELECT SELECT id, category_id, title, description, is_active
id,
category_id,
title,
description,
is_active
FROM courses FROM courses
WHERE id = $1 WHERE id = $1
` `
@ -92,46 +76,9 @@ func (q *Queries) GetCourseByID(ctx context.Context, id int64) (Course, error) {
return i, err return i, err
} }
const ListActiveCourses = `-- name: ListActiveCourses :many const GetCoursesByCategory = `-- name: GetCoursesByCategory :many
SELECT
id,
category_id,
title,
description,
is_active
FROM courses
WHERE is_active = TRUE
ORDER BY id DESC
`
func (q *Queries) ListActiveCourses(ctx context.Context) ([]Course, error) {
rows, err := q.db.Query(ctx, ListActiveCourses)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Course
for rows.Next() {
var i Course
if err := rows.Scan(
&i.ID,
&i.CategoryID,
&i.Title,
&i.Description,
&i.IsActive,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const ListCoursesByCategory = `-- name: ListCoursesByCategory :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
category_id, category_id,
title, title,
@ -139,20 +86,37 @@ SELECT
is_active is_active
FROM courses FROM courses
WHERE category_id = $1 WHERE category_id = $1
AND is_active = TRUE
ORDER BY id DESC ORDER BY id DESC
LIMIT $3::INT
OFFSET $2::INT
` `
func (q *Queries) ListCoursesByCategory(ctx context.Context, categoryID int64) ([]Course, error) { type GetCoursesByCategoryParams struct {
rows, err := q.db.Query(ctx, ListCoursesByCategory, categoryID) CategoryID int64 `json:"category_id"`
Offset pgtype.Int4 `json:"offset"`
Limit pgtype.Int4 `json:"limit"`
}
type GetCoursesByCategoryRow struct {
TotalCount int64 `json:"total_count"`
ID int64 `json:"id"`
CategoryID int64 `json:"category_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
IsActive bool `json:"is_active"`
}
func (q *Queries) GetCoursesByCategory(ctx context.Context, arg GetCoursesByCategoryParams) ([]GetCoursesByCategoryRow, error) {
rows, err := q.db.Query(ctx, GetCoursesByCategory, arg.CategoryID, arg.Offset, arg.Limit)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []Course var items []GetCoursesByCategoryRow
for rows.Next() { for rows.Next() {
var i Course var i GetCoursesByCategoryRow
if err := rows.Scan( if err := rows.Scan(
&i.TotalCount,
&i.ID, &i.ID,
&i.CategoryID, &i.CategoryID,
&i.Title, &i.Title,
@ -169,45 +133,28 @@ func (q *Queries) ListCoursesByCategory(ctx context.Context, categoryID int64) (
return items, nil return items, nil
} }
const UpdateCourse = `-- name: UpdateCourse :one const UpdateCourse = `-- name: UpdateCourse :exec
UPDATE courses UPDATE courses
SET SET
category_id = $2, title = COALESCE($1, title),
title = $3, description = COALESCE($2, description),
description = $4, is_active = COALESCE($3, is_active)
is_active = $5 WHERE id = $4
WHERE id = $1
RETURNING
id,
category_id,
title,
description,
is_active
` `
type UpdateCourseParams struct { type UpdateCourseParams struct {
ID int64 `json:"id"`
CategoryID int64 `json:"category_id"`
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdateCourse(ctx context.Context, arg UpdateCourseParams) (Course, error) { func (q *Queries) UpdateCourse(ctx context.Context, arg UpdateCourseParams) error {
row := q.db.QueryRow(ctx, UpdateCourse, _, err := q.db.Exec(ctx, UpdateCourse,
arg.ID,
arg.CategoryID,
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.IsActive, arg.IsActive,
arg.ID,
) )
var i Course return err
err := row.Scan(
&i.ID,
&i.CategoryID,
&i.Title,
&i.Description,
&i.IsActive,
)
return i, err
} }

View File

@ -0,0 +1,70 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: learning_tree.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const GetFullLearningTree = `-- name: GetFullLearningTree :many
SELECT
c.id AS course_id,
c.title AS course_title,
p.id AS program_id,
p.title AS program_title,
l.id AS level_id,
l.title AS level_title,
m.id AS module_id,
m.title AS module_title
FROM courses c
JOIN programs p ON p.course_id = c.id
JOIN levels l ON l.program_id = p.id
LEFT JOIN modules m ON m.level_id = l.id
WHERE c.is_active = true
ORDER BY p.display_order, l.level_index, m.display_order
`
type GetFullLearningTreeRow struct {
CourseID int64 `json:"course_id"`
CourseTitle string `json:"course_title"`
ProgramID int64 `json:"program_id"`
ProgramTitle string `json:"program_title"`
LevelID int64 `json:"level_id"`
LevelTitle string `json:"level_title"`
ModuleID pgtype.Int8 `json:"module_id"`
ModuleTitle pgtype.Text `json:"module_title"`
}
func (q *Queries) GetFullLearningTree(ctx context.Context) ([]GetFullLearningTreeRow, error) {
rows, err := q.db.Query(ctx, GetFullLearningTree)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetFullLearningTreeRow
for rows.Next() {
var i GetFullLearningTreeRow
if err := rows.Scan(
&i.CourseID,
&i.CourseTitle,
&i.ProgramID,
&i.ProgramTitle,
&i.LevelID,
&i.LevelTitle,
&i.ModuleID,
&i.ModuleTitle,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@ -19,28 +19,16 @@ INSERT INTO modules (
display_order, display_order,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, true))
$1, -- level_id RETURNING id, level_id, title, content, display_order, is_active
$2, -- title
$3, -- content
$4, -- display_order
$5 -- is_active
)
RETURNING
id,
level_id,
title,
content,
display_order,
is_active
` `
type CreateModuleParams struct { type CreateModuleParams struct {
LevelID int64 `json:"level_id"` LevelID int64 `json:"level_id"`
Title string `json:"title"` Title string `json:"title"`
Content pgtype.Text `json:"content"` Content pgtype.Text `json:"content"`
DisplayOrder int32 `json:"display_order"` Column4 interface{} `json:"column_4"`
IsActive bool `json:"is_active"` Column5 interface{} `json:"column_5"`
} }
func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Module, error) { func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Module, error) {
@ -48,8 +36,8 @@ func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Mod
arg.LevelID, arg.LevelID,
arg.Title, arg.Title,
arg.Content, arg.Content,
arg.DisplayOrder, arg.Column4,
arg.IsActive, arg.Column5,
) )
var i Module var i Module
err := row.Scan( err := row.Scan(
@ -63,45 +51,19 @@ func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Mod
return i, err return i, err
} }
const DeactivateModule = `-- name: DeactivateModule :exec const DeleteModule = `-- name: DeleteModule :exec
UPDATE modules DELETE FROM modules
SET is_active = FALSE
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) DeactivateModule(ctx context.Context, id int64) error { func (q *Queries) DeleteModule(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateModule, id) _, err := q.db.Exec(ctx, DeleteModule, id)
return err return err
} }
const GetModuleByID = `-- name: GetModuleByID :one const GetModulesByLevel = `-- name: GetModulesByLevel :many
SELECT
id,
level_id,
title,
content,
display_order,
is_active
FROM modules
WHERE id = $1
`
func (q *Queries) GetModuleByID(ctx context.Context, id int64) (Module, error) {
row := q.db.QueryRow(ctx, GetModuleByID, id)
var i Module
err := row.Scan(
&i.ID,
&i.LevelID,
&i.Title,
&i.Content,
&i.DisplayOrder,
&i.IsActive,
)
return i, err
}
const ListModulesByLevel = `-- name: ListModulesByLevel :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
level_id, level_id,
title, title,
@ -110,20 +72,30 @@ SELECT
is_active is_active
FROM modules FROM modules
WHERE level_id = $1 WHERE level_id = $1
AND is_active = TRUE ORDER BY display_order ASC
ORDER BY display_order ASC, id ASC
` `
func (q *Queries) ListModulesByLevel(ctx context.Context, levelID int64) ([]Module, error) { type GetModulesByLevelRow struct {
rows, err := q.db.Query(ctx, ListModulesByLevel, levelID) TotalCount int64 `json:"total_count"`
ID int64 `json:"id"`
LevelID int64 `json:"level_id"`
Title string `json:"title"`
Content pgtype.Text `json:"content"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
}
func (q *Queries) GetModulesByLevel(ctx context.Context, levelID int64) ([]GetModulesByLevelRow, error) {
rows, err := q.db.Query(ctx, GetModulesByLevel, levelID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []Module var items []GetModulesByLevelRow
for rows.Next() { for rows.Next() {
var i Module var i GetModulesByLevelRow
if err := rows.Scan( if err := rows.Scan(
&i.TotalCount,
&i.ID, &i.ID,
&i.LevelID, &i.LevelID,
&i.Title, &i.Title,
@ -141,47 +113,31 @@ func (q *Queries) ListModulesByLevel(ctx context.Context, levelID int64) ([]Modu
return items, nil return items, nil
} }
const UpdateModule = `-- name: UpdateModule :one const UpdateModule = `-- name: UpdateModule :exec
UPDATE modules UPDATE modules
SET SET
title = $2, title = COALESCE($1, title),
content = $3, content = COALESCE($2, content),
display_order = $4, display_order = COALESCE($3, display_order),
is_active = $5 is_active = COALESCE($4, is_active)
WHERE id = $1 WHERE id = $5
RETURNING
id,
level_id,
title,
content,
display_order,
is_active
` `
type UpdateModuleParams struct { type UpdateModuleParams struct {
ID int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Content pgtype.Text `json:"content"` Content pgtype.Text `json:"content"`
DisplayOrder int32 `json:"display_order"` DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdateModule(ctx context.Context, arg UpdateModuleParams) (Module, error) { func (q *Queries) UpdateModule(ctx context.Context, arg UpdateModuleParams) error {
row := q.db.QueryRow(ctx, UpdateModule, _, err := q.db.Exec(ctx, UpdateModule,
arg.ID,
arg.Title, arg.Title,
arg.Content, arg.Content,
arg.DisplayOrder, arg.DisplayOrder,
arg.IsActive, arg.IsActive,
arg.ID,
) )
var i Module return err
err := row.Scan(
&i.ID,
&i.LevelID,
&i.Title,
&i.Content,
&i.DisplayOrder,
&i.IsActive,
)
return i, err
} }

View File

@ -19,45 +19,17 @@ INSERT INTO module_videos (
video_url, video_url,
duration, duration,
resolution, resolution,
is_published,
publish_date,
visibility,
instructor_id, instructor_id,
thumbnail, thumbnail,
visibility,
is_active is_active
) )
VALUES ( VALUES (
$1, -- module_id $1, $2, $3, $4, $5, $6,
$2, -- title $7, $8, $9,
$3, -- description COALESCE($10, true)
$4, -- video_url
$5, -- duration
$6, -- resolution
$7, -- is_published
$8, -- publish_date
$9, -- visibility
$10, -- instructor_id
$11, -- thumbnail
$12 -- is_active
) )
RETURNING RETURNING id, module_id, title, description, video_url, duration, resolution, is_published, publish_date, visibility, instructor_id, thumbnail, is_active
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active
` `
type CreateModuleVideoParams struct { type CreateModuleVideoParams struct {
@ -67,12 +39,10 @@ type CreateModuleVideoParams struct {
VideoUrl string `json:"video_url"` VideoUrl string `json:"video_url"`
Duration int32 `json:"duration"` Duration int32 `json:"duration"`
Resolution pgtype.Text `json:"resolution"` Resolution pgtype.Text `json:"resolution"`
IsPublished bool `json:"is_published"`
PublishDate pgtype.Timestamptz `json:"publish_date"`
Visibility pgtype.Text `json:"visibility"`
InstructorID pgtype.Text `json:"instructor_id"` InstructorID pgtype.Text `json:"instructor_id"`
Thumbnail pgtype.Text `json:"thumbnail"` Thumbnail pgtype.Text `json:"thumbnail"`
IsActive bool `json:"is_active"` Visibility pgtype.Text `json:"visibility"`
Column10 interface{} `json:"column_10"`
} }
func (q *Queries) CreateModuleVideo(ctx context.Context, arg CreateModuleVideoParams) (ModuleVideo, error) { func (q *Queries) CreateModuleVideo(ctx context.Context, arg CreateModuleVideoParams) (ModuleVideo, error) {
@ -83,12 +53,10 @@ func (q *Queries) CreateModuleVideo(ctx context.Context, arg CreateModuleVideoPa
arg.VideoUrl, arg.VideoUrl,
arg.Duration, arg.Duration,
arg.Resolution, arg.Resolution,
arg.IsPublished,
arg.PublishDate,
arg.Visibility,
arg.InstructorID, arg.InstructorID,
arg.Thumbnail, arg.Thumbnail,
arg.IsActive, arg.Visibility,
arg.Column10,
) )
var i ModuleVideo var i ModuleVideo
err := row.Scan( err := row.Scan(
@ -109,79 +77,27 @@ func (q *Queries) CreateModuleVideo(ctx context.Context, arg CreateModuleVideoPa
return i, err return i, err
} }
const DeactivateModuleVideo = `-- name: DeactivateModuleVideo :exec const DeleteModuleVideo = `-- name: DeleteModuleVideo :exec
UPDATE module_videos DELETE FROM module_videos
SET is_active = FALSE
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) DeactivateModuleVideo(ctx context.Context, id int64) error { func (q *Queries) DeleteModuleVideo(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateModuleVideo, id) _, err := q.db.Exec(ctx, DeleteModuleVideo, id)
return err return err
} }
const GetModuleVideoByID = `-- name: GetModuleVideoByID :one const GetPublishedVideosByModule = `-- name: GetPublishedVideosByModule :many
SELECT SELECT id, module_id, title, description, video_url, duration, resolution, is_published, publish_date, visibility, instructor_id, thumbnail, is_active
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active
FROM module_videos
WHERE id = $1
`
func (q *Queries) GetModuleVideoByID(ctx context.Context, id int64) (ModuleVideo, error) {
row := q.db.QueryRow(ctx, GetModuleVideoByID, id)
var i ModuleVideo
err := row.Scan(
&i.ID,
&i.ModuleID,
&i.Title,
&i.Description,
&i.VideoUrl,
&i.Duration,
&i.Resolution,
&i.IsPublished,
&i.PublishDate,
&i.Visibility,
&i.InstructorID,
&i.Thumbnail,
&i.IsActive,
)
return i, err
}
const ListAllVideosByModule = `-- name: ListAllVideosByModule :many
SELECT
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active
FROM module_videos FROM module_videos
WHERE module_id = $1 WHERE module_id = $1
ORDER BY id ASC AND is_published = true
AND is_active = true
ORDER BY publish_date ASC
` `
func (q *Queries) ListAllVideosByModule(ctx context.Context, moduleID int64) ([]ModuleVideo, error) { func (q *Queries) GetPublishedVideosByModule(ctx context.Context, moduleID int64) ([]ModuleVideo, error) {
rows, err := q.db.Query(ctx, ListAllVideosByModule, moduleID) rows, err := q.db.Query(ctx, GetPublishedVideosByModule, moduleID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -214,150 +130,56 @@ func (q *Queries) ListAllVideosByModule(ctx context.Context, moduleID int64) ([]
return items, nil return items, nil
} }
const ListPublishedVideosByModule = `-- name: ListPublishedVideosByModule :many const PublishModuleVideo = `-- name: PublishModuleVideo :exec
SELECT
id,
module_id,
title,
description,
video_url,
duration,
resolution,
publish_date,
visibility,
instructor_id,
thumbnail
FROM module_videos
WHERE module_id = $1
AND is_active = TRUE
AND is_published = TRUE
ORDER BY publish_date ASC, id ASC
`
type ListPublishedVideosByModuleRow struct {
ID int64 `json:"id"`
ModuleID int64 `json:"module_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
VideoUrl string `json:"video_url"`
Duration int32 `json:"duration"`
Resolution pgtype.Text `json:"resolution"`
PublishDate pgtype.Timestamptz `json:"publish_date"`
Visibility pgtype.Text `json:"visibility"`
InstructorID pgtype.Text `json:"instructor_id"`
Thumbnail pgtype.Text `json:"thumbnail"`
}
func (q *Queries) ListPublishedVideosByModule(ctx context.Context, moduleID int64) ([]ListPublishedVideosByModuleRow, error) {
rows, err := q.db.Query(ctx, ListPublishedVideosByModule, moduleID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListPublishedVideosByModuleRow
for rows.Next() {
var i ListPublishedVideosByModuleRow
if err := rows.Scan(
&i.ID,
&i.ModuleID,
&i.Title,
&i.Description,
&i.VideoUrl,
&i.Duration,
&i.Resolution,
&i.PublishDate,
&i.Visibility,
&i.InstructorID,
&i.Thumbnail,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateModuleVideo = `-- name: UpdateModuleVideo :one
UPDATE module_videos UPDATE module_videos
SET SET
title = $2, is_published = true,
description = $3, publish_date = CURRENT_TIMESTAMP
video_url = $4,
duration = $5,
resolution = $6,
is_published = $7,
publish_date = $8,
visibility = $9,
instructor_id = $10,
thumbnail = $11,
is_active = $12
WHERE id = $1 WHERE id = $1
RETURNING `
id,
module_id, func (q *Queries) PublishModuleVideo(ctx context.Context, id int64) error {
title, _, err := q.db.Exec(ctx, PublishModuleVideo, id)
description, return err
video_url, }
duration,
resolution, const UpdateModuleVideo = `-- name: UpdateModuleVideo :exec
is_published, UPDATE module_videos
publish_date, SET
visibility, title = COALESCE($1, title),
instructor_id, description = COALESCE($2, description),
thumbnail, video_url = COALESCE($3, video_url),
is_active duration = COALESCE($4, duration),
resolution = COALESCE($5, resolution),
visibility = COALESCE($6, visibility),
thumbnail = COALESCE($7, thumbnail),
is_active = COALESCE($8, is_active)
WHERE id = $9
` `
type UpdateModuleVideoParams struct { type UpdateModuleVideoParams struct {
ID int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
VideoUrl string `json:"video_url"` VideoUrl string `json:"video_url"`
Duration int32 `json:"duration"` Duration int32 `json:"duration"`
Resolution pgtype.Text `json:"resolution"` Resolution pgtype.Text `json:"resolution"`
IsPublished bool `json:"is_published"`
PublishDate pgtype.Timestamptz `json:"publish_date"`
Visibility pgtype.Text `json:"visibility"` Visibility pgtype.Text `json:"visibility"`
InstructorID pgtype.Text `json:"instructor_id"`
Thumbnail pgtype.Text `json:"thumbnail"` Thumbnail pgtype.Text `json:"thumbnail"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdateModuleVideo(ctx context.Context, arg UpdateModuleVideoParams) (ModuleVideo, error) { func (q *Queries) UpdateModuleVideo(ctx context.Context, arg UpdateModuleVideoParams) error {
row := q.db.QueryRow(ctx, UpdateModuleVideo, _, err := q.db.Exec(ctx, UpdateModuleVideo,
arg.ID,
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.VideoUrl, arg.VideoUrl,
arg.Duration, arg.Duration,
arg.Resolution, arg.Resolution,
arg.IsPublished,
arg.PublishDate,
arg.Visibility, arg.Visibility,
arg.InstructorID,
arg.Thumbnail, arg.Thumbnail,
arg.IsActive, arg.IsActive,
arg.ID,
) )
var i ModuleVideo return err
err := row.Scan(
&i.ID,
&i.ModuleID,
&i.Title,
&i.Description,
&i.VideoUrl,
&i.Duration,
&i.Resolution,
&i.IsPublished,
&i.PublishDate,
&i.Visibility,
&i.InstructorID,
&i.Thumbnail,
&i.IsActive,
)
return i, err
} }

View File

@ -21,24 +21,8 @@ INSERT INTO practice_questions (
tips, tips,
type type
) )
VALUES ( VALUES ($1, $2, $3, $4, $5, $6, $7)
$1, -- practice_id RETURNING id, practice_id, question, question_voice_prompt, sample_answer_voice_prompt, sample_answer, tips, type
$2, -- question
$3, -- question_voice_prompt
$4, -- sample_answer_voice_prompt
$5, -- sample_answer
$6, -- tips
$7 -- type (MCQ, TRUE_FALSE, SHORT)
)
RETURNING
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
` `
type CreatePracticeQuestionParams struct { type CreatePracticeQuestionParams struct {
@ -85,53 +69,15 @@ func (q *Queries) DeletePracticeQuestion(ctx context.Context, id int64) error {
return err return err
} }
const GetPracticeQuestionByID = `-- name: GetPracticeQuestionByID :one const GetQuestionsByPractice = `-- name: GetQuestionsByPractice :many
SELECT SELECT id, practice_id, question, question_voice_prompt, sample_answer_voice_prompt, sample_answer, tips, type
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
FROM practice_questions
WHERE id = $1
`
func (q *Queries) GetPracticeQuestionByID(ctx context.Context, id int64) (PracticeQuestion, error) {
row := q.db.QueryRow(ctx, GetPracticeQuestionByID, id)
var i PracticeQuestion
err := row.Scan(
&i.ID,
&i.PracticeID,
&i.Question,
&i.QuestionVoicePrompt,
&i.SampleAnswerVoicePrompt,
&i.SampleAnswer,
&i.Tips,
&i.Type,
)
return i, err
}
const ListPracticeQuestions = `-- name: ListPracticeQuestions :many
SELECT
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
FROM practice_questions FROM practice_questions
WHERE practice_id = $1 WHERE practice_id = $1
ORDER BY id ASC ORDER BY id ASC
` `
func (q *Queries) ListPracticeQuestions(ctx context.Context, practiceID int64) ([]PracticeQuestion, error) { func (q *Queries) GetQuestionsByPractice(ctx context.Context, practiceID int64) ([]PracticeQuestion, error) {
rows, err := q.db.Query(ctx, ListPracticeQuestions, practiceID) rows, err := q.db.Query(ctx, GetQuestionsByPractice, practiceID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -159,57 +105,31 @@ func (q *Queries) ListPracticeQuestions(ctx context.Context, practiceID int64) (
return items, nil return items, nil
} }
const UpdatePracticeQuestion = `-- name: UpdatePracticeQuestion :one const UpdatePracticeQuestion = `-- name: UpdatePracticeQuestion :exec
UPDATE practice_questions UPDATE practice_questions
SET SET
question = $2, question = COALESCE($1, question),
question_voice_prompt = $3, sample_answer = COALESCE($2, sample_answer),
sample_answer_voice_prompt = $4, tips = COALESCE($3, tips),
sample_answer = $5, type = COALESCE($4, type)
tips = $6, WHERE id = $5
type = $7
WHERE id = $1
RETURNING
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
` `
type UpdatePracticeQuestionParams struct { type UpdatePracticeQuestionParams struct {
ID int64 `json:"id"`
Question string `json:"question"` Question string `json:"question"`
QuestionVoicePrompt pgtype.Text `json:"question_voice_prompt"`
SampleAnswerVoicePrompt pgtype.Text `json:"sample_answer_voice_prompt"`
SampleAnswer pgtype.Text `json:"sample_answer"` SampleAnswer pgtype.Text `json:"sample_answer"`
Tips pgtype.Text `json:"tips"` Tips pgtype.Text `json:"tips"`
Type string `json:"type"` Type string `json:"type"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdatePracticeQuestion(ctx context.Context, arg UpdatePracticeQuestionParams) (PracticeQuestion, error) { func (q *Queries) UpdatePracticeQuestion(ctx context.Context, arg UpdatePracticeQuestionParams) error {
row := q.db.QueryRow(ctx, UpdatePracticeQuestion, _, err := q.db.Exec(ctx, UpdatePracticeQuestion,
arg.ID,
arg.Question, arg.Question,
arg.QuestionVoicePrompt,
arg.SampleAnswerVoicePrompt,
arg.SampleAnswer, arg.SampleAnswer,
arg.Tips, arg.Tips,
arg.Type, arg.Type,
arg.ID,
) )
var i PracticeQuestion return err
err := row.Scan(
&i.ID,
&i.PracticeID,
&i.Question,
&i.QuestionVoicePrompt,
&i.SampleAnswerVoicePrompt,
&i.SampleAnswer,
&i.Tips,
&i.Type,
)
return i, err
} }

View File

@ -21,24 +21,8 @@ INSERT INTO practices (
persona, persona,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, true))
$1, -- owner_type (LEVEL | MODULE) RETURNING id, owner_type, owner_id, title, description, banner_image, persona, is_active
$2, -- owner_id
$3, -- title
$4, -- description
$5, -- banner_image
$6, -- persona
$7 -- is_active
)
RETURNING
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active
` `
type CreatePracticeParams struct { type CreatePracticeParams struct {
@ -48,7 +32,7 @@ type CreatePracticeParams struct {
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
BannerImage pgtype.Text `json:"banner_image"` BannerImage pgtype.Text `json:"banner_image"`
Persona pgtype.Text `json:"persona"` Persona pgtype.Text `json:"persona"`
IsActive bool `json:"is_active"` Column7 interface{} `json:"column_7"`
} }
func (q *Queries) CreatePractice(ctx context.Context, arg CreatePracticeParams) (Practice, error) { func (q *Queries) CreatePractice(ctx context.Context, arg CreatePracticeParams) (Practice, error) {
@ -59,7 +43,7 @@ func (q *Queries) CreatePractice(ctx context.Context, arg CreatePracticeParams)
arg.Description, arg.Description,
arg.BannerImage, arg.BannerImage,
arg.Persona, arg.Persona,
arg.IsActive, arg.Column7,
) )
var i Practice var i Practice
err := row.Scan( err := row.Scan(
@ -75,71 +59,31 @@ func (q *Queries) CreatePractice(ctx context.Context, arg CreatePracticeParams)
return i, err return i, err
} }
const DeactivatePractice = `-- name: DeactivatePractice :exec const DeletePractice = `-- name: DeletePractice :exec
UPDATE practices DELETE FROM practices
SET is_active = FALSE
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) DeactivatePractice(ctx context.Context, id int64) error { func (q *Queries) DeletePractice(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivatePractice, id) _, err := q.db.Exec(ctx, DeletePractice, id)
return err return err
} }
const GetPracticeByID = `-- name: GetPracticeByID :one const GetPracticesByOwner = `-- name: GetPracticesByOwner :many
SELECT SELECT id, owner_type, owner_id, title, description, banner_image, persona, is_active
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active
FROM practices
WHERE id = $1
`
func (q *Queries) GetPracticeByID(ctx context.Context, id int64) (Practice, error) {
row := q.db.QueryRow(ctx, GetPracticeByID, id)
var i Practice
err := row.Scan(
&i.ID,
&i.OwnerType,
&i.OwnerID,
&i.Title,
&i.Description,
&i.BannerImage,
&i.Persona,
&i.IsActive,
)
return i, err
}
const ListPracticesByOwner = `-- name: ListPracticesByOwner :many
SELECT
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active
FROM practices FROM practices
WHERE owner_type = $1 WHERE owner_type = $1
AND owner_id = $2 AND owner_id = $2
AND is_active = TRUE AND is_active = true
ORDER BY id ASC
` `
type ListPracticesByOwnerParams struct { type GetPracticesByOwnerParams struct {
OwnerType string `json:"owner_type"` OwnerType string `json:"owner_type"`
OwnerID int64 `json:"owner_id"` OwnerID int64 `json:"owner_id"`
} }
func (q *Queries) ListPracticesByOwner(ctx context.Context, arg ListPracticesByOwnerParams) ([]Practice, error) { func (q *Queries) GetPracticesByOwner(ctx context.Context, arg GetPracticesByOwnerParams) ([]Practice, error) {
rows, err := q.db.Query(ctx, ListPracticesByOwner, arg.OwnerType, arg.OwnerID) rows, err := q.db.Query(ctx, GetPracticesByOwner, arg.OwnerType, arg.OwnerID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -167,54 +111,34 @@ func (q *Queries) ListPracticesByOwner(ctx context.Context, arg ListPracticesByO
return items, nil return items, nil
} }
const UpdatePractice = `-- name: UpdatePractice :one const UpdatePractice = `-- name: UpdatePractice :exec
UPDATE practices UPDATE practices
SET SET
title = $2, title = COALESCE($1, title),
description = $3, description = COALESCE($2, description),
banner_image = $4, banner_image = COALESCE($3, banner_image),
persona = $5, persona = COALESCE($4, persona),
is_active = $6 is_active = COALESCE($5, is_active)
WHERE id = $1 WHERE id = $6
RETURNING
id,
owner_type,
owner_id,
title,
description,
banner_image,
persona,
is_active
` `
type UpdatePracticeParams struct { type UpdatePracticeParams struct {
ID int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
BannerImage pgtype.Text `json:"banner_image"` BannerImage pgtype.Text `json:"banner_image"`
Persona pgtype.Text `json:"persona"` Persona pgtype.Text `json:"persona"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdatePractice(ctx context.Context, arg UpdatePracticeParams) (Practice, error) { func (q *Queries) UpdatePractice(ctx context.Context, arg UpdatePracticeParams) error {
row := q.db.QueryRow(ctx, UpdatePractice, _, err := q.db.Exec(ctx, UpdatePractice,
arg.ID,
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.BannerImage, arg.BannerImage,
arg.Persona, arg.Persona,
arg.IsActive, arg.IsActive,
arg.ID,
) )
var i Practice return err
err := row.Scan(
&i.ID,
&i.OwnerType,
&i.OwnerID,
&i.Title,
&i.Description,
&i.BannerImage,
&i.Persona,
&i.IsActive,
)
return i, err
} }

View File

@ -17,31 +17,10 @@ INSERT INTO levels (
title, title,
description, description,
level_index, level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active is_active
) )
VALUES ( VALUES ($1, $2, $3, $4, COALESCE($5, true))
$1, -- program_id RETURNING id, program_id, title, description, level_index, number_of_modules, number_of_practices, number_of_videos, is_active
$2, -- title
$3, -- description
$4, -- level_index
$5, -- number_of_modules
$6, -- number_of_practices
$7, -- number_of_videos
$8 -- is_active
)
RETURNING
id,
program_id,
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active
` `
type CreateLevelParams struct { type CreateLevelParams struct {
@ -49,10 +28,7 @@ type CreateLevelParams struct {
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
LevelIndex int32 `json:"level_index"` LevelIndex int32 `json:"level_index"`
NumberOfModules int32 `json:"number_of_modules"` Column5 interface{} `json:"column_5"`
NumberOfPractices int32 `json:"number_of_practices"`
NumberOfVideos int32 `json:"number_of_videos"`
IsActive bool `json:"is_active"`
} }
func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level, error) { func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level, error) {
@ -61,10 +37,7 @@ func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.LevelIndex, arg.LevelIndex,
arg.NumberOfModules, arg.Column5,
arg.NumberOfPractices,
arg.NumberOfVideos,
arg.IsActive,
) )
var i Level var i Level
err := row.Scan( err := row.Scan(
@ -81,51 +54,19 @@ func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level
return i, err return i, err
} }
const DeactivateLevel = `-- name: DeactivateLevel :exec const DeleteLevel = `-- name: DeleteLevel :exec
UPDATE levels DELETE FROM levels
SET is_active = FALSE
WHERE id = $1 WHERE id = $1
` `
func (q *Queries) DeactivateLevel(ctx context.Context, id int64) error { func (q *Queries) DeleteLevel(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateLevel, id) _, err := q.db.Exec(ctx, DeleteLevel, id)
return err return err
} }
const GetLevelByID = `-- name: GetLevelByID :one const GetLevelsByProgram = `-- name: GetLevelsByProgram :many
SELECT
id,
program_id,
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active
FROM levels
WHERE id = $1
`
func (q *Queries) GetLevelByID(ctx context.Context, id int64) (Level, error) {
row := q.db.QueryRow(ctx, GetLevelByID, id)
var i Level
err := row.Scan(
&i.ID,
&i.ProgramID,
&i.Title,
&i.Description,
&i.LevelIndex,
&i.NumberOfModules,
&i.NumberOfPractices,
&i.NumberOfVideos,
&i.IsActive,
)
return i, err
}
const ListLevelsByProgram = `-- name: ListLevelsByProgram :many
SELECT SELECT
COUNT(*) OVER () AS total_count,
id, id,
program_id, program_id,
title, title,
@ -137,20 +78,33 @@ SELECT
is_active is_active
FROM levels FROM levels
WHERE program_id = $1 WHERE program_id = $1
AND is_active = TRUE
ORDER BY level_index ASC ORDER BY level_index ASC
` `
func (q *Queries) ListLevelsByProgram(ctx context.Context, programID int64) ([]Level, error) { type GetLevelsByProgramRow struct {
rows, err := q.db.Query(ctx, ListLevelsByProgram, programID) TotalCount int64 `json:"total_count"`
ID int64 `json:"id"`
ProgramID int64 `json:"program_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
LevelIndex int32 `json:"level_index"`
NumberOfModules int32 `json:"number_of_modules"`
NumberOfPractices int32 `json:"number_of_practices"`
NumberOfVideos int32 `json:"number_of_videos"`
IsActive bool `json:"is_active"`
}
func (q *Queries) GetLevelsByProgram(ctx context.Context, programID int64) ([]GetLevelsByProgramRow, error) {
rows, err := q.db.Query(ctx, GetLevelsByProgram, programID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
var items []Level var items []GetLevelsByProgramRow
for rows.Next() { for rows.Next() {
var i Level var i GetLevelsByProgramRow
if err := rows.Scan( if err := rows.Scan(
&i.TotalCount,
&i.ID, &i.ID,
&i.ProgramID, &i.ProgramID,
&i.Title, &i.Title,
@ -171,62 +125,64 @@ func (q *Queries) ListLevelsByProgram(ctx context.Context, programID int64) ([]L
return items, nil return items, nil
} }
const UpdateLevel = `-- name: UpdateLevel :one const IncrementLevelModuleCount = `-- name: IncrementLevelModuleCount :exec
UPDATE levels
SET number_of_modules = number_of_modules + 1
WHERE id = $1
`
func (q *Queries) IncrementLevelModuleCount(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, IncrementLevelModuleCount, id)
return err
}
const IncrementLevelPracticeCount = `-- name: IncrementLevelPracticeCount :exec
UPDATE levels
SET number_of_practices = number_of_practices + 1
WHERE id = $1
`
func (q *Queries) IncrementLevelPracticeCount(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, IncrementLevelPracticeCount, id)
return err
}
const IncrementLevelVideoCount = `-- name: IncrementLevelVideoCount :exec
UPDATE levels
SET number_of_videos = number_of_videos + 1
WHERE id = $1
`
func (q *Queries) IncrementLevelVideoCount(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, IncrementLevelVideoCount, id)
return err
}
const UpdateLevel = `-- name: UpdateLevel :exec
UPDATE levels UPDATE levels
SET SET
title = $2, title = COALESCE($1, title),
description = $3, description = COALESCE($2, description),
level_index = $4, level_index = COALESCE($3, level_index),
number_of_modules = $5, is_active = COALESCE($4, is_active)
number_of_practices = $6, WHERE id = $5
number_of_videos = $7,
is_active = $8
WHERE id = $1
RETURNING
id,
program_id,
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active
` `
type UpdateLevelParams struct { type UpdateLevelParams struct {
ID int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Description pgtype.Text `json:"description"` Description pgtype.Text `json:"description"`
LevelIndex int32 `json:"level_index"` LevelIndex int32 `json:"level_index"`
NumberOfModules int32 `json:"number_of_modules"`
NumberOfPractices int32 `json:"number_of_practices"`
NumberOfVideos int32 `json:"number_of_videos"`
IsActive bool `json:"is_active"` IsActive bool `json:"is_active"`
ID int64 `json:"id"`
} }
func (q *Queries) UpdateLevel(ctx context.Context, arg UpdateLevelParams) (Level, error) { func (q *Queries) UpdateLevel(ctx context.Context, arg UpdateLevelParams) error {
row := q.db.QueryRow(ctx, UpdateLevel, _, err := q.db.Exec(ctx, UpdateLevel,
arg.ID,
arg.Title, arg.Title,
arg.Description, arg.Description,
arg.LevelIndex, arg.LevelIndex,
arg.NumberOfModules,
arg.NumberOfPractices,
arg.NumberOfVideos,
arg.IsActive, arg.IsActive,
arg.ID,
) )
var i Level return err
err := row.Scan(
&i.ID,
&i.ProgramID,
&i.Title,
&i.Description,
&i.LevelIndex,
&i.NumberOfModules,
&i.NumberOfPractices,
&i.NumberOfVideos,
&i.IsActive,
)
return i, err
} }

View File

@ -0,0 +1,110 @@
package domain
import "time"
type TreeModule struct {
ID int64
Title string
}
type TreeLevel struct {
ID int64
Title string
Modules []TreeModule
}
type TreeProgram struct {
ID int64
Title string
Levels []TreeLevel
}
type TreeCourse struct {
ID int64
Title string
Programs []TreeProgram
}
type CourseCategory struct {
ID int64
Name string
IsActive bool
CreatedAt time.Time
}
type Program struct {
ID int64
CourseID int64
Title string
Description *string
Thumbnail *string
DisplayOrder int32
IsActive bool
}
type Course struct {
ID int64
CategoryID int64
Title string
Description *string
IsActive bool
}
type Module struct {
ID int64
LevelID int64
Title string
Content *string
DisplayOrder int32
IsActive bool
}
type ModuleVideo struct {
ID int64
ModuleID int64
Title string
Description *string
VideoURL string
Duration int32
Resolution *string
InstructorID *string
Thumbnail *string
Visibility *string
IsPublished bool
PublishDate *time.Time
IsActive bool
}
type PracticeQuestion struct {
ID int64
PracticeID int64
Question string
QuestionVoicePrompt *string
SampleAnswerVoicePrompt *string
SampleAnswer *string
Tips *string
Type string
}
type Practice struct {
ID int64
OwnerType string
OwnerID int64
Title string
Description *string
BannerImage *string
Persona *string
IsActive bool
}
type Level struct {
ID int64
ProgramID int64
Title string
Description *string
LevelIndex int
NumberOfModules int
NumberOfPractices int
NumberOfVideos int
IsActive bool
}

View File

@ -1,91 +0,0 @@
package domain
import "time"
type CourseCategory struct {
ID int64
Name string // "Learning English", "Other Courses"
IsActive bool
CreatedAt time.Time
}
type Course struct {
ID int64
CategoryID int64
Title string
Description string
IsActive bool
}
type Program struct {
ID int64
CourseID int64
Title string
Description string
Thumbnail string
Order int // ordering inside course
IsActive bool
}
type Level struct {
ID int64
ProgramID int64
Title string // "Beginner", "Level 1"
Description string
LevelIndex int // 1,2,3...
NumberOfModules int
NumberOfPractices int
NumberOfVideos int
IsActive bool
}
type Module struct {
ID int64
LevelID int64
Title string
Content string
Order int
IsActive bool
}
type ModuleVideo struct {
ID int64
ModuleID int64
Title string
Description string
VideoURL string
Duration int // seconds
Resolution string // "720p", "1080p"
PublishSettings PublishSettings
IsActive bool
InstructorId string
Thumbnail string
}
type PublishSettings struct {
IsPublished bool
PublishDate time.Time
Visibility string // "public", "private", "unlisted"
}
type Practice struct {
ID int64
OwnerType string // "LEVEL" | "MODULE"
OwnerID int64
Title string
Description string
BannerImage string
Persona string
IsActive bool
}
type PracticeQuestion struct {
ID int64
PracticeID int64
Question string
QuestionVoicePrompt string
SampleAnswerVoicePrompt string
SampleAnswer string
Tips string
Type string // MCQ, TRUE_FALSE, SHORT
}

View File

@ -1,60 +1,253 @@
package ports package ports
import ( import (
"context"
"Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/domain"
"context"
) )
type CourseStore interface { type CourseStore interface {
CreateCourseCategory(ctx context.Context, name string) (domain.CourseCategory, error) CreateCourseCategory(
GetCourseCategoryByID(ctx context.Context, Id int64) (domain.CourseCategory, error) ctx context.Context,
ListActiveCourseCategories(ctx context.Context) ([]domain.CourseCategory, error) name string,
UpdateCourseCategory(ctx context.Context, id int64, name string, isActive bool) (domain.CourseCategory, error) ) (domain.CourseCategory, error)
DeactivateCourseCategory(ctx context.Context, id int64) error GetCourseCategoryByID(
ctx context.Context,
CreateCourse(ctx context.Context, c domain.Course) (domain.Course, error) id int64,
GetCourseByID(ctx context.Context, id int64) (domain.Course, error) ) (domain.CourseCategory, error)
ListCoursesByCategory(ctx context.Context, categoryID int64) ([]domain.Course, error) GetAllCourseCategories(
ListActiveCourses(ctx context.Context) ([]domain.Course, error) ctx context.Context,
UpdateCourse(ctx context.Context, c domain.Course) (domain.Course, error) limit int32,
DeactivateCourse(ctx context.Context, id int64) error offset int32,
) ([]domain.CourseCategory, int64, error)
CreateProgram(ctx context.Context, p domain.Program) (domain.Program, error) UpdateCourseCategory(
GetProgramByID(ctx context.Context, id int64) (domain.Program, error) ctx context.Context,
ListProgramsByCourse(ctx context.Context, courseID int64) ([]domain.Program, error) id int64,
ListActivePrograms(ctx context.Context) ([]domain.Program, error) name *string,
UpdateProgram(ctx context.Context, p domain.Program) (domain.Program, error) isActive *bool,
DeactivateProgram(ctx context.Context, id int64) error ) error
DeleteCourseCategory(
CreateModule(ctx context.Context, m domain.Module) (domain.Module, error) ctx context.Context,
GetModuleByID(ctx context.Context, id int64) (domain.Module, error) id int64,
ListModulesByLevel(ctx context.Context, levelID int64) ([]domain.Module, error) ) error
UpdateModule(ctx context.Context, m domain.Module) (domain.Module, error) CreateProgram(
DeactivateModule(ctx context.Context, id int64) error ctx context.Context,
courseID int64,
CreateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error) title string,
GetModuleVideoByID(ctx context.Context, id int64) (domain.ModuleVideo, error) description *string,
ListAllVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error) thumbnail *string,
ListPublishedVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error) displayOrder *int32,
UpdateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error) ) (domain.Program, error)
DeactivateModuleVideo(ctx context.Context, id int64) error GetProgramByID(
ctx context.Context,
CreatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error) id int64,
GetPracticeByID(ctx context.Context, id int64) (domain.Practice, error) ) (domain.Program, error)
ListPracticesByOwner(ctx context.Context, ownerType string, ownerID int64) ([]domain.Practice, error) GetProgramsByCourse(
UpdatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error) ctx context.Context,
DeactivatePractice(ctx context.Context, id int64) error courseID int64,
) ([]domain.Program, int64, error)
CreatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error) ListProgramsByCourse(
GetPracticeQuestionByID(ctx context.Context, id int64) (domain.PracticeQuestion, error) ctx context.Context,
ListPracticeQuestions(ctx context.Context, practiceID int64) ([]domain.PracticeQuestion, error) courseID int64,
UpdatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error) ) ([]domain.Program, error)
DeletePracticeQuestion(ctx context.Context, id int64) error ListActivePrograms(
ctx context.Context,
CreateLevel(ctx context.Context, l domain.Level) (domain.Level, error) ) ([]domain.Program, error)
GetLevelByID(ctx context.Context, id int64) (domain.Level, error) UpdateProgramPartial(
ListLevelsByProgram(ctx context.Context, programID int64) ([]domain.Level, error) ctx context.Context,
UpdateLevel(ctx context.Context, l domain.Level) (domain.Level, error) id int64,
DeactivateLevel(ctx context.Context, id int64) error title *string,
description *string,
thumbnail *string,
displayOrder *int32,
isActive *bool,
) error
UpdateProgramFull(
ctx context.Context,
program domain.Program,
) (domain.Program, error)
DeactivateProgram(
ctx context.Context,
id int64,
) error
DeleteProgram(
ctx context.Context,
id int64,
) (domain.Program, error)
CreateCourse(
ctx context.Context,
categoryID int64,
title string,
description *string,
) (domain.Course, error)
GetCourseByID(
ctx context.Context,
id int64,
) (domain.Course, error)
GetCoursesByCategory(
ctx context.Context,
categoryID int64,
limit int32,
offset int32,
) ([]domain.Course, int64, error)
UpdateCourse(
ctx context.Context,
id int64,
title *string,
description *string,
isActive *bool,
) error
DeleteCourse(
ctx context.Context,
id int64,
) error
CreateModule(
ctx context.Context,
levelID int64,
title string,
content *string,
displayOrder *int32,
) (domain.Module, error)
GetModulesByLevel(
ctx context.Context,
levelID int64,
) ([]domain.Module, int64, error)
UpdateModule(
ctx context.Context,
id int64,
title *string,
content *string,
displayOrder *int32,
isActive *bool,
) error
DeleteModule(
ctx context.Context,
id int64,
) error
CreateModuleVideo(
ctx context.Context,
moduleID int64,
title string,
description *string,
videoURL string,
duration int32,
resolution *string,
instructorID *string,
thumbnail *string,
visibility *string,
) (domain.ModuleVideo, error)
PublishModuleVideo(
ctx context.Context,
videoID int64,
) error
GetPublishedVideosByModule(
ctx context.Context,
moduleID int64,
) ([]domain.ModuleVideo, error)
UpdateModuleVideo(
ctx context.Context,
id int64,
title *string,
description *string,
videoURL *string,
duration *int32,
resolution *string,
visibility *string,
thumbnail *string,
isActive *bool,
) error
DeleteModuleVideo(
ctx context.Context,
id int64,
) error
CreatePracticeQuestion(
ctx context.Context,
practiceID int64,
question string,
questionVoicePrompt *string,
sampleAnswerVoicePrompt *string,
sampleAnswer *string,
tips *string,
qType string,
) (domain.PracticeQuestion, error)
GetQuestionsByPractice(
ctx context.Context,
practiceID int64,
) ([]domain.PracticeQuestion, error)
UpdatePracticeQuestion(
ctx context.Context,
id int64,
question *string,
sampleAnswer *string,
tips *string,
qType *string,
) error
DeletePracticeQuestion(
ctx context.Context,
id int64,
) error
CreatePractice(
ctx context.Context,
ownerType string,
ownerID int64,
title string,
description *string,
bannerImage *string,
persona *string,
isActive *bool,
) (domain.Practice, error)
GetPracticesByOwner(
ctx context.Context,
ownerType string,
ownerID int64,
) ([]domain.Practice, error)
UpdatePractice(
ctx context.Context,
id int64,
title *string,
description *string,
bannerImage *string,
persona *string,
isActive *bool,
) error
DeletePractice(
ctx context.Context,
id int64,
) error
CreateLevel(
ctx context.Context,
programID int64,
title string,
description *string,
levelIndex int,
isActive *bool,
) (domain.Level, error)
GetLevelsByProgram(
ctx context.Context,
programID int64,
) ([]domain.Level, error)
UpdateLevel(
ctx context.Context,
id int64,
title *string,
description *string,
levelIndex *int,
isActive *bool,
) error
IncrementLevelModuleCount(
ctx context.Context,
levelID int64,
) error
IncrementLevelPracticeCount(
ctx context.Context,
levelID int64,
) error
IncrementLevelVideoCount(
ctx context.Context,
levelID int64,
) error
DeleteLevel(
ctx context.Context,
levelID int64,
) error
GetFullLearningTree(ctx context.Context) ([]domain.TreeCourse, error)
} }

View File

@ -11,65 +11,14 @@ import (
func NewCourseStore(s *Store) ports.CourseStore { return s } func NewCourseStore(s *Store) ports.CourseStore { return s }
func (s *Store) CreateCourseCategory(ctx context.Context, name string) (domain.CourseCategory, error) { func (s *Store) CreateCourseCategory(
tempCategory, err := s.queries.CreateCourseCategory(ctx, dbgen.CreateCourseCategoryParams{ ctx context.Context,
name string,
) (domain.CourseCategory, error) {
row, err := s.queries.CreateCourseCategory(ctx, dbgen.CreateCourseCategoryParams{
Name: name, Name: name,
IsActive: true, Column2: true,
})
if err != nil {
return domain.CourseCategory{}, err
}
category := domain.CourseCategory{
ID: tempCategory.ID,
Name: tempCategory.Name,
IsActive: tempCategory.IsActive,
CreatedAt: tempCategory.CreatedAt.Time,
}
return category, nil
}
func (s *Store) GetCourseCategoryByID(ctx context.Context, Id int64) (domain.CourseCategory, error) {
tempCategory, err := s.queries.GetCourseCategoryByID(ctx, Id)
if err != nil {
return domain.CourseCategory{}, err
}
category := domain.CourseCategory{
ID: tempCategory.ID,
Name: tempCategory.Name,
IsActive: tempCategory.IsActive,
CreatedAt: tempCategory.CreatedAt.Time,
}
return category, nil
}
func (s *Store) ListActiveCourseCategories(ctx context.Context) ([]domain.CourseCategory, error) {
rows, err := s.queries.ListActiveCourseCategories(ctx)
if err != nil {
return nil, err
}
result := make([]domain.CourseCategory, 0, len(rows))
for _, r := range rows {
result = append(result, domain.CourseCategory{
ID: r.ID,
Name: r.Name,
IsActive: r.IsActive,
CreatedAt: r.CreatedAt.Time,
})
}
return result, nil
}
func (s *Store) UpdateCourseCategory(ctx context.Context, id int64, name string, isActive bool) (domain.CourseCategory, error) {
row, err := s.queries.UpdateCourseCategory(ctx, dbgen.UpdateCourseCategoryParams{
ID: id,
Name: name,
IsActive: isActive,
}) })
if err != nil { if err != nil {
return domain.CourseCategory{}, err return domain.CourseCategory{}, err
@ -83,752 +32,90 @@ func (s *Store) UpdateCourseCategory(ctx context.Context, id int64, name string,
}, nil }, nil
} }
func (s *Store) DeactivateCourseCategory(ctx context.Context, id int64) error { func (s *Store) GetCourseCategoryByID(
return s.queries.DeactivateCourseCategory(ctx, id) ctx context.Context,
} id int64,
) (domain.CourseCategory, error) {
// Course related methods row, err := s.queries.GetCourseCategoryByID(ctx, id)
func (s *Store) CreateCourse(ctx context.Context, c domain.Course) (domain.Course, error) {
row, err := s.queries.CreateCourse(ctx, dbgen.CreateCourseParams{
CategoryID: c.CategoryID,
Title: c.Title,
Description: pgtype.Text{String: c.Description, Valid: c.Description != ""},
IsActive: c.IsActive,
})
if err != nil { if err != nil {
return domain.Course{}, err return domain.CourseCategory{}, err
} }
return domain.Course{ return domain.CourseCategory{
ID: row.ID, ID: row.ID,
CategoryID: row.CategoryID, Name: row.Name,
Title: row.Title,
Description: row.Description.String,
IsActive: row.IsActive, IsActive: row.IsActive,
CreatedAt: row.CreatedAt.Time,
}, nil }, nil
} }
func (s *Store) GetCourseByID(ctx context.Context, id int64) (domain.Course, error) { func (s *Store) GetAllCourseCategories(
row, err := s.queries.GetCourseByID(ctx, id) ctx context.Context,
limit int32,
offset int32,
) ([]domain.CourseCategory, int64, error) {
rows, err := s.queries.GetAllCourseCategories(ctx, dbgen.GetAllCourseCategoriesParams{
Limit: pgtype.Int4{Int32: limit},
Offset: pgtype.Int4{Int32: offset},
})
if err != nil { if err != nil {
return domain.Course{}, err return nil, 0, err
} }
return domain.Course{ var (
categories []domain.CourseCategory
totalCount int64
)
for i, row := range rows {
if i == 0 {
totalCount = row.TotalCount
}
categories = append(categories, domain.CourseCategory{
ID: row.ID, ID: row.ID,
CategoryID: row.CategoryID, Name: row.Name,
Title: row.Title,
Description: row.Description.String,
IsActive: row.IsActive, IsActive: row.IsActive,
}, nil CreatedAt: row.CreatedAt.Time,
}
func (s *Store) ListCoursesByCategory(ctx context.Context, categoryID int64) ([]domain.Course, error) {
rows, err := s.queries.ListCoursesByCategory(ctx, categoryID)
if err != nil {
return nil, err
}
res := make([]domain.Course, 0, len(rows))
for _, r := range rows {
res = append(res, domain.Course{
ID: r.ID,
CategoryID: r.CategoryID,
Title: r.Title,
Description: r.Description.String,
IsActive: r.IsActive,
}) })
} }
return res, nil
return categories, totalCount, nil
} }
func (s *Store) ListActiveCourses(ctx context.Context) ([]domain.Course, error) { func (s *Store) UpdateCourseCategory(
rows, err := s.queries.ListActiveCourses(ctx) ctx context.Context,
if err != nil { id int64,
return nil, err name *string,
isActive *bool,
) error {
var (
nameVal string
isActiveVal bool
)
if name != nil {
nameVal = *name
} }
res := make([]domain.Course, 0, len(rows)) if isActive != nil {
for _, r := range rows { isActiveVal = *isActive
res = append(res, domain.Course{ }
ID: r.ID,
CategoryID: r.CategoryID, return s.queries.UpdateCourseCategory(ctx, dbgen.UpdateCourseCategoryParams{
Title: r.Title, Name: nameVal,
Description: r.Description.String, IsActive: isActiveVal,
IsActive: r.IsActive, ID: id,
}) })
} }
return res, nil
}
func (s *Store) UpdateCourse(ctx context.Context, c domain.Course) (domain.Course, error) { func (s *Store) DeleteCourseCategory(
row, err := s.queries.UpdateCourse(ctx, dbgen.UpdateCourseParams{ ctx context.Context,
ID: c.ID, id int64,
CategoryID: c.CategoryID, ) error {
Title: c.Title,
Description: pgtype.Text{String: c.Description, Valid: c.Description != ""},
IsActive: c.IsActive,
})
if err != nil {
return domain.Course{}, err
}
return domain.Course{ return s.queries.DeleteCourseCategory(ctx, id)
ID: row.ID,
CategoryID: row.CategoryID,
Title: row.Title,
Description: row.Description.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivateCourse(ctx context.Context, id int64) error {
return s.queries.DeactivateCourse(ctx, id)
}
// Program methods
func (s *Store) CreateProgram(ctx context.Context, p domain.Program) (domain.Program, error) {
row, err := s.queries.CreateProgram(ctx, dbgen.CreateProgramParams{
CourseID: p.CourseID,
Title: p.Title,
Description: pgtype.Text{String: p.Description, Valid: p.Description != ""},
Thumbnail: pgtype.Text{String: p.Thumbnail, Valid: p.Thumbnail != ""},
DisplayOrder: int32(p.Order),
IsActive: p.IsActive,
})
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: row.Description.String,
Thumbnail: row.Thumbnail.String,
Order: int(row.DisplayOrder),
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetProgramByID(ctx context.Context, id int64) (domain.Program, error) {
row, err := s.queries.GetProgramByID(ctx, id)
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: row.Description.String,
Thumbnail: row.Thumbnail.String,
Order: int(row.DisplayOrder),
IsActive: row.IsActive,
}, nil
}
func (s *Store) ListProgramsByCourse(ctx context.Context, courseID int64) ([]domain.Program, error) {
rows, err := s.queries.ListProgramsByCourse(ctx, courseID)
if err != nil {
return nil, err
}
res := make([]domain.Program, 0, len(rows))
for _, r := range rows {
res = append(res, domain.Program{
ID: r.ID,
CourseID: r.CourseID,
Title: r.Title,
Description: r.Description.String,
Thumbnail: r.Thumbnail.String,
Order: int(r.DisplayOrder),
IsActive: r.IsActive,
})
}
return res, nil
}
func (s *Store) ListActivePrograms(ctx context.Context) ([]domain.Program, error) {
rows, err := s.queries.ListActivePrograms(ctx)
if err != nil {
return nil, err
}
res := make([]domain.Program, 0, len(rows))
for _, r := range rows {
res = append(res, domain.Program{
ID: r.ID,
CourseID: r.CourseID,
Title: r.Title,
Description: r.Description.String,
Thumbnail: r.Thumbnail.String,
Order: int(r.DisplayOrder),
IsActive: r.IsActive,
})
}
return res, nil
}
func (s *Store) UpdateProgram(ctx context.Context, p domain.Program) (domain.Program, error) {
row, err := s.queries.UpdateProgram(ctx, dbgen.UpdateProgramParams{
ID: p.ID,
CourseID: p.CourseID,
Title: p.Title,
Description: pgtype.Text{String: p.Description, Valid: p.Description != ""},
Thumbnail: pgtype.Text{String: p.Thumbnail, Valid: p.Thumbnail != ""},
DisplayOrder: int32(p.Order),
IsActive: p.IsActive,
})
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: row.Description.String,
Thumbnail: row.Thumbnail.String,
Order: int(row.DisplayOrder),
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivateProgram(ctx context.Context, id int64) error {
return s.queries.DeactivateProgram(ctx, id)
}
// Module methods
func (s *Store) CreateModule(ctx context.Context, m domain.Module) (domain.Module, error) {
row, err := s.queries.CreateModule(ctx, dbgen.CreateModuleParams{
LevelID: m.LevelID,
Title: m.Title,
Content: pgtype.Text{String: m.Content, Valid: m.Content != ""},
DisplayOrder: int32(m.Order),
IsActive: m.IsActive,
})
if err != nil {
return domain.Module{}, err
}
return domain.Module{
ID: row.ID,
LevelID: row.LevelID,
Title: row.Title,
Content: row.Content.String,
Order: int(row.DisplayOrder),
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetModuleByID(ctx context.Context, id int64) (domain.Module, error) {
row, err := s.queries.GetModuleByID(ctx, id)
if err != nil {
return domain.Module{}, err
}
return domain.Module{
ID: row.ID,
LevelID: row.LevelID,
Title: row.Title,
Content: row.Content.String,
Order: int(row.DisplayOrder),
IsActive: row.IsActive,
}, nil
}
func (s *Store) ListModulesByLevel(ctx context.Context, levelID int64) ([]domain.Module, error) {
rows, err := s.queries.ListModulesByLevel(ctx, levelID)
if err != nil {
return nil, err
}
res := make([]domain.Module, 0, len(rows))
for _, r := range rows {
res = append(res, domain.Module{
ID: r.ID,
LevelID: r.LevelID,
Title: r.Title,
Content: r.Content.String,
Order: int(r.DisplayOrder),
IsActive: r.IsActive,
})
}
return res, nil
}
func (s *Store) UpdateModule(ctx context.Context, m domain.Module) (domain.Module, error) {
row, err := s.queries.UpdateModule(ctx, dbgen.UpdateModuleParams{
ID: m.ID,
Title: m.Title,
Content: pgtype.Text{String: m.Content, Valid: m.Content != ""},
DisplayOrder: int32(m.Order),
IsActive: m.IsActive,
})
if err != nil {
return domain.Module{}, err
}
return domain.Module{
ID: row.ID,
LevelID: row.LevelID,
Title: row.Title,
Content: row.Content.String,
Order: int(row.DisplayOrder),
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivateModule(ctx context.Context, id int64) error {
return s.queries.DeactivateModule(ctx, id)
}
// Module video methods
func (s *Store) CreateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error) {
row, err := s.queries.CreateModuleVideo(ctx, dbgen.CreateModuleVideoParams{
ModuleID: v.ModuleID,
Title: v.Title,
Description: pgtype.Text{String: v.Description, Valid: v.Description != ""},
VideoUrl: v.VideoURL,
Duration: int32(v.Duration),
Resolution: pgtype.Text{String: v.Resolution, Valid: v.Resolution != ""},
IsPublished: v.PublishSettings.IsPublished,
PublishDate: pgtype.Timestamptz{Time: v.PublishSettings.PublishDate, Valid: !v.PublishSettings.PublishDate.IsZero()},
Visibility: pgtype.Text{String: v.PublishSettings.Visibility, Valid: v.PublishSettings.Visibility != ""},
InstructorID: pgtype.Text{String: v.InstructorId, Valid: v.InstructorId != ""},
Thumbnail: pgtype.Text{String: v.Thumbnail, Valid: v.Thumbnail != ""},
IsActive: v.IsActive,
})
if err != nil {
return domain.ModuleVideo{}, err
}
return domain.ModuleVideo{
ID: row.ID,
ModuleID: row.ModuleID,
Title: row.Title,
Description: row.Description.String,
VideoURL: row.VideoUrl,
Duration: int(row.Duration),
Resolution: row.Resolution.String,
PublishSettings: domain.PublishSettings{
IsPublished: row.IsPublished,
PublishDate: row.PublishDate.Time,
Visibility: row.Visibility.String,
},
InstructorId: row.InstructorID.String,
Thumbnail: row.Thumbnail.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetModuleVideoByID(ctx context.Context, id int64) (domain.ModuleVideo, error) {
row, err := s.queries.GetModuleVideoByID(ctx, id)
if err != nil {
return domain.ModuleVideo{}, err
}
return domain.ModuleVideo{
ID: row.ID,
ModuleID: row.ModuleID,
Title: row.Title,
Description: row.Description.String,
VideoURL: row.VideoUrl,
Duration: int(row.Duration),
Resolution: row.Resolution.String,
PublishSettings: domain.PublishSettings{
IsPublished: row.IsPublished,
PublishDate: row.PublishDate.Time,
Visibility: row.Visibility.String,
},
InstructorId: row.InstructorID.String,
Thumbnail: row.Thumbnail.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) ListAllVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error) {
rows, err := s.queries.ListAllVideosByModule(ctx, moduleID)
if err != nil {
return nil, err
}
res := make([]domain.ModuleVideo, 0, len(rows))
for _, r := range rows {
res = append(res, domain.ModuleVideo{
ID: r.ID,
ModuleID: r.ModuleID,
Title: r.Title,
Description: r.Description.String,
VideoURL: r.VideoUrl,
Duration: int(r.Duration),
Resolution: r.Resolution.String,
PublishSettings: domain.PublishSettings{
IsPublished: r.IsPublished,
PublishDate: r.PublishDate.Time,
Visibility: r.Visibility.String,
},
InstructorId: r.InstructorID.String,
Thumbnail: r.Thumbnail.String,
IsActive: r.IsActive,
})
}
return res, nil
}
func (s *Store) ListPublishedVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error) {
rows, err := s.queries.ListPublishedVideosByModule(ctx, moduleID)
if err != nil {
return nil, err
}
res := make([]domain.ModuleVideo, 0, len(rows))
for _, r := range rows {
res = append(res, domain.ModuleVideo{
ID: r.ID,
ModuleID: r.ModuleID,
Title: r.Title,
Description: r.Description.String,
VideoURL: r.VideoUrl,
Duration: int(r.Duration),
Resolution: r.Resolution.String,
PublishSettings: domain.PublishSettings{
IsPublished: true,
PublishDate: r.PublishDate.Time,
Visibility: r.Visibility.String,
},
InstructorId: r.InstructorID.String,
Thumbnail: r.Thumbnail.String,
IsActive: true,
})
}
return res, nil
}
func (s *Store) UpdateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error) {
row, err := s.queries.UpdateModuleVideo(ctx, dbgen.UpdateModuleVideoParams{
ID: v.ID,
Title: v.Title,
Description: pgtype.Text{String: v.Description, Valid: v.Description != ""},
VideoUrl: v.VideoURL,
Duration: int32(v.Duration),
Resolution: pgtype.Text{String: v.Resolution, Valid: v.Resolution != ""},
IsPublished: v.PublishSettings.IsPublished,
PublishDate: pgtype.Timestamptz{Time: v.PublishSettings.PublishDate, Valid: !v.PublishSettings.PublishDate.IsZero()},
Visibility: pgtype.Text{String: v.PublishSettings.Visibility, Valid: v.PublishSettings.Visibility != ""},
InstructorID: pgtype.Text{String: v.InstructorId, Valid: v.InstructorId != ""},
Thumbnail: pgtype.Text{String: v.Thumbnail, Valid: v.Thumbnail != ""},
IsActive: v.IsActive,
})
if err != nil {
return domain.ModuleVideo{}, err
}
return domain.ModuleVideo{
ID: row.ID,
ModuleID: row.ModuleID,
Title: row.Title,
Description: row.Description.String,
VideoURL: row.VideoUrl,
Duration: int(row.Duration),
Resolution: row.Resolution.String,
PublishSettings: domain.PublishSettings{
IsPublished: row.IsPublished,
PublishDate: row.PublishDate.Time,
Visibility: row.Visibility.String,
},
InstructorId: row.InstructorID.String,
Thumbnail: row.Thumbnail.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivateModuleVideo(ctx context.Context, id int64) error {
return s.queries.DeactivateModuleVideo(ctx, id)
}
// Practices and practice question methods
func (s *Store) CreatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error) {
row, err := s.queries.CreatePractice(ctx, dbgen.CreatePracticeParams{
OwnerType: p.OwnerType,
OwnerID: p.OwnerID,
Title: p.Title,
Description: pgtype.Text{String: p.Description, Valid: p.Description != ""},
BannerImage: pgtype.Text{String: p.BannerImage, Valid: p.BannerImage != ""},
Persona: pgtype.Text{String: p.Persona, Valid: p.Persona != ""},
IsActive: p.IsActive,
})
if err != nil {
return domain.Practice{}, err
}
return domain.Practice{
ID: row.ID,
OwnerType: row.OwnerType,
OwnerID: row.OwnerID,
Title: row.Title,
Description: row.Description.String,
BannerImage: row.BannerImage.String,
Persona: row.Persona.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetPracticeByID(ctx context.Context, id int64) (domain.Practice, error) {
row, err := s.queries.GetPracticeByID(ctx, id)
if err != nil {
return domain.Practice{}, err
}
return domain.Practice{
ID: row.ID,
OwnerType: row.OwnerType,
OwnerID: row.OwnerID,
Title: row.Title,
Description: row.Description.String,
BannerImage: row.BannerImage.String,
Persona: row.Persona.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) ListPracticesByOwner(ctx context.Context, ownerType string, ownerID int64) ([]domain.Practice, error) {
rows, err := s.queries.ListPracticesByOwner(ctx, dbgen.ListPracticesByOwnerParams{OwnerType: ownerType, OwnerID: ownerID})
if err != nil {
return nil, err
}
res := make([]domain.Practice, 0, len(rows))
for _, r := range rows {
res = append(res, domain.Practice{
ID: r.ID,
OwnerType: r.OwnerType,
OwnerID: r.OwnerID,
Title: r.Title,
Description: r.Description.String,
BannerImage: r.BannerImage.String,
Persona: r.Persona.String,
IsActive: r.IsActive,
})
}
return res, nil
}
func (s *Store) UpdatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error) {
row, err := s.queries.UpdatePractice(ctx, dbgen.UpdatePracticeParams{
ID: p.ID,
Title: p.Title,
Description: pgtype.Text{String: p.Description, Valid: p.Description != ""},
BannerImage: pgtype.Text{String: p.BannerImage, Valid: p.BannerImage != ""},
Persona: pgtype.Text{String: p.Persona, Valid: p.Persona != ""},
IsActive: p.IsActive,
})
if err != nil {
return domain.Practice{}, err
}
return domain.Practice{
ID: row.ID,
OwnerType: row.OwnerType,
OwnerID: row.OwnerID,
Title: row.Title,
Description: row.Description.String,
BannerImage: row.BannerImage.String,
Persona: row.Persona.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivatePractice(ctx context.Context, id int64) error {
return s.queries.DeactivatePractice(ctx, id)
}
// Practice question methods
func (s *Store) CreatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error) {
row, err := s.queries.CreatePracticeQuestion(ctx, dbgen.CreatePracticeQuestionParams{
PracticeID: qn.PracticeID,
Question: qn.Question,
QuestionVoicePrompt: pgtype.Text{String: qn.QuestionVoicePrompt, Valid: qn.QuestionVoicePrompt != ""},
SampleAnswerVoicePrompt: pgtype.Text{String: qn.SampleAnswerVoicePrompt, Valid: qn.SampleAnswerVoicePrompt != ""},
SampleAnswer: pgtype.Text{String: qn.SampleAnswer, Valid: qn.SampleAnswer != ""},
Tips: pgtype.Text{String: qn.Tips, Valid: qn.Tips != ""},
Type: qn.Type,
})
if err != nil {
return domain.PracticeQuestion{}, err
}
return domain.PracticeQuestion{
ID: row.ID,
PracticeID: row.PracticeID,
Question: row.Question,
QuestionVoicePrompt: row.QuestionVoicePrompt.String,
SampleAnswerVoicePrompt: row.SampleAnswerVoicePrompt.String,
SampleAnswer: row.SampleAnswer.String,
Tips: row.Tips.String,
Type: row.Type,
}, nil
}
func (s *Store) GetPracticeQuestionByID(ctx context.Context, id int64) (domain.PracticeQuestion, error) {
row, err := s.queries.GetPracticeQuestionByID(ctx, id)
if err != nil {
return domain.PracticeQuestion{}, err
}
return domain.PracticeQuestion{
ID: row.ID,
PracticeID: row.PracticeID,
Question: row.Question,
QuestionVoicePrompt: row.QuestionVoicePrompt.String,
SampleAnswerVoicePrompt: row.SampleAnswerVoicePrompt.String,
SampleAnswer: row.SampleAnswer.String,
Tips: row.Tips.String,
Type: row.Type,
}, nil
}
func (s *Store) ListPracticeQuestions(ctx context.Context, practiceID int64) ([]domain.PracticeQuestion, error) {
rows, err := s.queries.ListPracticeQuestions(ctx, practiceID)
if err != nil {
return nil, err
}
res := make([]domain.PracticeQuestion, 0, len(rows))
for _, r := range rows {
res = append(res, domain.PracticeQuestion{
ID: r.ID,
PracticeID: r.PracticeID,
Question: r.Question,
QuestionVoicePrompt: r.QuestionVoicePrompt.String,
SampleAnswerVoicePrompt: r.SampleAnswerVoicePrompt.String,
SampleAnswer: r.SampleAnswer.String,
Tips: r.Tips.String,
Type: r.Type,
})
}
return res, nil
}
func (s *Store) UpdatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error) {
row, err := s.queries.UpdatePracticeQuestion(ctx, dbgen.UpdatePracticeQuestionParams{
ID: qn.ID,
Question: qn.Question,
QuestionVoicePrompt: pgtype.Text{String: qn.QuestionVoicePrompt, Valid: qn.QuestionVoicePrompt != ""},
SampleAnswerVoicePrompt: pgtype.Text{String: qn.SampleAnswerVoicePrompt, Valid: qn.SampleAnswerVoicePrompt != ""},
SampleAnswer: pgtype.Text{String: qn.SampleAnswer, Valid: qn.SampleAnswer != ""},
Tips: pgtype.Text{String: qn.Tips, Valid: qn.Tips != ""},
Type: qn.Type,
})
if err != nil {
return domain.PracticeQuestion{}, err
}
return domain.PracticeQuestion{
ID: row.ID,
PracticeID: row.PracticeID,
Question: row.Question,
QuestionVoicePrompt: row.QuestionVoicePrompt.String,
SampleAnswerVoicePrompt: row.SampleAnswerVoicePrompt.String,
SampleAnswer: row.SampleAnswer.String,
Tips: row.Tips.String,
Type: row.Type,
}, nil
}
func (s *Store) DeletePracticeQuestion(ctx context.Context, id int64) error {
return s.queries.DeletePracticeQuestion(ctx, id)
}
// Level (program level) methods
func (s *Store) CreateLevel(ctx context.Context, l domain.Level) (domain.Level, error) {
row, err := s.queries.CreateLevel(ctx, dbgen.CreateLevelParams{
ProgramID: l.ProgramID,
Title: l.Title,
Description: pgtype.Text{String: l.Description, Valid: l.Description != ""},
LevelIndex: int32(l.LevelIndex),
NumberOfModules: int32(l.NumberOfModules),
NumberOfPractices: int32(l.NumberOfPractices),
NumberOfVideos: int32(l.NumberOfVideos),
IsActive: l.IsActive,
})
if err != nil {
return domain.Level{}, err
}
return domain.Level{
ID: row.ID,
ProgramID: row.ProgramID,
Title: row.Title,
Description: row.Description.String,
LevelIndex: int(row.LevelIndex),
NumberOfModules: int(row.NumberOfModules),
NumberOfPractices: int(row.NumberOfPractices),
NumberOfVideos: int(row.NumberOfVideos),
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetLevelByID(ctx context.Context, id int64) (domain.Level, error) {
row, err := s.queries.GetLevelByID(ctx, id)
if err != nil {
return domain.Level{}, err
}
return domain.Level{
ID: row.ID,
ProgramID: row.ProgramID,
Title: row.Title,
Description: row.Description.String,
LevelIndex: int(row.LevelIndex),
NumberOfModules: int(row.NumberOfModules),
NumberOfPractices: int(row.NumberOfPractices),
NumberOfVideos: int(row.NumberOfVideos),
IsActive: row.IsActive,
}, nil
}
func (s *Store) ListLevelsByProgram(ctx context.Context, programID int64) ([]domain.Level, error) {
rows, err := s.queries.ListLevelsByProgram(ctx, programID)
if err != nil {
return nil, err
}
res := make([]domain.Level, 0, len(rows))
for _, r := range rows {
res = append(res, domain.Level{
ID: r.ID,
ProgramID: r.ProgramID,
Title: r.Title,
Description: r.Description.String,
LevelIndex: int(r.LevelIndex),
NumberOfModules: int(r.NumberOfModules),
NumberOfPractices: int(r.NumberOfPractices),
NumberOfVideos: int(r.NumberOfVideos),
IsActive: r.IsActive,
})
}
return res, nil
}
func (s *Store) UpdateLevel(ctx context.Context, l domain.Level) (domain.Level, error) {
row, err := s.queries.UpdateLevel(ctx, dbgen.UpdateLevelParams{
ID: l.ID,
Title: l.Title,
Description: pgtype.Text{String: l.Description, Valid: l.Description != ""},
LevelIndex: int32(l.LevelIndex),
NumberOfModules: int32(l.NumberOfModules),
NumberOfPractices: int32(l.NumberOfPractices),
NumberOfVideos: int32(l.NumberOfVideos),
IsActive: l.IsActive,
})
if err != nil {
return domain.Level{}, err
}
return domain.Level{
ID: row.ID,
ProgramID: row.ProgramID,
Title: row.Title,
Description: row.Description.String,
LevelIndex: int(row.LevelIndex),
NumberOfModules: int(row.NumberOfModules),
NumberOfPractices: int(row.NumberOfPractices),
NumberOfVideos: int(row.NumberOfVideos),
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivateLevel(ctx context.Context, id int64) error {
return s.queries.DeactivateLevel(ctx, id)
} }

View File

@ -0,0 +1,240 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateProgram(
ctx context.Context,
courseID int64,
title string,
description *string,
thumbnail *string,
displayOrder *int32,
) (domain.Program, error) {
row, err := s.queries.CreateProgram(ctx, dbgen.CreateProgramParams{
CourseID: courseID,
Title: title,
Description: pgtype.Text{String: *description},
Thumbnail: pgtype.Text{String: *thumbnail},
Column5: displayOrder,
Column6: true,
})
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetProgramByID(
ctx context.Context,
id int64,
) (domain.Program, error) {
row, err := s.queries.GetProgramByID(ctx, id)
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetProgramsByCourse(
ctx context.Context,
courseID int64,
) ([]domain.Program, int64, error) {
rows, err := s.queries.GetProgramsByCourse(ctx, courseID)
if err != nil {
return nil, 0, err
}
var (
programs []domain.Program
totalCount int64
)
for i, row := range rows {
if i == 0 {
totalCount = row.TotalCount
}
programs = append(programs, domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
})
}
return programs, totalCount, nil
}
func (s *Store) ListProgramsByCourse(
ctx context.Context,
courseID int64,
) ([]domain.Program, error) {
rows, err := s.queries.ListProgramsByCourse(ctx, courseID)
if err != nil {
return nil, err
}
programs := make([]domain.Program, 0, len(rows))
for _, row := range rows {
programs = append(programs, domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
})
}
return programs, nil
}
func (s *Store) ListActivePrograms(
ctx context.Context,
) ([]domain.Program, error) {
rows, err := s.queries.ListActivePrograms(ctx)
if err != nil {
return nil, err
}
programs := make([]domain.Program, 0, len(rows))
for _, row := range rows {
programs = append(programs, domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
})
}
return programs, nil
}
func (s *Store) UpdateProgramPartial(
ctx context.Context,
id int64,
title *string,
description *string,
thumbnail *string,
displayOrder *int32,
isActive *bool,
) error {
return s.queries.UpdateProgramPartial(ctx, dbgen.UpdateProgramPartialParams{
Title: func() string {
if title != nil {
return *title
}
return ""
}(),
Description: pgtype.Text{String: *description},
Thumbnail: pgtype.Text{String: *thumbnail},
DisplayOrder: func() int32 {
if displayOrder != nil {
return *displayOrder
}
return 0
}(),
IsActive: func() bool {
if isActive != nil {
return *isActive
}
return false
}(),
ID: id,
})
}
func (s *Store) UpdateProgramFull(
ctx context.Context,
program domain.Program,
) (domain.Program, error) {
row, err := s.queries.UpdateProgramFull(ctx, dbgen.UpdateProgramFullParams{
ID: program.ID,
CourseID: program.CourseID,
Title: program.Title,
Description: pgtype.Text{String: *program.Description},
Thumbnail: pgtype.Text{String: *program.Thumbnail},
DisplayOrder: program.DisplayOrder,
IsActive: program.IsActive,
})
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
}, nil
}
func (s *Store) DeactivateProgram(
ctx context.Context,
id int64,
) error {
return s.queries.DeactivateProgram(ctx, id)
}
func (s *Store) DeleteProgram(
ctx context.Context,
id int64,
) (domain.Program, error) {
row, err := s.queries.DeleteProgram(ctx, id)
if err != nil {
return domain.Program{}, err
}
return domain.Program{
ID: row.ID,
CourseID: row.CourseID,
Title: row.Title,
Description: &row.Description.String,
Thumbnail: &row.Thumbnail.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
}, nil
}

View File

@ -0,0 +1,132 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateCourse(
ctx context.Context,
categoryID int64,
title string,
description *string,
) (domain.Course, error) {
row, err := s.queries.CreateCourse(ctx, dbgen.CreateCourseParams{
CategoryID: categoryID,
Title: title,
Description: pgtype.Text{String: *description},
Column4: true,
})
if err != nil {
return domain.Course{}, err
}
return domain.Course{
ID: row.ID,
CategoryID: row.CategoryID,
Title: row.Title,
Description: &row.Description.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetCourseByID(
ctx context.Context,
id int64,
) (domain.Course, error) {
row, err := s.queries.GetCourseByID(ctx, id)
if err != nil {
return domain.Course{}, err
}
return domain.Course{
ID: row.ID,
CategoryID: row.CategoryID,
Title: row.Title,
Description: &row.Description.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetCoursesByCategory(
ctx context.Context,
categoryID int64,
limit int32,
offset int32,
) ([]domain.Course, int64, error) {
rows, err := s.queries.GetCoursesByCategory(ctx, dbgen.GetCoursesByCategoryParams{
CategoryID: categoryID,
Limit: pgtype.Int4{Int32: limit},
Offset: pgtype.Int4{Int32: offset},
})
if err != nil {
return nil, 0, err
}
var (
courses []domain.Course
totalCount int64
)
for i, row := range rows {
if i == 0 {
totalCount = row.TotalCount
}
courses = append(courses, domain.Course{
ID: row.ID,
CategoryID: row.CategoryID,
Title: row.Title,
Description: &row.Description.String,
IsActive: row.IsActive,
})
}
return courses, totalCount, nil
}
func (s *Store) UpdateCourse(
ctx context.Context,
id int64,
title *string,
description *string,
isActive *bool,
) error {
var (
titleVal string
descriptionVal string
isActiveVal bool
)
if title != nil {
titleVal = *title
}
if description != nil {
descriptionVal = *description
}
if isActive != nil {
isActiveVal = *isActive
}
return s.queries.UpdateCourse(ctx, dbgen.UpdateCourseParams{
Title: titleVal,
Description: pgtype.Text{String: descriptionVal},
IsActive: isActiveVal,
ID: id,
})
}
func (s *Store) DeleteCourse(
ctx context.Context,
id int64,
) error {
return s.queries.DeleteCourse(ctx, id)
}

View File

@ -0,0 +1,90 @@
package repository
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Store) GetFullLearningTree(ctx context.Context) ([]domain.TreeCourse, error) {
rows, err := s.queries.GetFullLearningTree(ctx)
if err != nil {
return nil, err
}
coursesMap := make(map[int64]*domain.TreeCourse)
programsMap := make(map[int64]*domain.TreeProgram)
levelsMap := make(map[int64]*domain.TreeLevel)
for _, row := range rows {
// COURSE
course, ok := coursesMap[row.CourseID]
if !ok {
course = &domain.TreeCourse{
ID: row.CourseID,
Title: row.CourseTitle,
Programs: []domain.TreeProgram{},
}
coursesMap[row.CourseID] = course
}
// PROGRAM
program, ok := programsMap[row.ProgramID]
if !ok {
program = &domain.TreeProgram{
ID: row.ProgramID,
Title: row.ProgramTitle,
Levels: []domain.TreeLevel{},
}
programsMap[row.ProgramID] = program
course.Programs = append(course.Programs, *program)
}
// LEVEL
level, ok := levelsMap[row.LevelID]
if !ok {
level = &domain.TreeLevel{
ID: row.LevelID,
Title: row.LevelTitle,
Modules: []domain.TreeModule{},
}
levelsMap[row.LevelID] = level
// Append level to its program
for i := range course.Programs {
if course.Programs[i].ID == row.ProgramID {
course.Programs[i].Levels = append(course.Programs[i].Levels, *level)
break
}
}
}
// MODULE (may be nil)
if row.ModuleID.Valid {
module := domain.TreeModule{
ID: row.ModuleID.Int64,
Title: row.ModuleTitle.String,
}
// Append module to its level
for i := range course.Programs {
if course.Programs[i].ID == row.ProgramID {
for j := range course.Programs[i].Levels {
if course.Programs[i].Levels[j].ID == row.LevelID {
course.Programs[i].Levels[j].Modules = append(course.Programs[i].Levels[j].Modules, module)
break
}
}
break
}
}
}
}
// Flatten map to slice
courses := make([]domain.TreeCourse, 0, len(coursesMap))
for _, course := range coursesMap {
courses = append(courses, *course)
}
return courses, nil
}

View File

@ -0,0 +1,112 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateModule(
ctx context.Context,
levelID int64,
title string,
content *string,
displayOrder *int32,
) (domain.Module, error) {
row, err := s.queries.CreateModule(ctx, dbgen.CreateModuleParams{
LevelID: levelID,
Title: title,
Content: pgtype.Text{String: *content},
Column4: displayOrder,
Column5: true,
})
if err != nil {
return domain.Module{}, err
}
return domain.Module{
ID: row.ID,
LevelID: row.LevelID,
Title: row.Title,
Content: &row.Content.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetModulesByLevel(
ctx context.Context,
levelID int64,
) ([]domain.Module, int64, error) {
rows, err := s.queries.GetModulesByLevel(ctx, levelID)
if err != nil {
return nil, 0, err
}
var (
modules []domain.Module
totalCount int64
)
for i, row := range rows {
if i == 0 {
totalCount = row.TotalCount
}
modules = append(modules, domain.Module{
ID: row.ID,
LevelID: row.LevelID,
Title: row.Title,
Content: &row.Content.String,
DisplayOrder: row.DisplayOrder,
IsActive: row.IsActive,
})
}
return modules, totalCount, nil
}
func (s *Store) UpdateModule(
ctx context.Context,
id int64,
title *string,
content *string,
displayOrder *int32,
isActive *bool,
) error {
titleVal := ""
if title != nil {
titleVal = *title
}
var displayOrderVal int32
if displayOrder != nil {
displayOrderVal = *displayOrder
}
var isActiveVal bool
if isActive != nil {
isActiveVal = *isActive
}
return s.queries.UpdateModule(ctx, dbgen.UpdateModuleParams{
Title: titleVal,
Content: pgtype.Text{String: *content},
DisplayOrder: displayOrderVal,
IsActive: isActiveVal,
ID: id,
})
}
func (s *Store) DeleteModule(
ctx context.Context,
id int64,
) error {
return s.queries.DeleteModule(ctx, id)
}

View File

@ -0,0 +1,161 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"time"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateModuleVideo(
ctx context.Context,
moduleID int64,
title string,
description *string,
videoURL string,
duration int32,
resolution *string,
instructorID *string,
thumbnail *string,
visibility *string,
) (domain.ModuleVideo, error) {
row, err := s.queries.CreateModuleVideo(ctx, dbgen.CreateModuleVideoParams{
ModuleID: moduleID,
Title: title,
Description: pgtype.Text{String: *description},
VideoUrl: videoURL,
Duration: duration,
Resolution: pgtype.Text{String: *resolution},
InstructorID: pgtype.Text{String: *instructorID},
Thumbnail: pgtype.Text{String: *thumbnail},
Visibility: pgtype.Text{String: *visibility},
Column10: true,
})
if err != nil {
return domain.ModuleVideo{}, err
}
var publishDate *time.Time
if row.PublishDate.Valid {
publishDate = &row.PublishDate.Time
}
return domain.ModuleVideo{
ID: row.ID,
ModuleID: row.ModuleID,
Title: row.Title,
Description: &row.Description.String,
VideoURL: row.VideoUrl,
Duration: row.Duration,
Resolution: &row.Resolution.String,
InstructorID: &row.InstructorID.String,
Thumbnail: &row.Thumbnail.String,
Visibility: &row.Visibility.String,
IsPublished: row.IsPublished,
PublishDate: publishDate,
IsActive: row.IsActive,
}, nil
}
func (s *Store) PublishModuleVideo(
ctx context.Context,
videoID int64,
) error {
return s.queries.PublishModuleVideo(ctx, videoID)
}
func (s *Store) GetPublishedVideosByModule(
ctx context.Context,
moduleID int64,
) ([]domain.ModuleVideo, error) {
rows, err := s.queries.GetPublishedVideosByModule(ctx, moduleID)
if err != nil {
return nil, err
}
videos := make([]domain.ModuleVideo, 0, len(rows))
for _, row := range rows {
var publishDate *time.Time
if row.PublishDate.Valid {
publishDate = &row.PublishDate.Time
}
videos = append(videos, domain.ModuleVideo{
ID: row.ID,
ModuleID: row.ModuleID,
Title: row.Title,
Description: &row.Description.String,
VideoURL: row.VideoUrl,
Duration: row.Duration,
Resolution: &row.Resolution.String,
InstructorID: &row.InstructorID.String,
Thumbnail: &row.Thumbnail.String,
Visibility: &row.Visibility.String,
IsPublished: row.IsPublished,
PublishDate: publishDate,
IsActive: row.IsActive,
})
}
return videos, nil
}
func (s *Store) UpdateModuleVideo(
ctx context.Context,
id int64,
title *string,
description *string,
videoURL *string,
duration *int32,
resolution *string,
visibility *string,
thumbnail *string,
isActive *bool,
) error {
return s.queries.UpdateModuleVideo(ctx, dbgen.UpdateModuleVideoParams{
Title: func() string {
if title != nil {
return *title
}
return ""
}(),
Description: pgtype.Text{String: *description},
VideoUrl: func() string {
if videoURL != nil {
return *videoURL
}
return ""
}(),
Duration: func() int32 {
if duration != nil {
return *duration
}
return 0
}(),
Resolution: pgtype.Text{String: *resolution},
Visibility: pgtype.Text{String: *visibility},
Thumbnail: pgtype.Text{String: *thumbnail},
IsActive: func() bool {
if isActive != nil {
return *isActive
}
return false
}(),
ID: id,
})
}
func (s *Store) DeleteModuleVideo(
ctx context.Context,
id int64,
) error {
return s.queries.DeleteModuleVideo(ctx, id)
}

View File

@ -0,0 +1,108 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreatePracticeQuestion(
ctx context.Context,
practiceID int64,
question string,
questionVoicePrompt *string,
sampleAnswerVoicePrompt *string,
sampleAnswer *string,
tips *string,
qType string,
) (domain.PracticeQuestion, error) {
row, err := s.queries.CreatePracticeQuestion(ctx, dbgen.CreatePracticeQuestionParams{
PracticeID: practiceID,
Question: question,
QuestionVoicePrompt: pgtype.Text{String: *questionVoicePrompt},
SampleAnswerVoicePrompt: pgtype.Text{String: *sampleAnswerVoicePrompt},
SampleAnswer: pgtype.Text{String: *sampleAnswer},
Tips: pgtype.Text{String: *tips},
Type: qType,
})
if err != nil {
return domain.PracticeQuestion{}, err
}
return domain.PracticeQuestion{
ID: row.ID,
PracticeID: row.PracticeID,
Question: row.Question,
QuestionVoicePrompt: &row.QuestionVoicePrompt.String,
SampleAnswerVoicePrompt: &row.SampleAnswerVoicePrompt.String,
SampleAnswer: &row.SampleAnswer.String,
Tips: &row.Tips.String,
Type: row.Type,
}, nil
}
func (s *Store) GetQuestionsByPractice(
ctx context.Context,
practiceID int64,
) ([]domain.PracticeQuestion, error) {
rows, err := s.queries.GetQuestionsByPractice(ctx, practiceID)
if err != nil {
return nil, err
}
questions := make([]domain.PracticeQuestion, 0, len(rows))
for _, row := range rows {
questions = append(questions, domain.PracticeQuestion{
ID: row.ID,
PracticeID: row.PracticeID,
Question: row.Question,
QuestionVoicePrompt: &row.QuestionVoicePrompt.String,
SampleAnswerVoicePrompt: &row.SampleAnswerVoicePrompt.String,
SampleAnswer: &row.SampleAnswer.String,
Tips: &row.Tips.String,
Type: row.Type,
})
}
return questions, nil
}
func (s *Store) UpdatePracticeQuestion(
ctx context.Context,
id int64,
question *string,
sampleAnswer *string,
tips *string,
qType *string,
) error {
return s.queries.UpdatePracticeQuestion(ctx, dbgen.UpdatePracticeQuestionParams{
Question: func() string {
if question != nil {
return *question
}
return ""
}(),
SampleAnswer: pgtype.Text{String: *sampleAnswer},
Tips: pgtype.Text{String: *tips},
Type: func() string {
if qType != nil {
return *qType
}
return ""
}(),
ID: id,
})
}
func (s *Store) DeletePracticeQuestion(
ctx context.Context,
id int64,
) error {
return s.queries.DeletePracticeQuestion(ctx, id)
}

View File

@ -0,0 +1,114 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreatePractice(
ctx context.Context,
ownerType string,
ownerID int64,
title string,
description *string,
bannerImage *string,
persona *string,
isActive *bool,
) (domain.Practice, error) {
row, err := s.queries.CreatePractice(ctx, dbgen.CreatePracticeParams{
OwnerType: ownerType,
OwnerID: ownerID,
Title: title,
Description: pgtype.Text{String: *description},
BannerImage: pgtype.Text{String: *bannerImage},
Persona: pgtype.Text{String: *persona},
Column7: isActive,
})
if err != nil {
return domain.Practice{}, err
}
return domain.Practice{
ID: row.ID,
OwnerType: row.OwnerType,
OwnerID: row.OwnerID,
Title: row.Title,
Description: &row.Description.String,
BannerImage: &row.BannerImage.String,
Persona: &row.Persona.String,
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetPracticesByOwner(
ctx context.Context,
ownerType string,
ownerID int64,
) ([]domain.Practice, error) {
rows, err := s.queries.GetPracticesByOwner(ctx, dbgen.GetPracticesByOwnerParams{
OwnerType: ownerType,
OwnerID: ownerID,
})
if err != nil {
return nil, err
}
practices := make([]domain.Practice, 0, len(rows))
for _, row := range rows {
practices = append(practices, domain.Practice{
ID: row.ID,
OwnerType: row.OwnerType,
OwnerID: row.OwnerID,
Title: row.Title,
Description: &row.Description.String,
BannerImage: &row.BannerImage.String,
Persona: &row.Persona.String,
IsActive: row.IsActive,
})
}
return practices, nil
}
func (s *Store) UpdatePractice(
ctx context.Context,
id int64,
title *string,
description *string,
bannerImage *string,
persona *string,
isActive *bool,
) error {
return s.queries.UpdatePractice(ctx, dbgen.UpdatePracticeParams{
Title: func() string {
if title != nil {
return *title
}
return ""
}(),
Description: pgtype.Text{String: *description},
BannerImage: pgtype.Text{String: *bannerImage},
Persona: pgtype.Text{String: *persona},
IsActive: func() bool {
if isActive != nil {
return *isActive
}
return false
}(),
ID: id,
})
}
func (s *Store) DeletePractice(
ctx context.Context,
id int64,
) error {
return s.queries.DeletePractice(ctx, id)
}

View File

@ -0,0 +1,125 @@
package repository
import (
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"context"
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateLevel(
ctx context.Context,
programID int64,
title string,
description *string,
levelIndex int,
isActive *bool,
) (domain.Level, error) {
row, err := s.queries.CreateLevel(ctx, dbgen.CreateLevelParams{
ProgramID: programID,
Title: title,
Description: pgtype.Text{String: *description},
LevelIndex: int32(levelIndex),
Column5: isActive,
})
if err != nil {
return domain.Level{}, err
}
return domain.Level{
ID: row.ID,
ProgramID: row.ProgramID,
Title: row.Title,
Description: &row.Description.String,
LevelIndex: int(row.LevelIndex),
NumberOfModules: int(row.NumberOfModules),
NumberOfPractices: int(row.NumberOfPractices),
NumberOfVideos: int(row.NumberOfVideos),
IsActive: row.IsActive,
}, nil
}
func (s *Store) GetLevelsByProgram(
ctx context.Context,
programID int64,
) ([]domain.Level, error) {
rows, err := s.queries.GetLevelsByProgram(ctx, programID)
if err != nil {
return nil, err
}
levels := make([]domain.Level, 0, len(rows))
for _, row := range rows {
levels = append(levels, domain.Level{
ID: row.ID,
ProgramID: row.ProgramID,
Title: row.Title,
Description: &row.Description.String,
LevelIndex: int(row.LevelIndex),
NumberOfModules: int(row.NumberOfModules),
NumberOfPractices: int(row.NumberOfPractices),
NumberOfVideos: int(row.NumberOfVideos),
IsActive: row.IsActive,
})
}
return levels, nil
}
func (s *Store) UpdateLevel(
ctx context.Context,
id int64,
title *string,
description *string,
levelIndex *int,
isActive *bool,
) error {
return s.queries.UpdateLevel(ctx, dbgen.UpdateLevelParams{
Title: func() string {
if title != nil {
return *title
}
return ""
}(),
Description: pgtype.Text{String: *description},
LevelIndex: int32(*levelIndex),
IsActive: *isActive,
ID: id,
})
}
func (s *Store) IncrementLevelModuleCount(
ctx context.Context,
levelID int64,
) error {
return s.queries.IncrementLevelModuleCount(ctx, levelID)
}
func (s *Store) IncrementLevelPracticeCount(
ctx context.Context,
levelID int64,
) error {
return s.queries.IncrementLevelPracticeCount(ctx, levelID)
}
func (s *Store) IncrementLevelVideoCount(
ctx context.Context,
levelID int64,
) error {
return s.queries.IncrementLevelVideoCount(ctx, levelID)
}
func (s *Store) DeleteLevel(
ctx context.Context,
levelID int64,
) error {
return s.queries.DeleteLevel(ctx, levelID)
}

View File

@ -0,0 +1,44 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreateCourseCategory(
ctx context.Context,
name string,
) (domain.CourseCategory, error) {
return s.courseStore.CreateCourseCategory(ctx, name)
}
func (s *Service) GetCourseCategoryByID(
ctx context.Context,
id int64,
) (domain.CourseCategory, error) {
return s.courseStore.GetCourseCategoryByID(ctx, id)
}
func (s *Service) GetAllCourseCategories(
ctx context.Context,
limit int32,
offset int32,
) ([]domain.CourseCategory, int64, error) {
return s.courseStore.GetAllCourseCategories(ctx, limit, offset)
}
func (s *Service) UpdateCourseCategory(
ctx context.Context,
id int64,
name *string,
isActive *bool,
) error {
return s.courseStore.UpdateCourseCategory(ctx, id, name, isActive)
}
func (s *Service) DeleteCourseCategory(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeleteCourseCategory(ctx, id)
}

View File

@ -0,0 +1,77 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreateProgram(
ctx context.Context,
courseID int64,
title string,
description *string,
thumbnail *string,
displayOrder *int32,
) (domain.Program, error) {
return s.courseStore.CreateProgram(ctx, courseID, title, description, thumbnail, displayOrder)
}
func (s *Service) GetProgramByID(
ctx context.Context,
id int64,
) (domain.Program, error) {
return s.courseStore.GetProgramByID(ctx, id)
}
func (s *Service) GetProgramsByCourse(
ctx context.Context,
courseID int64,
) ([]domain.Program, int64, error) {
return s.courseStore.GetProgramsByCourse(ctx, courseID)
}
func (s *Service) ListProgramsByCourse(
ctx context.Context,
courseID int64,
) ([]domain.Program, error) {
return s.courseStore.ListProgramsByCourse(ctx, courseID)
}
func (s *Service) ListActivePrograms(
ctx context.Context,
) ([]domain.Program, error) {
return s.courseStore.ListActivePrograms(ctx)
}
func (s *Service) UpdateProgramPartial(
ctx context.Context,
id int64,
title *string,
description *string,
thumbnail *string,
displayOrder *int32,
isActive *bool,
) error {
return s.courseStore.UpdateProgramPartial(ctx, id, title, description, thumbnail, displayOrder, isActive)
}
func (s *Service) UpdateProgramFull(
ctx context.Context,
program domain.Program,
) (domain.Program, error) {
return s.courseStore.UpdateProgramFull(ctx, program)
}
func (s *Service) DeactivateProgram(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeactivateProgram(ctx, id)
}
func (s *Service) DeleteProgram(
ctx context.Context,
id int64,
) (domain.Program, error) {
return s.courseStore.DeleteProgram(ctx, id)
}

View File

@ -0,0 +1,48 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreateCourse(
ctx context.Context,
categoryID int64,
title string,
description *string,
) (domain.Course, error) {
return s.courseStore.CreateCourse(ctx, categoryID, title, description)
}
func (s *Service) GetCourseByID(
ctx context.Context,
id int64,
) (domain.Course, error) {
return s.courseStore.GetCourseByID(ctx, id)
}
func (s *Service) GetCoursesByCategory(
ctx context.Context,
categoryID int64,
limit int32,
offset int32,
) ([]domain.Course, int64, error) {
return s.courseStore.GetCoursesByCategory(ctx, categoryID, limit, offset)
}
func (s *Service) UpdateCourse(
ctx context.Context,
id int64,
title *string,
description *string,
isActive *bool,
) error {
return s.courseStore.UpdateCourse(ctx, id, title, description, isActive)
}
func (s *Service) DeleteCourse(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeleteCourse(ctx, id)
}

View File

@ -0,0 +1,10 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) GetFullLearningTree(ctx context.Context) ([]domain.TreeCourse, error) {
return s.courseStore.GetFullLearningTree(ctx)
}

View File

@ -0,0 +1,41 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreateModule(
ctx context.Context,
levelID int64,
title string,
content *string,
displayOrder *int32,
) (domain.Module, error) {
return s.courseStore.CreateModule(ctx, levelID, title, content, displayOrder)
}
func (s *Service) GetModulesByLevel(
ctx context.Context,
levelID int64,
) ([]domain.Module, int64, error) {
return s.courseStore.GetModulesByLevel(ctx, levelID)
}
func (s *Service) UpdateModule(
ctx context.Context,
id int64,
title *string,
content *string,
displayOrder *int32,
isActive *bool,
) error {
return s.courseStore.UpdateModule(ctx, id, title, content, displayOrder, isActive)
}
func (s *Service) DeleteModule(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeleteModule(ctx, id)
}

View File

@ -0,0 +1,57 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreateModuleVideo(
ctx context.Context,
moduleID int64,
title string,
description *string,
videoURL string,
duration int32,
resolution *string,
instructorID *string,
thumbnail *string,
visibility *string,
) (domain.ModuleVideo, error) {
return s.courseStore.CreateModuleVideo(ctx, moduleID, title, description, videoURL, duration, resolution, instructorID, thumbnail, visibility)
}
func (s *Service) PublishModuleVideo(
ctx context.Context,
videoID int64,
) error {
return s.courseStore.PublishModuleVideo(ctx, videoID)
}
func (s *Service) GetPublishedVideosByModule(
ctx context.Context,
moduleID int64,
) ([]domain.ModuleVideo, error) {
return s.courseStore.GetPublishedVideosByModule(ctx, moduleID)
}
func (s *Service) UpdateModuleVideo(
ctx context.Context,
id int64,
title *string,
description *string,
videoURL *string,
duration *int32,
resolution *string,
visibility *string,
thumbnail *string,
isActive *bool,
) error {
return s.courseStore.UpdateModuleVideo(ctx, id, title, description, videoURL, duration, resolution, visibility, thumbnail, isActive)
}
func (s *Service) DeleteModuleVideo(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeleteModuleVideo(ctx, id)
}

View File

@ -0,0 +1,44 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreatePracticeQuestion(
ctx context.Context,
practiceID int64,
question string,
questionVoicePrompt *string,
sampleAnswerVoicePrompt *string,
sampleAnswer *string,
tips *string,
qType string,
) (domain.PracticeQuestion, error) {
return s.courseStore.CreatePracticeQuestion(ctx, practiceID, question, questionVoicePrompt, sampleAnswerVoicePrompt, sampleAnswer, tips, qType)
}
func (s *Service) GetQuestionsByPractice(
ctx context.Context,
practiceID int64,
) ([]domain.PracticeQuestion, error) {
return s.courseStore.GetQuestionsByPractice(ctx, practiceID)
}
func (s *Service) UpdatePracticeQuestion(
ctx context.Context,
id int64,
question *string,
sampleAnswer *string,
tips *string,
qType *string,
) error {
return s.courseStore.UpdatePracticeQuestion(ctx, id, question, sampleAnswer, tips, qType)
}
func (s *Service) DeletePracticeQuestion(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeletePracticeQuestion(ctx, id)
}

View File

@ -0,0 +1,46 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreatePractice(
ctx context.Context,
ownerType string,
ownerID int64,
title string,
description *string,
bannerImage *string,
persona *string,
isActive *bool,
) (domain.Practice, error) {
return s.courseStore.CreatePractice(ctx, ownerType, ownerID, title, description, bannerImage, persona, isActive)
}
func (s *Service) GetPracticesByOwner(
ctx context.Context,
ownerType string,
ownerID int64,
) ([]domain.Practice, error) {
return s.courseStore.GetPracticesByOwner(ctx, ownerType, ownerID)
}
func (s *Service) UpdatePractice(
ctx context.Context,
id int64,
title *string,
description *string,
bannerImage *string,
persona *string,
isActive *bool,
) error {
return s.courseStore.UpdatePractice(ctx, id, title, description, bannerImage, persona, isActive)
}
func (s *Service) DeletePractice(
ctx context.Context,
id int64,
) error {
return s.courseStore.DeletePractice(ctx, id)
}

View File

@ -0,0 +1,63 @@
package course_management
import (
"Yimaru-Backend/internal/domain"
"context"
)
func (s *Service) CreateLevel(
ctx context.Context,
programID int64,
title string,
description *string,
levelIndex int,
isActive *bool,
) (domain.Level, error) {
return s.courseStore.CreateLevel(ctx, programID, title, description, levelIndex, isActive)
}
func (s *Service) GetLevelsByProgram(
ctx context.Context,
programID int64,
) ([]domain.Level, error) {
return s.courseStore.GetLevelsByProgram(ctx, programID)
}
func (s *Service) UpdateLevel(
ctx context.Context,
id int64,
title *string,
description *string,
levelIndex *int,
isActive *bool,
) error {
return s.courseStore.UpdateLevel(ctx, id, title, description, levelIndex, isActive)
}
func (s *Service) IncrementLevelModuleCount(
ctx context.Context,
levelID int64,
) error {
return s.courseStore.IncrementLevelModuleCount(ctx, levelID)
}
func (s *Service) IncrementLevelPracticeCount(
ctx context.Context,
levelID int64,
) error {
return s.courseStore.IncrementLevelPracticeCount(ctx, levelID)
}
func (s *Service) IncrementLevelVideoCount(
ctx context.Context,
levelID int64,
) error {
return s.courseStore.IncrementLevelVideoCount(ctx, levelID)
}
func (s *Service) DeleteLevel(
ctx context.Context,
levelID int64,
) error {
return s.courseStore.DeleteLevel(ctx, levelID)
}

View File

@ -1,10 +1,7 @@
package course_management package course_management
import ( import (
"context"
"Yimaru-Backend/internal/config" "Yimaru-Backend/internal/config"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports" "Yimaru-Backend/internal/ports"
notificationservice "Yimaru-Backend/internal/services/notification" notificationservice "Yimaru-Backend/internal/services/notification"
) )
@ -32,183 +29,3 @@ func NewService(
config: cfg, config: cfg,
} }
} }
// Course category methods
func (s *Service) CreateCourseCategory(ctx context.Context, name string) (domain.CourseCategory, error) {
return s.courseStore.CreateCourseCategory(ctx, name)
}
func (s *Service) GetCourseCategoryByID(ctx context.Context, id int64) (domain.CourseCategory, error) {
return s.courseStore.GetCourseCategoryByID(ctx, id)
}
func (s *Service) ListActiveCourseCategories(ctx context.Context) ([]domain.CourseCategory, error) {
return s.courseStore.ListActiveCourseCategories(ctx)
}
func (s *Service) UpdateCourseCategory(ctx context.Context, id int64, name string, isActive bool) (domain.CourseCategory, error) {
return s.courseStore.UpdateCourseCategory(ctx, id, name, isActive)
}
func (s *Service) DeactivateCourseCategory(ctx context.Context, id int64) error {
return s.courseStore.DeactivateCourseCategory(ctx, id)
}
// Courses
func (s *Service) CreateCourse(ctx context.Context, c domain.Course) (domain.Course, error) {
return s.courseStore.CreateCourse(ctx, c)
}
func (s *Service) GetCourseByID(ctx context.Context, id int64) (domain.Course, error) {
return s.courseStore.GetCourseByID(ctx, id)
}
func (s *Service) ListCoursesByCategory(ctx context.Context, categoryID int64) ([]domain.Course, error) {
return s.courseStore.ListCoursesByCategory(ctx, categoryID)
}
func (s *Service) ListActiveCourses(ctx context.Context) ([]domain.Course, error) {
return s.courseStore.ListActiveCourses(ctx)
}
func (s *Service) UpdateCourse(ctx context.Context, c domain.Course) (domain.Course, error) {
return s.courseStore.UpdateCourse(ctx, c)
}
func (s *Service) DeactivateCourse(ctx context.Context, id int64) error {
return s.courseStore.DeactivateCourse(ctx, id)
}
// Programs
func (s *Service) CreateProgram(ctx context.Context, p domain.Program) (domain.Program, error) {
return s.courseStore.CreateProgram(ctx, p)
}
func (s *Service) GetProgramByID(ctx context.Context, id int64) (domain.Program, error) {
return s.courseStore.GetProgramByID(ctx, id)
}
func (s *Service) ListProgramsByCourse(ctx context.Context, courseID int64) ([]domain.Program, error) {
return s.courseStore.ListProgramsByCourse(ctx, courseID)
}
func (s *Service) ListActivePrograms(ctx context.Context) ([]domain.Program, error) {
return s.courseStore.ListActivePrograms(ctx)
}
func (s *Service) UpdateProgram(ctx context.Context, p domain.Program) (domain.Program, error) {
return s.courseStore.UpdateProgram(ctx, p)
}
func (s *Service) DeactivateProgram(ctx context.Context, id int64) error {
return s.courseStore.DeactivateProgram(ctx, id)
}
// Modules
func (s *Service) CreateModule(ctx context.Context, m domain.Module) (domain.Module, error) {
return s.courseStore.CreateModule(ctx, m)
}
func (s *Service) GetModuleByID(ctx context.Context, id int64) (domain.Module, error) {
return s.courseStore.GetModuleByID(ctx, id)
}
func (s *Service) ListModulesByLevel(ctx context.Context, levelID int64) ([]domain.Module, error) {
return s.courseStore.ListModulesByLevel(ctx, levelID)
}
func (s *Service) UpdateModule(ctx context.Context, m domain.Module) (domain.Module, error) {
return s.courseStore.UpdateModule(ctx, m)
}
func (s *Service) DeactivateModule(ctx context.Context, id int64) error {
return s.courseStore.DeactivateModule(ctx, id)
}
// Module videos
func (s *Service) CreateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error) {
return s.courseStore.CreateModuleVideo(ctx, v)
}
func (s *Service) GetModuleVideoByID(ctx context.Context, id int64) (domain.ModuleVideo, error) {
return s.courseStore.GetModuleVideoByID(ctx, id)
}
func (s *Service) ListAllVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error) {
return s.courseStore.ListAllVideosByModule(ctx, moduleID)
}
func (s *Service) ListPublishedVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error) {
return s.courseStore.ListPublishedVideosByModule(ctx, moduleID)
}
func (s *Service) UpdateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error) {
return s.courseStore.UpdateModuleVideo(ctx, v)
}
func (s *Service) DeactivateModuleVideo(ctx context.Context, id int64) error {
return s.courseStore.DeactivateModuleVideo(ctx, id)
}
// Practices
func (s *Service) CreatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error) {
return s.courseStore.CreatePractice(ctx, p)
}
func (s *Service) GetPracticeByID(ctx context.Context, id int64) (domain.Practice, error) {
return s.courseStore.GetPracticeByID(ctx, id)
}
func (s *Service) ListPracticesByOwner(ctx context.Context, ownerType string, ownerID int64) ([]domain.Practice, error) {
return s.courseStore.ListPracticesByOwner(ctx, ownerType, ownerID)
}
func (s *Service) UpdatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error) {
return s.courseStore.UpdatePractice(ctx, p)
}
func (s *Service) DeactivatePractice(ctx context.Context, id int64) error {
return s.courseStore.DeactivatePractice(ctx, id)
}
// Practice questions
func (s *Service) CreatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error) {
return s.courseStore.CreatePracticeQuestion(ctx, qn)
}
func (s *Service) GetPracticeQuestionByID(ctx context.Context, id int64) (domain.PracticeQuestion, error) {
return s.courseStore.GetPracticeQuestionByID(ctx, id)
}
func (s *Service) ListPracticeQuestions(ctx context.Context, practiceID int64) ([]domain.PracticeQuestion, error) {
return s.courseStore.ListPracticeQuestions(ctx, practiceID)
}
func (s *Service) UpdatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error) {
return s.courseStore.UpdatePracticeQuestion(ctx, qn)
}
func (s *Service) DeletePracticeQuestion(ctx context.Context, id int64) error {
return s.courseStore.DeletePracticeQuestion(ctx, id)
}
// Levels
func (s *Service) CreateLevel(ctx context.Context, l domain.Level) (domain.Level, error) {
return s.courseStore.CreateLevel(ctx, l)
}
func (s *Service) GetLevelByID(ctx context.Context, id int64) (domain.Level, error) {
return s.courseStore.GetLevelByID(ctx, id)
}
func (s *Service) ListLevelsByProgram(ctx context.Context, programID int64) ([]domain.Level, error) {
return s.courseStore.ListLevelsByProgram(ctx, programID)
}
func (s *Service) UpdateLevel(ctx context.Context, l domain.Level) (domain.Level, error) {
return s.courseStore.UpdateLevel(ctx, l)
}
func (s *Service) DeactivateLevel(ctx context.Context, id int64) error {
return s.courseStore.DeactivateLevel(ctx, id)
}

File diff suppressed because it is too large Load Diff

View File

@ -125,31 +125,68 @@ func (a *App) initAppRoutes() {
// ) // )
// Course Management Routes // Course Management Routes
groupV1.Post("/course-categories", h.CreateCourseCategory)
groupV1.Get("/course-categories", h.ListActiveCourseCategories)
groupV1.Get("/course-categories/:id", h.GetCourseCategoryByID)
groupV1.Put("/course-categories/:id", h.UpdateCourseCategory)
groupV1.Post("/course-categories/:id/deactivate", h.DeactivateCourseCategory)
groupV1.Post("/courses", h.CreateCourse) // Course Categories
groupV1.Get("/courses", h.ListActiveCourses) groupV1.Post("/course-management/categories", a.authMiddleware, h.CreateCourseCategory)
groupV1.Get("/courses/:id", h.GetCourseByID) groupV1.Get("/course-management/categories", a.authMiddleware, h.GetAllCourseCategories)
groupV1.Put("/courses/:id", h.UpdateCourse) groupV1.Get("/course-management/categories/:id", a.authMiddleware, h.GetCourseCategoryByID)
groupV1.Post("/courses/:id/deactivate", h.DeactivateCourse) groupV1.Put("/course-management/categories/:id", a.authMiddleware, h.UpdateCourseCategory)
groupV1.Get("/course-categories/:category_id/courses", h.ListCoursesByCategory) groupV1.Delete("/course-management/categories/:id", a.authMiddleware, h.DeleteCourseCategory)
groupV1.Post("/courses/:course_id/programs", h.CreateProgram) // Courses
groupV1.Get("/courses/:course_id/programs", h.ListProgramsByCourse) groupV1.Post("/course-management/courses", a.authMiddleware, h.CreateCourse)
groupV1.Get("/course-management/courses/:id", a.authMiddleware, h.GetCourseByID)
groupV1.Get("/course-management/categories/:categoryId/courses", a.authMiddleware, h.GetCoursesByCategory)
groupV1.Put("/course-management/courses/:id", a.authMiddleware, h.UpdateCourse)
groupV1.Delete("/course-management/courses/:id", a.authMiddleware, h.DeleteCourse)
groupV1.Post("/modules", h.CreateModule) // Programs
groupV1.Get("/levels/:level_id/modules", h.ListModulesByLevel) groupV1.Post("/course-management/programs", a.authMiddleware, h.CreateProgram)
groupV1.Get("/course-management/programs/:id", a.authMiddleware, h.GetProgramByID)
groupV1.Get("/course-management/courses/:courseId/programs", a.authMiddleware, h.GetProgramsByCourse)
groupV1.Get("/course-management/courses/:courseId/programs/list", a.authMiddleware, h.ListProgramsByCourse)
groupV1.Get("/course-management/programs/active", a.authMiddleware, h.ListActivePrograms)
groupV1.Patch("/course-management/programs/:id", a.authMiddleware, h.UpdateProgramPartial)
groupV1.Put("/course-management/programs/:id/full", a.authMiddleware, h.UpdateProgramFull)
groupV1.Put("/course-management/programs/:id/deactivate", a.authMiddleware, h.DeactivateProgram)
groupV1.Delete("/course-management/programs/:id", a.authMiddleware, h.DeleteProgram)
groupV1.Post("/module-videos", h.CreateModuleVideo) // Levels
groupV1.Post("/course-management/levels", a.authMiddleware, h.CreateLevel)
groupV1.Get("/course-management/programs/:programId/levels", a.authMiddleware, h.GetLevelsByProgram)
groupV1.Put("/course-management/levels/:id", a.authMiddleware, h.UpdateLevel)
groupV1.Put("/course-management/levels/:levelId/increment-module", a.authMiddleware, h.IncrementLevelModuleCount)
groupV1.Put("/course-management/levels/:levelId/increment-practice", a.authMiddleware, h.IncrementLevelPracticeCount)
groupV1.Put("/course-management/levels/:levelId/increment-video", a.authMiddleware, h.IncrementLevelVideoCount)
groupV1.Delete("/course-management/levels/:levelId", a.authMiddleware, h.DeleteLevel)
groupV1.Post("/practices", h.CreatePractice) // Modules
groupV1.Post("/practice-questions", h.CreatePracticeQuestion) groupV1.Post("/course-management/modules", a.authMiddleware, h.CreateModule)
groupV1.Get("/course-management/levels/:levelId/modules", a.authMiddleware, h.GetModulesByLevel)
groupV1.Put("/course-management/modules/:id", a.authMiddleware, h.UpdateModule)
groupV1.Delete("/course-management/modules/:id", a.authMiddleware, h.DeleteModule)
groupV1.Post("/levels", h.CreateLevel) // Module Videos
groupV1.Post("/course-management/videos", a.authMiddleware, h.CreateModuleVideo)
groupV1.Put("/course-management/videos/:videoId/publish", a.authMiddleware, h.PublishModuleVideo)
groupV1.Get("/course-management/modules/:moduleId/videos/published", a.authMiddleware, h.GetPublishedVideosByModule)
groupV1.Put("/course-management/videos/:id", a.authMiddleware, h.UpdateModuleVideo)
groupV1.Delete("/course-management/videos/:id", a.authMiddleware, h.DeleteModuleVideo)
// Practices
groupV1.Post("/course-management/practices", a.authMiddleware, h.CreatePractice)
groupV1.Get("/course-management/owners/:ownerType/:ownerId/practices", a.authMiddleware, h.GetPracticesByOwner)
groupV1.Put("/course-management/practices/:id", a.authMiddleware, h.UpdatePractice)
groupV1.Delete("/course-management/practices/:id", a.authMiddleware, h.DeletePractice)
// Practice Questions
groupV1.Post("/course-management/questions", a.authMiddleware, h.CreatePracticeQuestion)
groupV1.Get("/course-management/practices/:practiceId/questions", a.authMiddleware, h.GetQuestionsByPractice)
groupV1.Put("/course-management/questions/:id", a.authMiddleware, h.UpdatePracticeQuestion)
groupV1.Delete("/course-management/questions/:id", a.authMiddleware, h.DeletePracticeQuestion)
// Learning Tree
groupV1.Get("/course-management/learning-tree", a.authMiddleware, h.GetFullLearningTree)
// Auth Routes // Auth Routes
groupV1.Post("/auth/google/android", h.GoogleAndroidLogin) groupV1.Post("/auth/google/android", h.GoogleAndroidLogin)