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

Use relative references for repeats only. #187

Merged
merged 12 commits into from
Dec 9, 2018

Conversation

ukanga
Copy link
Contributor

@ukanga ukanga commented Apr 3, 2018

This introduces relative referencing for ${variables} in repeat sections.

Not sure if my interpretation is accurate, I found a super long old thread in ODK forum, not sure if it is relevant still.

I picked the option of dealing with https://opendatakit.github.io/xforms-spec/#a-big-deviation-with-xforms hoping it does address it.

Fix #4

@ukanga ukanga changed the title Use relative references for repeats only. [DNM] Use relative references for repeats only. Apr 3, 2018
@MartijnR
Copy link
Contributor

MartijnR commented Apr 3, 2018

OMG, this is exciting! 👍 Maybe we can take out that deviation from the spec some time.

Will check it out!

Copy link
Contributor

@MartijnR MartijnR left a comment

Choose a reason for hiding this comment

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

The number of steps down ( current()/../ vs current()/../../../../) should be determined from the context (i.e. question where ref is used). See https://docs.google.com/spreadsheets/d/1DUgtclTL8fBOHS9h88a8eC6x1u5M17g1b7CPJBm7JP4/edit?usp=sharing.

You could decide to strip the current()/ part if the expression is not inside a predicate, e.g. calculate="../crop" instead of calculate="current()/../crop". That would be preferable. See https://docs.google.com/spreadsheets/d/1lTJ9-AnrGDdFK8UzvmSglqFgGjLsdzQzp_XLoYkV__Q/edit?usp=sharing

P.S. Gotta run now, but will markdownify these forms, to facilitate turning them into tests later.

@MartijnR MartijnR mentioned this pull request Nov 28, 2023
3 tasks
@lognaturel
Copy link
Contributor

lognaturel commented Apr 4, 2018

As the thread @ukanga links to notes, Javarosa doesn't interpret current() correctly in choice filters. This is the variant of @MartijnR's form above that has the expected behavior in Javarosa (note the wrong name = current()/crop and name = current()/../crop):

<select1 ref="/repeat-relative-current-1/rep/sel_a">
  <label> Verify <output value=" current()/../crop "/> </label>
  <itemset nodeset="instance('crop_list')/root/item[name =  current()/crop ]">
    <value ref="name"/>
    <label ref="jr:itext(itextId)"/>
  </itemset>
</select1>
<group ref="/repeat-relative-current-1/rep/group">
  <select1 ref="/repeat-relative-current-1/rep/group/sel_b">
    <label> Verify <output value=" current()/../../crop "/> </label>
  <itemset nodeset="instance('crop_list')/root/item[name = current()/../crop]">
      <value ref="name"/>
      <label ref="jr:itext(itextId)"/>
    </itemset>
  </select1>
</group>

Perhaps there can be some flags or model version information added in somehow as explored in the thread but I don't think it can be the default behavior for pyxform right now, at least not for choice filters. The problem is that even if we patch JR today (which doesn't sound trivial), all users running older versions of Collect (and there are many), will be unable to use new forms with choice filters. That's what the very long thread was about.

@MartijnR
Copy link
Contributor

MartijnR commented Apr 4, 2018

Ah, yes, when fixing the current() bug in JR, I understand you may need a way to determine whether an (old) XForm may be relying on a bug-dependent usage of current().

(UPDATE: made my comment less nasty (sorry) and moved useful parts here: getodk/javarosa#293)

@lognaturel lognaturel mentioned this pull request Apr 4, 2018
@MartijnR
Copy link
Contributor

MartijnR commented Apr 4, 2018

Test 1:

survey
type name label choice_filter
begin repeat rep
select_one crop_list crop Select
begin group group
select_one crop_list b Verify name = ${crop}
end group
end repeat

expected output for b itemset: [ name = current()/../../crop ]

Test 2:

survey
type name label calculate
begin repeat rep
select_one crop_list crop Select
text a Verify name = ${crop}
begin group group
text b Verify name = ${crop}
end group
end repeat

expected output for a bind: calculate="../crop"
expected output for b bind: calculate="../../crop"

@lognaturel
Copy link
Contributor

These tests look great. Would be good to get to a high-level game plan also. Can we do relative paths everywhere other than choice filters for now? Do we wait on the overall change?

@MartijnR
Copy link
Contributor

MartijnR commented Apr 4, 2018

I'm fine with just introducing them all at once (waiting a bit for the JR folks) or in 2 batches (splitting choice_name and rest). Whatever is easiest in terms of pyxform code changes.

What is important to me is to not lose momentum in getting the code ready to go now that this is hot (after 5 years). Merging can wait.

@ukanga
Copy link
Contributor Author

ukanga commented Apr 5, 2018

Thank you guys for the comments and tests, I will add them in the next iteration on this PR.

@ukanga ukanga force-pushed the relative-paths-issue-4 branch 2 times, most recently from 88ff9db to 6e60fe7 Compare April 10, 2018 15:18
@ukanga
Copy link
Contributor Author

ukanga commented Apr 10, 2018

Example of XForm XML with relative paths as generated by this PR. The first two are the examples @MartijnR provided above.

If you have the chance to test the above XML in ODK Collect or Enketo, let me know what's not working as expected.

@ukanga ukanga changed the title [DNM] Use relative references for repeats only. Use relative references for repeats only. Apr 10, 2018
@ukanga ukanga changed the title Use relative references for repeats only. [DNM] Use relative references for repeats only. Apr 18, 2018
@lognaturel
Copy link
Contributor

If you have the chance to test the above XML in ODK Collect or Enketo, let me know what's not working as expected.

Everything works as expected in Collect other than the choice filters. 🎉 I think we will have a fix for JavaRosa soon. I /think/ this is actually ok to merge though I'd like a sanity check on my thinking. The JavaRosa problem is that current() doesn't refer to the right thing in selects. As far as I can tell, this only affects two things: choice filters and the itemset nodeset. It's not very likely users are using relative paths to set the itemset nodeset. One would use that in a scenario like collecting household info in which the enumerator is asked to select the owner of each pet from a dynamically generated list of children:

repeat people
  repeat children
  repeat pets
    select itemset nodeset=current()../../children

It's cool and powerful but there's no XLSForm support for itemsets from arbitrary nodes (#38) and it's pretty esoteric. I've only shown users how to do it with absolute paths but have never seen it come up with relative paths before.

The other case is choice filters. XLSForms with choice filters in repeats fail today because an absolute path doesn't make sense there. Currently in Collect we get an error about needing indexed-repeat. With this change we'd get no error but also no choices shown. Maybe a little harder for users to troubleshoot but nothing too bad. And as soon as they got patched Collect it would Just Work™️.

The change we're going to make in JR would only affect users who are trying to use current() already. This PR doesn't affect them (maybe a good test to add @ukanga? Demonstrate that if a path is already relative this doesn't change anything?).

If someone else who uses Collect and understands the JR issue can run through this reasoning and validate it, I'd be comfortable merging.

@lognaturel
Copy link
Contributor

You could decide to strip the current()/ part if the expression is not inside a predicate, e.g. calculate="../crop" instead of calculate="current()/../crop"

I would be in favor of this. current() is a bit of a strange bird so the more it can be avoided the better.

@MartijnR
Copy link
Contributor

MartijnR commented Jul 9, 2018

Thanks. I can confirm the forms work in Enketo too. (after correcting calculation in calculate_w_relative_paths.xml to calculate="current()/../crop" and calculate="current()/../../crop ", but that's not a pyxform issue.)

I agree it is best to avoid using current() when it's not required.

@ggalmazor
Copy link
Contributor

Hi! Just wanted to drop by and say that @lognaturel's reasoning looks good to me. I also agree that I would prefer having ../ instead of current() wherever possible.

@ukanga
Copy link
Contributor Author

ukanga commented Aug 6, 2018

Thank you all for your feedback, I will incorporate it and ping you again for another review.

@ukanga ukanga changed the title [DNM] Use relative references for repeats only. Use relative references for repeats only. Nov 29, 2018
@ukanga
Copy link
Contributor Author

ukanga commented Nov 29, 2018

@MartijnR @lognaturel not sure what was pending on this but I see most of what was on your comments was addressed. Any blocker to getting this merged?

""", # noqa pylint: disable=line-too-long
xml__contains=[
"""<itemset nodeset="instance('crop_list')/root/item[name = ../crop ]">""", # noqa pylint: disable=line-too-long
"""<itemset nodeset="instance('crop_list')/root/item[name = ../../crop ]">""", # noqa pylint: disable=line-too-long
Copy link
Contributor

Choose a reason for hiding this comment

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

We're using a predicate here, and we are changing the context to instance('crop_list'), so we would need current() here: [name = current()/../crop] and [name = current()/../../crop]

@MartijnR
Copy link
Contributor

Thanks! Once current() is back in choice-filters, I'll run the test forms in Enketo for a final/proper check.

@ukanga
Copy link
Contributor Author

ukanga commented Dec 2, 2018

@MartijnR 3c31570 should address it.

@yanokwa
Copy link
Contributor

yanokwa commented Dec 2, 2018

I got some XForms consulting time with @lognaturel and this PR looks like what she wanted. That is pyxform generates relative paths with . and .. everywhere except in predicates like choice filters. In those cases, current() should be used. My only minor concern is that are there any other places besides choice filters where we could use predicates?

@MartijnR
Copy link
Contributor

MartijnR commented Dec 5, 2018

Will do a check with some forms today.

My only minor concern is that are there any other places besides choice filters where we could use predicates?

I believe all those would be hand-coded combinations of pure XPath mixed with ${shortcuts} such as a calculate that searches for a value:

calculate = "count(instance('households')/root/item[rooms > ${room_size}])"

Since this is so advanced, I think we can for now require those users to take care of using current() themselves (and not use ${shortcut}s)

@MartijnR
Copy link
Contributor

MartijnR commented Dec 5, 2018

I confirm that this works great! Thanks!

I noticed that the calculations in Test 2 that I provided (name = ${crop}) make no sense (will always result in false), but that doesn't matter as the test still serves its purpose and the expression is valid.

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

Successfully merging this pull request may close these issues.

5 participants