2019-11-14 12:55:22 +01:00
|
|
|
package orm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-12-13 08:32:20 +01:00
|
|
|
"log"
|
2019-12-13 13:25:33 +01:00
|
|
|
"math/rand"
|
2021-01-05 11:37:49 +01:00
|
|
|
"sort"
|
2019-12-13 13:25:33 +01:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-11-14 12:55:22 +01:00
|
|
|
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2020-01-27 11:43:56 +01:00
|
|
|
"git.andreafazzi.eu/andrea/oef/errors"
|
2019-11-14 12:55:22 +01:00
|
|
|
"git.andreafazzi.eu/andrea/oef/renderer"
|
2019-11-18 12:40:28 +01:00
|
|
|
"github.com/dgrijalva/jwt-go"
|
2019-11-14 12:55:22 +01:00
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Contest struct {
|
|
|
|
gorm.Model
|
|
|
|
|
2020-02-05 14:12:08 +01:00
|
|
|
Name string
|
|
|
|
Category string
|
2020-12-31 18:06:24 +01:00
|
|
|
Description string `gorm:"type:varchar(2048)"`
|
2019-11-14 12:55:22 +01:00
|
|
|
|
2019-12-07 08:58:30 +01:00
|
|
|
Date time.Time
|
|
|
|
StartTime time.Time
|
|
|
|
EndTime time.Time
|
2019-12-19 13:56:54 +01:00
|
|
|
Duration int // in minutes
|
2019-11-14 12:55:22 +01:00
|
|
|
|
2019-12-13 08:32:20 +01:00
|
|
|
NumQuestions int
|
|
|
|
NumAnswersPerQuestion int
|
|
|
|
|
2020-12-26 15:29:11 +01:00
|
|
|
ResetStartTime bool
|
|
|
|
|
2021-01-02 12:12:38 +01:00
|
|
|
Questions []*Question
|
|
|
|
Participants []*Participant `gorm:"many2many:subscriptions"`
|
|
|
|
Responses []*Response
|
|
|
|
StartedResponses []*Response
|
|
|
|
SavedResponses []*Response
|
2021-01-05 11:37:49 +01:00
|
|
|
Rank []*Response
|
2020-12-26 15:29:11 +01:00
|
|
|
|
|
|
|
prevStartTime time.Time
|
2019-11-14 12:55:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Contest) GetID() uint { return c.ID }
|
|
|
|
|
|
|
|
func (c *Contest) String() string {
|
|
|
|
return c.Name
|
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func (c *Contest) Create(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
2019-11-14 12:55:22 +01:00
|
|
|
if r.Method == "GET" {
|
|
|
|
return nil, nil
|
|
|
|
} else {
|
|
|
|
contest := new(Contest)
|
2019-11-15 10:41:32 +01:00
|
|
|
|
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
date := r.FormValue("Date")
|
|
|
|
startTime := r.FormValue("StartTime")
|
|
|
|
endTime := r.FormValue("EndTime")
|
2019-12-13 08:32:20 +01:00
|
|
|
log.Println("Zero time", date)
|
|
|
|
if date == "" {
|
2020-01-08 15:41:04 +01:00
|
|
|
r.PostForm.Set("Date", time.Time{}.Format(time.RFC3339))
|
|
|
|
r.PostForm.Set("StartTime", time.Time{}.Format(time.RFC3339))
|
|
|
|
r.PostForm.Set("EndTime", time.Time{}.Format(time.RFC3339))
|
2019-12-13 08:32:20 +01:00
|
|
|
} else {
|
2019-12-19 13:56:54 +01:00
|
|
|
r.PostForm.Set("Date", fmt.Sprintf("%sT%s:00+01:00", date, startTime))
|
|
|
|
r.PostForm.Set("StartTime", fmt.Sprintf("%sT%s:00+01:00", date, startTime))
|
|
|
|
r.PostForm.Set("EndTime", fmt.Sprintf("%sT%s:00+01:00", date, endTime))
|
2019-12-13 08:32:20 +01:00
|
|
|
}
|
2019-11-15 10:41:32 +01:00
|
|
|
|
|
|
|
err = renderer.Decode(contest, r)
|
2019-11-14 12:55:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-15 11:27:00 +01:00
|
|
|
contest, err = CreateContest(db, contest)
|
2019-11-14 12:55:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return contest, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func (c *Contest) Read(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
2019-11-14 12:55:22 +01:00
|
|
|
var contest Contest
|
|
|
|
|
|
|
|
id := args["id"]
|
|
|
|
|
2021-01-02 12:12:38 +01:00
|
|
|
if err := db._db.
|
|
|
|
Preload("Participants").
|
|
|
|
Preload("Responses").
|
|
|
|
Preload("StartedResponses", func(tx *gorm.DB) *gorm.DB {
|
2021-01-02 17:30:39 +01:00
|
|
|
return db.DB().Where("start_time <> ?", time.Time{})
|
2021-01-02 12:12:38 +01:00
|
|
|
}).
|
|
|
|
Preload("SavedResponses", func(tx *gorm.DB) *gorm.DB {
|
2021-01-02 17:30:39 +01:00
|
|
|
return db.DB().Where("end_time <> ?", time.Time{})
|
2021-01-02 12:12:38 +01:00
|
|
|
}).
|
2021-01-05 11:37:49 +01:00
|
|
|
Preload("Rank", func(tx *gorm.DB) *gorm.DB {
|
|
|
|
return db.DB().Order("end_time ASC")
|
|
|
|
}).
|
2021-01-02 12:12:38 +01:00
|
|
|
Preload("Questions").
|
|
|
|
First(&contest, id).Error; err != nil {
|
2019-11-14 12:55:22 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-05 11:37:49 +01:00
|
|
|
for _, r := range contest.Rank {
|
2021-01-05 12:28:40 +01:00
|
|
|
if err := calcScoreAndDuration(db, r); err != nil {
|
2021-01-05 11:37:49 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.SliceStable(contest.Rank, func(i, j int) bool {
|
2021-01-05 12:28:40 +01:00
|
|
|
return (contest.Rank[i].Score >= contest.Rank[j].Score) && (contest.Rank[i].Duration < contest.Rank[j].Duration)
|
2021-01-05 11:37:49 +01:00
|
|
|
})
|
|
|
|
|
2019-11-14 12:55:22 +01:00
|
|
|
return &contest, nil
|
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func (c *Contest) ReadAll(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
2019-11-14 12:55:22 +01:00
|
|
|
var contests []*Contest
|
2019-11-18 12:40:28 +01:00
|
|
|
|
|
|
|
claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
|
2020-01-27 11:43:56 +01:00
|
|
|
|
2019-11-18 12:40:28 +01:00
|
|
|
if claims["admin"].(bool) {
|
2020-01-15 11:27:00 +01:00
|
|
|
if err := db._db.Order("created_at").Find(&contests).Error; err != nil {
|
2019-11-18 12:40:28 +01:00
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
return contests, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
participant := &Participant{}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
if err := db._db.Preload("Contests").Where("username = ?", claims["name"].(string)).First(&participant).Error; err != nil {
|
2019-11-18 12:40:28 +01:00
|
|
|
return nil, err
|
|
|
|
} else {
|
|
|
|
return participant.Contests, nil
|
|
|
|
}
|
2019-11-14 12:55:22 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func (c *Contest) Update(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
2019-11-14 12:55:22 +01:00
|
|
|
if r.Method == "GET" {
|
2020-01-15 11:27:00 +01:00
|
|
|
result, err := c.Read(db, args, w, r)
|
2019-11-14 12:55:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
contest := result.(*Contest)
|
|
|
|
|
|
|
|
return contest, nil
|
|
|
|
} else {
|
2020-01-15 11:27:00 +01:00
|
|
|
contest, err := c.Read(db, args, w, r)
|
2019-11-14 12:55:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-12-26 15:29:11 +01:00
|
|
|
// FIXME: Should not be hard set.
|
|
|
|
contest.(*Contest).ResetStartTime = false
|
|
|
|
|
2019-11-14 12:55:22 +01:00
|
|
|
err = r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
date := r.FormValue("Date")
|
|
|
|
startTime := r.FormValue("StartTime")
|
|
|
|
endTime := r.FormValue("EndTime")
|
|
|
|
|
2019-12-13 08:32:20 +01:00
|
|
|
if date == "" {
|
|
|
|
r.PostForm.Set("Date", time.Time{}.Format(time.RFC3339))
|
|
|
|
r.PostForm.Set("StartTime", time.Time{}.Format(time.RFC3339))
|
|
|
|
r.PostForm.Set("EndTime", time.Time{}.Format(time.RFC3339))
|
|
|
|
} else {
|
2019-12-19 13:56:54 +01:00
|
|
|
r.PostForm.Set("Date", fmt.Sprintf("%sT%s:00+01:00", date, startTime))
|
|
|
|
r.PostForm.Set("StartTime", fmt.Sprintf("%sT%s:00+01:00", date, startTime))
|
|
|
|
r.PostForm.Set("EndTime", fmt.Sprintf("%sT%s:00+01:00", date, endTime))
|
2019-11-14 12:55:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
err = renderer.Decode(contest, r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-15 11:27:00 +01:00
|
|
|
_, err = SaveContest(db, contest)
|
2019-11-14 12:55:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-26 15:29:11 +01:00
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
contest, err = c.Read(db, args, w, r)
|
2019-11-14 12:55:22 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-26 15:29:11 +01:00
|
|
|
|
|
|
|
// Optionally reset participant start time
|
|
|
|
if contest.(*Contest).ResetStartTime {
|
2021-01-02 17:30:39 +01:00
|
|
|
err := db._db.Model(&Response{}).Where("contest_id=?", contest.(*Contest).ID).Update("start_time", time.Time{}).Error
|
2020-12-26 15:29:11 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-11-14 12:55:22 +01:00
|
|
|
return contest.(*Contest), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func (model *Contest) Delete(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
|
|
|
contest, err := model.Read(db, args, w, r)
|
2020-01-08 15:41:04 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-15 11:27:00 +01:00
|
|
|
if err := db._db.Unscoped().Delete(contest.(*Contest)).Error; err != nil {
|
2020-01-08 15:41:04 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return contest.(*Contest), nil
|
2019-11-14 12:55:22 +01:00
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func CreateContest(db *Database, contest *Contest) (*Contest, error) {
|
|
|
|
if err := db._db.Create(contest).Error; err != nil {
|
2019-11-14 12:55:22 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return contest, nil
|
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func SaveContest(db *Database, contest interface{}) (interface{}, error) {
|
|
|
|
if err := db._db.Omit("Contests").Save(contest).Error; err != nil {
|
2019-11-14 12:55:22 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return contest, nil
|
|
|
|
}
|
2019-12-13 13:25:33 +01:00
|
|
|
|
2021-01-02 17:30:39 +01:00
|
|
|
func (c *Contest) IsAlwaysActive() bool {
|
2019-12-19 13:56:54 +01:00
|
|
|
return c.StartTime.IsZero() || c.EndTime.IsZero() || c.Duration == 0
|
|
|
|
}
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
func (c *Contest) generateQuestionsOrder(db *gorm.DB) (string, error) {
|
2019-12-16 18:05:12 +01:00
|
|
|
var (
|
|
|
|
order []string
|
|
|
|
questions []*Question
|
|
|
|
)
|
|
|
|
|
2020-01-15 11:27:00 +01:00
|
|
|
if err := db.Find(&questions, Question{ContestID: c.ID}).Error; err != nil {
|
2019-12-16 18:05:12 +01:00
|
|
|
return "", err
|
|
|
|
}
|
2019-12-13 13:25:33 +01:00
|
|
|
|
2020-01-27 11:43:56 +01:00
|
|
|
if len(questions) == 0 {
|
|
|
|
return "", errors.ContestHasZeroQuestions
|
|
|
|
}
|
|
|
|
|
2019-12-13 13:25:33 +01:00
|
|
|
count := 0
|
|
|
|
generated := make(map[int]bool, 0)
|
|
|
|
|
2019-12-16 18:05:12 +01:00
|
|
|
rand.Seed(time.Now().UnixNano())
|
|
|
|
|
|
|
|
for count < len(questions) {
|
2019-12-18 10:46:39 +01:00
|
|
|
position := rand.Intn(len(questions))
|
|
|
|
|
|
|
|
if generated[position] {
|
2019-12-13 13:25:33 +01:00
|
|
|
continue
|
|
|
|
}
|
2019-12-18 10:46:39 +01:00
|
|
|
generated[position] = true
|
|
|
|
order = append(order, strconv.Itoa(int(questions[position].ID)))
|
2019-12-13 13:25:33 +01:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
2019-12-16 18:05:12 +01:00
|
|
|
return strings.Join(order, " "), nil
|
2019-12-13 13:25:33 +01:00
|
|
|
}
|