Tags
This commit is contained in:
parent
4f3ecda14c
commit
4da6162dd4
7 changed files with 138 additions and 9 deletions
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
5
models/tag.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in a new issue