UNPKG

quizzer

Version:

Quizzer is a webserver for collaborative writing lab support. Based on a _fail early, fail often? approach to written language, the tool is particularly suited to second-language learners. The workflow (essay - error - quiz - exam) treats mistakes as an o

269 lines (255 loc) 12.4 kB
(function () { var cogClass = function () {}; cogClass.prototype.exec = function (params, request, response) { var oops = this.utils.apiError; var classID = params.classid; var quizNumber = params.quizno; var pathName = params.pathname; var forcemail = params.forcemail; var sys = this.sys; // var hostname var hostname = this.sys.proxy_hostname; // var port var port = ':' + this.sys.real_port; var email_account = this.sys.email_account; var studentCount = 0; var mailData = {}; // Hack for reverse proxy support var stub = '/'; if (pathName && pathName !== '/') { port = ''; stub = pathName.replace(/(.*\/).*/, '$1/quiz.html'); } var templateMail = sys.fs.readFileSync('quiz-message.txt').toString(); var templatePastQuizzes = sys.fs.readFileSync('quiz-message-past-quizzes.txt').toString(); var templateAnswered = sys.fs.readFileSync('quiz-message-answered.txt').toString(); var templateUnanswered = sys.fs.readFileSync('quiz-message-unanswered.txt').toString(); var templateLink = ' http://@@HOSTNAME@@@@PORT@@@@STUB@@?studentid=@@STUDENT_ID@@&studentkey=@@STUDENT_KEY@@&classid=@@CLASS_ID@@&quizno=@@QUIZ_NUMBER@@\n'; // Okay. // Get the class name // Get a list of recipient students // update recipient keys // For each student, get a list of quizzes completed // For each student, get a list of quizzes not yet completed // Fire off the mailing beginTransaction(); function beginTransaction(){ sys.db.run('BEGIN TRANSACTION',function(err){ if (err){return oops(response,err,'quiz/sendquiz(1)')} getClassName(); }); }; function getClassName(){ var sql = 'SELECT name FROM classes WHERE classID=?;'; sys.db.get(sql,[classID],function(err,row){ if (err||!row){return oops(response,err,'quiz/sendquiz(2)')} mailData.className = row.name; getStudents(); }); }; function getStudents(){ mailData.students = []; // This will send links to exams as well. Editing will be blocked, view of results is permitted. var sql = 'SELECT memberships.studentID,studentKey,students.name,email,last_key_date,classes.name AS className ' + 'FROM memberships ' + 'JOIN students USING(studentID) ' + 'JOIN classes ON classes.classID=memberships.classID ' + 'JOIN quizzes ON quizzes.classID=memberships.classID ' + 'WHERE memberships.classID=? AND quizzes.quizNumber=? ' + 'AND (' + 'sent=0 ' + 'OR (' + 'sent=1 AND (' + 'last_mail_date IS NULL ' + "OR JULIANDAY('now')-JULIANDAY(last_mail_date)>6" + ') ' + 'AND memberships.studentID NOT IN (' + 'SELECT studentID ' + 'FROM quizzes ' + 'NATURAL JOIN questions ' + 'NATURAL JOIN answers ' + 'WHERE quizzes.classID=? ' + 'AND quizNumber=? ' + 'GROUP BY studentID' + ')' + ')' + ');'; sys.db.all(sql,[classID,quizNumber,classID,quizNumber],function(err,rows){ if (err||!rows){return oops(response,err,'quiz/sendquiz(3)')} for (var i=0,ilen=rows.length;i<ilen;i+=1) { var row = rows[i]; mailData.students.push(row); } updateStudentKeys(0,rows.length); }); }; function updateStudentKeys (pos,limit) { if (pos === limit) { getQuizLinks(0,limit); return; } var student = mailData.students[pos]; var studentID = student.studentID; var studentKey; // Set default key value if (student.studentKey) { studentKey = student.studentKey; } else { studentKey = sys.getRandomKey(8,36); } // Update key if more than two weeks have passed since last bump var lastKeyDate; if (student.last_key_date) { lastKeyDate = new Date(student.last_key_date); } else { lastKeyDate = new Date("1970-01-01"); } var nowDate = new Date(); if (((nowDate - lastKeyDate)/1000/60/60/24) > 14) { studentKey = sys.getRandomKey(8,36); student.last_key_date = sys.getDateString(nowDate); } sys.db.run('UPDATE memberships SET studentKey=? WHERE classID=? AND studentID=?',[studentKey,classID,studentID],function(err){ if (err) {return oops(response,err,'quiz/sendquiz(4)')}; // Good, so this does that. Flag as sent, and send the mail message. sys.membershipKeys[classID][studentID] = studentKey; mailData.students[pos].studentKey = studentKey; updateStudentKeys(pos+1,limit); }); }; function getQuizLinks(pos,limit) { if (pos === limit) { response.writeHead(500, {'Content-Type': 'application/json'}); response.end(JSON.stringify(['success'])); sendMail(0,limit); return; } // Get quizzes that have been answered by a given student // and quizzes that have not been. // This will include exams. Editing will be denied, viewing permitted. var sql = 'SELECT quizNumber,' + 'CASE WHEN COUNT(answers.answerID)>0 THEN 1 ELSE 0 END AS answered ' + 'FROM students ' + 'JOIN quizzes ' + 'JOIN questions USING(quizID) ' + 'LEFT JOIN answers ON answers.studentID=students.studentID AND answers.questionID=questions.questionID ' + 'WHERE classID=? AND students.studentID=? AND NOT quizNumber=? AND sent=1 ' + 'GROUP BY quizzes.quizID;' var studentID = mailData.students[pos].studentID; var studentID = mailData.students[pos].studentID; mailData.students[pos].answered = []; mailData.students[pos].unanswered = []; sys.db.all(sql,[classID,studentID,quizNumber],function(err,rows){ if (err||!rows) {return oops(response,err,'quiz/sendquiz(5)')}; for (var i=0,ilen=rows.length;i<ilen;i+=1) { var row = rows[i]; if (row.answered == 1) { mailData.students[pos].answered.push(row.quizNumber); } else { mailData.students[pos].unanswered.push(row.quizNumber); } } getQuizLinks(pos+1,limit); }); }; function sendMail (pos,limit) { if (pos === limit) { refreshDateStamps(0,limit); return; } var student = mailData.students[pos]; var mailText = templateMail .replace(/@@NAME@@/,student.name) .replace(/@@STUDENT_ID@@/g,student.studentID) .replace(/@@STUDENT_KEY@@/g,student.studentKey) .replace(/@@QUIZ_NUMBER@@/g,quizNumber) .replace(/@@HOSTNAME@@/g,hostname) .replace(/@@PORT@@/g,port) .replace(/@@STUB@@/g,stub) .replace(/@@CLASS_ID@@/g,classID) if (student.answered.length || student.unanswered.length) { mailText = mailText.replace(/@@PAST_QUIZZES@@/g,templatePastQuizzes); var unansweredQuizText = ''; if (student.unanswered.length) { var unansweredQuizLinks = ''; for (var i=0,ilen=student.unanswered.length;i<ilen;i+=1) { var oldQuizNumber = student.unanswered[i]; var link = templateLink .replace(/@@NAME@@/,student.name) .replace(/@@STUDENT_ID@@/g,student.studentID) .replace(/@@STUDENT_KEY@@/g,student.studentKey) .replace(/@@QUIZ_NUMBER@@/g,oldQuizNumber) .replace(/@@HOSTNAME@@/g,hostname) .replace(/@@PORT@@/g,port) .replace(/@@STUB@@/g,stub) .replace(/@@CLASS_ID@@/g,classID) unansweredQuizLinks += link; } unansweredQuizText = templateUnanswered.replace(/@@UNANSWERED_QUIZZES@@/,unansweredQuizLinks); } mailText = mailText.replace(/@@UNANSWERED@@/,unansweredQuizText); var answeredQuizText = ''; if (student.answered.length) { var answeredQuizLinks = ''; for (var i=0,ilen=student.answered.length;i<ilen;i+=1) { var oldQuizNumber = student.answered[i]; var link = templateLink .replace(/@@NAME@@/,student.name) .replace(/@@STUDENT_ID@@/g,student.studentID) .replace(/@@STUDENT_KEY@@/g,student.studentKey) .replace(/@@QUIZ_NUMBER@@/g,oldQuizNumber) .replace(/@@HOSTNAME@@/g,hostname) .replace(/@@PORT@@/g,port) .replace(/@@STUB@@/g,stub) .replace(/@@CLASS_ID@@/g,classID) answeredQuizLinks += link; } answeredQuizText = templateAnswered.replace(/@@ANSWERED_QUIZZES@@/,answeredQuizLinks); } mailText = mailText.replace(/@@ANSWERED@@/,answeredQuizText); } else { mailText = mailText.replace(/@@PAST_QUIZZES@@/g,''); } // console.log(mailText); sys.mailer.sendMail({ text: mailText, from: "Instructor <" + email_account + ">", to: student.email, subject: mailData.className + ': Quiz ' + quizNumber }, function(err, message) { if (err) { console.log(err); }; sendMail(pos+1,limit); }); //sendMail(pos+1,limit); }; function refreshDateStamps (pos,limit) { if (pos === limit) { updateSentFlag(); return; } var studentID = mailData.students[pos].studentID; // Set last_key_date by hand, so that key bumps can be throttled to every two weeks. var lastKeyDate = mailData.students[pos].last_key_date; sys.db.run('UPDATE memberships SET last_mail_date=DATE("now"),last_key_date=? WHERE classID=? AND studentID=?',[lastKeyDate,classID,studentID],function(err){ if (err) {return oops(response,err,'quiz/sendquiz(6)')}; refreshDateStamps(pos+1,limit); }); } function updateSentFlag () { sys.db.run('UPDATE quizzes SET sent=1 WHERE classID=? AND quizNumber=?',[classID,quizNumber],function(err){ if (err) {return oops(response,err,'quiz/sendquiz(7)')}; //console.log("Updated SENT flag"); endTransaction(); }); }; function endTransaction () { sys.db.run('END TRANSACTION',function(err){ if (err) {return oops(response,err,'quiz/sendquiz(8)')}; console.log("Sent quiz mail"); }); }; }; exports.cogClass = cogClass; })();