Skip to content

Commit

Permalink
Put dynamic defauls for repeat descendants in repeat
Browse files Browse the repository at this point in the history
  • Loading branch information
lognaturel committed Feb 14, 2020
1 parent d40e2ad commit c24e42c
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 11 deletions.
24 changes: 20 additions & 4 deletions pyxform/section.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,32 @@ def xml_control(self):
for n in Section.xml_control(self):
repeat_node.appendChild(n)

for e in self.children:
dynamic_default = e.get_setvalue_node_for_dynamic_default()
if dynamic_default:
repeat_node.appendChild(dynamic_default)
setvalue_nodes = self._get_setvalue_nodes_for_dynamic_defaults()

for setvalue_node in setvalue_nodes:
repeat_node.appendChild(setvalue_node)

label = self.xml_label()
if label:
return node("group", self.xml_label(), repeat_node, ref=self.get_xpath())
return node("group", repeat_node, ref=self.get_xpath(), **self.control)

# Get setvalue nodes for all descendants of this repeat that have dynamic defaults and aren't nested in other repeats.
def _get_setvalue_nodes_for_dynamic_defaults(self):
setvalue_nodes = []
self._dynamic_defaults_helper(self, setvalue_nodes)
return setvalue_nodes

def _dynamic_defaults_helper(self, current, nodes):
for e in current.children:
if e.type != "repeat": # let nested repeats handle their own defaults
dynamic_default = e.get_setvalue_node_for_dynamic_default(
in_repeat=True
)
if dynamic_default:
nodes.append(dynamic_default)
self._dynamic_defaults_helper(e, nodes)

# I'm anal about matching function signatures when overriding a function,
# but there's no reason for kwargs to be an argument
def template_instance(self, **kwargs):
Expand Down
16 changes: 12 additions & 4 deletions pyxform/survey_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,14 @@ def needs_itext_ref(self):
type(self.media) is dict and len(self.media) > 0
)

def get_setvalue_node_for_dynamic_default(self):
def get_setvalue_node_for_dynamic_default(self, in_repeat=False):
if not self.default or not default_is_dynamic(self.default, self.type):
return None

default_with_xpath_paths = self.get_root().insert_xpaths(self.default, self)

triggering_events = "odk-instance-first-load"

if self.parent.__class__.__name__ == "RepeatingSection":
if in_repeat:
triggering_events = triggering_events + " odk-new-repeat"

return node(
Expand Down Expand Up @@ -439,7 +438,16 @@ def xml_bindings(self):
result.append(xml_binding)

# dynamic defaults for repeats go in the body. All other dynamic defaults (setvalue actions) go in the model
if e.parent.__class__.__name__ != "RepeatingSection":
if (
len(
[
ancestor
for ancestor in e.get_lineage()
if ancestor.type == "repeat"
]
)
== 0
):
dynamic_default = e.get_setvalue_node_for_dynamic_default()
if dynamic_default:
result.append(dynamic_default)
Expand Down
54 changes: 51 additions & 3 deletions pyxform/tests_v1/test_dynamic_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ def test_handling_dynamic_default(self):
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/age" value="some_rando_func()"/>',
)

def test_static_defaults(self):
self.assertPyxformXform(
name="dynamic",
md="""
| survey | | | | |
| | type | name | label | default |
| | integer | foo | Foo | foo |
| | begin repeat | repeat | | |
| | integer | bar | Bar | 12 |
| | end repeat | repeat | | |
""",
model__contains=["<foo>foo</foo>", "<bar>12</bar>"],
model__excludes=["setvalue"],
)

def test_handling_dynamic_default_in_repeat(self):
"""
Should use set-value for dynamic default form inside repeat
Expand Down Expand Up @@ -161,13 +176,46 @@ def test_dynamic_default_in_group_nested_in_repeat(self):
| | end group | group | | |
| | end repeat | repeat | | |
""",
debug=True,
xml__contains=[
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/repeat/group/bar" value=" ../foo "/>'
],
model__excludes=[
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/repeat/group/bar" value=" ../foo "/>'
model__excludes=['<setvalue event="odk-instance-first-load'],
)

def test_dynamic_defaults_in_nested_repeat(self):
md = """
| survey | | | | |
| | type | name | label | default |
| | begin repeat | outer | | |
| | date | date | Date | now() |
| | integer | foo | Foo | |
| | begin repeat | inner | | |
| | integer | bar | Bar | ${foo} |
| | end repeat | inner | | |
| | end repeat | outer | | |
"""

self.assertPyxformXform(
name="dynamic",
md=md,
xml__contains=[
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/outer/inner/bar" value=" ../../foo "/>',
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/outer/date" value="now()"/>',
],
model__excludes=['<setvalue event="odk-instance-first-load'],
)

survey = self.md_to_pyxform_survey(
md_raw=md,
kwargs={"id_string": "id", "name": "dynamic", "title": "some-title"},
autoname=False,
)
survey_xml = survey._to_pretty_xml()

self.assertContains(
survey_xml,
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/outer/inner/bar" value=" ../../foo "/>',
1,
)

def test_handling_arithmetic_expression(self):
Expand Down

0 comments on commit c24e42c

Please sign in to comment.