normalize human language module and sub-module grouping
Enhance hierarchy parsing to group Module-N and Module-N.M naming into stable module/sub-module structures under each CEFR level for consistent rendering. Made-with: Cursor
This commit is contained in:
parent
4055ad46f6
commit
7918e62ca9
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -18,6 +20,8 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var humanLanguageModulePattern = regexp.MustCompile(`(?i)^module-(\d+)(?:\.(\d+))?$`)
|
||||||
|
|
||||||
// Course Category Handlers
|
// Course Category Handlers
|
||||||
|
|
||||||
type createCourseCategoryReq struct {
|
type createCourseCategoryReq struct {
|
||||||
|
|
@ -955,8 +959,7 @@ func (h *Handler) GetHumanLanguageHierarchy(c *fiber.Ctx) error {
|
||||||
if !isValidHumanLanguageCEFRLevel(levelKey) {
|
if !isValidHumanLanguageCEFRLevel(levelKey) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
levelsMap[levelKey] = append(levelsMap[levelKey], humanLanguageModuleRes{
|
||||||
module := humanLanguageModuleRes{
|
|
||||||
ID: sc.ID,
|
ID: sc.ID,
|
||||||
Title: sc.Title,
|
Title: sc.Title,
|
||||||
SubModules: []humanLanguageSubModuleRes{
|
SubModules: []humanLanguageSubModuleRes{
|
||||||
|
|
@ -967,8 +970,7 @@ func (h *Handler) GetHumanLanguageHierarchy(c *fiber.Ctx) error {
|
||||||
Practices: sc.Practices,
|
Practices: sc.Practices,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
levelsMap[levelKey] = append(levelsMap[levelKey], module)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
levels := make([]humanLanguageLevelRes, 0, 9)
|
levels := make([]humanLanguageLevelRes, 0, 9)
|
||||||
|
|
@ -977,9 +979,85 @@ func (h *Handler) GetHumanLanguageHierarchy(c *fiber.Ctx) error {
|
||||||
string(domain.SubCourseSubLevelB1), string(domain.SubCourseSubLevelB2), string(domain.SubCourseSubLevelB3),
|
string(domain.SubCourseSubLevelB1), string(domain.SubCourseSubLevelB2), string(domain.SubCourseSubLevelB3),
|
||||||
string(domain.SubCourseSubLevelC1), string(domain.SubCourseSubLevelC2), string(domain.SubCourseSubLevelC3),
|
string(domain.SubCourseSubLevelC1), string(domain.SubCourseSubLevelC2), string(domain.SubCourseSubLevelC3),
|
||||||
} {
|
} {
|
||||||
|
raw := levelsMap[cefr]
|
||||||
|
moduleBuckets := map[int]*humanLanguageModuleRes{}
|
||||||
|
fallbackCounter := 1000000
|
||||||
|
|
||||||
|
for _, item := range raw {
|
||||||
|
moduleNo := fallbackCounter
|
||||||
|
subNo := 0
|
||||||
|
matched := humanLanguageModulePattern.FindStringSubmatch(strings.TrimSpace(item.Title))
|
||||||
|
if len(matched) > 0 {
|
||||||
|
if parsed, parseErr := strconv.Atoi(matched[1]); parseErr == nil {
|
||||||
|
moduleNo = parsed
|
||||||
|
}
|
||||||
|
if len(matched) > 2 && strings.TrimSpace(matched[2]) != "" {
|
||||||
|
if parsed, parseErr := strconv.Atoi(matched[2]); parseErr == nil {
|
||||||
|
subNo = parsed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fallbackCounter++
|
||||||
|
}
|
||||||
|
|
||||||
|
mod, exists := moduleBuckets[moduleNo]
|
||||||
|
if !exists {
|
||||||
|
moduleTitle := item.Title
|
||||||
|
if moduleNo < 1000000 {
|
||||||
|
moduleTitle = fmt.Sprintf("Module-%d", moduleNo)
|
||||||
|
}
|
||||||
|
mod = &humanLanguageModuleRes{
|
||||||
|
ID: item.ID,
|
||||||
|
Title: moduleTitle,
|
||||||
|
SubModules: []humanLanguageSubModuleRes{},
|
||||||
|
}
|
||||||
|
moduleBuckets[moduleNo] = mod
|
||||||
|
}
|
||||||
|
|
||||||
|
subModuleTitle := item.Title
|
||||||
|
if moduleNo < 1000000 && subNo > 0 {
|
||||||
|
subModuleTitle = fmt.Sprintf("Sub-Module-%d.%d", moduleNo, subNo)
|
||||||
|
} else if moduleNo < 1000000 && subNo == 0 {
|
||||||
|
subModuleTitle = fmt.Sprintf("Sub-Module-%d.1", moduleNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
sub := humanLanguageSubModuleRes{
|
||||||
|
ID: item.ID,
|
||||||
|
Title: subModuleTitle,
|
||||||
|
Videos: item.SubModules[0].Videos,
|
||||||
|
Practices: item.SubModules[0].Practices,
|
||||||
|
}
|
||||||
|
mod.SubModules = append(mod.SubModules, sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleKeys := make([]int, 0, len(moduleBuckets))
|
||||||
|
for key := range moduleBuckets {
|
||||||
|
moduleKeys = append(moduleKeys, key)
|
||||||
|
}
|
||||||
|
sort.Ints(moduleKeys)
|
||||||
|
|
||||||
|
groupedModules := make([]humanLanguageModuleRes, 0, len(moduleKeys))
|
||||||
|
for _, key := range moduleKeys {
|
||||||
|
mod := moduleBuckets[key]
|
||||||
|
sort.SliceStable(mod.SubModules, func(i, j int) bool {
|
||||||
|
ai := humanLanguageModulePattern.FindStringSubmatch(strings.ReplaceAll(mod.SubModules[i].Title, "Sub-", ""))
|
||||||
|
aj := humanLanguageModulePattern.FindStringSubmatch(strings.ReplaceAll(mod.SubModules[j].Title, "Sub-", ""))
|
||||||
|
ival := 0
|
||||||
|
jval := 0
|
||||||
|
if len(ai) > 2 {
|
||||||
|
ival, _ = strconv.Atoi(ai[2])
|
||||||
|
}
|
||||||
|
if len(aj) > 2 {
|
||||||
|
jval, _ = strconv.Atoi(aj[2])
|
||||||
|
}
|
||||||
|
return ival < jval
|
||||||
|
})
|
||||||
|
groupedModules = append(groupedModules, *mod)
|
||||||
|
}
|
||||||
|
|
||||||
levels = append(levels, humanLanguageLevelRes{
|
levels = append(levels, humanLanguageLevelRes{
|
||||||
Level: cefr,
|
Level: cefr,
|
||||||
Modules: levelsMap[cefr],
|
Modules: groupedModules,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user