Skip to content

Commit

Permalink
Move the input matching code out of the DiaryHelper
Browse files Browse the repository at this point in the history
Two main reasons:
- It really doesn't belong there; the rest of the functions there deal with
  formatting trips and maps, and are in a common service to avoid duplication
  between the diary and the common tabs.
- It causes a circular dependency when we try to call the directive to process the inputs that we read

e-mission/e-mission-docs#727 (comment)
e-mission/e-mission-docs#727 (comment)
e-mission/e-mission-docs#727 (comment)
  • Loading branch information
shankari committed May 10, 2022
1 parent aec0051 commit 1db4c98
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 125 deletions.
1 change: 1 addition & 0 deletions www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<script src="js/main.js"></script>
<script src="js/recent.js"></script>
<script src="js/survey/survey.js"></script>
<script src="js/survey/input-matcher.js"></script>
<script src="js/survey/one-click-button.js"></script>
<script src="js/incident/post-trip-manual.js"></script>
<script src="js/survey/multilabel/infinite_scroll_filters.js"></script>
Expand Down
116 changes: 0 additions & 116 deletions www/js/diary/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,122 +372,6 @@ angular.module('emission.main.diary.services', ['emission.plugin.logger',
return getColoredStyle(baseDict, dh.getColor(feature.properties.sensed_mode));
};

var fmtTs = function(ts_in_secs, tz) {
return moment(ts_in_secs * 1000).tz(tz).format();
}

var printUserInput = function(ui) {
return fmtTs(ui.data.start_ts, ui.metadata.time_zone) + "("+ui.data.start_ts + ") -> "+
fmtTs(ui.data.end_ts, ui.metadata.time_zone) + "("+ui.data.end_ts + ")"+
" " + ui.data.label + " logged at "+ ui.metadata.write_ts;
}

dh.getUserInputForTrip = function(trip, nextTrip, userInputList) {
// If there is only one item in the list, return it.
// This make it compatible when fake list is given (for Enketo Survey).
// As well as optimize the performance.
if (userInputList.length === 1) {
return userInputList[0];
}
if (userInputList.length < 20) {
console.log("Input list = "+userInputList.map(printUserInput));
}
// 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 - trip.start_ts)+" should be positive");
console.log("endDelta = "+userInput.data.label+
"user("+fmtTs(userInput.data.end_ts, userInput.metadata.time_zone)+
") - 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(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 >= 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 = "+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 >= 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(nextTrip)) {
endChecks = userInput.data.end_ts <= nextTrip.start_ts;
Logger.log("Second level of end checks when the next trip is defined("+userInput.data.end_ts+" <= "+ nextTrip.start_ts+") = "+endChecks);
} else {
// next trip is not defined, last trip
endChecks = (userInput.data.end_local_dt.day == userInput.data.start_local_dt.day)
Logger.log("Second level of end checks for the last trip of the day");
Logger.log("compare "+userInput.data.end_local_dt.day + " with " + userInput.data.start_local_dt.day + " = " + endChecks);
}
if (endChecks) {
// If we have flipped the values, check to see that there
// is sufficient overlap
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("+trip.duration+") = "+ (overlapDuration / trip.duration));
endChecks = (overlapDuration/trip.duration) > 0.5;
}
}
return startChecks && endChecks;
}
});
if (potentialCandidates.length === 0) {
if (userInputList.length < 20) {
Logger.log("In getUserInputForTripStartEnd, no potential candidates, returning []");
}
return undefined;
}

if (potentialCandidates.length === 1) {
Logger.log("In getUserInputForTripStartEnd, one potential candidate, returning "+ printUserInput(potentialCandidates[0]));
return potentialCandidates[0];
}

Logger.log("potentialCandidates are "+potentialCandidates.map(printUserInput));
var sortedPC = potentialCandidates.sort(function(pc1, pc2) {
return pc2.metadata.write_ts - pc1.metadata.write_ts;
});
var mostRecentEntry = sortedPC[0];
Logger.log("Returning mostRecentEntry "+printUserInput(mostRecentEntry));
return mostRecentEntry;
}


return dh;
})
.factory('Timeline', function(CommHelper, ConfirmHelper, $http, $ionicLoading, $window,
Expand Down
7 changes: 4 additions & 3 deletions www/js/survey/enketo/enketo-trip-button.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
angular.module('emission.survey.enketo.trip.button',
['emission.stats.clientstats',
'emission.survey.enketo.launch',
'emission.main.diary.services'])
'emission.survey.inputmatcher'])
.directive('enketoTripButton', function() {
return {
scope: {
Expand Down Expand Up @@ -138,7 +138,7 @@ angular.module('emission.survey.enketo.trip.button',

$scope.init();
})
.factory("EnketoTripButtonService", function(DiaryHelper, $timeout) {
.factory("EnketoTripButtonService", function(InputMatcher, $timeout) {
var etbs = {};
console.log("Creating EnketoTripButtonService");
etbs.key = "manual/trip_user_input";
Expand All @@ -149,6 +149,7 @@ angular.module('emission.survey.enketo.trip.button',
*/

etbs.populateInputsAndInferences = function(trip, manualResultMap) {
console.log("ENKETO: populating trip,", trip, " with result map", manualResultMap);
if (angular.isDefined(trip)) {
// console.log("Expectation: "+JSON.stringify(trip.expectation));
// console.log("Inferred labels from server: "+JSON.stringify(trip.inferred_labels));
Expand All @@ -170,7 +171,7 @@ angular.module('emission.survey.enketo.trip.button',
*/
etbs.populateManualInputs = function (trip, nextTrip, inputType, inputList) {
// Check unprocessed labels first since they are more recent
const unprocessedLabelEntry = DiaryHelper.getUserInputForTrip(trip, nextTrip,
const unprocessedLabelEntry = InputMatcher.getUserInputForTrip(trip, nextTrip,
inputList);
var userInputLabel = unprocessedLabelEntry? unprocessedLabelEntry.data.label : undefined;
if (!angular.isDefined(userInputLabel)) {
Expand Down
6 changes: 3 additions & 3 deletions www/js/survey/enketo/service.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
angular.module('emission.survey.enketo.service', [
'ionic',
'emission.services',
'emission.main.diary.services',
'emission.survey.inputmatcher',
'emission.survey.enketo.answer'
])
.factory('EnketoSurvey', function(
$window, $http, UnifiedDataLoader,
DiaryHelper, EnketoSurveyAnswer,
InputMatcher, EnketoSurveyAnswer,
) {
/**
* @typedef EnketoSurveyConfig
Expand Down Expand Up @@ -114,7 +114,7 @@ angular.module('emission.survey.enketo.service', [
return null;
}
if (_state.opts.trip) {
answer = DiaryHelper.getUserInputForTrip(_state.opts.trip, undefined, answers);
answer = InputMatcher.getUserInputForTrip(_state.opts.trip, undefined, answers);
if (answer) {
return answer.data.xmlResponse;
}
Expand Down
121 changes: 121 additions & 0 deletions www/js/survey/input-matcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
'use strict';

angular.module('emission.survey.inputmatcher', ['emission.plugin.logger'])
.factory('InputMatcher', function($translate){
var im = {};
var fmtTs = function(ts_in_secs, tz) {
return moment(ts_in_secs * 1000).tz(tz).format();
}

var printUserInput = function(ui) {
return fmtTs(ui.data.start_ts, ui.metadata.time_zone) + "("+ui.data.start_ts + ") -> "+
fmtTs(ui.data.end_ts, ui.metadata.time_zone) + "("+ui.data.end_ts + ")"+
" " + ui.data.label + " logged at "+ ui.metadata.write_ts;
}

im.getUserInputForTrip = function(trip, nextTrip, userInputList) {
// If there is only one item in the list, return it.
// This make it compatible when fake list is given (for Enketo Survey).
// As well as optimize the performance.
if (userInputList.length === 1) {
return userInputList[0];
}
if (userInputList.length < 20) {
console.log("Input list = "+userInputList.map(printUserInput));
}
// 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 - trip.start_ts)+" should be positive");
console.log("endDelta = "+userInput.data.label+
"user("+fmtTs(userInput.data.end_ts, userInput.metadata.time_zone)+
") - 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(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 >= 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 = "+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 >= 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(nextTrip)) {
endChecks = userInput.data.end_ts <= nextTrip.start_ts;
Logger.log("Second level of end checks when the next trip is defined("+userInput.data.end_ts+" <= "+ nextTrip.start_ts+") = "+endChecks);
} else {
// next trip is not defined, last trip
endChecks = (userInput.data.end_local_dt.day == userInput.data.start_local_dt.day)
Logger.log("Second level of end checks for the last trip of the day");
Logger.log("compare "+userInput.data.end_local_dt.day + " with " + userInput.data.start_local_dt.day + " = " + endChecks);
}
if (endChecks) {
// If we have flipped the values, check to see that there
// is sufficient overlap
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("+trip.duration+") = "+ (overlapDuration / trip.duration));
endChecks = (overlapDuration/trip.duration) > 0.5;
}
}
return startChecks && endChecks;
}
});
if (potentialCandidates.length === 0) {
if (userInputList.length < 20) {
Logger.log("In getUserInputForTripStartEnd, no potential candidates, returning []");
}
return undefined;
}

if (potentialCandidates.length === 1) {
Logger.log("In getUserInputForTripStartEnd, one potential candidate, returning "+ printUserInput(potentialCandidates[0]));
return potentialCandidates[0];
}

Logger.log("potentialCandidates are "+potentialCandidates.map(printUserInput));
var sortedPC = potentialCandidates.sort(function(pc1, pc2) {
return pc2.metadata.write_ts - pc1.metadata.write_ts;
});
var mostRecentEntry = sortedPC[0];
Logger.log("Returning mostRecentEntry "+printUserInput(mostRecentEntry));
return mostRecentEntry;
}
return im;
});
6 changes: 3 additions & 3 deletions www/js/survey/multilabel/multi-label-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
angular.module('emission.survey.multilabel.buttons',
['emission.survey.multilabel.services',
'emission.stats.clientstats',
'emission.main.diary.services'])
'emission.survey.inputmatcher'])
.directive('multilabel', function() {
return {
scope: {
Expand Down Expand Up @@ -248,7 +248,7 @@ angular.module('emission.survey.multilabel.buttons',

$scope.init();
})
.factory("MultiLabelService", function(ConfirmHelper, DiaryHelper, $timeout) {
.factory("MultiLabelService", function(ConfirmHelper, InputMatcher, $timeout) {
var mls = {};
console.log("Creating MultiLabelService");
ConfirmHelper.inputParamsPromise.then((inputParams) => mls.inputParams = inputParams);
Expand Down Expand Up @@ -281,7 +281,7 @@ angular.module('emission.survey.multilabel.buttons',
*/
mls.populateManualInputs = function (trip, nextTrip, inputType, inputList) {
// Check unprocessed labels first since they are more recent
const unprocessedLabelEntry = DiaryHelper.getUserInputForTrip(trip, nextTrip,
const unprocessedLabelEntry = InputMatcher.getUserInputForTrip(trip, nextTrip,
inputList);
var userInputLabel = unprocessedLabelEntry? unprocessedLabelEntry.data.label : undefined;
if (!angular.isDefined(userInputLabel)) {
Expand Down

0 comments on commit 1db4c98

Please sign in to comment.