From: Benjamin Auder Date: Sat, 3 Feb 2018 21:27:30 +0000 (+0100) Subject: Simplify draft monitoring + sockets logic X-Git-Url: https://git.auder.net/variants/img/current/assets/css/doc/common.css?a=commitdiff_plain;h=f6648c37a3c13efbbc3b8c03c6ff725984c98843;p=qomet.git Simplify draft monitoring + sockets logic --- diff --git a/public/javascripts/monitor.js b/public/javascripts/monitor.js index 9f43ce0..5d5f237 100644 --- a/public/javascripts/monitor.js +++ b/public/javascripts/monitor.js @@ -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) -// Also buttons "start exam", "end exam" for logged in teacher - 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, + answers: { + displayAll: true, + showSolution: true, //TODO: allow to hide, to let teachers search too + inputs: [ ], + index : -1, + }, }, methods: { // stage 0 --> 1 @@ -25,54 +29,26 @@ new Vue({ $.ajax("/start/monitoring", { method: "GET", data: { - password: this., + password: this.password, aname: examName, cname: courseName, + initials: initials, }, dataType: "json", success: s => { if (!!s.errmsg) return this.warning(s.errmsg); + this.assessment = JSON.parse(s.assessment); 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; }, }); }, diff --git a/sockets.js b/sockets.js index d743a10..eeda127 100644 --- a/sockets.js +++ b/sockets.js @@ -3,97 +3,46 @@ const params = require("./config/parameters"); 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]++; - }); + }); + } }); } diff --git a/views/monitor.pug b/views/monitor.pug index f4f2e3c..5a64e89 100644 --- a/views/monitor.pug +++ b/views/monitor.pug @@ -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 - #stage2(v-show="stage==1") + #stage1(v-show="stage==1") .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")