-// TODO: YAML format for questions, parsed from text (nested questions)
-// Then yaml parsed to json --> array of indexed questions
-// Use open mode for question banks: add setting "nbQuestions" to show nbQuestions
-// at random among active questions
+/*Draft format (compiled to json)
+ * TODO: separate questions and answers in Evaluation object
+
+<some html question (or/+ exercise intro)>
+
+ <some html subQuestion>
+ * some answer [trigger input/index in answers]
+
+ <another subquestion>
+
+ <sub-subQuestion>
+ + choix1
+ - choix 2
+ + choix 3
+ - choix4
+
+ <another sub sub> with params interpolation £ (tout simplement)
+ / params: javascript parameter generator (another function, body only)
+ * answer 2 (which can
+ be on
+ several lines)
+
+<Some second question>
+* With answer
+
+ dans le cas de parametetrized, answer est une fonction javascript !! qui prend en arg le(s) param(s)
+ coté serveur on stock parameterized question + body func
+ une fois côté client, extra work first to 1) execute each func 2) replace (and store!!!) all params
+https://stackoverflow.com/questions/7650071/is-there-a-way-to-create-a-function-from-a-string-with-javascript
+
+ */
new Vue({
el: '#course',
data: {
- display: "assessments", //or "students", or "grades" (admin mode)
+ display: "evaluations", //or "students" (in admin mode)
course: course,
- mode: "view", //or "edit" (some assessment)
- // assessment data:
monitorPwd: "",
- newAssessment: { name: "" },
- assessmentArray: assessmentArray,
- assessmentIndex: 0, //current edited assessment index
- assessment: { }, //copy of assessment at editing index in array
- assessmentText: "", //questions in an assessment, in text format
- // grades data:
- settings: {
- totalPoints: 20,
- halfPoints: false,
- zeroSum: false,
- },
- group: 1, //for detailed grades tables
- grades: { }, //computed
+ newEvaluation: { name: "" },
+ evaluationArray: evaluationArray,
+ mode: "view", //or "edit" (some evaluation)
+ evaluationIndex: -1, //current edited evaluation index
+ evaluation: { }, //copy of evaluation at editing index in array
+ questionsText: "", //questions in an evaluation, in text format
},
mounted: function() {
- $('.modal').each( (i,elem) => {
- if (elem.id != "assessmentEdit")
- $(elem).modal();
- });
- $('ul.tabs').tabs();
- $('#assessmentEdit').modal({
- complete: () => {
- this.parseAssessment();
- Vue.nextTick( () => {
- $("#questionList").find("code[class^=language-]").each( (i,elem) => {
- Prism.highlightElement(elem);
- });
- MathJax.Hub.Queue(["Typeset",MathJax.Hub,"questionList"]);
- });
- },
- });
+ $('.modal').modal();
+ //Materialize.updateTextFields(); //textareas, time field...
},
methods: {
// GENERAL:
d.number = d.number.toString();
students.push(d);
});
- $.ajax("/import/students", {
- method: "POST",
+ $.ajax("/courses/student-list", {
+ method: "PUT",
data: {
cid: this.course._id,
students: JSON.stringify(students),
},
});
},
- // ASSESSMENT:
- addAssessment: function() {
+ // evaluation:
+ addEvaluation: function() {
if (!admin)
return;
// modal, fill code and description
- let error = Validator.checkObject(this.newAssessment, "Assessment");
+ let error = Validator.checkObject(this.newEvaluation, "Evaluation");
if (!!error)
return alert(error);
else
- $('#newAssessment').modal('close');
- $.ajax("/add/assessment",
+ $('#newEvaluation').modal('close');
+ $.ajax("/evaluations",
{
- method: "GET",
+ method: "POST",
data: {
- name: this.newAssessment.name,
+ name: this.newEvaluation.name,
cid: course._id,
},
dataType: "json",
success: res => {
if (!res.errmsg)
{
- this.newAssessment["name"] = "";
- this.assessmentArray.push(res);
+ this.newEvaluation["name"] = "";
+ this.evaluationArray.push(res);
}
else
alert(res.errmsg);
}
);
},
- materialOpenModal: function(id) {
- $("#" + id).modal("open");
- Materialize.updateTextFields(); //textareas, time field...
- },
- updateAssessment: function() {
- $.ajax("/update/assessment", {
- method: "POST",
- data: {assessment: JSON.stringify(this.assessment)},
+ updateEvaluation: function() {
+ $.ajax("/evaluations", {
+ method: "PUT",
+ data: {evaluation: JSON.stringify(this.evaluation)},
dataType: "json",
success: res => {
if (!res.errmsg)
{
- this.assessmentArray[this.assessmentIndex] = this.assessment;
+ this.evaluationArray[this.evaluationIndex] = this.evaluation;
this.mode = "view";
}
else
},
});
},
- deleteAssessment: function(assessment) {
+ deleteEvaluation: function(evaluation) {
if (!admin)
return;
- if (confirm("Delete assessment '" + assessment.name + "' ?"))
+ if (confirm("Delete evaluation '" + evaluation.name + "' ?"))
{
- $.ajax("/remove/assessment",
+ $.ajax("/evaluations",
{
- method: "GET",
- data: { qid: this.assessment._id },
+ method: "DELETE",
+ data: { qid: this.evaluation._id },
dataType: "json",
success: res => {
if (!res.errmsg)
- this.assessmentArray.splice( this.assessmentArray.findIndex( item => {
- return item._id == assessment._id;
+ this.evaluationArray.splice( this.evaluationArray.findIndex( item => {
+ return item._id == evaluation._id;
}), 1 );
else
alert(res.errmsg);
}
},
toggleState: function(questionIndex) {
- // add or remove from activeSet of current assessment
- let activeIndex = this.assessment.activeSet.findIndex( item => { return item == questionIndex; });
+ // add or remove from activeSet of current evaluation
+ let activeIndex = this.evaluation.activeSet.findIndex( item => { return item == questionIndex; });
if (activeIndex >= 0)
- this.assessment.activeSet.splice(activeIndex, 1);
+ this.evaluation.activeSet.splice(activeIndex, 1);
else
- this.assessment.activeSet.push(questionIndex);
+ this.evaluation.activeSet.push(questionIndex);
},
- setAssessmentText: function() {
+ setEvaluationText: function() {
let txt = "";
- this.assessment.questions.forEach( q => {
+ this.evaluation.questions.forEach( q => {
txt += q.wording; //already ended by \n
q.options.forEach( (o,i) => {
let symbol = q.answer.includes(i) ? "+" : "-";
});
txt += "\n"; //separate questions by new line
});
- this.assessmentText = txt;
+ this.questionsText = txt;
},
- parseAssessment: function() {
+ parseEvaluation: function() {
let questions = [ ];
- let lines = this.assessmentText.split("\n").map( L => { return L.trim(); })
+ let lines = this.questionsText.split("\n").map( L => { return L.trim(); })
lines.push(""); //easier parsing
let emptyQuestion = () => {
return {
}
}
});
- this.assessment.questions = questions;
+ this.evaluation.questions = questions;
},
- actionAssessment: function(index) {
+ actionEvaluation: function(index) {
if (admin)
{
// Edit screen
- this.assessmentIndex = index;
- this.assessment = $.extend(true, {}, this.assessmentArray[index]);
- this.setAssessmentText();
+ this.evaluationIndex = index;
+ this.evaluation = $.extend(true, {}, this.evaluationArray[index]);
+ this.setEvaluationText();
this.mode = "edit";
- Vue.nextTick( () => {
- $("#questionList").find("code[class^=language-]").each( (i,elem) => {
- Prism.highlightElement(elem);
- });
- MathJax.Hub.Queue(["Typeset",MathJax.Hub,"questionList"]);
- });
+ Vue.nextTick(statementsLibsRefresh);
}
- else //external user: show assessment
- this.redirect(this.assessmentArray[index].name);
+ else //external user: show evaluation
+ this.redirect(this.evaluationArray[index].name);
},
- redirect: function(assessmentName) {
- document.location.href = "/" + initials + "/" + course.code + "/" + assessmentName;
+ redirect: function(evaluationName) {
+ document.location.href = "/" + initials + "/" + course.code + "/" + evaluationName;
},
setPassword: function() {
let hashPwd = Sha1.Compute(this.monitorPwd);
let error = Validator.checkObject({password:hashPwd}, "Course");
if (error.length > 0)
return alert(error);
- $.ajax("/set/password",
+ $.ajax("/courses/password",
{
- method: "GET",
+ method: "PUT",
data: {
cid: this.course._id,
pwd: hashPwd,
);
},
// NOTE: artifact required for Vue v-model to behave well
- checkBoxFixedId: function(i) {
+ checkboxFixedId: function(i) {
return "questionFixed" + i;
},
- checkBoxActiveId: function(i) {
+ checkboxActiveId: function(i) {
return "questionActive" + i;
},
- // GRADES:
- gradeSettings: function() {
- $("#gradeSettings").modal("open");
- Materialize.updateTextFields(); //total points field in grade settings overlap
- },
- download: function() {
- // Download (all) grades as a CSV file
- let data = [ ];
- this.studentList(0).forEach( s => {
- let finalGrade = 0.;
- let gradesCount = 0;
- if (!!this.grades[s.number])
- {
- Object.keys(this.grades[s.number]).forEach( assessmentName => {
- s[assessmentName] = this.grades[s.number][assessmentName];
- if (_.isNumeric(s[assessmentName]) && !isNaN(s[assessmentName]))
- {
- finalGrade += s[assessmentName];
- gradesCount++;
- }
- if (gradesCount >= 1)
- finalGrade /= gradesCount;
- s["final"] = finalGrade; //TODO: forbid "final" as assessment name
- });
- }
- data.push(s); //number,name,group,assessName1...assessNameN,final
- });
- let csv = Papa.unparse(data, {
- quotes: true,
- header: true,
- });
- let downloadAnchor = $("#download");
- downloadAnchor.attr("download", this.course.code + "_results.csv");
- downloadAnchor.attr("href", "data:text/plain;charset=utf-8," + encodeURIComponent(csv));
- this.$refs.download.click()
- //downloadAnchor.click(); //fails
- },
- showDetails: function(group) {
- this.group = group;
- $("#detailedGrades").modal("open");
- },
- groupList: function() {
- let maxGrp = 1;
- this.course.students.forEach( s => {
- if (s.group > maxGrp)
- maxGrp = s.group;
- });
- return _.range(1,maxGrp+1);
- },
- grade: function(assessmentIndex, studentNumber) {
- if (!this.grades[assessmentIndex] || !this.grades[assessmentIndex][studentNumber])
- return ""; //no grade yet
- return this.grades[assessmentIndex][studentNumber];
- },
- groupId: function(group, prefix) {
- return (prefix || "") + "group" + group;
- },
- togglePresence: function(number, index) {
- // UNIMPLEMENTED
- // TODO: if no grade (thus automatic 0), toggle "exempt" state on student for current exam
- // --> automatic update of grades view (just a few number to change)
- },
- computeGrades: function() {
- // UNIMPLEMENTED
- // TODO: compute all grades using settings (points, coefficients, bonus/malus...).
- // If some questions with free answers (open), display answers and ask teacher action.
- // TODO: need a setting for that too (by student, by exercice, by question)
- },
},
});