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 might arrive late.', 3, 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),
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,
is_active
)
VALUES (
$1, -- name
$2 -- is_active
)
RETURNING
id,
name,
is_active,
created_at;
VALUES ($1, COALESCE($2, true))
RETURNING *;
-- name: GetCourseCategoryByID :one
SELECT
id,
name,
is_active,
created_at
SELECT *
FROM course_categories
WHERE id = $1;
-- name: ListActiveCourseCategories :many
-- name: GetAllCourseCategories :many
SELECT
COUNT(*) OVER () AS total_count,
id,
name,
is_active,
created_at
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
SET
name = $2,
is_active = $3
WHERE id = $1
RETURNING
id,
name,
is_active,
created_at;
name = COALESCE($1, name),
is_active = COALESCE($2, is_active)
WHERE id = $3;
-- name: DeactivateCourseCategory :exec
UPDATE course_categories
SET is_active = FALSE
-- name: DeleteCourseCategory :exec
DELETE FROM course_categories
WHERE id = $1;

View File

@ -7,14 +7,37 @@ INSERT INTO programs (
display_order,
is_active
)
VALUES (
$1, -- course_id
$2, -- title
$3, -- description
$4, -- thumbnail
$5, -- display_order
$6 -- is_active
)
VALUES ($1, $2, $3, $4, COALESCE($5, 0), COALESCE($6, true))
RETURNING *;
-- 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;
-- 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
id,
course_id,
@ -24,6 +47,7 @@ RETURNING
display_order,
is_active;
-- name: GetProgramByID :one
SELECT
id,
@ -63,7 +87,7 @@ FROM programs
WHERE is_active = TRUE
ORDER BY display_order ASC;
-- name: UpdateProgram :one
-- name: UpdateProgramFull :one
UPDATE programs
SET
course_id = $2,
@ -82,6 +106,7 @@ RETURNING
display_order,
is_active;
-- name: DeactivateProgram :exec
UPDATE programs
SET is_active = FALSE

View File

@ -5,31 +5,19 @@ INSERT INTO courses (
description,
is_active
)
VALUES (
$1, -- category_id
$2, -- title
$3, -- description
$4 -- is_active
)
RETURNING
id,
category_id,
title,
description,
is_active;
VALUES ($1, $2, $3, COALESCE($4, true))
RETURNING *;
-- name: GetCourseByID :one
SELECT
id,
category_id,
title,
description,
is_active
SELECT *
FROM courses
WHERE id = $1;
-- name: ListCoursesByCategory :many
-- name: GetCoursesByCategory :many
SELECT
COUNT(*) OVER () AS total_count,
id,
category_id,
title,
@ -37,37 +25,20 @@ SELECT
is_active
FROM courses
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
SET
category_id = $2,
title = $3,
description = $4,
is_active = $5
WHERE id = $1
RETURNING
id,
category_id,
title,
description,
is_active;
title = COALESCE($1, title),
description = COALESCE($2, description),
is_active = COALESCE($3, is_active)
WHERE id = $4;
-- name: DeactivateCourse :exec
UPDATE courses
SET is_active = FALSE
-- name: DeleteCourse :exec
DELETE FROM courses
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,
is_active
)
VALUES (
$1, -- level_id
$2, -- title
$3, -- content
$4, -- display_order
$5 -- is_active
)
RETURNING
id,
level_id,
title,
content,
display_order,
is_active;
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, true))
RETURNING *;
-- 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
COUNT(*) OVER () AS total_count,
id,
level_id,
title,
@ -42,26 +21,19 @@ SELECT
is_active
FROM modules
WHERE level_id = $1
AND is_active = TRUE
ORDER BY display_order ASC, id ASC;
ORDER BY display_order ASC;
-- name: UpdateModule :one
-- name: UpdateModule :exec
UPDATE modules
SET
title = $2,
content = $3,
display_order = $4,
is_active = $5
WHERE id = $1
RETURNING
id,
level_id,
title,
content,
display_order,
is_active;
title = COALESCE($1, title),
content = COALESCE($2, content),
display_order = COALESCE($3, display_order),
is_active = COALESCE($4, is_active)
WHERE id = $5;
-- name: DeactivateModule :exec
UPDATE modules
SET is_active = FALSE
-- name: DeleteModule :exec
DELETE FROM modules
WHERE id = $1;

View File

@ -6,136 +6,50 @@ INSERT INTO module_videos (
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
visibility,
is_active
)
VALUES (
$1, -- module_id
$2, -- title
$3, -- description
$4, -- video_url
$5, -- duration
$6, -- resolution
$7, -- is_published
$8, -- publish_date
$9, -- visibility
$10, -- instructor_id
$11, -- thumbnail
$12 -- is_active
$1, $2, $3, $4, $5, $6,
$7, $8, $9,
COALESCE($10, true)
)
RETURNING
id,
module_id,
title,
description,
video_url,
duration,
resolution,
is_published,
publish_date,
visibility,
instructor_id,
thumbnail,
is_active;
RETURNING *;
-- 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
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
-- name: PublishModuleVideo :exec
UPDATE module_videos
SET
title = $2,
description = $3,
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
is_published = true,
publish_date = CURRENT_TIMESTAMP
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,
type
)
VALUES (
$1, -- practice_id
$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;
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *;
-- 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
SELECT
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type
-- name: GetQuestionsByPractice :many
SELECT *
FROM practice_questions
WHERE practice_id = $1
ORDER BY id ASC;
-- name: UpdatePracticeQuestion :one
-- name: UpdatePracticeQuestion :exec
UPDATE practice_questions
SET
question = $2,
question_voice_prompt = $3,
sample_answer_voice_prompt = $4,
sample_answer = $5,
tips = $6,
type = $7
WHERE id = $1
RETURNING
id,
practice_id,
question,
question_voice_prompt,
sample_answer_voice_prompt,
sample_answer,
tips,
type;
question = COALESCE($1, question),
sample_answer = COALESCE($2, sample_answer),
tips = COALESCE($3, tips),
type = COALESCE($4, type)
WHERE id = $5;
-- name: DeletePracticeQuestion :exec
DELETE FROM practice_questions

View File

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

View File

@ -4,48 +4,15 @@ INSERT INTO levels (
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active
)
VALUES (
$1, -- program_id
$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;
VALUES ($1, $2, $3, $4, COALESCE($5, true))
RETURNING *;
-- 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
COUNT(*) OVER () AS total_count,
id,
program_id,
title,
@ -57,32 +24,37 @@ SELECT
is_active
FROM levels
WHERE program_id = $1
AND is_active = TRUE
ORDER BY level_index ASC;
-- name: UpdateLevel :one
-- name: UpdateLevel :exec
UPDATE levels
SET
title = $2,
description = $3,
level_index = $4,
number_of_modules = $5,
number_of_practices = $6,
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;
title = COALESCE($1, title),
description = COALESCE($2, description),
level_index = COALESCE($3, level_index),
is_active = COALESCE($4, is_active)
WHERE id = $5;
-- name: DeactivateLevel :exec
-- name: IncrementLevelModuleCount :exec
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;

View File

@ -26,7 +26,7 @@ services:
image: dpage/pgadmin4:latest
restart: always
ports:
- "5050:80"
- "5051:80"
environment:
PGADMIN_DEFAULT_EMAIL: admin@local.dev
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 (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateCourseCategory = `-- name: CreateCourseCategory :one
@ -14,24 +16,17 @@ INSERT INTO course_categories (
name,
is_active
)
VALUES (
$1, -- name
$2 -- is_active
)
RETURNING
id,
name,
is_active,
created_at
VALUES ($1, COALESCE($2, true))
RETURNING id, name, is_active, created_at
`
type CreateCourseCategoryParams struct {
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) {
row := q.db.QueryRow(ctx, CreateCourseCategory, arg.Name, arg.IsActive)
row := q.db.QueryRow(ctx, CreateCourseCategory, arg.Name, arg.Column2)
var i CourseCategory
err := row.Scan(
&i.ID,
@ -42,24 +37,71 @@ func (q *Queries) CreateCourseCategory(ctx context.Context, arg CreateCourseCate
return i, err
}
const DeactivateCourseCategory = `-- name: DeactivateCourseCategory :exec
UPDATE course_categories
SET is_active = FALSE
const DeleteCourseCategory = `-- name: DeleteCourseCategory :exec
DELETE FROM course_categories
WHERE id = $1
`
func (q *Queries) DeactivateCourseCategory(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateCourseCategory, id)
func (q *Queries) DeleteCourseCategory(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteCourseCategory, id)
return err
}
const GetCourseCategoryByID = `-- name: GetCourseCategoryByID :one
const GetAllCourseCategories = `-- name: GetAllCourseCategories :many
SELECT
COUNT(*) OVER () AS total_count,
id,
name,
is_active,
created_at
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
`
@ -75,69 +117,21 @@ func (q *Queries) GetCourseCategoryByID(ctx context.Context, id int64) (CourseCa
return i, err
}
const ListActiveCourseCategories = `-- name: ListActiveCourseCategories :many
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
const UpdateCourseCategory = `-- name: UpdateCourseCategory :exec
UPDATE course_categories
SET
name = $2,
is_active = $3
WHERE id = $1
RETURNING
id,
name,
is_active,
created_at
name = COALESCE($1, name),
is_active = COALESCE($2, is_active)
WHERE id = $3
`
type UpdateCourseCategoryParams struct {
ID int64 `json:"id"`
Name string `json:"name"`
IsActive bool `json:"is_active"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateCourseCategory(ctx context.Context, arg UpdateCourseCategoryParams) (CourseCategory, error) {
row := q.db.QueryRow(ctx, UpdateCourseCategory, arg.ID, arg.Name, arg.IsActive)
var i CourseCategory
err := row.Scan(
&i.ID,
&i.Name,
&i.IsActive,
&i.CreatedAt,
)
return i, err
func (q *Queries) UpdateCourseCategory(ctx context.Context, arg UpdateCourseCategoryParams) error {
_, err := q.db.Exec(ctx, UpdateCourseCategory, arg.Name, arg.IsActive, arg.ID)
return err
}

View File

@ -20,22 +20,8 @@ INSERT INTO programs (
display_order,
is_active
)
VALUES (
$1, -- course_id
$2, -- title
$3, -- description
$4, -- thumbnail
$5, -- display_order
$6 -- is_active
)
RETURNING
id,
course_id,
title,
description,
thumbnail,
display_order,
is_active
VALUES ($1, $2, $3, $4, COALESCE($5, 0), COALESCE($6, true))
RETURNING id, course_id, title, description, thumbnail, display_order, is_active
`
type CreateProgramParams struct {
@ -43,8 +29,8 @@ type CreateProgramParams 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"`
Column5 interface{} `json:"column_5"`
Column6 interface{} `json:"column_6"`
}
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.Description,
arg.Thumbnail,
arg.DisplayOrder,
arg.IsActive,
arg.Column5,
arg.Column6,
)
var i Program
err := row.Scan(
@ -80,6 +66,34 @@ func (q *Queries) DeactivateProgram(ctx context.Context, id int64) error {
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
SELECT
id,
@ -108,6 +122,61 @@ func (q *Queries) GetProgramByID(ctx context.Context, id int64) (Program, error)
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
SELECT
id,
@ -193,7 +262,7 @@ func (q *Queries) ListProgramsByCourse(ctx context.Context, courseID int64) ([]P
return items, nil
}
const UpdateProgram = `-- name: UpdateProgram :one
const UpdateProgramFull = `-- name: UpdateProgramFull :one
UPDATE programs
SET
course_id = $2,
@ -213,7 +282,7 @@ RETURNING
is_active
`
type UpdateProgramParams struct {
type UpdateProgramFullParams struct {
ID int64 `json:"id"`
CourseID int64 `json:"course_id"`
Title string `json:"title"`
@ -223,8 +292,8 @@ type UpdateProgramParams struct {
IsActive bool `json:"is_active"`
}
func (q *Queries) UpdateProgram(ctx context.Context, arg UpdateProgramParams) (Program, error) {
row := q.db.QueryRow(ctx, UpdateProgram,
func (q *Queries) UpdateProgramFull(ctx context.Context, arg UpdateProgramFullParams) (Program, error) {
row := q.db.QueryRow(ctx, UpdateProgramFull,
arg.ID,
arg.CourseID,
arg.Title,
@ -245,3 +314,35 @@ func (q *Queries) UpdateProgram(ctx context.Context, arg UpdateProgramParams) (P
)
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,
is_active
)
VALUES (
$1, -- category_id
$2, -- title
$3, -- description
$4 -- is_active
)
RETURNING
id,
category_id,
title,
description,
is_active
VALUES ($1, $2, $3, COALESCE($4, true))
RETURNING id, category_id, title, description, is_active
`
type CreateCourseParams struct {
CategoryID int64 `json:"category_id"`
Title string `json:"title"`
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) {
@ -44,7 +34,7 @@ func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Cou
arg.CategoryID,
arg.Title,
arg.Description,
arg.IsActive,
arg.Column4,
)
var i Course
err := row.Scan(
@ -57,24 +47,18 @@ func (q *Queries) CreateCourse(ctx context.Context, arg CreateCourseParams) (Cou
return i, err
}
const DeactivateCourse = `-- name: DeactivateCourse :exec
UPDATE courses
SET is_active = FALSE
const DeleteCourse = `-- name: DeleteCourse :exec
DELETE FROM courses
WHERE id = $1
`
func (q *Queries) DeactivateCourse(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateCourse, id)
func (q *Queries) DeleteCourse(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteCourse, id)
return err
}
const GetCourseByID = `-- name: GetCourseByID :one
SELECT
id,
category_id,
title,
description,
is_active
SELECT id, category_id, title, description, is_active
FROM courses
WHERE id = $1
`
@ -92,46 +76,9 @@ func (q *Queries) GetCourseByID(ctx context.Context, id int64) (Course, error) {
return i, err
}
const ListActiveCourses = `-- name: ListActiveCourses :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
const GetCoursesByCategory = `-- name: GetCoursesByCategory :many
SELECT
COUNT(*) OVER () AS total_count,
id,
category_id,
title,
@ -139,20 +86,37 @@ SELECT
is_active
FROM courses
WHERE category_id = $1
AND is_active = TRUE
ORDER BY id DESC
LIMIT $3::INT
OFFSET $2::INT
`
func (q *Queries) ListCoursesByCategory(ctx context.Context, categoryID int64) ([]Course, error) {
rows, err := q.db.Query(ctx, ListCoursesByCategory, categoryID)
type GetCoursesByCategoryParams struct {
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 {
return nil, err
}
defer rows.Close()
var items []Course
var items []GetCoursesByCategoryRow
for rows.Next() {
var i Course
var i GetCoursesByCategoryRow
if err := rows.Scan(
&i.TotalCount,
&i.ID,
&i.CategoryID,
&i.Title,
@ -169,45 +133,28 @@ func (q *Queries) ListCoursesByCategory(ctx context.Context, categoryID int64) (
return items, nil
}
const UpdateCourse = `-- name: UpdateCourse :one
const UpdateCourse = `-- name: UpdateCourse :exec
UPDATE courses
SET
category_id = $2,
title = $3,
description = $4,
is_active = $5
WHERE id = $1
RETURNING
id,
category_id,
title,
description,
is_active
title = COALESCE($1, title),
description = COALESCE($2, description),
is_active = COALESCE($3, is_active)
WHERE id = $4
`
type UpdateCourseParams struct {
ID int64 `json:"id"`
CategoryID int64 `json:"category_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
IsActive bool `json:"is_active"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateCourse(ctx context.Context, arg UpdateCourseParams) (Course, error) {
row := q.db.QueryRow(ctx, UpdateCourse,
arg.ID,
arg.CategoryID,
func (q *Queries) UpdateCourse(ctx context.Context, arg UpdateCourseParams) error {
_, err := q.db.Exec(ctx, UpdateCourse,
arg.Title,
arg.Description,
arg.IsActive,
arg.ID,
)
var i Course
err := row.Scan(
&i.ID,
&i.CategoryID,
&i.Title,
&i.Description,
&i.IsActive,
)
return i, err
return 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,
is_active
)
VALUES (
$1, -- level_id
$2, -- title
$3, -- content
$4, -- display_order
$5 -- is_active
)
RETURNING
id,
level_id,
title,
content,
display_order,
is_active
VALUES ($1, $2, $3, COALESCE($4, 0), COALESCE($5, true))
RETURNING id, level_id, title, content, display_order, is_active
`
type CreateModuleParams struct {
LevelID int64 `json:"level_id"`
Title string `json:"title"`
Content pgtype.Text `json:"content"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
Column4 interface{} `json:"column_4"`
Column5 interface{} `json:"column_5"`
}
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.Title,
arg.Content,
arg.DisplayOrder,
arg.IsActive,
arg.Column4,
arg.Column5,
)
var i Module
err := row.Scan(
@ -63,45 +51,19 @@ func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Mod
return i, err
}
const DeactivateModule = `-- name: DeactivateModule :exec
UPDATE modules
SET is_active = FALSE
const DeleteModule = `-- name: DeleteModule :exec
DELETE FROM modules
WHERE id = $1
`
func (q *Queries) DeactivateModule(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateModule, id)
func (q *Queries) DeleteModule(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteModule, id)
return err
}
const GetModuleByID = `-- name: GetModuleByID :one
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
const GetModulesByLevel = `-- name: GetModulesByLevel :many
SELECT
COUNT(*) OVER () AS total_count,
id,
level_id,
title,
@ -110,20 +72,30 @@ SELECT
is_active
FROM modules
WHERE level_id = $1
AND is_active = TRUE
ORDER BY display_order ASC, id ASC
ORDER BY display_order ASC
`
func (q *Queries) ListModulesByLevel(ctx context.Context, levelID int64) ([]Module, error) {
rows, err := q.db.Query(ctx, ListModulesByLevel, levelID)
type GetModulesByLevelRow struct {
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 {
return nil, err
}
defer rows.Close()
var items []Module
var items []GetModulesByLevelRow
for rows.Next() {
var i Module
var i GetModulesByLevelRow
if err := rows.Scan(
&i.TotalCount,
&i.ID,
&i.LevelID,
&i.Title,
@ -141,47 +113,31 @@ func (q *Queries) ListModulesByLevel(ctx context.Context, levelID int64) ([]Modu
return items, nil
}
const UpdateModule = `-- name: UpdateModule :one
const UpdateModule = `-- name: UpdateModule :exec
UPDATE modules
SET
title = $2,
content = $3,
display_order = $4,
is_active = $5
WHERE id = $1
RETURNING
id,
level_id,
title,
content,
display_order,
is_active
title = COALESCE($1, title),
content = COALESCE($2, content),
display_order = COALESCE($3, display_order),
is_active = COALESCE($4, is_active)
WHERE id = $5
`
type UpdateModuleParams struct {
ID int64 `json:"id"`
Title string `json:"title"`
Content pgtype.Text `json:"content"`
DisplayOrder int32 `json:"display_order"`
IsActive bool `json:"is_active"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateModule(ctx context.Context, arg UpdateModuleParams) (Module, error) {
row := q.db.QueryRow(ctx, UpdateModule,
arg.ID,
func (q *Queries) UpdateModule(ctx context.Context, arg UpdateModuleParams) error {
_, err := q.db.Exec(ctx, UpdateModule,
arg.Title,
arg.Content,
arg.DisplayOrder,
arg.IsActive,
arg.ID,
)
var i Module
err := row.Scan(
&i.ID,
&i.LevelID,
&i.Title,
&i.Content,
&i.DisplayOrder,
&i.IsActive,
)
return i, err
return err
}

View File

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

View File

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

View File

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

View File

@ -17,31 +17,10 @@ INSERT INTO levels (
title,
description,
level_index,
number_of_modules,
number_of_practices,
number_of_videos,
is_active
)
VALUES (
$1, -- program_id
$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
VALUES ($1, $2, $3, $4, COALESCE($5, true))
RETURNING id, program_id, title, description, level_index, number_of_modules, number_of_practices, number_of_videos, is_active
`
type CreateLevelParams struct {
@ -49,10 +28,7 @@ type CreateLevelParams struct {
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"`
Column5 interface{} `json:"column_5"`
}
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.Description,
arg.LevelIndex,
arg.NumberOfModules,
arg.NumberOfPractices,
arg.NumberOfVideos,
arg.IsActive,
arg.Column5,
)
var i Level
err := row.Scan(
@ -81,51 +54,19 @@ func (q *Queries) CreateLevel(ctx context.Context, arg CreateLevelParams) (Level
return i, err
}
const DeactivateLevel = `-- name: DeactivateLevel :exec
UPDATE levels
SET is_active = FALSE
const DeleteLevel = `-- name: DeleteLevel :exec
DELETE FROM levels
WHERE id = $1
`
func (q *Queries) DeactivateLevel(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeactivateLevel, id)
func (q *Queries) DeleteLevel(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteLevel, id)
return err
}
const GetLevelByID = `-- 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
`
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
const GetLevelsByProgram = `-- name: GetLevelsByProgram :many
SELECT
COUNT(*) OVER () AS total_count,
id,
program_id,
title,
@ -137,20 +78,33 @@ SELECT
is_active
FROM levels
WHERE program_id = $1
AND is_active = TRUE
ORDER BY level_index ASC
`
func (q *Queries) ListLevelsByProgram(ctx context.Context, programID int64) ([]Level, error) {
rows, err := q.db.Query(ctx, ListLevelsByProgram, programID)
type GetLevelsByProgramRow struct {
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 {
return nil, err
}
defer rows.Close()
var items []Level
var items []GetLevelsByProgramRow
for rows.Next() {
var i Level
var i GetLevelsByProgramRow
if err := rows.Scan(
&i.TotalCount,
&i.ID,
&i.ProgramID,
&i.Title,
@ -171,62 +125,64 @@ func (q *Queries) ListLevelsByProgram(ctx context.Context, programID int64) ([]L
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
SET
title = $2,
description = $3,
level_index = $4,
number_of_modules = $5,
number_of_practices = $6,
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
title = COALESCE($1, title),
description = COALESCE($2, description),
level_index = COALESCE($3, level_index),
is_active = COALESCE($4, is_active)
WHERE id = $5
`
type UpdateLevelParams struct {
ID int64 `json:"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"`
ID int64 `json:"id"`
}
func (q *Queries) UpdateLevel(ctx context.Context, arg UpdateLevelParams) (Level, error) {
row := q.db.QueryRow(ctx, UpdateLevel,
arg.ID,
func (q *Queries) UpdateLevel(ctx context.Context, arg UpdateLevelParams) error {
_, err := q.db.Exec(ctx, UpdateLevel,
arg.Title,
arg.Description,
arg.LevelIndex,
arg.NumberOfModules,
arg.NumberOfPractices,
arg.NumberOfVideos,
arg.IsActive,
arg.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
return 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
import (
"context"
"Yimaru-Backend/internal/domain"
"context"
)
type CourseStore interface {
CreateCourseCategory(ctx context.Context, name string) (domain.CourseCategory, error)
GetCourseCategoryByID(ctx context.Context, Id int64) (domain.CourseCategory, error)
ListActiveCourseCategories(ctx context.Context) ([]domain.CourseCategory, error)
UpdateCourseCategory(ctx context.Context, id int64, name string, isActive bool) (domain.CourseCategory, error)
DeactivateCourseCategory(ctx context.Context, id int64) error
CreateCourse(ctx context.Context, c domain.Course) (domain.Course, error)
GetCourseByID(ctx context.Context, id int64) (domain.Course, error)
ListCoursesByCategory(ctx context.Context, categoryID int64) ([]domain.Course, error)
ListActiveCourses(ctx context.Context) ([]domain.Course, error)
UpdateCourse(ctx context.Context, c domain.Course) (domain.Course, error)
DeactivateCourse(ctx context.Context, id int64) error
CreateProgram(ctx context.Context, p domain.Program) (domain.Program, error)
GetProgramByID(ctx context.Context, id int64) (domain.Program, error)
ListProgramsByCourse(ctx context.Context, courseID int64) ([]domain.Program, error)
ListActivePrograms(ctx context.Context) ([]domain.Program, error)
UpdateProgram(ctx context.Context, p domain.Program) (domain.Program, error)
DeactivateProgram(ctx context.Context, id int64) error
CreateModule(ctx context.Context, m domain.Module) (domain.Module, error)
GetModuleByID(ctx context.Context, id int64) (domain.Module, error)
ListModulesByLevel(ctx context.Context, levelID int64) ([]domain.Module, error)
UpdateModule(ctx context.Context, m domain.Module) (domain.Module, error)
DeactivateModule(ctx context.Context, id int64) error
CreateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error)
GetModuleVideoByID(ctx context.Context, id int64) (domain.ModuleVideo, error)
ListAllVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error)
ListPublishedVideosByModule(ctx context.Context, moduleID int64) ([]domain.ModuleVideo, error)
UpdateModuleVideo(ctx context.Context, v domain.ModuleVideo) (domain.ModuleVideo, error)
DeactivateModuleVideo(ctx context.Context, id int64) error
CreatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error)
GetPracticeByID(ctx context.Context, id int64) (domain.Practice, error)
ListPracticesByOwner(ctx context.Context, ownerType string, ownerID int64) ([]domain.Practice, error)
UpdatePractice(ctx context.Context, p domain.Practice) (domain.Practice, error)
DeactivatePractice(ctx context.Context, id int64) error
CreatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error)
GetPracticeQuestionByID(ctx context.Context, id int64) (domain.PracticeQuestion, error)
ListPracticeQuestions(ctx context.Context, practiceID int64) ([]domain.PracticeQuestion, error)
UpdatePracticeQuestion(ctx context.Context, qn domain.PracticeQuestion) (domain.PracticeQuestion, error)
DeletePracticeQuestion(ctx context.Context, id int64) error
CreateLevel(ctx context.Context, l domain.Level) (domain.Level, error)
GetLevelByID(ctx context.Context, id int64) (domain.Level, error)
ListLevelsByProgram(ctx context.Context, programID int64) ([]domain.Level, error)
UpdateLevel(ctx context.Context, l domain.Level) (domain.Level, error)
DeactivateLevel(ctx context.Context, id int64) error
CreateCourseCategory(
ctx context.Context,
name string,
) (domain.CourseCategory, error)
GetCourseCategoryByID(
ctx context.Context,
id int64,
) (domain.CourseCategory, error)
GetAllCourseCategories(
ctx context.Context,
limit int32,
offset int32,
) ([]domain.CourseCategory, int64, error)
UpdateCourseCategory(
ctx context.Context,
id int64,
name *string,
isActive *bool,
) error
DeleteCourseCategory(
ctx context.Context,
id int64,
) error
CreateProgram(
ctx context.Context,
courseID int64,
title string,
description *string,
thumbnail *string,
displayOrder *int32,
) (domain.Program, error)
GetProgramByID(
ctx context.Context,
id int64,
) (domain.Program, error)
GetProgramsByCourse(
ctx context.Context,
courseID int64,
) ([]domain.Program, int64, error)
ListProgramsByCourse(
ctx context.Context,
courseID int64,
) ([]domain.Program, error)
ListActivePrograms(
ctx context.Context,
) ([]domain.Program, error)
UpdateProgramPartial(
ctx context.Context,
id int64,
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 (s *Store) CreateCourseCategory(ctx context.Context, name string) (domain.CourseCategory, error) {
tempCategory, err := s.queries.CreateCourseCategory(ctx, dbgen.CreateCourseCategoryParams{
func (s *Store) CreateCourseCategory(
ctx context.Context,
name string,
) (domain.CourseCategory, error) {
row, err := s.queries.CreateCourseCategory(ctx, dbgen.CreateCourseCategoryParams{
Name: name,
IsActive: 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,
Column2: true,
})
if err != nil {
return domain.CourseCategory{}, err
@ -83,752 +32,90 @@ func (s *Store) UpdateCourseCategory(ctx context.Context, id int64, name string,
}, nil
}
func (s *Store) DeactivateCourseCategory(ctx context.Context, id int64) error {
return s.queries.DeactivateCourseCategory(ctx, id)
}
func (s *Store) GetCourseCategoryByID(
ctx context.Context,
id int64,
) (domain.CourseCategory, error) {
// Course related methods
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,
})
row, err := s.queries.GetCourseCategoryByID(ctx, id)
if err != nil {
return domain.Course{}, err
return domain.CourseCategory{}, err
}
return domain.Course{
return domain.CourseCategory{
ID: row.ID,
CategoryID: row.CategoryID,
Title: row.Title,
Description: row.Description.String,
Name: row.Name,
IsActive: row.IsActive,
CreatedAt: row.CreatedAt.Time,
}, nil
}
func (s *Store) GetCourseByID(ctx context.Context, id int64) (domain.Course, error) {
row, err := s.queries.GetCourseByID(ctx, id)
func (s *Store) GetAllCourseCategories(
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 {
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,
CategoryID: row.CategoryID,
Title: row.Title,
Description: row.Description.String,
Name: row.Name,
IsActive: row.IsActive,
}, nil
}
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,
CreatedAt: row.CreatedAt.Time,
})
}
return res, nil
return categories, totalCount, nil
}
func (s *Store) ListActiveCourses(ctx context.Context) ([]domain.Course, error) {
rows, err := s.queries.ListActiveCourses(ctx)
if err != nil {
return nil, err
func (s *Store) UpdateCourseCategory(
ctx context.Context,
id int64,
name *string,
isActive *bool,
) error {
var (
nameVal string
isActiveVal bool
)
if name != nil {
nameVal = *name
}
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,
if isActive != nil {
isActiveVal = *isActive
}
return s.queries.UpdateCourseCategory(ctx, dbgen.UpdateCourseCategoryParams{
Name: nameVal,
IsActive: isActiveVal,
ID: id,
})
}
return res, nil
}
func (s *Store) UpdateCourse(ctx context.Context, c domain.Course) (domain.Course, error) {
row, err := s.queries.UpdateCourse(ctx, dbgen.UpdateCourseParams{
ID: c.ID,
CategoryID: c.CategoryID,
Title: c.Title,
Description: pgtype.Text{String: c.Description, Valid: c.Description != ""},
IsActive: c.IsActive,
})
if err != nil {
return domain.Course{}, err
}
func (s *Store) DeleteCourseCategory(
ctx context.Context,
id int64,
) error {
return domain.Course{
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)
return s.queries.DeleteCourseCategory(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
import (
"context"
"Yimaru-Backend/internal/config"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
notificationservice "Yimaru-Backend/internal/services/notification"
)
@ -32,183 +29,3 @@ func NewService(
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
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)
groupV1.Get("/courses", h.ListActiveCourses)
groupV1.Get("/courses/:id", h.GetCourseByID)
groupV1.Put("/courses/:id", h.UpdateCourse)
groupV1.Post("/courses/:id/deactivate", h.DeactivateCourse)
groupV1.Get("/course-categories/:category_id/courses", h.ListCoursesByCategory)
// Course Categories
groupV1.Post("/course-management/categories", a.authMiddleware, h.CreateCourseCategory)
groupV1.Get("/course-management/categories", a.authMiddleware, h.GetAllCourseCategories)
groupV1.Get("/course-management/categories/:id", a.authMiddleware, h.GetCourseCategoryByID)
groupV1.Put("/course-management/categories/:id", a.authMiddleware, h.UpdateCourseCategory)
groupV1.Delete("/course-management/categories/:id", a.authMiddleware, h.DeleteCourseCategory)
groupV1.Post("/courses/:course_id/programs", h.CreateProgram)
groupV1.Get("/courses/:course_id/programs", h.ListProgramsByCourse)
// Courses
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)
groupV1.Get("/levels/:level_id/modules", h.ListModulesByLevel)
// Programs
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)
groupV1.Post("/practice-questions", h.CreatePracticeQuestion)
// Modules
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
groupV1.Post("/auth/google/android", h.GoogleAndroidLogin)