'update'
[qomet.git] / public / javascripts / course.js
CommitLineData
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
25new 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});