Skip Patterns with multiple relevant logics

sample for odk forum.xlsx (13.6 KB)
Hello All,
Thanks for the immense support provided on this platform. I have a slight challenge. I'm trying to insert two different skip logics under the "relevant" column, for example, I have a question that asks about the type of building structure. Succeeding questions become relevant based on the option selected e.g either residential, collective or other. There is also a question that asks about permission from building owner. If the answer is "No" it is expected that it should go to the end of the questionnaire (that's not a problem). However, Other questions too are dependent on the type of building structure. So I want to combine the responses for building structure with the affirmative permission .i.e. the relevant building option with the 'Yes' permisiioon from the owner of the building. How do I go about this? I have attached a sample of the form. The section in question is the last section (dwelling) and the logic in question is in red.

Found a way around it

Can you share your approach so others could benefit?

I believe your existing relevant tests may be incorrect:

relevant="${str_type} = selected(${str_type},'str_res') or selected(${str_type},'str_coll') or selected(${str_type},'str_oth')"

because you appear to be comparing ${str_type} - which is text - against a boolean selected(...) expression...

Note, if you have a whole bunch of questions that are all dependent on a particular response, rather than having to put a relevant='...' for each of them, it may be simpler to put all the dependent questions in a group, and then just have a single relevant='...' for the group.

Ditto, I'm also curious as to what your solution/'way around' is?

1 Like

Thanks for the assistance Dr. Bestor. Attached is a snippet of the form. Basically, I changed the logic to ensure that succeeding questions are dependent on permission being granted by the building owner. The permission question is only relevant based on a couple of responses.sample for odk forum.xlsx (13.6 KB)

NP. But your line 20 relevant expression for continue_1 is still not right:

${str_type} = selected(${str_type},'str_res') or selected(${str_type},'str_coll') or selected(${str_type},'str_oth')

This will if fact evaluate OK, and give you a true/false result, but its probably not going to be what you expect... If you compare a string (ie ${str_type}) to a boolean (ie selected(...)) then the XPath = equals operator will first convert the string to a boolean (!) and then do a boolean compare, and my guess is you probably dont know if "str_res" will turn out to be true() or false(), eh? :wink:

You may be right. However, series of tests and pilots after I made the correction showed no issues. I will run it again to be sure.

Your original expression probably works, but that is more from 'a series of fortunate events', than design... :slight_smile:

To explain, your original XPath expression:

${str_type} = selected(${str_type},'str_res') or selected(${str_type},'str_coll') or selected(${str_type},'str_oth')

will be evaluated according to XPath operator precedence (see here); ie shown with brackets it is equivalent to:

( ( (${str_type} = selected(${str_type},'str_res')) or selected(${str_type},'str_coll') ) or selected(${str_type},'str_oth') )

So the first subexpression evaluated is: ${str_type} = selected(${str_type},'str_res'). This compares different data types - a string element against a boolean element - which XPath does by making both booleans; again, from the W3C spec:

If one object to be compared is a node-set and the other is a boolean, then the comparison will be true if and only if the result of performing the comparison on the boolean and on the result of converting the node-set to a boolean using the boolean function is true.

So the subexpression becomes: boolean(${str_type}) = selected(${str_type},'str_res')

Now, because your ${str_type} just so happens to come from a select-one, and is therefore (always) going to be a non-empty string, boolean(${str_type}) will basically always return 1. Again, from the W3C spec:

The boolean function converts its argument to a boolean as follows:
- a number is true if and only if it is neither positive or negative zero nor NaN
- a node-set is true if and only if it is non-empty
- a string is true if and only if its length is non-zero

Which is to say: 1 = selected(${str_type},'str_res')

And because an equality comparison between a boolean against 1 is basically redundant, your overall expression simplifies to:

selected(${str_type},'str_res') or selected(${str_type},'str_coll') or selected(${str_type},'str_oth')

Which is probably what you really meant to say in the first place, eh! :wink:

Sorry for the extremely long-winded explanation, but I think its important to fully understand how these XPath expressions will evaluate your inputs. If, for example, your ${str_type} came from a regular user text input, it could be blank (ie the user didnt enter anything), and your expression would basically become not(selected(${str_type},'str_res')) or selected(${str_type},'str_coll') or selected(${str_type},'str_oth'), which may give an unanticipated result.