163 lines
3.6 KiB
Go
163 lines
3.6 KiB
Go
|
package file
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/fs"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"git.andreafazzi.eu/andrea/probo/client"
|
||
|
"git.andreafazzi.eu/andrea/probo/hasher/sha256"
|
||
|
"git.andreafazzi.eu/andrea/probo/models"
|
||
|
"git.andreafazzi.eu/andrea/probo/store/memory"
|
||
|
)
|
||
|
|
||
|
type FileProboCollectorStore struct {
|
||
|
Dir string
|
||
|
|
||
|
memoryStore *memory.MemoryProboCollectorStore
|
||
|
}
|
||
|
|
||
|
func NewFileProboCollectorStore(dirname string) (*FileProboCollectorStore, error) {
|
||
|
s := new(FileProboCollectorStore)
|
||
|
|
||
|
files, err := ioutil.ReadDir(dirname)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
markdownFiles := make([]fs.FileInfo, 0)
|
||
|
|
||
|
for _, file := range files {
|
||
|
filename := file.Name()
|
||
|
if !file.IsDir() && strings.HasSuffix(filename, ".md") {
|
||
|
markdownFiles = append(markdownFiles, file)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if len(markdownFiles) == 0 {
|
||
|
return nil, fmt.Errorf("The directory is empty.")
|
||
|
}
|
||
|
|
||
|
s.memoryStore = memory.NewMemoryProboCollectorStore(
|
||
|
sha256.NewDefault256Hasher(sha256.DefaultSHA256HashingFn),
|
||
|
)
|
||
|
|
||
|
for _, file := range markdownFiles {
|
||
|
filename := file.Name()
|
||
|
fullPath := filepath.Join(dirname, filename)
|
||
|
|
||
|
content, err := os.ReadFile(fullPath)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
quiz, err := QuizFromMarkdown(string(content))
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
s.memoryStore.CreateQuiz(&client.CreateUpdateQuizRequest{
|
||
|
Quiz: quiz,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
s.Dir = dirname
|
||
|
|
||
|
return s, nil
|
||
|
}
|
||
|
|
||
|
func (s *FileProboCollectorStore) ReadAllQuizzes() ([]*models.Quiz, error) {
|
||
|
return s.memoryStore.ReadAllQuizzes()
|
||
|
}
|
||
|
func (s *FileProboCollectorStore) CreateQuiz(r *client.CreateUpdateQuizRequest) (*models.Quiz, error) {
|
||
|
quiz, err := s.memoryStore.CreateQuiz(r)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
err = s.writeMarkdownFile(quiz)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return quiz, nil
|
||
|
}
|
||
|
|
||
|
func MarkdownFromQuiz(quiz *models.Quiz) (string, error) {
|
||
|
if quiz.Question == nil {
|
||
|
return "", errors.New("Quiz should contain a question but it wasn't provided.")
|
||
|
}
|
||
|
|
||
|
if len(quiz.Answers) == 0 {
|
||
|
return "", errors.New("Quiz should contain at least 2 answers but none was provided.")
|
||
|
}
|
||
|
|
||
|
if quiz.Correct == nil {
|
||
|
return "", errors.New("Quiz should contain a correct answer but non was provided.")
|
||
|
}
|
||
|
|
||
|
correctAnswer := "* " + quiz.Correct.Text
|
||
|
var otherAnswers string
|
||
|
|
||
|
for _, answer := range quiz.Answers {
|
||
|
otherAnswers += "* " + answer.Text + "\n"
|
||
|
}
|
||
|
|
||
|
markdown := quiz.Question.Text + "\n\n" + correctAnswer + "\n" + otherAnswers
|
||
|
|
||
|
return markdown, nil
|
||
|
}
|
||
|
|
||
|
func QuizFromMarkdown(markdown string) (*client.Quiz, error) {
|
||
|
lines := strings.Split(markdown, "\n")
|
||
|
|
||
|
questionText := ""
|
||
|
answers := []*client.Answer{}
|
||
|
|
||
|
for _, line := range lines {
|
||
|
if strings.HasPrefix(line, "*") {
|
||
|
answerText := strings.TrimPrefix(line, "* ")
|
||
|
correct := len(answers) == 0
|
||
|
answer := &client.Answer{Text: answerText, Correct: correct}
|
||
|
answers = append(answers, answer)
|
||
|
} else {
|
||
|
if questionText != "" {
|
||
|
questionText += "\n"
|
||
|
}
|
||
|
questionText += line
|
||
|
}
|
||
|
}
|
||
|
|
||
|
questionText = strings.TrimRight(questionText, "\n")
|
||
|
|
||
|
if questionText == "" {
|
||
|
return nil, fmt.Errorf("Question text should not be empty.")
|
||
|
}
|
||
|
|
||
|
if len(answers) < 2 {
|
||
|
return nil, fmt.Errorf("Number of answers should be at least 2 but parsed answers are %d.", len(answers))
|
||
|
}
|
||
|
|
||
|
question := &client.Question{Text: questionText}
|
||
|
quiz := &client.Quiz{Question: question, Answers: answers}
|
||
|
|
||
|
return quiz, nil
|
||
|
}
|
||
|
|
||
|
func (s *FileProboCollectorStore) writeMarkdownFile(quiz *models.Quiz) error {
|
||
|
markdown, err := MarkdownFromQuiz(quiz)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
filename := filepath.Join(s.Dir, quiz.Hash+".md")
|
||
|
err = ioutil.WriteFile(filename, []byte(markdown), 0644)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|