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

Update the model even when a cached choice list can be used #646

Merged
merged 2 commits into from
Oct 13, 2021

Conversation

lognaturel
Copy link
Member

@lognaturel lognaturel commented Sep 30, 2021

Closes #642, replaces #643

#643 re-introduced performance issues. See #643 (comment) for a description of when performance would matter.

This solution is similar to 7d483f1 but with a narrower slice of work when cached choices are returned.

What has been done to verify that this works as intended?

Added and ran a test. Verified that all other tests pass, in particular those that ensure performance in cases with repeats (the ones that failed in #643).

Why is this the best possible solution? Were any other approaches considered?

Conceptually, there are two things we need to do for any select:

  • make sure that the answer doesn't contain choices that are no longer available
  • make sure the answer is bound to the corresponding choice objects so that we can display the selected choice using its label

We don't want to do this on e.g. form load because it would mean having to iterate through all of the choices which we may never need to do. Doing this binding as late as possible is good for performance.

Semantically it feels like making sure the answer is synced with the choice list should happen any time the answer is requested. However, I don't think that's practical because the answers are represented as SelectOneData and SelectMultipleData objects which have no connection to the form definition or the currently filtered list of choices. I can't think of a way to kick off that syncing. What we've been doing which is updating the answer when the choice list is requested seems like the next best thing.

@grzesiek2010 says at #643 (comment)

it's kind of strange that a method called getChoices() not only returns a list of choices but also performs such an additional operations.

True, but no matter what I think we need to couple this update with some user-facing behavior. We don't want clients of getChoices (or whatever other useful user-facing behavior we’d tie the model update to) to have to explicitly do the model update, we want all the right updates to be triggered by choices being requested. We could do some kind of hook or listener but fundamentally I think the result would be the same and it's not worth a huge refactor at this time.

I briefly explored doing the update when the answer label is requested (currently in getSelectItemText). But that's not quite right, there are cases such as in validation where the choice list would be requested but the answer label would not.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

The intent is to narrowly fix the issue with editing forms with filtered selects in repeats. This feels like a quite safe change. It does mean that select answers are now updated any time a choice list is requested so there's risk there. We have very high test coverage, though.

There's a little bit more work done whenever returning a cached list but it should be very fast.

Do we need any specific form for testing your changes? If so, please attach one.

See issue.

Does this change require updates to documentation? If so, please file an issue here and include the link below.

No.

@lognaturel lognaturel requested a review from seadowg September 30, 2021 19:34
@@ -131,6 +131,7 @@ public void clearingValueAtLevel1_ShouldClearValuesAtLevels2And3() {
// If we set level2 to "aa", form validation passes. Currently, clearing a choice only updates filter expressions
// that directly depend on it. With this form, we could force clearing the third level when the first level is cleared
// by making the level3 filter expression in the form definition reference level1 AND level2.
scenario.answer("/data/level1", "b");
Copy link
Member Author

@lognaturel lognaturel Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A side-effect of this change is that it's no longer possible to set a value for the second level of a cascade if the first level isn't set. That's a good thing but in practice, I don't think it will make any difference for users since they'd have to visit the first level in order to change its value. I don't think Collect will let you end up in a state where level1 is not set and a value can be selected for level2.

@lognaturel
Copy link
Member Author

@seadowg I'm not sure whether you'll want to review or wait until @grzesiek2010 gets back. I think it'd be good to get to QA sooner than later. Hopefully it will be an easy review.

seadowg
seadowg previously approved these changes Oct 4, 2021
Copy link
Member

@seadowg seadowg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both solutions I've seen of this make sense to me, but I take @lognaturel's point that this preserves the performance benefits of caching for multiple different selects that use the same list. This is a pretty small change and the failing test (it's very specific, but I think it has to be) demonstrates this fixing the issue as far as I can see. I think it'd be good to get this to QA now!

@seadowg
Copy link
Member

seadowg commented Oct 4, 2021

I've tested this with Collect and it looks like there is a failing test - CatchFormDesignExceptionsTest#whenFormHasDesignErrors_explanationDialogShouldBeDisplayedAndTheFormShouldBeClosed. I'm thinking this fix to caching might be covering for a form design problem which might be ok? Either way, I'll think it's worth @lognaturel investigating before merge.

I also think we should have a quick chat about debugging JavaRosa from Collect as that process is now undocumented.

@lognaturel
Copy link
Member Author

lognaturel commented Oct 4, 2021

After this change, the first two cases described by getodk/collect#4750 are no longer problems because the answer gets updated earlier. That means if there's a calculate that produces a choice that exists in the choice list, it will be bound appropriately. If there's a calculate that produces a choice not in the choice list, then it will be cleared. I think all that is ok, it just makes it more pressing to address XLSForm/pyxform#546 so users get feedback when they make the mistake of putting a calculate on a user-editable select.

I believe we could still get a test to verify what the now-failing test did with something like getodk/collect#4170. For now, let's get a PR up for QA and I can work on that test.

debugging JavaRosa from Collect as that process is now undocumented.

I've always used the jar option from the instructions and that seems to work fine. Do you mean that the sourcetree option doesn't work? I've never used it because I get confused having multiple copies of the source. Perhaps we could just remove that part of the documentation?

@seadowg
Copy link
Member

seadowg commented Oct 4, 2021

Do you mean that the sourcetree option doesn't work?

No, just the jar route. To get it all working now you need to add dependencies to Collect (kxml and now the CSV parser) as well using the jar route (we should also document the command for this I think). We can add that stuff to the docs but, it seems a little brittle - you can end up with different versions of dependencies being used from what you'd get with a release. I'd like to have a quick play with using mvn install to see if we could go that way.

I've never used it because I get confused having multiple copies of the source. Perhaps we could just remove that part of the documentation?

Yeah I think we should.

@seadowg
Copy link
Member

seadowg commented Oct 4, 2021

I'd like to have a quick play with using mvn install to see if we could go that way.

After a bit of investigation it looks like we could set it up, so you can just do mvn install locally and then pull that version in Collect. We just need to make a change to our pom.xml here so that it's possible to override the version from the command line and also make a change to Collect, so it uses the mavenLocal() repo. The nice thing about our new Dependencies object is that we could define a javarosa_local dependency alongside javarosa and and then switching in Collect would be really simple. I'll put PRs together tomorrow.

@lognaturel
Copy link
Member Author

@seadowg I think this just needs your approval so I can merge and update Collect!

@lognaturel lognaturel merged commit c7a85c0 into getodk:master Oct 13, 2021
@lognaturel lognaturel deleted the issue-642 branch October 13, 2021 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants