oef/orm/contest.go

272 lines
6.4 KiB
Go
Raw Permalink Normal View History

2019-11-14 12:55:22 +01:00
package orm
import (
"fmt"
"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"
"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
NumQuestions int
NumAnswersPerQuestion int
2020-12-26 15:29:11 +01:00
ResetStartTime bool
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
}
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")
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))
} 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-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
}
contest, err = CreateContest(db, contest)
2019-11-14 12:55:22 +01:00
if err != nil {
return nil, err
}
return contest, nil
}
}
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"]
if err := db._db.
Preload("Participants").
Preload("Responses").
Preload("StartedResponses", func(tx *gorm.DB) *gorm.DB {
return db.DB().Where("start_time <> ?", time.Time{})
}).
Preload("SavedResponses", func(tx *gorm.DB) *gorm.DB {
return db.DB().Where("end_time <> ?", time.Time{})
}).
2021-01-05 11:37:49 +01:00
Preload("Rank", func(tx *gorm.DB) *gorm.DB {
return db.DB().Order("end_time ASC")
}).
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
}
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
claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
2020-01-27 11:43:56 +01:00
if claims["admin"].(bool) {
if err := db._db.Order("created_at").Find(&contests).Error; err != nil {
return nil, err
} else {
return contests, nil
}
}
participant := &Participant{}
if err := db._db.Preload("Contests").Where("username = ?", claims["name"].(string)).First(&participant).Error; err != nil {
return nil, err
} else {
return participant.Contests, nil
}
2019-11-14 12:55:22 +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" {
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 {
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")
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
}
_, 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
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 {
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
}
}
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
}
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
}
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
}
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
func (c *Contest) IsAlwaysActive() bool {
2019-12-19 13:56:54 +01:00
return c.StartTime.IsZero() || c.EndTime.IsZero() || c.Duration == 0
}
func (c *Contest) generateQuestionsOrder(db *gorm.DB) (string, error) {
2019-12-16 18:05:12 +01:00
var (
order []string
questions []*Question
)
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
}