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

Develop an integrated survey solution for onboarding and potentially each trip #727

Closed
shankari opened this issue May 3, 2022 · 64 comments

Comments

@shankari
Copy link
Contributor

shankari commented May 3, 2022

To use the behavioral data, we need to have a demographic survey for each user. We have typically used external surveys (Qualtrics/Google Forms) before, but those have the following limitations:

  • they are not approved survey tools at NREL
  • they are not integrated with the rest of the data, so we can't ensure that we only popup the survey if the user has not yet answered it earlier
  • we also can't experiment with fancy solutions where we break up the onboarding survey and ask the questions in little dribbles

A solution that would address all those issues is to store the survey information in mongodb, just like everything else. But we don't want to create a survey builder. Instead, we will use kobotoolbox to create a survey which we will display using the enketo library.

The UNSW group has already integrated with enketo core in the https://github.com/e-mission/e-mission-phone/tree/rciti branch, so let's start by exploring that approach.

If we can generalize this sufficiently, we can also have a config option to create surveys for each trip instead of using the labeling approach.

@shankari
Copy link
Contributor Author

shankari commented May 7, 2022

As of e-mission/e-mission-phone@0636a90, screenshot e-mission/e-mission-phone#826 (comment) we have the basic survey functionality working again. But there is still some special casing of "SURVEY" in the code that reads the manual inputs. Let's fix that next.

@shankari
Copy link
Contributor Author

shankari commented May 8, 2022

It is very tempting to refactor the trip button code even further to make it beautiful and to generalize to a single input per trip, but we don't have time for that right now. Our focus should be on what the enketo libraries do.

We should just comment/no-op everything to see what the survey code actually uses, and generalize that to work for the onboarding survey as well.

@shankari
Copy link
Contributor Author

shankari commented May 9, 2022

Tried to remove the special casing of "SURVEY" by moving the code to processManualInputs into the individual directives.
Loading the individual directive factories via the injector, but still running into a circular dependency

Error: [$injector:cdep] Circular dependency found: Timeline <- PostTripManualMarker <- DiaryHelper <- EnketoTripButtonService <- Timeline
http://errors.angularjs.org/1.5.3/$injector/cdep?p0=Timeline[object Object]3C-%6PostTripManualMarker%6%3C-%6DiaryHelper%6%3C-%6EnketoTripButtonService%6%3C-%6Timeline
@ionic://localhost/_app_file_/Users/kshankar/Library/Developer/CoreSimulator/Devices/72287858-3107-46F0-890C-F34F78427F7C/data/Containers/Data/Application/B3D241D4-6172-48AF-801D-6BB9C0BBCE1B/Library/NoCloud/phonegapdevapp/www/lib/ionic/js/ionic.bundle.js:13438:32

@shankari
Copy link
Contributor Author

shankari commented May 9, 2022

One fix might be to remove the dependency between the directive service and the diary helper.

const unprocessedLabelEntry = DiaryHelper.getUserInputForTrip(trip, nextTrip, inputList);

There doesn't appear to be a reason why that matching needs to happen in the directive. Why doesn't it just happen in the service, right after we read the input, or in the controller?

@shankari
Copy link
Contributor Author

shankari commented May 9, 2022

maybe because we need to iterate over the user inputs - e.g.

  mls.populateInputsAndInferences = function(trip, manualResultMap) {
...
        trip.userInput = {};
        ConfirmHelper.INPUTS.forEach(function(item, index) {
            mls.populateManualInputs(trip, trip.nextTrip, item,
                manualResultMap[item]);
        });
...
  }

We could get the keys (e.g. MODE, PURPOSE) in addition to manual/mode_confirm, manual/purpose_confirm from the directives, but it is not clear why getUserInputForTrip is even in DiaryHelper. The rest of the functions there are around formatting the diary, and we moved them to a service because we wanted to reuse them in the common screen.

Since this is only used for the trip label matching, it seems to make sense to move it to a separate service that is directly in the survey module.

@shankari
Copy link
Contributor Author

shankari commented May 10, 2022

Moved the matching code to the newly created InputMatching service. But a new problem is that the enketo service mapping code also returns a promise. So we can't just use

resultMap[etbs.SINGLE_KEY] = EnketoSurveyAnswer.filterByNameAndVersion('TripConfirmSurvey', surveyResult)

because then the value stored in the map is a promise

[Log] MULTILABEL: After processing manual results (3) (index.html, line 153)
[[], []] (2)
", resultMap "
{MODE: [], PURPOSE: []}

[Log] ENKETO: After processing manual results (3) (index.html, line 153)
[[]] (1)
", resultMap "
{SURVEY: Promise}

The obvious fix is to listen to the promise and then set the value

       EnketoSurveyAnswer.filterByNameAndVersion('TripConfirmSurvey', surveyResult)
           .then((answer) => { resultMap[etbs.SINGLE_KEY] = answer });

However, in this case, the result map is filled in asynchronously, which means that it is not properly accounted for in $scope.$apply

[Log] ENKETO: After processing manual results (3) (index.html, line 153)
[[]] (1)
", resultMap "
{}
[Log] ENKETO: After processing manual results (3) (index.html, line 153)
[[]] (1)
", resultMap "
{SURVEY: []}

@shankari
Copy link
Contributor Author

shankari commented May 10, 2022

wait, that can't be the reason because

[Log] ENKETO: After processing manual results (3) (index.html, line 153)
[[]] (1)
", resultMap "
{SURVEY: []}

and then

ENKETO: About to populate inputs and inferences for  (3)
{data: Object, style: function, onEachFeature: function, pointToLayer: function, start_place: Object, …}
", with resultMap "
{SURVEY: []}

The multi-label works, and it also has the manual map as blank at this stage

[Log] MULTILABEL: About to populate inputs and inferences for  (3) (index.html, line 153)
{data: Object, style: function, onEachFeature: function, pointToLayer: function, start_place: Object, …}
", with resultMap "
{MODE: [], PURPOSE: []}

Ah the multilabel works because the user_input is already in the trip

user_input: {mode_confirm: "walk", purpose_confirm: "library", replaced_mode: "drove_alone"}
verifiability: "already-verified"

After overriding with the labels, we do get some new inputs

[Log] MULTILABEL: About to populate inputs and inferences for  (3) (index.html, line 153)
{data: Object, style: function, onEachFeature: function, pointToLayer: function, start_place: Object, …}
", with resultMap "
Object
MODE: [Object] (1)
PURPOSE: [Object] (1)

@shankari
Copy link
Contributor Author

Reverting changes and restoring them one-by-one with logs to ensure that everything works.

Working version, survey is an array

[Log] ENKETO: populating trip, (3) (index.html, line 153)
{data: Object, style: function, onEachFeature: function, pointToLayer: function, start_place: Object, …}
" with result map"
Object

SURVEY: Array (24)
0 Object

data: {end_ts: 1651363948.849, xmlResponse: "<data xmlns:jr=\"http://openrosa.org/javarosa\" xmln…3e</instanceID>↵          </meta>↵        </data>", label: "1 purpose, 1 mode", start_ts: 1651362581.1776686, timestamp: "2022-05-09T16:47:43.148Z", …}

metadata: {time_zone: "America/Los_Angeles", plugin: "none", write_ts: 1652114863.1496382, platform: "ios", read_ts: 0, …}

Object Prototype
1 {data: Object, metadata: Object}
2 {data: Object, metadata: Object}
3 {data: Object, metadata: Object}
4 {data: Object, metadata: Object}
5 {data: Object, metadata: Object}
6 {data: Object, metadata: Object}
7 {data: Object, metadata: Object}
8 {data: Object, metadata: Object}
9 {data: Object, metadata: Object}
10 {data: Object, metadata: Object}
11 {data: Object, metadata: Object}
12 {data: Object, metadata: Object}
13 {data: Object, metadata: Object}
14 {data: Object, metadata: Object}
15 {data: Object, metadata: Object}
16 {data: Object, metadata: Object}
17 {data: Object, metadata: Object}
18 {data: Object, metadata: Object}
19 {data: Object, metadata: Object}
20 {data: Object, metadata: Object}
21 {data: Object, metadata: Object}
22 {data: Object, metadata: Object}
23 {data: Object, metadata: Object}

shankari added a commit to e-mission/e-mission-phone that referenced this issue May 10, 2022
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)
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 10, 2022
We do this as part of the promise response instead of folding it into the
`processManualInputs` function because enketo survey answers also returns a promise.
So we will have to set the `resultMap` asynchronously, which may run into timing issues
e-mission/e-mission-docs#727 (comment)

Let's revisit this later once we figure out how the answers should work
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 10, 2022
Instead, define `MANUAL_KEYS` in each directive and iterate over them.
Also, it turned out that this was not working earlier
e-mission/e-mission-docs#727 (comment)
not because of any timing issues, but because we were reading the wrong key

TODO: see if we can combine `extractResult` and `processManualInputs` since the
timing was not the issue after all. However, the timing *could* be an issue
since it is async, and we don't want to rely on timing effects, so we may also
want to leave it unchanged.
`
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 10, 2022
The previous implementation treated the enketo code as another type of survey.
However the enketo survey is different than the multi-label UI.
We have now separated out the functionality into separate directives, and removed all
references to the ConfirmHelper in the diary code

The eneketo survey is a single array entry with key "SURVEY", and is stored in the directive.
In the diary, deployers can choose between the multi-ui and enketo implementations
e-mission/e-mission-docs#727 (comment)
@shankari
Copy link
Contributor Author

Next steps:

  • the enketo wrappers do a lot of redundant work that they don't need to do. In particular, they deal with:
    • storing the survey results, which is bad because we want to reuse the same library to store both the initial onboarding survey result and the trip specific survey results
    • as I found out while changing e-mission/e-mission-phone@1db4c98, they do some additional level of matching inputs as well

Let's now try to understand the enketo wrappers better and pull out the load and save functionality into the directive.

@shankari
Copy link
Contributor Author

Couple of obvious things to change:

In the enketo service:

  const DATA_KEY = 'manual/survey_response';
  const ENKETO_SURVEY_CONFIG_PATH = 'json/enketoSurveyConfig.json';

From a performance perspective, we also read all entries

  function showModal() {
    const tq = $window.cordova.plugins.BEMUserCache.getAllTimeQuery();
    return UnifiedDataLoader.getUnifiedMessagesForInterval(DATA_KEY, tq)
      .then(answers => _restoreAnswer(answers))
      .then(instanceStr => _loadForm({ instanceStr }));
  }

I'm also not sure why we need to check against the UUID; we can only retrieve entries for the same UUID.
Ah here's a response from @atton16
#674 (comment)

My concern on uuid and timestamp is that the metadata and the provided user_id field is not ready until it is pushed. That is why I also embed it in data object.

#674 (comment)

Thank you for the clarification. I might be able to get rid of it in our survey and using the provided fields.

It looks like it is already not passed in

  $scope.openPopover = function ($event, trip, inputType) {
    return EnketoSurveyLaunch
      .launch($scope, 'TripConfirmSurvey', { trip: trip })

shankari added a commit to e-mission/e-mission-phone that referenced this issue May 10, 2022
Since:
- it is redundant; the phone only has information for one user anyway
- it is currently unused
e-mission/e-mission-docs#727 (comment)
@shankari
Copy link
Contributor Author

To fix the issue with reading all the entries again, we need to be a bit careful. The trip.user_input will be filled in once the data gets to the server. But in the interim, the trip.userInput["SURVEY"] only has the label, for reasons that I don't fully understand.

Let's make sure to store the full datastructure there.

shankari added a commit to e-mission/e-mission-phone that referenced this issue May 11, 2022
Since we already match the user input for the trip during
populateInputsAndInferences, we don't need to re-pull all the entries and
re-match during the survey launch.

This is likely to be particularly bad if we collect data over a long period of
time. Reading all the user inputs for a year every time we want to launch the
survey is quickly going to get old.

This also means that we need to store the entire survey object instead of only the label
when we match entries.
e-mission/e-mission-docs#727 (comment)
@shankari
Copy link
Contributor Author

Note to self: we should ensure that we support languages going forward
https://github.com/enketo/enketo-core/blob/master/tutorials/10-configuration.md#explicitly-set-the-default-form-language

@shankari
Copy link
Contributor Author

Now, we start exploring displaying the demographic survey during onboarding.

First, let's see if we can at least launch the survey from the onboarding screen as a button, so we have a nice backup.
Then, we can see whether we can make it work inline.

@shankari
Copy link
Contributor Author

Launching from button works.

Screen.Recording.2022-05-11.at.12.19.54.PM.mov

@shankari
Copy link
Contributor Author

I identified my previous attempts at creating the wrapper to work with the enketo form.
https://github.com/e-mission/e-mission-phone/pull/563/files

Comparing it to the current wrapper, potentially modified by the UNSW folks, the changes are minimal.
After removing the wrapper (modal vs. ion-view) and unifying the whitespace, we get
Screen Shot 2022-05-11 at 12 42 12 PM

--- enketo-survey.html	2022-05-11 12:43:15.000000000 -0700
+++ enketo-survey-new.html	2022-05-11 12:43:22.000000000 -0700
@@ -1,5 +1,5 @@
-   <ion-content overflow-scroll="true">
-    <div class="main">
+  <ion-content class="enketo-plugin overflow-scroll">
+    <div class="main" id="survey-paper">
         <article class="paper" data-tap-disabled="true">
             <!--
                 This section is to be removed in application
@@ -29,8 +29,8 @@
                     mother application. The HTML markup can be changed as well.
                 -->
                 <a href="#" class="previous-page disabled" style="position:absolute; left: 10px; bottom: 40px;">Back</a>
-                <button id="validate-form" class="btn btn-primary" ng-click="validateForm()" style="width:200px; margin-left: calc(50% - 100px);">Validate</button>
-                <a href="#" class="btn btn-primary next-page disabled" style="width: 200px; margin-left: calc(50% - 100px)">Next</a>
+                <button id="validate-form" class="btn btn-primary" ng-click="enketoSurvey.validateAndSave()" style="width:200px; margin-left: calc(50% - 100px);">Save</button>
+                <a href="#survey-paper" class="btn btn-primary next-page disabled" style="width: 200px; margin-left: calc(50% - 100px)" ng-click="enketoSurvey.onNext()">Next</a>

                 <div class="enketo-power" style="margin-bottom: 30px;">Powered by <a href="http://enketo.org" title="enketo.org website"><img src="templates/survey/enketo_bare_150x56.png" alt="enketo logo" /></a> </div>
                 <div class="form-footer__jump-nav" style="display: flex; flex-direction: row;">

The main differences are:

  • the top div has an ID survey-paper, which is referenced from the bottom #survey-paper instead of #
  • the ng-click functions are different. Both of those seem like reasonable changes, and nothing too complicated. Let's try to display inline now...

shankari added a commit to e-mission/e-mission-phone that referenced this issue May 12, 2022
Concretely:
- add a new survey.html which just has the inline function
- once the account is registered, move to the next slide instead of popping up the survey
- add a new `inline` directive which has all the information from the `<ion-content>` in the popup
- have the directive call `initSurvey`
    - which involved having `initSurvey` exported from launch

There are still many weirdnesses that need to be fixed, but let's first get this committed
e-mission/e-mission-docs#727 (comment)
@shankari
Copy link
Contributor Author

shankari commented May 12, 2022

Aha! I have it working inline in e-mission/e-mission-phone@c987e3f.

inline_demographic_survey.mov

I had to do some fairly hacky stuff which I will clean up later.

Notably:

  • We don't load any existing survey result (if any). We might also want to skip the survey if it has already been answered.
  • We have copy-pasted the <ion-content section from the popup
  • The final screen at the end of the onboarding process doesn't have a header
  • We had to add an additional export initSurvey export to the launcher
  • the callback when the survey is done ngdone is ugly

Let's clean each of those up one by one.

@shankari
Copy link
Contributor Author

Trying to convert the <ion-content to a <div to allow us to inline the form outside of a full-ion content, but failing.

  • content.html:
<div class="enketo-plugin overflow-scroll" height="100%">
<div class="main overflow-scroll" id="survey-paper">
  • survey.html:
<enketo-demographics-inline ng-done="finish"></enketo-demographics-inline>

works
Screen Shot 2022-05-11 at 10 53 20 PM

Putting the <ion-content with scrolling around it, causes it to fail. The div ends up with height 0 and does not display.

Screen Shot 2022-05-12 at 8 23 05 AM

@shankari
Copy link
Contributor Author

This is probably some subtle artifact of the scrolling and the height. We don't really need any other inlining of the survey now, so let's stick to the directive starting with the <ion-content and explore this later. But let's avoid copy-pasting the directive by re-using this in the popup.

@shankari
Copy link
Contributor Author

shankari commented May 12, 2022

One issue that I've been encountering consistently is that after we go through the onboarding screen, when we launch the popup, it doesn't show the form. If we reload the screen (e.g. by editing a file in the devapp), it works. I bet this is something subtle related to initSurvey since in the first approach, we call initSurvey twice and in the second, we call it once.

Screen Shot 2022-05-12 at 9 00 43 AM

@shankari
Copy link
Contributor Author

Aha! Figured it out! The problem is that after we have gone through the intro, there are two form.or selectors, one for the inline intro and one for the modal.

const formSelector = 'form.or:eq(0)';

Before launching the modal again

Both forms are populated - the modal from the previous load and in the inline from the intro screen

Screen Shot 2022-05-12 at 9 32 38 AM

Screen Shot 2022-05-12 at 9 33 12 AM

After launching the modal again

The form is reloaded, but the first form.or is selected, so the intro screen is loaded, not the popup. So there is no form in the popup and we can't see anyting.

Screen Shot 2022-05-12 at 9 39 18 AM

Screen Shot 2022-05-12 at 9 40 17 AM

Two potential solutions:

  • Figure out how to remove the inline version after the intro is done. Can we delete/unload an ion-view?
  • Switch to a popup even during onboarding so that there is only one instance of the form at one time.

Let's explore the first option with a time bound.

@shankari
Copy link
Contributor Author

This is even more obvious when you go back to the intro after launching the popup.

So popup -> intro (two copies) -> popup (second form added to intro) -> intro shows two forms in the slide. One of which is stuck at the last screen of the survey and the other which moves.

Page 1 Page 2 Page 3
Screen Shot 2022-05-12 at 2 34 58 PM Screen Shot 2022-05-12 at 2 35 19 PM Screen Shot 2022-05-12 at 2 35 42 PM

@shankari
Copy link
Contributor Author

shankari commented May 25, 2022

The phone and server changes are now done.
Phone: e-mission/e-mission-phone#826
Server: e-mission/e-mission-server#853

We can just merge the server code to master.
However, for the phone code, we only want to copy/merge the www/*/survey/enketo directory so that we don't introduce any inadvertent changes to the diary code.

Since we just merged from master, the chances of that happening are low, but don't want to introduce regressions at this point.

We probably want to do something like:
https://stackoverflow.com/questions/1214906/how-do-i-merge-a-sub-directory-in-git

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

Looking at the actual set of changes in the PR, most of them are in the survey/enketo directory but some are outside - e.g. the refactoring of www/js/diary/service.js
e-mission/e-mission-phone#826

JS files template files
Screen Shot 2022-05-25 at 5 14 03 PM Screen Shot 2022-05-25 at 5 14 31 PM

So maybe we should see what happens if we try to merge everything to master.

@shankari
Copy link
Contributor Author

Comparing to master, we have:
e-mission/e-mission-phone@master...refactor_enketo

Previous commits Previous commits
Screen Shot 2022-05-25 at 5 32 17 PM Screen Shot 2022-05-25 at 5 32 52 PM
Screen Shot 2022-05-25 at 5 33 23 PM Screen Shot 2022-05-25 at 5 33 49 PM
Screen Shot 2022-05-25 at 5 34 43 PM Screen Shot 2022-05-25 at 5 36 39 PM

Everything from May of this year is code that I want to keep

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

Those initial changes
e-mission/e-mission-phone@15cb98e...425d479

are primarily to
Screen Shot 2022-05-25 at 5 41 03 PM

most of which (intro, consent, etc) I will need to change back to non-RCITI values
Can I merge everything other than those?

Although those are a good template of what to modify while merging to master.

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

Looks like there are basically two options:

  • use cherry-pick
  • use rebase

https://stackoverflow.com/questions/1994463/how-to-cherry-pick-a-range-of-commits-and-merge-them-into-another-branch

cherry-pick is not considered a great option because the commit IDs will all be different and we could end up with merge conflicts

But once we have merged this to master, we can abandon the refactor_enketo branch so we don't have issues with merge conflicts later.

wrt "cherry picking a commit from one branch to another basically involves generating a patch, then applying it, thus losing history that way as well."

Most of the code is in new files, so not sure we are losing a lot of history. Let's try it on a new branch anyway and see what it looks like.
This is the range of new commits this month:
e-mission/e-mission-phone@26a015f...19f97a4

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

This doesn't sound very promising. The very second commit fails.

kshankar-35069s:phone-rciti-branch kshankar$ git cherry-pick 26a015faeb101b14fb2b63231d79274bc003422a...19f97a420a3388cb47dc34a651b7c88727622a4d
Auto-merging www/templates/main.html
[enketo_directives_only b2aea247] Comment out the dashboard screen
 Date: Fri May 6 07:31:02 2022 -0700
 1 file changed, 2 insertions(+), 2 deletions(-)
CONFLICT (rename/delete): www/templates/survey/enketo_bare_150x56.png deleted in HEAD and renamed to www/templates/survey/enketo/enketo_bare_150x56.png in 08d03f71 (Move all the enketo code into a separate directory). Version 08d03f71 (Move all the enketo code into a separate directory) of www/templates/survey/enketo/enketo_bare_150x56.png left in tree.
CONFLICT (rename/delete): www/templates/survey/enketo-survey-modal.html deleted in HEAD and renamed to www/templates/survey/enketo/enketo-survey-modal.html in 08d03f71 (Move all the enketo code into a separate directory). Version 08d03f71 (Move all the enketo code into a separate directory) of www/templates/survey/enketo/enketo-survey-modal.html left in tree.
Auto-merging www/js/survey/multilabel/multi-label-ui.js
CONFLICT (content): Merge conflict in www/js/survey/multilabel/multi-label-ui.js
CONFLICT (rename/delete): www/js/survey/enketo-survey-service.js deleted in HEAD and renamed to www/js/survey/enketo/enketo-survey-service.js in 08d03f71 (Move all the enketo code into a separate directory). Version 08d03f71 (Move all the enketo code into a separate directory) of www/js/survey/enketo/enketo-survey-service.js left in tree.
CONFLICT (rename/delete): www/js/survey/enketo-survey-launch.js deleted in HEAD and renamed to www/js/survey/enketo/enketo-survey-launch.js in 08d03f71 (Move all the enketo code into a separate directory). Version 08d03f71 (Move all the enketo code into a separate directory) of www/js/survey/enketo/enketo-survey-launch.js left in tree.
CONFLICT (rename/delete): www/js/survey/enketo-survey-answer.js deleted in HEAD and renamed to www/js/survey/enketo/enketo-survey-answer.js in 08d03f71 (Move all the enketo code into a separate directory). Version 08d03f71 (Move all the enketo code into a separate directory) of www/js/survey/enketo/enketo-survey-answer.js left in tree.
Auto-merging www/js/diary/services.js
CONFLICT (content): Merge conflict in www/js/diary/services.js
Auto-merging www/js/diary/list.js
CONFLICT (content): Merge conflict in www/js/diary/list.js
Auto-merging www/index.html
CONFLICT (content): Merge conflict in www/index.html
error: could not apply 08d03f71... Move all the enketo code into a separate directory
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

@shankari
Copy link
Contributor Author

Merging directly into master does not result in any merge conflicts
e-mission/e-mission-phone#830

We could just merge into this subsidiary branch and revert all the UNSW-specific changes that we don't want.

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

Let's go with that for now; a few additional commits in the history are not too terrible and at least then we don't need to deal with merge conflicts.

Ok so the new plan is:

  • merge everything into a branch
  • revert the changes that are UNSW-specific

@shankari
Copy link
Contributor Author

reverting individual changes results in merge conflicts, especially if they have been modified by subsequent changes.

$ git revert 15cb98e
Auto-merging www/templates/intro/consent-text.html
CONFLICT (content): Merge conflict in www/templates/intro/consent-text.html
error: could not revert 15cb98ea... Modify the consent text file
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

Trying to revert a range

$ git revert 15cb98e...26df155
error: commit 26df155ca68844c91fc14f1234c153bd5538ef88 is a merge but no -m option was given.
fatal: revert failed

the help around this is

Usually you cannot revert a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows revert to reverse the change relative to the specified parent.

Reverting a merge commit declares that you will never want the tree changes brought in by the merge. As a result, later merges will only bring in tree changes introduced by commits that are not ancestors of the previously reverted merge. This may or may not be what you want.

@shankari
Copy link
Contributor Author

reverting all the way to the end to avoid partial reverts gives us

kshankar-35069s:phone-rciti-branch kshankar$ git revert 15cb98e...d825fc2
[revert_unsw_specific_changes a9738ff7] Revert "chore: remove unused json"
 1 file changed, 30 insertions(+)
 create mode 100644 www/json/trip_confirm_options.json
Auto-merging www/js/diary/list.js
CONFLICT (content): Merge conflict in www/js/diary/list.js
error: could not revert c5ead07d... patch(Diary): update tripgj label directly with the new answer
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

one correct revert, and one conflict.
Let's try to resolve the conflict and see what happens

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

After resolving that conflict, moving on to the next revert.
Keeps repeating this.

$ git commit
On branch revert_unsw_specific_changes
Revert currently in progress.
  (run "git revert --continue" to continue)
  (use "git revert --skip" to skip this patch)
  (use "git revert --abort" to cancel the revert operation)

@shankari
Copy link
Contributor Author

Ah that is because of https://stackoverflow.com/a/65638479/4040267
used $ git commit --allow-empty and now there is no pending "stuck" git

@shankari
Copy link
Contributor Author

shankari commented May 26, 2022

Went through and reverted all changes to the best of my ability.
Have some weird merge conflicts
Now comparing differences between:

  • revert_unsw_specific_changes and integrate_odk_compatible_surveys_using_enketo
  • revert_unsw_specific_changes and master

to figure out which changes need to be retained

@shankari
Copy link
Contributor Author

Painstakingly went through, reverted all UNSW-specific changes and re-introduced only the changes needed for this program.

Disposition of changes
  • bower.json

    • not reverted
  • intro.css

    • reverted
  • diary.css

    • reverted
  • style.css

    • reverted
  • en.json

    • should edit later
    • too much reverting
  • index.html

    • too much reverting
    • re-reverted 28a0b637d1f21a2a2794c81250425af06d78a15f
      • edited to manually remove the isAnalysed functions (5d4145364c315c78730bf402a1c11e10b34059d1)
  • general-settings.js

    • remove survey launch since we no longer use an external survey (9cfa0604959ca4441c6e24682efc43adf43ec2b3)
  • diary/list.js (4271f284ea1c1700d55c5251e85d98a661d3eb43)

    • restore to multilabel instead of enketo since we want to enable the dashboard
    • re-enable the code that restores answers between diary and list views
    • revert whitespace changes
  • diary/services.js (83163b7aaf4e3b225e21657b2aaea1b7a3af7bdf)

    • Change the default options to map to MULTILABEL instead of ENKETO
      To be consistent with the list view
  • intro.js
    keep the new survey HTML
    a lot of random stuff could have come over as part of this change, including the randomized login, but will introduce that later

  • metrics.js

    • rename to berkeley
    • should edit later
  • launch.js

    • remove merge conflict
  • answer.js

  • enketo-demographics.js

  • enketo-preview.js

  • enketo-trip-button.js

  • service.js

  • input-matcher.js

    • no changes
  • multi-label-ui.js

    • no changes
  • survey.js

    • no changes
  • emailConfig.json

    • revert
    • should edit later
  • trip_confirm.json + enketoSurveyConfig.json

    • expand config samples and remove overrides
  • www/manual_lib/enketo/*

    • unchanged
  • main-control

    • add the demographics button
  • intro.html

    • add the survey file to the intro
  • content.html

  • demographics-button.html

  • enketo logo

  • form-base.html

  • inline.html

  • modal.html

  • preview.html

  • summary-trip-button.html

    • unchanged
  • wrapper

    • added enketo-trip-button with a passthrough recompute delay

Current status:

  • Demographic survey works from both profile and inline
  • On switching the trip survey to enketo (uncommitted change, just for testing)
    • the diary looks weird
    • the label screen doesn't work
Diary Label
Screen Shot 2022-05-26 at 12 38 50 PM

Let's go ahead and merge the reverts for now
We can address the trip survey buttons in a separate commit.

@shankari
Copy link
Contributor Author

Fixing remaining minor issues:

If there are no user inputs saved (ever), we get this error.

Screen Shot 2022-05-26 at 5 51 53 PM

while reading confirmed tripsCannot read property 'length' of undefined
TypeError: Cannot read property 'length' of undefined
    at Object.im.getUserInputForTrip (http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/js/survey/input-matcher.js:20:23)
    at Object.mls.populateManualInputs (http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/js/survey/multilabel/multi-label-ui.js:298:50)
    at http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/js/survey/multilabel/multi-label-ui.js:280:17
    at Array.forEach (<anonymous>)
    at Object.mls.populateInputsAndInferences (http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/js/survey/multilabel/multi-label-ui.js:279:30)
    at http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/js/diary/infinite_scroll_list.js:109:41
    at Array.forEach (<anonymous>)
    at http://localhost/_app_file_/data/user/0/edu.berkeley.eecs.emission.devapp/files/phonegapdevapp/www/js/diary/infinite_scroll_list.js:107:16

@shankari
Copy link
Contributor Author

Next, buttons are not covering the entire row. During testing, we had width=100%, but we reverted it because that is not true for all trip buttons.

https://github.com/e-mission/e-mission-phone/pull/831/files#diff-fdd1795534648690de8f7d6ba83a002fc2068d076bb80bab0c7555ed8f44bc6dL144

Screen Shot 2022-05-26 at 6 16 16 PM

@shankari
Copy link
Contributor Author

Adding a text-align style, similar to the multi-label buttons centers it
Screen Shot 2022-05-26 at 6 19 36 PM

and adding a width of 100% makes it take up the entire row. Note that the width needs to be on the button, not the parent DIV to override the hardcoded width in the diary.css

Screen Shot 2022-05-26 at 6 22 40 PM

@shankari
Copy link
Contributor Author

shankari commented May 27, 2022

Remaining issues:

  • After filling out the first survey on a day, we end up with all surveys ostensibly filled out
  • Surveys filled in from the diary are not visible on the label screen
  • Surveys filled out from the label screen cannot be saved
Trip inputs are duplicated And don't show up on the label screen
Screen Shot 2022-05-26 at 6 39 46 PM Screen Shot 2022-05-26 at 6 39 58 PM

Not even for the multi-label

Screen.Recording.2022-05-26.at.8.18.27.PM.mov

@shankari
Copy link
Contributor Author

First issue fixed

Screen Shot 2022-05-26 at 6 49 54 PM

Second issue fixed

Screen.Recording.2022-05-26.at.8.35.30.PM.mov

Third issue fixed

labeling_from_label_screen_working.mov

shankari added a commit to e-mission/e-mission-phone that referenced this issue May 27, 2022
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 27, 2022
So that it looks proper

It centers the button and ensures that it takes up all the width of the item.
e-mission/e-mission-docs#727 (comment)
and
e-mission/e-mission-docs#727 (comment)
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 27, 2022
Where if the user input list has a single entry, we can return it.
If we have one user input and multiple trips, we can't just return that one
input. That is the input for one trip, not the others!
e-mission/e-mission-docs#727 (comment)

wrt performance, we already handled the major performance issue by only having
to manually match unprocessed entries after the end of the pipeline

Testing done:
- loaded a day with a single user input
- before this change, all trips were flagged with that input
- after this change, only the first was
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 27, 2022
Before this, we used to return the Promises.all and fill in the map in its
then.

As part of the refactoring for the enketo trip labels,
aec0051

we changed it to return an array of the result and the Promise.all

This meant that the infinite scroll code, which was expecting a result map, got
a promise array instead, and so the mapping of user inputs in the infinite
scroll list = label list failed consistently.

Fixing this by re-returning `Promise.all` and the result and mapping from the `then`

Note that the labels from the diary were not visible on the label screen even for the classic multilabel UI
e-mission/e-mission-docs#727 (comment)

Original code:

```

            return Promise.all(manualPromises).then((manualResults) => {
                const manualConfirmResults = {};
                manualResults.forEach(function(mr, index) {
                  manualConfirmResults[ConfirmHelper.INPUTS[index]] = mr;
                });
                return [result, manualConfirmResults];
            });
```

Broken code:

```
            return [result, Promise.all(manualPromises).then((manualResults) =>
                manualInputFactory.processManualInputs(manualResults, manualConfirmResults))];
```

Fixed code:

```
            return Promise.all(manualPromises).then((manualResults) => {
                manualInputFactory.processManualInputs(manualResults, manualConfirmResults);
                return [result, manualConfirmResults];
            });
```
shankari added a commit to e-mission/e-mission-phone that referenced this issue May 27, 2022
By handling the different trip structure formats in the diary and the label
view
e-mission/e-mission-docs#727 (comment)
@shankari
Copy link
Contributor Author

Note that the related server fix is at:
e-mission/e-mission-server#853

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant