From a524c37795ad465b3e578019d3062e038cf9be44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= Date: Tue, 25 Aug 2015 19:03:17 +0200 Subject: Work on sending activity trace. --- js/codeq/comms.js | 71 ++++++++++++++++++++++++++++++++++++--------- js/prolog.js | 87 +++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 122 insertions(+), 36 deletions(-) diff --git a/js/codeq/comms.js b/js/codeq/comms.js index d2c2910..eece7e7 100644 --- a/js/codeq/comms.js +++ b/js/codeq/comms.js @@ -3,6 +3,7 @@ var activityQueue = []; var send = function (service, json) { + if (json instanceof Object) json = codeq.jsonize(json); return Q.Promise(function (resolve, reject, notify) { $.ajax({ 'type': 'POST', @@ -24,21 +25,36 @@ }; var sendCount = 0, + resolveWaiters = [], + currentSolution = null, + resolveActivity = function () { + var i; + // signal everyone that the queue is flushed + for (i = 0; i < resolveWaiters.length; i++) { + try { + resolveWaiters[i][0](); + } + catch (e) {} + } + resolveWaiters.length = 0; + }, sendActivityInternal = function () { - var request; + var trace; // send max. 100 activities, do not be excessive + if (activityQueue.length == 0) return resolveActivity(); if (activityQueue.length > 100) { sendCount = 100; - request = '[' + activityQueue.slice(0, 100).join(',') + ']'; + trace = activityQueue.slice(0, 100); } else { sendCount = activityQueue.length; - request = '[' + activityQueue.join(',') + ']'; + trace = activityQueue; } - send('activity', request).then( + send('trace', {'trace': trace, 'solution': currentSolution}).then( function sendActivitySuccess() { activityQueue.splice(0, sendCount); if (activityQueue.length > 0) sendActivityInternal(); + else resolveActivity(); }, function sendActivityFailure() { Q.delay(500).then(sendActivityInternal).done(); @@ -47,27 +63,54 @@ }; codeq.comms = { - sendActivity: function commsSendActivity (json) { - var triggerSending = activityQueue.length == 0; - json['sid'] = codeq.sid; - activityQueue.push(codeq.jsonize(json)); - if (triggerSending) { - setTimeout(sendActivityInternal, 0); // async trigger: see if you can collect some more payload - } + sendActivity: function commsSendActivity (json, solution) { + return Q.Promise(function (resolve, reject, notify) { + var triggerSending = activityQueue.length == 0, + i, js; + if (json instanceof Array) { + for (i = 0; i < json.length; i++) { + js = json[i]; + js['sid'] = codeq.sid; + activityQueue.push(js); + } + } + else { + json['sid'] = codeq.sid; + activityQueue.push(json); + } + if (solution) currentSolution = solution; + resolveWaiters.push([resolve, reject]); + if (triggerSending) { + setTimeout(sendActivityInternal, 0); // async trigger: see if you can collect some more payload + } + }); }, sendQuery: function commsSendQuery (json) { + var thisTrace; json['sid'] = codeq.sid; - return send('query', codeq.jsonize(json)); + if (activityQueue.length > 0) { + thisTrace = json['trace']; + if (thisTrace instanceof Array) { + activityQueue.splice(0, 0, 0, 0); // add two zeros in front, they will form the first two parameters to the next splice() + thisTrace.splice.apply(thisTrace, activityQueue); + } + else { + json['trace'] = activityQueue.slice(); + } + activityQueue.length = 0; + if (currentSolution) json['solution'] = currentSolution; + } + return send('query', json); }, getProblem: function commsGetProblem (language, problem_group, problem) { - return send('get_problem', codeq.jsonize({ + return send('get_problem', { 'sid': codeq.sid, 'language': language, 'problem_group': problem_group, 'problem': problem - })); + }); } }; })(); diff --git a/js/prolog.js b/js/prolog.js index 50b9176..429ac72 100644 --- a/js/prolog.js +++ b/js/prolog.js @@ -6,7 +6,7 @@ // a constant var firstCharacterPos = {'line': 0, 'ch': 0}; - var makePrologTerminalHandler = function (jqConsole, editor, problem_id) { + var makePrologTerminalHandler = function (jqConsole, editor, problem_id, activityHandler) { var promptMode = true, // default: query composition; alternative: query result browsing tcs = function terminalCommandSuccess (data) { var t, lines, i; @@ -36,16 +36,15 @@ 'step': 'run', 'program': editor.getDoc().getValue(), 'query': command, -// 'language': 'prolog', -// 'problem_group': problem_group, -// 'problem': problem - 'problem_id': problem_id + 'problem_id': problem_id, + 'trace': activityHandler.addAndPurge({'typ': 'slv', 'qry': command}) }).then(tcs, tcf).done(); } else { // not in prompt mode -- we should never land here, but handle it anyway codeq.comms.sendQuery({ - 'step': 'end' + 'step': 'end', + 'trace': activityHandler.addAndPurge({'typ': 'stp'}) }).then(tcs, tcf).done(); } }, { @@ -64,14 +63,16 @@ // space or semicolon -> show next answer event.which = 59; // semicolon codeq.comms.sendQuery({ - 'step': 'next' + 'step': 'next', + 'trace': activityHandler.addAndPurge({'typ': 'nxt'}) }).then(tcs, tcf).done(); } else { // everything else: stop searching for answers event.which = 46; // full stop codeq.comms.sendQuery({ - 'step': 'end' + 'step': 'end', + 'trace': activityHandler.addAndPurge({'typ': 'stp'}) }).then(tcs, tcf).done(); } } @@ -80,6 +81,56 @@ return {}; }; + var makeActivityHandler = function (editor) { + var lastActivityMillis = Date.now(), + deltaActivityMillis = function deltaActivityMillisFunc () { + var now = Date.now(), + dt = Math.max(0, Math.min(30000, now - lastActivityMillis)); // 0 sec <= dt <= 30 sec + lastActivityMillis = now; + return dt; + }, + queue = [], + ts = null, + timer = function () { + var promise; + ts = null; + if (queue.length === 0) return Q(true); + promise = codeq.comms.sendActivity(queue, editor.getDoc().getValue()); + queue.length = 0; + return promise; + }, + flush = function () { + clearTimeout(ts); + return timer(); + }; + + return { + "trace": function (trace) { + trace['dt'] = deltaActivityMillis(); + return trace; + }, + 'queue': function (trace) { + queue.push(trace); + if (ts === null) setTimeout(timer, 10000); // flush every 10 seconds + }, + 'queueTrace': function (trace) { + this.queue(this.trace(trace)); + }, + 'flush': flush, + 'addAndPurge': function (trace) { + var accumulatedTrace = queue; + queue = []; + trace['dt'] = deltaActivityMillis(); + accumulatedTrace.push(trace); + if (ts !== null) { + clearTimeout(ts); + ts = null; + } + return accumulatedTrace; + } + }; + }; + /** * Creates a new handler for the given Prolog assignment definition. * @@ -93,6 +144,7 @@ jqConsole = $('#console'), jqHints = $('#info'), editor = CodeMirror(jqEditor[0], { cursorHeight: 0.85, lineNumbers: true, matchBrackets: true }), + activityHandler = makeActivityHandler(editor), /* controller = jqConsole.console({ promptLabel: '?- ', commandValidate: function (line) { @@ -106,7 +158,7 @@ promptHistory: false, welcomeMessage: 'Prolog REPL.' }),*/ - terminal = makePrologTerminalHandler(jqConsole, editor, problem.id), + terminal = makePrologTerminalHandler(jqConsole, editor, problem.id, activityHandler), /** Object. */ hintDefs = problem.hint, hintCounter = 0, // for generating unique class-names @@ -211,13 +263,6 @@ hintCleaners.push(close); } - }, - lastActivityMillis = Date.now(), - deltaActivityMillis = function deltaActivityMillisFunc () { - var now = Date.now(), - dt = Math.max(0, Math.min(30000, now - lastActivityMillis)); // 0 sec <= dt <= 30 sec - lastActivityMillis = now; - return dt; }; editor.setValue(info.solution); @@ -226,15 +271,14 @@ editor.on('change', function (instance, changeObj) { var doc = editor.getDoc(), - pos = codeq.codePointCount(doc.getRange(firstCharacterPos, changeObj.from)), - dt = deltaActivityMillis(); + pos = codeq.codePointCount(doc.getRange(firstCharacterPos, changeObj.from)); if (changeObj.removed) { -// codeq.comms.sendActivity({'typ': 'rm', 'dt': dt, 'off': pos, 'len': codeq.codePointCount(changeObj.removed)}); + activityHandler.queueTrace({'typ': 'rm', 'off': pos, 'len': codeq.codePointCount(changeObj.removed)}); } if (changeObj.text) { -// codeq.comms.sendActivity({'typ': 'ins', 'dt': dt, 'off': pos, 'txt': changeObj.text}); + activityHandler.queueTrace({'typ': 'ins', 'off': pos, 'txt': changeObj.text}); } }); @@ -297,8 +341,7 @@ // $(jqButtons.get(2)).on('click', function () { handler.processServerHints([{id:'drop_down', start: 20, end: 26, choices:['ena', 'dva', 'tri']}]); }); $('#btn_code_run').on('click', function () { - var doc = editor.getDoc(), - dt = deltaActivityMillis(); + var doc = editor.getDoc(); // codeq.comms.sendActivity({'typ': 'slv', 'dt': dt, 'qry': }); // handler.processServerHints([{id:'list_empty'}]); }); -- cgit v1.2.1