diff --git a/.gitignore b/.gitignore index 14413157..ec7937d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea node_modules .DS_store +test/cafe/node_modules \ No newline at end of file diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/client/app/config/common.js b/client/app/config/common.js old mode 100644 new mode 100755 diff --git a/client/app/config/jiff.js b/client/app/config/jiff.js old mode 100644 new mode 100755 diff --git a/client/app/controllers/analystController.js b/client/app/controllers/analystController.js old mode 100644 new mode 100755 diff --git a/client/app/controllers/clientController.js b/client/app/controllers/clientController.js old mode 100644 new mode 100755 index b60dd1e1..bf5b0fdb --- a/client/app/controllers/clientController.js +++ b/client/app/controllers/clientController.js @@ -1,6 +1,6 @@ /* global alertify, $ */ -define(['jquery', 'controllers/tableController', 'controllers/jiffController', 'alertify', 'alertify_defaults', 'table_template'], - function ($, tableController, jiffController, alertify, _, table_template) { +define(['jquery', 'controllers/tableController', 'controllers/jiffController', 'controllers/usabilityController', 'alertify', 'alertify_defaults', 'table_template'], + function ($, tableController, jiffController, usabilityController, alertify, _, table_template) { var client = (function () { /** * Displays the given submission as the last submission in @@ -94,7 +94,7 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' dataType: 'text' }).then(function (response) { JSON.parse(response); // verify response is json (error responses are string messages) - + var $parent = $('#session, #participation-code').parent(); $parent.removeClass('has-error').addClass('has-success has-feedback'); $parent.find('.success-icon').removeClass('hidden').addClass('show'); @@ -104,6 +104,7 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' callback && callback(true); }).catch(function (err) { var errorMsg = SERVER_ERR; + usabilityController.addValidationError("SESSION_INFO_ERROR"); if (err && err.hasOwnProperty('responseText') && err.responseText !== undefined) { errorMsg = err.responseText; } @@ -127,11 +128,13 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' var $session = $('#session'); if (!validateSessionInput($session, false)) { errors.push(SESSION_KEY_ERROR); + usabilityController.addValidationError("SESSION_KEY_ERROR"); } var $participationCode = $('#participation-code'); if (!validateSessionInput($participationCode, false)) { errors.push(PARTICIPATION_CODE_ERROR); + usabilityController.addValidationError("PARTICIPATION_CODE_ERROR"); } // Validate the remaining components after session and @@ -139,12 +142,14 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' var validateRemainingComponents = function (result) { if (!result) { errors.push(SESSION_PARTICIPATION_CODE_SERVER_ERROR); + usabilityController.addValidationError("SESSION_PARTICIPATION_CODE_SERVER_ERROR"); } // Verify confirmation check box was checked var verifyChecked = $('#verify').is(':checked'); if (!verifyChecked) { errors.push(UNCHECKED_ERR); + usabilityController.addValidationError("UNCHECKED_ERR"); } // Verify additional questions @@ -194,6 +199,7 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' } if (errors.indexOf(errorMsg) === -1) { errors.push(errorMsg); + usabilityController.addValidationError("CELL_ERROR"); } }; tableController.registerErrorHandler(errorHandler); @@ -264,7 +270,7 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' var text = $(questions_text[q]).text(); text = text.replace(/\s+/g, ' '); // Replace many white spaces with just one space. data_submission['questions'][text] = question_data; - questions_public[text] = Object.assign({}, question_data); + questions_public[text] = Object.assign({}, question_data); } // Handle table data, tables are represented as 2D associative arrays @@ -274,6 +280,12 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' data_submission[tables_data[i].name] = tables_data[i].data; } + if (document.getElementById('choose-file').files.length > 0) { + usabilityController.dataPrefilled(); + } + + data_submission['usability'] = usabilityController.analytics; + // Secret share / mask the data. jiffController.client.submit(session, participationCode, data_submission, function (err, response) { if (err == null || err === 200) { @@ -286,8 +298,10 @@ define(['jquery', 'controllers/tableController', 'controllers/jiffController', ' } else if (err === 0 || err === 500) { // check for status 0 or status 500 (Server not reachable.) error(SERVER_ERR); + usabilityController.addValidationError("SERVER_ERR"); } else { error(GENERIC_SUBMISSION_ERR); + usabilityController.addValidationError("GENERIC_SUBMISSION_ERR"); } la.stop(); diff --git a/client/app/controllers/jiffController.js b/client/app/controllers/jiffController.js old mode 100644 new mode 100755 index 4e44401a..fee7d342 --- a/client/app/controllers/jiffController.js +++ b/client/app/controllers/jiffController.js @@ -99,6 +99,16 @@ define(['mpc', 'pki', 'BigNumber', 'jiff', 'jiff_bignumber', 'jiff_restAPI', 'ta values.push(dataSubmission['questions'][q.question][q.option]); } + for (var k = 0; k < ordering.usability.length; k++) { + const m = ordering.usability[k].metric; + const f = ordering.usability[k].field; + if (f !== null) { + values.push(dataSubmission.usability[m][f]); + } else { + values.push(dataSubmission.usability[m]); + } + } + // Handle jiff errors returned from server var options = { onError: function (errorString) { @@ -149,18 +159,22 @@ define(['mpc', 'pki', 'BigNumber', 'jiff', 'jiff_bignumber', 'jiff_restAPI', 'ta // Meta-info var ordering = mpc.consistentOrdering(table_template); - var submitters = JSON.parse(msg); + msg = JSON.parse(msg); + var submitters = msg['submitters']; + var resubmission_avg = msg['resubmission_avg']; // Compute and Format var promise = mpc.compute(jiff, submitters, ordering); promise = mpc.format(promise, submitters, ordering); promise.then(function (result) { jiff.disconnect(false, false); + result['usability']['resubmission_avg'] = resubmission_avg; callback(result); }).catch(function (err) { error(err); }); }); + }; // Exports @@ -169,7 +183,7 @@ define(['mpc', 'pki', 'BigNumber', 'jiff', 'jiff_bignumber', 'jiff_restAPI', 'ta submit: clientSubmit }, analyst: { - computeAndFormat: computeAndFormat + computeAndFormat } } }); diff --git a/client/app/controllers/tableController.js b/client/app/controllers/tableController.js old mode 100644 new mode 100755 index 98dcec4b..5aca7a2d --- a/client/app/controllers/tableController.js +++ b/client/app/controllers/tableController.js @@ -1013,6 +1013,11 @@ define(['jquery', 'Handsontable', 'table_template', 'filesaver', 'alertify', 'qt return colWidths; } + function saveUsability(usability, session) { + var json = JSON.stringify(usability); + filesaver.saveAs(new Blob([json], {type: 'application/json'}), 'Usability_' + session + '.json'); + } + function saveQuestions(questions, session) { if (questions == null) { return; @@ -1050,6 +1055,7 @@ define(['jquery', 'Handsontable', 'table_template', 'filesaver', 'alertify', 'qt fillData, saveTables, saveQuestions, + saveUsability, displayReadTable, resetTableWidth, updateTableWidth, diff --git a/client/app/controllers/usabilityController.js b/client/app/controllers/usabilityController.js new file mode 100644 index 00000000..a8c26bc3 --- /dev/null +++ b/client/app/controllers/usabilityController.js @@ -0,0 +1,109 @@ +define(['table_template'], function (table_template) { + + let analytics = {}; + let timers = {}; + + function initialize() { + for (var metric of table_template.usability) { + if (typeof(metric) === 'string') { + analytics[metric] = 0; + } else if (typeof(metric) === 'object') { + var key = Object.keys(metric)[0]; + var fields = metric[key]; + analytics[key] = {}; + + for (var f of fields) { + analytics[key][f] = 0; + } + } + } + + for (let k of Object.keys(analytics.time_spent)) { + + if (k === 'page') { + timers[k] = new Date(); + } else { + timers[k] = null; + } + + $('#session-area').on("mouseenter", function() { + startTimer(k); + }); + + $('#session-area').on("mouseleave", function() { + endTimer(k); + }); + } + window.addEventListener('blur', endTimer); + window.addEventListener('beforeunload', endTimer); + } + + function detectBrave() { + // initial assertions + if (!window.google_onload_fired && + navigator.userAgent && + !navigator.userAgent.includes('Chrome')) + return false; + + // set up test + var test = document.createElement('iframe'); + test.style.display = 'none'; + document.body.appendChild(test); + + // empty frames only have this attribute set in Brave Shield + var is_brave = (test.contentWindow.google_onload_fired === true); + + // teardown test + test.parentNode.removeChild(test); + + return is_brave; + } + + function startTimer(key) { + timers[key] = new Date(); + }; + + function endTimer(key) { + if (typeof(key) !== 'string') { + key = 'page'; + } + const endDate = new Date(); + const spentTime = endDate.getTime() - timers[key].getTime(); + analytics.time_spent[key] += spentTime; + }; + + function saveBrowser() { + // check Edge + var ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|edge|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; + var browser = M[0].toLowerCase(); + + if (detectBrave) { + analytics.browser.brave += 1; + return; + } + + for (let key in analytics.browser) { + if (browser.includes(key)) { + analytics.browser[key] += 1; + return; + } + } + analytics.browser.other += 1; + } + + function addValidationError(err) { + analytics.validation_errors[err] += 1; + } + + function dataPrefilled() { + analytics.data_prefilled += 1; + } + + return { + addValidationError, + analytics, + dataPrefilled, + initialize, + saveBrowser + }; +}); diff --git a/client/app/data/bwwc.js b/client/app/data/bwwc.js old mode 100644 new mode 100755 diff --git a/client/app/data/pacesetters.js b/client/app/data/pacesetters.js old mode 100644 new mode 100755 index bc34c6df..eafc6435 --- a/client/app/data/pacesetters.js +++ b/client/app/data/pacesetters.js @@ -203,6 +203,26 @@ define([], function () { } ] } + ], + 'usability': [ + 'data_prefilled', + {'time_spent': ['page', 'session_area', 'tables-area', 'review-and-submit', 'amount-spent', 'addressable-spend', 'number-MBEs']}, + {'browser': ['brave', 'chrome', 'edge', 'msie', 'firefox', 'opera', 'other', 'safari'] + }, + {'validation_errors': [ + 'SESSION_KEY_ERROR', + 'SESSION_INFO_ERROR', + 'PARTICIPATION_CODE_ERROR', + 'SESSION_PARTICIPATION_CODE_SERVER_ERROR', + 'UNCHECKED_ERR', + 'GENERIC_TABLE_ERR', + 'SERVER_ERR', + 'GENERIC_SUBMISSION_ERR', + 'NAN_EMPTY_CELLS', + 'SEMANTIC_CELLS', + 'CELL_ERROR' + ] + } ] } }); diff --git a/client/app/helper/alertify_defaults.js b/client/app/helper/alertify_defaults.js old mode 100644 new mode 100755 diff --git a/client/app/helper/array.reduce-polyfill.min.js b/client/app/helper/array.reduce-polyfill.min.js old mode 100644 new mode 100755 diff --git a/client/app/helper/drop_sheet.js b/client/app/helper/drop_sheet.js old mode 100644 new mode 100755 diff --git a/client/app/helper/mpc.js b/client/app/helper/mpc.js index 8a9e2f78..ac9804d4 100644 --- a/client/app/helper/mpc.js +++ b/client/app/helper/mpc.js @@ -19,6 +19,7 @@ define([], function () { var consistentOrdering = function (table_template) { var tables = []; var questions = []; + var usability = []; // order tables for (var i = 0; i < table_template.tables.length; i++) { var table_def = table_template.tables[i]; @@ -47,7 +48,20 @@ define([], function () { } } } - return { tables: tables, questions: questions }; + + for (let metric of table_template.usability) { + if (typeof(metric) === 'string') { + usability.push({metric: metric, field: null}); + } else if (typeof(metric) === 'object') { + const key = Object.keys(metric)[0]; + const fields = metric[key]; + + for (let f of fields) { + usability.push({metric: key, field: f}); + } + } + } + return { tables, questions, usability }; }; var compute = function (jiff_instance, submitters, ordering) { @@ -55,7 +69,7 @@ define([], function () { // sum all received shares for that entry, and reveal the sum // for that element to the analyst in order. var shares = []; - for (var k = 0; k < ordering.tables.length + ordering.questions.length; k++) { + for (var k = 0; k < ordering.tables.length + ordering.questions.length + ordering.usability.length; k++) { var subid = submitters[0]; var entry_sum = jiff_instance.share(null, null, [1, 's1'], [subid]); @@ -98,7 +112,6 @@ define([], function () { shares.push(promise); } - return Promise.all(shares); }; @@ -115,9 +128,12 @@ define([], function () { // } var format = function (resultsPromise, submitters, ordering) { return resultsPromise.then(function (results) { + var averages = {}; // results array will be transformed to an object of the correct form var questions = {}; var deviations = {}; + var usability = {}; + for (var i = 0; i < ordering.tables.length; i++) { var table = ordering.tables[i].table; var row = ordering.tables[i].row; @@ -156,13 +172,29 @@ define([], function () { questions[question][label] = results[i+j].toString(); } - return { averages: averages, questions: questions, deviations: deviations }; + for (let k = 0; k < ordering.usability.length; k++) { + + const m = ordering.usability[k].metric; + const f = ordering.usability[k].field; + const value = results[i+j+k].c[0].toString(); + + if (f === null) { + usability[m] = value; + } else { + if (!(m in usability)) { + usability[m] = {}; + } + usability[m][f] = value; + + } + } + return { averages, questions, deviations, usability }; }); }; return { - consistentOrdering: consistentOrdering, - compute: compute, - format: format + consistentOrdering, + compute, + format } }); diff --git a/client/app/helper/pki.js b/client/app/helper/pki.js old mode 100644 new mode 100755 diff --git a/client/app/helper/sheetjsw.js b/client/app/helper/sheetjsw.js old mode 100644 new mode 100755 diff --git a/client/app/index.js b/client/app/index.js old mode 100644 new mode 100755 diff --git a/client/app/session.js b/client/app/session.js old mode 100644 new mode 100755 diff --git a/client/app/track.js b/client/app/track.js old mode 100644 new mode 100755 diff --git a/client/app/unmask.js b/client/app/unmask.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/ResizeSensor.js b/client/app/vendor/ResizeSensor.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/alertify.js b/client/app/vendor/alertify.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/bootstrap.min.js b/client/app/vendor/bootstrap.min.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/filesaver.js b/client/app/vendor/filesaver.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/forge.js b/client/app/vendor/forge.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/jquery.js b/client/app/vendor/jquery.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/jquery_qtip.js b/client/app/vendor/jquery_qtip.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/ladda.js b/client/app/vendor/ladda.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/require.js b/client/app/vendor/require.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/spin.js b/client/app/vendor/spin.js old mode 100644 new mode 100755 diff --git a/client/app/vendor/xlsx.js b/client/app/vendor/xlsx.js old mode 100644 new mode 100755 diff --git a/client/app/views/clientView.js b/client/app/views/clientView.js old mode 100644 new mode 100755 index a302ff4a..69ae71c7 --- a/client/app/views/clientView.js +++ b/client/app/views/clientView.js @@ -1,5 +1,5 @@ -define(['jquery', 'controllers/clientController', 'controllers/tableController', 'helper/drop_sheet', 'spin', 'Ladda', 'ResizeSensor', 'alertify', 'table_template', 'bootstrap'], - function ($, clientController, tableController, DropSheet, Spinner, Ladda, ResizeSensor, alertify, table_template) { +define(['jquery', 'controllers/clientController', 'controllers/tableController', 'controllers/usabilityController', 'helper/drop_sheet', 'spin', 'Ladda', 'ResizeSensor', 'alertify', 'table_template', 'bootstrap'], + function ($, clientController, tableController, usabilityController, DropSheet, Spinner, Ladda, ResizeSensor, alertify, table_template) { function createQuestionText(text) { var p = document.createElement('p'); @@ -66,12 +66,16 @@ define(['jquery', 'controllers/clientController', 'controllers/tableController', // Hide by default $('#additional-questions').hide(); + usabilityController.initialize(); + tableController.createTableElems(table_template.tables, '#tables-area'); displaySurveyQuestions(); // Create the tabless var tables = tableController.makeTables(table_template.tables); + usabilityController.saveBrowser(); + // TODO //createResizeSensors(tables); THIS FUNCTION BREAKS THINGS I THINK! -IRA var totals_table = null; diff --git a/client/app/views/sessionView.js b/client/app/views/sessionView.js old mode 100644 new mode 100755 diff --git a/client/app/views/trackView.js b/client/app/views/trackView.js old mode 100644 new mode 100755 diff --git a/client/app/views/unmaskView.js b/client/app/views/unmaskView.js old mode 100644 new mode 100755 index 95d31a08..1e565085 --- a/client/app/views/unmaskView.js +++ b/client/app/views/unmaskView.js @@ -2,6 +2,10 @@ define(['jquery', 'controllers/jiffController', 'controllers/tableController', 'helper/drop_sheet', 'alertify', 'table_template'], function ($, jiffController, tableController, DropSheet, alertify, table_template) { + + document.getElementById('session').value = 'gv5bsr26k3vghtkpr6rs6nd3n4'; + document.getElementById('session-password').value = '2rnategc605cmxqwt2wvmcpkec'; + function error(msg) { alertify.alert('ErrorError!', msg); } @@ -26,13 +30,15 @@ define(['jquery', 'controllers/jiffController', 'controllers/tableController', ' var privateKey = e.target.result; jiffController.analyst.computeAndFormat(sessionKey, sessionPass, privateKey, error, function (result) { - var questions = result.questions; + var questions = result['questions']; + var usability = result['usability']; + delete result['usability']; + delete result['questions']; // for tables only var averages = result.averages; var deviations = result.deviations; - // download averages and deviations tableController.saveTables(averages, sessionKey, 'Averages'); tableController.saveTables(deviations, sessionKey, 'Standard_Deviations'); @@ -40,6 +46,10 @@ define(['jquery', 'controllers/jiffController', 'controllers/tableController', ' tableController.saveQuestions(questions, sessionKey); } + if (usability != null) { + tableController.saveUsability(usability, sessionKey); + } + // Only display averages in the table tableController.createTableElems(table_template.tables, '#tables-area'); tableController.displayReadTable(averages); diff --git a/client/closed.html b/client/closed.html old mode 100644 new mode 100755 diff --git a/client/create.html b/client/create.html old mode 100644 new mode 100755 diff --git a/client/definitions.html b/client/definitions.html old mode 100644 new mode 100755 diff --git a/client/images/accept.png b/client/images/accept.png old mode 100644 new mode 100755 diff --git a/client/images/bu.gif b/client/images/bu.gif old mode 100644 new mode 100755 diff --git a/client/images/bu.png b/client/images/bu.png old mode 100644 new mode 100755 diff --git a/client/images/bwwc.png b/client/images/bwwc.png old mode 100644 new mode 100755 diff --git a/client/images/cancel.png b/client/images/cancel.png old mode 100644 new mode 100755 diff --git a/client/images/expand.png b/client/images/expand.png old mode 100644 new mode 100755 diff --git a/client/images/gbcc-small.png b/client/images/gbcc-small.png old mode 100644 new mode 100755 diff --git a/client/images/gbcc.jpg b/client/images/gbcc.jpg old mode 100644 new mode 100755 diff --git a/client/images/gbcc.png b/client/images/gbcc.png old mode 100644 new mode 100755 diff --git a/client/index.html b/client/index.html index a4abd85d..799eb4b4 100755 --- a/client/index.html +++ b/client/index.html @@ -70,8 +70,8 @@

Input your data

- +