Skip to content

Commit

Permalink
Merge pull request #499 from gushil/fix-484-indexed-repeat-argument-path
Browse files Browse the repository at this point in the history
Fixed indexed-repeat argument path evaluation in integer column type
  • Loading branch information
lognaturel authored Jan 6, 2021
2 parents fb5dbf1 + 211d575 commit 45ebf9f
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 49 deletions.
76 changes: 34 additions & 42 deletions pyxform/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -912,52 +912,44 @@ def _is_return_relative_path():
current_matchobj = matchobj

if not last_saved and context:
if context["type"] == "text":

if not is_indexed_repeat:
return True
if not is_indexed_repeat:
return True

# It is possible to have multiple indexed-repeat in an expression
indexed_repeats_iter = indexed_repeat_regex.finditer(
matchobj.string
)
for indexed_repeat in indexed_repeats_iter:

# Make sure current ${name} is in the correct indexed-repeat
if current_matchobj.end() > indexed_repeat.end():
try:
next(indexed_repeats_iter)
continue
except StopIteration:
return True

# ${name} outside of indexed-repeat always using relative path
if (
current_matchobj.end() < indexed_repeat.start()
or current_matchobj.start() > indexed_repeat.end()
):
# It is possible to have multiple indexed-repeat in an expression
indexed_repeats_iter = indexed_repeat_regex.finditer(matchobj.string)
for indexed_repeat in indexed_repeats_iter:

# Make sure current ${name} is in the correct indexed-repeat
if current_matchobj.end() > indexed_repeat.end():
try:
next(indexed_repeats_iter)
continue
except StopIteration:
return True

indexed_repeat_name_index = None
indexed_repeat_args = (
function_args_regex.match(indexed_repeat.group())
.group(1)
.split(",")
)
name_arg = "${{{0}}}".format(name)
for idx, arg in enumerate(indexed_repeat_args):
if name_arg in arg.strip():
indexed_repeat_name_index = idx

return (
indexed_repeat_name_index is not None
and indexed_repeat_name_index
not in indexed_repeat_relative_path_args_index
)
else:
return not (
context["type"] == "calculate"
and "indexed-repeat" in context["bind"]["calculate"]
# ${name} outside of indexed-repeat always using relative path
if (
current_matchobj.end() < indexed_repeat.start()
or current_matchobj.start() > indexed_repeat.end()
):
return True

indexed_repeat_name_index = None
indexed_repeat_args = (
function_args_regex.match(indexed_repeat.group())
.group(1)
.split(",")
)
name_arg = "${{{0}}}".format(name)
for idx, arg in enumerate(indexed_repeat_args):
if name_arg in arg.strip():
indexed_repeat_name_index = idx

return (
indexed_repeat_name_index is not None
and indexed_repeat_name_index
not in indexed_repeat_relative_path_args_index
)

return False
Expand Down
15 changes: 15 additions & 0 deletions pyxform/tests_v1/test_dynamic_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,18 @@ def test_default_date_not_considered_dynamic(self):
""",
xml__contains=["<foo>2020-01-01</foo>"],
)

def test_dynamic_default_on_calculate(self):
self.assertPyxformXform(
name="dynamic",
md="""
| survey | | | | | |
| | type | name | label | calculation | default |
| | calculate | r | | | random() + 0.5 |
| | calculate | one | | | if(${r} < 1,'A','B') |
""",
xml__contains=[
"""<setvalue event="odk-instance-first-load" ref="/dynamic/r" value="random() + 0.5"/>""",
"""<setvalue event="odk-instance-first-load" ref="/dynamic/one" value="if( /dynamic/r &lt; 1,'A','B')"/>""",
],
)
40 changes: 33 additions & 7 deletions pyxform/tests_v1/test_repeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ def test_choice_from_previous_repeat_answers_in_child_repeat(self):
name="data",
id_string="some-id",
md=xlsform_md,
xml__contains=['<itemset nodeset="../../../member[ ./age &gt; 18]">',],
xml__contains=['<itemset nodeset="../../../member[ ./age &gt; 18]">'],
)

def test_choice_from_previous_repeat_answers_in_nested_repeat(self):
Expand All @@ -444,7 +444,7 @@ def test_choice_from_previous_repeat_answers_in_nested_repeat(self):
name="data",
id_string="some-id",
md=xlsform_md,
xml__contains=['<itemset nodeset="../../person[ ./age &gt; 18]">',],
xml__contains=['<itemset nodeset="../../person[ ./age &gt; 18]">'],
)

def test_choice_from_previous_repeat_answers_in_nested_repeat_uses_current(self):
Expand Down Expand Up @@ -534,7 +534,9 @@ def test_indexed_repeat_nested_repeat_relative_path_exception(self):
],
)

def test_indexed_repeat_math_epression_nested_repeat_relative_path_exception(self,):
def test_indexed_repeat_math_expression_nested_repeat_relative_path_exception(
self,
):
"""Test relative path exception (absolute path) in indexed-repeat() with math expression using nested repeat."""
self.assertPyxformXform(
name="data",
Expand All @@ -556,7 +558,7 @@ def test_indexed_repeat_math_epression_nested_repeat_relative_path_exception(sel
],
)

def test_multiple_indexed_repeat_in_epression_nested_repeat_relative_path_exception(
def test_multiple_indexed_repeat_in_expression_nested_repeat_relative_path_exception(
self,
):
"""Test relative path exception (absolute path) in multiple indexed-repeat() inside an expression using nested repeat."""
Expand All @@ -580,10 +582,10 @@ def test_multiple_indexed_repeat_in_epression_nested_repeat_relative_path_except
],
)

def test_mixed_variables_and_indexed_repeat_in_epression_nested_repeat_relative_path_exception(
def test_mixed_variables_and_indexed_repeat_in_expression_text_type_nested_repeat_relative_path_exception(
self,
):
"""Test relative path exception (absolute path) in an expression contains variables and indexed-repeat() using nested repeat."""
"""Test relative path exception (absolute path) in an expression contains variables and indexed-repeat() in a text type using nested repeat."""
self.assertPyxformXform(
name="data",
title="In nested repeat, indexed-repeat 1st, 2nd, 4th, and 6th argument is using absolute path",
Expand All @@ -604,7 +606,31 @@ def test_mixed_variables_and_indexed_repeat_in_epression_nested_repeat_relative_
],
)

def test_indexed_repeat_math_epression_with_double_variable_in_nested_repeat_relative_path_exception(
def test_mixed_variables_and_indexed_repeat_in_expression_integer_type_nested_repeat_relative_path_exception(
self,
):
"""Test relative path exception (absolute path) in an expression contains variables and indexed-repeat() in an integer type using nested repeat."""
self.assertPyxformXform(
name="data",
title="In nested repeat, indexed-repeat 1st, 2nd, 4th, and 6th argument is using absolute path",
md="""
| survey | | | | |
| | type | name | label | default |
| | begin_group | page | | |
| | begin_repeat | bp_rg | | |
| | integer | bp_row | Repeating group entry | |
| | text | bp_pos | Position | |
| | integer | bp_sys | Systolic pressure | |
| | integer | bp_dia | Diastolic pressure | if(${bp_row} = 1, '', indexed-repeat(${bp_dia}, ${bp_rg}, ${bp_row} - 1)) |
| | end repeat | | | |
| | end group | | | |
""", # noqa pylint: disable=line-too-long
xml__contains=[
"""<setvalue event="odk-instance-first-load odk-new-repeat" ref="/data/page/bp_rg/bp_dia" value="if( ../bp_row = 1, '', indexed-repeat( /data/page/bp_rg/bp_dia , /data/page/bp_rg , ../bp_row - 1))"/>""" # noqa pylint: disable=line-too-long
],
)

def test_indexed_repeat_math_expression_with_double_variable_in_nested_repeat_relative_path_exception(
self,
):
"""Test relative path exception (absolute path) in indexed-repeat() with math expression and double variable using nested repeat."""
Expand Down

0 comments on commit 45ebf9f

Please sign in to comment.