probo/store/file/file.go
2023-06-28 17:21:59 +02:00

162 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
}