Draft logic to toggle students presence in exam + send answers only if everyone finished
[qomet.git] / public / javascripts / monitor.js
1 let socket = null; //monitor answers in real time
2
3 new Vue({
4 el: "#monitor",
5 data: {
6 password: "", //from password field
7 assessment: { }, //obtained after authentication
8 // Stage 0: unauthenticated (password),
9 // 1: authenticated (password hash validated), start monitoring
10 stage: 0,
11 answers: {
12 displayAll: true,
13 showSolution: true, //TODO: allow to hide, to let teachers search too
14 inputs: [ ],
15 index : -1,
16 },
17 students: [ ], //to know their names
18 display: "assessment", //or student's answers
19 },
20 methods: {
21 // TODO: redundant code, next 4 funcs already exist in course.js
22 toggleDisplay: function(area) {
23 if (this.display == area)
24 this.display = "";
25 else
26 this.display = area;
27 },
28 studentList: function(group) {
29 return this.students
30 .filter( s => { return group==0 || s.group == group; })
31 .map( s => { return Object.assign({}, s); }) //not altering initial array
32 .sort( (a,b) => { return a.name.localeCompare(b.name); });
33 },
34 groupList: function() {
35 let maxGrp = 1;
36 this.students.forEach( s => {
37 if (s.group > maxGrp)
38 maxGrp = s.group;
39 });
40 return _.range(1,maxGrp+1);
41 },
42 groupId: function(group, prefix) {
43 return (prefix || "") + "group" + group;
44 },
45 togglePresence: function(s) {
46 s.present = !s.present;
47 },
48 allFinished: function() {
49 for (s of this.students)
50 {
51 if (!s.present)
52 continue;
53 const paperIdx = this.assessment.papers.findIndex( item => { return item.number == number; });
54 if (paperIdx === -1)
55 return false;
56 const paper = this.assessment.papers[paperIdx];
57 if (paper.inputs.length < this.assessment.questions.length)
58 return false;
59 }
60 return true;
61 },
62 getColor: function(number, qIdx) {
63 // For the moment, green if correct and red if wrong; grey if unanswered yet
64 // TODO: in-between color for partially right (especially for multi-questions)
65 const paperIdx = this.assessment.papers.findIndex( item => { return item.number == number; });
66 if (paperIdx === -1)
67 return "grey"; //student didn't start yet
68 const inputIdx = this.assessment.papers[paperIdx].inputs.findIndex( item => {
69 const qNum = parseInt(item.index.split(".")[0]); //indexes separated by dots
70 return qIdx == qNum;
71 });
72 if (inputIdx === -1)
73 return "grey";
74 if (_.isEqual(this.assessment.papers[paperIdx].inputs[inputIdx].input, this.assessment.questions[qIdx].answer))
75 return "green";
76 return "red";
77 },
78 seeDetails: function(number, i) {
79 // UNIMPLEMENTED: see question details, with current answer(s)
80 },
81 // stage 0 --> 1
82 startMonitoring: function() {
83 $.ajax("/start/monitoring", {
84 method: "GET",
85 data: {
86 password: Sha1.Compute(this.password),
87 aname: examName,
88 ccode: courseCode,
89 initials: initials,
90 },
91 dataType: "json",
92 success: s => {
93 if (!!s.errmsg)
94 return alert(s.errmsg);
95 this.assessment = s.assessment;
96 this.answers.inputs = s.assessment.questions.map( q => {
97 let input = _(q.options.length).times( _.constant(false) );
98 q.answer.forEach( idx => { input[idx] = true; });
99 return input;
100 });
101 this.students = s.students;
102 this.students.forEach( s => { s.present = true; }); //a priori...
103 this.stage = 1;
104 socket = io.connect("/", {
105 query: "aid=" + this.assessment._id + "&secret=" + s.secret
106 });
107 socket.on(message.studentBlur, m => {
108 const sIdx = this.students.findIndex( item => { return item.number == m.number; });
109 Vue.set(this.students, sIdx, Object.assign({},this.students[sIdx],{blur: true}));
110 //this.students[sIdx].blur = true;
111 });
112 socket.on(message.studentFocus, m => {
113 const sIdx = this.students.findIndex( item => { return item.number == m.number; });
114 this.students[sIdx].blur = false;
115 });
116 socket.on(message.studentResize, m => {
117 const sIdx = this.students.findIndex( item => { return item.number == m.number; });
118 Vue.set(this.students, sIdx, Object.assign({},this.students[sIdx],{resize: true}));
119 //this.students[sIdx].resize = true;
120 });
121 socket.on(message.studentFullscreen, m => {
122 const sIdx = this.students.findIndex( item => { return item.number == m.number; });
123 this.students[sIdx].resize = false;
124 });
125 socket.on(message.studentDisconnect, m => {
126 const sIdx = this.students.findIndex( item => { return item.number == m.number; });
127 Vue.set(this.students, sIdx, Object.assign({},this.students[sIdx],{disco: true}));
128 //this.students[sIdx].disco = true;
129 });
130 socket.on(message.studentConnect, m => {
131 const sIdx = this.students.findIndex( item => { return item.number == m.number; });
132 this.students[sIdx].disco = false;
133 });
134 socket.on(message.newAnswer, m => {
135 let paperIdx = this.assessment.papers.findIndex( item => {
136 return item.number == m.number;
137 });
138 if (paperIdx === -1)
139 {
140 // First answer
141 paperIdx = this.assessment.papers.length;
142 this.assessment.papers.push({
143 number: m.number,
144 inputs: [ ], //other fields irrelevant here
145 });
146 }
147 // TODO: notations not coherent (input / answer... when, which ?)
148 this.assessment.papers[paperIdx].inputs.push(JSON.parse(m.answer)); //input+index
149 });
150 },
151 });
152 },
153 endMonitoring: function() {
154 // In the end, send answers to students
155 // TODO: disable this button until everyone finished (need ability to mark absents)
156 socket.emit(
157 message.allAnswers,
158 { answers: JSON.stringify(this.assessment.questions.map( q => { return q.answer; })) }
159 );
160 },
161 },
162 });