New settings to only count bad submissions
This commit is contained in:
parent
b887288c78
commit
cd73622cae
|
@ -62,6 +62,7 @@ func ApplySettings(config settings.FICSettings) {
|
|||
fic.WChoiceCoefficient = config.WChoiceCurCoefficient
|
||||
fic.SubmissionCostBase = config.SubmissionCostBase
|
||||
fic.SubmissionUniqueness = config.SubmissionUniqueness
|
||||
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
|
||||
}
|
||||
|
||||
func ResetSettings() error {
|
||||
|
|
|
@ -200,6 +200,13 @@
|
|||
</label>
|
||||
</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">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="config.eventKindness">
|
||||
|
|
|
@ -83,6 +83,7 @@ func reloadSettings(config settings.FICSettings) {
|
|||
fic.FirstBlood = config.FirstBlood
|
||||
fic.SubmissionCostBase = config.SubmissionCostBase
|
||||
fic.SubmissionUniqueness = config.SubmissionUniqueness
|
||||
fic.CountOnlyNotGoodTries = config.CountOnlyNotGoodTries
|
||||
|
||||
if !skipInitialGeneration {
|
||||
log.Println("Generating files...")
|
||||
|
|
|
@ -24,11 +24,14 @@
|
|||
|
||||
function waitDiff(i) {
|
||||
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;
|
||||
refresh_teams();
|
||||
} else if (i > 0) {
|
||||
setTimeout(waitDiff, 450, i-1);
|
||||
} else {
|
||||
timeouted = true;
|
||||
submitInProgress = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -110,11 +113,11 @@
|
|||
{exercice.flags.length}/{exercice.nb_flags}
|
||||
</Progress>
|
||||
{/if}
|
||||
{#if exercice.tries || exercice.submitted || sberr}
|
||||
{#if exercice.tries || exercice.solved_time || exercice.submitted || sberr || timeouted}
|
||||
<ListGroup class="border-dark">
|
||||
{#if exercice.solved_time && exercice.tries}
|
||||
{#if exercice.solved_time || exercice.tries}
|
||||
<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} />.
|
||||
</ListGroupItem>
|
||||
{/if}
|
||||
|
|
|
@ -353,6 +353,7 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
|
|||
time TIMESTAMP NOT NULL,
|
||||
cksum BINARY(64) NOT NULL,
|
||||
nbdiff INTEGER NOT NULL DEFAULT 0,
|
||||
onegood BOOLEAN NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
|
||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
|
@ -488,7 +489,13 @@ CREATE TABLE IF NOT EXISTS teams_qa_view(
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
// Updated values are time and the given nbdiff.
|
||||
func (e Exercice) UpdateTry(t Team, nbdiff int) 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 {
|
||||
func (e Exercice) UpdateTry(t Team, nbdiff int, oneGood bool) error {
|
||||
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
|
||||
} else {
|
||||
return nil
|
||||
|
@ -422,6 +422,7 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
|
|||
} else {
|
||||
valid := true
|
||||
diff := 0
|
||||
goodResponses := 0
|
||||
|
||||
// Check 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 {
|
||||
mcq.FoundBy(t)
|
||||
goodResponses += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,6 +441,7 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
|
|||
if valid {
|
||||
for _, mcq := range mcqs {
|
||||
mcq.FoundBy(t)
|
||||
goodResponses += 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,11 +455,12 @@ func (e Exercice) CheckResponse(cksum []byte, respflags map[int]string, respmcq
|
|||
}
|
||||
} else {
|
||||
flag.FoundBy(t)
|
||||
goodResponses += 1
|
||||
}
|
||||
}
|
||||
|
||||
if diff > 0 {
|
||||
e.UpdateTry(t, diff)
|
||||
if diff > 0 || goodResponses > 0 {
|
||||
e.UpdateTry(t, diff, goodResponses > 0)
|
||||
}
|
||||
|
||||
return valid, nil
|
||||
|
|
|
@ -15,17 +15,24 @@ var SubmissionCostBase = 0.5
|
|||
// SubmissionUniqueness don't count multiple times identical tries.
|
||||
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 {
|
||||
tries_table := "exercice_tries"
|
||||
if SubmissionUniqueness {
|
||||
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 (
|
||||
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
|
||||
) S INNER JOIN exercices E ON S.id_exercice = E.id_exercice ` + whereExo + ` 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`
|
||||
) S INNER JOIN exercices E ON S.id_exercice = E.id_exercice ` + whereExo + ` 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`
|
||||
}
|
||||
|
||||
func teamptsQuery() string {
|
||||
|
@ -59,11 +66,11 @@ func (t Team) ScoreGrid() (grid []map[string]interface{}, err error) {
|
|||
}
|
||||
|
||||
grid = append(grid, map[string]interface{}{
|
||||
"reason": reason,
|
||||
"reason": reason,
|
||||
"id_exercice": exercice,
|
||||
"time": time,
|
||||
"points": points,
|
||||
"coeff": coeff,
|
||||
"time": time,
|
||||
"points": points,
|
||||
"coeff": coeff,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +82,7 @@ func (t Team) ScoreGrid() (grid []map[string]interface{}, err error) {
|
|||
// EstimateGain calculates the amount of point the Team has (or could have, if not already solved) won.
|
||||
func (e Exercice) EstimateGain(t Team, solved bool) (float64, error) {
|
||||
var pts float64
|
||||
err := DBQueryRow("SELECT SUM(A.points * A.coeff) AS score FROM (" + exoptsQuery("WHERE S.id_team = ? AND S.id_exercice = ?") + ") A GROUP BY id_team", t.Id, e.Id, t.Id, e.Id).Scan(&pts)
|
||||
err := DBQueryRow("SELECT SUM(A.points * A.coeff) AS score FROM ("+exoptsQuery("WHERE S.id_team = ? AND S.id_exercice = ?")+") A GROUP BY id_team", t.Id, e.Id, t.Id, e.Id).Scan(&pts)
|
||||
if solved {
|
||||
return pts, err
|
||||
} else {
|
||||
|
@ -127,7 +134,6 @@ func GetRank() (map[int64]int, error) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Attempts
|
||||
|
||||
// GetTries retrieves all attempts made by the matching Team or challenge (both can be nil to not filter).
|
||||
|
|
|
@ -247,9 +247,21 @@ 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.
|
||||
func (t Team) CountTries(e Exercice) (int64, time.Time) {
|
||||
table := "exercice_tries"
|
||||
if CountOnlyNotGoodTries {
|
||||
table += "_notgood"
|
||||
}
|
||||
|
||||
var nb *int64
|
||||
var tm *time.Time
|
||||
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 "+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 {
|
||||
return 0, time.Unix(0, 0)
|
||||
} else {
|
||||
return 0, *tm
|
||||
}
|
||||
}
|
||||
return 0, time.Unix(0, 0)
|
||||
} else if nb == nil {
|
||||
return 0, *tm
|
||||
|
|
|
@ -71,6 +71,8 @@ type FICSettings struct {
|
|||
UnlockedChallengeDepth int `json:"unlockedChallengeDepth"`
|
||||
// SubmissionUniqueness don't count multiple times identical tries.
|
||||
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 bool `json:"displayAllFlags,omitempty"`
|
||||
// EventKindness will ask browsers to delay notification interval.
|
||||
|
|
Loading…
Reference in New Issue