New option to allow teams to self reset their progression
This commit is contained in:
parent
a0c34018cf
commit
d6ff46ca7f
@ -182,6 +182,13 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="config.canResetProgress" ng-disabled="!config.wip">
|
||||
<span class="custom-control-label" ng-class="{'text-primary font-weight-bold': !config.canResetProgress != !dist_config.canResetProgress}">Autoriser les équipes à auto-effacer leur progression</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<label class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" ng-model="config.enableExerciceDepend" ng-change="exerciceDependChange()">
|
||||
|
@ -71,6 +71,7 @@ func reloadSettings(config *settings.Settings) {
|
||||
allowRegistration = config.AllowRegistration
|
||||
canJoinTeam = config.CanJoinTeam
|
||||
denyTeamCreation = config.DenyTeamCreation
|
||||
canResetProgression = config.WorkInProgress && config.CanResetProgression
|
||||
fic.HintCoefficient = config.HintCurCoefficient
|
||||
fic.WChoiceCoefficient = config.WChoiceCurCoefficient
|
||||
fic.ExerciceCurrentCoefficient = config.ExerciceCurCoefficient
|
||||
@ -211,6 +212,8 @@ func treat(raw_path string) {
|
||||
treatOpeningHint(raw_path, team)
|
||||
case "choices":
|
||||
treatWantChoices(raw_path, team)
|
||||
case "reset_progress":
|
||||
treatResetProgress(raw_path, team)
|
||||
case ".locked":
|
||||
treatLocked(raw_path, team)
|
||||
default:
|
||||
|
54
checker/reset_progress.go
Normal file
54
checker/reset_progress.go
Normal file
@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
var canResetProgression = false
|
||||
|
||||
type askResetProgress struct {
|
||||
ExerciceId int64 `json:"eid"`
|
||||
}
|
||||
|
||||
func treatResetProgress(pathname string, team *fic.Team) {
|
||||
if !canResetProgression {
|
||||
log.Printf("[!!!] Receive reset_progress, whereas desactivated in settings.\n")
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a unique identifier to follow the request in logs
|
||||
bid := make([]byte, 5)
|
||||
binary.LittleEndian.PutUint32(bid, rand.Uint32())
|
||||
id := "[" + base64.StdEncoding.EncodeToString(bid) + "]"
|
||||
log.Println(id, "New ResetProgress receive", pathname)
|
||||
|
||||
var ask askResetProgress
|
||||
|
||||
if cnt_raw, err := ioutil.ReadFile(pathname); err != nil {
|
||||
log.Printf("%s [ERR] %s\n", id, err)
|
||||
} else if err = json.Unmarshal(cnt_raw, &ask); err != nil {
|
||||
log.Printf("%s [ERR] %s\n", id, err)
|
||||
} else if ask.ExerciceId == 0 {
|
||||
log.Printf("%s [WRN] Invalid content in reset_progress file: %s\n", id, pathname)
|
||||
os.Remove(pathname)
|
||||
} else if exercice, err := fic.GetExercice(ask.ExerciceId); err != nil {
|
||||
log.Printf("%s [ERR] Unable to retrieve the given exercice: %s\n", id, err)
|
||||
} else if exercice.Disabled {
|
||||
log.Println("[!!!] The team submits something for a disabled exercice")
|
||||
} else if err := team.ResetProgressionOnExercice(exercice); err != nil {
|
||||
log.Printf("%s [ERR] Unable to reset progression: %s\n", id, err)
|
||||
} else {
|
||||
appendGenQueue(fic.GenStruct{Id: id, Type: fic.GenTeam, TeamId: team.Id})
|
||||
if err = os.Remove(pathname); err != nil {
|
||||
log.Printf("%s [ERR] %s\n", id, err)
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
Col,
|
||||
Icon,
|
||||
Row,
|
||||
Spinner,
|
||||
} from 'sveltestrap';
|
||||
|
||||
import ExerciceDownloads from '$lib/components/ExerciceDownloads.svelte';
|
||||
@ -24,8 +25,29 @@
|
||||
import { current_theme } from '$lib/stores/themes';
|
||||
import { settings } from '$lib/stores/settings';
|
||||
|
||||
import { waitDiff, waitInProgress } from '$lib/wait.js';
|
||||
|
||||
let solved = {};
|
||||
let openResolution = false;
|
||||
|
||||
async function askProgressReset() {
|
||||
waitInProgress.set(true);
|
||||
const response = await fetch(
|
||||
"reset_progress",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {'Accept': 'application/json'},
|
||||
body: JSON.stringify({"eid": $current_exercice.id}),
|
||||
}
|
||||
);
|
||||
if (response.status < 300) {
|
||||
waitDiff(13, $my.exercices[$current_exercice.id]);
|
||||
} else {
|
||||
data = await response.json();
|
||||
waitInProgress.set(false);
|
||||
alert("Quelque chose s'est mal passé : " + data.errmsg);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $current_exercice}
|
||||
@ -162,19 +184,29 @@
|
||||
</Row>
|
||||
</Col>
|
||||
{#if $my && $my.team_id}
|
||||
<Col xs="auto">
|
||||
<Col xs="auto" class="d-flex flex-column justify-content-between">
|
||||
{#if $settings.acceptNewIssue}
|
||||
<a href="issues/?eid={$current_exercice.id}" class="float-end btn btn-sm btn-warning">
|
||||
<a href="issues/?eid={$current_exercice.id}" class="btn btn-sm btn-warning">
|
||||
<Icon name="bug" />
|
||||
Rapporter une anomalie sur ce défi
|
||||
</a>
|
||||
{/if}
|
||||
{#if $settings.QAenabled}
|
||||
<a href="qa/exercices/{$current_exercice.id}" class="float-end btn btn-sm btn-info" target="_self">
|
||||
<a href="qa/exercices/{$current_exercice.id}" class="btn btn-sm btn-info" target="_self">
|
||||
<Icon name="bug" />
|
||||
Voir les éléments QA sur ce défi
|
||||
</a>
|
||||
{/if}
|
||||
{#if $settings.wip && $settings.canResetProgress}
|
||||
<Button size="sm" color="dark" on:click={askProgressReset} disabled={!$my.exercices[$current_exercice.id].solved_time || $waitInProgress}>
|
||||
{#if $waitInProgress}
|
||||
<Spinner size="sm" />
|
||||
{:else}
|
||||
<Icon name="skip-start-fill" />
|
||||
{/if}
|
||||
Effacer ma progression sur ce défi
|
||||
</Button>
|
||||
{/if}
|
||||
</Col>
|
||||
{/if}
|
||||
</Row>
|
||||
|
@ -139,3 +139,47 @@ func (t *Team) DelHistoryItem(kind string, h time.Time, primary *int64, secondar
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ResetProgressionOnExercice removes all tries and validations for a given Exercice.
|
||||
func (t *Team) ResetProgressionOnExercice(exercice *Exercice) error {
|
||||
hints, err := exercice.GetHints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flags, err := exercice.GetFlags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := DBExec("DELETE FROM exercice_tries WHERE id_team = ? AND id_exercice = ?", t.Id, exercice.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := DBExec("DELETE FROM exercice_solved WHERE id_team = ? AND id_exercice = ?", t.Id, exercice.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, hint := range hints {
|
||||
if _, err := DBExec("DELETE FROM team_hints WHERE id_team = ? AND id_hint = ?", t.Id, hint.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, flag := range flags {
|
||||
if k, ok := flag.(*FlagKey); ok {
|
||||
if _, err := DBExec("DELETE FROM team_wchoices WHERE id_team = ? AND id_flag = ?", t.Id, k.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := DBExec("DELETE FROM flag_found WHERE id_team = ? AND id_flag = ?", t.Id, k.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if mcq, ok := flag.(*MCQ); ok {
|
||||
if _, err := DBExec("DELETE FROM mcq_found WHERE id_team = ? AND id_mcq = ?", t.Id, mcq.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ func main() {
|
||||
http.Handle(fmt.Sprintf("%s/wantchoices/", *prefix), http.StripPrefix(fmt.Sprintf("%s/wantchoices/", *prefix), submissionTeamChecker{"wantint choices", WantChoicesHandler, *teamsDir, *simulator}))
|
||||
http.Handle(fmt.Sprintf("%s/registration", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration", *prefix), submissionChecker{"registration", RegistrationHandler}))
|
||||
http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{}))
|
||||
http.Handle(fmt.Sprintf("%s/reset_progress", *prefix), http.StripPrefix(fmt.Sprintf("%s/reset_progress", *prefix), submissionTeamChecker{"reset_progress", ResetProgressHandler, *teamsDir, *simulator}))
|
||||
http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), submissionTeamChecker{"submission", SubmissionHandler, *teamsDir, *simulator}))
|
||||
|
||||
if *simulator != "" {
|
||||
|
26
receiver/reset.go
Normal file
26
receiver/reset.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
var enableResetProgression = false
|
||||
|
||||
func ResetProgressHandler(w http.ResponseWriter, r *http.Request, team string, sURL []string) {
|
||||
if !enableResetProgression {
|
||||
http.Error(w, "{\"errmsg\":\"Le challenge est terminé, trop tard !\"}", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
if challengeEnd != nil && time.Now().After(*challengeEnd) {
|
||||
http.Error(w, "{\"errmsg\":\"Le challenge est terminé, trop tard !\"}", http.StatusGone)
|
||||
return
|
||||
}
|
||||
|
||||
// Enqueue file for backend treatment
|
||||
if saveTeamFile(path.Join(team, "reset_progress"), w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Demande acceptée...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
@ -64,4 +64,5 @@ func reloadSettings(config *settings.Settings) {
|
||||
denyNameChange = config.DenyNameChange
|
||||
acceptNewIssues = config.AcceptNewIssue
|
||||
allowRegistration = config.AllowRegistration
|
||||
enableResetProgression = config.WorkInProgress && config.CanResetProgression
|
||||
}
|
||||
|
@ -61,6 +61,8 @@ type Settings struct {
|
||||
AcceptNewIssue bool `json:"acceptNewIssue,omitempty"`
|
||||
// QAenabled enables links to QA interface.
|
||||
QAenabled bool `json:"QAenabled,omitempty"`
|
||||
// CanResetProgression allows a team to reset the progress it made on a given exercice.
|
||||
CanResetProgression bool `json:"canResetProgress,omitempty"`
|
||||
// EnableResolutionRoute activates the route displaying resolution movies.
|
||||
EnableResolutionRoute bool `json:"enableResolutionRoute,omitempty"`
|
||||
// PartialValidation validates each correct given answers, don't expect Team to give all correct answer in a try.
|
||||
|
Loading…
x
Reference in New Issue
Block a user