diff --git a/internal/web_server/handlers/hierarchy_handler.go b/internal/web_server/handlers/hierarchy_handler.go index 49ec5b9..7feb9a2 100644 --- a/internal/web_server/handlers/hierarchy_handler.go +++ b/internal/web_server/handlers/hierarchy_handler.go @@ -74,6 +74,15 @@ type createSubModulePracticeReq struct { IsActive *bool `json:"is_active"` } +type legacyHierarchyRow struct { + CategoryID int64 `json:"category_id"` + CategoryName string `json:"category_name"` + SubCategoryID *int64 `json:"sub_category_id"` + SubCategoryName *string `json:"sub_category_name"` + CourseID *int64 `json:"course_id"` + CourseTitle *string `json:"course_title"` +} + func toText(v *string) pgtype.Text { if v == nil { return pgtype.Text{Valid: false} @@ -113,6 +122,13 @@ func intOrNil(v *int32) interface{} { func (h *Handler) UnifiedHierarchy(c *fiber.Ctx) error { rows, err := h.analyticsDB.GetCoursesWithHierarchy(c.Context()) if err != nil { + if isMissingCourseSubCategoryTableErr(err) { + legacyRows, legacyErr := h.buildLegacyHierarchyRows(c) + if legacyErr != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to load hierarchy", Error: legacyErr.Error()}) + } + return c.JSON(domain.Response{Message: "Unified hierarchy retrieved successfully", Data: legacyRows}) + } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to load hierarchy", Error: err.Error()}) } return c.JSON(domain.Response{Message: "Unified hierarchy retrieved successfully", Data: rows}) @@ -135,11 +151,88 @@ func (h *Handler) UnifiedHierarchyByCourse(c *fiber.Ctx) error { } rows, err := h.analyticsDB.GetFullHierarchyByCourseID(c.Context(), courseID) if err != nil { + if isMissingCourseSubCategoryTableErr(err) { + course, getCourseErr := h.analyticsDB.GetCourseByID(c.Context(), courseID) + if getCourseErr != nil { + return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to load course hierarchy", Error: getCourseErr.Error()}) + } + return c.JSON(domain.Response{ + Message: "Course hierarchy retrieved successfully", + Data: []map[string]interface{}{ + { + "course_id": course.ID, + "course_title": course.Title, + "level_id": nil, + "cefr_level": nil, + "module_id": nil, + "module_title": nil, + "sub_module_id": nil, + "sub_module_title": nil, + }, + }, + }) + } return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{Message: "Failed to load course hierarchy", Error: err.Error()}) } return c.JSON(domain.Response{Message: "Course hierarchy retrieved successfully", Data: rows}) } +func isMissingCourseSubCategoryTableErr(err error) bool { + if err == nil { + return false + } + return strings.Contains(strings.ToLower(err.Error()), "relation \"course_sub_categories\" does not exist") +} + +func (h *Handler) buildLegacyHierarchyRows(c *fiber.Ctx) ([]legacyHierarchyRow, error) { + categories, err := h.analyticsDB.GetAllCourseCategories(c.Context(), dbgen.GetAllCourseCategoriesParams{ + Offset: pgtype.Int4{Int32: 0, Valid: true}, + Limit: pgtype.Int4{Int32: 10000, Valid: true}, + }) + if err != nil { + return nil, err + } + + out := make([]legacyHierarchyRow, 0, len(categories)) + for _, cat := range categories { + courses, courseErr := h.analyticsDB.GetCoursesByCategory(c.Context(), dbgen.GetCoursesByCategoryParams{ + CategoryID: cat.ID, + Offset: pgtype.Int4{Int32: 0, Valid: true}, + Limit: pgtype.Int4{Int32: 10000, Valid: true}, + }) + if courseErr != nil { + return nil, courseErr + } + + if len(courses) == 0 { + out = append(out, legacyHierarchyRow{ + CategoryID: cat.ID, + CategoryName: cat.Name, + SubCategoryID: nil, + SubCategoryName: nil, + CourseID: nil, + CourseTitle: nil, + }) + continue + } + + for _, course := range courses { + courseID := course.ID + courseTitle := course.Title + out = append(out, legacyHierarchyRow{ + CategoryID: cat.ID, + CategoryName: cat.Name, + SubCategoryID: nil, + SubCategoryName: nil, + CourseID: &courseID, + CourseTitle: &courseTitle, + }) + } + } + + return out, nil +} + // CreateCourseSubCategory godoc // @Summary Create course sub-category // @Description Creates a sub-category under a course category