From a97c5ad85d19f993b38b2d58ac7b241d65776913 Mon Sep 17 00:00:00 2001 From: Lloyd Hilaiel <lloyd@hilaiel.com> Date: Wed, 2 May 2012 21:08:04 -0600 Subject: [PATCH] interaction_data frontend abstraction --- resources/static/shared/interaction_data.js | 135 ++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 resources/static/shared/interaction_data.js diff --git a/resources/static/shared/interaction_data.js b/resources/static/shared/interaction_data.js new file mode 100644 index 000000000..0aaf769e1 --- /dev/null +++ b/resources/static/shared/interaction_data.js @@ -0,0 +1,135 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * interaction_data is a module responsible for collecting and + * reporting anonymous interaction data that represents a user's + * interaction with the dialog. It aggregates information that is not + * user specific like the user's OS, Browser, and the interface + * elements they've clicked on. It stores this information in + * localstorage, and at initialization reports previous interaction + * data to the server. This data is then used to optimize the user + * experience of the Persona dialog. + * + * More information about interaction data and 'Key Performance Indicators' + * stats that are derived from it: + * + * https://wiki.mozilla.org/Privacy/Reviews/KPI_Backend + */ + +// TODO: +// * should code explicitly call .addEvent? or instead should this module +// listen for events via the mediator? +// * the primary flow will cause unload and reload. omg. How do we deal? + +BrowserID.InteractionData = (function() { + var bid = BrowserID, + mediator = bid.Mediator, + storage = bid.Storage.interactionData, + network = bid.Network; + + var startTime = new Date(); + + // sample rate is specified from the server. it's set at the + // first 'context_info' event, which corresponds to the first time + // we get 'session_context' from the server. When sampleRate is + // not undefined, then the interaction data is initialized. + // sample will be true or false + var sample = undefined; + + var currentData = { + event_stream: [ + ] + }; + + // whenever session_context is hit, let's hear about it so we can + // extract the information that's important to us (like, whether we + // should be running or not) + mediator.subscribe('context_info', onSessionContext); + + function onSessionContext(msg, result) { + // defend against onSessionContext being called multiple times + if (sample !== undefined) return; + + // set the sample rate as defined by the server. It's a value + // between 0..1, integer or float, and it specifies the percentage + // of the time that we should capture + var sampleRate = result.data_sample_rate || 0; + + currentData.sample_rate = sampleRate; + + // now that we've got sample rate, let's smash it into a boolean + // probalistically + sample = (Math.random() <= sampleRate); + + // if we're not going to sample, kick out early. + if (!sample) { + currentData = undefined; + return; + } + + // set current time + currentData.timestamp = result.server_time; + + // language + currentData.lang = $('html').attr('lang'); + + if (window.screen) { + currentData.screen_size = { + width: window.screen.width, + height: window.screen.height + }; + } + + // XXX: implement me! + currentData.user_agent = { + os: null, + browser: null, + version: null + }; + + // cool. now let's persist this data + storage.push(currentData); + currentData = undefined; + + // finally, let's try to publish any old data + setTimeout(publishOld, 10); + } + + // At every load, after session_context returns, we'll try to publish + // past interaction data to the server if it exists. The psuedo + // transactional model employed here is to attempt to post, and only + // once we receive a server response do we purge data. We don't + // care if the post is a success or failure as this data is not + // critical to the functioning of the system (and some failure scenarios + // simply won't resolve with retries - like corrupt data, or too much + // data) + function publishOld() { + var data = storage.get(); + if (data.length === 0) return; + network.sendInteractionData(data, complete, complete); + return; + + function complete() { + storage.clear(); + } + } + + function addEvent(name) { + if (sample === false) return; + + if (currentData) { + currentData.event_stream.push([ name, new Date() - startTime ]); + } else { + var d = storage.current(); + if (!d.event_stream) d.event_stream = []; + d.event_stream.push([ name, new Date() - startTime ]); + storage.setCurrent(d); + } + } + + return { + addEvent: addEvent + }; +}()); -- GitLab