Add lesson detail retrieval endpoints.
Expose APIs to list lessons by submodule and fetch a single lesson by ID, including title, description, intro video URL, and question count. Made-with: Cursor
This commit is contained in:
parent
d686bdf8bd
commit
01914cb81e
|
|
@ -60,6 +60,25 @@ WHERE smp.sub_module_id = $1
|
|||
AND qs.set_type = 'QUIZ'
|
||||
ORDER BY smp.display_order ASC, smp.id ASC;
|
||||
|
||||
-- name: GetSubModuleLessonByID :one
|
||||
SELECT
|
||||
smp.id,
|
||||
smp.sub_module_id,
|
||||
smp.question_set_id,
|
||||
smp.intro_video_url,
|
||||
smp.display_order,
|
||||
smp.is_active,
|
||||
qs.title,
|
||||
qs.description,
|
||||
qs.status,
|
||||
qs.set_type,
|
||||
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
|
||||
FROM sub_module_lessons smp
|
||||
JOIN question_sets qs ON qs.id = smp.question_set_id
|
||||
WHERE smp.id = $1
|
||||
AND smp.is_active = TRUE
|
||||
AND qs.set_type = 'QUIZ';
|
||||
|
||||
-- name: GetSubModulePractices :many
|
||||
SELECT
|
||||
smp.id,
|
||||
|
|
|
|||
|
|
@ -544,6 +544,59 @@ func (q *Queries) GetModulesByLevelID(ctx context.Context, levelID int64) ([]Mod
|
|||
return items, nil
|
||||
}
|
||||
|
||||
const GetSubModuleLessonByID = `-- name: GetSubModuleLessonByID :one
|
||||
SELECT
|
||||
smp.id,
|
||||
smp.sub_module_id,
|
||||
smp.question_set_id,
|
||||
smp.intro_video_url,
|
||||
smp.display_order,
|
||||
smp.is_active,
|
||||
qs.title,
|
||||
qs.description,
|
||||
qs.status,
|
||||
qs.set_type,
|
||||
(SELECT COUNT(*) FROM question_set_items qsi WHERE qsi.set_id = qs.id) AS question_count
|
||||
FROM sub_module_lessons smp
|
||||
JOIN question_sets qs ON qs.id = smp.question_set_id
|
||||
WHERE smp.id = $1
|
||||
AND smp.is_active = TRUE
|
||||
AND qs.set_type = 'QUIZ'
|
||||
`
|
||||
|
||||
type GetSubModuleLessonByIDRow struct {
|
||||
ID int64 `json:"id"`
|
||||
SubModuleID int64 `json:"sub_module_id"`
|
||||
QuestionSetID int64 `json:"question_set_id"`
|
||||
IntroVideoUrl pgtype.Text `json:"intro_video_url"`
|
||||
DisplayOrder int32 `json:"display_order"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Title string `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
Status string `json:"status"`
|
||||
SetType string `json:"set_type"`
|
||||
QuestionCount int64 `json:"question_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetSubModuleLessonByID(ctx context.Context, id int64) (GetSubModuleLessonByIDRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetSubModuleLessonByID, id)
|
||||
var i GetSubModuleLessonByIDRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SubModuleID,
|
||||
&i.QuestionSetID,
|
||||
&i.IntroVideoUrl,
|
||||
&i.DisplayOrder,
|
||||
&i.IsActive,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.Status,
|
||||
&i.SetType,
|
||||
&i.QuestionCount,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetSubModuleLessons = `-- name: GetSubModuleLessons :many
|
||||
SELECT
|
||||
smp.id,
|
||||
|
|
|
|||
|
|
@ -945,6 +945,75 @@ func (h *Handler) AttachSubModuleLesson(c *fiber.Ctx) error {
|
|||
return c.Status(fiber.StatusCreated).JSON(domain.Response{Message: "Lesson attached to sub-module", Data: attached})
|
||||
}
|
||||
|
||||
// GetSubModuleLessons godoc
|
||||
// @Summary Get lessons under sub-module
|
||||
// @Description Returns all active lessons attached to a sub-module with question-set details
|
||||
// @Tags course-management
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param subModuleId path int true "Sub-module ID"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/course-management/sub-modules/{subModuleId}/lessons [get]
|
||||
func (h *Handler) GetSubModuleLessons(c *fiber.Ctx) error {
|
||||
subModuleID, err := strconv.ParseInt(c.Params("subModuleId"), 10, 64)
|
||||
if err != nil || subModuleID <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid sub-module ID",
|
||||
Error: "subModuleId must be a valid positive integer",
|
||||
})
|
||||
}
|
||||
|
||||
lessons, err := h.analyticsDB.GetSubModuleLessons(c.Context(), subModuleID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
||||
Message: "Failed to get sub-module lessons",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "Sub-module lessons retrieved successfully",
|
||||
Data: lessons,
|
||||
})
|
||||
}
|
||||
|
||||
// GetSubModuleLessonByID godoc
|
||||
// @Summary Get lesson detail
|
||||
// @Description Returns one active lesson detail by lesson ID
|
||||
// @Tags course-management
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param lessonId path int true "Lesson ID"
|
||||
// @Success 200 {object} domain.Response
|
||||
// @Failure 400 {object} domain.ErrorResponse
|
||||
// @Failure 404 {object} domain.ErrorResponse
|
||||
// @Failure 500 {object} domain.ErrorResponse
|
||||
// @Router /api/v1/course-management/sub-module-lessons/{lessonId} [get]
|
||||
func (h *Handler) GetSubModuleLessonByID(c *fiber.Ctx) error {
|
||||
lessonID, err := strconv.ParseInt(c.Params("lessonId"), 10, 64)
|
||||
if err != nil || lessonID <= 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||
Message: "Invalid lesson ID",
|
||||
Error: "lessonId must be a valid positive integer",
|
||||
})
|
||||
}
|
||||
|
||||
lesson, err := h.analyticsDB.GetSubModuleLessonByID(c.Context(), lessonID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(domain.ErrorResponse{
|
||||
Message: "Lesson not found",
|
||||
Error: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
||||
Message: "Lesson detail retrieved successfully",
|
||||
Data: lesson,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateSubModulePractice godoc
|
||||
// @Summary Create practice under sub-module
|
||||
// @Description Creates a sub-module practice with metadata and linked question set
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ func (a *App) initAppRoutes() {
|
|||
groupV1.Post("/course-management/sub-module-videos", a.authMiddleware, a.RequirePermission("videos.create"), h.CreateSubModuleVideo)
|
||||
groupV1.Put("/course-management/sub-module-videos/:videoId", a.authMiddleware, a.RequirePermission("videos.update"), h.UpdateSubModuleVideo)
|
||||
groupV1.Delete("/course-management/sub-module-videos/:videoId", a.authMiddleware, a.RequirePermission("videos.delete"), h.DeleteSubModuleVideo)
|
||||
groupV1.Get("/course-management/sub-modules/:subModuleId/lessons", a.authMiddleware, a.RequirePermission("question_sets.list"), h.GetSubModuleLessons)
|
||||
groupV1.Get("/course-management/sub-module-lessons/:lessonId", a.authMiddleware, a.RequirePermission("question_sets.get"), h.GetSubModuleLessonByID)
|
||||
groupV1.Post("/course-management/sub-module-lessons", a.authMiddleware, a.RequirePermission("question_sets.update"), h.AttachSubModuleLesson)
|
||||
groupV1.Post("/course-management/sub-module-practices", a.authMiddleware, a.RequirePermission("question_sets.update"), h.CreateSubModulePractice)
|
||||
groupV1.Put("/course-management/practices/:practiceId", a.authMiddleware, a.RequirePermission("question_sets.update"), h.UpdatePractice)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user