From a524c37795ad465b3e578019d3062e038cf9be44 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ale=C5=A1=20Smodi=C5=A1?= <aless@guru.si>
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(-)

(limited to 'js')

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.<string, HintDefinition> */
             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