New settings to only count bad submissions

This commit is contained in:
nemunaire 2021-09-08 03:34:48 +02:00
parent b887288c78
commit cd73622cae
9 changed files with 61 additions and 18 deletions

View file

@ -62,6 +62,7 @@ func ApplySettings(config settings.FICSettings) {
fic.WChoiceCoefficient = config.WChoiceCurCoefficient fic.WChoiceCoefficient = config.WChoiceCurCoefficient
fic.SubmissionCostBase = config.SubmissionCostBase fic.SubmissionCostBase = config.SubmissionCostBase
fic.SubmissionUniqueness = config.SubmissionUniqueness fic.SubmissionUniqueness = config.SubmissionUniqueness
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
} }
func ResetSettings() error { func ResetSettings() error {

View file

@ -200,6 +200,13 @@
</label> </label>
</div> </div>
<div class="form-check">
<label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" ng-model="config.countOnlyNotGoodTries">
<span class="custom-control-label">Comptabiliser seulement les tentatives sans bonne réponse dans le score</span>
</label>
</div>
<div class="form-check"> <div class="form-check">
<label class="custom-control custom-checkbox"> <label class="custom-control custom-checkbox">
<input class="custom-control-input" type="checkbox" ng-model="config.eventKindness"> <input class="custom-control-input" type="checkbox" ng-model="config.eventKindness">

View file

@ -83,6 +83,7 @@ func reloadSettings(config settings.FICSettings) {
fic.FirstBlood = config.FirstBlood fic.FirstBlood = config.FirstBlood
fic.SubmissionCostBase = config.SubmissionCostBase fic.SubmissionCostBase = config.SubmissionCostBase
fic.SubmissionUniqueness = config.SubmissionUniqueness fic.SubmissionUniqueness = config.SubmissionUniqueness
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
if !skipInitialGeneration { if !skipInitialGeneration {
log.Println("Generating files...") log.Println("Generating files...")

View file

@ -24,11 +24,14 @@
function waitDiff(i) { function waitDiff(i) {
refresh_my((my) => { refresh_my((my) => {
if (my && my.exercices[exercice.id].tries != exercice.tries) { if (my && my.exercices[exercice.id].solved_time != exercice.solved_time) {
submitInProgress = false; submitInProgress = false;
refresh_teams(); refresh_teams();
} else if (i > 0) { } else if (i > 0) {
setTimeout(waitDiff, 450, i-1); setTimeout(waitDiff, 450, i-1);
} else {
timeouted = true;
submitInProgress = false;
} }
}) })
} }
@ -110,11 +113,11 @@
{exercice.flags.length}/{exercice.nb_flags} {exercice.flags.length}/{exercice.nb_flags}
</Progress> </Progress>
{/if} {/if}
{#if exercice.tries || exercice.submitted || sberr} {#if exercice.tries || exercice.solved_time || exercice.submitted || sberr || timeouted}
<ListGroup class="border-dark"> <ListGroup class="border-dark">
{#if exercice.solved_time && exercice.tries} {#if exercice.solved_time || exercice.tries}
<ListGroupItem class="text-warning rounded-0"> <ListGroupItem class="text-warning rounded-0">
{exercice.tries} {exercice.tries==1?"tentative effectuée":"tentatives effectuées"}. {#if exercice.tries > 0}{exercice.tries} {exercice.tries==1?"tentative effectuée":"tentatives effectuées"}.{/if}
Dernière solution envoyée à <DateFormat date={exercice.solved_time} />. Dernière solution envoyée à <DateFormat date={exercice.solved_time} />.
</ListGroupItem> </ListGroupItem>
{/if} {/if}

View file

@ -353,6 +353,7 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
time TIMESTAMP NOT NULL, time TIMESTAMP NOT NULL,
cksum BINARY(64) NOT NULL, cksum BINARY(64) NOT NULL,
nbdiff INTEGER NOT NULL DEFAULT 0, nbdiff INTEGER NOT NULL DEFAULT 0,
onegood BOOLEAN NOT NULL DEFAULT 0,
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice), FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
FOREIGN KEY(id_team) REFERENCES teams(id_team) FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; ) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
@ -488,7 +489,13 @@ CREATE TABLE IF NOT EXISTS teams_qa_view(
return err return err
} }
if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff FROM exercice_tries GROUP BY id_team, id_exercice, cksum;"); err != nil { if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff, MAX(onegood) FROM exercice_tries GROUP BY id_team, id_exercice, cksum;"); err != nil {
return err
}
if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_tries_notgood AS SELECT id_exercice, id_team, time, cksum, nbdiff, onegood FROM exercice_tries WHERE onegood = 0;"); err != nil {
return err
}
if _, err := db.Exec("CREATE OR REPLACE VIEW exercice_distinct_tries_notgood AS SELECT id_exercice, id_team, MAX(time) AS time, cksum, nbdiff, MAX(onegood) FROM exercice_tries_notgood GROUP BY id_team, id_exercice, cksum;"); err != nil {
return err return err
} }

View file

@ -315,8 +315,8 @@ func (e Exercice) NewTry(t Team, cksum []byte) error {
// UpdateTry applies modifications to the latest try registered for the given Team. // UpdateTry applies modifications to the latest try registered for the given Team.
// Updated values are time and the given nbdiff. // Updated values are time and the given nbdiff.
func (e Exercice) UpdateTry(t Team, nbdiff int) error { func (e Exercice) UpdateTry(t Team, nbdiff int, oneGood bool) error {
if _, err := DBExec("UPDATE exercice_tries SET nbdiff = ?, time = ? WHERE id_exercice = ? AND id_team = ? ORDER BY time DESC LIMIT 1", nbdiff, time.Now(), e.Id, t.Id); err != nil { if _, err := DBExec("UPDATE exercice_tries SET nbdiff = ?, onegood = ?, time = ? WHERE id_exercice = ? AND id_team = ? ORDER BY time DESC LIMIT 1", nbdiff, oneGood, time.Now(), e.Id, t.Id); err != nil {
return err return err
} else { } else {
return nil return nil
@ -422,6 +422,7 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
} else { } else {
valid := true valid := true
diff := 0 diff := 0
goodResponses := 0
// Check MCQs // Check MCQs
for _, mcq := range mcqs { for _, mcq := range mcqs {
@ -432,6 +433,7 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
} }
} else if !PartialMCQValidation && d == 0 { } else if !PartialMCQValidation && d == 0 {
mcq.FoundBy(t) mcq.FoundBy(t)
goodResponses += 1
} }
} }
@ -439,6 +441,7 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
if valid { if valid {
for _, mcq := range mcqs { for _, mcq := range mcqs {
mcq.FoundBy(t) mcq.FoundBy(t)
goodResponses += 1
} }
} }
@ -452,11 +455,12 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
} }
} else { } else {
flag.FoundBy(t) flag.FoundBy(t)
goodResponses += 1
} }
} }
if diff > 0 { if diff > 0 || goodResponses > 0 {
e.UpdateTry(t, diff) e.UpdateTry(t, diff, goodResponses > 0)
} }
return valid, nil return valid, nil

View file

@ -15,12 +15,19 @@ var SubmissionCostBase = 0.5
// SubmissionUniqueness don't count multiple times identical tries. // SubmissionUniqueness don't count multiple times identical tries.
var SubmissionUniqueness = false var SubmissionUniqueness = false
// CountOnlyNotGoodTries don't count as a try when one good response is given at least.
var CountOnlyNotGoodTries = false
func exoptsQuery(whereExo string) string { func exoptsQuery(whereExo string) string {
tries_table := "exercice_tries" tries_table := "exercice_tries"
if SubmissionUniqueness { if SubmissionUniqueness {
tries_table = "exercice_distinct_tries" tries_table = "exercice_distinct_tries"
} }
if CountOnlyNotGoodTries {
tries_table += "_notgood"
}
return `SELECT S.id_team, S.time, E.gain AS points, coeff, S.reason, S.id_exercice FROM ( return `SELECT S.id_team, S.time, E.gain AS points, coeff, S.reason, S.id_exercice FROM (
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
@ -127,7 +134,6 @@ func GetRank() (map[int64]int, error) {
} }
} }
// Attempts // Attempts
// GetTries retrieves all attempts made by the matching Team or challenge (both can be nil to not filter). // GetTries retrieves all attempts made by the matching Team or challenge (both can be nil to not filter).

View file

@ -247,10 +247,22 @@ func (t Team) DisplayChoices(k FlagKey) error {
// CountTries gets the amount of attempts made by the Team and retrieves the time of the latest attempt. // CountTries gets the amount of attempts made by the Team and retrieves the time of the latest attempt.
func (t Team) CountTries(e Exercice) (int64, time.Time) { func (t Team) CountTries(e Exercice) (int64, time.Time) {
table := "exercice_tries"
if CountOnlyNotGoodTries {
table += "_notgood"
}
var nb *int64 var nb *int64
var tm *time.Time var tm *time.Time
if DBQueryRow("SELECT COUNT(id_exercice), MAX(time) FROM "+table+" WHERE id_team = ? AND id_exercice = ?", t.Id, e.Id).Scan(&nb, &tm); tm == nil {
if CountOnlyNotGoodTries {
if DBQueryRow("SELECT COUNT(id_exercice), MAX(time) FROM exercice_tries WHERE id_team = ? AND id_exercice = ?", t.Id, e.Id).Scan(&nb, &tm); tm == nil { if DBQueryRow("SELECT COUNT(id_exercice), MAX(time) FROM exercice_tries WHERE id_team = ? AND id_exercice = ?", t.Id, e.Id).Scan(&nb, &tm); tm == nil {
return 0, time.Unix(0, 0) return 0, time.Unix(0, 0)
} else {
return 0, *tm
}
}
return 0, time.Unix(0, 0)
} else if nb == nil { } else if nb == nil {
return 0, *tm return 0, *tm
} else { } else {

View file

@ -71,6 +71,8 @@ type FICSettings struct {
UnlockedChallengeDepth int `json:"unlockedChallengeDepth"` UnlockedChallengeDepth int `json:"unlockedChallengeDepth"`
// SubmissionUniqueness don't count multiple times identical tries. // SubmissionUniqueness don't count multiple times identical tries.
SubmissionUniqueness bool `json:"submissionUniqueness,omitempty"` SubmissionUniqueness bool `json:"submissionUniqueness,omitempty"`
// CountOnlyNotGoodTries don't count as a try when one good response is given at least.
CountOnlyNotGoodTries bool `json:"countOnlyNotGoodTries,omitempty"`
// DisplayAllFlags doesn't respect the predefined constraint existing between flags. // DisplayAllFlags doesn't respect the predefined constraint existing between flags.
DisplayAllFlags bool `json:"displayAllFlags,omitempty"` DisplayAllFlags bool `json:"displayAllFlags,omitempty"`
// EventKindness will ask browsers to delay notification interval. // EventKindness will ask browsers to delay notification interval.