'update'
[qomet.git] / public / javascripts / course.js
CommitLineData
43828378 1/*Draft format (compiled to json)
14c9c66a 2 * TODO: separate questions and answers in Evaluation object
43828378 3
43828378
BA
4<some html question (or/+ exercise intro)>
5
6 <some html subQuestion>
7 * some answer [trigger input/index in answers]
8
9 <another subquestion>
10
11 <sub-subQuestion>
12 + choix1
13 - choix 2
14 + choix 3
15 - choix4
16
14c9c66a
BA
17 <another sub sub> with params interpolation £ (tout simplement)
18 / params: javascript parameter generator (another function, body only)
43828378
BA
19 * answer 2 (which can
20 be on
21 several lines)
22
23<Some second question>
24* With answer
14c9c66a
BA
25
26 dans le cas de parametetrized, answer est une fonction javascript !! qui prend en arg le(s) param(s)
27 coté serveur on stock parameterized question + body func
28 une fois côté client, extra work first to 1) execute each func 2) replace (and store!!!) all params
29https://stackoverflow.com/questions/7650071/is-there-a-way-to-create-a-function-from-a-string-with-javascript
30
31 */
e99c53fb 32
8a2b3260
BA
33new Vue({
34 el: '#course',
35 data: {
14c9c66a 36 display: "evaluations", //or "students" (in admin mode)
8a2b3260 37 course: course,
8a2b3260 38 monitorPwd: "",
a3080c33
BA
39 newEvaluation: { name: "" },
40 evaluationArray: evaluationArray,
41 mode: "view", //or "edit" (some evaluation)
42 evaluationIndex: 0, //current edited evaluation index
43 evaluation: { }, //copy of evaluation at editing index in array
44 questionsText: "", //questions in an evaluation, in text format
8a2b3260
BA
45 },
46 mounted: function() {
a3080c33
BA
47
48
49
8a2b3260 50 $('.modal').each( (i,elem) => {
a3080c33 51 if (elem.id != "evaluationEdit")
8a2b3260
BA
52 $(elem).modal();
53 });
a3080c33
BA
54 $('ul.tabs').tabs(); //--> migrate to grade.js
55
56
57
58 $('#evaluationEdit').modal({
8a2b3260 59 complete: () => {
a3080c33
BA
60 this.parseEvaluation();
61 Vue.nextTick(statementsLibsRefresh);
e99c53fb 62 },
8a2b3260
BA
63 });
64 },
65 methods: {
66 // GENERAL:
67 toggleDisplay: function(area) {
68 if (this.display == area)
69 this.display = "";
70 else
71 this.display = area;
72 },
73 studentList: function(group) {
74 return this.course.students
75 .filter( s => { return group==0 || s.group == group; })
76 .map( s => { return Object.assign({}, s); }) //not altering initial array
77 .sort( (a,b) => { return a.name.localeCompare(b.name); })
78 },
79 // STUDENTS:
80 uploadTrigger: function() {
81 $("#upload").click();
82 },
83 upload: function(e) {
84 let file = (e.target.files || e.dataTransfer.files)[0];
85 Papa.parse(file, {
86 header: true,
87 skipEmptyLines: true,
88 complete: (results,file) => {
89 let students = [ ];
90 // Post-process: add group/number if missing
91 let number = 1;
92 results.data.forEach( d => {
93 if (!d.group)
94 d.group = 1;
95 if (!d.number)
96 d.number = number++;
97 if (typeof d.number !== "string")
98 d.number = d.number.toString();
99 students.push(d);
100 });
73609d3b
BA
101 $.ajax("/courses/student-list", {
102 method: "PUT",
e99c53fb 103 data: {
8a2b3260
BA
104 cid: this.course._id,
105 students: JSON.stringify(students),
e99c53fb
BA
106 },
107 dataType: "json",
108 success: res => {
109 if (!res.errmsg)
8a2b3260 110 this.course.students = students;
e99c53fb
BA
111 else
112 alert(res.errmsg);
113 },
8a2b3260
BA
114 });
115 },
116 });
117 },
a3080c33
BA
118 // evaluation:
119 addEvaluation: function() {
8a2b3260
BA
120 if (!admin)
121 return;
122 // modal, fill code and description
a3080c33 123 let error = Validator.checkObject(this.newEvaluation, "Evaluation");
8a2b3260
BA
124 if (!!error)
125 return alert(error);
126 else
a3080c33
BA
127 $('#newEvaluation').modal('close');
128 $.ajax("/evaluations",
8a2b3260 129 {
73609d3b 130 method: "POST",
8a2b3260 131 data: {
a3080c33 132 name: this.newEvaluation.name,
8a2b3260
BA
133 cid: course._id,
134 },
e99c53fb
BA
135 dataType: "json",
136 success: res => {
137 if (!res.errmsg)
138 {
a3080c33
BA
139 this.newEvaluation["name"] = "";
140 this.evaluationArray.push(res);
e99c53fb
BA
141 }
142 else
143 alert(res.errmsg);
144 },
e99c53fb 145 }
8a2b3260
BA
146 );
147 },
148 materialOpenModal: function(id) {
149 $("#" + id).modal("open");
150 Materialize.updateTextFields(); //textareas, time field...
151 },
a3080c33
BA
152 updateEvaluation: function() {
153 $.ajax("/evaluations", {
73609d3b 154 method: "PUT",
a3080c33 155 data: {evaluation: JSON.stringify(this.evaluation)},
8a2b3260
BA
156 dataType: "json",
157 success: res => {
158 if (!res.errmsg)
e99c53fb 159 {
a3080c33 160 this.evaluationArray[this.evaluationIndex] = this.evaluation;
8a2b3260 161 this.mode = "view";
e99c53fb
BA
162 }
163 else
8a2b3260
BA
164 alert(res.errmsg);
165 },
166 });
167 },
a3080c33 168 deleteEvaluation: function(evaluation) {
8a2b3260
BA
169 if (!admin)
170 return;
a3080c33 171 if (confirm("Delete evaluation '" + evaluation.name + "' ?"))
8a2b3260 172 {
a3080c33 173 $.ajax("/evaluations",
e99c53fb 174 {
73609d3b 175 method: "DELETE",
a3080c33 176 data: { qid: this.evaluation._id },
e99c53fb
BA
177 dataType: "json",
178 success: res => {
179 if (!res.errmsg)
a3080c33
BA
180 this.evaluationArray.splice( this.evaluationArray.findIndex( item => {
181 return item._id == evaluation._id;
8a2b3260 182 }), 1 );
e99c53fb
BA
183 else
184 alert(res.errmsg);
185 },
186 }
187 );
8a2b3260
BA
188 }
189 },
190 toggleState: function(questionIndex) {
a3080c33
BA
191 // add or remove from activeSet of current evaluation
192 let activeIndex = this.evaluation.activeSet.findIndex( item => { return item == questionIndex; });
8a2b3260 193 if (activeIndex >= 0)
a3080c33 194 this.evaluation.activeSet.splice(activeIndex, 1);
8a2b3260 195 else
a3080c33 196 this.evaluation.activeSet.push(questionIndex);
8a2b3260 197 },
a3080c33 198 setEvaluationText: function() {
8a2b3260 199 let txt = "";
a3080c33 200 this.evaluation.questions.forEach( q => {
8a2b3260
BA
201 txt += q.wording; //already ended by \n
202 q.options.forEach( (o,i) => {
203 let symbol = q.answer.includes(i) ? "+" : "-";
204 txt += symbol + " " + o + "\n";
205 });
206 txt += "\n"; //separate questions by new line
207 });
a3080c33 208 this.questionsText = txt;
8a2b3260 209 },
a3080c33 210 parseEvaluation: function() {
8a2b3260 211 let questions = [ ];
a3080c33 212 let lines = this.questionsText.split("\n").map( L => { return L.trim(); })
8a2b3260
BA
213 lines.push(""); //easier parsing
214 let emptyQuestion = () => {
215 return {
216 wording: "",
217 options: [ ],
218 answer: [ ],
219 active: true, //default
220 };
221 };
222 let q = emptyQuestion();
223 lines.forEach( L => {
224 if (L.length > 0)
225 {
226 if (['+','-'].includes(L.charAt(0)))
e99c53fb 227 {
8a2b3260
BA
228 if (L.charAt(0) == '+')
229 q.answer.push(q.options.length);
230 q.options.push(L.slice(1).trim());
e99c53fb 231 }
8a2b3260
BA
232 else if (L.charAt(0) == '*')
233 {
234 // TODO: read current + next lines into q.answer (HTML, 1-elem array)
235 }
236 else
237 q.wording += L + "\n";
238 }
239 else
240 {
241 // Flush current question (if any)
242 if (q.wording.length > 0)
243 {
244 questions.push(q);
245 q = emptyQuestion();
246 }
247 }
248 });
a3080c33 249 this.evaluation.questions = questions;
8a2b3260 250 },
a3080c33 251 actionEvaluation: function(index) {
8a2b3260
BA
252 if (admin)
253 {
254 // Edit screen
a3080c33
BA
255 this.evaluationIndex = index;
256 this.evaluation = $.extend(true, {}, this.evaluationArray[index]);
257 this.setEvaluationText();
8a2b3260 258 this.mode = "edit";
a3080c33 259 Vue.nextTick(statementsLibsRefresh);
8a2b3260 260 }
a3080c33
BA
261 else //external user: show evaluation
262 this.redirect(this.evaluationArray[index].name);
e99c53fb 263 },
a3080c33
BA
264 redirect: function(evaluationName) {
265 document.location.href = "/" + initials + "/" + course.code + "/" + evaluationName;
8a2b3260
BA
266 },
267 setPassword: function() {
268 let hashPwd = Sha1.Compute(this.monitorPwd);
269 let error = Validator.checkObject({password:hashPwd}, "Course");
270 if (error.length > 0)
271 return alert(error);
73609d3b 272 $.ajax("/courses/password",
8a2b3260 273 {
73609d3b 274 method: "PUT",
8a2b3260
BA
275 data: {
276 cid: this.course._id,
277 pwd: hashPwd,
278 },
279 dataType: "json",
280 success: res => {
281 if (!res.errmsg)
282 alert("Password saved!");
283 else
284 alert(res.errmsg);
285 },
286 }
287 );
288 },
289 // NOTE: artifact required for Vue v-model to behave well
43828378 290 checkboxFixedId: function(i) {
8a2b3260
BA
291 return "questionFixed" + i;
292 },
43828378 293 checkboxActiveId: function(i) {
8a2b3260
BA
294 return "questionActive" + i;
295 },
8a2b3260
BA
296 },
297});