Rename SessionManager
This commit is contained in:
parent
142741ab5f
commit
7ff0d348b8
12 changed files with 295 additions and 304 deletions
|
@ -4,7 +4,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
|
||||
"git.andreafazzi.eu/andrea/probo/session"
|
||||
sessionmanager "git.andreafazzi.eu/andrea/probo/session"
|
||||
"git.andreafazzi.eu/andrea/probo/store/file"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
@ -37,7 +37,7 @@ func main() {
|
|||
log.Fatalf("An error occurred: %v", err)
|
||||
}
|
||||
|
||||
session, err := session.NewSession(
|
||||
sm, err := sessionmanager.NewSessionManager(
|
||||
"http://localhost:8080/create",
|
||||
cCtx.Args().First(),
|
||||
pStore.Storer,
|
||||
|
@ -49,7 +49,7 @@ func main() {
|
|||
log.Fatalf("An error occurred: %v", err)
|
||||
}
|
||||
|
||||
id, err := session.Push()
|
||||
id, err := sm.Push()
|
||||
if err != nil {
|
||||
log.Fatalf("An error occurred: %v", err)
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Exam struct {
|
||||
Meta
|
||||
Name string
|
||||
Participant *Participant
|
||||
Quizzes []*Quiz
|
||||
}
|
||||
|
@ -19,7 +16,7 @@ func (e *Exam) String() 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) {
|
||||
|
|
|
@ -17,7 +17,7 @@ type Participant struct {
|
|||
Firstname string `csv:"firstname"`
|
||||
Lastname string `csv:"lastname"`
|
||||
|
||||
Token int `csv:"token"`
|
||||
Token string `csv:"token"`
|
||||
|
||||
Attributes AttributeList `csv:"attributes"`
|
||||
}
|
||||
|
|
|
@ -1 +1,30 @@
|
|||
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)
|
||||
}
|
||||
|
|
119
server/main.go
119
server/main.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
|
@ -12,24 +13,25 @@ import (
|
|||
"text/template"
|
||||
|
||||
"git.andreafazzi.eu/andrea/probo/models"
|
||||
"git.andreafazzi.eu/andrea/probo/store"
|
||||
"git.andreafazzi.eu/andrea/probo/store/file"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultDataDir = "data"
|
||||
DefaultSessionDir = "sessions"
|
||||
DefaultResponseDir = "responses"
|
||||
DefaultTemplateDir = "templates"
|
||||
DefaultStaticDir = "static"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
SessionDir string
|
||||
ResponseDir string
|
||||
TemplateDir string
|
||||
StaticDir string
|
||||
}
|
||||
|
||||
type ExamSession []*models.Exam
|
||||
|
||||
type ExamTemplateData struct {
|
||||
*models.Exam
|
||||
|
||||
|
@ -37,13 +39,11 @@ type ExamTemplateData struct {
|
|||
}
|
||||
|
||||
type Server struct {
|
||||
config *Config
|
||||
mux *http.ServeMux
|
||||
responseStore *file.ResponseFileStore
|
||||
}
|
||||
config *Config
|
||||
mux *http.ServeMux
|
||||
|
||||
func GetDefaultSessionDir() string {
|
||||
return filepath.Join(DefaultDataDir, DefaultSessionDir)
|
||||
sessionFileStore *file.SessionFileStore
|
||||
responseFileStore *file.ResponseFileStore
|
||||
}
|
||||
|
||||
func GetDefaultTemplateDir() string {
|
||||
|
@ -54,8 +54,15 @@ func GetDefaultStaticDir() string {
|
|||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -71,10 +78,43 @@ func NewServer(config *Config) (*Server, error) {
|
|||
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{
|
||||
config,
|
||||
http.NewServeMux(),
|
||||
nil,
|
||||
sStore,
|
||||
rStore,
|
||||
}
|
||||
|
||||
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) {
|
||||
return NewServer(&Config{
|
||||
SessionDir: GetDefaultSessionDir(),
|
||||
ResponseDir: GetDefaultResponseDir(),
|
||||
TemplateDir: GetDefaultTemplateDir(),
|
||||
StaticDir: GetDefaultStaticDir(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var p ExamSession
|
||||
err := json.NewDecoder(r.Body).Decode(&p)
|
||||
session := new(models.Session)
|
||||
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
id := generateRandomID()
|
||||
path := filepath.Join(s.config.SessionDir, id)
|
||||
err = session.Unmarshal(data)
|
||||
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 {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for _, exam := range p {
|
||||
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}
|
||||
response := map[string]string{"id": memorySession.ID}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) {
|
||||
urlParts := strings.Split(r.URL.Path, "/")
|
||||
|
||||
examID := urlParts[1]
|
||||
sessionID := urlParts[1]
|
||||
token := urlParts[2]
|
||||
|
||||
filePath := filepath.Join(s.config.SessionDir, examID, token+".json")
|
||||
|
||||
data, err := os.ReadFile(filePath)
|
||||
session, err := s.sessionFileStore.Read(sessionID)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
exam := new(models.Exam)
|
||||
err = json.Unmarshal(data, &exam)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
exam := session.Exams[token]
|
||||
|
||||
if r.Method == "GET" {
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
|
||||
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)))
|
||||
|
||||
err = tmpl.Execute(w, ExamTemplateData{exam, examID})
|
||||
err = tmpl.Execute(w, ExamTemplateData{exam, session.ID})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -174,8 +197,18 @@ 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],
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
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>"))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -14,174 +14,176 @@ import (
|
|||
)
|
||||
|
||||
var examPayload = `
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "fe0a7ee0-f31a-413d-f123-ab5068bcaaaa",
|
||||
"Name": "Test session",
|
||||
"Quizzes": [
|
||||
{
|
||||
"id": "0610939b-a1a3-4d0e-bbc4-2aae0e8ee4b9",
|
||||
"created_at": "2023-11-27T17:51:53.910642221+01:00",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"hash": "",
|
||||
"question": {
|
||||
"id": "98c0eec9-677f-464e-9e3e-864a859f29a3",
|
||||
"created_at": "0001-01-01T00:00:00Z",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"text": "Question text with #tag1."
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
{
|
||||
"id": "74547724-b905-476f-8cfc-6ee633f92ef3",
|
||||
"text": "Answer 2"
|
||||
},
|
||||
{
|
||||
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
|
||||
"text": "Answer 3"
|
||||
},
|
||||
{
|
||||
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
|
||||
"text": "Answer 4"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"CreatedAt": "0001-01-01T00:00:00Z",
|
||||
"UpdatedAt": "0001-01-01T00:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"name": "#tag1"
|
||||
}
|
||||
],
|
||||
"correct": {
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
"CorrectPos": 0,
|
||||
"type": 0
|
||||
},
|
||||
{
|
||||
"id": "915818c4-b0ce-4efc-8def-7fc8d5fa7454",
|
||||
"created_at": "2023-11-27T17:51:53.91077796+01:00",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"hash": "",
|
||||
"question": {
|
||||
"id": "70793f0d-2855-4140-814e-40167464424b",
|
||||
"created_at": "0001-01-01T00:00:00Z",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"text": "Another question text with #tag1."
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
{
|
||||
"id": "74547724-b905-476f-8cfc-6ee633f92ef3",
|
||||
"text": "Answer 2"
|
||||
},
|
||||
{
|
||||
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
|
||||
"text": "Answer 3"
|
||||
},
|
||||
{
|
||||
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
|
||||
"text": "Answer 4"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"CreatedAt": "0001-01-01T00:00:00Z",
|
||||
"UpdatedAt": "0001-01-01T00:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"name": "#tag1"
|
||||
}
|
||||
],
|
||||
"correct": {
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
"CorrectPos": 0,
|
||||
"type": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "12345678-abcd-efgh-ijkl-9876543210ef",
|
||||
"created_at": "2023-12-01T12:00:00Z",
|
||||
"updated_at": "2023-12-01T12:00:00Z",
|
||||
"Participant": {
|
||||
"ID": "5678",
|
||||
"Firstname": "Jane",
|
||||
"Lastname": "Doe",
|
||||
"Token": 333444,
|
||||
"Attributes": {
|
||||
"class": "2 A SCI"
|
||||
}
|
||||
"Exams": {
|
||||
"111222": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"Quizzes": [
|
||||
{
|
||||
"id": "0610939b-a1a3-4d0e-bbc4-2aae0e8ee4b9",
|
||||
"created_at": "2023-11-27T17:51:53.910642221+01:00",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"hash": "",
|
||||
"question": {
|
||||
"id": "98c0eec9-677f-464e-9e3e-864a859f29a3",
|
||||
"created_at": "0001-01-01T00:00:00Z",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"text": "Question text with #tag1."
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
{
|
||||
"id": "74547724-b905-476f-8cfc-6ee633f92ef3",
|
||||
"text": "Answer 2"
|
||||
},
|
||||
{
|
||||
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
|
||||
"text": "Answer 3"
|
||||
},
|
||||
{
|
||||
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
|
||||
"text": "Answer 4"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"CreatedAt": "0001-01-01T00:00:00Z",
|
||||
"UpdatedAt": "0001-01-01T00:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"name": "#tag1"
|
||||
}
|
||||
],
|
||||
"correct": {
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
"CorrectPos": 0,
|
||||
"type": 0
|
||||
},
|
||||
{
|
||||
"id": "915818c4-b0ce-4efc-8def-7fc8d5fa7454",
|
||||
"created_at": "2023-11-27T17:51:53.91077796+01:00",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"hash": "",
|
||||
"question": {
|
||||
"id": "70793f0d-2855-4140-814e-40167464424b",
|
||||
"created_at": "0001-01-01T00:00:00Z",
|
||||
"updated_at": "0001-01-01T00:00:00Z",
|
||||
"text": "Another question text with #tag1."
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
{
|
||||
"id": "74547724-b905-476f-8cfc-6ee633f92ef3",
|
||||
"text": "Answer 2"
|
||||
},
|
||||
{
|
||||
"id": "96c1a8ee-c50c-4ebc-89e4-9f3ca356adbd",
|
||||
"text": "Answer 3"
|
||||
},
|
||||
{
|
||||
"id": "16c4b95e-64ce-4666-8cbe-b66fa59eb23b",
|
||||
"text": "Answer 4"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"CreatedAt": "0001-01-01T00:00:00Z",
|
||||
"UpdatedAt": "0001-01-01T00:00:00Z",
|
||||
"DeletedAt": null,
|
||||
"name": "#tag1"
|
||||
}
|
||||
],
|
||||
"correct": {
|
||||
"id": "1ab5ff1f-74c8-4efc-abdc-d03a3640f3bc",
|
||||
"text": "Answer 1"
|
||||
},
|
||||
"CorrectPos": 0,
|
||||
"type": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Name": "Test session",
|
||||
"Quizzes": [
|
||||
{
|
||||
"id": "22222222-abcd-efgh-ijkl-9876543210ef",
|
||||
"created_at": "2023-12-01T12:00:00Z",
|
||||
"updated_at": "2023-12-01T12:00:00Z",
|
||||
"hash": "",
|
||||
"question": {
|
||||
"id": "33333333-abcd-efgh-ijkl-9876543210ef",
|
||||
"created_at": "2023-12-01T12:00:00Z",
|
||||
"updated_at": "2023-12-01T12:00:00Z",
|
||||
"text": "Sample question text."
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"id": "44444444-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 1"
|
||||
},
|
||||
{
|
||||
"id": "55555555-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 2"
|
||||
},
|
||||
{
|
||||
"id": "66666666-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 3"
|
||||
},
|
||||
{
|
||||
"id": "77777777-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 4"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"333444": {
|
||||
"id": "12345678-abcd-efgh-ijkl-9876543210ef",
|
||||
"created_at": "2023-12-01T12:00:00Z",
|
||||
"updated_at": "2023-12-01T12:00:00Z",
|
||||
"Participant": {
|
||||
"ID": "5678",
|
||||
"Firstname": "Jane",
|
||||
"Lastname": "Doe",
|
||||
"Token": "333444",
|
||||
"Attributes": {
|
||||
"class": "2 A SCI"
|
||||
}
|
||||
},
|
||||
"Quizzes": [
|
||||
{
|
||||
"id": "22222222-abcd-efgh-ijkl-9876543210ef",
|
||||
"created_at": "2023-12-01T12:00:00Z",
|
||||
"updated_at": "2023-12-01T12:00:00Z",
|
||||
"hash": "",
|
||||
"question": {
|
||||
"id": "33333333-abcd-efgh-ijkl-9876543210ef",
|
||||
"created_at": "2023-12-01T12:00:00Z",
|
||||
"updated_at": "2023-12-01T12:00:00Z",
|
||||
"text": "Sample question text."
|
||||
},
|
||||
"answers": [
|
||||
{
|
||||
"id": "44444444-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 1"
|
||||
},
|
||||
{
|
||||
"id": "55555555-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 2"
|
||||
},
|
||||
{
|
||||
"id": "66666666-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 3"
|
||||
},
|
||||
{
|
||||
"id": "77777777-abcd-efgh-ijkl-9876543210ef",
|
||||
"text": "Option 4"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
{
|
||||
"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 {
|
||||
|
@ -218,16 +220,15 @@ func (t *serverTestSuite) TestCreate() {
|
|||
err := json.Unmarshal(response.Body.Bytes(), &result)
|
||||
t.Nil(err)
|
||||
|
||||
path := filepath.Join(GetDefaultSessionDir(), result["id"])
|
||||
path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
|
||||
|
||||
_, err = os.Stat(path)
|
||||
defer os.RemoveAll(path)
|
||||
|
||||
files, err := os.ReadDir(path)
|
||||
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)
|
||||
t.Nil(err)
|
||||
|
||||
path := filepath.Join(GetDefaultSessionDir(), result["id"])
|
||||
path := filepath.Join(GetDefaultSessionDir(), "session_fe0a7ee0-f31a-413d-f123-ab5068bcaaaa.json")
|
||||
_, err = os.Stat(path)
|
||||
t.Nil(err)
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/neat.css">
|
||||
<title>{{.Name}}</title>
|
||||
<title>Exam</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{.Name}}</h1>
|
||||
<h1>Exam</h1>
|
||||
<h2>{{.Participant.Firstname}} {{.Participant.Lastname}}</h2>
|
||||
<form action="/{{.SessionID}}/{{.Participant.Token}}" method="post">
|
||||
{{range $index, $quiz := .Quizzes}}
|
||||
|
@ -15,7 +15,7 @@
|
|||
{{range $answer := $quiz.Answers}}
|
||||
<input type="radio"
|
||||
id="{{$answer.ID}}" name="answer"
|
||||
value="{{$answer.ID}}">
|
||||
value="{{$quiz.Question.ID}}_{{$answer.ID}}">
|
||||
<label
|
||||
for="{{$answer.ID}}">{{$answer.Text}}</label><br>
|
||||
{{end}}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -10,6 +10,7 @@ var (
|
|||
DefaultGroupsSubdir = "groups"
|
||||
DefaultExamsSubdir = "exams"
|
||||
DefaultResponsesSubdir = "responses"
|
||||
DefaultSessionSubdir = "sessions"
|
||||
)
|
||||
|
||||
func GetDefaultQuizzesDir() string {
|
||||
|
@ -32,6 +33,10 @@ func GetDefaultExamsDir() string {
|
|||
return filepath.Join(DefaultBaseDir, DefaultExamsSubdir)
|
||||
}
|
||||
|
||||
func GetDefaultSessionDir() string {
|
||||
return filepath.Join(DefaultBaseDir, DefaultSessionSubdir)
|
||||
}
|
||||
|
||||
func GetDefaultResponsesDir() string {
|
||||
return filepath.Join(DefaultBaseDir, DefaultResponsesSubdir)
|
||||
}
|
||||
|
|
|
@ -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"}}
|
|
@ -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"}}
|
|
@ -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"}}
|
Loading…
Reference in a new issue