Prechádzať zdrojové kódy

Something like an MVP

andrea 5 mesiacov pred
rodič
commit
68b7efa585

+ 1 - 1
cli/.gitignore

@@ -1,3 +1,3 @@
 cli
 testdata
-
+*.bk

+ 86 - 17
cli/main.go

@@ -1,10 +1,11 @@
 package main
 
 import (
+	"fmt"
 	"log"
 	"os"
 
-	sessionmanager "git.andreafazzi.eu/andrea/probo/session"
+	"git.andreafazzi.eu/andrea/probo/sessionmanager"
 	"git.andreafazzi.eu/andrea/probo/store/file"
 	"github.com/urfave/cli/v2"
 )
@@ -12,10 +13,43 @@ import (
 func main() {
 	file.DefaultBaseDir = "testdata"
 
+	sStore, err := file.NewDefaultSessionFileStore()
+	if err != nil {
+		log.Fatalf("An error occurred: %v", err)
+	}
+
+	pStore, err := file.NewParticipantDefaultFileStore()
+	if err != nil {
+		log.Fatalf("An error occurred: %v", err)
+	}
+	qStore, err := file.NewDefaultQuizFileStore()
+	if err != nil {
+		log.Fatalf("An error occurred: %v", err)
+	}
+
+	sm, err := sessionmanager.NewSessionManager(
+		"http://localhost:8080/",
+		pStore.Storer,
+		qStore.Storer,
+		nil,
+		nil,
+	)
+	if err != nil {
+		log.Fatalf("An error occurred: %v", err)
+	}
+
 	app := &cli.App{
 		Name:  "probo-cli",
-		Usage: "Quiz Management System for Power Teachers!",
+		Usage: "Quiz Management System for Hackers!",
 		Commands: []*cli.Command{
+			{
+				Name:    "init",
+				Aliases: []string{"i"},
+				Usage:   "Initialize the current directory",
+				Action: func(cCtx *cli.Context) error {
+					return nil
+				},
+			},
 			{
 				Name:    "session",
 				Aliases: []string{"s"},
@@ -28,37 +62,72 @@ func main() {
 							if cCtx.Args().Len() < 1 {
 								log.Fatalf("Please provide a session name as first argument of create.")
 							}
-							pStore, err := file.NewParticipantDefaultFileStore()
+
+							session, err := sm.Push(cCtx.Args().First())
 							if err != nil {
 								log.Fatalf("An error occurred: %v", err)
 							}
-							qStore, err := file.NewDefaultQuizFileStore()
+
+							_, err = sStore.Create(session)
 							if err != nil {
 								log.Fatalf("An error occurred: %v", err)
 							}
 
-							sm, err := sessionmanager.NewSessionManager(
-								"http://localhost:8080/create",
-								cCtx.Args().First(),
-								pStore.Storer,
-								qStore.Storer,
-								nil,
-								nil,
-							)
+							log.Println("Session upload completed with success!")
+
+							for _, p := range pStore.ReadAll() {
+								log.Printf("http://localhost:8080/%v/%v", session.ID, p.Token)
+							}
+
+							return nil
+
+						},
+					},
+					{
+						Name:  "pull",
+						Usage: "Download responses from a session",
+						Action: func(cCtx *cli.Context) error {
+							if cCtx.Args().Len() < 1 {
+								log.Fatalf("Please provide a session ID as first argument of pull.")
+							}
+
+							rStore, err := file.NewDefaultResponseFileStore()
 							if err != nil {
 								log.Fatalf("An error occurred: %v", err)
 							}
-
-							id, err := sm.Push()
+							err = sm.Pull(rStore, cCtx.Args().First())
 							if err != nil {
 								log.Fatalf("An error occurred: %v", err)
 							}
 
-							log.Printf("Session upload completed with success. URL: https://localhost:8080/%v", id)
+							log.Println("Responses download completed with success!")
 
-							for _, p := range pStore.ReadAll() {
-								log.Printf("http://localhost:8080/%v/%v", id, p.Token)
+							return nil
+
+						},
+					},
+					{
+						Name:  "scores",
+						Usage: "Show the scores for the given session",
+						Action: func(cCtx *cli.Context) error {
+							if cCtx.Args().Len() < 1 {
+								log.Fatalf("Please provide a session name as first argument of 'score'.")
 							}
+							session, err := sStore.Read(cCtx.Args().First())
+							if err != nil {
+								log.Fatalf("An error occurred: %v", err)
+							}
+							rStore, err := file.NewDefaultResponseFileStore()
+							if err != nil {
+								log.Fatalf("An error occurred: %v", err)
+							}
+
+							scores, err := sessionmanager.NewScores(rStore, session)
+							if err != nil {
+								log.Fatalf("An error occurred: %v", err)
+							}
+
+							fmt.Println(scores)
 
 							return nil
 

+ 9 - 1
models/exam.go

@@ -1,12 +1,16 @@
 package models
 
 import (
+	"crypto/sha256"
 	"encoding/json"
 	"fmt"
+	"strings"
 )
 
 type Exam struct {
 	Meta
+
+	SessionID   string
 	Participant *Participant
 	Quizzes     []*Quiz
 }
@@ -16,7 +20,11 @@ func (e *Exam) String() string {
 }
 
 func (e *Exam) GetHash() string {
-	return ""
+	qHashes := ""
+	for _, q := range e.Quizzes {
+		qHashes += q.GetHash()
+	}
+	return fmt.Sprintf("%x", sha256.Sum256([]byte(strings.Join([]string{e.Participant.GetHash(), qHashes}, ""))))
 }
 
 func (e *Exam) Marshal() ([]byte, error) {

+ 5 - 0
models/meta.go

@@ -6,9 +6,14 @@ type Meta struct {
 	ID        string    `json:"id" yaml:"id" gorm:"primaryKey"`
 	CreatedAt time.Time `json:"created_at" yaml:"created_at"`
 	UpdatedAt time.Time `json:"updated_at" yaml:"updated_at"`
+
+	UniqueIDFunc func() string `json:"-" yaml:"-"`
 }
 
 func (m *Meta) GetID() string {
+	if m.UniqueIDFunc != nil {
+		return m.UniqueIDFunc()
+	}
 	return m.ID
 }
 

+ 5 - 5
models/response.go

@@ -1,23 +1,23 @@
 package models
 
 import (
-	"crypto/sha256"
 	"encoding/json"
 	"fmt"
 )
 
 type Response struct {
 	Meta
-	QuestionID string
-	AnswerID   string
+
+	Questions map[string]string
 }
 
 func (r *Response) String() string {
-	return fmt.Sprintf("QID: %v, AID:%v", r.QuestionID, r.AnswerID)
+	return fmt.Sprintf("Questions/Answers: %v", r.Questions)
 }
 
 func (r *Response) GetHash() string {
-	return fmt.Sprintf("%x", sha256.Sum256([]byte(r.QuestionID+r.AnswerID)))
+	//	return fmt.Sprintf("%x", sha256.Sum256([]byte(r.QuestionID+r.AnswerID)))
+	return ""
 }
 
 func (r *Response) Marshal() ([]byte, error) {

+ 1 - 0
server/.gitignore

@@ -1 +1,2 @@
 server
+data

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
server/data/sessions/session_24e5a5f5-8293-4e13-a825-656117db4d5b.json


+ 51 - 14
server/main.go

@@ -119,6 +119,7 @@ func NewServer(config *Config) (*Server, error) {
 
 	s.mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(config.StaticDir))))
 	s.mux.HandleFunc("/create", s.createExamSessionHandler)
+	s.mux.HandleFunc("/responses/", s.getResponsesHandler)
 	s.mux.HandleFunc("/", s.getExamHandler)
 
 	return s, nil
@@ -133,6 +134,35 @@ func NewDefaultServer() (*Server, error) {
 	})
 }
 
+func (s *Server) getResponsesHandler(w http.ResponseWriter, r *http.Request) {
+	result := make([]*models.Response, 0)
+
+	urlParts := strings.Split(r.URL.Path, "/")
+
+	sessionID := urlParts[2]
+
+	if r.Method == "GET" {
+		session, err := s.sessionFileStore.Read(sessionID)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		for _, exam := range session.Exams {
+			responses := s.responseFileStore.ReadAll()
+			for _, r := range responses {
+				if r.ID == exam.ID {
+					result = append(result, r)
+				}
+			}
+		}
+		err = json.NewEncoder(w).Encode(result)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+	}
+}
+
 func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request) {
 	session := new(models.Session)
 
@@ -154,8 +184,12 @@ func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request
 		return
 	}
 
-	response := map[string]string{"id": memorySession.ID}
-	json.NewEncoder(w).Encode(response)
+	err = json.NewEncoder(w).Encode(memorySession)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+
 }
 
 func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
@@ -197,22 +231,25 @@ func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		parts := strings.Split(r.FormValue("answer"), "_")
-		_, err = s.responseFileStore.Create(&models.Response{
-			QuestionID: parts[0],
-			AnswerID:   parts[1],
-		})
+		response := new(models.Response)
+		response.UniqueIDFunc = func() string {
+			return exam.GetID()
+		}
+
+		response.Questions = make(map[string]string)
+		for qID, values := range r.Form {
+			for _, aID := range values {
+				response.Questions[qID] = aID
+			}
+		}
+
+		_, err = s.responseFileStore.Create(response)
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusBadRequest)
 			return
 		}
 
-		w.Header().Set("Content-Type", "text/html")
-		if parts[1] == exam.Quizzes[0].Correct.ID {
-			w.Write([]byte("<p>Corretto!</p>"))
-			return
-		}
-		w.Write([]byte("<p>Errato!</p>"))
+		w.Write([]byte("<p>Thank you for your response.</p>"))
 		return
 	}
 
@@ -236,6 +273,6 @@ func main() {
 		panic(err)
 	}
 
-	log.Println("Probo server started.", "Config", server.config)
+	log.Println("Probo server started.")
 	http.ListenAndServe(":8080", server)
 }

+ 7 - 6
server/server_test.go

@@ -10,6 +10,7 @@ import (
 	"strings"
 	"testing"
 
+	"git.andreafazzi.eu/andrea/probo/models"
 	"github.com/remogatto/prettytest"
 )
 
@@ -215,9 +216,9 @@ func (t *serverTestSuite) TestCreate() {
 		t.Equal(http.StatusOK, response.Code)
 
 		if !t.Failed() {
-			result := map[string]string{}
+			var session *models.Session
 
-			err := json.Unmarshal(response.Body.Bytes(), &result)
+			err := json.Unmarshal(response.Body.Bytes(), &session)
 			t.Nil(err)
 
 			path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
@@ -227,7 +228,7 @@ func (t *serverTestSuite) TestCreate() {
 
 			defer os.Remove(path)
 
-			t.Equal("fe0a7ee0-f31a-413d-f123-ab5068bcaaaa", result["id"])
+			t.Equal("fe0a7ee0-f31a-413d-f123-ab5068bcaaaa", session.ID)
 
 		}
 	}
@@ -251,9 +252,9 @@ func (t *serverTestSuite) TestRead() {
 		t.Equal(http.StatusOK, response.Code)
 
 		if !t.Failed() {
-			result := map[string]string{}
+			var session *models.Session
 
-			err := json.Unmarshal(response.Body.Bytes(), &result)
+			err := json.Unmarshal(response.Body.Bytes(), &session)
 			t.Nil(err)
 
 			path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
@@ -263,7 +264,7 @@ func (t *serverTestSuite) TestRead() {
 			if !t.Failed() {
 				defer os.RemoveAll(path)
 
-				request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/%s/%s", result["id"], "111222"), nil)
+				request, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/%s/%s", session.ID, "111222"), nil)
 				response := httptest.NewRecorder()
 
 				handler := http.HandlerFunc(s.getExamHandler)

+ 3 - 2
server/templates/exam.tpl

@@ -2,6 +2,7 @@
 <html>                                                                 
   <head>
     <meta charset="UTF-8">
+    <!-- <link rel="stylesheet" href="https://unpkg.com/mvp.css"> -->
     <link rel="stylesheet" type="text/css" href="/static/css/neat.css">
     <title>Exam</title>                                          
   </head>                                                                
@@ -14,8 +15,8 @@
       <p>{{$quiz.Question.Text}}</p>                                  
       {{range $answer := $quiz.Answers}}           
       <input type="radio"                                    
-	     id="{{$answer.ID}}" name="answer"        
-	     value="{{$quiz.Question.ID}}_{{$answer.ID}}">                                                      
+	     id="{{$quiz.Question.ID}}_{{$answer.ID}}" name="{{$quiz.Question.ID}}"        
+	     value="{{$answer.ID}}">                                                      
       <label                                                 
 	for="{{$answer.ID}}">{{$answer.Text}}</label><br>    
       {{end}}                                                    

+ 52 - 0
sessionmanager/score.go

@@ -0,0 +1,52 @@
+package sessionmanager
+
+import (
+	"fmt"
+	"log"
+
+	"git.andreafazzi.eu/andrea/probo/models"
+	"git.andreafazzi.eu/andrea/probo/store/file"
+)
+
+type Score struct {
+	Exam  *models.Exam
+	Score float32
+}
+
+type Scores []*Score
+
+func NewScores(responseFileStore *file.ResponseFileStore, session *models.Session) (Scores, error) {
+	var scores Scores
+
+	for _, exam := range session.Exams {
+		response, err := responseFileStore.Read(exam.ID)
+		if err != nil {
+			log.Println(err)
+			continue
+		}
+		scores = append(scores, CalcScore(response, exam))
+	}
+
+	return scores, nil
+}
+
+func (ss Scores) String() string {
+	var result string
+
+	for _, s := range ss {
+		result += fmt.Sprintf("%v\t%f\n", s.Exam.Participant, s.Score)
+	}
+
+	return result
+}
+
+func CalcScore(response *models.Response, exam *models.Exam) *Score {
+	var score float32
+	for _, q := range exam.Quizzes {
+		answerId := response.Questions[q.Question.ID]
+		if answerId == q.Correct.ID {
+			score++
+		}
+	}
+	return &Score{exam, score}
+}

+ 60 - 18
sessionmanager/sessionmanager.go

@@ -5,14 +5,14 @@ import (
 	"encoding/json"
 	"io"
 	"net/http"
+	"net/url"
 
 	"git.andreafazzi.eu/andrea/probo/models"
 	"git.andreafazzi.eu/andrea/probo/store"
+	"git.andreafazzi.eu/andrea/probo/store/file"
 )
 
 type SessionManager struct {
-	Name string
-
 	ParticipantStore *store.ParticipantStore
 	QuizStore        *store.QuizStore
 
@@ -21,18 +21,23 @@ type SessionManager struct {
 	ServerURL         string
 	Token             int
 
-	examStore *store.ExamStore
+	examFileStore *file.ExamFileStore
 }
 
-func NewSessionManager(url string, name string, pStore *store.ParticipantStore, qStore *store.QuizStore, pFilter map[string]string, qFilter map[string]string) (*SessionManager, error) {
+func NewSessionManager(url string, pStore *store.ParticipantStore, qStore *store.QuizStore, pFilter map[string]string, qFilter map[string]string) (*SessionManager, error) {
 	sm := new(SessionManager)
 
-	sm.Name = name
 	sm.ServerURL = url
-	sm.examStore = store.NewStore[*models.Exam]()
+
+	var err error
+
+	sm.examFileStore, err = file.NewDefaultExamFileStore()
+	if err != nil {
+		return nil, err
+	}
 
 	for _, p := range pStore.ReadAll() {
-		_, err := sm.examStore.Create(&models.Exam{
+		_, err := sm.examFileStore.Create(&models.Exam{
 			Participant: p,
 			Quizzes:     qStore.ReadAll(),
 		})
@@ -45,12 +50,45 @@ func NewSessionManager(url string, name string, pStore *store.ParticipantStore,
 }
 
 func (sm *SessionManager) GetExams() []*models.Exam {
-	return sm.examStore.ReadAll()
+	return sm.examFileStore.ReadAll()
 }
 
-func (sm *SessionManager) Push() (string, error) {
+func (sm *SessionManager) Pull(rStore *file.ResponseFileStore, sessionID string) error {
+	url, err := url.JoinPath(sm.ServerURL, "responses/", sessionID)
+	if err != nil {
+		return err
+	}
+
+	rr, err := http.Get(url)
+	if err != nil {
+		return err
+	}
+
+	responseBody, err := io.ReadAll(rr.Body)
+	if err != nil {
+		return err
+	}
+
+	responses := make([]*models.Response, 0)
+
+	err = json.Unmarshal(responseBody, &responses)
+	if err != nil {
+		return err
+	}
+
+	for _, response := range responses {
+		_, err := rStore.Create(response)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (sm *SessionManager) Push(name string) (*models.Session, error) {
 	session := &models.Session{
-		Name:  sm.Name,
+		Name:  name,
 		Exams: make(map[string]*models.Exam),
 	}
 
@@ -60,24 +98,28 @@ func (sm *SessionManager) Push() (string, error) {
 
 	payload, err := session.Marshal()
 	if err != nil {
-		return "", err
+		return nil, err
+	}
+
+	url, err := url.JoinPath(sm.ServerURL, "create")
+	if err != nil {
+		return nil, err
 	}
 
-	response, err := http.Post(sm.ServerURL, "application/json", bytes.NewReader(payload))
+	response, err := http.Post(url, "application/json", bytes.NewReader(payload))
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 
 	responseBody, err := io.ReadAll(response.Body)
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 
-	result := map[string]string{}
-	err = json.Unmarshal(responseBody, &result)
+	err = json.Unmarshal(responseBody, &session)
 	if err != nil {
-		return "", err
+		return nil, err
 	}
 
-	return result["id"], nil
+	return session, nil
 }

+ 0 - 5
store/collection.go

@@ -1,5 +0,0 @@
-package store
-
-import "git.andreafazzi.eu/andrea/probo/models"
-
-type CollectionStore = Store[*models.Collection]

+ 0 - 72
store/collection_test.go

@@ -1,72 +0,0 @@
-package store
-
-import (
-	"git.andreafazzi.eu/andrea/probo/models"
-	"github.com/remogatto/prettytest"
-)
-
-type collectionTestSuite struct {
-	prettytest.Suite
-}
-
-func (t *collectionTestSuite) TestCreateCollection() {
-	quizStore := NewQuizStore()
-	quiz_1, _ := quizStore.Create(
-		&models.Quiz{
-			Question: &models.Question{Text: "Question text #tag1 #tag3."},
-			Answers: []*models.Answer{
-				{Text: "Answer 1"},
-				{Text: "Answer 2"},
-				{Text: "Answer 3"},
-				{Text: "Answer 4"},
-			},
-		})
-
-	quizStore.Create(
-		&models.Quiz{
-			Question: &models.Question{Text: "Question text #tag2."},
-			Answers: []*models.Answer{
-				{Text: "Answer 1"},
-				{Text: "Answer 2"},
-				{Text: "Answer 3"},
-				{Text: "Answer 4"},
-			},
-		})
-
-	quiz_2, _ := quizStore.Create(
-		&models.Quiz{
-			Question: &models.Question{Text: "Question text #tag3."},
-			Answers: []*models.Answer{
-				{Text: "Answer 1"},
-				{Text: "Answer 2"},
-				{Text: "Answer 3"},
-				{Text: "Answer 4"},
-			},
-		})
-
-	collectionStore := NewStore[*models.Collection]()
-	collection, err := collectionStore.Create(
-		&models.Collection{
-			Name: "My Collection",
-		})
-	t.Nil(err, "Collection should be created without error")
-
-	if !t.Failed() {
-		quizzes := quizStore.FilterInCollection(collection, map[string]string{
-			"tags": "#tag1,#tag3",
-		})
-
-		t.Equal(1, len(quizzes))
-
-		count := 0
-		for _, q := range collection.Quizzes {
-			if quiz_1.ID == q.ID || quiz_2.ID == q.ID {
-				count++
-			}
-		}
-
-		t.Equal(1, count)
-		t.Equal(1, len(collection.Quizzes))
-	}
-
-}

+ 0 - 27
store/file/collection.go

@@ -1,27 +0,0 @@
-package file
-
-import (
-	"git.andreafazzi.eu/andrea/probo/models"
-	"git.andreafazzi.eu/andrea/probo/store"
-)
-
-type CollectionFileStore = FileStore[*models.Collection, *store.Store[*models.Collection]]
-
-func NewCollectionFileStore(config *FileStoreConfig[*models.Collection, *store.CollectionStore]) (*CollectionFileStore, error) {
-	return NewFileStore[*models.Collection](config, store.NewStore[*models.Collection]())
-}
-
-func NewDefaultCollectionFileStore() (*CollectionFileStore, error) {
-	return NewCollectionFileStore(
-		&FileStoreConfig[*models.Collection, *store.CollectionStore]{
-			FilePathConfig: FilePathConfig{GetDefaultCollectionsDir(), "collection", ".json"},
-			IndexDirFunc:   DefaultIndexDirFunc[*models.Collection, *store.CollectionStore],
-			CreateEntityFunc: func() *models.Collection {
-				return &models.Collection{
-					Quizzes: make([]*models.Quiz, 0),
-				}
-			},
-		},
-	)
-
-}

+ 0 - 68
store/file/collection_test.go

@@ -1,68 +0,0 @@
-package file
-
-import (
-	"os"
-
-	"git.andreafazzi.eu/andrea/probo/models"
-	"git.andreafazzi.eu/andrea/probo/store"
-	"github.com/remogatto/prettytest"
-)
-
-type collectionTestSuite struct {
-	prettytest.Suite
-}
-
-func (t *collectionTestSuite) TestCreateCollection() {
-	quizStore := store.NewQuizStore()
-
-	quizStore.Create(
-		&models.Quiz{
-			Question: &models.Question{Text: "Question text #tag1 #tag3."},
-			Answers: []*models.Answer{
-				{Text: "Answer 1"},
-				{Text: "Answer 2"},
-				{Text: "Answer 3"},
-				{Text: "Answer 4"},
-			},
-		})
-
-	quizStore.Create(
-		&models.Quiz{
-			Question: &models.Question{Text: "Question text #tag2."},
-			Answers: []*models.Answer{
-				{Text: "Answer 1"},
-				{Text: "Answer 2"},
-				{Text: "Answer 3"},
-				{Text: "Answer 4"},
-			},
-		})
-
-	quizStore.Create(
-		&models.Quiz{
-			Question: &models.Question{Text: "Question text #tag3."},
-			Answers: []*models.Answer{
-				{Text: "Answer 1"},
-				{Text: "Answer 2"},
-				{Text: "Answer 3"},
-				{Text: "Answer 4"},
-			},
-		})
-
-	store, err := NewDefaultCollectionFileStore()
-	t.Nil(err)
-
-	c := new(models.Collection)
-	c.Name = "MyCollection"
-
-	quizStore.FilterInCollection(c, map[string]string{"tags": "#tag3"})
-
-	_, err = store.Create(c)
-
-	exists, err := os.Stat(store.GetPath(c))
-
-	t.Nil(err)
-	t.Not(t.Nil(exists))
-	t.Equal(2, len(c.Quizzes))
-
-	defer os.Remove(store.GetPath(c))
-}

+ 7 - 0
store/file/defaults.go

@@ -11,6 +11,13 @@ var (
 	DefaultExamsSubdir        = "exams"
 	DefaultResponsesSubdir    = "responses"
 	DefaultSessionSubdir      = "sessions"
+
+	Dirs = []string{
+		GetDefaultQuizzesDir(),
+		GetDefaultParticipantsDir(),
+		GetDefaultExamsDir(),
+		GetDefaultSessionDir(),
+	}
 )
 
 func GetDefaultQuizzesDir() string {

+ 0 - 2
store/file/file_test.go

@@ -14,9 +14,7 @@ func TestRunner(t *testing.T) {
 	prettytest.Run(
 		t,
 		new(quizTestSuite),
-		new(collectionTestSuite),
 		new(participantTestSuite),
-		new(groupTestSuite),
 		new(examTestSuite),
 	)
 }

+ 0 - 27
store/file/group.go

@@ -1,27 +0,0 @@
-package file
-
-import (
-	"git.andreafazzi.eu/andrea/probo/models"
-	"git.andreafazzi.eu/andrea/probo/store"
-)
-
-type GroupFileStore = FileStore[*models.Group, *store.Store[*models.Group]]
-
-func NewGroupFileStore(config *FileStoreConfig[*models.Group, *store.GroupStore]) (*GroupFileStore, error) {
-	return NewFileStore[*models.Group](config, store.NewStore[*models.Group]())
-}
-
-func NewDefaultGroupFileStore() (*GroupFileStore, error) {
-	return NewGroupFileStore(
-		&FileStoreConfig[*models.Group, *store.GroupStore]{
-			FilePathConfig: FilePathConfig{GetDefaultGroupsDir(), "group", ".csv"},
-			IndexDirFunc:   DefaultIndexDirFunc[*models.Group, *store.GroupStore],
-			CreateEntityFunc: func() *models.Group {
-				return &models.Group{
-					Participants: make([]*models.Participant, 0),
-				}
-			},
-		},
-	)
-
-}

+ 0 - 77
store/file/group_test.go

@@ -1,77 +0,0 @@
-package file
-
-import (
-	"fmt"
-	"os"
-
-	"git.andreafazzi.eu/andrea/probo/models"
-	"git.andreafazzi.eu/andrea/probo/store"
-	"github.com/gocarina/gocsv"
-	"github.com/remogatto/prettytest"
-)
-
-type groupTestSuite struct {
-	prettytest.Suite
-}
-
-func (t *groupTestSuite) TestCreate() {
-	participantStore := store.NewParticipantStore()
-
-	participantStore.Create(
-		&models.Participant{
-			Firstname:  "John",
-			Lastname:   "Smith",
-			Token:      111222,
-			Attributes: models.AttributeList{"class": "1 D LIN"},
-		})
-
-	participantStore.Create(
-		&models.Participant{
-			Firstname:  "Jack",
-			Lastname:   "Sparrow",
-			Token:      222333,
-			Attributes: models.AttributeList{"class": "2 D LIN"},
-		})
-
-	groupStore, err := NewDefaultGroupFileStore()
-	t.Nil(err)
-
-	if !t.Failed() {
-		g := new(models.Group)
-		g.Name = "Test Group"
-
-		participantStore.FilterInGroup(g, map[string]string{"class": "1 D LIN"})
-
-		_, err = groupStore.Create(g)
-		t.Nil(err)
-
-		defer os.Remove(groupStore.GetPath(g))
-
-		participantsFromDisk, err := readGroupFromCSV(g.GetID())
-		t.Nil(err)
-
-		if !t.Failed() {
-			t.Equal("Smith", participantsFromDisk[0].Lastname)
-		}
-	}
-}
-
-func readGroupFromCSV(groupID string) ([]*models.Participant, error) {
-	// Build the path to the CSV file
-	csvPath := fmt.Sprintf("testdata/groups/group_%s.csv", groupID)
-
-	// Open the CSV file
-	file, err := os.Open(csvPath)
-	if err != nil {
-		return nil, fmt.Errorf("failed to open CSV file: %w", err)
-	}
-	defer file.Close()
-
-	// Parse the CSV file
-	var participants []*models.Participant
-	if err := gocsv.UnmarshalFile(file, &participants); err != nil {
-		return nil, fmt.Errorf("failed to parse CSV file: %w", err)
-	}
-
-	return participants, nil
-}

+ 1 - 1
store/file/testdata/exams/participants/jack.json

@@ -1 +1 @@
-{"id":"5467","created_at":"2023-12-05T22:00:51.525533451+01:00","updated_at":"2023-12-11T17:20:07.682915159+01:00","Firstname":"Jack","Lastname":"Sparrow","Token":333444,"Attributes":{"class":"2 D LIN"}}
+{"id":"5467","created_at":"2023-12-05T22:00:51.525533451+01:00","updated_at":"2023-12-17T18:54:31.169024304+01:00","Firstname":"Jack","Lastname":"Sparrow","Token":"333444","Attributes":{"class":"2 D LIN"}}

+ 1 - 1
store/file/testdata/exams/participants/john.json

@@ -1 +1 @@
-{"id":"1234","created_at":"2023-12-05T22:00:51.525601298+01:00","updated_at":"2023-12-11T17:20:07.682995386+01:00","Firstname":"John","Lastname":"Smith","Token":111222,"Attributes":{"class":"1 D LIN"}}
+{"id":"1234","created_at":"2023-12-05T22:00:51.525601298+01:00","updated_at":"2023-12-17T18:54:31.169085845+01:00","Firstname":"John","Lastname":"Smith","Token":"111222","Attributes":{"class":"1 D LIN"}}

+ 1 - 1
store/file/testdata/exams/participants/wendy.json

@@ -1 +1 @@
-{"id":"567812","created_at":"2023-12-05T22:00:51.525667963+01:00","updated_at":"2023-12-11T17:20:07.683058985+01:00","Firstname":"Wendy","Lastname":"Darling","Token":333444,"Attributes":{"class":"2 D LIN"}}
+{"id":"567812","created_at":"2023-12-05T22:00:51.525667963+01:00","updated_at":"2023-12-17T18:54:31.169144803+01:00","Firstname":"Wendy","Lastname":"Darling","Token":"333444","Attributes":{"class":"2 D LIN"}}

+ 0 - 5
store/group.go

@@ -1,5 +0,0 @@
-package store
-
-import "git.andreafazzi.eu/andrea/probo/models"
-
-type GroupStore = Store[*models.Group]

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov