frontend: treat MCQ justification as key flag, instead of special case

This commit is contained in:
nemunaire 2019-01-17 22:30:39 +01:00
parent 2879b697c0
commit b6769086c2
4 changed files with 126 additions and 99 deletions

View file

@ -1,3 +1,32 @@
function treatFlagKey(flag) {
if (flag.values !== undefined) {
if (flag.separator) {
for (var i = flag.values.length - 1; i >= 0; i--) {
if (!flag.values[i].length)
flag.values.splice(i, 1);
}
if (flag.ignore_order)
flag.value = flag.values.slice().sort().join(flag.separator) + flag.separator;
else
flag.value = flag.values.join(flag.separator) + flag.separator;
if (flag.values.length == 0)
flag.values = [""];
}
else
flag.value = flag.values[0];
}
if (flag.found == null && flag.soluce !== undefined) {
if (check === undefined) check = true;
if (flag.value && flag.soluce == b2sum(flag.value))
flag.found = new Date();
check &= flag.found;
}
}
angular.module("FICApp", ["ngRoute", "ngSanitize"])
.config(function($routeProvider, $locationProvider) {
$routeProvider
@ -55,6 +84,42 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
$rootScope.notify_field = 0;
});
})
.component('flagKey', {
bindings: {
kid: '=',
key: '=',
settings: '=',
wantchoices: '=',
},
controller: function() {
this.additem = function(key) {
this.key.values.push("");
};
},
template: `
<div class="form-group">
<label for="sol_{{ $ctrl.kid }}_0">{{ $ctrl.key.label }}&nbsp;:</label>
<span ng-if="$ctrl.key.found && $ctrl.key.value" ng-bind="$ctrl.key.value"></span>
<div class="input-group" ng-repeat="v in $ctrl.key.values track by $index" ng-class="{'mt-1': !$first}" ng-if="!$ctrl.key.found">
<input type="text" class="form-control flag" id="sol_{{ $ctrl.kid }}_{{ $index }}" autocomplete="off" name="sol_{{ $ctrl.kid }}_{{ $index }}" ng-model="$ctrl.key.values[$index]" ng-if="!$ctrl.key.choices" placeholder="{{ $ctrl.key.help }}" title="{{ $ctrl.key.help }}">
<select class="custom-select" id="sol_{{ $ctrl.kid }}" name="sol_{{ $ctrl.kid }}" ng-model="$ctrl.key.values[$index]" ng-if="$ctrl.key.choices" ng-options="l as v for (l, v) in $ctrl.key.choices"></select>
<div class="input-group-append" ng-if="$ctrl.key.choices_cost">
<button class="btn btn-success" type="button" ng-click="$ctrl.wantchoices($ctrl.kid)" ng-class="{disabled: $ctrl.key.wcsubmitted}" title="Cliquez pour échanger ce champ de texte par une liste de choix. L'opération vous coûtera {{ $ctrl.key.choices_cost * $ctrl.settings.wchoiceCurrentCoefficient }} points.">
<span class="glyphicon glyphicon-tasks" aria-hidden="true"></span>
Liste de propositions (<ng-pluralize count="$ctrl.key.choices_cost * $ctrl.settings.wchoiceCurrentCoefficient" when="{'one': '{} point', 'other': '{} points'}"></ng-pluralize>)
</button>
</div>
<div class="input-group-append" ng-if="$ctrl.key.separator && $last">
<button class="btn btn-success" type="button" ng-click="$ctrl.additem(key)" title="Ajouter un élément.">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</div>
</div>
<small class="form-text text-muted" ng-if="!$ctrl.key.found && $ctrl.key.help2.length > 0" ng-bind="$ctrl.key.help2"></small>
<span class="glyphicon glyphicon-ok form-control-feedback text-success" aria-hidden="true" ng-if="$ctrl.key.found" title="Flag trouvé à {{ $ctrl.key.found | date:'mediumTime'}}"></span>
</div>
`
})
.controller("DataController", function($sce, $scope, $http, $rootScope, $interval, $location) {
var actMenu = function() {
if ($scope.my && $scope.themes) {
@ -243,17 +308,30 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
label: choice,
};
this[cid].disabled = mcq.solved || mcq.part_solved || this[cid].solved;
if (!this[cid].help)
this[cid].help = "Flag correspondant";
this[cid].disabled = mcq.solved || mcq.part_solved || (this[cid].justification && this[cid].justification.solved);
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.exercices[eid].mcqs[mid].choices[cid].justify !== undefined)
this[cid].justify = $scope.my.exercices[eid].mcqs[mid].choices[cid].justify;
if (mcq.justify) {
if (!this[cid].justification)
this[cid].justification = {};
if (!this[cid].justification.label) {
this[cid].justification.label = "Flag correspondant";
this[cid].justification.help2 = "Trouvez et validez les choix du QCM pour avoir des indications supplémentaires";
}
if ($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].justification) {
if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justification.value !== undefined)
data.exercices[eid].mcqs[mid].choices[cid].justification.value = $scope.my.exercices[eid].mcqs[mid].choices[cid].justification.value;
if ($scope.my.exercices[eid].mcqs[mid].choices[cid].justification.values !== undefined)
data.exercices[eid].mcqs[mid].choices[cid].justification.values = $scope.my.exercices[eid].mcqs[mid].choices[cid].justification.values;
else
data.exercices[eid].mcqs[mid].choices[cid].justification.values = [""];
}
else
data.exercices[eid].mcqs[mid].choices[cid].justification.values = [""];
}
}, mcq.choices);
});
@ -336,10 +414,6 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
var cbs;
var cbd;
$scope.additem = function(key) {
key.values.push("");
};
$scope.ssubmit = function() {
var resp = {}
var check = undefined
@ -349,36 +423,10 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
{
resp["flags"] = {};
angular.forEach($scope.my.exercices[$rootScope.current_exercice].flags, function(flag,kid) {
if (flag.values !== undefined) {
if (flag.separator) {
for (var i = flag.values.length - 1; i >= 0; i--) {
if (!flag.values[i].length)
flag.values.splice(i, 1);
}
if (flag.ignoreorder)
flag.value = flag.values.slice().sort().join(flag.separator) + flag.separator;
else
flag.value = flag.values.join(flag.separator) + flag.separator;
if (flag.values.length == 0)
flag.values = [""];
}
else
flag.value = flag.values[0];
}
if (flag.found == null) {
if (flag.soluce !== undefined) {
if (check === undefined) check = true;
if (flag.value && flag.soluce == b2sum(flag.value))
flag.found = new Date();
check &= flag.found;
} else {
treatFlagKey(flag);
if (flag.found == null && flag.soluce === undefined) {
resp["flags"][kid] = flag.value;
}
}
});
}
@ -396,11 +444,11 @@ angular.module("FICApp", ["ngRoute", "ngSanitize"])
} else {
if (choice.value) {
resp["mcqs"][cid] = choice.value;
if (mcq.justify !== undefined) {
if (choice.justification !== undefined) {
if (resp["justifications"] == undefined)
resp["justifications"] = {};
if (choice.justify)
resp["justifications"][cid] = choice.justify;
treatFlagKey(choice.justification);
resp["justifications"][cid] = choice.justification.value;
}
}
}

View file

@ -90,26 +90,7 @@
</ul>
<div class="card-body" ng-if="!my.exercices[current_exercice].submitted || sberr">
<form ng-submit="ssubmit()">
<div class="form-group" ng-repeat="(kid,key) in my.exercices[current_exercice].flags">
<label for="sol_{{ kid }}_0">{{ key.label }}&nbsp;:</label>
<span ng-if="key.found && key.value" ng-bind="key.value"></span>
<div class="input-group" ng-repeat="v in key.values track by $index" ng-class="{'mt-1': !$first}" ng-if="!key.found">
<input type="text" class="form-control flag" id="sol_{{ kid }}_{{ $index }}" autocomplete="off" name="sol_{{ kid }}_{{ $index }}" ng-model="key.values[$index]" ng-if="!key.choices" placeholder="{{ key.help }}" title="{{ key.help }}">
<select class="custom-select" id="sol_{{ kid }}" name="sol_{{ kid }}" ng-model="key.values[$index]" ng-if="key.choices" ng-options="l as v for (l, v) in key.choices"></select>
<div class="input-group-append" ng-if="key.choices_cost">
<button class="btn btn-success" type="button" ng-click="wantchoices(kid)" ng-class="{disabled: key.wcsubmitted}" title="Cliquez pour échanger ce champ de texte par une liste de choix. L'opération vous coûtera {{ key.choices_cost * settings.wchoiceCurrentCoefficient }} points.">
<span class="glyphicon glyphicon-tasks" aria-hidden="true"></span>
Liste de propositions (<ng-pluralize count="key.choices_cost * settings.wchoiceCurrentCoefficient" when="{'one': '{} point', 'other': '{} points'}"></ng-pluralize>)
</button>
</div>
<div class="input-group-append" ng-if="key.separator && $last">
<button class="btn btn-success" type="button" ng-click="additem(key)" title="Ajouter un élément.">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</div>
</div>
<span class="glyphicon glyphicon-ok form-control-feedback text-success" aria-hidden="true" ng-if="key.found" title="Flag trouvé à {{ key.found | date:'mediumTime'}}"></span>
</div>
<flag-key ng-repeat="(kid,key) in my.exercices[current_exercice].flags" kid="kid" key="key" settings="settings" wantchoices="wantchoices"></flag-key>
<div class="form-group" ng-repeat="(k,mcq) in my.exercices[current_exercice].mcqs">
<p ng-if="mcq.title">{{ mcq.title }} <span class="glyphicon glyphicon-ok form-control-feedback text-success" aria-hidden="true" ng-if="mcq.solved" title="QCM réussi à {{ mcq.solved | date:'mediumTime'}}"></span></p>
@ -117,7 +98,7 @@
<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">
<label class="custom-control-label" for="mcq_{{k}}_{{cid}}" ng-bind="choice.label"></label>
<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">
<flag-key kid="cid" key="choice.justification" settings="settings" wantchoices="wantchoices" ng-if="choice.value && mcq.justify && (!choice.justification || !choice.justification.solved)"></flag-key>
<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>
</div>

View file

@ -9,8 +9,6 @@ import (
type FlagLabel struct {
Label string
IdChoice int64
Checksum []byte
Solved bool
}
// IsMCQJustification tells you if this key represent a justification from a MCQ.

View file

@ -22,20 +22,6 @@ type myTeamHint struct {
File string `json:"file,omitempty"`
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 {
Title string `json:"title"`
Justify bool `json:"justify,omitempty"`
Choices map[int64]interface{} `json:"choices,omitempty"`
Solved *time.Time `json:"solved,omitempty"`
PSolved *time.Time `json:"part_solved,omitempty"`
Soluce string `json:"soluce,omitempty"`
}
type myTeamFlag struct {
Label string `json:"label"`
Help string `json:"help,omitempty"`
@ -46,6 +32,19 @@ type myTeamFlag struct {
Choices map[string]string `json:"choices,omitempty"`
ChoicesCost int64 `json:"choices_cost,omitempty"`
}
type myTeamMCQJustifiedChoice struct {
Label string `json:"label"`
Value bool `json:"value,omitempty"`
Justification myTeamFlag `json:"justification,omitempty"`
}
type myTeamMCQ struct {
Title string `json:"title"`
Justify bool `json:"justify,omitempty"`
Choices map[int64]interface{} `json:"choices,omitempty"`
Solved *time.Time `json:"solved,omitempty"`
PSolved *time.Time `json:"part_solved,omitempty"`
Soluce string `json:"soluce,omitempty"`
}
type myTeamExercice struct {
ThemeId int64 `json:"theme_id"`
Statement string `json:"statement"`
@ -171,7 +170,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
// Expose exercice flags
justifiedMCQ := map[int64]FlagLabel{}
justifiedMCQ := map[int64]myTeamFlag{}
exercice.Flags = map[int64]myTeamFlag{}
if flags, err := e.GetFlagKeys(); err != nil {
@ -185,19 +184,19 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
continue
}
if fl, err := k.GetMCQJustification(); err == nil {
fl.Checksum = k.Checksum
if t != nil && t.HasPartiallySolved(k) != nil {
fl.Solved = true
}
justifiedMCQ[fl.IdChoice] = fl
continue
} else if t == nil {
// Retrieve solved state or solution for public iface
if t == nil {
flag.Soluce = hex.EncodeToString(k.Checksum)
} else if PartialValidation {
flag.Solved = t.HasPartiallySolved(k)
}
var fl FlagLabel
if fl, err = k.GetMCQJustification(); err == nil {
k.Label = fl.Label
}
// Treat array flags
if k.Label[0] == '`' && len(k.Label) > 3 {
flag.Label = k.Label[3:]
flag.Separator = string(k.Label[1])
@ -206,6 +205,7 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
flag.Label = k.Label
}
// Fill more information if the flag is displaied
if flag.Solved == nil {
flag.Help = k.Help
if choices, err := k.GetChoices(); err != nil {
@ -220,9 +220,14 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
}
}
// Append to corresponding flags' map
if fl.IdChoice != 0 {
justifiedMCQ[fl.IdChoice] = flag
} else {
exercice.Flags[k.Id] = flag
}
}
}
// Expose exercice MCQs
@ -258,19 +263,14 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
if v, ok := justifiedMCQ[e.Id]; ok {
m.Justify = true
if t == nil {
soluce += hex.EncodeToString(v.Checksum)
}
if m.PSolved != nil || v.Solved {
if m.PSolved != nil || v.Solved != nil {
jc := myTeamMCQJustifiedChoice{
Label: e.Label,
Solved: v.Solved,
Value: v.Solved,
Help: v.Label,
Value: v.Solved != nil,
Justification: v,
}
if !v.Solved {
if v.Solved == nil {
fullySolved = false
}