package main import ( "log" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" ) func declareAPIAdminCorrectionsRoutes(router *gin.RouterGroup) { router.GET("/corrections", func(c *gin.Context) { q := c.MustGet("question").(*Question) cts, err := q.GetCorrectionTemplates() if err != nil { log.Println("Unable to GetCorrectionTemplates:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrive correction's templates"}) return } c.JSON(http.StatusOK, cts) }) router.POST("/corrections", func(c *gin.Context) { q := c.MustGet("question").(*Question) var new CorrectionTemplate if err := c.ShouldBindJSON(&new); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } ct, err := q.NewCorrectionTemplate(new.Label, new.RegExp, new.Score, new.ScoreExplaination) if err != nil { log.Println("Unable to NewCorrectionTemplate:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to insert new correction template."}) return } c.JSON(http.StatusOK, ct) }) correctionsRoutes := router.Group("/corrections/:cid") correctionsRoutes.Use(correctionHandler) correctionsRoutes.GET("", func(c *gin.Context) { ct := c.MustGet("correctiontemplate").(*CorrectionTemplate) users, err := ct.GetUserCorrected() if err != nil { log.Println("Unable to GetUserCorrected:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve users' corrections"}) return } c.JSON(http.StatusOK, users) }) correctionsRoutes.PUT("", func(c *gin.Context) { current := c.MustGet("correctiontemplate").(*CorrectionTemplate) var new CorrectionTemplate if err := c.ShouldBindJSON(&new); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } new.Id = current.Id if err := new.Update(); err != nil { log.Println("Unable to Update correctionTemplate:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to update the correction template"}) return } c.JSON(http.StatusOK, new) }) correctionsRoutes.DELETE("", func(c *gin.Context) { ct := c.MustGet("correctiontemplate").(*CorrectionTemplate) if _, err := ct.Delete(); err != nil { log.Println("Unable to Delete correctionTemplate:", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to delete the correction template."}) return } c.JSON(http.StatusOK, nil) }) } func declareAPIAdminUserCorrectionsRoutes(router *gin.RouterGroup) { router.GET("/corrections", func(c *gin.Context) { user := c.MustGet("user").(*User) corrections, err := user.GetCorrections() if err != nil { log.Printf("Unable to GetCorrections(uid=%d): %s", user.Id, err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to retrieve corrections."}) return } c.JSON(http.StatusOK, corrections) }) router.POST("/corrections", func(c *gin.Context) { user := c.MustGet("user").(*User) var new UserCorrection if err := c.ShouldBindJSON(&new); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } correction, err := user.NewCorrection(new.IdTemplate) if err != nil { log.Printf("Unable to NewCorrection(uid=%d): %s", user.Id, err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to insert the new correction."}) return } c.JSON(http.StatusOK, correction) }) router.PUT("/corrections", func(c *gin.Context) { user := c.MustGet("user").(*User) var new map[int64]bool if err := c.ShouldBindJSON(&new); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": err.Error()}) return } correction, err := user.EraseCorrections(new) if err != nil { log.Printf("Unable to EraseCorrections(uid=%d): %s", user.Id, err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to erase the correction."}) return } c.JSON(http.StatusOK, correction) }) correctionsRoutes := router.Group("/corrections/:cid") correctionsRoutes.Use(userCorrectionHandler) correctionsRoutes.DELETE("", func(c *gin.Context) { user := c.MustGet("user").(*User) uc := c.MustGet("correction").(*UserCorrection) if _, err := uc.Delete(user); err != nil { log.Printf("Unable to Delete(uid=%d, cid=%d) user correction: %s", user.Id, uc.Id, err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to delete this correction."}) return } c.JSON(http.StatusOK, nil) }) } func correctionHandler(c *gin.Context) { q := c.MustGet("question").(*Question) if cid, err := strconv.Atoi(string(c.Param("cid"))); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid correction id"}) return } else if correction, err := q.GetCorrectionTemplate(cid); err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Correction not found"}) return } else { c.Set("correctiontemplate", correction) c.Next() } } type CorrectionTemplate struct { Id int64 `json:"id"` IdQuestion int64 `json:"id_question"` Label string `json:"label"` RegExp string `json:"regexp"` Score int `json:"score"` ScoreExplaination string `json:"score_explaination,omitempty"` } func (q *Question) GetCorrectionTemplates() (ct []*CorrectionTemplate, err error) { if rows, errr := DBQuery("SELECT id_template, id_question, label, re, score, score_explanation FROM correction_templates WHERE id_question=?", q.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { var c CorrectionTemplate if err = rows.Scan(&c.Id, &c.IdQuestion, &c.Label, &c.RegExp, &c.Score, &c.ScoreExplaination); err != nil { return } ct = append(ct, &c) } if err = rows.Err(); err != nil { return } return } } func (q *Question) GetCorrectionTemplate(id int) (c *CorrectionTemplate, err error) { c = new(CorrectionTemplate) err = DBQueryRow("SELECT id_template, id_question, label, re, score, score_explanation FROM correction_templates WHERE id_question=? AND id_template=?", q.Id, id).Scan(&c.Id, &c.IdQuestion, &c.Label, &c.RegExp, &c.Score, &c.ScoreExplaination) return } func GetCorrectionTemplate(id int64) (c *CorrectionTemplate, err error) { c = new(CorrectionTemplate) err = DBQueryRow("SELECT id_template, id_question, label, re, score, score_explanation FROM correction_templates WHERE id_template=?", id).Scan(&c.Id, &c.IdQuestion, &c.Label, &c.RegExp, &c.Score, &c.ScoreExplaination) return } func (q *Question) NewCorrectionTemplate(label string, regexp string, score int, score_explaination string) (*CorrectionTemplate, error) { if res, err := DBExec("INSERT INTO correction_templates (id_question, label, re, score, score_explanation) VALUES (?, ?, ?, ?, ?)", q.Id, label, regexp, score, score_explaination); err != nil { return nil, err } else if cid, err := res.LastInsertId(); err != nil { return nil, err } else { return &CorrectionTemplate{cid, q.Id, label, regexp, score, score_explaination}, nil } } func (t *CorrectionTemplate) Update() error { _, err := DBExec("UPDATE correction_templates SET id_question = ?, label = ?, re = ?, score = ?, score_explanation = ? WHERE id_template = ?", t.IdQuestion, t.Label, t.RegExp, t.Score, t.ScoreExplaination, t.Id) return err } func (t *CorrectionTemplate) Delete() (int64, error) { if res, err := DBExec("DELETE FROM correction_templates WHERE id_template = ?", t.Id); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } func (t *CorrectionTemplate) GetUserCorrected() (ucs []*UserCorrection, err error) { if rows, errr := DBQuery("SELECT id_correction, id_user, id_template FROM student_corrected WHERE id_template=?", t.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { var c UserCorrection if err = rows.Scan(&c.Id, &c.IdUser, &c.IdTemplate); err != nil { return } ucs = append(ucs, &c) } if err = rows.Err(); err != nil { return } return } } func ClearTemplates() (int64, error) { if res, err := DBExec("DELETE FROM correction_templates"); err != nil { return 0, err } else if nb, err := res.RowsAffected(); err != nil { return 0, err } else { return nb, err } } type UserCorrection struct { Id int64 `json:"id"` IdUser int64 `json:"id_user,omitempty"` IdTemplate int64 `json:"id_template"` } func userCorrectionHandler(c *gin.Context) { u := c.MustGet("user").(*User) if cid, err := strconv.Atoi(string(c.Param("cid"))); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"errmsg": "Invalid correction id"}) return } else if correction, err := u.GetCorrection(cid); err != nil { c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Correction not found"}) return } else { c.Set("correction", correction) c.Next() } } func (u *User) GetCorrections() (uc []UserCorrection, err error) { if rows, errr := DBQuery("SELECT id_correction, id_template FROM student_corrected WHERE id_user=?", u.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { var c UserCorrection if err = rows.Scan(&c.Id, &c.IdTemplate); err != nil { return } uc = append(uc, c) } if err = rows.Err(); err != nil { return } return } } func (u *User) GetCorrectionsTemplate() (tpls []int64, err error) { if rows, errr := DBQuery("SELECT id_template FROM student_corrected WHERE id_user=?", u.Id); errr != nil { return nil, errr } else { defer rows.Close() for rows.Next() { var tpl int64 if err = rows.Scan(&tpl); err != nil { return } tpls = append(tpls, tpl) } if err = rows.Err(); err != nil { return } return } } func (u *User) GetCorrection(id int) (c *UserCorrection, err error) { c = new(UserCorrection) err = DBQueryRow("SELECT id_correction, id_template FROM student_corrected WHERE id_user=? AND id_correction=?", u.Id, id).Scan(&c.Id, &c.IdTemplate) return } func (u *User) NewCorrection(id int64) (*UserCorrectionSummary, error) { if res, err := DBExec("INSERT INTO student_corrected (id_user, id_template) VALUES (?, ?)", u.Id, id); err != nil { return nil, err } else if cid, err := res.LastInsertId(); err != nil { return nil, err } else if ucs, err := u.ComputeScoreQuestion(id); err != nil { return nil, err } else { ucs.LastId = cid return ucs, nil } } func (u *User) EraseCorrections(ids map[int64]bool) (*UserCorrectionSummary, error) { var lastid int64 for id, st := range ids { lastid = id if st { DBExec("INSERT INTO student_corrected (id_user, id_template) VALUES (?, ?)", u.Id, id) } else { DBExec("DELETE FROM student_corrected WHERE id_user = ? AND id_template = ?", u.Id, id) } } if ucs, err := u.ComputeScoreQuestion(lastid); err != nil { return nil, err } else { return ucs, nil } } func (c *UserCorrection) Delete(u *User) (*UserCorrectionSummary, error) { if res, err := DBExec("DELETE FROM student_corrected WHERE id_correction = ?", c.Id); err != nil { return nil, err } else if _, err := res.RowsAffected(); err != nil { return nil, err } else if ucs, err := u.ComputeScoreQuestion(c.IdTemplate); err != nil { return nil, err } else { return ucs, nil } } type UserCorrectionSummary struct { LastId int64 `json:"last_id,omitempty"` Score int `json:"score"` ScoreExplaination string `json:"score_explaination"` } func (u *User) ComputeScoreQuestion(idtpl int64) (*UserCorrectionSummary, error) { if tpl, err := GetCorrectionTemplate(idtpl); err != nil { return nil, err } else if question, err := getQuestion(int(tpl.IdQuestion)); err != nil { return nil, err } else { return question.ComputeScoreQuestion(u) } } func (q *Question) ComputeScoreQuestion(u *User) (*UserCorrectionSummary, error) { if templates, err := q.GetCorrectionTemplates(); err != nil { return nil, err } else if corrections, err := u.GetCorrectionsTemplate(); err != nil { return nil, err } else { tpls := map[int64]*CorrectionTemplate{} for _, tpl := range templates { tpls[tpl.Id] = tpl } score := 100 var expl []string for _, correction := range corrections { if _, ok := tpls[correction]; ok { score += tpls[correction].Score if tpls[correction].ScoreExplaination != "" { expl = append(expl, tpls[correction].ScoreExplaination) } delete(tpls, correction) } } return &UserCorrectionSummary{Score: score, ScoreExplaination: strings.Join(expl, ". ")}, nil } }