Handle optionnal flags

This commit is contained in:
nemunaire 2022-05-31 22:03:51 +02:00
parent e581630d5e
commit a414cd22c8
18 changed files with 68 additions and 22 deletions

View file

@ -735,7 +735,8 @@ type uploadedFlag struct {
SortReGroups bool `json:"sort_re_grps"` SortReGroups bool `json:"sort_re_grps"`
Flag string Flag string
Value []byte Value []byte
ChoicesCost int64 `json:"choices_cost"` ChoicesCost int32 `json:"choices_cost"`
BonusGain int32 `json:"bonus_gain"`
} }
func createExerciceFlag(c *gin.Context) { func createExerciceFlag(c *gin.Context) {
@ -758,7 +759,7 @@ func createExerciceFlag(c *gin.Context) {
exercice := c.MustGet("exercice").(*fic.Exercice) exercice := c.MustGet("exercice").(*fic.Exercice)
flag, err := exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.NoTrim, uk.Multiline, vre, uk.SortReGroups, []byte(uk.Flag), uk.ChoicesCost) flag, err := exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.NoTrim, uk.Multiline, vre, uk.SortReGroups, []byte(uk.Flag), uk.ChoicesCost, uk.BonusGain)
if err != nil { if err != nil {
log.Println("Unable to createExerciceFlag:", err.Error()) log.Println("Unable to createExerciceFlag:", err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to create flag."}) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to create flag."})
@ -839,6 +840,7 @@ func updateExerciceFlag(c *gin.Context) {
flag.Checksum = uk.Value flag.Checksum = uk.Value
} }
flag.ChoicesCost = uk.ChoicesCost flag.ChoicesCost = uk.ChoicesCost
flag.BonusGain = uk.BonusGain
if uk.ValidatorRe != nil && len(*uk.ValidatorRe) > 0 { if uk.ValidatorRe != nil && len(*uk.ValidatorRe) > 0 {
flag.ValidatorRegexp = uk.ValidatorRe flag.ValidatorRegexp = uk.ValidatorRe

View file

@ -62,6 +62,9 @@
<div class="col-2"> <div class="col-2">
<input type="text" id="kccost{{flag.id}}" ng-model="flag.choices_cost" class="form-control form-control-sm" placeholder="Choices cost" title="Choices cost" integer> <input type="text" id="kccost{{flag.id}}" ng-model="flag.choices_cost" class="form-control form-control-sm" placeholder="Choices cost" title="Choices cost" integer>
</div> </div>
<div class="col-2">
<input type="text" id="kbgain{{flag.id}}" ng-model="flag.bonus_gain" class="form-control form-control-sm" placeholder="Bonus gain" title="Bonus gain" integer>
</div>
<div class="col-2"> <div class="col-2">
<input type="text" id="korder{{flag.id}}" ng-model="flag.order" class="form-control form-control-sm" placeholder="Order" title="Order" integer> <input type="text" id="korder{{flag.id}}" ng-model="flag.order" class="form-control form-control-sm" placeholder="Order" title="Order" integer>
</div> </div>

View file

@ -13,7 +13,7 @@
<th>Date</th> <th>Date</th>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="row in scores | filter: query | orderBy:'time'" ng-class="{'bg-ffound': row.reason == 'First blood', 'bg-wchoices': row.reason == 'Display choices', 'bg-success': row.reason == 'Validation', 'bg-info': row.reason == 'Hint', 'bg-warning': row.reason == 'Tries'}"> <tr ng-repeat="row in scores | filter: query | orderBy:'time'" ng-class="{'bg-danger': row.reason == 'Bonus flag', 'bg-ffound': row.reason == 'First blood', 'bg-wchoices': row.reason == 'Display choices', 'bg-success': row.reason == 'Validation', 'bg-info': row.reason == 'Hint', 'bg-warning': row.reason == 'Tries'}">
<td> <td>
<a ng-repeat="exercice in exercices" ng-if="exercice.id == row.id_exercice" href="exercices/{{ row.id_exercice }}">{{ exercice.title }}</a> <a ng-repeat="exercice in exercices" ng-if="exercice.id == row.id_exercice" href="exercices/{{ row.id_exercice }}">{{ exercice.title }}</a>
</td> </td>

View file

@ -43,7 +43,8 @@ type ExerciceFlag struct {
SortReGroups bool `toml:"sort_validator_regexp_groups,omitempty"` SortReGroups bool `toml:"sort_validator_regexp_groups,omitempty"`
Placeholder string `toml:",omitempty"` Placeholder string `toml:",omitempty"`
Help string `toml:",omitempty"` Help string `toml:",omitempty"`
ChoicesCost int64 `toml:"choices_cost,omitempty"` BonusGain int32 `toml:"bonus_gain,omitempty"`
ChoicesCost int32 `toml:"choices_cost,omitempty"`
Choice []ExerciceFlagChoice Choice []ExerciceFlagChoice
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"` LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"` NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"`

View file

@ -170,6 +170,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
SortReGroups: flag.SortReGroups, SortReGroups: flag.SortReGroups,
Checksum: hashedFlag[:], Checksum: hashedFlag[:],
ChoicesCost: flag.ChoicesCost, ChoicesCost: flag.ChoicesCost,
BonusGain: flag.BonusGain,
}) })
f = &fl f = &fl

View file

@ -82,8 +82,7 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
// Ensure the team didn't already solve this exercice // Ensure the team didn't already solve this exercice
tm := team.HasSolved(exercice) tm := team.HasSolved(exercice)
if tm != nil { if tm != nil {
log.Printf("%s [WRN] Team %d ALREADY solved exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title) log.Printf("%s [WRN] Team %d ALREADY solved exercice %d (%s : %s), continuing for eventual bonus flags\n", id, team.Id, exercice.Id, theme.Name, exercice.Title)
return
} }
// Handle MCQ justifications: convert to expected keyid // Handle MCQ justifications: convert to expected keyid
@ -119,7 +118,9 @@ func treatSubmission(pathname string, team *fic.Team, exercice_id string) {
log.Println(id, "[ERR] Can't remove file:", err) log.Println(id, "[ERR] Can't remove file:", err)
} }
if solved { if tm != nil {
genTeamQueue <- team
} else if solved {
log.Printf("%s Team %d SOLVED exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title) log.Printf("%s Team %d SOLVED exercice %d (%s : %s)\n", id, team.Id, exercice.Id, theme.Name, exercice.Title)
if err := exercice.Solved(team); err != nil { if err := exercice.Solved(team); err != nil {
log.Println(id, "[ERR] Unable to mark the challenge as solved:", err) log.Println(id, "[ERR] Unable to mark the challenge as solved:", err)

View file

@ -100,6 +100,11 @@
</script> </script>
<div class="form-group mb-3"> <div class="form-group mb-3">
{#if flag.bonus_gain}
<div class={'float-end badge bg-' + (flag.found?'success':'danger')} title={'Ce flag est optionnel, si vous le complétez il vous rapportera ' + flag.bonus_gain + ' points supplémentaires'}>
optionnel | {#if flag.bonus_gain > 0}+{/if}{flag.bonus_gain}&nbsp;pts
</div>
{/if}
<label for="sol_{flag.type}{flag.id}_0">{flag.label}&nbsp;:</label> <label for="sol_{flag.type}{flag.id}_0">{flag.label}&nbsp;:</label>
{#if flag.found && flag.value} {#if flag.found && flag.value}
<span>{flag.value}</span> <span>{flag.value}</span>

View file

@ -161,7 +161,7 @@
{/if} {/if}
</Col> </Col>
<Col lg="6" class="mb-5"> <Col lg="6" class="mb-5">
{#if !$my.exercices[exercice.id].solved_rank && !solved[exercice.id]} {#if $my.exercices[exercice.id].flags && $my.exercices[exercice.id].non_found_flags > 0 && !solved[exercice.id]}
<ExerciceFlags <ExerciceFlags
{refresh_my} {refresh_my}
{refresh_teams} {refresh_teams}
@ -169,7 +169,8 @@
bind:forcesolved={solved[exercice.id]} bind:forcesolved={solved[exercice.id]}
flags={$my.exercices[exercice.id].flags} flags={$my.exercices[exercice.id].flags}
/> />
{:else} {/if}
{#if $my.exercices[exercice.id].solved_rank || solved[exercice.id]}
<ExerciceSolved <ExerciceSolved
{theme} {theme}
exercice={$my.exercices[exercice.id]} exercice={$my.exercices[exercice.id]}

View file

@ -11,6 +11,15 @@ function createMyStore() {
for (let k in my.exercices) { for (let k in my.exercices) {
my.exercices[k].id = k; my.exercices[k].id = k;
if (my.exercices[k].flags) {
let nb = 0;
for (let j in my.exercices[k].flags) {
if (!my.exercices[k].flags[j].found)
nb += 1;
}
my.exercices[k].non_found_flags = nb;
}
if (my.team_id === 0 && my.exercices[k].hints) { if (my.team_id === 0 && my.exercices[k].hints) {
for (let j in my.exercices[k].hints) { for (let j in my.exercices[k].hints) {
my.exercices[k].hints[j].hidden = true; my.exercices[k].hints[j].hidden = true;

View file

@ -195,7 +195,8 @@ CREATE TABLE IF NOT EXISTS exercice_flags(
validator_regexp VARCHAR(255) NULL, validator_regexp VARCHAR(255) NULL,
sort_re_grps BOOLEAN NOT NULL DEFAULT 0, sort_re_grps BOOLEAN NOT NULL DEFAULT 0,
cksum BINARY(64) NOT NULL, cksum BINARY(64) NOT NULL,
choices_cost INTEGER NOT NULL, choices_cost MEDIUMINT NOT NULL,
bonus_gain MEDIUMINT NOT NULL,
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice) FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil { `); err != nil {

View file

@ -471,10 +471,10 @@ func (e *Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
// Check flags // Check flags
for _, flag := range flags { for _, flag := range flags {
if res, ok := respflags[flag.Id]; !ok && (!PartialValidation || t.HasPartiallySolved(flag) == nil) { if res, ok := respflags[flag.Id]; !ok && (!PartialValidation || t.HasPartiallySolved(flag) == nil) {
valid = false valid = valid && flag.IsOptionnal()
} else if flag.Check([]byte(res)) != 0 { } else if flag.Check([]byte(res)) != 0 {
if !PartialValidation || t.HasPartiallySolved(flag) == nil { if !PartialValidation || t.HasPartiallySolved(flag) == nil {
valid = false valid = valid && flag.IsOptionnal()
} }
} else { } else {
flag.FoundBy(t) flag.FoundBy(t)

View file

@ -338,7 +338,7 @@ func (f *EFile) GetDepends() ([]Flag, error) {
if err := rows.Scan(&d); err != nil { if err := rows.Scan(&d); err != nil {
return nil, err return nil, err
} }
deps = append(deps, &FlagKey{d, f.IdExercice, 0, "", "", "", "", "", false, false, false, nil, false, []byte{}, 0}) deps = append(deps, &FlagKey{d, f.IdExercice, 0, "", "", "", "", "", false, false, false, nil, false, []byte{}, 0, 0})
} }
if err := rows.Err(); err != nil { if err := rows.Err(); err != nil {
return nil, err return nil, err

View file

@ -12,6 +12,7 @@ type Flag interface {
GetDepends() ([]Flag, error) GetDepends() ([]Flag, error)
GetOrder() int8 GetOrder() int8
Check(val interface{}) int Check(val interface{}) int
IsOptionnal() bool
FoundBy(t *Team) FoundBy(t *Team)
} }

View file

@ -42,12 +42,14 @@ type FlagKey struct {
// Checksum is the expected hashed flag // Checksum is the expected hashed flag
Checksum []byte `json:"value"` Checksum []byte `json:"value"`
// ChoicesCost is the number of points lost to display choices. // ChoicesCost is the number of points lost to display choices.
ChoicesCost int64 `json:"choices_cost"` ChoicesCost int32 `json:"choices_cost"`
// BonusGain makes the flag completion optionnal. If it is filled and correct, it gives some points.
BonusGain int32 `json:"bonus_gain"`
} }
// GetFlagKeys returns a list of key's flags comming with the challenge. // GetFlagKeys returns a list of key's flags comming with the challenge.
func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) { func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) {
if rows, err := DBQuery("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil { if rows, err := DBQuery("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost, bonus_gain FROM exercice_flags WHERE id_exercice = ?", e.Id); err != nil {
return nil, err return nil, err
} else { } else {
defer rows.Close() defer rows.Close()
@ -57,7 +59,7 @@ func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) {
k := &FlagKey{} k := &FlagKey{}
k.IdExercice = e.Id k.IdExercice = e.Id
if err := rows.Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost); err != nil { if err := rows.Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost, &k.BonusGain); err != nil {
return nil, err return nil, err
} }
@ -74,21 +76,21 @@ func (e *Exercice) GetFlagKeys() ([]*FlagKey, error) {
// GetFlagKey returns a list of flags comming with the challenge. // GetFlagKey returns a list of flags comming with the challenge.
func GetFlagKey(id int) (k *FlagKey, err error) { func GetFlagKey(id int) (k *FlagKey, err error) {
k = &FlagKey{} k = &FlagKey{}
err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.Multiline, &k.NoTrim, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost) err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost, bonus_gain FROM exercice_flags WHERE id_flag = ?", id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.Multiline, &k.NoTrim, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost, &k.BonusGain)
return return
} }
// GetFlagKey returns a flag. // GetFlagKey returns a flag.
func (e *Exercice) GetFlagKey(id int) (k *FlagKey, err error) { func (e *Exercice) GetFlagKey(id int) (k *FlagKey, err error) {
k = &FlagKey{} k = &FlagKey{}
err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE id_flag = ? AND id_exercice = ?", id, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost) err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost, bonus_gain FROM exercice_flags WHERE id_flag = ? AND id_exercice = ?", id, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost, &k.BonusGain)
return return
} }
// GetFlagKeyByLabel returns a flag matching the given label. // GetFlagKeyByLabel returns a flag matching the given label.
func (e *Exercice) GetFlagKeyByLabel(label string) (k *FlagKey, err error) { func (e *Exercice) GetFlagKeyByLabel(label string) (k *FlagKey, err error) {
k = &FlagKey{} k = &FlagKey{}
err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost) err = DBQueryRow("SELECT id_flag, id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost, bonus_gain FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Order, &k.Label, &k.Type, &k.Placeholder, &k.Help, &k.Unit, &k.IgnoreCase, &k.NoTrim, &k.Multiline, &k.ValidatorRegexp, &k.SortReGroups, &k.Checksum, &k.ChoicesCost, &k.BonusGain)
return return
} }
@ -139,7 +141,7 @@ func ExecValidatorRegexp(vre string, val []byte, ignorecase bool, sortregroups b
} }
// AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database. // AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database.
func (e *Exercice) AddRawFlagKey(name string, t string, placeholder string, ignorecase bool, multiline bool, notrim bool, validator_regexp *string, sortregroups bool, raw_value []byte, choicescost int64) (*FlagKey, error) { func (e *Exercice) AddRawFlagKey(name string, t string, placeholder string, ignorecase bool, multiline bool, notrim bool, validator_regexp *string, sortregroups bool, raw_value []byte, choicescost int32, bonusgain int32) (*FlagKey, error) {
hash, err := ComputeHashedFlag(raw_value, ignorecase, notrim, validator_regexp, sortregroups) hash, err := ComputeHashedFlag(raw_value, ignorecase, notrim, validator_regexp, sortregroups)
if err != nil { if err != nil {
return nil, err return nil, err
@ -156,6 +158,7 @@ func (e *Exercice) AddRawFlagKey(name string, t string, placeholder string, igno
SortReGroups: sortregroups, SortReGroups: sortregroups,
Checksum: hash[:], Checksum: hash[:],
ChoicesCost: choicescost, ChoicesCost: choicescost,
BonusGain: bonusgain,
} }
_, err = f.Create(e) _, err = f.Create(e)
@ -185,7 +188,7 @@ func (k *FlagKey) Create(e *Exercice) (Flag, error) {
} }
} }
if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.SortReGroups, k.Checksum, k.ChoicesCost); err != nil { if res, err := DBExec("INSERT INTO exercice_flags (id_exercice, ordre, label, type, placeholder, help, unit, ignorecase, notrim, multiline, validator_regexp, sort_re_grps, cksum, choices_cost, bonus_gain) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", e.Id, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.SortReGroups, k.Checksum, k.ChoicesCost, k.BonusGain); err != nil {
return k, err return k, err
} else if kid, err := res.LastInsertId(); err != nil { } else if kid, err := res.LastInsertId(); err != nil {
return k, err return k, err
@ -210,7 +213,7 @@ func (k *FlagKey) Update() (int64, error) {
} }
} }
if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, ordre = ?, label = ?, type = ?, placeholder = ?, help = ?, unit = ?, ignorecase = ?, notrim = ?, multiline = ?, validator_regexp = ?, sort_re_grps = ?, cksum = ?, choices_cost = ? WHERE id_flag = ?", k.IdExercice, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.SortReGroups, k.Checksum, k.ChoicesCost, k.Id); err != nil { if res, err := DBExec("UPDATE exercice_flags SET id_exercice = ?, ordre = ?, label = ?, type = ?, placeholder = ?, help = ?, unit = ?, ignorecase = ?, notrim = ?, multiline = ?, validator_regexp = ?, sort_re_grps = ?, cksum = ?, choices_cost = ?, bonus_gain = ? WHERE id_flag = ?", k.IdExercice, k.Order, k.Label, k.Type, k.Placeholder, k.Help, k.Unit, k.IgnoreCase, k.NoTrim, k.Multiline, k.ValidatorRegexp, k.SortReGroups, k.Checksum, k.ChoicesCost, k.BonusGain, k.Id); err != nil {
return 0, err return 0, err
} else if nb, err := res.RowsAffected(); err != nil { } else if nb, err := res.RowsAffected(); err != nil {
return 0, err return 0, err
@ -299,6 +302,11 @@ func (k *FlagKey) GetDepends() ([]Flag, error) {
return deps, nil return deps, nil
} }
// IsOptionnal to know if the flag can be omitted when validating the step.
func (k *FlagKey) IsOptionnal() bool {
return k.BonusGain != 0
}
// Check if the given val is the expected one for this flag. // Check if the given val is the expected one for this flag.
func (k *FlagKey) Check(v interface{}) int { func (k *FlagKey) Check(v interface{}) int {
var val []byte var val []byte

View file

@ -167,6 +167,11 @@ func (k *FlagLabel) GetDepends() ([]Flag, error) {
return deps, nil return deps, nil
} }
// IsOptionnal to know if the flag can be omitted when validating the step.
func (k *FlagLabel) IsOptionnal() bool {
return true
}
// Check if the given val is the expected one for this flag. // Check if the given val is the expected one for this flag.
func (k *FlagLabel) Check(v interface{}) int { func (k *FlagLabel) Check(v interface{}) int {
return 0 return 0

View file

@ -314,6 +314,11 @@ func (c *MCQ_entry) GetJustifiedFlag(e *Exercice) (*FlagKey, error) {
return e.GetFlagKeyByLabel(fmt.Sprintf("\\%%%d\\%%%%", c.Id)) return e.GetFlagKeyByLabel(fmt.Sprintf("\\%%%d\\%%%%", c.Id))
} }
// IsOptionnal to know if the flag can be omitted when validating the step.
func (m *MCQ) IsOptionnal() bool {
return false
}
// Check if the given vals are the expected ones to validate this flag. // Check if the given vals are the expected ones to validate this flag.
func (m *MCQ) Check(v interface{}) int { func (m *MCQ) Check(v interface{}) int {
var vals map[int]bool var vals map[int]bool

View file

@ -32,6 +32,7 @@ func exoptsQuery(whereExo string) string {
SELECT id_team, id_exercice, MIN(time) AS time, ` + fmt.Sprintf("%f", FirstBlood) + ` AS coeff, "First blood" AS reason FROM exercice_solved GROUP BY id_exercice UNION SELECT id_team, id_exercice, MIN(time) AS time, ` + fmt.Sprintf("%f", FirstBlood) + ` AS coeff, "First blood" AS reason FROM exercice_solved GROUP BY id_exercice UNION
SELECT id_team, id_exercice, time, coefficient AS coeff, "Validation" AS reason FROM exercice_solved SELECT id_team, id_exercice, time, coefficient AS coeff, "Validation" AS reason FROM exercice_solved
) S INNER JOIN exercices E ON S.id_exercice = E.id_exercice ` + whereExo + ` UNION ALL ) S INNER JOIN exercices E ON S.id_exercice = E.id_exercice ` + whereExo + ` UNION ALL
SELECT B.id_team, B.time, F.bonus_gain AS points, 1 AS coeff, "Bonus flag" AS reason, F.id_exercice FROM flag_found B INNER JOIN exercice_flags F ON F.id_flag = B.id_flag WHERE F.bonus_gain != 0 HAVING points != 0 UNION ALL
SELECT id_team, MAX(time) AS time, (FLOOR(COUNT(*)/10 - 1) * (FLOOR(COUNT(*)/10)))/0.2 + (FLOOR(COUNT(*)/10) * (COUNT(*)%10)) AS points, ` + fmt.Sprintf("%f", SubmissionCostBase*-1) + ` AS coeff, "Tries" AS reason, id_exercice FROM ` + tries_table + ` S ` + whereExo + ` GROUP BY id_exercice, id_team` SELECT id_team, MAX(time) AS time, (FLOOR(COUNT(*)/10 - 1) * (FLOOR(COUNT(*)/10)))/0.2 + (FLOOR(COUNT(*)/10) * (COUNT(*)%10)) AS points, ` + fmt.Sprintf("%f", SubmissionCostBase*-1) + ` AS coeff, "Tries" AS reason, id_exercice FROM ` + tries_table + ` S ` + whereExo + ` GROUP BY id_exercice, id_team`
} }

View file

@ -48,6 +48,7 @@ type myTeamFlag struct {
PSolved *time.Time `json:"part_solved,omitempty"` PSolved *time.Time `json:"part_solved,omitempty"`
Soluce string `json:"soluce,omitempty"` Soluce string `json:"soluce,omitempty"`
Justify bool `json:"justify,omitempty"` Justify bool `json:"justify,omitempty"`
BonusGain int64 `json:"bonus_gain,omitempty"`
Choices map[string]interface{} `json:"choices,omitempty"` Choices map[string]interface{} `json:"choices,omitempty"`
ChoicesCost int64 `json:"choices_cost,omitempty"` ChoicesCost int64 `json:"choices_cost,omitempty"`
Variant string `json:"variant,omitempty"` Variant string `json:"variant,omitempty"`
@ -275,6 +276,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
} }
flag.Multiline = k.Multiline flag.Multiline = k.Multiline
flag.BonusGain = int64(float64(k.BonusGain) * GlobalScoreCoefficient)
var fl FlagMCQLabel var fl FlagMCQLabel
if fl, err = k.GetMCQJustification(); err == nil { if fl, err = k.GetMCQJustification(); err == nil {