Working on response/contest time management

This commit is contained in:
Andrea Fazzi 2019-12-19 12:12:56 +01:00
parent 560ba79ab8
commit 96485fc9f3
11 changed files with 149 additions and 96 deletions

2
dist/main.bundle.js vendored

File diff suppressed because one or more lines are too long

9
dist/styles.css vendored
View file

@ -15447,4 +15447,13 @@ ul.karmen-related-elements {
text-transform: uppercase;
}
.sticky-offset {
top: 56px;
}
.oef-anchor-selection:target {
background: yellow;
}
/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJzdHlsZXMuY3NzIiwic291cmNlUm9vdCI6IiJ9*/

View file

@ -110,8 +110,15 @@ func (a *Answer) Update(args map[string]string, w http.ResponseWriter, r *http.R
}
}
func (a *Answer) Delete(args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
return nil, nil
func (model *Answer) Delete(args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
answer, err := model.Read(args, w, r)
if err != nil {
return nil, err
}
if err := DB().Unscoped().Delete(answer.(*Answer)).Error; err != nil {
return nil, err
}
return answer.(*Answer), nil
}
func CreateAnswer(answer *Answer) (*Answer, error) {

View file

@ -24,6 +24,7 @@ type Contest struct {
Date time.Time
StartTime time.Time
EndTime time.Time
Duration int // minutes
NumQuestions int
NumAnswersPerQuestion int

View file

@ -107,8 +107,15 @@ func (q *Question) Update(args map[string]string, w http.ResponseWriter, r *http
}
}
func (q *Question) Delete(args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
return nil, nil
func (model *Question) Delete(args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
question, err := model.Read(args, w, r)
if err != nil {
return nil, err
}
if err := DB().Unscoped().Delete(question.(*Question)).Error; err != nil {
return nil, err
}
return question.(*Question), nil
}
func CreateQuestion(question *Question) (*Question, error) {

View file

@ -2,9 +2,12 @@ package orm
import (
"fmt"
"log"
"net/http"
"strconv"
"strings"
"time"
"git.andreafazzi.eu/andrea/oef/renderer"
"github.com/jinzhu/gorm"
@ -31,6 +34,10 @@ type Response struct {
QuestionsOrder string
AnswersIDs string
StartTime time.Time
EndTime time.Time
TimeLeft time.Duration
Score int `gorm:"-"`
Questions []*Question
@ -164,6 +171,22 @@ func (model *Response) Update(args map[string]string, w http.ResponseWriter, r *
response := result.(*Response)
// Write StartTime for the first time if user is a participant
if isParticipant(r) && !response.Contest.StartTime.IsZero() && !response.Contest.EndTime.IsZero() {
if response.StartTime.IsZero() {
if err := DB().Model(&response).Update("start_time", time.Now()).Error; err != nil {
return nil, err
}
if err := DB().Model(&response).Update("end_time", time.Now().Add(time.Duration(response.Contest.Duration)*time.Minute)).Error; err != nil {
return nil, err
}
log.Println("StartTime/EndTime", response.StartTime, response.EndTime)
}
response.TimeLeft = response.EndTime.Sub(time.Now())
log.Println("TimeLeft:", response.TimeLeft)
}
return response, nil
} else {
response, err := model.Read(args, w, r)

View file

@ -30,6 +30,7 @@ var (
"prettyTime": prettyTime,
"prettyDateTime": prettyDateTime,
"zeroTime": zeroTime,
"minutes": minutes,
"modelPath": modelPath,
"dict": dict,
"yaml": yaml,
@ -308,6 +309,10 @@ func zeroTime(t *time.Time) bool {
return *t == time.Time{}
}
func minutes(d time.Duration) int {
return int(d.Minutes())
}
func modelPath(model string, action string, id uint) string {
var q template.URL

View file

@ -97,3 +97,12 @@ ul.karmen-related-elements {
.uppercase:valid {
text-transform: uppercase;
}
.sticky-offset {
top: 56px;
}
.oef-anchor-selection:target {
background: yellow;
}

View file

@ -35,6 +35,10 @@
{{$options := ` { name: "EndTime",id: "contest_end_time",label: "Alle ore",type: "time" } `}}
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "EndTime"|convertTime) "update" $update}}
</div>
<div class="col">
{{$options := ` { name: "Duration",id: "contest_duration",label: "Durata",type: "number" } `}}
{{template "input" dict "options" ($options|yaml) "value" (.Data|field "Duration") "update" $update}}
</div>
</div>
<div class="form-row">

View file

@ -1,10 +1,8 @@
{{define "base"}}
{{$isAdmin := .Claims|isAdmin}}
{{$isSubscriber := .Claims|isSubscriber}}
{{$isSchool := .Claims|isSchool}}
{{$isParticipant := .Claims|isParticipant}}
{{- define "base" -}}
{{- $isAdmin := .Claims|isAdmin -}}
{{- $isSubscriber := .Claims|isSubscriber -}}
{{- $isSchool := .Claims|isSchool -}}
{{- $isParticipant := .Claims|isParticipant -}}
<!DOCTYPE html>
<html lang="it">
<head>
@ -14,17 +12,13 @@
<link rel="stylesheet" href="/styles.css" />
<title>Olimpiadi di Economia e Finanza - Piattaforma di gara</title>
</head>
<body>
<nav class="navbar navbar-expand-lg fixed-top navbar-dark bg-primary">
{{$homeURL := ""}}
{{if $isAdmin}}{{$homeURL = all "Contest"}}{{end}}
{{if $isSubscriber}}{{$homeURL = "#"}}{{end}}
{{if $isParticipant}}{{$homeURL = "#"}}{{end}}
{{if $isSchool}}{{$homeURL = "#"}}{{end}}
{{- $homeURL := "" -}}
{{- if $isAdmin}}{{$homeURL = all "Contest"}}{{end}}
{{- if $isSubscriber}}{{$homeURL = "#"}}{{end}}
{{- if $isParticipant}}{{$homeURL = "#"}}{{end}}
{{- if $isSchool}}{{$homeURL = "#"}}{{end -}}
<a class="navbar-brand" href="{{$homeURL}}">
<span class="fa fa-landmark"></span>
OEF 2020
@ -33,34 +27,30 @@
<span class="navbar-toggler-icon"></span>
</button>
<div id="navbar" class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
{{if (.Claims|isAdmin)}}
{{- if (.Claims|isAdmin) -}}
<a class="nav-item nav-link {{.Options|active "Contest"}}" href="{{all "Contest"}}">Gare</a>
<a class="nav-item nav-link {{.Options|active "Question"}}" href="{{all "Question"}}">Domande</a>
<a class="nav-item nav-link {{.Options|active "Answer"}}" href="{{all "Answer"}}">Risposte</a>
<a class="nav-item nav-link {{.Options|active "School"}}" href="{{all "School"}}">Scuole</a>
<a class="nav-item nav-link {{.Options|active "Participant"}}" href="{{all "Participant"}}">Partecipanti</a>
<a class="nav-item nav-link {{.Options|active "Response"}}" href="{{all "Response"}}">Prove</a>
{{end}}
{{if $isSchool}}
{{- end -}}
{{- if $isSchool -}}
<a class="nav-item nav-link {{.Options|active "School"}}" href="{{.Claims|userId|show "School"}}">Scuola</a>
<a class="nav-item nav-link {{.Options|active "Participant"}}" href="{{all "Participant"}}">Partecipanti</a>
{{end}}
{{- end -}}
</ul>
<ul class="nav navbar-nav navbar-right">
{{if .Claims|isSubscriber}}
{{- if .Claims|isSubscriber -}}
<li><a class="nav-link" href="/logout">Esci</a></li>
{{else}}
{{- else -}}
<li><a class="nav-link" href="/logout">Disconnetti {{.Claims|username}}</a></li>
{{end}}
{{- end -}}
</ul>
</div><!--/.nav-collapse -->
</nav>
{{if .FlashMessages}}
{{- if .FlashMessages -}}
{{range $message := .FlashMessages}}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<strong>Attenzione!</strong> {{$message}}
@ -69,12 +59,10 @@
</button>
</div>
{{end}}
{{end}}
{{- end -}}
<div class="base-template">
{{ template "content" . }}
</div>
<div class="modal fade" id="karmen-modal-remove" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
@ -94,9 +82,7 @@
</div>
</div>
</div>
<script src="/main.bundle.js"></script>
</body>
</html>
{{end}}

View file

@ -1,65 +1,67 @@
{{ define "content" }}
{{$isAdmin := .Claims|isAdmin}}
{{$isParticipant := .Claims|isParticipant}}
{{$update := .Options.Get "update"}}
<div class="container">
{{if $isAdmin}}
{{if $update}}
{{- define "content" -}}
{{- $isAdmin := .Claims|isAdmin -}}
{{- $isParticipant := .Claims|isParticipant -}}
{{- $update := .Options.Get "update" -}}
<div class="container-fluid">
{{- if $isAdmin -}}
{{- if $update -}}
{{template "breadcrumb" toSlice "Prove" (all "Response") (.Data|string) (.Data.ID|show "Response") "Aggiorna" "current"}}
{{else}}
{{- else -}}
{{template "breadcrumb" toSlice "Prove" (all "Response") "Aggiungi" "current"}}
{{end}}
{{end}}
{{if $isAdmin}}
{{- end -}}
{{- end -}}
{{- if $isAdmin -}}
{{template "add_update_header" dict "update" $update "addTitle" "Rispondi al questionario" "updateTitle" (printf "Aggiorna %s" (.Data|string))}}
{{else}}
{{- else -}}
{{template "add_update_header" dict "update" $update "addTitle" "Rispondi al questionario" "updateTitle" (.Data|string)}}
{{end}}
{{$form := "form_add_update"}}
<form
class="needs-validation"
action="{{if $update}}{{.Data.ID|update "Response"}}{{else}}{{create "Response"}}{{end}}"
method="POST"
role="form"
id={{$form}}>
{{range $id,$question := .Data.Questions}}
<div class="form-group p-3">
<p class="lead">{{$question.Text}}</p>
{{range $answer := $question.Answers}}
<div class="form-check">
{{$checked := false}}
{{if isResponseIn $answer.ID $.Data.AnswersIDs}}
{{$checked = true}}
{{end}}
<input class="form-check-input" type="radio" name="SingleResponses.{{$id}}" id="answer_{{$answer.ID}}" value="{{$answer.ID}}" required {{if $checked}}checked{{end}}>
<label class="form-check-label {{if and $isAdmin $answer.Correct}}text-success{{end}}" for="answer_{{$answer.ID}}">
{{$answer}}
</label>
{{- end -}}
{{- $form := "form_add_update" -}}
<div class="row">
<div class="col-2 border-right">
<div class="sticky-top sticky-offset">
<nav class="nav flex-column">
{{range $n, $question := .Data.Questions -}}
<a class="nav-link" href="#question_{{$n}}">Domanda {{$n|incr}}</a>
{{end -}}
</nav>
</div>
{{end}}
</div>
{{end}}
{{if $isAdmin}}
{{$options := ` { cancelTitle: "Annulla", saveTitle: "Salva", model: "Response" } `}}
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
{{else}}
{{$options := ` { saveTitle: "Invia le risposte", model: "Response" } `}}
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
{{end}}
</form>
<div class="col">
<form
class="needs-validation"
action="{{if $update}}{{.Data.ID|update "Response"}}{{else}}{{create "Response"}}{{end}}"
method="POST"
role="form"
id={{$form}}>
{{range $id,$question := .Data.Questions -}}
<div class="oef-anchor-selection" id="question_{{$id}}">
<div class="form-group p-3">
<p class="lead">{{$id|incr}}. {{$question.Text}}</p>
{{range $answer := $question.Answers -}}
<div class="form-check">
{{$checked := false}}
{{if isResponseIn $answer.ID $.Data.AnswersIDs}}
{{$checked = true}}
{{end}}
<input class="form-check-input" type="radio" name="SingleResponses.{{$id}}" id="answer_{{$answer.ID}}" value="{{$answer.ID}}" required {{if $checked}}checked{{end}}>
<label class="form-check-label {{if and $isAdmin $answer.Correct}}text-success{{end}}" for="answer_{{$answer.ID}}">
{{$answer}}
</label>
</div>
{{end}}
</div>
</div>
<hr>
{{end}}
{{- if $isAdmin -}}
{{- $options := ` { cancelTitle: "Annulla", saveTitle: "Salva", model: "Response" } ` -}}
{{- template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update -}}
{{- else -}}
{{- $options := ` { saveTitle: "Invia le risposte", model: "Response" } ` -}}
{{template "submit_cancel_buttons" dict "options" ($options|yaml) "id" (.Data|field "ID") "update" $update}}
{{end -}}
</form>
</div>
</div>
</div>
{{ end }}
{{ end -}}