Rename SessionManager

This commit is contained in:
andrea 2023-12-12 09:21:55 +01:00
parent 142741ab5f
commit 7ff0d348b8
12 changed files with 295 additions and 304 deletions

View file

@ -4,7 +4,7 @@ import (
"log" "log"
"os" "os"
"git.andreafazzi.eu/andrea/probo/session" sessionmanager "git.andreafazzi.eu/andrea/probo/session"
"git.andreafazzi.eu/andrea/probo/store/file" "git.andreafazzi.eu/andrea/probo/store/file"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -37,7 +37,7 @@ func main() {
log.Fatalf("An error occurred: %v", err) log.Fatalf("An error occurred: %v", err)
} }
session, err := session.NewSession( sm, err := sessionmanager.NewSessionManager(
"http://localhost:8080/create", "http://localhost:8080/create",
cCtx.Args().First(), cCtx.Args().First(),
pStore.Storer, pStore.Storer,
@ -49,7 +49,7 @@ func main() {
log.Fatalf("An error occurred: %v", err) log.Fatalf("An error occurred: %v", err)
} }
id, err := session.Push() id, err := sm.Push()
if err != nil { if err != nil {
log.Fatalf("An error occurred: %v", err) log.Fatalf("An error occurred: %v", err)
} }

View file

@ -1,15 +1,12 @@
package models package models
import ( import (
"crypto/sha256"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
) )
type Exam struct { type Exam struct {
Meta Meta
Name string
Participant *Participant Participant *Participant
Quizzes []*Quiz Quizzes []*Quiz
} }
@ -19,7 +16,7 @@ func (e *Exam) String() string {
} }
func (e *Exam) GetHash() string { func (e *Exam) GetHash() string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(strings.Join([]string{e.Name, e.Participant.GetHash()}, "")))) return ""
} }
func (e *Exam) Marshal() ([]byte, error) { func (e *Exam) Marshal() ([]byte, error) {

View file

@ -17,7 +17,7 @@ type Participant struct {
Firstname string `csv:"firstname"` Firstname string `csv:"firstname"`
Lastname string `csv:"lastname"` Lastname string `csv:"lastname"`
Token int `csv:"token"` Token string `csv:"token"`
Attributes AttributeList `csv:"attributes"` Attributes AttributeList `csv:"attributes"`
} }

View file

@ -1 +1,30 @@
package models package models
import (
"crypto/sha256"
"encoding/json"
"fmt"
)
type Session struct {
Meta
Name string
Exams map[string]*Exam
}
func (s *Session) String() string {
return s.Name
}
func (s *Session) GetHash() string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(s.Name)))
}
func (s *Session) Marshal() ([]byte, error) {
return json.Marshal(s)
}
func (s *Session) Unmarshal(data []byte) error {
return json.Unmarshal(data, s)
}

View file

@ -2,6 +2,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"io"
"log" "log"
"math/rand" "math/rand"
"net/http" "net/http"
@ -12,24 +13,25 @@ import (
"text/template" "text/template"
"git.andreafazzi.eu/andrea/probo/models" "git.andreafazzi.eu/andrea/probo/models"
"git.andreafazzi.eu/andrea/probo/store"
"git.andreafazzi.eu/andrea/probo/store/file" "git.andreafazzi.eu/andrea/probo/store/file"
) )
var ( var (
DefaultDataDir = "data" DefaultDataDir = "data"
DefaultSessionDir = "sessions" DefaultSessionDir = "sessions"
DefaultResponseDir = "responses"
DefaultTemplateDir = "templates" DefaultTemplateDir = "templates"
DefaultStaticDir = "static" DefaultStaticDir = "static"
) )
type Config struct { type Config struct {
SessionDir string SessionDir string
ResponseDir string
TemplateDir string TemplateDir string
StaticDir string StaticDir string
} }
type ExamSession []*models.Exam
type ExamTemplateData struct { type ExamTemplateData struct {
*models.Exam *models.Exam
@ -37,13 +39,11 @@ type ExamTemplateData struct {
} }
type Server struct { type Server struct {
config *Config config *Config
mux *http.ServeMux mux *http.ServeMux
responseStore *file.ResponseFileStore
}
func GetDefaultSessionDir() string { sessionFileStore *file.SessionFileStore
return filepath.Join(DefaultDataDir, DefaultSessionDir) responseFileStore *file.ResponseFileStore
} }
func GetDefaultTemplateDir() string { func GetDefaultTemplateDir() string {
@ -54,8 +54,15 @@ func GetDefaultStaticDir() string {
return DefaultStaticDir return DefaultStaticDir
} }
func NewServer(config *Config) (*Server, error) { func GetDefaultSessionDir() string {
return filepath.Join(DefaultDataDir, DefaultSessionDir)
}
func GetDefaultResponseDir() string {
return filepath.Join(DefaultDataDir, DefaultResponseDir)
}
func NewServer(config *Config) (*Server, error) {
_, err := os.Stat(config.SessionDir) _, err := os.Stat(config.SessionDir)
if err != nil { if err != nil {
return nil, err return nil, err
@ -71,10 +78,43 @@ func NewServer(config *Config) (*Server, error) {
return nil, err return nil, err
} }
sStore, err := file.NewSessionFileStore(
&file.FileStoreConfig[*models.Session, *store.SessionStore]{
FilePathConfig: file.FilePathConfig{Dir: config.SessionDir, FilePrefix: "session", FileSuffix: ".json"},
IndexDirFunc: file.DefaultIndexDirFunc[*models.Session, *store.SessionStore],
CreateEntityFunc: func() *models.Session {
return &models.Session{}
},
},
)
if err != nil {
return nil, err
}
rStore, err := file.NewResponseFileStore(
&file.FileStoreConfig[*models.Response, *store.ResponseStore]{
FilePathConfig: file.FilePathConfig{Dir: config.ResponseDir, FilePrefix: "response", FileSuffix: ".json"},
IndexDirFunc: file.DefaultIndexDirFunc[*models.Response, *store.ResponseStore],
CreateEntityFunc: func() *models.Response {
return &models.Response{}
},
},
)
if err != nil {
return nil, err
}
rStore.FilePathConfig = file.FilePathConfig{
Dir: config.ResponseDir,
FilePrefix: "response",
FileSuffix: ".json",
}
s := &Server{ s := &Server{
config, config,
http.NewServeMux(), http.NewServeMux(),
nil, sStore,
rStore,
} }
s.mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(config.StaticDir)))) s.mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(config.StaticDir))))
@ -87,69 +127,52 @@ func NewServer(config *Config) (*Server, error) {
func NewDefaultServer() (*Server, error) { func NewDefaultServer() (*Server, error) {
return NewServer(&Config{ return NewServer(&Config{
SessionDir: GetDefaultSessionDir(), SessionDir: GetDefaultSessionDir(),
ResponseDir: GetDefaultResponseDir(),
TemplateDir: GetDefaultTemplateDir(), TemplateDir: GetDefaultTemplateDir(),
StaticDir: GetDefaultStaticDir(), StaticDir: GetDefaultStaticDir(),
}) })
} }
func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request) { func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request) {
var p ExamSession session := new(models.Session)
err := json.NewDecoder(r.Body).Decode(&p)
data, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
id := generateRandomID() err = session.Unmarshal(data)
path := filepath.Join(s.config.SessionDir, id) if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
err = os.MkdirAll(path, os.ModePerm) memorySession, err := s.sessionFileStore.Create(session)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
for _, exam := range p { response := map[string]string{"id": memorySession.ID}
file, err := os.Create(filepath.Join(path, strconv.Itoa(exam.Participant.Token)) + ".json")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer file.Close()
err = json.NewEncoder(file).Encode(exam)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
response := map[string]string{"id": id}
json.NewEncoder(w).Encode(response) json.NewEncoder(w).Encode(response)
} }
func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) { func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
urlParts := strings.Split(r.URL.Path, "/") urlParts := strings.Split(r.URL.Path, "/")
examID := urlParts[1] sessionID := urlParts[1]
token := urlParts[2] token := urlParts[2]
filePath := filepath.Join(s.config.SessionDir, examID, token+".json") session, err := s.sessionFileStore.Read(sessionID)
data, err := os.ReadFile(filePath)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
exam := new(models.Exam) exam := session.Exams[token]
err = json.Unmarshal(data, &exam)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if r.Method == "GET" { if r.Method == "GET" {
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
tplData, err := os.ReadFile(filepath.Join(GetDefaultTemplateDir(), "exam.tpl")) tplData, err := os.ReadFile(filepath.Join(GetDefaultTemplateDir(), "exam.tpl"))
@ -160,7 +183,7 @@ func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
} }
tmpl := template.Must(template.New("exam").Parse(string(tplData))) tmpl := template.Must(template.New("exam").Parse(string(tplData)))
err = tmpl.Execute(w, ExamTemplateData{exam, examID}) err = tmpl.Execute(w, ExamTemplateData{exam, session.ID})
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@ -174,8 +197,18 @@ func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
parts := strings.Split(r.FormValue("answer"), "_")
_, err = s.responseFileStore.Create(&models.Response{
QuestionID: parts[0],
AnswerID: parts[1],
})
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
if r.FormValue("answer") == exam.Quizzes[0].Correct.ID { if parts[1] == exam.Quizzes[0].Correct.ID {
w.Write([]byte("<p>Corretto!</p>")) w.Write([]byte("<p>Corretto!</p>"))
return return
} }

View file

@ -14,174 +14,176 @@ import (
) )
var examPayload = ` var examPayload = `
[ {
{ "ID": "fe0a7ee0-f31a-413d-f123-ab5068bcaaaa",
"id": "fe0a7ee0-f31a-413d-ba81-ab5068bc4c73",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"Participant": {
"ID": "1234",
"Firstname": "John",
"Lastname": "Smith",
"Token": 111222,
"Attributes": {
"class": "1 D LIN"
}
},
"Name": "Test session", "Name": "Test session",
"Quizzes": [ "Exams": {
{ "111222": {
"id": "0610939b-a1a3-4d0e-bbc4-2aae0e8ee4b9", "id": "fe0a7ee0-f31a-413d-ba81-ab5068bc4c73",
"created_at": "2023-11-27T17:51:53.910642221+01:00", "created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z", "updated_at": "0001-01-01T00:00:00Z",
"hash": "", "Participant": {
"question": { "ID": "1234",
"id": "98c0eec9-677f-464e-9e3e-864a859f29a3", "Firstname": "John",
"created_at": "0001-01-01T00:00:00Z", "Lastname": "Smith",
"updated_at": "0001-01-01T00:00:00Z", "Token": "111222",
"text": "Question text with #tag1." "Attributes": {
}, "class": "1 D LIN"
"answers": [ }
{ },
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc", "Quizzes": [
"text": "Answer 1" {
}, "id": "0610939b-a1a3-4d0e-bbc4-2aae0e8ee4b9",
{ "created_at": "2023-11-27T17:51:53.910642221+01:00",
"id": "74547724-b905-476f-8cfc-6ee633f92ef3", "updated_at": "0001-01-01T00:00:00Z",
"text": "Answer 2" "hash": "",
}, "question": {
{ "id": "98c0eec9-677f-464e-9e3e-864a859f29a3",
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd", "created_at": "0001-01-01T00:00:00Z",
"text": "Answer 3" "updated_at": "0001-01-01T00:00:00Z",
}, "text": "Question text with #tag1."
{ },
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b", "answers": [
"text": "Answer 4" {
} "id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
], "text": "Answer 1"
"tags": [ },
{ {
"CreatedAt": "0001-01-01T00:00:00Z", "id": "74547724-b905-476f-8cfc-6ee633f92ef3",
"UpdatedAt": "0001-01-01T00:00:00Z", "text": "Answer 2"
"DeletedAt": null, },
"name": "#tag1" {
} "id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
], "text": "Answer 3"
"correct": { },
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc", {
"text": "Answer 1" "id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
}, "text": "Answer 4"
"CorrectPos": 0, }
"type": 0 ],
}, "tags": [
{ {
"id": "915818c4-b0ce-4efc-8def-7fc8d5fa7454", "CreatedAt": "0001-01-01T00:00:00Z",
"created_at": "2023-11-27T17:51:53.91077796+01:00", "UpdatedAt": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z", "DeletedAt": null,
"hash": "", "name": "#tag1"
"question": { }
"id": "70793f0d-2855-4140-814e-40167464424b", ],
"created_at": "0001-01-01T00:00:00Z", "correct": {
"updated_at": "0001-01-01T00:00:00Z", "id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
"text": "Another question text with #tag1." "text": "Answer 1"
}, },
"answers": [ "CorrectPos": 0,
{ "type": 0
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc", },
"text": "Answer 1" {
}, "id": "915818c4-b0ce-4efc-8def-7fc8d5fa7454",
{ "created_at": "2023-11-27T17:51:53.91077796+01:00",
"id": "74547724-b905-476f-8cfc-6ee633f92ef3", "updated_at": "0001-01-01T00:00:00Z",
"text": "Answer 2" "hash": "",
}, "question": {
{ "id": "70793f0d-2855-4140-814e-40167464424b",
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd", "created_at": "0001-01-01T00:00:00Z",
"text": "Answer 3" "updated_at": "0001-01-01T00:00:00Z",
}, "text": "Another question text with #tag1."
{ },
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b", "answers": [
"text": "Answer 4" {
} "id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
], "text": "Answer 1"
"tags": [ },
{ {
"CreatedAt": "0001-01-01T00:00:00Z", "id": "74547724-b905-476f-8cfc-6ee633f92ef3",
"UpdatedAt": "0001-01-01T00:00:00Z", "text": "Answer 2"
"DeletedAt": null, },
"name": "#tag1" {
} "id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
], "text": "Answer 3"
"correct": { },
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc", {
"text": "Answer 1" "id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
}, "text": "Answer 4"
"CorrectPos": 0, }
"type": 0 ],
} "tags": [
] {
}, "CreatedAt": "0001-01-01T00:00:00Z",
{ "UpdatedAt": "0001-01-01T00:00:00Z",
"id": "12345678-abcd-efgh-ijkl-9876543210ef", "DeletedAt": null,
"created_at": "2023-12-01T12:00:00Z", "name": "#tag1"
"updated_at": "2023-12-01T12:00:00Z", }
"Participant": { ],
"ID": "5678", "correct": {
"Firstname": "Jane", "id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
"Lastname": "Doe", "text": "Answer 1"
"Token": 333444, },
"Attributes": { "CorrectPos": 0,
"class": "2 A SCI" "type": 0
} }
]
}
}, },
"Name": "Test session", "333444": {
"Quizzes": [ "id": "12345678-abcd-efgh-ijkl-9876543210ef",
{ "created_at": "2023-12-01T12:00:00Z",
"id": "22222222-abcd-efgh-ijkl-9876543210ef", "updated_at": "2023-12-01T12:00:00Z",
"created_at": "2023-12-01T12:00:00Z", "Participant": {
"updated_at": "2023-12-01T12:00:00Z", "ID": "5678",
"hash": "", "Firstname": "Jane",
"question": { "Lastname": "Doe",
"id": "33333333-abcd-efgh-ijkl-9876543210ef", "Token": "333444",
"created_at": "2023-12-01T12:00:00Z", "Attributes": {
"updated_at": "2023-12-01T12:00:00Z", "class": "2 A SCI"
"text": "Sample question text." }
}, },
"answers": [ "Quizzes": [
{ {
"id": "44444444-abcd-efgh-ijkl-9876543210ef", "id": "22222222-abcd-efgh-ijkl-9876543210ef",
"text": "Option 1" "created_at": "2023-12-01T12:00:00Z",
}, "updated_at": "2023-12-01T12:00:00Z",
{ "hash": "",
"id": "55555555-abcd-efgh-ijkl-9876543210ef", "question": {
"text": "Option 2" "id": "33333333-abcd-efgh-ijkl-9876543210ef",
}, "created_at": "2023-12-01T12:00:00Z",
{ "updated_at": "2023-12-01T12:00:00Z",
"id": "66666666-abcd-efgh-ijkl-9876543210ef", "text": "Sample question text."
"text": "Option 3" },
}, "answers": [
{ {
"id": "77777777-abcd-efgh-ijkl-9876543210ef", "id": "44444444-abcd-efgh-ijkl-9876543210ef",
"text": "Option 4" "text": "Option 1"
} },
], {
"tags": [ "id": "55555555-abcd-efgh-ijkl-9876543210ef",
{ "text": "Option 2"
"CreatedAt": "2023-12-01T12:00:00Z", },
"UpdatedAt": "2023-12-01T12:00:00Z", {
"DeletedAt": null, "id": "66666666-abcd-efgh-ijkl-9876543210ef",
"name": "#tag2" "text": "Option 3"
} },
], {
"correct": { "id": "77777777-abcd-efgh-ijkl-9876543210ef",
"id": "44444444-abcd-efgh-ijkl-9876543210ef", "text": "Option 4"
"text": "Option 1" }
}, ],
"CorrectPos": 0, "tags": [
"type": 0 {
} "CreatedAt": "2023-12-01T12:00:00Z",
] "UpdatedAt": "2023-12-01T12:00:00Z",
} "DeletedAt": null,
] "name": "#tag2"
}
],
"correct": {
"id": "44444444-abcd-efgh-ijkl-9876543210ef",
"text": "Option 1"
},
"CorrectPos": 0,
"type": 0
}
]
}
}
` `
type serverTestSuite struct { type serverTestSuite struct {
@ -218,16 +220,15 @@ func (t *serverTestSuite) TestCreate() {
err := json.Unmarshal(response.Body.Bytes(), &result) err := json.Unmarshal(response.Body.Bytes(), &result)
t.Nil(err) t.Nil(err)
path := filepath.Join(GetDefaultSessionDir(), result["id"]) path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
_, err = os.Stat(path) _, err = os.Stat(path)
defer os.RemoveAll(path)
files, err := os.ReadDir(path)
t.Nil(err) t.Nil(err)
t.Equal(2, len(files)) defer os.Remove(path)
t.Equal("fe0a7ee0-f31a-413d-f123-ab5068bcaaaa", result["id"])
t.Nil(err)
} }
} }
} }
@ -255,7 +256,7 @@ func (t *serverTestSuite) TestRead() {
err := json.Unmarshal(response.Body.Bytes(), &result) err := json.Unmarshal(response.Body.Bytes(), &result)
t.Nil(err) t.Nil(err)
path := filepath.Join(GetDefaultSessionDir(), result["id"]) path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
_, err = os.Stat(path) _, err = os.Stat(path)
t.Nil(err) t.Nil(err)

View file

@ -3,10 +3,10 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="/static/css/neat.css"> <link rel="stylesheet" type="text/css" href="/static/css/neat.css">
<title>{{.Name}}</title> <title>Exam</title>
</head> </head>
<body> <body>
<h1>{{.Name}}</h1> <h1>Exam</h1>
<h2>{{.Participant.Firstname}} {{.Participant.Lastname}}</h2> <h2>{{.Participant.Firstname}} {{.Participant.Lastname}}</h2>
<form action="/{{.SessionID}}/{{.Participant.Token}}" method="post"> <form action="/{{.SessionID}}/{{.Participant.Token}}" method="post">
{{range $index, $quiz := .Quizzes}} {{range $index, $quiz := .Quizzes}}
@ -15,7 +15,7 @@
{{range $answer := $quiz.Answers}} {{range $answer := $quiz.Answers}}
<input type="radio" <input type="radio"
id="{{$answer.ID}}" name="answer" id="{{$answer.ID}}" name="answer"
value="{{$answer.ID}}"> value="{{$quiz.Question.ID}}_{{$answer.ID}}">
<label <label
for="{{$answer.ID}}">{{$answer.Text}}</label><br> for="{{$answer.ID}}">{{$answer.Text}}</label><br>
{{end}} {{end}}

View file

@ -1,74 +0,0 @@
package session
import (
"bytes"
"encoding/json"
"io"
"net/http"
"git.andreafazzi.eu/andrea/probo/models"
"git.andreafazzi.eu/andrea/probo/store"
)
type Session struct {
Name string
ParticipantStore *store.ParticipantStore
QuizStore *store.QuizStore
ParticipantFilter map[string]string
QuizFilter map[string]string
ServerURL string
Token int
examStore *store.ExamStore
}
func NewSession(url string, name string, pStore *store.ParticipantStore, qStore *store.QuizStore, pFilter map[string]string, qFilter map[string]string) (*Session, error) {
session := new(Session)
session.ServerURL = url
session.examStore = store.NewStore[*models.Exam]()
for _, p := range pStore.ReadAll() {
_, err := session.examStore.Create(&models.Exam{
Name: name,
Participant: p,
Quizzes: qStore.ReadAll(),
})
if err != nil {
return nil, err
}
}
return session, nil
}
func (s *Session) GetExams() []*models.Exam {
return s.examStore.ReadAll()
}
func (s *Session) Push() (string, error) {
payload, err := json.Marshal(s.examStore.ReadAll())
if err != nil {
return "", err
}
response, err := http.Post(s.ServerURL, "application/json", bytes.NewReader(payload))
if err != nil {
return "", err
}
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return "", err
}
result := map[string]string{}
err = json.Unmarshal(responseBody, &result)
if err != nil {
return "", err
}
return result["id"], nil
}

View file

@ -10,6 +10,7 @@ var (
DefaultGroupsSubdir = "groups" DefaultGroupsSubdir = "groups"
DefaultExamsSubdir = "exams" DefaultExamsSubdir = "exams"
DefaultResponsesSubdir = "responses" DefaultResponsesSubdir = "responses"
DefaultSessionSubdir = "sessions"
) )
func GetDefaultQuizzesDir() string { func GetDefaultQuizzesDir() string {
@ -32,6 +33,10 @@ func GetDefaultExamsDir() string {
return filepath.Join(DefaultBaseDir, DefaultExamsSubdir) return filepath.Join(DefaultBaseDir, DefaultExamsSubdir)
} }
func GetDefaultSessionDir() string {
return filepath.Join(DefaultBaseDir, DefaultSessionSubdir)
}
func GetDefaultResponsesDir() string { func GetDefaultResponsesDir() string {
return filepath.Join(DefaultBaseDir, DefaultResponsesSubdir) return filepath.Join(DefaultBaseDir, DefaultResponsesSubdir)
} }

View file

@ -1 +1 @@
{"id":"5467","created_at":"2023-12-05T22:00:51.525533451+01:00","updated_at":"2023-12-09T19:59:19.383186974+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-11T17:20:07.682915159+01:00","Firstname":"Jack","Lastname":"Sparrow","Token":333444,"Attributes":{"class":"2 D LIN"}}

View file

@ -1 +1 @@
{"id":"1234","created_at":"2023-12-05T22:00:51.525601298+01:00","updated_at":"2023-12-09T19:59:19.383367508+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-11T17:20:07.682995386+01:00","Firstname":"John","Lastname":"Smith","Token":111222,"Attributes":{"class":"1 D LIN"}}

View file

@ -1 +1 @@
{"id":"567812","created_at":"2023-12-05T22:00:51.525667963+01:00","updated_at":"2023-12-09T19:59:19.383472724+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-11T17:20:07.683058985+01:00","Firstname":"Wendy","Lastname":"Darling","Token":333444,"Attributes":{"class":"2 D LIN"}}