Add correction steps

This commit is contained in:
nemunaire 2020-09-13 16:37:15 +02:00
parent 1d8c4a375c
commit 7201c3d02a
5 changed files with 577 additions and 29 deletions

326
corrections.go Normal file
View File

@ -0,0 +1,326 @@
package main
import (
"encoding/json"
"strconv"
"strings"
"github.com/julienschmidt/httprouter"
)
func init() {
router.GET("/api/surveys/:sid/questions/:qid/corrections", apiHandler(questionHandler(
func(q Question, _ []byte) HTTPResponse {
if cts, err := q.GetCorrectionTemplates(); err != nil {
return APIErrorResponse{err: err}
} else {
return APIResponse{cts}
}
}), adminRestricted))
router.POST("/api/surveys/:sid/questions/:qid/corrections", apiHandler(questionHandler(func(q Question, body []byte) HTTPResponse {
var new CorrectionTemplate
if err := json.Unmarshal(body, &new); err != nil {
return APIErrorResponse{err: err}
}
return formatApiResponse(q.NewCorrectionTemplate(new.Label, new.Score, new.ScoreExplaination))
}), adminRestricted))
router.GET("/api/surveys/:sid/questions/:qid/corrections/:cid", apiHandler(correctionHandler(
func(ct CorrectionTemplate, _ []byte) HTTPResponse {
if users, err := ct.GetUserCorrected(); err != nil {
return APIErrorResponse{err: err}
} else {
return APIResponse{users}
}
}), adminRestricted))
router.PUT("/api/surveys/:sid/questions/:qid/corrections/:cid", apiHandler(correctionHandler(func(current CorrectionTemplate, body []byte) HTTPResponse {
var new CorrectionTemplate
if err := json.Unmarshal(body, &new); err != nil {
return APIErrorResponse{err: err}
}
new.Id = current.Id
if err := new.Update(); err != nil {
return APIErrorResponse{err: err}
} else {
return APIResponse{new}
}
}), adminRestricted))
router.DELETE("/api/surveys/:sid/questions/:qid/corrections/:cid", apiHandler(correctionHandler(func(ct CorrectionTemplate, body []byte) HTTPResponse {
return formatApiResponse(ct.Delete())
}), adminRestricted))
router.GET("/api/users/:uid/questions/:qid", apiAuthHandler(func(u *User, ps httprouter.Params, body []byte) HTTPResponse {
return userHandler(func(u User, _ []byte) HTTPResponse {
if qid, err := strconv.Atoi(string(ps.ByName("qid"))); err != nil {
return APIErrorResponse{err: err}
} else if question, err := getQuestion(qid); err != nil {
return APIErrorResponse{err: err}
} else {
return formatApiResponse(question.ComputeScoreQuestion(&u))
}
})(ps, body)
}, adminRestricted))
router.GET("/api/users/:uid/corrections", apiHandler(userHandler(
func(u User, _ []byte) HTTPResponse {
return formatApiResponse(u.GetCorrections())
}), adminRestricted))
router.POST("/api/users/:uid/corrections", apiHandler(userHandler(func(u User, body []byte) HTTPResponse {
var new UserCorrection
if err := json.Unmarshal(body, &new); err != nil {
return APIErrorResponse{err: err}
}
return formatApiResponse(u.NewCorrection(new.IdTemplate))
}), adminRestricted))
router.DELETE("/api/users/:uid/corrections/:cid", apiHandler(userCorrectionHandler(func(u User, uc UserCorrection, body []byte) HTTPResponse {
return formatApiResponse(uc.Delete(u))
}), adminRestricted))
}
func correctionHandler(f func(CorrectionTemplate, []byte) HTTPResponse) func(httprouter.Params, []byte) HTTPResponse {
return func(ps httprouter.Params, body []byte) HTTPResponse {
return questionHandler(func(q Question, body []byte) HTTPResponse {
if cid, err := strconv.Atoi(string(ps.ByName("cid"))); err != nil {
return APIErrorResponse{err: err}
} else if correction, err := q.GetCorrectionTemplate(cid); err != nil {
return APIErrorResponse{err: err}
} else {
return f(correction, body)
}
})(ps, body)
}
}
func userCorrectionHandler(f func(User, UserCorrection, []byte) HTTPResponse) func(httprouter.Params, []byte) HTTPResponse {
return func(ps httprouter.Params, body []byte) HTTPResponse {
return userHandler(func(u User, body []byte) HTTPResponse {
if cid, err := strconv.Atoi(string(ps.ByName("cid"))); err != nil {
return APIErrorResponse{err: err}
} else if correction, err := u.GetCorrection(cid); err != nil {
return APIErrorResponse{err: err}
} else {
return f(u, correction, body)
}
})(ps, body)
}
}
type CorrectionTemplate struct {
Id int64 `json:"id"`
IdQuestion int64 `json:"id_question"`
Label string `json:"label"`
Score int `json:"score"`
ScoreExplaination string `json:"score_explaination,omitempty"`
}
func (q *Question) GetCorrectionTemplates() (ct []CorrectionTemplate, err error) {
if rows, errr := DBQuery("SELECT id_template, id_question, label, score, score_explanation FROM correction_templates WHERE id_question=?", q.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var c CorrectionTemplate
if err = rows.Scan(&c.Id, &c.IdQuestion, &c.Label, &c.Score, &c.ScoreExplaination); err != nil {
return
}
ct = append(ct, c)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (q *Question) GetCorrectionTemplate(id int) (c CorrectionTemplate, err error) {
err = DBQueryRow("SELECT id_template, id_question, label, score, score_explanation FROM correction_templates WHERE id_question=? AND id_template=?", q.Id, id).Scan(&c.Id, &c.IdQuestion, &c.Label, &c.Score, &c.ScoreExplaination)
return
}
func GetCorrectionTemplate(id int64) (c CorrectionTemplate, err error) {
err = DBQueryRow("SELECT id_template, id_question, label, score, score_explanation FROM correction_templates WHERE id_template=?", id).Scan(&c.Id, &c.IdQuestion, &c.Label, &c.Score, &c.ScoreExplaination)
return
}
func (q *Question) NewCorrectionTemplate(label string, score int, score_explaination string) (CorrectionTemplate, error) {
if res, err := DBExec("INSERT INTO correction_templates (id_question, label, score, score_explanation) VALUES (?, ?, ?, ?)", q.Id, label, score, score_explaination); err != nil {
return CorrectionTemplate{}, err
} else if cid, err := res.LastInsertId(); err != nil {
return CorrectionTemplate{}, err
} else {
return CorrectionTemplate{cid, q.Id, label, score, score_explaination}, nil
}
}
func (t *CorrectionTemplate) Update() error {
_, err := DBExec("UPDATE correction_templates SET id_question = ?, label = ?, score = ?, score_explanation = ? WHERE id_template = ?", t.IdQuestion, t.Label, t.Score, t.ScoreExplaination, t.Id)
return err
}
func (t *CorrectionTemplate) Delete() (int64, error) {
if res, err := DBExec("DELETE FROM correction_templates WHERE id_template = ?", t.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
func (t *CorrectionTemplate) GetUserCorrected() (ucs []UserCorrection, err error) {
if rows, errr := DBQuery("SELECT id_correction, id_user, id_template FROM student_corrected WHERE id_template=?", t.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var c UserCorrection
if err = rows.Scan(&c.Id, &c.IdUser, &c.IdTemplate); err != nil {
return
}
ucs = append(ucs, c)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func ClearTemplates() (int64, error) {
if res, err := DBExec("DELETE FROM correction_templates"); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
} else {
return nb, err
}
}
type UserCorrection struct {
Id int64 `json:"id"`
IdUser int64 `json:"id_user,omitempty"`
IdTemplate int64 `json:"id_template"`
}
func (u *User) GetCorrections() (uc []UserCorrection, err error) {
if rows, errr := DBQuery("SELECT id_correction, id_template FROM student_corrected WHERE id_user=?", u.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var c UserCorrection
if err = rows.Scan(&c.Id, &c.IdTemplate); err != nil {
return
}
uc = append(uc, c)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (u *User) GetCorrectionsTemplate() (tpls []int64, err error) {
if rows, errr := DBQuery("SELECT id_template FROM student_corrected WHERE id_user=?", u.Id); errr != nil {
return nil, errr
} else {
defer rows.Close()
for rows.Next() {
var tpl int64
if err = rows.Scan(&tpl); err != nil {
return
}
tpls = append(tpls, tpl)
}
if err = rows.Err(); err != nil {
return
}
return
}
}
func (u *User) GetCorrection(id int) (c UserCorrection, err error) {
err = DBQueryRow("SELECT id_correction, id_template FROM student_corrected WHERE id_user=? AND id_correction=?", u.Id, id).Scan(&c.Id, &c.IdTemplate)
return
}
func (u *User) NewCorrection(id int64) (*UserCorrectionSummary, error) {
if res, err := DBExec("INSERT INTO student_corrected (id_user, id_template) VALUES (?, ?)", u.Id, id); err != nil {
return nil, err
} else if cid, err := res.LastInsertId(); err != nil {
return nil, err
} else if ucs, err := u.ComputeScoreQuestion(id); err != nil {
return nil, err
} else {
ucs.LastId = cid
return ucs, nil
}
}
func (c *UserCorrection) Delete(u User) (*UserCorrectionSummary, error) {
if res, err := DBExec("DELETE FROM student_corrected WHERE id_correction = ?", c.Id); err != nil {
return nil, err
} else if _, err := res.RowsAffected(); err != nil {
return nil, err
} else if ucs, err := u.ComputeScoreQuestion(c.IdTemplate); err != nil {
return nil, err
} else {
return ucs, nil
}
}
type UserCorrectionSummary struct {
LastId int64 `json:"last_id,omitempty"`
Score int `json:"score"`
ScoreExplaination string `json:"score_explaination"`
}
func (u *User) ComputeScoreQuestion(idtpl int64) (*UserCorrectionSummary, error) {
if tpl, err := GetCorrectionTemplate(idtpl); err != nil {
return nil, err
} else if question, err := getQuestion(int(tpl.IdQuestion)); err != nil {
return nil, err
} else {
return question.ComputeScoreQuestion(u)
}
}
func (q *Question) ComputeScoreQuestion(u *User) (*UserCorrectionSummary, error) {
if templates, err := q.GetCorrectionTemplates(); err != nil {
return nil, err
} else if corrections, err := u.GetCorrectionsTemplate(); err != nil {
return nil, err
} else {
tpls := map[int64]CorrectionTemplate{}
for _, tpl := range templates {
tpls[tpl.Id] = tpl
}
score := 100
var expl []string
for _, correction := range corrections {
if _, ok := tpls[correction]; ok {
score += tpls[correction].Score
if tpls[correction].ScoreExplaination != "" {
expl = append(expl, tpls[correction].ScoreExplaination)
}
delete(tpls, correction)
}
}
return &UserCorrectionSummary{Score: score, ScoreExplaination: strings.Join(expl, ". ")}, nil
}
}

11
db.go
View File

@ -137,6 +137,17 @@ CREATE TABLE IF NOT EXISTS correction_templates(
score_explanation TEXT,
FOREIGN KEY(id_question) REFERENCES survey_quests(id_question)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS student_corrected(
id_correction INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_user INTEGER NOT NULL,
id_template INTEGER NOT NULL,
FOREIGN KEY(id_user) REFERENCES users(id_user),
FOREIGN KEY(id_template) REFERENCES correction_templates(id_template)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}

View File

@ -34,6 +34,11 @@ angular.module("AtsebaytApp")
'update': {method: 'PUT'},
})
})
.factory("CorrectionTemplate", function($resource) {
return $resource("/api/surveys/:surveyId/questions/:questId/corrections/:correctId", { surveyId: '@id', questId: '@id', correctId: '@id' }, {
'update': {method: 'PUT'},
})
})
.factory("MyResponse", function($resource) {
return $resource("/api/surveys/:surveyId/responses/:respId", { surveyId: '@id', respId: '@id' })
})
@ -135,7 +140,8 @@ angular.module("AtsebaytApp")
<td class="bg-success" ng-if="survey.end_availability < $ctrl.now && survey.corrected">Corrigé</td>
<td ng-if="survey.start_availability > $ctrl.now">{{ survey.start_availability | date: "medium" }} <svg class="bi bi-arrow-bar-right" width="1em" height="1em" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M12.146 6.646a.5.5 0 01.708 0l3 3a.5.5 0 010 .708l-3 3a.5.5 0 01-.708-.708L14.793 10l-2.647-2.646a.5.5 0 010-.708z" clip-rule="evenodd"></path><path fill-rule="evenodd" d="M8 10a.5.5 0 01.5-.5H15a.5.5 0 010 1H8.5A.5.5 0 018 10zm-2.5 6a.5.5 0 01-.5-.5v-11a.5.5 0 011 0v11a.5.5 0 01-.5.5z" clip-rule="evenodd"></path></svg></td>
<td ng-if="survey.start_availability <= $ctrl.now"><svg class="bi bi-arrow-bar-left" width="1em" height="1em" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.854 6.646a.5.5 0 00-.708 0l-3 3a.5.5 0 000 .708l3 3a.5.5 0 00.708-.708L5.207 10l2.647-2.646a.5.5 0 000-.708z" clip-rule="evenodd"></path><path fill-rule="evenodd" d="M12 10a.5.5 0 00-.5-.5H5a.5.5 0 000 1h6.5a.5.5 0 00.5-.5zm2.5 6a.5.5 0 01-.5-.5v-11a.5.5 0 011 0v11a.5.5 0 01-.5.5z" clip-rule="evenodd"></path></svg> {{ survey.end_availability | date: "medium" }}</td>
<td ng-if="$ctrl.islogged">N/A</td>
<td ng-if="$ctrl.islogged && !survey.corrected">N/A</td>
<td ng-if="$ctrl.islogged && survey.corrected" ng-controller="ScoreController">{{ score.score }}</td>
</tr>
</tbody>
<tfoot ng-if="$ctrl.isadmin">
@ -276,19 +282,43 @@ angular.module("AtsebaytApp")
})
.controller("ScoreController", function($scope, SurveyScore) {
$scope.score = SurveyScore.get({ surveyId: $scope.survey.id, questId: $scope.question.id })
$scope.score = SurveyScore.get({ surveyId: $scope.survey.id })
})
.controller("QuestionController", function($scope, Survey, SurveyQuest, SurveyQuest, AllResponses, $http, $routeParams) {
.controller("QuestionController", function($scope, Survey, SurveyQuest, SurveyQuest, AllResponses, CorrectionTemplate, $http, $routeParams) {
$scope.survey = Survey.get({ surveyId: $routeParams.surveyId });
$scope.survey.$promise.then(function(survey) {
survey.start_availability = Date.parse(survey.start_availability)
survey.end_availability = Date.parse(survey.end_availability)
})
$scope.question = SurveyQuest.get({ surveyId: $routeParams.surveyId, questId: $routeParams.questId });
$scope.responses = AllResponses.query({ surveyId: $routeParams.surveyId, questId: $routeParams.questId });
$scope.responses = AllResponses.query({ surveyId: $routeParams.surveyId, questId: $routeParams.questId }, function (responses) {
$scope.users_corrected = {}
responses.forEach(function(r) {
$http({
url: "/api/users/" + r.id_user + "/questions/" + $routeParams.questId
}).then(function(response) {
$scope.users_corrected[r.id_user] = response.data
})
})
});
$scope.submitCorrection = function() {
if ($scope.users_corrected[this.response.id_user]) {
if (this.response.score === undefined)
this.response.score = $scope.users_corrected[this.response.id_user].score
if ($scope.users_corrected[this.response.id_user].score_explaination && (!this.response.score_explaination || this.response.score_explaination.indexOf($scope.users_corrected[this.response.id_user].score_explaination) == -1)) {
if (this.response.score_explaination)
this.response.score_explaination += '\n' + $scope.users_corrected[this.response.id_user].score_explaination
else
this.response.score_explaination = $scope.users_corrected[this.response.id_user].score_explaination
}
}
this.response.id_corrector = $scope.user.id
if (!this.response.time_scored)
this.response.time_scored = (new Date()).toISOString()
this.response.$update()
}
$scope.submitCorrections = function() {
@ -296,6 +326,76 @@ angular.module("AtsebaytApp")
response.$update()
})
}
$scope.templates = CorrectionTemplate.query({ surveyId: $routeParams.surveyId, questId: $routeParams.questId }, function (tpls) {
$scope.template_corrected = {}
tpls.forEach(function(tpl) {
$scope.template_corrected[tpl.id] = {}
CorrectionTemplate.query({ surveyId: $routeParams.surveyId, questId: $routeParams.questId, correctId: tpl.id }, function (cts) {
cts.forEach(function(ct) {
$scope.template_corrected[tpl.id][ct.id_user] = ct
})
});
})
});
$scope.changeCorrection = function(id_user) {
var tpl = this.template
if ($scope.template_corrected[tpl.id] && $scope.template_corrected[tpl.id][id_user]) {
$http({
url: "/api/users/" + id_user + "/corrections/" + $scope.template_corrected[tpl.id][id_user].id,
method: "DELETE"
}).then(function(response) {
$scope.template_corrected[tpl.id][id_user] = false
$scope.users_corrected[id_user] = response.data
})
} else {
$http({
url: "/api/users/" + id_user + "/corrections",
data: {id_template: tpl.id},
method: "POST"
}).then(function(response) {
if ($scope.template_corrected[tpl.id] === undefined)
$scope.template_corrected[tpl.id] = {}
$scope.template_corrected[tpl.id][id_user] = { id: response.data.last_id, id_template: tpl.id }
$scope.users_corrected[id_user] = response.data
})
}
}
$scope.deleteTemplate = function() {
var template = new CorrectionTemplate({
id: $('#tcid').val() == "" ? null : parseInt($('#tcid').val()),
})
template.$remove({ surveyId: $scope.survey.id, questId: $scope.question.id, correctId: template.id }, function() {
$scope.templates = CorrectionTemplate.query({ surveyId: $scope.survey.id, questId: $scope.question.id });
angular.element('#correctionTemplateModal').modal('hide')
});
}
$scope.saveTemplate = function() {
var template = new CorrectionTemplate({
id: $('#tcid').val() == "" ? null : parseInt($('#tcid').val()),
id_question: parseInt($('#tcidquestion').val()),
label: $('#tclabel').val(),
score: parseInt($('#tcscore').val()),
score_explaination: $('#tcexplaination').val()
})
if (template.id) {
template.$update({ surveyId: $scope.survey.id, questId: $scope.question.id, correctId: template.id }, function () {
$scope.templates = CorrectionTemplate.query({ surveyId: $scope.survey.id, questId: $scope.question.id });
angular.element('#correctionTemplateModal').modal('hide')
});
} else {
template.$save({ surveyId: $scope.survey.id, questId: $scope.question.id }, function () {
$scope.templates = CorrectionTemplate.query({ surveyId: $scope.survey.id, questId: $scope.question.id });
angular.element('#correctionTemplateModal').modal('hide')
});
}
}
})
.controller("QuestionsController", function($scope, SurveyQuest, MyResponse, $http, $location) {
@ -424,3 +524,24 @@ angular.module("AtsebaytApp")
}
}
})
.controller("CorrectionsTemplateController", function($scope, CorrectionTemplate) {
$scope.addTemplate = function() {
var element = angular.element('#correctionTemplateModal');
var tpl = new CorrectionTemplate({id_question: $scope.question.id})
if (element.data('bs.modal'))
element.data('bs.modal')._config.template = tpl
element.modal({
template: tpl
});
}
$scope.editTemplate = function() {
var element = angular.element('#correctionTemplateModal');
if (element.data('bs.modal'))
element.data('bs.modal')._config.template = this.template
element.modal({
template: this.template
});
}
})

View File

@ -10,10 +10,10 @@
{{ question.title }}
</h3>
<form class="mb-5" ng-submit="submitCorrections()" ng-cloak>
<div class="card mb-2" ng-repeat="response in responses">
<div class="mb-5" ng-submit="submitCorrections()" ng-cloak>
<form class="card mb-2" ng-repeat="response in responses">
<div class="card-body">
<button class="btn btn-success ml-1 float-right" ng-click="submitCorrection()"><svg class="bi bi-check" width="1em" height="1em" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.854 5.646a.5.5 0 010 .708l-7 7a.5.5 0 01-.708 0l-3.5-3.5a.5.5 0 11.708-.708L8.5 12.293l6.646-6.647a.5.5 0 01.708 0z" clip-rule="evenodd"></path></svg></button>
<button type="button" class="btn btn-success ml-1 float-right" ng-click="submitCorrection()"><svg class="bi bi-check" width="1em" height="1em" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M15.854 5.646a.5.5 0 010 .708l-7 7a.5.5 0 01-.708 0l-3.5-3.5a.5.5 0 11.708-.708L8.5 12.293l6.646-6.647a.5.5 0 01.708 0z" clip-rule="evenodd"></path></svg></button>
<p class="card-text" style="white-space: pre-line" ng-bind="response.value" ng-if="question.kind == 'text'"></p>
<div class="card-text" ng-if="question.kind == 'mcq'" ng-controller="ProposalsController">
<div class="form-group form-check" ng-repeat="proposal in proposals">
@ -28,31 +28,114 @@
</div>
</div>
<div class="form-group">
<div class="input-group mb-1">
<input class="form-control" type="number" ng-model="response.score" integer>
<div class="input-group-append">
<span class="input-group-text">/100</span>
<div class="row">
<div class="col-6" ng-controller="CorrectionsTemplateController">
<div ng-repeat="template in templates">
<div class="form-group mb-2 form-check">
<input type="checkbox" class="form-check-input" id="u{{response.id_user}}p{{template.id}}" ng-checked="template_corrected[template.id][response.id_user]" ng-click="changeCorrection(response.id_user)">
<label class="form-check-label" for="u{{response.id_user}}p{{template.id}}">{{ template.label }}</label>
<button type="button" class="btn btn-sm btn-primary ml-1 float-right" ng-click="editTemplate()"><svg class="bi bi-pencil" width="1em" height="1em" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.293 3.293a1 1 0 011.414 0l2 2a1 1 0 010 1.414l-9 9a1 1 0 01-.39.242l-3 1a1 1 0 01-1.266-1.265l1-3a1 1 0 01.242-.391l9-9zM14 4l2 2-9 9-3 1 1-3 9-9z" clip-rule="evenodd"></path><path fill-rule="evenodd" d="M14.146 8.354l-2.5-2.5.708-.708 2.5 2.5-.708.708zM5 12v.5a.5.5 0 00.5.5H6v.5a.5.5 0 00.5.5H7v.5a.5.5 0 00.5.5H8v-1.5a.5.5 0 00-.5-.5H7v-.5a.5.5 0 00-.5-.5H5z" clip-rule="evenodd"></path></svg></button>
<button type="button" class="btn btn-sm btn-info ml-1 float-right" ng-click="addTemplate()" ng-if="$last"><svg class="bi bi-plus" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 3.5a.5.5 0 01.5.5v4a.5.5 0 01-.5.5H4a.5.5 0 010-1h3.5V4a.5.5 0 01.5-.5z" clip-rule="evenodd"></path><path fill-rule="evenodd" d="M7.5 8a.5.5 0 01.5-.5h4a.5.5 0 010 1H8.5V12a.5.5 0 01-1 0V8z" clip-rule="evenodd"></path></svg></button>
</div>
</div>
<button type="button" class="btn btn-info ml-1 float-right" ng-click="addTemplate()" ng-if="templates.length === 0"><svg class="bi bi-plus" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 3.5a.5.5 0 01.5.5v4a.5.5 0 01-.5.5H4a.5.5 0 010-1h3.5V4a.5.5 0 01.5-.5z" clip-rule="evenodd"></path><path fill-rule="evenodd" d="M7.5 8a.5.5 0 01.5-.5h4a.5.5 0 010 1H8.5V12a.5.5 0 01-1 0V8z" clip-rule="evenodd"></path></svg> Ajouter un template</button>
</div>
<div class="col-6">
<div class="form-group row mb-2">
<div class="col-6 input-group">
<input class="form-control" type="number" ng-model="response.score" integer>
<div class="input-group-append">
<span class="input-group-text">/100</span>
</div>
</div>
<div class="col-6 row">
<label class="col-4 col-form-label">Auto&nbsp;:</label>
<div class="col-8">
<input type="text" class="form-control-plaintext" value="{{ users_corrected[response.id_user].score}} / 100" readonly>
</div>
</div>
</div>
<div class="form-group mb-2">
<textarea class="form-control" rows="1" ng-model="response.score_explaination" placeholder="Appréciation : "></textarea>
{{ users_corrected[response.id_user].score_explaination }}
</div>
<div class="form-group row mb-1" ng-if="response.time_scored">
<label class="col-4 col-form-label col-form-label-sm">Correction effectuée&nbsp;:</label>
<div class="col-8">
<input type="text" class="form-control-plaintext form-control-sm" value="{{ response.time_scored | date:'medium'}}" readonly>
</div>
</div>
<div class="form-group row mb-1" ng-if="response.time_scored">
<label class="col-4 col-form-label col-form-label-sm">Correcteur&nbsp;:</label>
<div class="col-8">
<input type="text" class="form-control-plaintext form-control-sm" value="{{ response.id_corrector }}" readonly>
</div>
</div>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="1" ng-model="response.score_explaination" placeholder="Appréciation"></textarea>
</div>
<div class="form-group row" ng-if="response.time_scored">
<label class="col-2 col-form-label col-form-label-sm">Correction effectuée&nbsp;:</label>
<div class="col-10">
<input type="text" class="form-control-plaintext form-control-sm" value="{{ response.time_scored | date:'medium'}}" readonly>
</div>
</div>
<div class="form-group row" ng-if="response.time_scored">
<label class="col-2 col-form-label col-form-label-sm">Correcteur&nbsp;:</label>
<div class="col-10">
<input type="text" class="form-control-plaintext form-control-sm" value="{{ response.id_corrector }}" readonly>
</div>
</div>
</div>
</div>
</form>
<button type="submit" class="btn btn-primary" ng-disabled="submitInProgress">Soumettre les corrections</button>
</form>
<div class="modal fade" id="correctionTemplateModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Édition de modèle de correction</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form ng-submit="saveTemplate()">
<input type="hidden" id="tcid">
<input type="hidden" id="tcidquestion">
<div class="form-group">
<label for="tclabel">Label</label>
<input type="text" class="form-control" id="tclabel" placeholder="Label">
</div>
<div class="form-group">
<label for="tcscore">Score</label>
<input type="number" class="form-control" id="tcscore" placeholder="Score">
</div>
<div class="form-group">
<label for="tcexplaination">Explaination</label>
<textarea class="form-control" id="tcexplaination" placeholder="Explaination"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="tcdelbtn" ng-click="deleteTemplate()">Supprimer</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
<button type="button" class="btn btn-primary" ng-click="saveTemplate()">Enregistrer</button>
</div>
</div>
</div>
</div>
<script>
$('#correctionTemplateModal').on('show.bs.modal', function (event) {
var template = $(this).data('bs.modal')._config.template
var modal = $(this)
modal.find('#tclabel').val("")
modal.find('#tcscore').val(0)
modal.find('#tcexplaination').val("")
modal.find('#tcidquestion').val(0)
modal.find('#tcid').val(0)
modal.find('#tclabel').val(template.label)
modal.find('#tcscore').val(template.score)
modal.find('#tcexplaination').val(template.score_explaination)
modal.find('#tcidquestion').val(template.id_question)
modal.find('#tcid').val(template.id)
if (template.id) {
modal.find('#tcdelbtn').show()
} else {
modal.find('#tcdelbtn').hide()
}
})
</script>

View File

@ -109,7 +109,14 @@
</button>
</div>
<p class="card-text alert alert-success" style="white-space: pre-line" ng-if="!question.edit && (question.response.score_explaination || question.response.score)"><strong>{{question.response.score}}&nbsp;:</strong> {{ question.response.score_explaination }}</p>
<div class="ml-3 card-text alert alert-success" ng-if="!question.edit && (question.response.score_explaination || question.response.score)">
<div class="row">
<div class="col-auto">
<strong>{{question.response.score}}&nbsp;%</strong>
</div>
<p class="col mb-0" style="white-space: pre-line">{{ question.response.score_explaination }}</p>
</div>
</div>
</div>
</div>