Skip to content

Commit

Permalink
Merge pull request #1064 from the-bay-kay/UnifiedDataLoader-rewrite
Browse files Browse the repository at this point in the history
🏗️🗃️ UnifiedDataLoader rewrite
  • Loading branch information
shankari authored Nov 11, 2023
2 parents 4992e8d + 7e8f07b commit cf9eee0
Show file tree
Hide file tree
Showing 46 changed files with 367 additions and 299 deletions.
2 changes: 1 addition & 1 deletion www/__tests__/commHelper.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mockLogger } from '../__mocks__/globalMocks';
import { fetchUrlCached } from '../js/commHelper';
import { fetchUrlCached } from '../js/services/commHelper';

mockLogger();

Expand Down
2 changes: 1 addition & 1 deletion www/__tests__/confirmHelper.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { mockLogger } from '../__mocks__/globalMocks';
import * as CommHelper from '../js/commHelper';
import * as CommHelper from '../js/services/commHelper';
import {
baseLabelInputDetails,
getLabelInputDetails,
Expand Down
2 changes: 1 addition & 1 deletion www/__tests__/storeDeviceSettings.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readConsentState, markConsented } from '../js/splash/startprefs';
import { storageClear } from '../js/plugin/storage';
import { getUser } from '../js/commHelper';
import { getUser } from '../js/services/commHelper';
import { initStoreDeviceSettings, teardownDeviceSettings } from '../js/splash/storeDeviceSettings';
import {
mockBEMDataCollection,
Expand Down
150 changes: 150 additions & 0 deletions www/__tests__/unifiedDataLoader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { mockLogger } from '../__mocks__/globalMocks';
import { removeDup, combinedPromises } from '../js/services/unifiedDataLoader';
import { ServerData } from '../js/types/serverData';

mockLogger();

const testOne: ServerData<any> = {
data: '',
metadata: {
key: '',
platform: '',
write_ts: 1, // the only value checked by removeDup
time_zone: '',
write_fmt_time: '',
write_local_dt: null,
},
};

const testTwo = JSON.parse(JSON.stringify(testOne));
testTwo.metadata.write_ts = 2;
const testThree = JSON.parse(JSON.stringify(testOne));
testThree.metadata.write_ts = 3;
const testFour = JSON.parse(JSON.stringify(testOne));
testFour.metadata.write_ts = 4;

describe('removeDup can', () => {
it('work with an empty array', () => {
expect(removeDup([])).toEqual([]);
});

it('work with an array of len 1', () => {
expect(removeDup([testOne])).toEqual([testOne]);
});

it('work with an array of len >=1', () => {
expect(removeDup([testOne, testTwo])).toEqual([testOne, testTwo]);
expect(removeDup([testOne, testOne])).toEqual([testOne]);
expect(removeDup([testOne, testTwo, testThree])).toEqual([testOne, testTwo, testThree]);
expect(removeDup([testOne, testOne, testThree])).toEqual([testOne, testThree]);
expect(removeDup([testOne, testOne, testOne])).toEqual([testOne]);
});
});

// combinedPromises tests
const promiseGenerator = (values: Array<ServerData<any>>) => {
return Promise.resolve(values);
};
const badPromiseGenerator = (input: string) => {
return Promise.reject(input);
};

it('throws an error on an empty input', async () => {
expect(() => {
combinedPromises([], removeDup);
}).toThrow();
});

it('catches when all promises fails', async () => {
expect(combinedPromises([badPromiseGenerator('')], removeDup)).rejects.toEqual(['']);
expect(
combinedPromises([badPromiseGenerator('bad'), badPromiseGenerator('promise')], removeDup),
).rejects.toEqual(['bad', 'promise']);
expect(
combinedPromises(
[badPromiseGenerator('very'), badPromiseGenerator('bad'), badPromiseGenerator('promise')],
removeDup,
),
).rejects.toEqual(['very', 'bad', 'promise']);

expect(
combinedPromises([badPromiseGenerator('bad'), promiseGenerator([testOne])], removeDup),
).resolves.toEqual([testOne]);
expect(
combinedPromises([promiseGenerator([testOne]), badPromiseGenerator('bad')], removeDup),
).resolves.toEqual([testOne]);
});

it('work with arrays of len 1', async () => {
const promiseArrayOne = [promiseGenerator([testOne])];
const promiseArrayTwo = [promiseGenerator([testOne, testTwo])];
const testResultOne = await combinedPromises(promiseArrayOne, removeDup);
const testResultTwo = await combinedPromises(promiseArrayTwo, removeDup);

expect(testResultOne).toEqual([testOne]);
expect(testResultTwo).toEqual([testOne, testTwo]);
});

it('works with arrays of len 2', async () => {
const promiseArrayOne = [promiseGenerator([testOne]), promiseGenerator([testTwo])];
const promiseArrayTwo = [promiseGenerator([testOne, testTwo]), promiseGenerator([testThree])];
const promiseArrayThree = [promiseGenerator([testOne]), promiseGenerator([testTwo, testThree])];
const promiseArrayFour = [
promiseGenerator([testOne, testTwo]),
promiseGenerator([testThree, testFour]),
];
const promiseArrayFive = [
promiseGenerator([testOne, testTwo]),
promiseGenerator([testTwo, testThree]),
];

const testResultOne = await combinedPromises(promiseArrayOne, removeDup);
const testResultTwo = await combinedPromises(promiseArrayTwo, removeDup);
const testResultThree = await combinedPromises(promiseArrayThree, removeDup);
const testResultFour = await combinedPromises(promiseArrayFour, removeDup);
const testResultFive = await combinedPromises(promiseArrayFive, removeDup);

expect(testResultOne).toEqual([testOne, testTwo]);
expect(testResultTwo).toEqual([testOne, testTwo, testThree]);
expect(testResultThree).toEqual([testOne, testTwo, testThree]);
expect(testResultFour).toEqual([testOne, testTwo, testThree, testFour]);
expect(testResultFive).toEqual([testOne, testTwo, testThree]);
});

it('works with arrays of len >= 2', async () => {
const promiseArrayOne = [
promiseGenerator([testOne]),
promiseGenerator([testTwo]),
promiseGenerator([testThree]),
];
const promiseArrayTwo = [
promiseGenerator([testOne]),
promiseGenerator([testTwo]),
promiseGenerator([testTwo]),
];
const promiseArrayThree = [
promiseGenerator([testOne]),
promiseGenerator([testTwo]),
promiseGenerator([testThree, testFour]),
];
const promiseArrayFour = [
promiseGenerator([testOne]),
promiseGenerator([testTwo, testThree]),
promiseGenerator([testFour]),
];

const testResultOne = await combinedPromises(promiseArrayOne, removeDup);
const testResultTwo = await combinedPromises(promiseArrayTwo, removeDup);
const testResultThree = await combinedPromises(promiseArrayThree, removeDup);
const testResultFour = await combinedPromises(promiseArrayFour, removeDup);

expect(testResultOne).toEqual([testOne, testTwo, testThree]);
expect(testResultTwo).toEqual([testOne, testTwo]);
expect(testResultThree).toEqual([testOne, testTwo, testThree, testFour]);
expect(testResultFour).toEqual([testOne, testTwo, testThree, testFour]);
});

/*
TO-DO: Once getRawEnteries can be tested via end-to-end testing, we will be able to
test getUnifiedDataForInterval as well.
*/
Binary file removed www/img/adam.jpg
Binary file not shown.
Binary file removed www/img/avatar_1.png
Binary file not shown.
Binary file removed www/img/banana.png
Binary file not shown.
Binary file removed www/img/ben.png
Binary file not shown.
Binary file removed www/img/cookie.png
Binary file not shown.
Binary file removed www/img/ic_header_gem.png
Binary file not shown.
Binary file removed www/img/ic_header_gold.png
Binary file not shown.
Binary file removed www/img/ic_header_healer.png
Binary file not shown.
Binary file removed www/img/ic_header_mage.png
Binary file not shown.
Binary file removed www/img/ic_header_rogue.png
Binary file not shown.
Binary file removed www/img/ic_header_silver.png
Binary file not shown.
Binary file removed www/img/ic_header_warrior.png
Binary file not shown.
Binary file removed www/img/ic_navigation_black_24dp.png
Binary file not shown.
Binary file removed www/img/icecream.png
Binary file not shown.
Binary file removed www/img/intro/splash_screen_logo.png
Binary file not shown.
Binary file removed www/img/ionic.png
Binary file not shown.
Binary file removed www/img/max.png
Binary file not shown.
Binary file removed www/img/mike.png
Binary file not shown.
Binary file removed www/img/minus.gif
Binary file not shown.
Binary file removed www/img/nileredsea_126b5740_small.png
Binary file not shown.
Binary file removed www/img/pacman.gif
Binary file not shown.
Binary file removed www/img/perry.png
Binary file not shown.
Binary file removed www/img/plus.gif
Binary file not shown.
3 changes: 1 addition & 2 deletions www/js/config/dynamicConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import i18next from 'i18next';
import { displayError, logDebug, logWarn } from '../plugin/logger';
import { getAngularService } from '../angular-react-helper';
import { fetchUrlCached } from '../commHelper';
import { fetchUrlCached } from '../services/commHelper';
import { storageClear, storageGet, storageSet } from '../plugin/storage';

export const CONFIG_PHONE_UI = 'config/app_ui_config';
Expand Down
2 changes: 1 addition & 1 deletion www/js/control/ControlSyncHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SettingRow from './SettingRow';
import AlertBar from './AlertBar';
import moment from 'moment';
import { addStatEvent, statKeys } from '../plugin/clientStats';
import { updateUser } from '../commHelper';
import { updateUser } from '../services/commHelper';

/*
* BEGIN: Simple read/write wrappers
Expand Down
25 changes: 0 additions & 25 deletions www/js/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,29 +70,4 @@ angular
},
);
console.log('SplashCtrl invoke finished');
})

.controller('ChatsCtrl', function ($scope, Chats) {
// With the new view caching in Ionic, Controllers are only called
// when they are recreated or on app start, instead of every page change.
// To listen for when this page is active (for example, to refresh data),
// listen for the $ionicView.enter event:
//
//$scope.$on('$ionicView.enter', function(e) {
//});

$scope.chats = Chats.all();
$scope.remove = function (chat) {
Chats.remove(chat);
};
})

.controller('ChatDetailCtrl', function ($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})

.controller('AccountCtrl', function ($scope) {
$scope.settings = {
enableFriends: true,
};
});
2 changes: 1 addition & 1 deletion www/js/diary/LabelTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { fillLocationNamesOfTrip, resetNominatimLimiter } from './addressNamesHe
import { getLabelOptions } from '../survey/multilabel/confirmHelper';
import { displayError, displayErrorMsg, logDebug, logWarn } from '../plugin/logger';
import { useTheme } from 'react-native-paper';
import { getPipelineRangeTs } from '../commHelper';
import { getPipelineRangeTs } from '../services/commHelper';
import { mapInputsToTimelineEntries } from '../survey/inputMatcher';
import { configuredFilters as multilabelConfiguredFilters } from '../survey/multilabel/infinite_scroll_filters';
import { configuredFilters as enketoConfiguredFilters } from '../survey/enketo/infinite_scroll_filters';
Expand Down
122 changes: 58 additions & 64 deletions www/js/diary/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,14 @@

import angular from 'angular';
import { getConfig } from '../config/dynamicConfig';
import { getRawEntries } from '../commHelper';
import { getRawEntries } from '../services/commHelper';
import { getUnifiedDataForInterval } from '../services/unifiedDataLoader';

angular
.module('emission.main.diary.services', ['emission.plugin.logger', 'emission.services'])
.factory(
'Timeline',
function (
$http,
$ionicLoading,
$ionicPlatform,
$window,
$rootScope,
UnifiedDataLoader,
Logger,
$injector,
) {
function ($http, $ionicLoading, $ionicPlatform, $window, $rootScope, Logger, $injector) {
var timeline = {};
// corresponds to the old $scope.data. Contains all state for the current
// day, including the indication of the current day
Expand Down Expand Up @@ -256,64 +248,65 @@ angular
' -> ' +
moment.unix(tripEndTransition.data.ts).toString(),
);
return UnifiedDataLoader.getUnifiedSensorDataForInterval(
'background/filtered_location',
tq,
).then(function (locationList) {
if (locationList.length == 0) {
return undefined;
}
var sortedLocationList = locationList.sort(tsEntrySort);
var retainInRange = function (loc) {
return (
tripStartTransition.data.ts <= loc.data.ts && loc.data.ts <= tripEndTransition.data.ts
);
};
const getMethod = window['cordova'].plugins.BEMUserCache.getSensorDataForInterval;
return getUnifiedDataForInterval('background/filtered_location', tq, getMethod).then(
function (locationList) {
if (locationList.length == 0) {
return undefined;
}
var sortedLocationList = locationList.sort(tsEntrySort);
var retainInRange = function (loc) {
return (
tripStartTransition.data.ts <= loc.data.ts &&
loc.data.ts <= tripEndTransition.data.ts
);
};

var filteredLocationList = sortedLocationList.filter(retainInRange);
var filteredLocationList = sortedLocationList.filter(retainInRange);

// Fix for https://github.com/e-mission/e-mission-docs/issues/417
if (filteredLocationList.length == 0) {
return undefined;
}
// Fix for https://github.com/e-mission/e-mission-docs/issues/417
if (filteredLocationList.length == 0) {
return undefined;
}

var tripStartPoint = filteredLocationList[0];
var tripEndPoint = filteredLocationList[filteredLocationList.length - 1];
Logger.log(
'tripStartPoint = ' +
JSON.stringify(tripStartPoint) +
'tripEndPoint = ' +
JSON.stringify(tripEndPoint),
);
// if we get a list but our start and end are undefined
// let's print out the complete original list to get a clue
// this should help with debugging
// https://github.com/e-mission/e-mission-docs/issues/417
// if it ever occurs again
if (angular.isUndefined(tripStartPoint) || angular.isUndefined(tripEndPoint)) {
Logger.log('BUG 417 check: locationList = ' + JSON.stringify(locationList));
var tripStartPoint = filteredLocationList[0];
var tripEndPoint = filteredLocationList[filteredLocationList.length - 1];
Logger.log(
'transitions: start = ' +
JSON.stringify(tripStartTransition.data) +
' end = ' +
JSON.stringify(tripEndTransition.data.ts),
'tripStartPoint = ' +
JSON.stringify(tripStartPoint) +
'tripEndPoint = ' +
JSON.stringify(tripEndPoint),
);
}
// if we get a list but our start and end are undefined
// let's print out the complete original list to get a clue
// this should help with debugging
// https://github.com/e-mission/e-mission-docs/issues/417
// if it ever occurs again
if (angular.isUndefined(tripStartPoint) || angular.isUndefined(tripEndPoint)) {
Logger.log('BUG 417 check: locationList = ' + JSON.stringify(locationList));
Logger.log(
'transitions: start = ' +
JSON.stringify(tripStartTransition.data) +
' end = ' +
JSON.stringify(tripEndTransition.data.ts),
);
}

const tripProps = points2TripProps(filteredLocationList);

return {
...tripProps,
start_loc: {
type: 'Point',
coordinates: [tripStartPoint.data.longitude, tripStartPoint.data.latitude],
},
end_loc: {
type: 'Point',
coordinates: [tripEndPoint.data.longitude, tripEndPoint.data.latitude],
},
};
});
const tripProps = points2TripProps(filteredLocationList);

return {
...tripProps,
start_loc: {
type: 'Point',
coordinates: [tripStartPoint.data.longitude, tripStartPoint.data.latitude],
},
end_loc: {
type: 'Point',
coordinates: [tripEndPoint.data.longitude, tripEndPoint.data.latitude],
},
};
},
);
};

var linkTrips = function (trip1, trip2) {
Expand Down Expand Up @@ -342,7 +335,8 @@ angular
' -> ' +
moment.unix(tq.endTs).toString(),
);
return UnifiedDataLoader.getUnifiedMessagesForInterval('statemachine/transition', tq).then(
const getMethod = window['cordova'].plugins.BEMUserCache.getMessagesForInterval;
return getUnifiedDataForInterval('statemachine/transition', tq, getMethod).then(
function (transitionList) {
if (transitionList.length == 0) {
Logger.log('No unprocessed trips. yay!');
Expand Down
Loading

0 comments on commit cf9eee0

Please sign in to comment.