ODK Collect - Calculate fires even when not relevant

What is the problem? Please be detailed!
In an XlsForm, we have a calculate called selected_female_name. It has a relevant set to (${roster_complete} = 'yes'). Its value is indexed-repeat(${firstname}, ${hh_rpt}, ${randpos}).

There is a household roster in the form which is a repeat group hh_rpt. roster_complete is a select_one question that comes after the repeat group ends, and the respondent verifies by selecting from a 'yes' or 'no'.

The issue is occurring at times during the roster. Clearly, the roster is not complete yet, so the question roster_complete has not yet been asked. Therefore we expect its value to be empty, thus falsy. If it is in fact falsy, then we expect the relevant on the selected_female_name calculate to evaluate to false, and the calculate to also be empty, thus falsy. However we get the following error.

What ODK tool and version are you using? And on what device and operating system version?
ODK Collect (can check later for version if needed) on Android 6.

What steps can we take to reproduce the problem?
The form has been uploaded here: https://drive.google.com/open?id=0ByFhlRDkSVPoYjVfbmJMR2pqZ3M

The form is rather short, and the error is not very far into the form.

What you have you tried to fix the problem?
We changed the value of selected_female_name from indexed-repeat(${firstname}, ${hh_rpt}, ${randpos}) to if((${roster_complete} = 'yes') and (${randpos} != ''), indexed-repeat(${firstname}, ${hh_rpt}, ${randpos}), '').

This has fixed the problem for us in the form of a workaround. However I am wondering if it is a bug in ODK that relevants on calculates are not firing correctly? It does not seem to make sense to me that we would need to wrap this logic in an if statement in addition to a relevant.

3 Likes

Hi Joseph! Thanks for being so detailed with your post. It really helps us figure out what the problem might be.

From your screenshot, this looks like an old fork of Collect. Are you able to replicate this problem on the latest version of Collect?

Hello Yaw,

Thanks for the help. You are absolutely correct! An old fork. I did install the latest version of ODK Collect on my phone from Google Play, but I am still getting the same issue.

This appears to be the logic that is causing the issue to trigger:

Hmm, this might be a bug in Collect or JavaRosa. @Grzesiek2010 can you try to reproduce this when you have a chance?

Sure, I'll look at it.

1 Like

@Joseph_E_Flack_IV @yanokwa

I investigated and reproduce this issue and I don't think it's a bug I think it works as expected.
Using indexed-repeat according to the documentation:

References the field of the n-th repeat of a group. Groups and indices for 1 to 3 nested repeats can be specified. For example, the XLSForm calculate expression "indexed-repeat(${name}, ${names}, 1)" will return the value of the first "name" field inside a repeat group named "names".

The last value defines number of section. In this example it's 1 so it's the first group but @Joseph_E_Flack_IV in your case you use ${randpos}. Everything is ok if a value of that ${randpos} is defined otherwise you should be informed about this fact and you are (you can see the dialog). So in my opinion your workaround with using if statement is not exactly a workaround because you should use if statement if it's possible that related value is not defined to avoid trying using indexed-repeat without defined value (without the number of group).

Regards,
Grzegorz

2 Likes

@Grzesiek2010 @Joseph_E_Flack_IV

Let's separate concerns properly. We know that the function indexed-repeat is not being properly called because ${randpos} is not defined (so empty). That is not our question.

The question is why is the calculate firing at all if it is not relevant? Isn't it the case that fields that are not relevant do not appear in the XML instance that is returned to the server? Thus, this calculate, when it is not relevant, does not appear in the XML instance. Despite that, it is being evaluated. Since it does not appear in the XML instance, it isn't "saved," so to speak. This appears to be a bug.

I'm not an javarosa expert but I think relevant is not considered to be using with calculations.
If we use relevant="false()" with calculation it makes invisible the related question and it makes sense to me. We can use calculation like below:
<bind nodeset="/data/Number" type="int" calculate="2*3"/>
or:
<bind nodeset="/data/Number" type="int"/>
<bind calculate="2*3" nodeset="/data/Number" type="integer"/>

In the first case adding relevant = false() should make this question invisible of course so it should work in the same way in the second case I think (and it works so it's ok to me).

@jpringle You are correct that fields that are not relevant do not appear in the XML instance. But according to my tests in Collect 1.8.1, calculations only fire when they are relevant.

In my tests, I ask if we want to make a particular calculation relevant. If I say yes, I get the value printed. If I say no, I don't get the value and nothing is printed. Below are the forms I used.

t8923.xlsx (10.5 KB)
t8923.xml (1.5 KB)

There might still be a bug and I want to make sure we get to the bottom of this!

@Joseph_E_Flack_IV can you try my form on your build of Collect to see if you get the same behavior? And can you post a much-reduced version of your form where calculates fire even when relevant?

@yanokwa it looks like relevant = false() causes returned value is null but despite the fact it's false it's calculated. Compare these two forms:
calculate.xls (7.5 KB)
calculate2.xls (7.5 KB)

I mean even if it shouldn't be returned because relevant is false its is calculated.

@jpringle @Joseph_E_Flack_IV, thanks for bringing this up. I agree that it's unexpected that calculates that are not relevant are evaluated. At the very least, it should be explicitly documented with the workaround that you used called out. I think we should also explore whether this was a deliberate choice or not and whether it would be possible to change.