|
@@ -2,19 +2,20 @@ package memory
|
|
|
|
|
|
import (
|
|
|
"errors"
|
|
|
- "fmt"
|
|
|
- "strings"
|
|
|
"sync"
|
|
|
|
|
|
- "git.andreafazzi.eu/andrea/probo/client"
|
|
|
"git.andreafazzi.eu/andrea/probo/hasher"
|
|
|
"git.andreafazzi.eu/andrea/probo/models"
|
|
|
- "github.com/google/uuid"
|
|
|
)
|
|
|
|
|
|
type MemoryProboCollectorStore struct {
|
|
|
- quizzes map[string]*models.Quiz
|
|
|
- collections map[string]*models.Collection
|
|
|
+
|
|
|
+ // IDs maps
|
|
|
+ quizzes map[string]*models.Quiz
|
|
|
+ collections map[string]*models.Collection
|
|
|
+ participants map[string]*models.Participant
|
|
|
+
|
|
|
+ // Hashes maps
|
|
|
questionsHashes map[string]*models.Question
|
|
|
answersHashes map[string]*models.Answer
|
|
|
quizzesHashes map[string]*models.Quiz
|
|
@@ -36,181 +37,17 @@ func NewMemoryProboCollectorStore(hasher hasher.Hasher) *MemoryProboCollectorSto
|
|
|
|
|
|
s.quizzes = make(map[string]*models.Quiz)
|
|
|
s.collections = make(map[string]*models.Collection)
|
|
|
+ s.participants = make(map[string]*models.Participant)
|
|
|
|
|
|
return s
|
|
|
}
|
|
|
|
|
|
-func (s *MemoryProboCollectorStore) getQuizFromHash(hash string) *models.Quiz {
|
|
|
- s.lock.RLock()
|
|
|
- defer s.lock.RUnlock()
|
|
|
-
|
|
|
- quiz, ok := s.quizzesHashes[hash]
|
|
|
- if ok {
|
|
|
- return quiz
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) getQuizFromID(id string) *models.Quiz {
|
|
|
- s.lock.RLock()
|
|
|
- defer s.lock.RUnlock()
|
|
|
-
|
|
|
- quiz, ok := s.quizzes[id]
|
|
|
- if ok {
|
|
|
- return quiz
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) getCollectionFromID(id string) *models.Collection {
|
|
|
- s.lock.RLock()
|
|
|
- defer s.lock.RUnlock()
|
|
|
-
|
|
|
- collection, ok := s.collections[id]
|
|
|
- if ok {
|
|
|
- return collection
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) getQuestionFromHash(hash string) *models.Question {
|
|
|
- s.lock.RLock()
|
|
|
- defer s.lock.RUnlock()
|
|
|
-
|
|
|
- question, ok := s.questionsHashes[hash]
|
|
|
- if ok {
|
|
|
- return question
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) getAnswerFromHash(hash string) *models.Answer {
|
|
|
- s.lock.RLock()
|
|
|
- defer s.lock.RUnlock()
|
|
|
-
|
|
|
- answer, ok := s.answersHashes[hash]
|
|
|
- if ok {
|
|
|
- return answer
|
|
|
- }
|
|
|
-
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) createQuizFromHash(id string, hash string, quiz *models.Quiz) *models.Quiz {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- quiz.ID = id
|
|
|
- quiz.Hash = hash
|
|
|
-
|
|
|
- s.quizzesHashes[hash] = quiz
|
|
|
- s.quizzes[id] = quiz
|
|
|
-
|
|
|
- return quiz
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) createQuestionFromHash(hash string, question *models.Question) *models.Question {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- s.questionsHashes[hash] = question
|
|
|
-
|
|
|
- return question
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) createAnswerFromHash(hash string, answer *models.Answer) *models.Answer {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- s.answersHashes[hash] = answer
|
|
|
-
|
|
|
- return answer
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) deleteQuiz(id string) (*models.Quiz, error) {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- quiz := s.quizzes[id]
|
|
|
- if quiz == nil {
|
|
|
- return nil, fmt.Errorf("Trying to delete a quiz that doesn't exist in memory (ID: %v)", id)
|
|
|
- }
|
|
|
-
|
|
|
- delete(s.quizzes, id)
|
|
|
- delete(s.quizzesHashes, quiz.Hash)
|
|
|
-
|
|
|
- return quiz, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) ReadAllQuizzes() ([]*models.Quiz, error) {
|
|
|
- result := make([]*models.Quiz, 0)
|
|
|
- for id := range s.quizzes {
|
|
|
- if quiz := s.getQuizFromID(id); quiz != nil {
|
|
|
- result = append(result, quiz)
|
|
|
- }
|
|
|
- }
|
|
|
- return result, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) ReadQuizByID(id string) (*models.Quiz, error) {
|
|
|
- quiz := s.getQuizFromID(id)
|
|
|
- if quiz == nil {
|
|
|
- return nil, fmt.Errorf("Quiz with ID %s was not found in the store.", id)
|
|
|
+func Create[T any](s *MemoryProboCollectorStore, elem *T) (*T, error) {
|
|
|
+ if elem == nil {
|
|
|
+ return nil, errors.New("A creation request was made passing a nil element")
|
|
|
}
|
|
|
- return quiz, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) ReadQuizByHash(hash string) (*models.Quiz, error) {
|
|
|
- quiz := s.getQuizFromHash(hash)
|
|
|
- if quiz == nil {
|
|
|
- return nil, fmt.Errorf("Quiz with hash %s was not found in the store.", hash)
|
|
|
- }
|
|
|
- return quiz, nil
|
|
|
-}
|
|
|
|
|
|
-func (s *MemoryProboCollectorStore) CalculateQuizHash(quiz *client.Quiz) string {
|
|
|
- hashes := s.hasher.QuizHashes(quiz)
|
|
|
- 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) {
|
|
|
- if r.Quiz == nil {
|
|
|
- return nil, false, errors.New("A request was made passing a nil quiz object")
|
|
|
- }
|
|
|
+ // Check for duplicates
|
|
|
hashes := s.hasher.QuizHashes(r.Quiz)
|
|
|
quizHash := hashes[len(hashes)-1]
|
|
|
|
|
@@ -219,172 +56,4 @@ func (s *MemoryProboCollectorStore) createOrUpdateQuiz(r *client.CreateUpdateQui
|
|
|
return quiz, false, nil
|
|
|
}
|
|
|
|
|
|
- if id != "" { // we're updating a quiz
|
|
|
- quiz = s.getQuizFromID(id)
|
|
|
- if quiz == nil { // Quiz is not present in the store
|
|
|
- return nil, false, fmt.Errorf("Quiz ID %v doesn't exist in the store!", id)
|
|
|
- }
|
|
|
- } else {
|
|
|
- if r.Meta != nil {
|
|
|
- if r.Meta.ID != "" {
|
|
|
- id = r.Meta.ID
|
|
|
- } else {
|
|
|
- id = uuid.New().String()
|
|
|
- }
|
|
|
- } else {
|
|
|
- id = uuid.New().String()
|
|
|
- }
|
|
|
- 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{
|
|
|
- Meta: models.Meta{ID: uuid.New().String()},
|
|
|
- Text: s.parseTextForTags(r.Quiz.Question.Text, &quiz.Tags),
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // Populate Question field
|
|
|
- quiz.Question = q
|
|
|
-
|
|
|
- // Reset answer slice
|
|
|
- quiz.Answers = make([]*models.Answer, 0)
|
|
|
-
|
|
|
- for i, answer := range r.Quiz.Answers {
|
|
|
- answerHash := hashes[i+1]
|
|
|
- a := s.getAnswerFromHash(answerHash)
|
|
|
- if a == nil { // if the answer is not in the store add it
|
|
|
- a = s.createAnswerFromHash(answerHash, &models.Answer{
|
|
|
- ID: uuid.New().String(),
|
|
|
- Text: s.parseTextForTags(answer.Text, &quiz.Tags),
|
|
|
- })
|
|
|
- }
|
|
|
- if answer.Correct {
|
|
|
- quiz.Correct = a
|
|
|
- }
|
|
|
- quiz.Answers = append(quiz.Answers, a)
|
|
|
- }
|
|
|
-
|
|
|
- return s.createQuizFromHash(id, quizHash, quiz), true, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) CreateQuiz(r *client.CreateUpdateQuizRequest) (*models.Quiz, error) {
|
|
|
- q, _, err := s.createOrUpdateQuiz(r, "")
|
|
|
- return q, err
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) UpdateQuiz(r *client.CreateUpdateQuizRequest, id string) (*models.Quiz, bool, error) {
|
|
|
- return s.createOrUpdateQuiz(r, id)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) DeleteQuiz(r *client.DeleteQuizRequest) (*models.Quiz, error) {
|
|
|
- return s.deleteQuiz(r.ID)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) ReadAllCollections() ([]*models.Collection, error) {
|
|
|
- result := make([]*models.Collection, 0)
|
|
|
- for id := range s.collections {
|
|
|
- if collection := s.getCollectionFromID(id); collection != nil {
|
|
|
- result = append(result, collection)
|
|
|
- }
|
|
|
- }
|
|
|
- return result, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) CreateCollection(r *client.CreateUpdateCollectionRequest) (*models.Collection, error) {
|
|
|
- q, _, err := s.createOrUpdateCollection(r, "")
|
|
|
- return q, err
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) UpdateCollection(r *client.CreateUpdateCollectionRequest, id string) (*models.Collection, bool, error) {
|
|
|
- return s.createOrUpdateCollection(r, id)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) ReadCollectionByID(id string) (*models.Collection, error) {
|
|
|
- if id == "" {
|
|
|
- return nil, errors.New("ID should not be an empty string!")
|
|
|
- }
|
|
|
- collection := s.getCollectionFromID(id)
|
|
|
- if collection == nil {
|
|
|
- return nil, fmt.Errorf("Collection ID %v not found in the store", id)
|
|
|
- }
|
|
|
- return collection, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) DeleteCollection(r *client.DeleteCollectionRequest) (*models.Collection, error) {
|
|
|
- return s.deleteCollection(r.ID)
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) deleteCollection(id string) (*models.Collection, error) {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- collection := s.collections[id]
|
|
|
- if collection == nil {
|
|
|
- return nil, fmt.Errorf("Trying to delete a collection that doesn't exist in memory (ID: %v)", id)
|
|
|
- }
|
|
|
-
|
|
|
- delete(s.collections, id)
|
|
|
-
|
|
|
- return collection, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) createOrUpdateCollection(r *client.CreateUpdateCollectionRequest, id string) (*models.Collection, bool, error) {
|
|
|
- var collection *models.Collection
|
|
|
-
|
|
|
- if r.Collection == nil {
|
|
|
- return nil, false, errors.New("A request was made passing a nil collection object")
|
|
|
- }
|
|
|
-
|
|
|
- if id != "" { // we're updating a collection
|
|
|
- collection = s.getCollectionFromID(id)
|
|
|
- if collection == nil { // Quiz is not present in the store
|
|
|
- return nil, false, fmt.Errorf("Collection ID %v doesn't exist in the store!", id)
|
|
|
- }
|
|
|
- } else {
|
|
|
- id = uuid.New().String()
|
|
|
- collection = new(models.Collection)
|
|
|
- }
|
|
|
-
|
|
|
- collection.Name = r.Collection.Name
|
|
|
- collection.Query = r.Collection.Query
|
|
|
-
|
|
|
- collection.Quizzes = s.query(collection.Query)
|
|
|
-
|
|
|
- return s.createCollectionFromID(id, collection), true, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) query(query string) []*models.Quiz {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- result := make([]*models.Quiz, 0)
|
|
|
-
|
|
|
- for _, quiz := range s.quizzes {
|
|
|
- for _, tag := range quiz.Tags {
|
|
|
- if query == tag.Name {
|
|
|
- result = append(result, quiz)
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return result
|
|
|
-}
|
|
|
-
|
|
|
-func (s *MemoryProboCollectorStore) createCollectionFromID(id string, collection *models.Collection) *models.Collection {
|
|
|
- s.lock.Lock()
|
|
|
- defer s.lock.Unlock()
|
|
|
-
|
|
|
- collection.ID = id
|
|
|
-
|
|
|
- s.collections[id] = collection
|
|
|
-
|
|
|
- return collection
|
|
|
}
|