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

213 lines (194 loc) 8.47 kB
(function () { var initClass = function (config,mailer,scheduler) { this.config = config; this.config.admin = {}; this.config.membershipKeys = {}; this.config.scheduler = scheduler; var fs = require('fs'); var sqlite3 = require('sqlite3').verbose(); var events = require("events"); this.config.utf8 = require("utf8"); var eventEmitter = new events.EventEmitter(); var db; // Getting sane about this // When the DB is ready, we emit an event // The event is picked up, and items are scheduled by scheduleAllMail // The schedule controllers are set on config(sys).admin // When an event is rescheduled, the controller is destroyed, and we reschedule with scheduleMail // So we need those two functions, and that's it // They should just be bare objects, available on scheduler // Nothing else is required try { var fh = fs.openSync('quizzer-' + config.real_port + '.sqlite', 'r') fs.close(fh); openDatabase(getStarted); } catch (e) { if (e.code === 'ENOENT') { openDatabase(initEverything); } else { throw e; } } function initEverything () { var sql = fs.readFileSync(__dirname + '/../resource/schema.sql').toString(); sql += "INSERT INTO version VALUES ('quizzer'," + getNewSchemaVersion() + ");" db.exec(sql,function(err){ if (err) throw "Error initializing database: " + err; setupAdmin(showUrl); }); }; function setupAdmin(callback) { db.run("INSERT OR IGNORE INTO admin VALUES (NULL,?,?,?,?,NULL)",['admin',getRandomKey(8,36), 1,0], function (err) { if (err) console.log("Error in setupAdmin(): "+err); callback(); }); }; function openDatabase (callback) { eventEmitter.on('databaseIsReady',function (db) { scheduler.scheduleAllMail(db); }); db = new sqlite3.Database('quizzer-' + config.real_port + '.sqlite'); process.on('SIGINT', function() { console.log('\nGot SIGINT. So that\'s it, then.'); try { db.close(); } catch (e) { console.log("Database already closed, apparently"); } process.exit(); }); process.on('exit', function() { try { db.close(); } catch (e) {} console.log('About to exit.'); }); setForeignKeyConstraints(callback); }; function setForeignKeyConstraints(callback) { db.run('PRAGMA foreign_keys = ON;',function(err){ if (err) {throw 'Error while turning on foreign key constraints: '+err}; callback(); }); }; // Check if we're versioning yet function getStarted () { db.get("SELECT name FROM sqlite_master WHERE type=? AND name=?", ['table','version'], function(err,row){ if (err) { throw 'Error: init(1): '+err; }; if (!row || !row.name) { createVersionTable(); } else { checkSchemaVersion(); } }); }; function createVersionTable () { db.exec( "CREATE TABLE version (schema TEXT PRIMARY KEY, version INT NOT NULL);" + "CREATE INDEX schema ON version(schema);" + "INSERT INTO version VALUES ('quizzer',1);", function(err){ if (err) {throw 'Error: init(2): '+err}; checkSchemaVersion(); } ); }; function getNewSchemaVersion () { var sql = fs.readFileSync(__dirname + '/../resource/schema.sql').toString(); var schemaVersion = parseInt(sql.match(/^-- ([0-9]+)/)[1],10); return schemaVersion; }; function checkSchemaVersion () { var schemaVersion = getNewSchemaVersion(); db.get('SELECT version FROM version WHERE schema=?',['quizzer'],function(err,row){ if (err||!row) { db.run('INSERT INTO version VALUES(?,?)',['quizzer',dbVersion],function(){ checkSchemaVersion2(schemaVersion,schemaVersion); }); } else { var dbVersion = parseInt(row.version,10); checkSchemaVersion2(dbVersion,schemaVersion); } }); }; function checkSchemaVersion2 (dbVersion,schemaVersion) { if (schemaVersion > dbVersion) { var upgraderModule = require('./upgrades.js'); var upgrader = new upgraderModule.upgraderClass(db,dbVersion,schemaVersion); upgrader.run(showUrl); } else { showUrl(); } }; /* Utility stuff */ function purgeStrings () { var sql = "DELETE FROM strings" + " WHERE stringID NOT IN" + " (SELECT DISTINCT stringID FROM questions" + " UNION SELECT stringID FROM choices" + " UNION SELECT stringID FROM comments);" db.run(sql,function(err){ if (err) console.log("Error in purgeStrings(): "+err); }) }; function showUrl () { purgeStrings(); db.all('SELECT adminID,name,adminKey,role FROM admin',[],function(err,rows){ for (var i=0,ilen=rows.length;i<ilen;i+=1) { var row = rows[i]; var port = ':' + config.real_port; if (config.proxy_hostname.match(/.*\..*/) && config.proxy_hostname !== '127.0.0.1') { port = ''; } if (row.name === 'admin') { console.log("Admin URL: http://" + config.proxy_hostname + port + '/?admin=' + row.adminKey); } config.admin[row.adminKey] = {name:row.name,role:row.role,id:row.adminID,sched:null}; if (row.role == 1) { console.log("Adding admin role"); } else if (row.role == 2) { console.log("Adding commenter '" + row.name + "' with URL http://" + config.proxy_hostname + port + '/?commenter=' + row.adminKey); } } // Emit an event to say the DB is ready and waiting eventEmitter.emit('databaseIsReady',db); }); db.all('SELECT classID,studentID,studentKey FROM memberships',[],function(err,rows){ for (var i=0,ilen=rows.length;i<ilen;i+=1) { var row = rows[i]; var mk = config.membershipKeys; if (!mk[row.classID]) { mk[row.classID] = {}; } if (!mk[row.classID][row.studentID]) { mk[row.classID][row.studentID] = {}; } mk[row.classID][row.studentID] = row.studentKey; } console.log("Loaded class membership keys"); }); }; function getRandomKey(len, base) { // Modified from http://jsperf.com/random-md5-hash-implementations len = len ? len : 16; base = base ? base : 16; var _results; _results = []; for (var i=0;i<len;i+=1) { _results.push((Math.random() * base | 0).toString(base)); } return _results.join(""); }; this.config.getRandomKey = getRandomKey; this.config.db = db; }; initClass.prototype.getInit = function () { return this.config; }; exports.initClass = initClass; })();