Simplify draft monitoring + sockets logic
authorBenjamin Auder <benjamin.auder@somewhere>
Sat, 3 Feb 2018 21:27:30 +0000 (22:27 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Sat, 3 Feb 2018 21:27:30 +0000 (22:27 +0100)
public/javascripts/monitor.js
sockets.js
views/monitor.pug

index 9f43ce0..5d5f237 100644 (file)
@@ -6,8 +6,6 @@
 // Affiché si (hash du) mdp du cours est correctement entré
 // Doit reprendre les données en base si refresh (sinon : sockets)
 
 // Affiché si (hash du) mdp du cours est correctement entré
 // Doit reprendre les données en base si refresh (sinon : sockets)
 
-// Also buttons "start exam", "end exam" for logged in teacher
-
 let socket = null; //monitor answers in real time
 
 new Vue({
 let socket = null; //monitor answers in real time
 
 new Vue({
@@ -18,6 +16,12 @@ new Vue({
                // Stage 0: unauthenticated (password),
                //       1: authenticated (password hash validated), start monitoring
                stage: 0,
                // Stage 0: unauthenticated (password),
                //       1: authenticated (password hash validated), start monitoring
                stage: 0,
+               answers: {
+                       displayAll: true,
+                       showSolution: true, //TODO: allow to hide, to let teachers search too
+                       inputs: [ ],
+                       index : -1,
+               },
        },
        methods: {
                // stage 0 --> 1
        },
        methods: {
                // stage 0 --> 1
@@ -25,54 +29,26 @@ new Vue({
                        $.ajax("/start/monitoring", {
                                method: "GET",
                                data: {
                        $.ajax("/start/monitoring", {
                                method: "GET",
                                data: {
-                                       password: this.,
+                                       password: this.password,
                                        aname: examName,
                                        cname: courseName,
                                        aname: examName,
                                        cname: courseName,
+                                       initials: initials,
                                },
                                dataType: "json",
                                success: s => {
                                        if (!!s.errmsg)
                                                return this.warning(s.errmsg);
                                },
                                dataType: "json",
                                success: s => {
                                        if (!!s.errmsg)
                                                return this.warning(s.errmsg);
+                                       this.assessment = JSON.parse(s.assessment);
                                        this.stage = 1;
                                        this.stage = 1;
-                               },
-                       });
-               },
-               // TODO: 2-level sockets, for prof and monitors
-                                       socket = io.connect("/" + assessment.name, {
-                                               query: "number=" + this.student.number + "&password=" + this.password
+                                       socket = io.connect("/", {
+                                               query: "aid=" + this.assessment._id + "&secret=" + s.secret
+                                       });
+                                       socket.on(message.newAnswer, m => {
+                                               let paperIdx = this.assessment.papers.findIndex( item => {
+                                                       return item.number == m.number;
+                                               });
+                                               this.assessment.papers[paperIdx].inputs.push(m.input); //answer+index
                                        });
                                        });
-                                       socket.on(message.allAnswers, this.setAnswers);
-                                       initializeStage2(s.questions, s.paper);
-                               },
-                       });
-               },
-               // stage 2 --> 3 (or 4)
-               // from a message by statements component, or time over
-               // TODO: also function startAssessment (for main teacher only)
-               endAssessment: function() {
-                       // Set endTime, destroy password
-                       $("#leftButton, #rightButton").show();
-                       if (assessment.mode == "open")
-                       {
-                               this.stage = 4;
-                               return;
-                       }
-                       $.ajax("/end/assessment", {
-                               method: "GET",
-                               data: {
-                                       aid: assessment._id,
-                                       number: this.student.number,
-                                       password: this.student.password,
-                               },
-                               dataType: "json",
-                               success: ret => {
-                                       if (!!ret.errmsg)
-                                               return this.warning(ret.errmsg);
-                                       assessment.conclusion = ret.conclusion;
-                                       this.stage = 3;
-                                       delete this.student["password"]; //unable to send new answers now
-                                       socket.disconnect();
-                                       socket = null;
                                },
                        });
                },
                                },
                        });
                },
index d743a10..eeda127 100644 (file)
@@ -3,97 +3,46 @@ const params = require("./config/parameters");
 const AssessmentEntity = require("./entities/assessment");
 const ObjectId = require("bson-objectid");
 
 const AssessmentEntity = require("./entities/assessment");
 const ObjectId = require("bson-objectid");
 
-// TODO: when teacher connect on monitor, io.of("appropriate namespace").on(connect student) { ... }
-// --> 2 sockets on monitoring page: one with ns "/" et one dedicated to the exam, triggered after the first
-// --> The monitoring page should not be closed during exam (otherwise monitors won't receive any more data)
-
-// TOOD need to re-introduce disconnections count + time (showed in monitoring and stored
-
-function examRoom(socket) {
-       let students = { };
-       const aid = ObjectId(socket.handshake.query.aid);
-
-       // Student or monitor stuff
-       const isTeacher = !!socket.handshake.query.secret && socket.handshake.query.secret == params.secret;
-
-       if (isTeacher)
-       {
-               socket.on(message.newAnswer, m => { //got answer from student
-                       socket.emit(message.newAnswer, m);
-               });
-               socket.on(message.allAnswers, m => { //send feedback to student (answers)
-                       if (!!students[m.number]) //TODO: namespace here... room quiz
-                               socket.broadcast.to(students[m.number]).emit(message.allAnswers, m);
-               });
-               socket.on("disconnect", m => {
-                       // Reset student array if no more active teacher connections (TODO: condition)
-                       students = { };
-               });
-       }
-
-       else //student
-       {
-               const number = socket.handshake.query.number;
-               const password = socket.handshake.query.password;
-               AssessmentEntity.checkPassword(aid, number, password, (err,ret) => {
-                       if (!!err || !ret)
-                               return; //wrong password, or some unexpected error...
-                       // Prevent socket connection (just ignore) if student already connected
-                       if (!!students[number])
-                               return;
-                       students[number] = {
-                               sid: socket.id,
-                               password: password,
-                       };
-                       socket.on(message.allAnswers, () => { //got all answers from teacher
-                               socket.emit(message.allAnswers, m);
+module.exports = function(io)
+{
+       io.of("/").on("connection", socket => {
+               const aid = socket.handshake.query.aid;
+               socket.join(aid);
+               // Student or monitor connexion
+               const isTeacher = !!socket.handshake.query.secret && socket.handshake.query.secret == params.secret;
+               if (isTeacher)
+               {
+                       socket.on(message.newAnswer, m => { //got answer from student
+                               socket.emit(message.newAnswer, m);
                        });
                        });
-                       socket.on("disconnect", () => {
-                               // ..
-                               //TODO: notify monitor (highlight red), redirect
+                       socket.on(message.allAnswers, m => { //send feedback to student (answers)
+                               if (!!students[m.number]) //TODO: namespace here... room quiz
+                                       socket.broadcast.to(aid).emit(message.allAnswers, m);
                        });
                        });
-                       // NOTE: nothing on disconnect --> teacher disconnect trigger students cleaning
-               });
-       }
-}
-
-module.exports = function(io) {
-
-       // NOTE: if prof connected with 2 tabs and close 1, quizz should not break, thus following counter
-       let namespaces = { };
-
-       io.of("/").on("connection", socketProf => {
-               function closeQuizz(fullPath) {
-                       namespaces[fullPath].counter--;
-                       if (namespaces[fullPath].counter == 0)
-                       {
-                               // https://stackoverflow.com/questions/26400595/socket-io-how-do-i-remove-a-namespace
-                               const connectedSockets = Object.keys(namespaces[fullPath].nsp.connected);
-                               connectedSockets.forEach( sid => {
-                                       namespaces[fullPath].nsp.connected[sid].disconnect();
-                               });
-                               namespaces[fullPath].nsp.removeAllListeners();
-                               delete io.nsps[fullPath];
-                       }
                }
                }
-               // Only prof account can connect default namespace
-               socketProf.on(message.startQuizz, m => {
-                       // m contient quizz ID + fullPath (initials+path+name)
-                       const quizzNamespace = io.of(m.fullPath);
-                       if (!namespaces[m.fullPath])
-                       {
-                               namespaces[m.fullPath] = { nsp:quizzNamespace, counter:1 };
-                               quizzNamespace.on("connection", quizzRoom); //après ça : prof can connect in quizz too
-                               socketProf.emit(message.quizzReady);
-                               socketProf.on(message.endQuizz, m2 => {
-                                       closeQuizz(m.fullPath);
+               else //student
+               {
+                       const number = socket.handshake.query.number;
+                       const password = socket.handshake.query.password;
+                       AssessmentEntity.checkPassword(ObjectId(aid), number, password, (err,ret) => {
+                               if (!!err || !ret)
+                                       return; //wrong password, or some unexpected error...
+                               // TODO: Prevent socket connection (just ignore) if student already connected
+//                             io.of('/').in(aid).clients((error, clients) => {
+//                                     if (error)
+//                                             throw error;
+//                                     if (clients.some( c => { return c. .. == number; }))
+//                                             // Problem: we just have a list of socket IDs (not handshakes)
+//                             });
+                               // TODO: next is conditional to "student not already taking the exam"
+                               socket.on(message.allAnswers, () => { //got all answers from teacher
+                                       socket.emit(message.allAnswers, m);
                                });
                                });
-                               socketProf.on("disconnect", m2 => {
-                                       closeQuizz(m.fullPath); //TODO: this should delete all students in array
+                               socket.on("disconnect", () => {
+                                       //TODO: notify monitor (grey low opacity background)
+                                       //Also send to server: discoTime in assessment.papers ...
                                });
                                });
-                       }
-                       else
-                               namespaces[m.fullPath]++;
-               });
+                       });
+               }
        });
 }
        });
 }
index f4f2e3c..5a64e89 100644 (file)
@@ -19,11 +19,11 @@ block content
                                                        label(for="password") Password
                                                        input#password(type="password" v-model="password" @keyup.enter="startMonitoring()")
                                                button.waves-effect.waves-light.btn(@click="startMonitoring()") Send
                                                        label(for="password") Password
                                                        input#password(type="password" v-model="password" @keyup.enter="startMonitoring()")
                                                button.waves-effect.waves-light.btn(@click="startMonitoring()") Send
-                               #stage2(v-show="stage==1")
+                               #stage1(v-show="stage==1")
                                        .card
                                                .introduction(v-html="assessment.introduction")
                                        .card
                                        .card
                                                .introduction(v-html="assessment.introduction")
                                        .card
-                                               statements(:assessment="assessment" :student="student" :stage="stage" :inputs="inputs" @gameover="endAssessment" @warning="warning")
+                                               statements(:questions="assessment.questions" :answers:"answers")
                                        .card
                                                .conclusion(v-html="assessment.conclusion")
 
                                        .card
                                                .conclusion(v-html="assessment.conclusion")