From 01b05aaed0e43922702e2b62ff6e9684834fd65c Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Fri, 21 Jan 2022 13:06:37 +0100 Subject: [PATCH] Implement label only flag --- admin/sync/exercice_defines.go | 1 + admin/sync/exercice_keys.go | 45 ++++- .../ui/src/components/ExerciceFlags.svelte | 6 +- libfic/db.go | 32 +++ libfic/flag.go | 14 ++ libfic/flag_label.go | 187 ++++++++++++++++++ libfic/mcq_justification.go | 4 +- libfic/team.go | 5 +- libfic/team_my.go | 28 ++- 9 files changed, 311 insertions(+), 11 deletions(-) create mode 100644 libfic/flag_label.go diff --git a/admin/sync/exercice_defines.go b/admin/sync/exercice_defines.go index f7841e33..0fabe9e7 100644 --- a/admin/sync/exercice_defines.go +++ b/admin/sync/exercice_defines.go @@ -49,6 +49,7 @@ type ExerciceFlag struct { NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` NoShuffle bool Unit string `toml:"unit,omitempty"` + Variant string `toml:"variant,omitempty"` NumberMin interface{} `toml:"min,omitempty"` NumberMax interface{} `toml:"max,omitempty"` NumberStep interface{} `toml:"step,omitempty"` diff --git a/admin/sync/exercice_keys.go b/admin/sync/exercice_keys.go index 4c028d95..c7ffc972 100644 --- a/admin/sync/exercice_keys.go +++ b/admin/sync/exercice_keys.go @@ -95,11 +95,37 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo return } +func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f *fic.FlagLabel, errs []string) { + if len(flag.Label) == 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: Label cannot be empty.", path.Base(exercice.Path), flagline)) + return + } + + if flag.Raw != nil { + errs = append(errs, fmt.Sprintf("%q: flag #%d: raw cannot be defined.", path.Base(exercice.Path), flagline)) + } + + if len(flag.Choice) != 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: choices cannot be defined.", path.Base(exercice.Path), flagline)) + } + + f = &fic.FlagLabel{ + Order: int8(flagline), + Label: flag.Label, + Variant: flag.Variant, + } + return +} + func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string) (f *fic.Flag, choices []*fic.FlagChoice, errs []string) { if len(flag.Label) == 0 { flag.Label = defaultLabel } + if len(flag.Variant) != 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), flagline)) + } + if flag.Label[0] == '`' { errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not begin with `.", path.Base(exercice.Path), flagline)) flag.Label = flag.Label[1:] @@ -221,6 +247,8 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl switch strings.ToLower(flag.Type) { case "": flag.Type = "key" + case "label": + flag.Type = "label" case "key": flag.Type = "key" case "number": @@ -273,7 +301,18 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl } } - if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" { + if flag.Type == "label" { + addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1) + if len(berrs) > 0 { + errs = append(errs, berrs...) + } + if addedFlag != nil { + ret = append(ret, importFlag{ + Line: nline + 1, + Flag: addedFlag, + }) + } + } else if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" { addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag") if len(berrs) > 0 { errs = append(errs, berrs...) @@ -296,6 +335,10 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl hasOne := false isJustified := false + if len(flag.Variant) != 0 { + errs = append(errs, fmt.Sprintf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), nline+1)) + } + if !flag.NoShuffle { rand.Shuffle(len(flag.Choice), func(i, j int) { flag.Choice[i], flag.Choice[j] = flag.Choice[j], flag.Choice[i] diff --git a/frontend/ui/src/components/ExerciceFlags.svelte b/frontend/ui/src/components/ExerciceFlags.svelte index efdc8178..c176db57 100644 --- a/frontend/ui/src/components/ExerciceFlags.svelte +++ b/frontend/ui/src/components/ExerciceFlags.svelte @@ -198,7 +198,11 @@
{#each flags as flag ((flag.type?flag.type:"i") + flag.id)} - {#if flag.type == "mcq"} + {#if !flag.type && !flag.id} +
+ +
+ {:else if flag.type == "mcq"} = 3 && len(spl[0]) == 0 { var idChoice int64 diff --git a/libfic/team.go b/libfic/team.go index 96cc19e5..9260d3fc 100644 --- a/libfic/team.go +++ b/libfic/team.go @@ -312,7 +312,10 @@ func (t *Team) GetSolvedRank(e *Exercice) (nb int64, err error) { // HasPartiallySolved checks if the Team already has unlocked the given flag and returns the validation's timestamp. func (t *Team) HasPartiallySolved(f Flag) (tm *time.Time) { - if k, ok := f.(*FlagKey); ok { + if _, ok := f.(*FlagLabel); ok { + now := time.Now() + return &now + } else if k, ok := f.(*FlagKey); ok { DBQueryRow("SELECT MIN(time) FROM flag_found WHERE id_team = ? AND id_flag = ?", t.Id, k.Id).Scan(&tm) } else if m, ok := f.(*MCQ); ok { DBQueryRow("SELECT MIN(time) FROM mcq_found WHERE id_team = ? AND id_mcq = ?", t.Id, m.Id).Scan(&tm) diff --git a/libfic/team_my.go b/libfic/team_my.go index f860e021..f516df7f 100644 --- a/libfic/team_my.go +++ b/libfic/team_my.go @@ -31,7 +31,7 @@ type myTeamHint struct { Cost int64 `json:"cost"` } type myTeamFlag struct { - Id int `json:"id"` + Id int `json:"id,omitempty"` order int8 `json:"order"` Label string `json:"label"` Type string `json:"type,omitempty"` @@ -50,6 +50,7 @@ type myTeamFlag struct { Justify bool `json:"justify,omitempty"` Choices map[string]interface{} `json:"choices,omitempty"` ChoicesCost int64 `json:"choices_cost,omitempty"` + Variant string `json:"variant,omitempty"` Min *float64 `json:"min,omitempty"` Max *float64 `json:"max,omitempty"` Step *float64 `json:"step,omitempty"` @@ -142,10 +143,8 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { } else { exercice.Tries, stime = t.CountTries(e) exercice.SolvedTime = stime - if exercice.Tries > 0 { - if DisplayMCQBadCount { - exercice.SolveDist = t.LastTryDist(e) - } + if DisplayMCQBadCount && exercice.Tries > 0 { + exercice.SolveDist = t.LastTryDist(e) } } @@ -194,6 +193,23 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { justifiedMCQ := map[int]myTeamFlag{} + if labels, err := e.GetFlagLabels(); err != nil { + return nil, err + } else { + for _, l := range labels { + if !DisplayAllFlags && t != nil && !t.CanSeeFlag(l) { + // Dependancy missing, skip the flag for now + continue + } + + exercice.Flags = append(exercice.Flags, myTeamFlag{ + order: l.Order, + Label: l.Label, + Variant: l.Variant, + }) + } + } + if flags, err := e.GetFlagKeys(); err != nil { return nil, err } else { @@ -257,7 +273,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) { flag.Multiline = k.Multiline - var fl FlagLabel + var fl FlagMCQLabel if fl, err = k.GetMCQJustification(); err == nil { k.Label = fl.Label }