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 }