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