From 18fbe719ef8a9addc9b810dd6c7961c64b0b122e Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Fri, 21 Oct 2016 14:00:58 -0700 Subject: [PATCH 01/10] Change the log calls to use the new promisified interface as well Works now! --- www/js/recent.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/www/js/recent.js b/www/js/recent.js index 1dbf5dada..e5960392d 100644 --- a/www/js/recent.js +++ b/www/js/recent.js @@ -20,7 +20,7 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) $scope.logCtrl = {}; $scope.refreshEntries = function() { - window.Logger.getMaxIndex(function(maxIndex) { + window.Logger.getMaxIndex().then(function(maxIndex) { console.log("maxIndex = "+maxIndex); $scope.logCtrl.currentStart = maxIndex; $scope.logCtrl.gotMaxIndex = true; @@ -46,8 +46,8 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) $scope.addEntries = function() { console.log("calling addEntries"); - window.Logger.getMessagesFromIndex($scope.logCtrl.currentStart, RETRIEVE_COUNT, - function(entryList) { + window.Logger.getMessagesFromIndex($scope.logCtrl.currentStart, RETRIEVE_COUNT) + .then(function(entryList) { $scope.$apply($scope.processEntries(entryList)); console.log("entry list size = "+$scope.entries.length); console.log("Broadcasting infinite scroll complete"); @@ -64,6 +64,7 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) $scope.processEntries = function(entryList) { for (i = 0; i < entryList.length; i++) { var currEntry = entryList[i]; + currEntry.fmt_time = moment.unix(currEntry.ts).format("llll"); $scope.entries.push(currEntry); } if (entryList.length == 0) { From 92df971fef565415e8122e45fc1d7c03b096a638 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Tue, 1 Nov 2016 17:39:43 -0700 Subject: [PATCH 02/10] Remove the app preferences plugin since we use local storage for all user stuff And have a direct method to check/set consent for the native code. --- package.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 26a3241ee..c2b743fad 100644 --- a/package.json +++ b/package.json @@ -72,10 +72,6 @@ "locator": "https://github.com/e-mission/cordova-server-sync.git", "id": "edu.berkeley.eecs.emission.cordova.serversync" }, - { - "locator": "https://github.com/apla/me.apla.cordova.app-preferences", - "id": "cordova-plugin-app-preferences" - }, "ionic-plugin-deploy", "cordova-plugin-crosswalk-webview", { @@ -95,4 +91,4 @@ "ios", "android" ] -} \ No newline at end of file +} From e5d9a87e591fe2b0967c97fe0a335bc04ccfbb47 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Tue, 1 Nov 2016 17:41:15 -0700 Subject: [PATCH 03/10] Clear all storage In addition to clearing the usercache, also clear all local storage. This makes the actual behavior more consistent with something that says "Nuke all buffers and cache". --- www/js/control.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/www/js/control.js b/www/js/control.js index e89aeb3b8..5ba49511c 100644 --- a/www/js/control.js +++ b/www/js/control.js @@ -3,11 +3,12 @@ angular.module('emission.main.control',['emission.services', 'emission.splash.startprefs', 'emission.splash.updatecheck', - 'emission.main.metrics.factory']) + 'emission.main.metrics.factory', + 'angularLocalStorage']) .controller('ControlCtrl', function($scope, $window, $ionicScrollDelegate, $state, $ionicPopup, $ionicActionSheet, $ionicPopover, - $rootScope, StartPrefs, ControlHelper, UpdateCheck, + $rootScope, storage, StartPrefs, ControlHelper, UpdateCheck, CalorieCal) { $scope.emailLog = ControlHelper.emailLog; $scope.dark_theme = $rootScope.dark_theme; @@ -187,6 +188,7 @@ angular.module('emission.main.control',['emission.services', $ionicPopup.alert({template: "WATCH OUT! If there is unsynced data, you may lose it. If you want to keep the data, use 'Force Sync' before doing this"}) .then(function(result) { if (result) { + storage.clearAll(); window.cordova.plugins.BEMUserCache.clearAll() .then(function(result) { $scope.$apply(function() { From c9dfd56940271922223cf52d4a0c9cea112975b7 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Tue, 1 Nov 2016 17:48:59 -0700 Subject: [PATCH 04/10] Fix the recent tab I know that nobody cares about this tab but me, but I like to keep my functionality working without regression. And the recent tab is actually useful! In order to match https://github.com/e-mission/cordova-usercache/pull/25/commits/9ccd387b8be8b2a81b713bcdc4615c3b87002cb4 from https://github.com/e-mission/cordova-usercache/pull/25 ensure that we pass in the withMetadata argument for each usercache get function --- www/js/common/services.js | 2 +- www/js/diary/services.js | 2 +- www/js/recent.js | 11 +++++++---- www/js/services.js | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/www/js/common/services.js b/www/js/common/services.js index d2d9aa4c7..d74f964a5 100644 --- a/www/js/common/services.js +++ b/www/js/common/services.js @@ -18,7 +18,7 @@ angular.module('emission.main.common.services', []) }; commonGraph.updateCurrent = function() { - db.getDocument(selKey).then(function(entryList) { + db.getDocument(selKey, false).then(function(entryList) { try{ var cmGraph = entryList; if (db.isEmptyDoc(cmGraph)) { diff --git a/www/js/diary/services.js b/www/js/diary/services.js index a7470d8da..58c84abff 100644 --- a/www/js/diary/services.js +++ b/www/js/diary/services.js @@ -373,7 +373,7 @@ angular.module('emission.main.diary.services', ['emission.services', 'emission.m $ionicLoading.show({ template: 'Reading from cache...' }); - window.cordova.plugins.BEMUserCache.getDocument(getKeyForDate(day)) + window.cordova.plugins.BEMUserCache.getDocument(getKeyForDate(day), false) .then(function (timelineDoc) { if (!window.cordova.plugins.BEMUserCache.isEmptyDoc(timelineDoc)) { var tripList = timelineDoc; diff --git a/www/js/recent.js b/www/js/recent.js index e5960392d..23f9d46d7 100644 --- a/www/js/recent.js +++ b/www/js/recent.js @@ -183,20 +183,23 @@ angular.module('emission.main.recent', ['ngCordova', 'emission.services']) usercacheFn = $scope.config.key_data_mapping[$scope.selected.key]["fn"] usercacheKey = $scope.config.key_data_mapping[$scope.selected.key]["key"] } - usercacheFn(usercacheKey, function(entryList) { + usercacheFn(usercacheKey).then(function(entryList) { $scope.entries = []; $scope.$apply(function() { for (i = 0; i < entryList.length; i++) { // $scope.entries.push({metadata: {write_ts: 1, write_fmt_time: "1"}, data: "1"}) var currEntry = entryList[i]; - currEntry.data = JSON.stringify(JSON.parse(currEntry.data), null, 2); + currEntry.metadata.write_fmt_time = moment.unix(currEntry.metadata.write_ts) + .tz(currEntry.metadata.time_zone) + .format("llll"); + currEntry.data = JSON.stringify(currEntry.data, null, 2); // window.Logger.log(window.Logger.LEVEL_DEBUG, // "currEntry.data = "+currEntry.data); $scope.entries.push(currEntry); - // This should really be within a try/catch/finally block - $scope.$broadcast('scroll.refreshComplete'); } }) + // This should really be within a try/catch/finally block + $scope.$broadcast('scroll.refreshComplete'); }, function(error) { $ionicPopup.alert({template: JSON.stringify(error)}) .then(function(res) {console.log("finished showing alert");}); diff --git a/www/js/services.js b/www/js/services.js index 47094df5b..d7dcf29f6 100644 --- a/www/js/services.js +++ b/www/js/services.js @@ -175,7 +175,7 @@ angular.module('emission.services', []) }; this.getConsentDocument = function() { - return window.cordova.plugins.BEMUserCache.getDocument("config/consent"); + return window.cordova.plugins.BEMUserCache.getDocument("config/consent", false); }; }) From 716621eaac1d7407a334c8b54325c9da0f2324d6 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Wed, 2 Nov 2016 00:41:06 -0700 Subject: [PATCH 05/10] Now that we have merged the client stats with the usercache we can delete that plugin, and also delete the sqlite plugin, since we don't want to have any native + sqlite code any more. --- package.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/package.json b/package.json index c2b743fad..b0c75f8da 100644 --- a/package.json +++ b/package.json @@ -30,10 +30,6 @@ "cordova-plugin-whitelist", "cordova-plugin-statusbar", "ionic-plugin-keyboard", - { - "locator": "https://github.com/litehelpers/Cordova-sqlite-storage.git", - "id": "cordova-sqlite-storage" - }, { "locator": "https://github.com/e-mission/cordova-connection-settings.git", "id": "edu.berkeley.eecs.emission.cordova.settings" @@ -54,10 +50,6 @@ "locator": "https://github.com/e-mission/cordova-jwt-auth.git", "id": "edu.berkeley.eecs.emission.cordova.auth" }, - { - "locator": "https://github.com/e-mission/cordova-stats.git", - "id": "edu.berkeley.eecs.emission.cordova.clientstats" - }, { "locator": "https://github.com/e-mission/e-mission-data-collection.git", "id": "edu.berkeley.eecs.emission.cordova.datacollection" From 58adfe4c3b58073cd6d2c6ca4ee416275d88b9a2 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Wed, 2 Nov 2016 09:09:00 -0700 Subject: [PATCH 06/10] Handle the case where the common data is undefined because it takes too long to load This fixes #111. We were already checking for the result of trip2Common, but this didn't work if there was an error in trip2Common... --- www/js/common/services.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/www/js/common/services.js b/www/js/common/services.js index d74f964a5..acc35632f 100644 --- a/www/js/common/services.js +++ b/www/js/common/services.js @@ -9,7 +9,7 @@ angular.module('emission.main.common.services', []) var db = window.cordova.plugins.BEMUserCache; var selKey = "common-trips"; - + commonGraph.createEmpty = function() { return { 'user_id': 'unknown', 'common_trips': [], @@ -46,11 +46,21 @@ angular.module('emission.main.common.services', []) * Returns the common trip corresponding to the specified tripId */ commonGraph.trip2Common = function(tripId) { - return commonGraph.data.trip2CommonMap[tripId]; + if (angular.isDefined(commonGraph.data)) { + return commonGraph.data.trip2CommonMap[tripId]; + } else { + // return undefined because that is what the invoking locations expect + return; + } }; commonGraph.place2Common = function(placeId) { - return commonGraph.data.place2CommonMap[placeId]; + if (angular.isDefined(commonGraph.data)) { + return commonGraph.data.place2CommonMap[placeId]; + } else { + // return undefined because that is what the invoking locations expect + return; + } }; commonGraph.time_fns = {}; @@ -60,7 +70,7 @@ angular.module('emission.main.common.services', []) return localTime.hour; } var hourMap = binEntries(timeArray, binFn); - return hourMap; + return hourMap; } commonGraph.time_fns.getMostFrequentHour = function(timeArray) { var binFn = function(localTime) { @@ -162,7 +172,7 @@ angular.module('emission.main.common.services', []) obj.start_displayName = name; break; } - + }; var responseListener1 = function(data) { var address = data["address"]; @@ -183,11 +193,11 @@ angular.module('emission.main.common.services', []) } console.log("got response, setting display name to "+name); obj.end_displayName = name; - + }; switch (mode) { case 'place': - var url = "http://nominatim.openstreetmap.org/reverse?format=json&lat=" + obj.geometry.coordinates[1] + var url = "http://nominatim.openstreetmap.org/reverse?format=json&lat=" + obj.geometry.coordinates[1] + "&lon=" + obj.geometry.coordinates[0]; $http.get(url).then(function(response) { console.log("while reading data from nominatim, status = "+response.status @@ -200,7 +210,7 @@ angular.module('emission.main.common.services', []) case 'cplace': var url = "http://nominatim.openstreetmap.org/reverse?format=json&lat=" + obj.location.coordinates[1] + "&lon=" + obj.location.coordinates[0]; - + $http.get(url).then(function(response) { console.log("while reading data from nominatim, status = "+response.status +" data = "+JSON.stringify(response.data)); @@ -229,7 +239,7 @@ angular.module('emission.main.common.services', []) responseListener1(response.data); }, function(error) { console.log("while reading data from nominatim, error = "+error); - }); + }); break; } }; From 667aa722276b9480cffb0e95304f83d7cbbe85ec Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Wed, 2 Nov 2016 20:43:55 -0700 Subject: [PATCH 07/10] Remove the statusbar plugin We don't use it, and it takes ~ 100ms to load. Also comment out the one use of it in the code. --- package.json | 1 - www/js/diary/list.js | 1 - 2 files changed, 2 deletions(-) diff --git a/package.json b/package.json index b0c75f8da..7c9dc04c4 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "cordova-plugin-device", "cordova-plugin-console", "cordova-plugin-whitelist", - "cordova-plugin-statusbar", "ionic-plugin-keyboard", { "locator": "https://github.com/e-mission/cordova-connection-settings.git", diff --git a/www/js/diary/list.js b/www/js/diary/list.js index 5cc354c34..2c1fd0a60 100644 --- a/www/js/diary/list.js +++ b/www/js/diary/list.js @@ -20,7 +20,6 @@ angular.module('emission.main.diary.list',['ui-leaflet', data.leafletObject.invalidateSize(); }); - StatusBar.styleDefault(); var readAndUpdateForDay = function(day) { // This just launches the update. The update can complete in the background // based on the time when the database finishes reading. From 5ee2f9df8f60f960a3e8d28371407eb0c036a9c5 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Wed, 2 Nov 2016 20:44:48 -0700 Subject: [PATCH 08/10] Fallback to isNaN if Number.isFinite is not defined It looks like Number.isFinite is not available in version 8.4. I wonder if this is why Joan was getting the error in the dashboard. Let's fall back to isNaN in that case. --- www/js/metrics.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/www/js/metrics.js b/www/js/metrics.js index 6137b4096..b594e9209 100644 --- a/www/js/metrics.js +++ b/www/js/metrics.js @@ -376,6 +376,14 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date return getMetricsResult; } + var isValidNumber = function(number) { + if (angular.isDefined(Number.isFinite)) { + return Number.isFinite(number); + } else { + return !isNaN(number); + } + } + var getMetrics = function() { $ionicLoading.show({ template: 'Loading...' @@ -600,7 +608,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date // TODO: Refactor this so that we can filter out bad values ahead of time // instead of having to work around it here var calorieCalculation = Math.abs(Math.round((lastWeekCalories/twoWeeksAgoCalories) * 100 - 100)); - if (Number.isFinite(calorieCalculation)) { + if (isValidNumber(calorieCalculation)) { $scope.caloriesData.changeInPercentage = calorieCalculation + "%"; if(lastWeekCalories > twoWeeksAgoCalories){ $scope.caloriesData.change = " increase over a week"; @@ -697,7 +705,7 @@ angular.module('emission.main.metrics',['nvd3', 'emission.services', 'ionic-date // TODO: Refactor this so that we can filter out bad values ahead of time // instead of having to work around it here - if (Number.isFinite(calculation)) { + if (isValidNumber(calculation)) { if(lastWeekCarbonInt[0] > twoWeeksAgoCarbonInt[0]){ $scope.carbonData.change = " increase over a week"; $scope.carbonUp = true; From 621bcc68af525328610101380e0ebc42922d5441 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Wed, 2 Nov 2016 20:51:36 -0700 Subject: [PATCH 09/10] Use the correct reference to createEmpty() If there was no common doc, `createEmpty()` was called. But now that we are a factory and not a service, we need to preface the method name with the variable name. --- www/js/common/services.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/js/common/services.js b/www/js/common/services.js index acc35632f..14ba2b72b 100644 --- a/www/js/common/services.js +++ b/www/js/common/services.js @@ -22,7 +22,7 @@ angular.module('emission.main.common.services', []) try{ var cmGraph = entryList; if (db.isEmptyDoc(cmGraph)) { - cmGraph = createEmpty(); + cmGraph = commonGraph.createEmpty(); } } catch(err) { window.Logger.log("Error "+err+" while parsing common trip data"); @@ -30,7 +30,7 @@ angular.module('emission.main.common.services', []) // there is no existing cached common trips, we create a blank // version so that other things don't crash if (angular.isUndefined(cmGraph)) { - cmGraph = createEmpty(); + cmGraph = commonGraph.createEmpty(); } } commonGraph.data.graph = cmGraph; From 58b0d6320ab2ac4bad7379a8eb6dacaef6b7f386 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Wed, 2 Nov 2016 21:00:11 -0700 Subject: [PATCH 10/10] Client stats factory and usage - Add a new factory for the client stats - Use it to define client time, event and error methods - Log a stat every time we switch tabs (example of client_time) - Log errors in the tab switching (example of client_error) - Log when we forced sync (example of client_nav_event) - Add the new factory to index.html! Related usercache code is https://github.com/e-mission/cordova-usercache/commit/67ae0c9d8d6fe454d3dfded1a46d95b262f75aac server code is https://github.com/e-mission/e-mission-server/commit/7487c82578e8933f4da8f9d3fa3522c102906c81 --- www/index.html | 1 + www/js/control.js | 13 ++++-- www/js/controllers.js | 10 ++++- www/js/stats/clientstats.js | 79 +++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 www/js/stats/clientstats.js diff --git a/www/index.html b/www/index.html index 3d852b3ae..6cf9ff064 100644 --- a/www/index.html +++ b/www/index.html @@ -62,6 +62,7 @@ + diff --git a/www/js/control.js b/www/js/control.js index 5ba49511c..a82e06751 100644 --- a/www/js/control.js +++ b/www/js/control.js @@ -4,12 +4,13 @@ angular.module('emission.main.control',['emission.services', 'emission.splash.startprefs', 'emission.splash.updatecheck', 'emission.main.metrics.factory', + 'emission.stats.clientstats', 'angularLocalStorage']) .controller('ControlCtrl', function($scope, $window, $ionicScrollDelegate, $state, $ionicPopup, $ionicActionSheet, $ionicPopover, - $rootScope, storage, StartPrefs, ControlHelper, UpdateCheck, - CalorieCal) { + $rootScope, storage, StartPrefs, ControlHelper, UpdateCheck, + CalorieCal, ClientStats) { $scope.emailLog = ControlHelper.emailLog; $scope.dark_theme = $rootScope.dark_theme; $scope.userData = [] @@ -174,7 +175,7 @@ angular.module('emission.main.control',['emission.services', $scope.showMap = function() { $state.go("root.main.map"); } - $scope.getState = function() { + $scope.getState = function() { ControlHelper.getState().then(function(response) { $scope.$apply(function() { $scope.settings.collect.state = response; @@ -187,7 +188,7 @@ angular.module('emission.main.control',['emission.services', $scope.nukeUserCache = function() { $ionicPopup.alert({template: "WATCH OUT! If there is unsynced data, you may lose it. If you want to keep the data, use 'Force Sync' before doing this"}) .then(function(result) { - if (result) { + if (result) { storage.clearAll(); window.cordova.plugins.BEMUserCache.clearAll() .then(function(result) { @@ -255,6 +256,10 @@ angular.module('emission.main.control',['emission.services', }; $scope.forceSync = function() { + ClientStats.addEvent(ClientStats.getStatKeys().BUTTON_FORCE_SYNC).then( + function() { + console.log("Added "+ClientStats.getStatKeys().BUTTON_FORCE_SYNC+" event"); + }); ControlHelper.forceSync().then(function(response) { $ionicPopup.alert({template: 'success -> '+response}); }, function(error) { diff --git a/www/js/controllers.js b/www/js/controllers.js index 07def9f84..2c4e76e24 100644 --- a/www/js/controllers.js +++ b/www/js/controllers.js @@ -3,6 +3,7 @@ angular.module('emission.controllers', ['emission.splash.updatecheck', 'emission.splash.startprefs', 'emission.splash.referral', + 'emission.stats.clientstats', 'customURLScheme']) .controller('RootCtrl', function($scope) {}) @@ -10,11 +11,12 @@ angular.module('emission.controllers', ['emission.splash.updatecheck', .controller('DashCtrl', function($scope) {}) .controller('SplashCtrl', function($scope, $state, $interval, $rootScope, - CustomURLScheme, UpdateCheck, StartPrefs, ReferralHandler) { + CustomURLScheme, UpdateCheck, StartPrefs, ReferralHandler, ClientStats) { console.log('SplashCtrl invoked'); // alert("attach debugger!"); CustomURLScheme.onLaunch(function(event, url){ console.log("GOT URL:"+url); + var kvList = ReferralHandler.parseURL(url); // There are 3 types of users in total if (kvList.route == 'join') { @@ -35,16 +37,22 @@ angular.module('emission.controllers', ['emission.splash.updatecheck', UpdateCheck.checkForUpdates(); $rootScope.checkedForUpdates = true; } */ + ClientStats.addReading(ClientStats.getStatKeys().STATE_CHANGED, + fromState.name + '-2-' + toState.name).then(function() {}, function() {}); }); $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error){ console.log("Error "+error+" while changing state from "+JSON.stringify(fromState) +" to "+JSON.stringify(toState)); + ClientStats.addError(ClientStats.getStatKeys().STATE_CHANGED, + fromState.name + '-2-' + toState.name+ "_" + error).then(function() {}, function() {}); }); $rootScope.$on('$stateNotFound', function(event, unfoundState, fromState, fromParams){ console.log("unfoundState.to = "+unfoundState.to); // "lazy.state" console.log("unfoundState.toParams = " + unfoundState.toParams); // {a:1, b:2} console.log("unfoundState.options = " + unfoundState.options); // {inherit:false} + default options + ClientStats.addError(ClientStats.getStatKeys().STATE_CHANGED, + fromState.name + '-2-' + unfoundState.name).then(function() {}, function() {}); }); var isInList = function(element, list) { diff --git a/www/js/stats/clientstats.js b/www/js/stats/clientstats.js new file mode 100644 index 000000000..3e5a4b3b4 --- /dev/null +++ b/www/js/stats/clientstats.js @@ -0,0 +1,79 @@ +'use strict'; + +angular.module('emission.stats.clientstats', []) + +.factory('ClientStats', function($window) { + var clientStat = {}; + + clientStat.CLIENT_TIME = "stats/client_time"; + clientStat.CLIENT_ERROR = "stats/client_error"; + clientStat.CLIENT_NAV_EVENT = "stats/client_nav_event"; + + clientStat.getStatKeys = function() { + return { + STATE_CHANGED: "state_changed", + BUTTON_FORCE_SYNC: "button_sync_forced" + }; + } + + clientStat.getDB = function() { + if (angular.isDefined($window) && angular.isDefined($window.cordova) && + angular.isDefined($window.cordova.plugins)) { + return $window.cordova.plugins.BEMUserCache; + } else { + return; // undefined + } + } + + clientStat.getAppVersion = function() { + if (angular.isDefined(clientStat.appVersion)) { + return clientStat.appVersion; + } else { + if (angular.isDefined($window) && angular.isDefined($window.cordova) && + angular.isDefined($window.cordova.getAppVersion)) { + $window.cordova.getAppVersion.getVersionNumber().then(function(version) { + clientStat.appVersion = version; + }); + } + return; + } + } + + clientStat.getStatsEvent = function(name, reading) { + var ts_sec = Date.now() / 1000; + var appVersion = clientStat.getAppVersion(); + return { + 'name': name, + 'ts': ts_sec, + 'reading': reading, + 'client_app_version': appVersion, + 'client_os_version': $window.device.version + }; + } + clientStat.addReading = function(name, reading) { + var db = clientStat.getDB(); + if (angular.isDefined(db)) { + return db.putMessage(clientStat.CLIENT_TIME, + clientStat.getStatsEvent(name, reading)); + } + } + + clientStat.addEvent = function(name) { + var db = clientStat.getDB(); + if (angular.isDefined(db)) { + return db.putMessage(clientStat.CLIENT_NAV_EVENT, + clientStat.getStatsEvent(name, null)); + } + } + + clientStat.addError = function(name, errorStr) { + var db = clientStat.getDB(); + if (angular.isDefined(db)) { + return db.putMessage(clientStat.CLIENT_ERROR, + clientStat.getStatsEvent(name, errorStr)); + } + } + + return clientStat; +}) +