package orm import ( "fmt" "log" "math/rand" "sort" "strconv" "strings" "net/http" "time" "git.andreafazzi.eu/andrea/oef/errors" "git.andreafazzi.eu/andrea/oef/renderer" "github.com/dgrijalva/jwt-go" "github.com/jinzhu/gorm" ) type Contest struct { gorm.Model Name string Category string Description string `gorm:"type:varchar(2048)"` Date time.Time StartTime time.Time EndTime time.Time Duration int // in minutes NumQuestions int NumAnswersPerQuestion int ResetStartTime bool Questions []*Question Participants []*Participant `gorm:"many2many:subscriptions"` Responses []*Response StartedResponses []*Response SavedResponses []*Response Rank []*Response prevStartTime time.Time } 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) { if r.Method == "GET" { return nil, nil } else { contest := new(Contest) 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 == "" { 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 { 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)) } err = renderer.Decode(contest, r) if err != nil { return nil, err } contest, err = CreateContest(db, contest) 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) { 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{}) }). Preload("Rank", func(tx *gorm.DB) *gorm.DB { return db.DB().Order("end_time ASC") }). Preload("Questions"). First(&contest, id).Error; err != nil { return nil, err } for _, r := range contest.Rank { if err := calcScoreAndDuration(db, r); err != nil { return nil, err } } sort.SliceStable(contest.Rank, func(i, j int) bool { return (contest.Rank[i].Score >= contest.Rank[j].Score) && (contest.Rank[i].Duration < contest.Rank[j].Duration) }) return &contest, nil } func (c *Contest) ReadAll(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) { var contests []*Contest claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) 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 } } func (c *Contest) Update(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) { if r.Method == "GET" { result, err := c.Read(db, args, w, r) if err != nil { return nil, err } contest := result.(*Contest) return contest, nil } else { contest, err := c.Read(db, args, w, r) if err != nil { return nil, err } // FIXME: Should not be hard set. contest.(*Contest).ResetStartTime = false 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 { 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)) } err = renderer.Decode(contest, r) if err != nil { return nil, err } _, err = SaveContest(db, contest) if err != nil { return nil, err } contest, err = c.Read(db, args, w, r) if err != nil { return nil, err } // 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 if err != nil { return nil, err } } 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) if err != nil { return nil, err } if err := db._db.Unscoped().Delete(contest.(*Contest)).Error; err != nil { return nil, err } return contest.(*Contest), nil } func CreateContest(db *Database, contest *Contest) (*Contest, error) { if err := db._db.Create(contest).Error; err != nil { 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 { return nil, err } return contest, nil } func (c *Contest) IsAlwaysActive() bool { return c.StartTime.IsZero() || c.EndTime.IsZero() || c.Duration == 0 } func (c *Contest) generateQuestionsOrder(db *gorm.DB) (string, error) { var ( order []string questions []*Question ) if err := db.Find(&questions, Question{ContestID: c.ID}).Error; err != nil { return "", err } if len(questions) == 0 { return "", errors.ContestHasZeroQuestions } count := 0 generated := make(map[int]bool, 0) rand.Seed(time.Now().UnixNano()) for count < len(questions) { position := rand.Intn(len(questions)) if generated[position] { continue } generated[position] = true order = append(order, strconv.Itoa(int(questions[position].ID))) count++ } return strings.Join(order, " "), nil }