mirror of
https://github.com/kjanat/articulate-parser.git
synced 2026-01-16 18:22:08 +01:00
- Implement tests for the app service, including course processing from file and URI. - Create mock implementations for CourseParser and Exporter to facilitate testing. - Add tests for HTML cleaner service to validate HTML content cleaning functionality. - Develop tests for the parser service, covering course fetching and loading from files. - Introduce tests for utility functions in the main package, ensuring URI validation and string joining. - Include benchmarks for performance evaluation of key functions.
190 lines
5.5 KiB
Go
190 lines
5.5 KiB
Go
// Package exporters provides implementations of the Exporter interface
|
|
// for converting Articulate Rise courses into various file formats.
|
|
package exporters
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/fumiama/go-docx"
|
|
"github.com/kjanat/articulate-parser/internal/interfaces"
|
|
"github.com/kjanat/articulate-parser/internal/models"
|
|
"github.com/kjanat/articulate-parser/internal/services"
|
|
)
|
|
|
|
// DocxExporter implements the Exporter interface for DOCX format.
|
|
// It converts Articulate Rise course data into a Microsoft Word document
|
|
// using the go-docx package.
|
|
type DocxExporter struct {
|
|
// htmlCleaner is used to convert HTML content to plain text
|
|
htmlCleaner *services.HTMLCleaner
|
|
}
|
|
|
|
// NewDocxExporter creates a new DocxExporter instance.
|
|
// It takes an HTMLCleaner to handle HTML content conversion.
|
|
//
|
|
// Parameters:
|
|
// - htmlCleaner: Service for cleaning HTML content in course data
|
|
//
|
|
// Returns:
|
|
// - An implementation of the Exporter interface for DOCX format
|
|
func NewDocxExporter(htmlCleaner *services.HTMLCleaner) interfaces.Exporter {
|
|
return &DocxExporter{
|
|
htmlCleaner: htmlCleaner,
|
|
}
|
|
}
|
|
|
|
// Export exports the course to a DOCX file.
|
|
// It creates a Word document with formatted content based on the course data
|
|
// and saves it to the specified output path.
|
|
//
|
|
// Parameters:
|
|
// - course: The course data model to export
|
|
// - outputPath: The file path where the DOCX content will be written
|
|
//
|
|
// Returns:
|
|
// - An error if creating or saving the document fails
|
|
func (e *DocxExporter) Export(course *models.Course, outputPath string) error {
|
|
doc := docx.New()
|
|
|
|
// Add title
|
|
titlePara := doc.AddParagraph()
|
|
titlePara.AddText(course.Course.Title).Size("32").Bold()
|
|
|
|
// Add description if available
|
|
if course.Course.Description != "" {
|
|
descPara := doc.AddParagraph()
|
|
cleanDesc := e.htmlCleaner.CleanHTML(course.Course.Description)
|
|
descPara.AddText(cleanDesc)
|
|
}
|
|
|
|
// Add each lesson
|
|
for _, lesson := range course.Course.Lessons {
|
|
e.exportLesson(doc, &lesson)
|
|
}
|
|
|
|
// Ensure output directory exists and add .docx extension
|
|
if !strings.HasSuffix(strings.ToLower(outputPath), ".docx") {
|
|
outputPath = outputPath + ".docx"
|
|
}
|
|
|
|
// Create the file
|
|
file, err := os.Create(outputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create output file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Save the document
|
|
_, err = doc.WriteTo(file)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save document: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// exportLesson adds a lesson to the document with appropriate formatting.
|
|
// It creates a lesson heading, adds the description, and processes all items in the lesson.
|
|
//
|
|
// Parameters:
|
|
// - doc: The Word document being created
|
|
// - lesson: The lesson data model to export
|
|
func (e *DocxExporter) exportLesson(doc *docx.Docx, lesson *models.Lesson) {
|
|
// Add lesson title
|
|
lessonPara := doc.AddParagraph()
|
|
lessonPara.AddText(fmt.Sprintf("Lesson: %s", lesson.Title)).Size("28").Bold()
|
|
|
|
// Add lesson description if available
|
|
if lesson.Description != "" {
|
|
descPara := doc.AddParagraph()
|
|
cleanDesc := e.htmlCleaner.CleanHTML(lesson.Description)
|
|
descPara.AddText(cleanDesc)
|
|
}
|
|
|
|
// Add each item in the lesson
|
|
for _, item := range lesson.Items {
|
|
e.exportItem(doc, &item)
|
|
}
|
|
}
|
|
|
|
// exportItem adds an item to the document.
|
|
// It creates an item heading and processes all sub-items within the item.
|
|
//
|
|
// Parameters:
|
|
// - doc: The Word document being created
|
|
// - item: The item data model to export
|
|
func (e *DocxExporter) exportItem(doc *docx.Docx, item *models.Item) {
|
|
// Add item type as heading
|
|
if item.Type != "" {
|
|
itemPara := doc.AddParagraph()
|
|
itemPara.AddText(strings.Title(item.Type)).Size("24").Bold()
|
|
}
|
|
|
|
// Add sub-items
|
|
for _, subItem := range item.Items {
|
|
e.exportSubItem(doc, &subItem)
|
|
}
|
|
}
|
|
|
|
// exportSubItem adds a sub-item to the document.
|
|
// It handles different components of a sub-item like title, heading,
|
|
// paragraph content, answers, and feedback.
|
|
//
|
|
// Parameters:
|
|
// - doc: The Word document being created
|
|
// - subItem: The sub-item data model to export
|
|
func (e *DocxExporter) exportSubItem(doc *docx.Docx, subItem *models.SubItem) {
|
|
// Add title if available
|
|
if subItem.Title != "" {
|
|
subItemPara := doc.AddParagraph()
|
|
subItemPara.AddText(" " + subItem.Title).Bold() // Indented
|
|
}
|
|
|
|
// Add heading if available
|
|
if subItem.Heading != "" {
|
|
headingPara := doc.AddParagraph()
|
|
cleanHeading := e.htmlCleaner.CleanHTML(subItem.Heading)
|
|
headingPara.AddText(" " + cleanHeading).Bold() // Indented
|
|
}
|
|
|
|
// Add paragraph content if available
|
|
if subItem.Paragraph != "" {
|
|
contentPara := doc.AddParagraph()
|
|
cleanContent := e.htmlCleaner.CleanHTML(subItem.Paragraph)
|
|
contentPara.AddText(" " + cleanContent) // Indented
|
|
}
|
|
|
|
// Add answers if this is a question
|
|
if len(subItem.Answers) > 0 {
|
|
answersPara := doc.AddParagraph()
|
|
answersPara.AddText(" Answers:").Bold()
|
|
|
|
for i, answer := range subItem.Answers {
|
|
answerPara := doc.AddParagraph()
|
|
prefix := fmt.Sprintf(" %d. ", i+1)
|
|
if answer.Correct {
|
|
prefix += "✓ "
|
|
}
|
|
cleanAnswer := e.htmlCleaner.CleanHTML(answer.Title)
|
|
answerPara.AddText(prefix + cleanAnswer)
|
|
}
|
|
}
|
|
|
|
// Add feedback if available
|
|
if subItem.Feedback != "" {
|
|
feedbackPara := doc.AddParagraph()
|
|
cleanFeedback := e.htmlCleaner.CleanHTML(subItem.Feedback)
|
|
feedbackPara.AddText(" Feedback: " + cleanFeedback).Italic()
|
|
}
|
|
}
|
|
|
|
// GetSupportedFormat returns the format name this exporter supports.
|
|
//
|
|
// Returns:
|
|
// - A string representing the supported format ("docx")
|
|
func (e *DocxExporter) GetSupportedFormat() string {
|
|
return "docx"
|
|
}
|