Skip to content

Commit

Permalink
Expand refs in predicates using current() even if preceded by whitesp…
Browse files Browse the repository at this point in the history
…ace (#536)
  • Loading branch information
gushil authored May 18, 2021
1 parent ae7777c commit 3184e9d
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 9 deletions.
22 changes: 13 additions & 9 deletions pyxform/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -911,26 +911,30 @@ def _var_repl_function(
"""

name = matchobj.group(2)
bracketed_tag_name = "${{{0}}}".format(name)
last_saved = matchobj.group(1) is not None
is_indexed_repeat = matchobj.string.find("indexed-repeat(") > -1
indexed_repeat_regex = re.compile(r"indexed-repeat\([^)]+\)")
function_args_regex = re.compile(r"\b[^()]+\((.*)\)$")
instance_regex = re.compile(r"instance\([^)]+\S+")
instance_regex = re.compile(r"instance\([^)]+.+")
bracket_regex = re.compile(r"\[([^]]+)\]")

def _in_secondary_instance_predicate():
"""
check if ${} expression represented by matchobj
is in a predicate for a path expression for a secondary instance
"""

# It is possible to have multiple instance in an expression
instance_match_iter = instance_regex.finditer(matchobj.string)
for instance_match in instance_match_iter:
if (
matchobj.start() >= instance_match.start()
and matchobj.end() <= instance_match.end()
):
return True
if instance_regex.search(matchobj.string) is not None:
bracket_regex_match_iter = bracket_regex.finditer(matchobj.string)
# Check whether current ${varname} is in the correct bracket_regex_match
for bracket_regex_match in bracket_regex_match_iter:
if (
matchobj.start() >= bracket_regex_match.start()
and matchobj.end() <= bracket_regex_match.end()
):
return True
return False
return False

def _relative_path(name):
Expand Down
172 changes: 172 additions & 0 deletions pyxform/tests_v1/test_repeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,80 @@ def test_repeat_with_reference_path_in_predicate_uses_current(self,):
],
)

def test_repeat_with_reference_path_with_spaces_in_predicate_uses_current(self,):
"""
Test relative path expansion using current if reference path (with whitespaces before/after an operator of ${name}) is inside a predicate
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | calculate | pos1 | | position(..) |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | item5 | | instance('item')/root/item[index= ${pos5} and ${pos1} = 1]/label |
| | calculate | item6 | | instance('item')/root/item[index =${pos5} and ${pos1} = 1]/label |
| | calculate | item7 | | instance('item')/root/item[index = ${pos5} and ${pos1} = 1]/label |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="instance('item')/root/item[index= current()/../pos5 and /data/pos1 = 1]/label" nodeset="/data/rep5/item5" type="string"/>""", # noqa pylint: disable=line-too-long
"""<bind calculate="instance('item')/root/item[index = current()/../pos5 and /data/pos1 = 1]/label" nodeset="/data/rep5/item6" type="string"/>""", # noqa pylint: disable=line-too-long
"""<bind calculate="instance('item')/root/item[index = current()/../pos5 and /data/pos1 = 1]/label" nodeset="/data/rep5/item7" type="string"/>""", # noqa pylint: disable=line-too-long
],
)

def test_repeat_with_reference_path_in_a_method_with_spaces_in_predicate_uses_current(
self,
):
"""
Test relative path expansion using current if reference path in a method (with whitespaces before/after an operator of ${name}) is inside apredicate
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | calculate | pos1 | | position(..) |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | item5 | | instance('item')/root/item[index=number(1+ ${pos5} and ${pos1} = 1]/label |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="instance('item')/root/item[index=number(1+ current()/../pos5 and /data/pos1 = 1]/label" nodeset="/data/rep5/item5" type="string"/>""" # noqa pylint: disable=line-too-long
],
)

def test_repeat_with_reference_path_with_spaces_in_predicate_with_parenthesis_uses_current(
self,
):
"""
Test relative path expansion using current if reference path (with whitespaces before/after an operator of ${name}) is inside a predicate with parenthesis
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | calculate | pos1 | | position(..) |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | item5 | | instance('item')/root/item[index = ${pos5} and ${pos1} = 1]/label |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="instance('item')/root/item[index = current()/../pos5 and /data/pos1 = 1]/label" nodeset="/data/rep5/item5" type="string"/>""" # noqa pylint: disable=line-too-long
],
)

def test_relative_path_expansion_not_using_current_if_reference_path_is_predicate_but_not_in_a_repeat(
self,
):
Expand Down Expand Up @@ -822,3 +896,101 @@ def test_relative_path_expansion_not_using_current_if_reference_path_is_predicat
"""<bind calculate=" /data/rep1 [number( ../pos2 )]/label" nodeset="/data/rep1/item2" type="string"/>""" # noqa pylint: disable=line-too-long
],
)

def test_repeat_with_reference_path_in_multiple_predicate_uses_current(self,):
"""
Test relative path expansion using current if reference path is in multiple predicate
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | pos6 | | position(..) |
| | calculate | item5 | | instance('item')/root/item[index = ${pos5}][position()=${pos6}]/label |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="instance('item')/root/item[index = current()/../pos5 ][position()= current()/../pos6 ]/label" nodeset="/data/rep5/item5" type="string"/>""", # noqa pylint: disable=line-too-long
],
)

def test_repeat_with_reference_path_in_multiple_complex_predicate_uses_current(
self,
):
"""
Test relative path expansion using current if reference path is in multiple predicate, one of it is a complex one
${pos1} is not using current because it is not in repeat
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | calculate | pos1 | | position(..) |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | pos6 | | position(..) |
| | calculate | item5 | | instance('item')/root/item[index =${pos5} and selected('1 2 3 4', ${pos1})][position()=${pos6}]/label |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="instance('item')/root/item[index = current()/../pos5 and selected('1 2 3 4', /data/pos1 )][position()= current()/../pos6 ]/label" nodeset="/data/rep5/item5" type="string"/>""", # noqa pylint: disable=line-too-long
],
)

def test_repeat_with_reference_path_after_instance_in_predicate_uses_current(self,):
"""
Test relative reference path expansion uses current if reference path is in predicate
even if the reference path is found after the instance() expression
${pos5} that is in 'index =${pos5}'' uses current because it is in a predicate
${pos5} that is in 'position()=${pos5}'' uses current because it is in a predicate (eventhough not in an instance)
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | item5 | | concat(instance('item')/root/item[index =${pos5}]/label, @data/form/[position()=${pos5}]/text |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="concat(instance('item')/root/item[index = current()/../pos5 ]/label, @data/form/[position()= current()/../pos5 ]/text" nodeset="/data/rep5/item5" type="string"/>""", # noqa pylint: disable=line-too-long
],
)

def test_repeat_with_reference_path_after_instance_not_in_predicate_not_using_current(
self,
):
"""
Test relative reference path expansion not using current if reference path is not in predicate
and the reference path is found after the instance() expression
${pos5} that is in 'index =${pos5} uses current because it is in a predicate
${pos5} that is in '${pos5} + 1'' not using current because it is not in a predicate (regardless in an instance or not)
"""
xlsform_md = """
| survey | | | | |
| | type | name | label | calculation |
| | xml-external | item | | |
| | begin repeat | rep5 | | |
| | calculate | pos5 | | position(..) |
| | calculate | item5 | | concat(instance('item')/root/item[index =${pos5}]/label, ${pos5} + 1) |
| | end repeat | | | |
"""
self.assertPyxformXform(
name="data",
md=xlsform_md,
xml__contains=[
"""<bind calculate="concat(instance('item')/root/item[index = current()/../pos5 ]/label, ../pos5 + 1)" nodeset="/data/rep5/item5" type="string"/>""", # noqa pylint: disable=line-too-long
],
)

0 comments on commit 3184e9d

Please sign in to comment.