Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ongoing PR to track the progress of modularizing and modernizing user input handling #799

Merged
merged 30 commits into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9a6bd26
Create a very simple directive and include it in the list view
shankari Sep 27, 2021
89da98f
Have the directive include the actual functionality instead of a plac…
shankari Sep 27, 2021
94d1dc9
Add a "next" pointer to each trip
shankari Sep 27, 2021
52c3326
Some cleanup before the next big stage
shankari Sep 27, 2021
a010db8
Add class "col" to the multilabel
shankari Sep 27, 2021
fc10c6c
Pull out the controller code into a separate module
shankari Sep 27, 2021
2624cfb
Convert the trip-confirm-service into a real singleton
shankari Sep 28, 2021
c9621c3
Final cleanup of the main list code
shankari Sep 29, 2021
8a4201d
First step at unifying the diary and label screens
shankari Sep 29, 2021
eb1fd97
Initial version of the "verify check" button linked to the multilabel…
shankari Oct 2, 2021
0f22450
Link the two directives completely and generalizably
shankari Oct 2, 2021
37439dc
Labeling code: infinite scroll list -> directive
shankari Oct 4, 2021
8bc5185
Remove view controller's dependency on one specfic set of filters
shankari Oct 4, 2021
b382b8f
Pull the populate code out into a service
shankari Oct 4, 2021
5074b4e
Address performance issue by pre-populating and removing the directiv…
shankari Oct 4, 2021
354dcd8
First round of unification
shankari Oct 4, 2021
3ec5734
Simplify the invocation of `getUserInputForTrip`
shankari Oct 4, 2021
8a6d4b5
Simplify and unify of the diary and label screens
shankari Oct 4, 2021
a96194d
Second round of unification/cleanup
shankari Oct 4, 2021
a3623e1
patch(SurveyLaunch): startSurveyPrefilled support auto close
atton16 Sep 28, 2021
2b02d7d
Invoke additional methods directly from the DiaryHelper
shankari Oct 5, 2021
9d677bf
Created a more principled directory structure for the surveys
shankari Oct 5, 2021
5ac9e71
Final round of renaming
shankari Oct 5, 2021
3d315a3
Final attempt at generalization for now
shankari Oct 5, 2021
e8f8a0c
Really final set of updates
shankari Oct 5, 2021
2551337
Final cleanup fixes
shankari Oct 5, 2021
19d03cd
Finally get the survey to also be dynamic
shankari Oct 6, 2021
d9cc13b
Copy over the inferred labels to match the code
shankari Oct 6, 2021
4ac493c
Fix the `verifyTrip` code to use `$scope.trip` instead of `trip`
shankari Oct 6, 2021
f3ea875
Refactor the retrieval code for simplicity and performance
shankari Oct 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,17 @@
<script src="js/intro.js"></script>
<script src="js/main.js"></script>
<script src="js/recent.js"></script>
<script src="js/survey/survey.js"></script>
<script src="js/survey/one-click-button.js"></script>
<script src="js/incident/post-trip-manual.js"></script>
<script src="js/tripconfirm/post-trip-prompt.js"></script>
<script src="js/tripconfirm/post-trip-map-display.js"></script>
<script src="js/tripconfirm/trip-confirm-services.js"></script>
<script src="js/survey/multilabel/infinite_scroll_filters.js"></script>
<script src="js/survey/multilabel/post-trip-prompt.js"></script>
<script src="js/survey/multilabel/post-trip-map-display.js"></script>
<script src="js/survey/multilabel/trip-confirm-services.js"></script>
<script src="js/survey/multilabel/multi-label-ui.js"></script>
<script src="js/diary.js"></script>
<script src="js/diary/list.js"></script>
<script src="js/diary/infinite_scroll_list.js"></script>
<script src="js/diary/infinite_scroll_filters.js"></script>
<script src="js/diary/current.js"></script>
<script src="js/diary/detail.js"></script>
<script src="js/diary/services.js"></script>
Expand All @@ -96,7 +99,7 @@
<script src="js/common/place-list.js"></script>
<script src="js/common/place-detail.js"></script>
<script src="js/common/services.js"></script>
<script src="js/survey/launch.js"></script>
<script src="js/survey/external/launch.js"></script>
<script src="js/heatmap.js"></script>
<script src="js/metrics.js"></script>
<script src="js/goals.js"></script>
Expand Down
5 changes: 2 additions & 3 deletions www/js/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ angular.module('emission.controllers', ['emission.splash.updatecheck',
'emission.splash.pushnotify',
'emission.splash.storedevicesettings',
'emission.splash.localnotify',
'emission.survey.launch',
'emission.stats.clientstats',
'emission.tripconfirm.posttrip.prompt'])
'emission.survey.multilabel.posttrip.prompt'])

.controller('RootCtrl', function($scope) {})

.controller('DashCtrl', function($scope) {})

.controller('SplashCtrl', function($scope, $state, $interval, $rootScope,
UpdateCheck, StartPrefs, PushNotify, StoreDeviceSettings,
LocalNotify, ClientStats, PostTripAutoPrompt, SurveyLaunch) {
LocalNotify, ClientStats, PostTripAutoPrompt) {
console.log('SplashCtrl invoked');
// alert("attach debugger!");
// PushNotify.startupInit();
Expand Down
489 changes: 25 additions & 464 deletions www/js/diary/infinite_scroll_list.js

Large diffs are not rendered by default.

284 changes: 62 additions & 222 deletions www/js/diary/list.js

Large diffs are not rendered by default.

186 changes: 96 additions & 90 deletions www/js/diary/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

angular.module('emission.main.diary.services', ['emission.plugin.logger',
'emission.services', 'emission.main.common.services',
'emission.incident.posttrip.manual', 'emission.tripconfirm.services'])
'emission.incident.posttrip.manual', 'emission.survey.multilabel.services'])
.factory('DiaryHelper', function(CommonGraph, PostTripManualMarker, $translate){
var dh = {};
// dh.expandEarlierOrLater = function(id) {
Expand Down Expand Up @@ -155,6 +155,11 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',
return (dist_in_meters/1000).toFixed(3);
}
}

dh.getFormattedDistanceInMiles = function(input) {
return (0.621371 * dh.getFormattedDistance(input)).toFixed(1);
}

dh.getSectionDetails = function(section) {
var startMoment = moment(section.properties.start_ts * 1000);
var endMoment = moment(section.properties.end_ts * 1000);
Expand Down Expand Up @@ -405,62 +410,62 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',
" " + ui.data.label + " logged at "+ ui.metadata.write_ts;
}

dh.getUserInputForTrip = function(tripgj, nextTripgj, userInputList) {
dh.getUserInputForTrip = function(trip, nextTrip, userInputList) {
if (userInputList.length < 20) {
console.log("Input list = "+userInputList.map(printUserInput));
}
var tripProp = tripgj.data.properties;
var isDraft = dh.isDraft(tripgj);
// undefined != true, so this covers the label view case as well
var isDraft = trip.isDraft == true;
var potentialCandidates = userInputList.filter(function(userInput) {
/*
console.log("startDelta "+userInput.data.label+
"= user("+fmtTs(userInput.data.start_ts, userInput.metadata.time_zone)+
") - trip("+fmtTs(userInput.data.start_ts, userInput.metadata.time_zone)+") = "+
(userInput.data.start_ts - tripProp.start_ts)+" should be positive");
(userInput.data.start_ts - trip.start_ts)+" should be positive");
console.log("endDelta = "+userInput.data.label+
"user("+fmtTs(userInput.data.end_ts, userInput.metadata.time_zone)+
") - trip("+fmtTs(tripProp.end_ts, userInput.metadata.time_zone)+") = "+
(userInput.data.end_ts - tripProp.end_ts)+" should be negative");
") - trip("+fmtTs(trip.end_ts, userInput.metadata.time_zone)+") = "+
(userInput.data.end_ts - trip.end_ts)+" should be negative");
*/
// logic described in
// https://github.com/e-mission/e-mission-docs/issues/423
if (isDraft) {
if (userInputList.length < 20) {
var logStr = "Draft trip: comparing user = "+fmtTs(userInput.data.start_ts, userInput.metadata.time_zone)
+" -> "+fmtTs(userInput.data.end_ts, userInput.metadata.time_zone)
+" trip = "+fmtTs(tripProp.start_ts, userInput.metadata.time_zone)
+" -> "+fmtTs(tripProp.end_ts, userInput.metadata.time_zone)
+" checks are ("+(userInput.data.start_ts >= tripProp.start_ts)
+" && "+(userInput.data.start_ts <= tripProp.end_ts)
+" || "+(-(userInput.data.start_ts - tripProp.start_ts) <= 15 * 60)
+") && "+(userInput.data.end_ts <= tripProp.end_ts);
+" trip = "+fmtTs(trip.start_ts, userInput.metadata.time_zone)
+" -> "+fmtTs(trip.end_ts, userInput.metadata.time_zone)
+" checks are ("+(userInput.data.start_ts >= trip.start_ts)
+" && "+(userInput.data.start_ts <= trip.end_ts)
+" || "+(-(userInput.data.start_ts - trip.start_ts) <= 15 * 60)
+") && "+(userInput.data.end_ts <= trip.end_ts);
console.log(logStr);
// Logger.log(logStr);
}
return (userInput.data.start_ts >= tripProp.start_ts
&& userInput.data.start_ts <= tripProp.end_ts
|| -(userInput.data.start_ts - tripProp.start_ts) <= 15 * 60)
&& userInput.data.end_ts <= tripProp.end_ts;
return (userInput.data.start_ts >= trip.start_ts
&& userInput.data.start_ts <= trip.end_ts
|| -(userInput.data.start_ts - trip.start_ts) <= 15 * 60)
&& userInput.data.end_ts <= trip.end_ts;
} else {
// we know that the trip is cleaned so we can use the fmt_time
// but the confirm objects are not necessarily filled out
if (userInputList.length < 20) {
var logStr = "Cleaned trip: comparing user = "
+fmtTs(userInput.data.start_ts, userInput.metadata.time_zone)
+" -> "+fmtTs(userInput.data.end_ts, userInput.metadata.time_zone)
+" trip = "+tripProp.start_fmt_time
+" -> "+tripProp.end_fmt_time
+" start checks are "+(userInput.data.start_ts >= tripProp.start_ts)
+" && "+(userInput.data.start_ts <= tripProp.end_ts)
+" end checks are "+(userInput.data.end_ts <= tripProp.end_ts)
+" || "+((userInput.data.end_ts - tripProp.end_ts) <= 15 * 60)+")";
+" trip = "+trip.start_fmt_time
+" -> "+trip.end_fmt_time
+" start checks are "+(userInput.data.start_ts >= trip.start_ts)
+" && "+(userInput.data.start_ts <= trip.end_ts)
+" end checks are "+(userInput.data.end_ts <= trip.end_ts)
+" || "+((userInput.data.end_ts - trip.end_ts) <= 15 * 60)+")";
Logger.log(logStr);
}
// https://github.com/e-mission/e-mission-docs/issues/476#issuecomment-747222181
const startChecks = userInput.data.start_ts >= tripProp.start_ts &&
userInput.data.start_ts <= tripProp.end_ts;
var endChecks = (userInput.data.end_ts <= tripProp.end_ts ||
(userInput.data.end_ts - tripProp.end_ts) <= 15 * 60);
const startChecks = userInput.data.start_ts >= trip.start_ts &&
userInput.data.start_ts <= trip.end_ts;
var endChecks = (userInput.data.end_ts <= trip.end_ts ||
(userInput.data.end_ts - trip.end_ts) <= 15 * 60);
if (startChecks && !endChecks) {
if (angular.isDefined(nextTripgj)) {
endChecks = userInput.data.end_ts <= nextTripgj.data.properties.start_ts;
Expand All @@ -474,10 +479,10 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',
if (endChecks) {
// If we have flipped the values, check to see that there
// is sufficient overlap
const overlapDuration = Math.min(userInput.data.end_ts, tripProp.end_ts) - Math.max(userInput.data.start_ts, tripProp.start_ts)
const overlapDuration = Math.min(userInput.data.end_ts, trip.end_ts) - Math.max(userInput.data.start_ts, trip.start_ts)
Logger.log("Flipped endCheck, overlap("+overlapDuration+
")/trip("+tripProp.duration+") = "+ (overlapDuration / tripProp.duration));
endChecks = (overlapDuration/tripProp.duration) > 0.5;
")/trip("+trip.duration+") = "+ (overlapDuration / trip.duration));
endChecks = (overlapDuration/trip.duration) > 0.5;
}
}
return startChecks && endChecks;
Expand Down Expand Up @@ -1105,34 +1110,20 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',

var localCacheReadFn = timeline.updateFromDatabase;

// Functions
timeline.updateForDay = function(day) { // currDay is a moment
// First, we try the server
var tripsFromServerPromise = timeline.updateFromServer(day);
var isProcessingCompletePromise = timeline.isProcessingComplete(day);

// Also mode/purpose and (currently disabled) survey answers
var tq = $window.cordova.plugins.BEMUserCache.getAllTimeQuery();
var manualPromises = ConfirmHelper.INPUTS.map(function(inp) {
return UnifiedDataLoader.getUnifiedMessagesForInterval(
ConfirmHelper.inputDetails[inp].key, tq);
});
// var surveyAnswersPromise = EnketoSurvey.getAllSurveyAnswers("manual/confirm_survey", { populateLabels: true });

// Deal with all the trip retrieval
Promise.all([tripsFromServerPromise, isProcessingCompletePromise].concat(manualPromises))
.then(function([processedTripList, completeStatus, ...manualResults]) {
console.log("Promise.all() finished successfully with length "
+processedTripList.length+" completeStatus = "+completeStatus);
var mrString = 'with ' + manualResults.map(function(item, index) {
return ' ${mr.length} ${ConfirmHelper.INPUTS[index]}';
});
var processManualInputs = function(manualResults) {
var mrString = 'unprocessed manual inputs '
+ manualResults.map(function(item, index) {
return ` ${item.length} ${ConfirmHelper.INPUTS[index]}`;
});
console.log(mrString);
var tripList = processedTripList;
timeline.data.unifiedConfirmsResults = {}
manualResults.forEach(function(mr, index) {
timeline.data.unifiedConfirmsResults[ConfirmHelper.INPUTS[index]] = mr;
});
}

var addUnprocessedTrips = function(processedTripList, day, completeStatus) {
var tripList = processedTripList;
if (!completeStatus) {
return timeline.readUnprocessedTrips(day, processedTripList)
.then(function(unprocessedTripList) {
Expand All @@ -1145,46 +1136,61 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',
} else {
return tripList;
}
}).then(function(combinedTripList) {
processOrDisplayNone(day, combinedTripList);
}).catch(function(error) {
// If there is any error reading from the server, we fallback on the local cache
Logger.log("while reading data from server for "+day +" error = "+JSON.stringify(error));
console.log("About to hide loading overlay");
$ionicLoading.hide();

var tripsFromCachePromise = localCacheReadFn(day);
}

// Also mode/purpose and (currently disabled) survey answers
var tq = $window.cordova.plugins.BEMUserCache.getAllTimeQuery();
var manualPromises = ConfirmHelper.INPUTS.map(function(inp) {
return UnifiedDataLoader.getUnifiedMessagesForInterval(
var readTripsAndUnprocessedInputs = function(day, tripReadFn, completeStatus, tq) {
var manualPromises = ConfirmHelper.INPUTS.map(function(inp) {
return UnifiedDataLoader.getUnifiedMessagesForInterval(
ConfirmHelper.inputDetails[inp].key, tq);
});
Promise.all([tripsFromCachePromise].concat(manualPromises)).then(function(
[processedTripList, ...manualResults]) {
console.log(' in local cache, found ${modes.length} modes, ${purposes.length} purposes');
var tripList = processedTripList;
timeline.data.unifiedConfirmsResults = {}
manualResults.forEach(function(mr, index) {
timeline.data.unifiedConfirmsResults[ConfirmHelper.INPUTS[index]] = mr;
});
return timeline.readUnprocessedTrips(day, processedTripList)
.then(function(unprocessedTripList) {
Logger.log("tripList.length = "+tripList.length
+"unprocessedTripList.length = "+unprocessedTripList.length);
Array.prototype.push.apply(tripList, unprocessedTripList);
console.log("After merge, returning trip list of size "+tripList.length);
return tripList;
})
}).then(function(combinedTripList) {
processOrDisplayNone(day, combinedTripList);
}).catch(function(error) {
console.log("About to hide loading overlay");
$ionicLoading.hide();
Logger.displayError("while reading data from cache for "+day, error);
})
});
let tripsReadPromise = tripReadFn(day);
// var surveyAnswersPromise = EnketoSurvey.getAllSurveyAnswers("manual/confirm_survey", { populateLabels: true });
let allManualPromise = Promise.all(manualPromises).then(processManualInputs);

let allTripsPromise = tripsReadPromise.then((processedTripList) => {
console.log("Reading trips from server finished successfully with length "
+processedTripList.length+" completeStatus = "+completeStatus);
return addUnprocessedTrips(processedTripList, completeStatus);
}).then((combinedTripList) => processOrDisplayNone(day, combinedTripList));
return Promise.all([allManualPromise, allTripsPromise]).then(() => {
console.log("Finished reading processed/unprocessed trips with length "
+timeline.data.currDayTrips.length);
});
}

// Functions
timeline.updateForDay = function(day) { // currDay is a moment
// First, we try the server
var isProcessingCompletePromise = timeline.isProcessingComplete(day);

// First get the pipeline complete timestamp
isProcessingCompletePromise.then((completeTs, completeStatus) => {
// then, in parallel, read unprocessed user inputs
// and trips
// Also mode/purpose and (currently disabled) survey answers
var pendingTq = {
key: "write_ts",
startTs: completeTs,
endTs: moment().unix()
};
readTripsAndUnprocessedInputs(day, timeline.updateFromServer,
completeStatus, pendingTq)
.catch(function(error) {
// If there is any error reading from the server, we fallback on the local cache
Logger.log("while reading data from server for "+day +" error = "+JSON.stringify(error));
console.log("About to hide loading overlay");
$ionicLoading.hide();

// Also mode/purpose and (currently disabled) survey answers
let allTq = $window.cordova.plugins.BEMUserCache.getAllTimeQuery();
readTripsAndUnprocessedInputs(day, localCacheReadFn, undefined, allTq)
.catch(function(error) {
console.log("About to hide loading overlay");
$ionicLoading.hide();
Logger.displayError("while reading data from cache for "+day, error);
})
});
});
}

timeline.getTrip = function(tripId) {
Expand Down
2 changes: 1 addition & 1 deletion www/js/goals.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

angular.module('emission.main.goals',['emission.services', 'emission.plugin.logger',
'emission.plugin.kvstore',
'emission.survey.launch',
'emission.survey.external.launch',
'ngSanitize', 'ngAnimate',
'emission.splash.referral',
'ng-walkthrough', 'nzTour'])
Expand Down
1 change: 1 addition & 0 deletions www/js/intro.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

angular.module('emission.intro', ['emission.splash.startprefs',
'emission.splash.updatecheck',
'emission.survey.external.launch',
'emission.i18n.utils',
'ionic-toast'])

Expand Down
2 changes: 1 addition & 1 deletion www/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ angular.module('emission.main', ['emission.main.recent',
'emission.main.common',
'emission.main.heatmap',
'emission.main.metrics',
'emission.tripconfirm.posttrip.map',
'emission.survey.multilabel.posttrip.map',
'emission.services',
'emission.services.upload'])

Expand Down
2 changes: 2 additions & 0 deletions www/js/stats/clientstats.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ angular.module('emission.stats.clientstats', [])
return {
STATE_CHANGED: "state_changed",
BUTTON_FORCE_SYNC: "button_sync_forced",
CHECKED_DIARY: "checked_diary",
DIARY_TIME: "diary_time",
CHECKED_INF_SCROLL: "checked_inf_scroll",
INF_SCROLL_TIME: "inf_scroll_time",
VERIFY_TRIP: "verify_trip",
Expand Down
Loading