This commit is contained in:
andrea 2023-10-02 12:55:03 +02:00
parent 4f3ecda14c
commit 4da6162dd4
7 changed files with 138 additions and 9 deletions

View file

@ -5,4 +5,5 @@ import "time"
type Meta struct { type Meta struct {
ID string `json:"id" yaml:"id"` ID string `json:"id" yaml:"id"`
CreatedAt time.Time `json:"created_at" yaml:"created_at"` CreatedAt time.Time `json:"created_at" yaml:"created_at"`
Tags []*Tag `json:"tags" yaml:"tags"`
} }

View file

@ -2,7 +2,6 @@ package models
type Quiz struct { type Quiz struct {
Meta Meta
// ID string `json:"id"`
Hash string `json:"hash"` Hash string `json:"hash"`
Question *Question `json:"question"` Question *Question `json:"question"`
Answers []*Answer `json:"answers"` Answers []*Answer `json:"answers"`

5
models/tag.go Normal file
View file

@ -0,0 +1,5 @@
package models
type Tag struct {
Name string `json:"name"`
}

View file

@ -84,7 +84,6 @@ func (s *FileProboCollectorStore) Reindex() error {
if err != nil { if err != nil {
return err return err
} }
q, err := s.memoryStore.CreateQuiz(&client.CreateUpdateQuizRequest{ q, err := s.memoryStore.CreateQuiz(&client.CreateUpdateQuizRequest{
Quiz: quiz, Quiz: quiz,
Meta: meta, Meta: meta,
@ -217,6 +216,67 @@ func MarkdownFromQuiz(quiz *models.Quiz) (string, error) {
return markdown, nil return markdown, nil
} }
// func QuizFromMarkdown(markdown string) (*client.Quiz, *models.Meta, error) {
// meta, remainingMarkdown, err := parseMetaHeaderFromMarkdown(markdown)
// if err != nil {
// return nil, nil, err
// }
// if meta == nil {
// meta = new(models.Meta)
// if meta.Tags == nil {
// meta.Tags = make([]*models.Tag, 0)
// }
// } else if meta.Tags == nil {
// meta.Tags = make([]*models.Tag, 0)
// }
// lines := strings.Split(remainingMarkdown, "\n")
// questionText := ""
// answers := []*client.Answer{}
// for _, line := range lines {
// // Check if the line contains a tag
// if strings.Contains(line, "#") {
// // Split the line into words
// words := strings.Split(line, " ")
// for _, word := range words {
// // If the word starts with '#', add it to the tags
// if strings.HasPrefix(word, "#") {
// meta.Tags = append(meta.Tags, &models.Tag{Name: word[1:]})
// }
// }
// }
// 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, nil, fmt.Errorf("Question text should not be empty.")
// }
// if len(answers) < 2 {
// return nil, 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, meta, nil
// }
func QuizFromMarkdown(markdown string) (*client.Quiz, *models.Meta, error) { func QuizFromMarkdown(markdown string) (*client.Quiz, *models.Meta, error) {
meta, remainingMarkdown, err := parseMetaHeaderFromMarkdown(markdown) meta, remainingMarkdown, err := parseMetaHeaderFromMarkdown(markdown)
if err != nil { if err != nil {
@ -275,7 +335,6 @@ func (s *FileProboCollectorStore) WriteMetaHeaderToFile(filename string, meta *m
if err != nil { if err != nil {
return nil, err return nil, err
} }
if readMeta == nil { if readMeta == nil {
_, err := writeMetaHeader(path.Join(s.Dir, filename), meta) _, err := writeMetaHeader(path.Join(s.Dir, filename), meta)
if err != nil { if err != nil {

View file

@ -29,7 +29,7 @@ func (t *testSuite) TestQuizFromMarkdown() {
Question text (2). Question text (2).
Question text (3). Question text with #tag1 #tag2 (3).
* Answer 1 * Answer 1
* Answer 2 * Answer 2
@ -37,7 +37,7 @@ Question text (3).
* Answer 4` * Answer 4`
expectedQuiz := &client.Quiz{ expectedQuiz := &client.Quiz{
Question: &client.Question{Text: "Question text (1).\n\nQuestion text (2).\n\nQuestion text (3)."}, Question: &client.Question{Text: "Question text (1).\n\nQuestion text (2).\n\nQuestion text with #tag1 #tag2 (3)."},
Answers: []*client.Answer{ Answers: []*client.Answer{
{Text: "Answer 1", Correct: true}, {Text: "Answer 1", Correct: true},
{Text: "Answer 2", Correct: false}, {Text: "Answer 2", Correct: false},
@ -51,6 +51,7 @@ Question text (3).
if !t.Failed() { if !t.Failed() {
t.True(reflect.DeepEqual(quiz, expectedQuiz), fmt.Sprintf("Expected %+v, got %+v", expectedQuiz, quiz)) t.True(reflect.DeepEqual(quiz, expectedQuiz), fmt.Sprintf("Expected %+v, got %+v", expectedQuiz, quiz))
} }
} }
@ -205,7 +206,7 @@ func (t *testSuite) TestUpdateQuiz() {
if !t.Failed() { if !t.Failed() {
clientQuiz := &client.Quiz{ clientQuiz := &client.Quiz{
Question: &client.Question{Text: "Updated question text."}, Question: &client.Question{Text: "Updated question text with #tag."},
Answers: []*client.Answer{ Answers: []*client.Answer{
{Text: "Answer 1", Correct: true}, {Text: "Answer 1", Correct: true},
{Text: "Answer 2", Correct: false}, {Text: "Answer 2", Correct: false},
@ -221,6 +222,7 @@ func (t *testSuite) TestUpdateQuiz() {
t.Nil(err, fmt.Sprintf("Quiz should be updated without errors: %v", err)) t.Nil(err, fmt.Sprintf("Quiz should be updated without errors: %v", err))
t.Equal(updatedQuiz.ID, quiz.ID, fmt.Sprintf("IDs should remain the same after an update: updated ID %v original ID %v", updatedQuiz.ID, quiz.ID)) t.Equal(updatedQuiz.ID, quiz.ID, fmt.Sprintf("IDs should remain the same after an update: updated ID %v original ID %v", updatedQuiz.ID, quiz.ID))
t.True(len(updatedQuiz.Tags) == 1, "Length of tags array should be 1")
if !t.Failed() { if !t.Failed() {
path, err := store.GetPath(updatedQuiz) path, err := store.GetPath(updatedQuiz)
@ -265,7 +267,7 @@ func (t *testSuite) TestWriteMetaHeaderToFile() {
if !t.Failed() { if !t.Failed() {
meta, err := store.ReadMetaHeaderFromFile("quiz_5.md") meta, err := store.ReadMetaHeaderFromFile("quiz_5.md")
t.True(err == nil) t.True(err == nil, fmt.Sprintf("Reading the header returns the following error: %v", err))
if !t.Failed() { if !t.Failed() {
t.True(meta != nil, "Meta header should not be nil") t.True(meta != nil, "Meta header should not be nil")

View file

@ -2,6 +2,7 @@ package memory
import ( import (
"fmt" "fmt"
"strings"
"sync" "sync"
"git.andreafazzi.eu/andrea/probo/client" "git.andreafazzi.eu/andrea/probo/client"
@ -153,6 +154,36 @@ func (s *MemoryProboCollectorStore) CalculateQuizHash(quiz *client.Quiz) string
return hashes[len(hashes)-1] return hashes[len(hashes)-1]
} }
func (s *MemoryProboCollectorStore) parseTextForTags(text string, tags *[]*models.Tag) string {
// Trim the following chars
trimChars := "*:.,/\\@()[]{}<>"
// Split the text into words
words := strings.Fields(text)
for _, word := range words {
// If the word starts with '#', it is considered as a tag
if strings.HasPrefix(word, "#") {
// Check if the tag already exists in the tags slice
exists := false
for _, tag := range *tags {
if tag.Name == word {
exists = true
break
}
}
// If the tag does not exist in the tags slice, add it
if !exists {
*tags = append(*tags, &models.Tag{Name: strings.TrimRight(word, trimChars)})
}
}
}
return text
}
func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQuizRequest, id string) (*models.Quiz, bool, error) { func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQuizRequest, id string) (*models.Quiz, bool, error) {
hashes := s.hasher.QuizHashes(r.Quiz) hashes := s.hasher.QuizHashes(r.Quiz)
quizHash := hashes[len(hashes)-1] quizHash := hashes[len(hashes)-1]
@ -171,6 +202,8 @@ func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQui
if r.Meta != nil { if r.Meta != nil {
if r.Meta.ID != "" { if r.Meta.ID != "" {
id = r.Meta.ID id = r.Meta.ID
} else {
id = uuid.New().String()
} }
} else { } else {
id = uuid.New().String() id = uuid.New().String()
@ -178,12 +211,16 @@ func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQui
quiz = new(models.Quiz) quiz = new(models.Quiz)
} }
if quiz.Tags == nil {
quiz.Tags = make([]*models.Tag, 0)
}
questionHash := hashes[0] questionHash := hashes[0]
q := s.getQuestionFromHash(questionHash) q := s.getQuestionFromHash(questionHash)
if q == nil { // if the question is not in the store then we should add it if q == nil { // if the question is not in the store then we should add it
q = s.createQuestionFromHash(questionHash, &models.Question{ q = s.createQuestionFromHash(questionHash, &models.Question{
ID: uuid.New().String(), ID: uuid.New().String(),
Text: r.Quiz.Question.Text, Text: s.parseTextForTags(r.Quiz.Question.Text, &quiz.Tags),
}) })
} }
@ -199,7 +236,7 @@ func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQui
if a == nil { // if the answer is not in the store add it if a == nil { // if the answer is not in the store add it
a = s.createAnswerFromHash(answerHash, &models.Answer{ a = s.createAnswerFromHash(answerHash, &models.Answer{
ID: uuid.New().String(), ID: uuid.New().String(),
Text: answer.Text, Text: s.parseTextForTags(answer.Text, &quiz.Tags),
}) })
} }
if answer.Correct { if answer.Correct {

View file

@ -45,6 +45,32 @@ func (t *testSuite) TestReadQuizByHash() {
} }
func (t *testSuite) TestParseTextForTags() {
store := NewMemoryProboCollectorStore(
sha256.NewDefault256Hasher(sha256.DefaultSHA256HashingFn),
)
quiz, _ := store.CreateQuiz(
&client.CreateUpdateQuizRequest{
Quiz: &client.Quiz{
Question: &client.Question{Text: "Newly created question text with #tag1."},
Answers: []*client.Answer{
{Text: "Answer 1", Correct: true},
{Text: "Answer 2 with #tag2", Correct: false},
{Text: "Answer 3", Correct: false},
{Text: "Answer 4", Correct: false},
},
},
})
quizFromMemory, err := store.ReadQuizByHash(quiz.Hash)
t.Nil(err, "Quiz should be found in the store")
if !t.Failed() {
t.True(len(quizFromMemory.Tags) == 2, "Two tags should be present.")
t.Equal("#tag1", quizFromMemory.Tags[0].Name)
t.Equal("#tag2", quizFromMemory.Tags[1].Name)
}
}
func (t *testSuite) TestUpdateQuiz() { func (t *testSuite) TestUpdateQuiz() {
store := NewMemoryProboCollectorStore( store := NewMemoryProboCollectorStore(
sha256.NewDefault256Hasher(sha256.DefaultSHA256HashingFn), sha256.NewDefault256Hasher(sha256.DefaultSHA256HashingFn),