diff --git a/models/meta.go b/models/meta.go index 0f8a44e..aa69ad1 100644 --- a/models/meta.go +++ b/models/meta.go @@ -5,4 +5,5 @@ import "time" type Meta struct { ID string `json:"id" yaml:"id"` CreatedAt time.Time `json:"created_at" yaml:"created_at"` + Tags []*Tag `json:"tags" yaml:"tags"` } diff --git a/models/quiz.go b/models/quiz.go index 8919ad2..749b2d4 100644 --- a/models/quiz.go +++ b/models/quiz.go @@ -2,7 +2,6 @@ package models type Quiz struct { Meta - // ID string `json:"id"` Hash string `json:"hash"` Question *Question `json:"question"` Answers []*Answer `json:"answers"` diff --git a/models/tag.go b/models/tag.go new file mode 100644 index 0000000..d2c8e5c --- /dev/null +++ b/models/tag.go @@ -0,0 +1,5 @@ +package models + +type Tag struct { + Name string `json:"name"` +} diff --git a/store/file/file.go b/store/file/file.go index 6cf6b18..edf2c36 100644 --- a/store/file/file.go +++ b/store/file/file.go @@ -84,7 +84,6 @@ func (s *FileProboCollectorStore) Reindex() error { if err != nil { return err } - q, err := s.memoryStore.CreateQuiz(&client.CreateUpdateQuizRequest{ Quiz: quiz, Meta: meta, @@ -217,6 +216,67 @@ func MarkdownFromQuiz(quiz *models.Quiz) (string, error) { 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) { meta, remainingMarkdown, err := parseMetaHeaderFromMarkdown(markdown) if err != nil { @@ -275,7 +335,6 @@ func (s *FileProboCollectorStore) WriteMetaHeaderToFile(filename string, meta *m if err != nil { return nil, err } - if readMeta == nil { _, err := writeMetaHeader(path.Join(s.Dir, filename), meta) if err != nil { diff --git a/store/file/file_test.go b/store/file/file_test.go index 1728ebb..3a0d70f 100644 --- a/store/file/file_test.go +++ b/store/file/file_test.go @@ -29,7 +29,7 @@ func (t *testSuite) TestQuizFromMarkdown() { Question text (2). -Question text (3). +Question text with #tag1 #tag2 (3). * Answer 1 * Answer 2 @@ -37,7 +37,7 @@ Question text (3). * Answer 4` 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{ {Text: "Answer 1", Correct: true}, {Text: "Answer 2", Correct: false}, @@ -51,6 +51,7 @@ Question text (3). if !t.Failed() { 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() { clientQuiz := &client.Quiz{ - Question: &client.Question{Text: "Updated question text."}, + Question: &client.Question{Text: "Updated question text with #tag."}, Answers: []*client.Answer{ {Text: "Answer 1", Correct: true}, {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.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() { path, err := store.GetPath(updatedQuiz) @@ -265,7 +267,7 @@ func (t *testSuite) TestWriteMetaHeaderToFile() { if !t.Failed() { 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() { t.True(meta != nil, "Meta header should not be nil") diff --git a/store/memory/memory.go b/store/memory/memory.go index c8a0c18..f1536f7 100644 --- a/store/memory/memory.go +++ b/store/memory/memory.go @@ -2,6 +2,7 @@ package memory import ( "fmt" + "strings" "sync" "git.andreafazzi.eu/andrea/probo/client" @@ -153,6 +154,36 @@ func (s *MemoryProboCollectorStore) CalculateQuizHash(quiz *client.Quiz) string 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) { hashes := s.hasher.QuizHashes(r.Quiz) quizHash := hashes[len(hashes)-1] @@ -171,6 +202,8 @@ func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQui if r.Meta != nil { if r.Meta.ID != "" { id = r.Meta.ID + } else { + id = uuid.New().String() } } else { id = uuid.New().String() @@ -178,12 +211,16 @@ func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQui quiz = new(models.Quiz) } + if quiz.Tags == nil { + quiz.Tags = make([]*models.Tag, 0) + } + questionHash := hashes[0] q := s.getQuestionFromHash(questionHash) if q == nil { // if the question is not in the store then we should add it q = s.createQuestionFromHash(questionHash, &models.Question{ 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 a = s.createAnswerFromHash(answerHash, &models.Answer{ ID: uuid.New().String(), - Text: answer.Text, + Text: s.parseTextForTags(answer.Text, &quiz.Tags), }) } if answer.Correct { diff --git a/store/memory/memory_test.go b/store/memory/memory_test.go index 197c4e6..ef54564 100644 --- a/store/memory/memory_test.go +++ b/store/memory/memory_test.go @@ -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() { store := NewMemoryProboCollectorStore( sha256.NewDefault256Hasher(sha256.DefaultSHA256HashingFn),