const getPresentationStats = coroutine(function *getPresentationStats(req, res, next) { let slideshow ; try{ slideshow = yield Slideshow.findById(req.params.presentationId).exec(); }catch(err){ logger.error("Presentation %s not found", req.params.presentationId); logger.error(err.message, { err: err.stack }); res.status(404); return res.render('404', {'msg': 'Presentation not found'}); } try{ const statsObj = yield stats.getPresentationStats(slideshow); statsObj.presentationId = req.params.presentationId; statsObj.username = req.user.username; statsObj.host = req.app.locals.urlHost; statsObj.port = req.app.locals.urlPort; return res.render('presentationStats', statsObj); }catch(err){ logger.error("Presentation %s not found", req.params.presentationId); logger.error(err.message, { err: err.stack }); next(err) } });
Session.findOne({_id: socket.request.sessionId}, function(err, session){ if(err){ logger.error(err.message, { err: err.stack }); }else{ try{ var sessionFlow = flow[session.flow](socketUtils); sessionFlow.handleSocketEvent('asq:goto', socket, evt); }catch(err){ logger.error(err.message, { err: err.stack }); } } })
var uploadPresentation = coroutine(function *uploadPresentationGen (req, res, next){ try{ let owner_id = req.user._id; let presentationFramework = req.body.presentationFramework; var name = req.body.title || req.files.upload.name; var uploadFilePath = req.files.upload.path; const slideshowid = yield upload.createPresentationFromFile( owner_id, name, presentationFramework, uploadFilePath); const slideshow = yield Slideshow.findById(slideshowid).lean().exec(); logger.log({ owner_id: req.user._id, slideshow: slideshow._id, file_path: uploadFilePath, file_name: name }, "uploaded presentation"); res.redirect(303, ['/', req.user.username, '/presentations/?alert=', slideshow.title, ' uploaded successfully!&type=success'] .join('')); }catch(err){ console.log(err.stack); logger.error({ err: err, owner_id: req.user._id, file_path: uploadFilePath, file_name: name }, "error uploading presentation"); } });
archiveStream.on('error', function(err) { logger.error({ err: err, presentation: presentation, }, "creating zip archive failed"); throw Boom.badImplementation(err.message); });
var putPresentation = coroutine(function *putPresentationGen(req, res, next) { try{ let owner_id = req.user._id var name = req.body.title //if name is null of undefined it won't be updated let presentationFramework = req.body.presentationFramework; //if presentationFramework is null of undefined it won't be updated var zipPath = req.files.upload.path; let slideshow = yield Slideshow.findById(req.params.presentationId).exec(); if(! slideshow){ //treat it as new upload return next(); } // TODO: handle this better if(slideshow.owner.toString() !== owner_id.toString() ){ next(new Error("You are not the owner of this presentation")) } var options = { preserveSession: req.query.preserveSession } const slideshowid = yield upload.updatePresentationFromZipArchive( slideshow._id, name, presentationFramework, zipPath, options); slideshow = yield Slideshow.findById(slideshowid).lean().exec(); //remove zip file yield fs.unlinkAsync(zipPath); // HACK: empty dust cache // if multiple servers are used then the rest of the servers WILL NOT // get notified :-( // TODO: should only delete the updated presentation path cons.clearCache() logger.log({ owner_id: req.user._id, slideshow: slideshow._id, file_path: zipPath, file_name: name }, "updated presentation from zip"); res.redirect(303, ['/', req.user.username, '/presentations/?alert=', slideshow.title, ' updated successfully!&type=success'] .join('')); }catch(err){ console.log(err.stack); logger.error({ err: err, owner_id: req.user._id, file_path: zipPath, file_name: name }, "error updating presentation from zip"); } });
}).on('error', function (error) { logger.error({ err: error, source: source, destination: destination }, "error generating zipped HTML from PDF file"); reject(error); }).on('end', function (res) {
getPresentationsByCourse : coroutine(function *getPresentationsByCourseGen(userId, Session, Slideshow) { logger.debug('getPresentationsByCourse'); var slidesByCourse = null; //to evaluate as false in dustjs try{ var sessionPromise = Session.getLiveSessions(userId); var slideshowPromise = Slideshow.find({ owner : userId }, '_id title course lastSession lastEdit thumbnails fontFaces pdfFile conversionStatus').exec(); var results = yield Promise.all([sessionPromise, slideshowPromise]) var sessions = results[0]; var slides = results[1]; var live = {}; sessions.forEach(function(session){ live[session.slides.toString()]=session._id; }); if (typeof slides != "undefined" && slides != null && slides.length > 0) { slidesByCourse = {}; for (var i = 0; i < slides.length; i++) { var slideshow = slides[i].toJSON(); if (live.hasOwnProperty(slideshow._id)) { slideshow.live = live[slideshow._id] }else{ slideshow.live = null; } if (!slidesByCourse.hasOwnProperty(slideshow.course)) { slidesByCourse[slideshow.course] = []; } slideshow.lastEdit = moment( slideshow.lastEdit) .format('DD.MM.YYYY HH:mm'); if( slideshow.lastSession){ slideshow.lastSession = moment( slideshow.lastSession) .format('DD.MM.YYYY HH:mm'); } slideshow.isConverting = !(!slideshow.pdfFile || 0 === slideshow.pdfFile.length) && (slideshow.conversionStatus !== 'done'); slideshow.conversionMessage = getConverstionMsg(slideshow.conversionStatus); delete slideshow.pdfFile; delete slideshow.conversionStatus; slidesByCourse[slideshow.course].push(slideshow); } } }catch(err){ logger.error( err.toString(), { err: err.stack }); } return slidesByCourse; })
Slideshow.findById(req.params.presentationId, function(err, slideshow) { if (err) { logger.error(err.toString()); } else { /* Load presentation html file */ fs.readFileAsync(slideshow.presenterFile, 'utf-8').then(function(data) { //Array with one field per slide. Each field has questions and stats const slides = []; $ = cheerio.load(data); $('.step').each(function(slide) { //Get questions on this slide. Get their text and push it into an array const questionsOnSlide = []; $(this).find('.assessment').each(function(el) { var text = $(this).find('.stem').first().text(); if (text == undefined || text.length == 0) { text = 'Missing question text'; } questionsOnSlide.push(text); }); //Get stats on this slide. Get their text and push it into an array var statsOnSlide = new Array(); $(this).find('.stats').each(function(el) { var text = $(this).find('.stem').first().text(); if (text == undefined || text.length == 0) { text = 'Missing question text'; } statsOnSlide.push(text); }); //Push questions and stats on this slide into array slides.push({ questions : questionsOnSlide, stats : statsOnSlide }); }); res.render('edit', { title : slideshow.title, slides : slides, slideshow : slideshow, }); }, function(error){ //TODO How about handling the error? logger.error('This is an error left unhandeled...'); logger.error(error.toString()); }); } });
exports.getSessionStats = coroutine(function *getSessionStatsGen(req, res, next) { var session ; try{ var session = yield Session.findById(req.params.sessionId).exec(); }catch(err){ logger.error("Session %s not found", req.params.sessionId); logger.error(err.message, { err: err.stack }); res.status(404); return res.render('404', {'msg': 'Presentation not found'}); } try{ var statsObj = yield stats.getSessionStats(session); var endDate = session.endDate ? moment(session.endDate).format('MMMM Do YYYY, h:mm:ss a') : undefined; statsObj.session = { id : session._id.toString(), startDate: moment(session.startDate).format('MMMM Do YYYY, h:mm:ss a'), endDate : endDate }; statsObj.username = req.user.username; statsObj.questionWidth = (100/statsObj.questions.length); if(session.endData === null || session.endData === undefined){ statsObj.host = req.app.locals.urlHost; statsObj.port = req.app.locals.urlPort; statsObj.live = true; statsObj.mode = 'ctrl'; } return res.render('sessionStats', statsObj); }catch(err){ logger.error("Session %s not found", req.params.sessionId); logger.error(err.message, { err: err.stack }); next(err) } });
Question.find({_id : {$in: this.questions}}, (err, questions) => { if (err) { done(err); } else if (questions.length !== this.questions.length) { let err = new Error( 'All question items should have a real Question _id'); logger.error({ err: err, 'presentation_id': this._id, 'questions_length': questions.length, 'this_questions_length': this.questions.length, }, 'error on saving presentation'); return done(err); } done(); });
const terminatePresentation = coroutine(function *terminatePresentationGen(req, res, next) { try{ logger.debug({ owner_id: req.user._id, slideshow: req.params.presentationId }, 'Stopping session'); const userId = req.user._id; const presentationId = req.params.presentationId; const terminatedSessions = yield Session.terminateAllSessionsForPresentation(userId, presentationId) // if sessions has zero length there was no live sessions if (! terminatedSessions.length) { const err404 = Error.http(404, 'No session found', {type:'invalid_request_error'}); throw err404; } res.sendStatus(204); logger.log({ owner_id: req.user._id, slideshow: req.params.presentationId, sessions: terminatedSessions.map( s => s._id.toString()), }, "stopped session"); }catch(err){ logger.error({ err: err, owner_id: req.user._id, sessions: terminatedSessions, }, "error stopping session"); //let error middleware take care of it next(err); } });
var ctrlConnect = coroutine(function *ctrlConnectGen(socket) { try{ var screenName = socket.request.screenName; var sessionId = socket.request.sessionId; var evt = { screenName : screenName }; var session = yield Session.findById(sessionId); if(! session) throw new Error("Could not find session"); ioConnect(socket, 'ctrl'); socketUtils.emitToRoles('asq:ctrl-connected', evt, sessionId, 'ctrl', 'ghost', 'folo'); // async createSessionEvent("ctrl-connected", sessionId, socket.user); socketUtils.sendConnectedClients(sessionId, 'ctrl'); socketUtils.sendProgresses(sessionId); var result = yield hooks.doHook('presenter_connected', { socketId: socket.id, session_id: session._id, presentation_id: session.slides, whitelistId: socket.request.token }); }catch(err){ logger.error({ err: require('util').inspect(err), stack: err.stack }, "err in ctrConnect"); } });
var assessed = coroutine(function *assessedGen(socket, evt) { logger.debug('Assessment'); try{ if (!evt.assessment) { logger.error(new Error('Invalid Submission: Missing or invalid exercise.')); return; } if (!evt.assessment.exercise) { logger.error(new Error('Invalid or missing exercise reference.')); } var assessee = evt.assessment.assessee; var assessor = socket.request.token; var session = yield Session.findOne({_id: socket.request.sessionId}).exec(); var exercise = yield Exercise.findById(evt.assessment.exercise).exec(); var assessments = yield assessment.save(session, evt.assessment, assessor); var progress = yield assessment.job .terminateJobsAndUpdateProgress(session, exercise._id, assessor, assessee); progress = progress.toObject(); // Convert to object to add audience. // Add audience size to progress. progress.audience = socketUtils.getNumOfConnectedClients('/folo', progress.session) var score=0; for (var i = assessments.length - 1; i >= 0; i--) { score += assessments[i].score; }; score = ~~(score/assessments.length); //notify ctrl for the assessment var assessEvt={ exerciseId : exercise._id, assessor: {token: assessor}, assessee: {token: assessee}, score: score } socketUtils.emitToRoles('asq:assess', assessEvt, session._id, 'ctrl'); // Send updated progress to ctrl socketUtils.emitToRoles('asq:submitted', { progress: progress }, progress.session, 'ctrl'); socket.emit('asq:submitted', { exercise : exercise.id, resubmit : false, status : 'success', type : 'assessment' }); var job = yield assessment.job.getNextAssessmentJob(progress.session, exercise, assessor); var jobEvt = { exerciseId: exercise._id, assessor: {token:job.assessor}, assessee: {token:job.assessee} }; socketUtils.emitToRoles('asq:new-assessment-job', jobEvt, session._id, 'ctrl'); var html = yield assessment.render(job); if (html) { socket.emit('asq:assess', { html: html, exercise: exercise.id }); } }catch(err){ logger.error(err.message, { err: err.stack }); } });
function logErrors(err, req, res, next) { logger.error(err.stack); next(err); }
setTimeout(function() { logger.error("Could not close connections in time, forcefully shutting down"); process.exit() }, 10*1000);
.catch(function onError(err) { logger.error( err.toString(), { err: err.stack }); });
function(err){ var where = "@ getLivePresentations"; err = err instanceof Error ? err : new Error(err); logger.error(where + ': ', { error: err.stack }); res.render('500', { where: where , error: err, stack: err.stack }); });
var foloConnect = coroutine(function *foloConnectGen(socket) { try{ var screenName = socket.request.screenName; var sessionId = socket.request.sessionId; var token = socket.request.token; var foloConnectedEvent = { screenName: screenName, token: token } var session = yield Session.findById(sessionId); if(! session) throw new Error("Could not find session"); ioConnect(socket, 'folo'); yield socketUtils.saveConnectionToRedis(token, socket); logger.info('[Redis] saved socket id'); socketUtils.emitToRoles('asq:folo-connected', foloConnectedEvent, sessionId, 'ctrl'); socketUtils.sendConnectedClients(sessionId, 'ctrl'); // async createSessionEvent("folo-connected", sessionId, socket.user); // send progress yield submission.progress.updateConnect(sessionId, token); yield socketUtils.sendProgresses(sessionId); yield socketUtils.sendSubmissions(socket, sessionId, token); // find assessment jobs var jobs = yield AssessmentJob.find({ assessor : token, status : 'active', session : sessionId, }); var exerciseIds = jobs.map(function(job) { return job.exercise }); var dbExerciseIds = yield Answer.distinct('exercise', { answeree : token, session : sessionId, exercise : {$nin: exerciseIds } }).exec(); var exercises = yield Exercise.find({ _id: {$in: dbExerciseIds} }); var jobs = yield Promise.map(exercises, function getNextJob(ex) { return assessment.job.getNextAssessmentJob(sessionId, ex, token); }) jobs = _.remove(_.flatten(jobs), function(j) { return j !== null}); socketUtils.notifyCtrlForNewJobs(jobs); //activate jobs yield Promise.map(jobs, function(j) { return assessment.job.activateJob(j); }); //get and send assessment job html var htmls = yield Promise.map(jobs, function(j) { return assessment.render(j); }); var i = Math.min(jobs.length, htmls.length); while(i--) { if (!! jobs[i] && !! htmls[i]) { socket.emit('asq:assess', { exercise : jobs[i]. exercise, html : htmls[i] }); } } var hookResults = yield hooks.doHook('viewer_connected', { socketId: socket.id, session_id: session._id, presentation_id: session.slides, whitelistId: socket.request.token }); }catch(err){ logger.error({ err: require('util').inspect(err), stack: err.stack }, "err in foloConnect"); } });
function foloDisconnectErr(err) { logger.error('Failed to disconnect client from \'folo\':\n\t' + err.toString(), { err: err.stack }); });
function statDisconnectErr(err) { logger.error('Failed to disconnect client from \'stat\':\n\t' + err.toString()); });
}, function(error){ //TODO How about handling the error? logger.error('This is an error left unhandeled...'); logger.error(error.toString()); });
.catch(function onError(err) { logger.error('Error on terminate session:\n\t' + err.toString()); })
}).catch(function(err){ logger.error('Failed to get user session stats' + err.message, { err: err.stack }); });
var submit = coroutine(function *submitGen(socket, evt) { try{ logger.debug('Submission'); if (!evt.exercise || !evt.exercise.id) { logger.error(new Error('Invalid Submission: Missing or invalid exercise.')); return; } // Notify the sender socket.emit('asq:submitted', { exercise : evt.exercise.id, status : 'processing' }); var token = socket.request.token; var session = yield Session.findOne({_id: socket.request.sessionId}).exec(); //TODO : return object instead of an array var data = yield submission.answer.save(session, token, evt.exercise); var exercise = data[0]; var questions = data[1]; var answers = data[2]; var progress = data[3]; var self = data[4]; var peer = data[5]; socketUtils.sendProgress(progress, session.id); socket.emit('asq:submitted', { exercise : exercise.id, resubmit : exercise.allowResubmit, status : 'success', type : 'answer' }); //see if automatic asssemsent was created var assessments = yield Assessment.find({ session: session._id, exercise: exercise._id, assessee : token, type : 'auto' }, {'_id': 0, 'assessee': '1', 'question': '1', 'answer' : '1', 'score': '1', 'type': '1', 'submittedDate' :'1' }) .populate('question answer') .exec(); assessments.forEach(function(assessment){ socket.emit('asq:assessment', assessment.toObject()); socketUtils.emitToRoles('asq:assessment',assessment.toObject() , session.id, 'ctrl'); }) //check if the user has answered every question var slideshow = yield Slideshow .findById(session.slides) .exec(); var answeredQuestions = yield Answer.aggregate() .match({ session: session._id, answeree: token }) .group({_id : 0, questions: { $addToSet: "$question" }}) .project({ _id: 0 , sameElements: { '$setEquals': [ "$questions", slideshow.questions ] } } ) .exec(); if(answeredQuestions[0].sameElements === true){ var entry = yield WhitelistEntry.findById(token).exec(); entry.sessionData.answeredAllDate = new Date(); entry.markModified('sessionData'); //save user var saved = yield entry.save(); socket.emit('asq:answered-all'); socketUtils.emitToRoles('asq:answered-all', { userId: token }, session.id, 'ctrl'); } //async calculateRankings(session); // Handle peer assessment if (self || peer) { // Add answers for peers // in the case of assessment -> add the answer to the queue var jobs = yield socketUtils.enqueueAnswersForAssessment(session._id, exercise, answers, token); var job = yield assessment.job.getNextAssessmentJob(session._id, exercise, token); job = assessment.job.activateJob(job); var html = yield assessment.render(job); if (!! html) { logger.info('Sending assessment to ' + socket.id); socket.emit('asq:assess', { html: html, exercise: exercise.id }); } // notify ctrl of the assessment socketUtils.notifyCtrlForNewJobs([job]); var newJobs = yield assessment.job.getNextJobForIdleViewers(session._id, exercise); var JobSocketsPairs = yield socketUtils.getSocketsForJobs(newJobs); JobSocketsPairs = yield socketUtils.activateJobsForSockets(JobSocketsPairs); var HtmlSocketsPairs = socketUtils.renderJobsForSockets(JobSocketsPairs); socketUtils.sendHtmlForSockets(HtmlSocketsPairs, exercise.id); socketUtils.notifyCtrlForNewJobs( newJobs); } }catch(err){ logger.error(err.message, { err: err.stack }); } });