Handle choices in UCQ (db, sync done)
This commit is contained in:
parent
333bb408e1
commit
488a032eba
@ -36,6 +36,11 @@ func init() {
|
||||
router.GET("/api/exercices/:eid/flags/:kid", apiHandler(flagHandler(showExerciceFlag)))
|
||||
router.PUT("/api/exercices/:eid/flags/:kid", apiHandler(flagHandler(updateExerciceFlag)))
|
||||
router.DELETE("/api/exercices/:eid/flags/:kid", apiHandler(flagHandler(deleteExerciceFlag)))
|
||||
router.GET("/api/exercices/:eid/flags/:kid/choices/", apiHandler(flagHandler(listFlagChoices)))
|
||||
router.GET("/api/exercices/:eid/flags/:kid/choices/:cid", apiHandler(choiceHandler(showFlagChoice)))
|
||||
router.POST("/api/exercices/:eid/flags/:kid/choices/", apiHandler(flagHandler(createFlagChoice)))
|
||||
router.PUT("/api/exercices/:eid/flags/:kid/choices/:cid", apiHandler(choiceHandler(updateFlagChoice)))
|
||||
router.DELETE("/api/exercices/:eid/flags/:kid/choices/:cid", apiHandler(choiceHandler(deleteFlagChoice)))
|
||||
|
||||
router.GET("/api/exercices/:eid/quiz", apiHandler(exerciceHandler(listExerciceQuiz)))
|
||||
router.GET("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(showExerciceQuiz)))
|
||||
@ -86,6 +91,10 @@ func listExerciceFlags(exercice fic.Exercice, body []byte) (interface{}, error)
|
||||
return exercice.GetFlags()
|
||||
}
|
||||
|
||||
func listFlagChoices(flag fic.Flag, _ fic.Exercice, body []byte) (interface{}, error) {
|
||||
return flag.GetChoices()
|
||||
}
|
||||
|
||||
func listExerciceQuiz(exercice fic.Exercice, body []byte) (interface{}, error) {
|
||||
return exercice.GetMCQ()
|
||||
}
|
||||
@ -310,6 +319,51 @@ func deleteExerciceFlag(flag fic.Flag, _ fic.Exercice, _ []byte) (interface{}, e
|
||||
return flag.Delete()
|
||||
}
|
||||
|
||||
type uploadedChoice struct {
|
||||
Label string
|
||||
Value string
|
||||
}
|
||||
|
||||
func createFlagChoice(flag fic.Flag, exercice fic.Exercice, body []byte) (interface{}, error) {
|
||||
var uc uploadedChoice
|
||||
if err := json.Unmarshal(body, &uc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uc.Label) == 0 {
|
||||
uc.Label = uc.Value
|
||||
}
|
||||
|
||||
return flag.AddChoice(uc.Label, uc.Value)
|
||||
}
|
||||
|
||||
func showFlagChoice(choice fic.FlagChoice, _ fic.Exercice, body []byte) (interface{}, error) {
|
||||
return choice, nil
|
||||
}
|
||||
|
||||
func updateFlagChoice(choice fic.FlagChoice, _ fic.Exercice, body []byte) (interface{}, error) {
|
||||
var uc uploadedChoice
|
||||
if err := json.Unmarshal(body, &uc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(uc.Label) == 0 {
|
||||
choice.Label = uc.Value
|
||||
} else {
|
||||
choice.Label = uc.Label
|
||||
}
|
||||
|
||||
if _, err := choice.Update(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return choice, nil
|
||||
}
|
||||
|
||||
func deleteFlagChoice(choice fic.FlagChoice, _ fic.Exercice, _ []byte) (interface{}, error) {
|
||||
return choice.Delete()
|
||||
}
|
||||
|
||||
func showExerciceQuiz(quiz fic.MCQ, _ fic.Exercice, body []byte) (interface{}, error) {
|
||||
return quiz, nil
|
||||
}
|
||||
|
@ -188,6 +188,26 @@ func flagHandler(f func(fic.Flag, fic.Exercice, []byte) (interface{}, error)) fu
|
||||
}
|
||||
}
|
||||
|
||||
func choiceHandler(f func(fic.FlagChoice, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var exercice fic.Exercice
|
||||
var flag fic.Flag
|
||||
flagHandler(func(fl fic.Flag, ex fic.Exercice, _ []byte) (interface{}, error) {
|
||||
exercice = ex
|
||||
flag = fl
|
||||
return nil, nil
|
||||
})(ps, body)
|
||||
|
||||
if cid, err := strconv.Atoi(string(ps.ByName("cid"))); err != nil {
|
||||
return nil, err
|
||||
} else if choice, err := flag.GetChoice(cid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(choice, exercice, body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func quizHandler(f func(fic.MCQ, fic.Exercice, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var exercice fic.Exercice
|
||||
|
@ -90,6 +90,26 @@ func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Import choices
|
||||
hasOne := false
|
||||
for cid, choice := range flag.Choice {
|
||||
if len(choice.Label) == 0 {
|
||||
choice.Label = choice.Value
|
||||
}
|
||||
|
||||
if _, err := k.AddChoice(choice.Label, choice.Value); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%q: error in UCQ %d choice %d: %s", path.Base(exercice.Path), nline + 1, cid, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if choice.Value == flag.Raw {
|
||||
hasOne = true
|
||||
}
|
||||
}
|
||||
if !hasOne {
|
||||
errs = append(errs, fmt.Sprintf("%q: error in UCQ %d: no valid answer defined.", path.Base(exercice.Path), nline + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
11
libfic/db.go
11
libfic/db.go
@ -168,6 +168,17 @@ CREATE TABLE IF NOT EXISTS exercice_flags(
|
||||
cksum BINARY(64) NOT NULL,
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS flag_choices(
|
||||
id_choice INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_flag INTEGER NOT NULL,
|
||||
label VARCHAR(255) NOT NULL,
|
||||
response VARCHAR(255) NOT NULL,
|
||||
FOREIGN KEY(id_flag) REFERENCES exercice_flags(id_flag)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -118,6 +118,8 @@ func (k Flag) Update() (int64, error) {
|
||||
func (k Flag) Delete() (int64, error) {
|
||||
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_flag = ?", k.Id); err != nil {
|
||||
return 0, err
|
||||
} else if _, err := DBExec("DELETE FROM flag_choices WHERE id_flag = ?", k.Id); err != nil {
|
||||
return 0, err
|
||||
} else if res, err := DBExec("DELETE FROM exercice_flags WHERE id_flag = ?", k.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
@ -131,6 +133,8 @@ func (k Flag) Delete() (int64, error) {
|
||||
func (e Exercice) WipeFlags() (int64, error) {
|
||||
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil {
|
||||
return 0, err
|
||||
} else if _, err := DBExec("DELETE FROM flag_choices WHERE id_flag IN (SELECT id_flag FROM exercice_flags WHERE id_exercice = ?)", e.Id); err != nil {
|
||||
return 0, err
|
||||
} else if res, err := DBExec("DELETE FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
|
92
libfic/flag_choice.go
Normal file
92
libfic/flag_choice.go
Normal file
@ -0,0 +1,92 @@
|
||||
package fic
|
||||
|
||||
import ()
|
||||
|
||||
// FlagChoice represents a choice a respond to a classic flag
|
||||
type FlagChoice struct {
|
||||
Id int64 `json:"id"`
|
||||
// IdFlag is the identifier of the underlying flag
|
||||
IdFlag int64 `json:"idFlag"`
|
||||
// Label is the title of the choice as displayed to players
|
||||
Label string `json:"label"`
|
||||
// Value is the raw content that'll be written as response if this choice is selected
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// GetChoices returns a list of choices for the given Flag.
|
||||
func (f Flag) GetChoices() ([]FlagChoice, error) {
|
||||
if rows, err := DBQuery("SELECT id_choice, id_flag, label, response FROM flag_choices WHERE id_flag = ?", f.Id); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
var choices = make([]FlagChoice, 0)
|
||||
for rows.Next() {
|
||||
var c FlagChoice
|
||||
c.IdFlag = f.Id
|
||||
|
||||
if err := rows.Scan(&c.Id, &c.IdFlag, &c.Label, &c.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
choices = append(choices, c)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return choices, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetChoice returns a choice for the given Flag.
|
||||
func (f Flag) GetChoice(id int) (c FlagChoice, err error) {
|
||||
if errr := DBQueryRow("SELECT id_choice, id_flag, label, response FROM flag_choices WHERE id_choice = ?", id).Scan(&c.Id, &c.IdFlag, &c.Label, &c.Value); errr != nil {
|
||||
return c, errr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddChoice creates and fills a new struct FlagChoice, from a label and a value.
|
||||
func (f Flag) AddChoice(label string, value string) (FlagChoice, error) {
|
||||
if res, err := DBExec("INSERT INTO flag_choices (id_flag, label, response) VALUES (?, ?, ?)", f.Id, label, value); err != nil {
|
||||
return FlagChoice{}, err
|
||||
} else if cid, err := res.LastInsertId(); err != nil {
|
||||
return FlagChoice{}, err
|
||||
} else {
|
||||
return FlagChoice{cid, f.Id, label, value}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Update applies modifications back to the database.
|
||||
func (c FlagChoice) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE flag_choices SET id_flag = ?, label = ?, value = ? WHERE id_choice = ?", c.IdFlag, c.Label, c.Value, c.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the flag from the database.
|
||||
func (c FlagChoice) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM flag_choices WHERE id_choice = ?", c.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
// WipeFlags deletes flags coming with the challenge.
|
||||
func (f Flag) WipeChoices() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM flag_choices WHERE id_flag = ?", f.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ func ResetGame() (error) {
|
||||
|
||||
// ResetExercices wipes out all challenges (both attempts and statements).
|
||||
func ResetExercices() (error) {
|
||||
return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "flag_found", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercice_tags", "exercices", "themes")
|
||||
return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "flag_found", "flag_choices", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercice_tags", "exercices", "themes")
|
||||
}
|
||||
|
||||
// ResetTeams wipes out all teams, incluings members and attempts.
|
||||
|
Loading…
Reference in New Issue
Block a user