early draft of sockets logic for monitoring
[qomet.git] / public / javascripts / monitor.js
1 // UNIMPLEMENTED
2
3 // TODO: onglets pour chaque groupe + section déroulante questionnaire (chargé avec réponses)
4 // NOM Prenom (par grp, puis alphabétique)
5 // réponse : vert si OK (+ choix), rouge si faux, gris si texte (clic pour voir)
6 // + temps total ?
7 // click sur en-tête de colonne : tri alphabétique, tri décroissant...
8 // Affiché si (hash du) mdp du cours est correctement entré
9 // Doit reprendre les données en base si refresh (sinon : sockets)
10
11 // Also buttons "start exam", "end exam" for logged in teacher
12
13 // TODO: réutiliser le component... trouver un moyen
14
15 let socket = null; //monitor answers in real time
16
17 function libsRefresh()
18 {
19 // Run Prism + MathJax on questions text
20 $("#statements").find("code[class^=language-]").each( (i,elem) => {
21 Prism.highlightElement(elem);
22 });
23 MathJax.Hub.Queue(["Typeset",MathJax.Hub,"statements"]);
24 }
25
26 new Vue({
27 el: "#monitor",
28 data: {
29 password: "", //from password field
30 assessment: null, //obtained after authentication
31 // Stage 0: unauthenticated (password),
32 // 1: authenticated (password hash validated), start monitoring
33 stage: 0,
34 },
35 components: {
36 "statements": {
37 props: ['assessment','inputs','student','stage'],
38 // TODO: general render function for nested exercises
39 // There should be a questions navigator below, or next (visible if display=='all')
40 // Full questions tree is rendered, but some parts hidden depending on display settings
41 render(h) {
42 let self = this;
43 let questions = (assessment.questions || [ ]).map( (q,i) => {
44 let questionContent = [ ];
45 questionContent.push(
46 h(
47 "div",
48 {
49 "class": {
50 wording: true,
51 },
52 domProps: {
53 innerHTML: q.wording,
54 },
55 }
56 )
57 );
58 let optionsOrder = _.range(q.options.length);
59 if (!q.fixed)
60 optionsOrder = _.shuffle(optionsOrder);
61 let optionList = [ ];
62 optionsOrder.forEach( idx => {
63 let option = [ ];
64 option.push(
65 h(
66 "input",
67 {
68 domProps: {
69 checked: this.inputs.length > 0 && this.inputs[i][idx],
70 },
71 attrs: {
72 id: this.inputId(i,idx),
73 type: "checkbox",
74 },
75 on: {
76 change: e => { this.inputs[i][idx] = e.target.checked; },
77 },
78 },
79 )
80 );
81 option.push(
82 h(
83 "label",
84 {
85 domProps: {
86 innerHTML: q.options[idx],
87 },
88 attrs: {
89 "for": this.inputId(i,idx),
90 },
91 }
92 )
93 );
94 optionList.push(
95 h(
96 "div",
97 {
98 "class": {
99 option: true,
100 choiceCorrect: this.stage == 4 && assessment.questions[i].answer.includes(idx),
101 choiceWrong: this.stage == 4 && this.inputs[i][idx] && !assessment.questions[i].answer.includes(idx),
102 },
103 },
104 option
105 )
106 );
107 });
108 questionContent.push(
109 h(
110 "div",
111 {
112 "class": {
113 optionList: true,
114 },
115 },
116 optionList
117 )
118 );
119 return h(
120 "div",
121 {
122 "class": {
123 "question": true,
124 "hide": this.stage == 2 && assessment.display == 'one' && assessment.indices[assessment.index] != i,
125 },
126 },
127 questionContent
128 );
129 });
130 if (this.stage == 2)
131 {
132 questions.unshift(
133 h(
134 "button",
135 {
136 "class": {
137 "waves-effect": true,
138 "waves-light": true,
139 "btn": true,
140 },
141 style: {
142 "display": "block",
143 "margin-left": "auto",
144 "margin-right": "auto",
145 },
146 on: {
147 click: () => this.sendAnswer(assessment.indices[assessment.index]),
148 },
149 },
150 "Send"
151 )
152 );
153 }
154 return h(
155 "div",
156 {
157 attrs: {
158 id: "statements",
159 },
160 },
161 questions
162 );
163 },
164 mounted: function() {
165 libsRefresh();
166 },
167 methods: {
168 inputId: function(i,j) {
169 return "q" + i + "_" + "input" + j;
170 },
171 },
172 },
173 },
174 methods: {
175 // stage 0 --> 1
176 startMonitoring: function() {
177 $.ajax("/start/monitoring", {
178 method: "GET",
179 data: {
180 password: this.,
181 aname: examName,
182 cname: courseName,
183 },
184 dataType: "json",
185 success: s => {
186 if (!!s.errmsg)
187 return this.warning(s.errmsg);
188 this.stage = 1;
189 },
190 });
191 },
192 // TODO: 2-level sockets, for prof and monitors
193 socket = io.connect("/" + assessment.name, {
194 query: "number=" + this.student.number + "&password=" + this.password
195 });
196 socket.on(message.allAnswers, this.setAnswers);
197 initializeStage2(s.questions, s.paper);
198 },
199 });
200 },
201 // stage 2 --> 3 (or 4)
202 // from a message by statements component, or time over
203 // TODO: also function startAssessment (for main teacher only)
204 endAssessment: function() {
205 // Set endTime, destroy password
206 $("#leftButton, #rightButton").show();
207 if (assessment.mode == "open")
208 {
209 this.stage = 4;
210 return;
211 }
212 $.ajax("/end/assessment", {
213 method: "GET",
214 data: {
215 aid: assessment._id,
216 number: this.student.number,
217 password: this.student.password,
218 },
219 dataType: "json",
220 success: ret => {
221 if (!!ret.errmsg)
222 return this.warning(ret.errmsg);
223 assessment.conclusion = ret.conclusion;
224 this.stage = 3;
225 delete this.student["password"]; //unable to send new answers now
226 socket.disconnect();
227 socket = null;
228 },
229 });
230 },
231 },
232 });