Add new helper string related to justified MCQ flag
This commit is contained in:
parent
11e0b46034
commit
c5b65289d3
@ -28,7 +28,8 @@ Tous les textes doivent utiliser l'encodage UTF8.
|
|||||||
* `label = "Intitulé du groupe"` : (facultatif) intitulé du groupe de choix ;
|
* `label = "Intitulé du groupe"` : (facultatif) intitulé du groupe de choix ;
|
||||||
* `[[flag_mcq.choice]]` : représente un choix, répétez autant de fois qu'il y a de choix :
|
* `[[flag_mcq.choice]]` : représente un choix, répétez autant de fois qu'il y a de choix :
|
||||||
+ `label = "Intitulé de la réponse"`,
|
+ `label = "Intitulé de la réponse"`,
|
||||||
+ `value = true` : (facultatif, par défaut `false`) valeur attendue pour ce choix ; pour un QCM justifié, utilisez une chaîne de caractères (notez qu'il n'est pas possible de combiner des réponses vraies justifiées et justifiées) ;
|
+ `value = true` : (facultatif, par défaut `false`) valeur attendue pour ce choix ; pour un QCM justifié, utilisez une chaîne de caractères (notez qu'il n'est pas possible de combiner des réponses vraies justifiées et justifiées),
|
||||||
|
+ `help = "Flag correspondant"` : (facultatif) indication affichée dans le champ de texte des QCM justifiés ;
|
||||||
- `[[flag_ucq]]` : drapeau sous forme de question à choix unique :
|
- `[[flag_ucq]]` : drapeau sous forme de question à choix unique :
|
||||||
* `label = "Intitulé du groupe"` : (facultatif) intitulé du groupe de choix ;
|
* `label = "Intitulé du groupe"` : (facultatif) intitulé du groupe de choix ;
|
||||||
* `raw = 'MieH2athxuPhai6u'` : drapeau attendu parmi les propositions ;
|
* `raw = 'MieH2athxuPhai6u'` : drapeau attendu parmi les propositions ;
|
||||||
@ -222,5 +223,5 @@ Exemple `DIGESTS.txt` {#exemple-digeststxt}
|
|||||||
---
|
---
|
||||||
title: Format des répertoires pour la synchronisation
|
title: Format des répertoires pour la synchronisation
|
||||||
author: FIC team 2019
|
author: FIC team 2019
|
||||||
date: "Dernière mise à jour du document : 27 novembre 2018"
|
date: "Dernière mise à jour du document : 1 décembre 2018"
|
||||||
---
|
---
|
||||||
|
@ -39,6 +39,7 @@ type ExerciceFlag struct {
|
|||||||
type ExerciceFlagMCQChoice struct {
|
type ExerciceFlagMCQChoice struct {
|
||||||
Label string
|
Label string
|
||||||
Value interface{} `toml:",omitempty"`
|
Value interface{} `toml:",omitempty"`
|
||||||
|
Help string `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExerciceFlagMCQ holds information about a MCQ flag.
|
// ExerciceFlagMCQ holds information about a MCQ flag.
|
||||||
|
@ -123,33 +123,44 @@ func SyncExerciceFlags(i Importer, exercice fic.Exercice) (errs []string) {
|
|||||||
isJustified := false
|
isJustified := false
|
||||||
for cid, choice := range quest.Choice {
|
for cid, choice := range quest.Choice {
|
||||||
var val bool
|
var val bool
|
||||||
|
var justify string
|
||||||
|
|
||||||
if choice.Value == nil {
|
if choice.Value == nil {
|
||||||
val = false
|
val = false
|
||||||
} else if p, ok := choice.Value.(bool); ok {
|
} else if p, ok := choice.Value.(bool); ok {
|
||||||
val = p
|
val = p
|
||||||
|
if len(choice.Help) > 0 {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: help field has to be used only with justified mcq.", path.Base(exercice.Path), nline + 1))
|
||||||
|
continue
|
||||||
|
}
|
||||||
if isJustified {
|
if isJustified {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline + 1))
|
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline + 1))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else if p, ok := choice.Value.(string); ok {
|
} else if p, ok := choice.Value.(string); ok {
|
||||||
val = true
|
val = true
|
||||||
|
if len(choice.Help) == 0 {
|
||||||
|
choice.Help = "Flag correspondant"
|
||||||
|
}
|
||||||
if hasOne && !isJustified {
|
if hasOne && !isJustified {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline + 1))
|
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline + 1))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
isJustified = true
|
isJustified = true
|
||||||
if _, err := exercice.AddRawFlag("%" + quest.Label + "%" + choice.Label, "", false, nil, []byte(p)); err != nil {
|
justify = p
|
||||||
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: %s", path.Base(exercice.Path), nline + 1, err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: has an invalid type. Expected true, false or a string", path.Base(exercice.Path), nline + 1, cid))
|
errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: has an invalid type. Expected true, false or a string", path.Base(exercice.Path), nline + 1, cid))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := flag.AddEntry(choice.Label, val); err != nil {
|
if e, err := flag.AddEntry(choice.Label, val); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: %s", path.Base(exercice.Path), nline + 1, cid, err))
|
errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: %s", path.Base(exercice.Path), nline + 1, cid, err))
|
||||||
continue
|
continue
|
||||||
|
} else if len(justify) > 0 {
|
||||||
|
if _, err := exercice.AddRawFlag(fmt.Sprintf("%%%d%%%s", e.Id, choice.Help), "", false, nil, []byte(justify)); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: %s", path.Base(exercice.Path), nline + 1, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if val {
|
if val {
|
||||||
|
@ -87,7 +87,7 @@ func treatSubmission(pathname string, team fic.Team, exercice_id string) {
|
|||||||
} else if mcq.IdExercice != exercice.Id {
|
} else if mcq.IdExercice != exercice.Id {
|
||||||
log.Println(id, "[ERR] We retrieve an invalid MCQ: from exercice", mcq.IdExercice, "whereas expected from exercice", exercice.Id)
|
log.Println(id, "[ERR] We retrieve an invalid MCQ: from exercice", mcq.IdExercice, "whereas expected from exercice", exercice.Id)
|
||||||
return
|
return
|
||||||
} else if key, err := mcq.GetJustifiedFlag(exercice, choice); err != nil {
|
} else if key, err := choice.GetJustifiedFlag(exercice); err != nil {
|
||||||
// Most probably, we enter here because the selected choice has not to be justified
|
// Most probably, we enter here because the selected choice has not to be justified
|
||||||
// So, just ignore this case as it will be invalidated by the mcq validation
|
// So, just ignore this case as it will be invalidated by the mcq validation
|
||||||
continue
|
continue
|
||||||
|
@ -233,18 +233,20 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
|
|||||||
angular.forEach(data.exercices, function(exercice, eid) {
|
angular.forEach(data.exercices, function(exercice, eid) {
|
||||||
angular.forEach(exercice.mcqs, function(mcq, mid) {
|
angular.forEach(exercice.mcqs, function(mcq, mid) {
|
||||||
angular.forEach(mcq.choices, function(choice, cid) {
|
angular.forEach(mcq.choices, function(choice, cid) {
|
||||||
|
if (!(choice instanceof Object))
|
||||||
this[cid] = {
|
this[cid] = {
|
||||||
label: choice,
|
label: choice,
|
||||||
value: mcq["checks_solved"] !== undefined && mcq["checks_solved"][cid] !== undefined && mcq["checks_solved"][cid] > 0
|
|
||||||
};
|
};
|
||||||
if (mcq["checks_solved"] !== undefined) {
|
|
||||||
this[cid].disabled = mcq["checks_solved"][cid] === undefined || mcq["checks_solved"][cid] >= 0;
|
this[cid].disabled = mcq.solved || mcq.part_solved || this[cid].solved;
|
||||||
this[cid].solved = mcq["checks_solved"][cid] !== undefined && mcq["checks_solved"][cid] >= 2;
|
|
||||||
}
|
if (!this[cid].help)
|
||||||
|
this[cid].help = "Flag correspondant";
|
||||||
|
|
||||||
|
if (!this[cid].disabled)
|
||||||
|
this[cid].value = $scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid] && $scope.my.exercices[eid].mcqs[mid].choices[cid].value
|
||||||
|
|
||||||
if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid]) {
|
if ($scope.my && $scope.my.exercices[eid] && $scope.my.exercices[eid].mcqs[mid] && $scope.my.exercices[eid].mcqs[mid].choices[cid]) {
|
||||||
if ($scope.my.exercices[eid].mcqs[mid].choices[cid].value !== undefined)
|
|
||||||
this[cid].value = $scope.my.exercices[eid].mcqs[mid].choices[cid].value;
|
|
||||||
if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justify !== undefined)
|
if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justify !== undefined)
|
||||||
this[cid].justify = $scope.my.exercices[eid].mcqs[mid].choices[cid].justify;
|
this[cid].justify = $scope.my.exercices[eid].mcqs[mid].choices[cid].justify;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@
|
|||||||
<div class="custom-control custom-checkbox" ng-repeat="(cid,choice) in mcq.choices" ng-if="!mcq.solved || mcq.justify">
|
<div class="custom-control custom-checkbox" ng-repeat="(cid,choice) in mcq.choices" ng-if="!mcq.solved || mcq.justify">
|
||||||
<input class="custom-control-input" type="checkbox" id="mcq_{{k}}_{{cid}}" name="mcq_{{k}}_{{cid}}" ng-model="choice.value" ng-disabled="choice.disabled">
|
<input class="custom-control-input" type="checkbox" id="mcq_{{k}}_{{cid}}" name="mcq_{{k}}_{{cid}}" ng-model="choice.value" ng-disabled="choice.disabled">
|
||||||
<label class="custom-control-label" for="mcq_{{k}}_{{cid}}" ng-bind="choice.label"></label>
|
<label class="custom-control-label" for="mcq_{{k}}_{{cid}}" ng-bind="choice.label"></label>
|
||||||
<input type="text" class="form-control" autocomplete="off" placeholder="Flag correspondant" name="sol_{{ cid }}" ng-model="choice.justify" ng-if="choice.value && mcq.justify && !choice.solved">
|
<input type="text" class="form-control" autocomplete="off" placeholder="{{ choice.help }}" name="sol_{{ cid }}" ng-model="choice.justify" ng-if="choice.value && mcq.justify && !choice.solved">
|
||||||
<span ng-if="choice.value && mcq.justify && choice.solved" ng-bind="choice.justify"></span>
|
<span ng-if="choice.value && mcq.justify && choice.solved" ng-bind="choice.justify"></span>
|
||||||
<span class="glyphicon glyphicon-ok form-control-feedback text-success" aria-hidden="true" ng-if="choice.solved" title="Flag trouvé !"></span>
|
<span class="glyphicon glyphicon-ok form-control-feedback text-success" aria-hidden="true" ng-if="choice.solved" title="Flag trouvé !"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,7 +54,7 @@ func (e Exercice) GetFlags() ([]Flag, error) {
|
|||||||
|
|
||||||
// GetFlagByLabel returns a flag matching the given label.
|
// GetFlagByLabel returns a flag matching the given label.
|
||||||
func (e Exercice) GetFlagByLabel(label string) (k Flag, err error) {
|
func (e Exercice) GetFlagByLabel(label string) (k Flag, err error) {
|
||||||
err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, validator_regexp, cksum FROM exercice_flags WHERE type = ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.ValidatorRegexp, &k.Checksum)
|
err = DBQueryRow("SELECT id_flag, id_exercice, type, help, ignorecase, validator_regexp, cksum FROM exercice_flags WHERE type LIKE ? AND id_exercice = ?", label, e.Id).Scan(&k.Id, &k.IdExercice, &k.Label, &k.Help, &k.IgnoreCase, &k.ValidatorRegexp, &k.Checksum)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fic
|
package fic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -175,8 +176,8 @@ func (e Exercice) WipeMCQs() (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetJustifiedFlag searchs for a flag in the scope of the given exercice.
|
// GetJustifiedFlag searchs for a flag in the scope of the given exercice.
|
||||||
func (m MCQ) GetJustifiedFlag(e Exercice, c MCQ_entry) (Flag, error) {
|
func (c MCQ_entry) GetJustifiedFlag(e Exercice) (Flag, error) {
|
||||||
return e.GetFlagByLabel("%" + m.Title + "%" + c.Label)
|
return e.GetFlagByLabel(fmt.Sprintf("\\%%%d\\%%%%", c.Id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
31
libfic/mcq_justification.go
Normal file
31
libfic/mcq_justification.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package fic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FlagLabel struct {
|
||||||
|
Label string
|
||||||
|
IdChoice int64
|
||||||
|
Checksum []byte
|
||||||
|
Solved bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMCQJustification tells you if this key represent a justification from a MCQ.
|
||||||
|
func (k Flag) IsMCQJustification() (bool) {
|
||||||
|
return len(k.Label) > 0 && k.Label[0] == '%'
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMCQJustification returns the structure corresponding to the given flag.
|
||||||
|
func (k Flag) GetMCQJustification() (fl FlagLabel, err error) {
|
||||||
|
spl := strings.Split(k.Label, "%")
|
||||||
|
if len(spl) >= 3 && len(spl[0]) == 0 {
|
||||||
|
fl.IdChoice, err = strconv.ParseInt(spl[1], 10, 64)
|
||||||
|
fl.Label = strings.Join(spl[2:], "%")
|
||||||
|
} else {
|
||||||
|
err = errors.New("This is not a MCQ justification")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -21,12 +21,18 @@ type myTeamHint struct {
|
|||||||
File string `json:"file,omitempty"`
|
File string `json:"file,omitempty"`
|
||||||
Cost int64 `json:"cost"`
|
Cost int64 `json:"cost"`
|
||||||
}
|
}
|
||||||
|
type myTeamMCQJustifiedChoice struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Solved bool `json:"solved,omitempty"`
|
||||||
|
Value bool `json:"value,omitempty"`
|
||||||
|
Help string `json:"help,omitempty"`
|
||||||
|
}
|
||||||
type myTeamMCQ struct {
|
type myTeamMCQ struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Justify bool `json:"justify,omitempty"`
|
Justify bool `json:"justify,omitempty"`
|
||||||
Choices map[int64]string `json:"choices,omitempty"`
|
Choices map[int64]interface{} `json:"choices,omitempty"`
|
||||||
Solved *time.Time `json:"solved,omitempty"`
|
Solved *time.Time `json:"solved,omitempty"`
|
||||||
PSolved map[int64]int `json:"checks_solved,omitempty"`
|
PSolved *time.Time `json:"part_solved,omitempty"`
|
||||||
Soluce string `json:"soluce,omitempty"`
|
Soluce string `json:"soluce,omitempty"`
|
||||||
}
|
}
|
||||||
type myTeamFlag struct {
|
type myTeamFlag struct {
|
||||||
@ -156,8 +162,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||||||
|
|
||||||
// Expose exercice flags
|
// Expose exercice flags
|
||||||
|
|
||||||
justifiedMCQ := map[string][]byte{}
|
justifiedMCQ := map[int64]FlagLabel{}
|
||||||
justifiedMCQ_ids := map[string]int64{}
|
|
||||||
exercice.Flags = map[int64]myTeamFlag{}
|
exercice.Flags = map[int64]myTeamFlag{}
|
||||||
|
|
||||||
if flags, err := e.GetFlags(); err != nil {
|
if flags, err := e.GetFlags(); err != nil {
|
||||||
@ -166,11 +171,12 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||||||
for _, k := range flags {
|
for _, k := range flags {
|
||||||
var flag myTeamFlag
|
var flag myTeamFlag
|
||||||
|
|
||||||
if k.Label[0] == '%' {
|
if fl, err := k.GetMCQJustification(); err == nil {
|
||||||
justifiedMCQ[k.Label[1:]] = k.Checksum
|
fl.Checksum = k.Checksum
|
||||||
if t == nil || t.HasPartiallySolved(k) == nil {
|
if t != nil && t.HasPartiallySolved(k) != nil {
|
||||||
justifiedMCQ_ids[k.Label[1:]] = k.Id
|
fl.Solved = true
|
||||||
}
|
}
|
||||||
|
justifiedMCQ[fl.IdChoice] = fl
|
||||||
continue
|
continue
|
||||||
} else if t == nil {
|
} else if t == nil {
|
||||||
flag.Soluce = hex.EncodeToString(k.Checksum)
|
flag.Soluce = hex.EncodeToString(k.Checksum)
|
||||||
@ -206,65 +212,64 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||||||
for _, mcq := range mcqs {
|
for _, mcq := range mcqs {
|
||||||
m := myTeamMCQ{
|
m := myTeamMCQ{
|
||||||
Title: mcq.Title,
|
Title: mcq.Title,
|
||||||
Choices: map[int64]string{},
|
Choices: map[int64]interface{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
soluce := ""
|
soluce := ""
|
||||||
solved_justify := true
|
fullySolved := true
|
||||||
|
if t != nil {
|
||||||
|
m.PSolved = t.HasPartiallyRespond(mcq)
|
||||||
|
}
|
||||||
|
|
||||||
for _, e := range mcq.Entries {
|
for _, e := range mcq.Entries {
|
||||||
m.Choices[e.Id] = e.Label
|
|
||||||
if e.Response {
|
if e.Response {
|
||||||
soluce += "t"
|
soluce += "t"
|
||||||
} else {
|
} else {
|
||||||
soluce += "f"
|
soluce += "f"
|
||||||
}
|
}
|
||||||
if v, ok := justifiedMCQ[m.Title + "%" + e.Label]; ok {
|
|
||||||
|
if v, ok := justifiedMCQ[e.Id]; ok {
|
||||||
m.Justify = true
|
m.Justify = true
|
||||||
|
|
||||||
if t == nil {
|
if t == nil {
|
||||||
soluce += hex.EncodeToString(v)
|
soluce += hex.EncodeToString(v.Checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := justifiedMCQ_ids[m.Title + "%" + e.Label]; ok {
|
if m.PSolved != nil || v.Solved {
|
||||||
solved_justify = false
|
jc := myTeamMCQJustifiedChoice{
|
||||||
|
Label: e.Label,
|
||||||
|
Solved: v.Solved,
|
||||||
|
Value: v.Solved,
|
||||||
|
Help: v.Label,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !v.Solved {
|
||||||
|
fullySolved = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if PartialValidation && m.PSolved != nil {
|
||||||
|
jc.Value = e.Response
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Choices[e.Id] = jc
|
||||||
|
} else {
|
||||||
|
m.Choices[e.Id] = e.Label
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.Choices[e.Id] = e.Label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if t != nil {
|
if t == nil {
|
||||||
if solved_justify {
|
|
||||||
m.Solved = t.HasPartiallyRespond(mcq)
|
|
||||||
} else if PartialValidation && t.HasPartiallyRespond(mcq) != nil {
|
|
||||||
m.PSolved = map[int64]int{}
|
|
||||||
for _, e := range mcq.Entries {
|
|
||||||
if _, ok := justifiedMCQ[m.Title + "%" + e.Label]; ok {
|
|
||||||
if _, ok := justifiedMCQ_ids[m.Title + "%" + e.Label]; !ok {
|
|
||||||
m.PSolved[e.Id] = 2
|
|
||||||
} else if e.Response {
|
|
||||||
m.PSolved[e.Id] = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if PartialValidation {
|
|
||||||
m.PSolved = map[int64]int{}
|
|
||||||
for _, e := range mcq.Entries {
|
|
||||||
if _, ok := justifiedMCQ[m.Title + "%" + e.Label]; ok {
|
|
||||||
if _, ok := justifiedMCQ_ids[m.Title + "%" + e.Label]; !ok {
|
|
||||||
m.PSolved[e.Id] = 2
|
|
||||||
} else {
|
|
||||||
m.PSolved[e.Id] = -1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m.PSolved[e.Id] = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
h := getHashedFlag([]byte(soluce))
|
h := getHashedFlag([]byte(soluce))
|
||||||
m.Soluce = hex.EncodeToString(h[:])
|
m.Soluce = hex.EncodeToString(h[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fullySolved {
|
||||||
|
m.Solved = m.PSolved
|
||||||
|
m.PSolved = nil
|
||||||
|
}
|
||||||
|
|
||||||
exercice.MCQs[mcq.Id] = m
|
exercice.MCQs[mcq.Id] = m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user