302 lines
7 KiB
Go
302 lines
7 KiB
Go
package orm
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.andreafazzi.eu/andrea/oef/errors"
|
|
"git.andreafazzi.eu/andrea/oef/renderer"
|
|
"github.com/jinzhu/gorm"
|
|
)
|
|
|
|
type SingleResponse struct {
|
|
AnswerID uint
|
|
}
|
|
|
|
type Response struct {
|
|
gorm.Model
|
|
|
|
UserModifier
|
|
|
|
Name string
|
|
|
|
Participant *Participant
|
|
ParticipantID uint
|
|
|
|
Contest *Contest
|
|
ContestID uint
|
|
|
|
QuestionsOrder string
|
|
AnswersIDs string
|
|
|
|
StartTime time.Time
|
|
EndTime time.Time
|
|
TimeLeft time.Duration
|
|
|
|
Score int `gorm:"-"`
|
|
|
|
Questions []*Question
|
|
SingleResponses []*SingleResponse `gorm:"-"`
|
|
}
|
|
|
|
func (sr *SingleResponse) UnmarshalText(text []byte) error {
|
|
id, err := strconv.Atoi(string(text))
|
|
sr.AnswerID = uint(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
|
|
}
|
|
func (model *Response) GetID() uint { return model.ID }
|
|
|
|
func (model *Response) String() string {
|
|
return model.Name
|
|
}
|
|
|
|
func (model *Response) IsActive() bool {
|
|
if !model.StartTime.IsZero() {
|
|
return !time.Now().After(model.EndTime) || model.Contest.IsAlwaysActive()
|
|
}
|
|
return (!time.Now().Before(model.Contest.StartTime) && !time.Now().After(model.Contest.EndTime)) || model.Contest.IsAlwaysActive()
|
|
}
|
|
|
|
func (model *Response) SetCreatorID(id uint) {
|
|
model.CreatorID = id
|
|
}
|
|
|
|
func (model *Response) SetCreatorRole(role string) {
|
|
model.CreatorRole = role
|
|
}
|
|
|
|
func (model *Response) SetCreatorIP(addr string) {
|
|
model.CreatorIP = addr
|
|
}
|
|
|
|
func (model *Response) SetUpdaterID(id uint) {
|
|
model.UpdaterID = id
|
|
}
|
|
|
|
func (model *Response) SetUpdaterRole(role string) {
|
|
model.UpdaterRole = role
|
|
}
|
|
|
|
func (model *Response) SetUpdaterIP(addr string) {
|
|
model.UpdaterIP = addr
|
|
}
|
|
|
|
func (model *Response) generateAnswersIDs() string {
|
|
ids := make([]string, 0)
|
|
for _, sr := range model.SingleResponses {
|
|
ids = append(ids, strconv.Itoa(int(sr.AnswerID)))
|
|
}
|
|
return strings.Join(ids, " ")
|
|
}
|
|
|
|
func (model *Response) BeforeSave(tx *gorm.DB) error {
|
|
model.AnswersIDs = model.generateAnswersIDs()
|
|
return nil
|
|
}
|
|
|
|
func (model *Response) Create(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
|
if r.Method == "GET" {
|
|
response := new(Response)
|
|
|
|
contestID := r.URL.Query().Get("contest_id")
|
|
|
|
if err := db._db.Preload("Answers").Where("contest_id = ?", contestID).Find(&response.Questions).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
} else {
|
|
response := new(Response)
|
|
err := renderer.Decode(response, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
WriteCreator(r, response)
|
|
|
|
response, err = CreateResponse(db, response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
}
|
|
}
|
|
|
|
func (model *Response) Read(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
|
var response Response
|
|
|
|
id := args["id"]
|
|
|
|
if err := db._db.
|
|
Preload("Contest").
|
|
Preload("Participant").
|
|
Preload("Creator").
|
|
Preload("Updater").
|
|
First(&response, id).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if isParticipant(r) {
|
|
if strconv.Itoa(int(response.ParticipantID)) != getModelIDFromToken(r) {
|
|
return nil, errors.NotAuthorized
|
|
}
|
|
}
|
|
|
|
if response.AnswersIDs != "" {
|
|
srIDs := strings.Split(response.AnswersIDs, " ")
|
|
for _, srID := range srIDs {
|
|
id, err := strconv.Atoi(srID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
response.SingleResponses = append(response.SingleResponses, &SingleResponse{uint(id)})
|
|
}
|
|
|
|
for _, sr := range response.SingleResponses {
|
|
var answer Answer
|
|
|
|
if err := db._db.First(&answer, sr.AnswerID).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if answer.Correct {
|
|
response.Score++
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if response.QuestionsOrder == "" {
|
|
return nil, errors.QuestionsOrderIsEmpty
|
|
}
|
|
|
|
qOrder := make([]uint, 0)
|
|
qIDs := strings.Split(response.QuestionsOrder, " ")
|
|
for _, id := range qIDs {
|
|
id, err := strconv.Atoi(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
qOrder = append(qOrder, uint(id))
|
|
}
|
|
|
|
// Fetch questions in the given order
|
|
|
|
field := fmt.Sprintf("FIELD(id,%s)", strings.Replace(response.QuestionsOrder, " ", ",", -1))
|
|
if err := db._db.Order(field).Where("contest_id = ?", response.Contest.ID).Preload("Answers", func(db *gorm.DB) *gorm.DB {
|
|
return db.Order("RAND()")
|
|
}).Find(&response.Questions).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
func (model *Response) ReadAll(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
|
var responses []*Response
|
|
if err := db._db.Preload("Contest").Preload("Participant").Order("created_at").Find(&responses).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return responses, nil
|
|
}
|
|
|
|
func (model *Response) Update(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
|
if r.Method == "GET" {
|
|
result, err := model.Read(db, args, w, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
response := result.(*Response)
|
|
|
|
// Write StartTime for the first time if user is a participant
|
|
|
|
if isParticipant(r) && !response.Contest.IsAlwaysActive() {
|
|
if !response.IsActive() {
|
|
return nil, errors.OutOfTime
|
|
}
|
|
if response.StartTime.IsZero() {
|
|
if err := db._db.Model(&response).Update("start_time", time.Now()).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
if err := db._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())
|
|
}
|
|
|
|
return response, nil
|
|
} else {
|
|
response, err := model.Read(db, args, w, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check if the contest is still active
|
|
if isParticipant(r) && !response.(*Response).IsActive() {
|
|
return nil, errors.OutOfTime
|
|
}
|
|
|
|
response.(*Response).SingleResponses = make([]*SingleResponse, 0)
|
|
|
|
err = renderer.Decode(response, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If there aren't single responses then participant
|
|
// response Starttime is resetted.
|
|
if isAdministrator(r) {
|
|
if len(response.(*Response).SingleResponses) == 0 {
|
|
response.(*Response).StartTime = time.Time{}
|
|
}
|
|
}
|
|
|
|
WriteUpdater(r, response.(*Response))
|
|
|
|
_, err = SaveResponse(db, response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
response, err = model.Read(db, args, w, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response.(*Response), nil
|
|
}
|
|
}
|
|
|
|
func (model *Response) Delete(db *Database, args map[string]string, w http.ResponseWriter, r *http.Request) (interface{}, error) {
|
|
response, err := model.Read(db, args, w, r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := db._db.Unscoped().Delete(response.(*Response)).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return response.(*Response), nil
|
|
}
|
|
|
|
func CreateResponse(db *Database, response *Response) (*Response, error) {
|
|
if err := db._db.Create(response).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
}
|
|
|
|
func SaveResponse(db *Database, response interface{}) (interface{}, error) {
|
|
if err := db._db.Omit("Creator", "Updater").Save(response).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return response, nil
|
|
}
|