From 7db80858d1687e4836fa22bcf6d42471e6d21da7 Mon Sep 17 00:00:00 2001 From: Izaak Schroeder Date: Fri, 28 Feb 2014 12:06:26 -0800 Subject: [PATCH] Adding better run views and stuff. --- assets/scripts/ansi.js | 33 ++----- assets/scripts/assignment.js | 27 +----- assets/scripts/io.js | 22 +++++ assets/scripts/run.js | 6 ++ assets/styles/ui/table.css | 2 +- lib/activator/io.js | 119 ++++++++++++++++++++++++++ lib/workers/evaluation.js | 5 ++ views/run.jade | 21 ++++- views/submission-evaluation-sent.jade | 10 ++- 9 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 assets/scripts/io.js create mode 100644 assets/scripts/run.js create mode 100644 lib/activator/io.js diff --git a/assets/scripts/ansi.js b/assets/scripts/ansi.js index e1f0039..dfb5d95 100644 --- a/assets/scripts/ansi.js +++ b/assets/scripts/ansi.js @@ -26,12 +26,12 @@ define(function() { ] ]; - function Ansi_Up() { + function Ansi() { this.fg = this.bg = null; this.bright = 0; } - Ansi_Up.prototype.escape_for_html = function (txt) { + Ansi.prototype.escape_for_html = function (txt) { return txt.replace(/[&<>]/gm, function(str) { if (str == "&") return "&"; if (str == "<") return "<"; @@ -39,13 +39,13 @@ define(function() { }); }; - Ansi_Up.prototype.linkify = function (txt) { + Ansi.prototype.linkify = function (txt) { return txt.replace(/(https?:\/\/[^\s]+)/gm, function(str) { return "" + str + ""; }); }; - Ansi_Up.prototype.ansi_to_html = function (txt, options) { + Ansi.prototype.ansi_to_html = function (txt, options) { var data4 = txt.split(/\033\[/); @@ -71,7 +71,7 @@ define(function() { return escaped_data; }; - Ansi_Up.prototype.process_chunk = function (text, options) { + Ansi.prototype.process_chunk = function (text, options) { // Are we using classes or styles? options = typeof options == 'undefined' ? {} : options; @@ -130,28 +130,7 @@ define(function() { } }; - // Module exports - return = { - - escape_for_html: function (txt) { - var a2h = new Ansi_Up(); - return a2h.escape_for_html(txt); - }, - - linkify: function (txt) { - var a2h = new Ansi_Up(); - return a2h.linkify(txt); - }, - - ansi_to_html: function (txt, options) { - var a2h = new Ansi_Up(); - return a2h.ansi_to_html(txt, options); - }, - - ansi_to_html_obj: function () { - return new Ansi_Up(); - } - }; + return Ansi; }); \ No newline at end of file diff --git a/assets/scripts/assignment.js b/assets/scripts/assignment.js index ae4f3db..42f7bb9 100644 --- a/assets/scripts/assignment.js +++ b/assets/scripts/assignment.js @@ -1,31 +1,8 @@ -requirejs.config({ - paths: { - 'sockjs': [ 'http://cdn.sockjs.org/sockjs-0.3.min' ] - } -}) - -require(['jquery', 'fuzzy-time', 'spinner', 'sockjs', 'event-emitter', 'util'], function($, fz, Spinner, SockJS, EventEmitter, util) { +require(['jquery', 'fuzzy-time', 'spinner'], function($, fz, Spinner) { //alert('assignment 4 lol!') - function IO(opts) { - EventEmitter.call(this); - //var path = window.location.protocol+'//'+window.location.host+'/io'; - var path = '/io'; - this.socket = new SockJS(path); - this.socket.onopen = function() { - console.log('OPEN') - } - this.socket.onmessage = function() { - console.log('MESSAGE') - } - this.socket.onclose = function() { - console.log('CLOSE') - } - } - util.inherits(IO, EventEmitter); - - var io = new IO(); + $(function() { diff --git a/assets/scripts/io.js b/assets/scripts/io.js new file mode 100644 index 0000000..bcfc2e5 --- /dev/null +++ b/assets/scripts/io.js @@ -0,0 +1,22 @@ + +define(['events', 'util'], function(events, util) { + + function IO(opts) { + EventEmitter.call(this); + //var path = window.location.protocol+'//'+window.location.host+'/io'; + var path = '/io'; + this.socket = new SockJS(path); + this.socket.onopen = function() { + console.log('OPEN') + } + this.socket.onmessage = function() { + console.log('MESSAGE') + } + this.socket.onclose = function() { + console.log('CLOSE') + } + } + util.inherits(IO, events.EventEmitter); + + return IO; +}) \ No newline at end of file diff --git a/assets/scripts/run.js b/assets/scripts/run.js new file mode 100644 index 0000000..b597358 --- /dev/null +++ b/assets/scripts/run.js @@ -0,0 +1,6 @@ + +require(['jquery', 'ansi'], function($, Ansi) { + + // properly render escape sequences + +}); diff --git a/assets/styles/ui/table.css b/assets/styles/ui/table.css index e2eed72..46594eb 100644 --- a/assets/styles/ui/table.css +++ b/assets/styles/ui/table.css @@ -78,7 +78,7 @@ table tr { } table td, table th { - + vertical-align: top; } table tbody td { diff --git a/lib/activator/io.js b/lib/activator/io.js new file mode 100644 index 0000000..81d5211 --- /dev/null +++ b/lib/activator/io.js @@ -0,0 +1,119 @@ + +var WebSocket = require('ws'), + async = require('async'), + express = require('express'), + app = express(); + + +function hybi(req, socket, head, cb) { + // handle premature socket errors + var errorHandler = function() { + try { socket.destroy(); } catch (e) {} + } + socket.on('error', errorHandler); + + // verify key presence + if (!req.get('sec-websocket-key')) + return next({ statusCode: 400, error: 'HYBI_NO_KEY' }) + + // verify version + var version = parseInt(req.get('sec-websocket-version')); + if ([8, 13].indexOf(version) === -1) + return next({ statusCode: 400, error: 'HYBI_INVALID_VERSION', version: version }); + + // verify protocol + var protocols = req.get('sec-websocket-protocol'); + + // verify client + var origin = version < 13 ? req.get('sec-websocket-origin') : req.get('origin'); + + async.serial([ + function authorize(next) { + var info = { + origin: origin, + secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', + req: req + }; + + if (this.options.verifyClient.length == 2) { + this.options.verifyClient(info, function(result) { + if (!result) abortConnection(socket, 401, 'Unauthorized') + else completeHybiUpgrade1(); + }); + return; + } + else if (!this.options.verifyClient(info)) { + abortConnection(socket, 401, 'Unauthorized'); + return; + } + }, + function protocol(next) { + // choose from the sub-protocols + if (typeof self.options.handleProtocols == 'function') { + var protList = (protocols || "").split(/, */); + var callbackCalled = false; + var res = self.options.handleProtocols(protList, function(result, protocol) { + callbackCalled = true; + if (!result) abortConnection(socket, 404, 'Unauthorized') + else completeHybiUpgrade2(protocol); + }); + + if (!callbackCalled) { + // the handleProtocols handler never called our callback + abortConnection(socket, 501, 'Could not process protocols'); + } + return; + } + else { + if (typeof protocols !== 'undefined') { + completeHybiUpgrade2(protocols.split(/, */)[0]); + } + else { + completeHybiUpgrade2(); + } + } + }, + function complete(next) { + var headers = [ + 'HTTP/1.1 101 Switching Protocols', + 'Connection: Upgrade', + 'Upgrade: websocket', + 'Sec-WebSocket-Accept: '+crypto.createHash('sha1') + .update(req.get('sec-websocket-key') + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11") + .digest('base64'), + ]; + + if (typeof protocol != 'undefined') + headers.push('Sec-WebSocket-Protocol: '+protocol); + + socket.setTimeout(0); + socket.setNoDelay(true); + + try { + socket.write(headers.concat('', '').join('\r\n')); + } + catch (e) { + // if the upgrade write fails, shut the connection down hard + try { socket.destroy(); } catch (e) {} + return; + } + + var client = new WebSocket([req, socket, head], { + protocolVersion: version, + protocol: protocol + }); + + // signal upgrade complete + socket.removeListener('error', errorHandler); + next(undefined, client); + } + ], next); +} + + + +app.on('upgrade', function(req, socket, head) { + hybi(req, socket, Buffer(head)); +}); + +module.exports = app; diff --git a/lib/workers/evaluation.js b/lib/workers/evaluation.js index db945e9..f10ef9a 100644 --- a/lib/workers/evaluation.js +++ b/lib/workers/evaluation.js @@ -20,8 +20,13 @@ Job.process(1, function(job, done) { function(next) { job.populate('source', next) }, function(next) { job.source.populate('submission', next) } ], function(err) { + if (err) return done(err); + // TODO: Think about the logic here. + // Only completed runs should get marked + if (job.source.state !== 'complete') return done({ error: 'INCOMPLETE_RUN' }); + EvaluationSettings.findOne({ assignment: job.source.submission.assignment }, function(err, settings) { if (err) return done(err); if (!settings) return done({ error: 'NO_EVALUATION_SETTINGS' }); diff --git a/views/run.jade b/views/run.jade index 4fa8b4f..1849430 100644 --- a/views/run.jade +++ b/views/run.jade @@ -26,8 +26,21 @@ mixin evaluation() div block content - .container - h1 Run #{job.id} + .run(style="padding: 12px; background: #f0f0f0;") + .container + h1 Run #{job.id} + + h2 Log Output + table + tbody + tr + th Type + th Data + each entry in job.logs + tr + td #{entry.type} + td + each line in entry.data.toString('utf8').split('\n') + div #{line} + - each entry in job.logs - div #{entry.data.toString('utf8')} \ No newline at end of file diff --git a/views/submission-evaluation-sent.jade b/views/submission-evaluation-sent.jade index 3c359d3..7a91c35 100644 --- a/views/submission-evaluation-sent.jade +++ b/views/submission-evaluation-sent.jade @@ -6,7 +6,9 @@ block styles block content - .container - h1 Evaluation - p Your submission has been sent for evaluation. - p You've sent in run: #{run.id} \ No newline at end of file + div(style="padding: 12px 0; background: #f0f0f0;") + .container + h1 Evaluation + p Your submission has been sent for evaluation. + p You've sent in run: + a(href="/run/"+run.id) #{run.id} \ No newline at end of file