diff --git a/cli/.gitignore b/cli/.gitignore
index 45b31d1..2de4cc4 100644
--- a/cli/.gitignore
+++ b/cli/.gitignore
@@ -1,3 +1,3 @@
cli
testdata
-
+*.bk
diff --git a/cli/main.go b/cli/main.go
index e6d6ac1..c1bfe44 100644
--- a/cli/main.go
+++ b/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,42 +62,77 @@ func main() {
if cCtx.Args().Len() < 1 {
log.Fatalf("Please provide a session name as first argument of create.")
}
- pStore, err := file.NewParticipantDefaultFileStore()
- if err != nil {
- log.Fatalf("An error occurred: %v", err)
- }
- qStore, err := file.NewDefaultQuizFileStore()
+
+ session, err := sm.Push(cCtx.Args().First())
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,
- )
+ _, err = sStore.Create(session)
if err != nil {
log.Fatalf("An error occurred: %v", err)
}
- id, err := sm.Push()
- 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("Session upload completed with success!")
for _, p := range pStore.ReadAll() {
- log.Printf("http://localhost:8080/%v/%v", id, p.Token)
+ 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)
+ }
+ err = sm.Pull(rStore, cCtx.Args().First())
+ if err != nil {
+ log.Fatalf("An error occurred: %v", err)
+ }
+
+ log.Println("Responses download completed with success!")
+
+ 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
+
+ },
+ },
},
},
},
diff --git a/models/exam.go b/models/exam.go
index 508d14d..cd36235 100644
--- a/models/exam.go
+++ b/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) {
diff --git a/models/meta.go b/models/meta.go
index 49e814a..19646d6 100644
--- a/models/meta.go
+++ b/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
}
diff --git a/models/response.go b/models/response.go
index bbdb775..82eda44 100644
--- a/models/response.go
+++ b/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) {
diff --git a/server/.gitignore b/server/.gitignore
index 254defd..a1e9e7d 100644
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -1 +1,2 @@
server
+data
diff --git a/server/data/sessions/session_24e5a5f5-8293-4e13-a825-656117db4d5b.json b/server/data/sessions/session_24e5a5f5-8293-4e13-a825-656117db4d5b.json
deleted file mode 100644
index 500d4aa..0000000
--- a/server/data/sessions/session_24e5a5f5-8293-4e13-a825-656117db4d5b.json
+++ /dev/null
@@ -1 +0,0 @@
-{"id":"24e5a5f5-8293-4e13-a825-656117db4d5b","created_at":"2023-12-12T09:00:57.405268351+01:00","updated_at":"2023-12-12T09:20:41.050431537+01:00","Name":"My session","Exams":{"111222":{"id":"c0140b05-a843-4599-b405-3517357bd2b5","created_at":"2023-12-12T09:00:57.402306603+01:00","updated_at":"2023-12-12T09:00:57.402306722+01:00","Participant":{"id":"1234","created_at":"2023-12-05T21:11:37.566330708+01:00","updated_at":"2023-12-12T09:00:57.402100526+01:00","Firstname":"Giuly","Lastname":"Mon Amour","Token":"111222","Attributes":{"class":"1 D LIN"}},"Quizzes":[{"id":"908bb025-6370-4273-8448-8470f9336f26","created_at":"2023-12-05T21:22:06.322398595+01:00","updated_at":"2023-12-12T09:00:57.40230252+01:00","hash":"","question":{"id":"8e963b00-477a-4a1a-bf22-d74b931f9058","created_at":"2023-12-12T09:00:57.402266804+01:00","updated_at":"2023-12-12T09:00:57.402266953+01:00","text":"Chi è l'#amore più grande della mia vita?"},"answers":[{"id":"df78ba57-b53b-411d-a8fc-ef57bce961d2","created_at":"2023-12-12T09:00:57.402274057+01:00","updated_at":"2023-12-12T09:00:57.40227419+01:00","text":"Giuly (❤️)"},{"id":"eb89e7fa-0909-4f84-bcbb-5fe89a015757","created_at":"2023-12-12T09:00:57.402276551+01:00","updated_at":"2023-12-12T09:00:57.402276604+01:00","text":"Daisy"},{"id":"3185a66b-5fce-4e22-bcd0-6dc06f2d4261","created_at":"2023-12-12T09:00:57.402278951+01:00","updated_at":"2023-12-12T09:00:57.402279003+01:00","text":"Jenny"},{"id":"49bbe2ca-9408-40e9-8625-442b27b32026","created_at":"2023-12-12T09:00:57.402281071+01:00","updated_at":"2023-12-12T09:00:57.402281125+01:00","text":"Lilly"}],"tags":[],"correct":{"id":"df78ba57-b53b-411d-a8fc-ef57bce961d2","created_at":"2023-12-12T09:00:57.402274057+01:00","updated_at":"2023-12-12T09:00:57.40227419+01:00","text":"Giuly (❤️)"},"CorrectPos":0,"type":0}]},"333222":{"id":"5b6e3f92-1a6f-4028-b618-ad434a1f6e16","created_at":"2023-12-12T09:00:57.402308673+01:00","updated_at":"2023-12-12T09:00:57.402308733+01:00","Participant":{"id":"564d0c7f-4840-443c-8325-ecd02d03624d","created_at":"2023-12-05T21:11:37.566330708+01:00","updated_at":"2023-12-12T09:00:57.401968656+01:00","Firstname":"Andrea","Lastname":"Fazzi","Token":"333222","Attributes":{"class":"1 D LIN"}},"Quizzes":[{"id":"908bb025-6370-4273-8448-8470f9336f26","created_at":"2023-12-05T21:22:06.322398595+01:00","updated_at":"2023-12-12T09:00:57.40230252+01:00","hash":"","question":{"id":"8e963b00-477a-4a1a-bf22-d74b931f9058","created_at":"2023-12-12T09:00:57.402266804+01:00","updated_at":"2023-12-12T09:00:57.402266953+01:00","text":"Chi è l'#amore più grande della mia vita?"},"answers":[{"id":"df78ba57-b53b-411d-a8fc-ef57bce961d2","created_at":"2023-12-12T09:00:57.402274057+01:00","updated_at":"2023-12-12T09:00:57.40227419+01:00","text":"Giuly (❤️)"},{"id":"eb89e7fa-0909-4f84-bcbb-5fe89a015757","created_at":"2023-12-12T09:00:57.402276551+01:00","updated_at":"2023-12-12T09:00:57.402276604+01:00","text":"Daisy"},{"id":"3185a66b-5fce-4e22-bcd0-6dc06f2d4261","created_at":"2023-12-12T09:00:57.402278951+01:00","updated_at":"2023-12-12T09:00:57.402279003+01:00","text":"Jenny"},{"id":"49bbe2ca-9408-40e9-8625-442b27b32026","created_at":"2023-12-12T09:00:57.402281071+01:00","updated_at":"2023-12-12T09:00:57.402281125+01:00","text":"Lilly"}],"tags":[],"correct":{"id":"df78ba57-b53b-411d-a8fc-ef57bce961d2","created_at":"2023-12-12T09:00:57.402274057+01:00","updated_at":"2023-12-12T09:00:57.40227419+01:00","text":"Giuly (❤️)"},"CorrectPos":0,"type":0}]}}}
\ No newline at end of file
diff --git a/server/main.go b/server/main.go
index b4d422c..2f90bb8 100644
--- a/server/main.go
+++ b/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("
Corretto!
"))
- return
- }
- w.Write([]byte("Errato!
"))
+ w.Write([]byte("Thank you for your response.
"))
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)
}
diff --git a/server/server_test.go b/server/server_test.go
index 46463f8..ea7f28a 100644
--- a/server/server_test.go
+++ b/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)
diff --git a/server/templates/exam.tpl b/server/templates/exam.tpl
index 6513d02..2c23708 100644
--- a/server/templates/exam.tpl
+++ b/server/templates/exam.tpl
@@ -2,6 +2,7 @@
+
Exam
@@ -14,8 +15,8 @@
{{$quiz.Question.Text}}
{{range $answer := $quiz.Answers}}
+ id="{{$quiz.Question.ID}}_{{$answer.ID}}" name="{{$quiz.Question.ID}}"
+ value="{{$answer.ID}}">
{{end}}
diff --git a/sessionmanager/score.go b/sessionmanager/score.go
new file mode 100644
index 0000000..a2974b6
--- /dev/null
+++ b/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}
+}
diff --git a/sessionmanager/sessionmanager.go b/sessionmanager/sessionmanager.go
index 0908f06..acbf4c2 100644
--- a/sessionmanager/sessionmanager.go
+++ b/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
}
- response, err := http.Post(sm.ServerURL, "application/json", bytes.NewReader(payload))
+ url, err := url.JoinPath(sm.ServerURL, "create")
if err != nil {
- return "", err
+ return nil, err
+ }
+
+ response, err := http.Post(url, "application/json", bytes.NewReader(payload))
+ if err != nil {
+ 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
}
diff --git a/store/collection.go b/store/collection.go
deleted file mode 100644
index 365bad1..0000000
--- a/store/collection.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package store
-
-import "git.andreafazzi.eu/andrea/probo/models"
-
-type CollectionStore = Store[*models.Collection]
diff --git a/store/collection_test.go b/store/collection_test.go
deleted file mode 100644
index 64ab324..0000000
--- a/store/collection_test.go
+++ /dev/null
@@ -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))
- }
-
-}
diff --git a/store/file/collection.go b/store/file/collection.go
deleted file mode 100644
index a980a53..0000000
--- a/store/file/collection.go
+++ /dev/null
@@ -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),
- }
- },
- },
- )
-
-}
diff --git a/store/file/collection_test.go b/store/file/collection_test.go
deleted file mode 100644
index 79ac320..0000000
--- a/store/file/collection_test.go
+++ /dev/null
@@ -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))
-}
diff --git a/store/file/defaults.go b/store/file/defaults.go
index 6ee1425..b2a7f62 100644
--- a/store/file/defaults.go
+++ b/store/file/defaults.go
@@ -11,6 +11,13 @@ var (
DefaultExamsSubdir = "exams"
DefaultResponsesSubdir = "responses"
DefaultSessionSubdir = "sessions"
+
+ Dirs = []string{
+ GetDefaultQuizzesDir(),
+ GetDefaultParticipantsDir(),
+ GetDefaultExamsDir(),
+ GetDefaultSessionDir(),
+ }
)
func GetDefaultQuizzesDir() string {
diff --git a/store/file/file_test.go b/store/file/file_test.go
index e3e35d7..73fd9f0 100644
--- a/store/file/file_test.go
+++ b/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),
)
}
diff --git a/store/file/group.go b/store/file/group.go
deleted file mode 100644
index 0c2f431..0000000
--- a/store/file/group.go
+++ /dev/null
@@ -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),
- }
- },
- },
- )
-
-}
diff --git a/store/file/group_test.go b/store/file/group_test.go
deleted file mode 100644
index 4674bc1..0000000
--- a/store/file/group_test.go
+++ /dev/null
@@ -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
-}
diff --git a/store/file/testdata/exams/participants/jack.json b/store/file/testdata/exams/participants/jack.json
index 6e2093f..33e17d0 100644
--- a/store/file/testdata/exams/participants/jack.json
+++ b/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"}}
\ No newline at end of file
+{"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"}}
\ No newline at end of file
diff --git a/store/file/testdata/exams/participants/john.json b/store/file/testdata/exams/participants/john.json
index 34f23a7..8b0e8a5 100644
--- a/store/file/testdata/exams/participants/john.json
+++ b/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"}}
\ No newline at end of file
+{"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"}}
\ No newline at end of file
diff --git a/store/file/testdata/exams/participants/wendy.json b/store/file/testdata/exams/participants/wendy.json
index 1d03169..aea8b9d 100644
--- a/store/file/testdata/exams/participants/wendy.json
+++ b/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"}}
\ No newline at end of file
+{"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"}}
\ No newline at end of file
diff --git a/store/group.go b/store/group.go
deleted file mode 100644
index 5b314c3..0000000
--- a/store/group.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package store
-
-import "git.andreafazzi.eu/andrea/probo/models"
-
-type GroupStore = Store[*models.Group]