mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-01-16 15:42:11 +01:00
Replaces the manual string-building implementation of the HTML exporter with a more robust and maintainable solution using Go's `html/template` package. This improves readability, security, and separation of concerns. - HTML structure and CSS styles are moved into their own files and embedded into the binary using `go:embed`. - A new data preparation layer adapts the course model for the template, simplifying rendering logic. - Tests are updated to reflect the new implementation, removing obsolete test cases for the old string-building methods. Additionally, this commit: - Adds an `AGENTS.md` file with development and contribution guidelines. - Updates `.golangci.yml` to allow standard Go patterns for interface package naming.
132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
package exporters
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/language"
|
|
|
|
"github.com/kjanat/articulate-parser/internal/models"
|
|
"github.com/kjanat/articulate-parser/internal/services"
|
|
)
|
|
|
|
// Item type constants.
|
|
const (
|
|
itemTypeText = "text"
|
|
itemTypeList = "list"
|
|
itemTypeKnowledgeCheck = "knowledgecheck"
|
|
itemTypeMultimedia = "multimedia"
|
|
itemTypeImage = "image"
|
|
itemTypeInteractive = "interactive"
|
|
itemTypeDivider = "divider"
|
|
)
|
|
|
|
// templateData represents the data structure passed to the HTML template.
|
|
type templateData struct {
|
|
Course models.CourseInfo
|
|
ShareID string
|
|
Sections []templateSection
|
|
CSS string
|
|
}
|
|
|
|
// templateSection represents a course section or lesson.
|
|
type templateSection struct {
|
|
Type string
|
|
Title string
|
|
Number int
|
|
Description string
|
|
Items []templateItem
|
|
}
|
|
|
|
// templateItem represents a course item with preprocessed data.
|
|
type templateItem struct {
|
|
Type string
|
|
TypeTitle string
|
|
Items []templateSubItem
|
|
}
|
|
|
|
// templateSubItem represents a sub-item with preprocessed data.
|
|
type templateSubItem struct {
|
|
Heading string
|
|
Paragraph string
|
|
Title string
|
|
Caption string
|
|
CleanText string
|
|
Answers []models.Answer
|
|
Feedback string
|
|
Media *models.Media
|
|
}
|
|
|
|
// prepareTemplateData converts a Course model into template-friendly data.
|
|
func prepareTemplateData(course *models.Course, htmlCleaner *services.HTMLCleaner) *templateData {
|
|
data := &templateData{
|
|
Course: course.Course,
|
|
ShareID: course.ShareID,
|
|
Sections: make([]templateSection, 0, len(course.Course.Lessons)),
|
|
CSS: defaultCSS,
|
|
}
|
|
|
|
lessonCounter := 0
|
|
for _, lesson := range course.Course.Lessons {
|
|
section := templateSection{
|
|
Type: lesson.Type,
|
|
Title: lesson.Title,
|
|
Description: lesson.Description,
|
|
}
|
|
|
|
if lesson.Type != "section" {
|
|
lessonCounter++
|
|
section.Number = lessonCounter
|
|
section.Items = prepareItems(lesson.Items, htmlCleaner)
|
|
}
|
|
|
|
data.Sections = append(data.Sections, section)
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
// prepareItems converts model Items to template Items.
|
|
func prepareItems(items []models.Item, htmlCleaner *services.HTMLCleaner) []templateItem {
|
|
result := make([]templateItem, 0, len(items))
|
|
|
|
for _, item := range items {
|
|
tItem := templateItem{
|
|
Type: strings.ToLower(item.Type),
|
|
Items: make([]templateSubItem, 0, len(item.Items)),
|
|
}
|
|
|
|
// Set type title for unknown items
|
|
if tItem.Type != itemTypeText && tItem.Type != itemTypeList && tItem.Type != itemTypeKnowledgeCheck &&
|
|
tItem.Type != itemTypeMultimedia && tItem.Type != itemTypeImage && tItem.Type != itemTypeInteractive &&
|
|
tItem.Type != itemTypeDivider {
|
|
caser := cases.Title(language.English)
|
|
tItem.TypeTitle = caser.String(item.Type)
|
|
}
|
|
|
|
// Process sub-items
|
|
for _, subItem := range item.Items {
|
|
tSubItem := templateSubItem{
|
|
Heading: subItem.Heading,
|
|
Paragraph: subItem.Paragraph,
|
|
Title: subItem.Title,
|
|
Caption: subItem.Caption,
|
|
Answers: subItem.Answers,
|
|
Feedback: subItem.Feedback,
|
|
Media: subItem.Media,
|
|
}
|
|
|
|
// Clean HTML for list items
|
|
if tItem.Type == itemTypeList && subItem.Paragraph != "" {
|
|
tSubItem.CleanText = htmlCleaner.CleanHTML(subItem.Paragraph)
|
|
}
|
|
|
|
tItem.Items = append(tItem.Items, tSubItem)
|
|
}
|
|
|
|
result = append(result, tItem)
|
|
}
|
|
|
|
return result
|
|
}
|