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