diff --git a/gpii/node_modules/eventLog/src/eventLog.js b/gpii/node_modules/eventLog/src/eventLog.js index 1b72e1ef1..e10a252e4 100644 --- a/gpii/node_modules/eventLog/src/eventLog.js +++ b/gpii/node_modules/eventLog/src/eventLog.js @@ -22,7 +22,8 @@ var fluid = require("infusion"); var fs = require("fs"), - path = require("path"); + path = require("path"), + moment = require("moment"); var gpii = fluid.registerNamespace("gpii"), $ = fluid.registerNamespace("jQuery"); @@ -43,7 +44,10 @@ fluid.defaults("gpii.eventLog", { } }, invokers: { - logEvent: "gpii.eventLog.log", + logEvent: { + funcName: "gpii.eventLog.log", // moduleName, event, data, level + args: ["{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2", "{arguments}.3"] + }, logError: "gpii.eventLog.logError", getGpiiSettingsDir: "{settingsDir}.getGpiiSettingsDir", getLogFile: "gpii.eventLog.getLogFile" @@ -60,18 +64,17 @@ fluid.defaults("gpii.eventLog", { args: ["{that}"] }, "onCreate.log": { - func: "{that}.logEvent", - args: ["{that}", "gpii", "Start", {}] + func: "gpii.eventLog.logStartStop", + args: ["{that}", "start"] }, "onDestroy.log": { - func: "{that}.logEvent", - args: [ "{that}", "gpii", "Stop", {}] + func: "gpii.eventLog.logStartStop", + args: ["{that}", "stop"] }, "{lifecycleManager}.events.onSessionStart": { namespace: "eventLog", func: "{that}.logEvent", args: [ - "{that}", "lifecycle", "SessionStart", { @@ -85,7 +88,6 @@ fluid.defaults("gpii.eventLog", { namespace: "eventLog", func: "{that}.logEvent", args: [ - "{that}", "lifecycle", "SessionStop", { @@ -98,11 +100,25 @@ fluid.defaults("gpii.eventLog", { } }); -/* - * Returns the actual Date and time +/** + * Returns the actual date and time, as a string, in ISO 8601 format with the localtime + offset from UTC. + * eg: '2018-07-13T13:03:03.863+01:00' + * @return {String} The current date, in ISO-8601 format with localtime and UTC offset. */ gpii.eventLog.getTimestamp = function () { - return new Date(); + return moment().toISOString(true); +}; + +/** + * Logs the start or stop of GPII, without duplication. + * @param {Component} that - The gpii.eventLog instance. + * @param {String} state - "start" or "stop". + */ +gpii.eventLog.logStartStop = function (that, state) { + if (state !== gpii.eventLog.logStartStop.currentState) { + gpii.eventLog.logStartStop.currentState = state; + that.logEvent("gpii", state); + } }; /** diff --git a/gpii/node_modules/eventLog/src/metrics.js b/gpii/node_modules/eventLog/src/metrics.js index f476eea81..32b2d4b66 100644 --- a/gpii/node_modules/eventLog/src/metrics.js +++ b/gpii/node_modules/eventLog/src/metrics.js @@ -30,7 +30,7 @@ fluid.defaults("gpii.metrics", { checks: { windows: { contextValue: "{gpii.contexts.windows}", - gradeNames: "gpii.metrics.windows" + gradeNames: "gpii.windowsMetrics" } } } @@ -38,7 +38,7 @@ fluid.defaults("gpii.metrics", { invokers: { logMetric: { func: "{eventLog}.logEvent", - args: ["{eventLog}", "metrics", "{arguments}.0", "{arguments}.1"] + args: ["metrics", "{arguments}.0", "{arguments}.1"] // event, data } }, members: { @@ -55,7 +55,12 @@ fluid.defaults("gpii.metrics", { } }, listeners: { - "{lifecycleManager}.events.onSessionStart": "{that}.events.onStartMetrics", + "{lifecycleManager}.events.onSessionStart": [{ + funcName: "gpii.metrics.sessionStarted", + args: [ "{that}", "{lifecycleManager}", "{arguments}.1"] + }, { + func: "{that}.events.onStartMetrics" + }], "{lifecycleManager}.events.onSessionStop": [{ namespace: "metrics.session", funcName: "gpii.metrics.sessionStopped", @@ -96,6 +101,60 @@ gpii.metrics.snapshotUpdate = function (that, sessionID, originalSettings) { }); }; +/** + * Handles the lifecycle.onSessionStarted event. + * + * Adds a listener to the new session's model to listen for changes to the preferences. + * + * @param {Component} that - The gpii.metrics instance. + * @param {Component} lifecycleManager - The lifecycleManager instance. + * @param {String} gpiiKey - The gpii key of the session. + */ +gpii.metrics.sessionStarted = function (that, lifecycleManager, gpiiKey) { + var session = lifecycleManager.getSession(gpiiKey); + session.applier.modelChanged.addListener("preferences.contexts", function (newValue, oldValue) { + var current = newValue && newValue[session.model.activeContextName]; + current = current && current.preferences; + var previous = oldValue && oldValue[session.model.activeContextName]; + previous = previous && previous.preferences; + + gpii.metrics.preferenceChanged(that, current, previous); + }); +}; + +/** + * Called when a preference has changed. (a change in the preferences field of the session's model.) + * + * @param {Comment} that - The gpii.metrics instance. + * @param {Object} current - The current preferences map for the active context. + * @param {Object} previous - The previous preferences map for the active context. + */ +gpii.metrics.preferenceChanged = function (that, current, previous) { + var diff = { changeMap: {}, changes: 0, unchanged: 0}; + var same = fluid.model.diff(previous, current, diff); + if (!same) { + var changedPreferences; + if (fluid.isPlainObject(diff.changeMap)) { + changedPreferences = {}; + fluid.each(diff.changeMap, function (value, key) { + if (value === "ADD") { + changedPreferences[key] = current[key]; + } + }); + } else if (diff.changeMap === "ADD") { + // Everything is new + changedPreferences = current; + } + + fluid.each(changedPreferences, function (value, name) { + that.logMetric("preference", { + name: name, + value: value + }); + }); + } +}; + /** * Removes the logged solution IDs for the session. * @param {Component} that - The gpii.metrics instance. diff --git a/gpii/node_modules/eventLog/test/EventLogTests.js b/gpii/node_modules/eventLog/test/EventLogTests.js index 07217af74..62e5daf27 100644 --- a/gpii/node_modules/eventLog/test/EventLogTests.js +++ b/gpii/node_modules/eventLog/test/EventLogTests.js @@ -81,7 +81,7 @@ gpii.tests.eventLog.testDefs = fluid.freezeRecursive(fluid.transform([ expected: { "level": "INFO", "module": "gpii", - "event": "Start" + "event": "start" } }, { diff --git a/gpii/node_modules/eventLog/test/all-tests.js b/gpii/node_modules/eventLog/test/all-tests.js new file mode 100644 index 000000000..8f276e0ce --- /dev/null +++ b/gpii/node_modules/eventLog/test/all-tests.js @@ -0,0 +1,22 @@ +/* + * Event log and metrics tests. + * + * Copyright 2017 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; + +require("./EventLogTests.js"); +require("./metricsTests.js"); diff --git a/gpii/node_modules/eventLog/test/metricsTests.js b/gpii/node_modules/eventLog/test/metricsTests.js new file mode 100644 index 000000000..74d85931b --- /dev/null +++ b/gpii/node_modules/eventLog/test/metricsTests.js @@ -0,0 +1,211 @@ +/* + * Metrics Tests + * + * Copyright 2017 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; + +var fluid = require("infusion"); + +var jqUnit = fluid.require("node-jqunit"); +var gpii = fluid.registerNamespace("gpii"); +fluid.registerNamespace("gpii.tests.metrics"); + +require("../index.js"); + +var teardowns = []; + +jqUnit.module("gpii.tests.metrics", { + teardown: function () { + while (teardowns.length) { + teardowns.pop()(); + } + } +}); + +// Tests ensure preferenceChanged logs added and changed preferences. +gpii.tests.metrics.preferenceChangedTestData = fluid.freezeRecursive({ + "unchanged 1/1": { + previous: { + pref1: "value1" + }, + current: { + pref1: "value1" + }, + expect: [] + }, + "unchanged 2/2": { + previous: { + pref1: "value1", + pref2: "value2" + }, + current: { + pref1: "value1", + pref2: "value2" + }, + expect: [] + }, + "changed 1/1": { + previous: { + pref1: "value1" + }, + current: { + pref1: "changed value1" + }, + expect: { + name: "pref1", + value: "changed value1" + } + }, + "changed 2/2": { + previous: { + pref1: "value1", + pref2: "value2" + }, + current: { + pref1: "changed value1", + pref2: "changed value2" + }, + expect: [{ + name: "pref1", + value: "changed value1" + }, { + name: "pref2", + value: "changed value2" + }] + }, + "changed 2/3": { + previous: { + pref1: "value1", + pref2: "value2", + pref3: "value3" + }, + current: { + pref1: "changed value1", + pref2: "changed value2", + pref3: "value3" + }, + expect: [{ + name: "pref1", + value: "changed value1" + }, { + name: "pref2", + value: "changed value2" + }] + }, + "add 1+1": { + previous: { + pref1: "value1" + }, + current: { + pref1: "value1", + pref2: "new value2" + }, + expect: [{ + name: "pref2", + value: "new value2" + }] + }, + "add+change": { + previous: { + pref1: "value1" + }, + current: { + pref1: "changed value1", + pref2: "new value2" + }, + expect: [{ + name: "pref1", + value: "changed value1" + }, { + name: "pref2", + value: "new value2" + }] + }, + "remove 1-1": { + previous: { + pref1: "value1" + }, + current: { + }, + expect: [] + }, + "remove 2-1": { + previous: { + pref1: "value1", + pref2: "value2" + }, + current: { + pref2: "value2" + }, + expect: [] + }, + "remove+change+static+add": { + previous: { + pref1: "value1", + pref2: "value2", + pref3: "value3" + }, + current: { + pref2: "changed value2", + pref3: "value3", + pref4: "new value4" + }, + expect: [{ + name: "pref2", + value: "changed value2" + }, { + name: "pref4", + value: "new value4" + }] + } +}); + +fluid.defaults("gpii.tests.metricsWrapper", { + gradeNames: ["fluid.component", "gpii.metrics", "gpii.eventLog", "gpii.lifecycleManager"], + listeners: { + "onStartMetrics.application": null, + "onStartMetrics.input": null, + "onStopMetrics.application": null, + "onStopMetrics.input": null + } +}); + + +jqUnit.test("preferenceChanged", function () { + + var tests = gpii.tests.metrics.preferenceChangedTestData; + + var logValues; + + var metrics = gpii.tests.metricsWrapper({ + invokers: { + "logMetric": function (metric, data) { + logValues.push(data); + } + } + }); + + fluid.each(tests, function (test) { + logValues = []; + + gpii.metrics.preferenceChanged(metrics, test.current, test.previous); + + jqUnit.assertDeepEq("preferenceChanged should have logged the expected value.", + fluid.makeArray(test.expect), logValues); + }); + +}); diff --git a/package.json b/package.json index c3d0b82eb..2089e1092 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "json5": "1.0.1", "kettle": "1.7.3", "mkdirp": "0.5.1", + "moment": "2.22.2", "ndef": "0.2.0", "nock": "9.3.0", "node-jqunit": "1.1.8", diff --git a/tests/all-tests.js b/tests/all-tests.js index 2ba36b3af..aea63ab6e 100644 --- a/tests/all-tests.js +++ b/tests/all-tests.js @@ -53,7 +53,7 @@ var testIncludes = [ "../gpii/node_modules/accessRequester/test/AccessRequesterTests.js", "../gpii/node_modules/canopyMatchMaker/test/CanopyMatchMakerTests.js", "../gpii/node_modules/contextManager/test/ContextManagerTests.js", - "../gpii/node_modules/eventLog/test/EventLogTests.js", + "../gpii/node_modules/eventLog/test/all-tests.js", "../gpii/node_modules/flatMatchMaker/test/FlatMatchMakerTests.js", "../gpii/node_modules/flowManager/test/BrowserChannelTests.js", "../gpii/node_modules/flowManager/test/GetGpiiKeyTests.js",