Skip to content

Commit

Permalink
Merge pull request #380 from nribeka/pyxform-182-v2
Browse files Browse the repository at this point in the history
Adding repeat instance to the model for repeat
  • Loading branch information
yanokwa authored Dec 19, 2019
2 parents c131e55 + 8503d1b commit 154ae78
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 8 deletions.
27 changes: 22 additions & 5 deletions pyxform/section.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def xml_instance(self, **kwargs):
"""
Creates an xml representation of the section
"""
append_template = kwargs.pop("append_template", False)

attributes = {}
attributes.update(kwargs)
attributes.update(self.get("instance", {}))
Expand All @@ -40,12 +42,30 @@ def xml_instance(self, **kwargs):
for key, value in attributes.items():
attributes[key] = survey.insert_xpaths(value, self)
result = node(self.name, **attributes)

for child in self.children:
repeating_template = None
if child.get("flat"):
for grandchild in child.xml_instance_array():
result.appendChild(grandchild)
elif isinstance(child, ExternalInstance):
continue
else:
if isinstance(child, RepeatingSection) and not append_template:
append_template = not append_template
repeating_template = child.generate_repeating_template()
result.appendChild(child.xml_instance(append_template=append_template))
if append_template and repeating_template:
append_template = not append_template
result.insertBefore(repeating_template, result._get_lastChild())
return result

def generate_repeating_template(self, **kwargs):
attributes = {"jr:template": ""}
result = node(self.name, **attributes)
for child in self.children:
if isinstance(child, RepeatingSection):
result.appendChild(child.template_instance())
else:
result.appendChild(child.xml_instance())
return result
Expand Down Expand Up @@ -109,11 +129,8 @@ def xml_control(self):

# I'm anal about matching function signatures when overriding a function,
# but there's no reason for kwargs to be an argument
def xml_instance(self, **kwargs):
kwargs = {"jr:template": ""} # It might make more sense to add this
# as a child on initialization

return super(RepeatingSection, self).xml_instance(**kwargs)
def template_instance(self, **kwargs):
return super(RepeatingSection, self).generate_repeating_template(**kwargs)


class GroupedSection(Section):
Expand Down
4 changes: 4 additions & 0 deletions pyxform/tests/test_expected_output/repeat_date_test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<table_list_3/>
<table_list_4/>
</repeat_test>
<repeat_test>
<table_list_3/>
<table_list_4/>
</repeat_test>
<generated_note_name_8/>
<meta>
<instanceID/>
Expand Down
16 changes: 16 additions & 0 deletions pyxform/tests/test_expected_output/xlsform_spec_test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,22 @@
<compact-test/>
<compact-2-test/>
</repeat_test>
<repeat_test>
<group_test>
<required_text/>
<select_multiple_test/>
</group_test>
<labeled_select_group>
<label-test/>
<list-nolabel-test/>
</labeled_select_group>
<name>
<reserved_name_for_field_list_labels_25/>
<table_list_question/>
</name>
<compact-test/>
<compact-2-test/>
</repeat_test>
<acknowledge_test/>
<date_test/>
<time_test/>
Expand Down
21 changes: 21 additions & 0 deletions pyxform/tests/test_output/cascades_old.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@
<q31_child_faeces_disposal/>
<q32_latrine_hygiene/>
</villager>
<villager>
<q25_faeces_mouth/>
<q26_prevent_faeces_mouth/>
<q27_handwashing/>
<q28_handwashing_facilities/>
<q29_water_storage/>
<q30_diseases/>
<q31_child_faeces_disposal/>
<q32_latrine_hygiene/>
</villager>
</sectionC3>
<sectionC4>
<sectionC4_note/>
Expand All @@ -71,6 +81,17 @@
<q40_latrine_handwash/>
<q41_latrine_soap/>
</household>
<household>
<q33_hh_latrine_usage/>
<q34_latrine_screen_vent/>
<q35_latrine_seat_cover/>
<q36_latrine_door/>
<q37_latrine_flies/>
<q38_latrine_odour/>
<q39_latrine_cleaned/>
<q40_latrine_handwash/>
<q41_latrine_soap/>
</household>
</sectionC4>
<sectionC5>
<sectionC5_note/>
Expand Down
2 changes: 1 addition & 1 deletion pyxform/tests/xlsform_spec_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def runTest(self):
path_to_excel_file, default_name="warnings", warnings=warnings
)
self.assertEquals(
len(warnings), 21, "Found " + str(len(warnings)) + " warnings"
len(warnings), 22, "Found " + str(len(warnings)) + " warnings"
)


Expand Down
4 changes: 2 additions & 2 deletions pyxform/tests_v1/test_dynamic_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ def test_handling_dynamic_default_in_repeat(self):
)
survey_xml = survey._to_pretty_xml()

self.assertContains(survey_xml, "<feeling>not_func$</feeling>", 1)
self.assertContains(survey_xml, "<age/>", 1)
self.assertContains(survey_xml, "<feeling>not_func$</feeling>", 2)
self.assertContains(survey_xml, "<age/>", 2)
self.assertContains(
survey_xml,
'<setvalue event="odk-instance-first-load odk-new-repeat" ref="/dynamic/household/age" value="some_rando_func()"/>',
Expand Down
141 changes: 141 additions & 0 deletions pyxform/tests_v1/test_repeat_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
"""
Test repeat template and instance structure.
"""
from pyxform.tests_v1.pyxform_test_case import PyxformTestCase


class TestRepeatTemplate(PyxformTestCase):
"""
Ensuring the template and instance structure are added correctly.
"""

def test_repeat_adding_template_and_instance(self):
"""
Repeat should add template and instances
"""
md = """
| survey | | | |
| | type | name | label |
| | text | aa | Text AA |
| | begin repeat | section | Section |
| | text | a | Text A |
| | text | b | Text B |
| | text | c | Text C |
| | note | d | Note D |
| | end repeat | | |
| | | | |
| | begin repeat | repeat_a| Section A |
| | begin repeat | repeat_b| Section B |
| | text | e | Text E |
| | begin repeat | repeat_c| Section C |
| | text | f | Text F |
| | end repeat | | |
| | end repeat | | |
| | text | g | Text G |
| | begin repeat | repeat_d| Section D |
| | note | h | Note H |
| | end repeat | | |
| | note | i | Note I |
| | end repeat | | |
"""

survey = self.md_to_pyxform_survey(md_raw=md)
survey_xml = survey._to_pretty_xml()

section_template = '<section jr:template="">'
self.assertEqual(1, survey_xml.count(section_template))
repeat_a_template = '<repeat_a jr:template="">'
self.assertEqual(1, survey_xml.count(repeat_a_template))
repeat_b_template = '<repeat_b jr:template="">'
self.assertEqual(1, survey_xml.count(repeat_b_template))
repeat_c_template = '<repeat_c jr:template="">'
self.assertEqual(1, survey_xml.count(repeat_c_template))
repeat_d_template = '<repeat_d jr:template="">'
self.assertEqual(1, survey_xml.count(repeat_d_template))

section_instance = "<section>"
self.assertEqual(1, survey_xml.count(section_instance))
repeat_a_instance = "<repeat_a>"
self.assertEqual(1, survey_xml.count(repeat_a_instance))
repeat_b_instance = "<repeat_b>"
self.assertEqual(1, survey_xml.count(repeat_b_instance))
repeat_c_instance = "<repeat_c>"
self.assertEqual(1, survey_xml.count(repeat_c_instance))
repeat_d_instance = "<repeat_d>"
self.assertEqual(1, survey_xml.count(repeat_d_instance))

self.assertPyxformXform(
md=md,
instance__contains=[
'<section jr:template="">',
'<repeat_a jr:template="">',
'<repeat_b jr:template="">',
'<repeat_c jr:template="">',
'<repeat_d jr:template="">',
"<section>",
"<repeat_a>",
"<repeat_b>",
"<repeat_c>",
"<repeat_d>",
],
)

def test_repeat_adding_template_and_instance_with_group(self):
"""
Repeat should add template and instance even when they are inside grouping
"""
md = """
| survey | | | |
| | type | name | label |
| | text | aa | Text AA |
| | begin repeat | section | Section |
| | text | a | Text A |
| | text | b | Text B |
| | text | c | Text C |
| | note | d | Note D |
| | end repeat | | |
| | | | |
| | begin group | group_a | Group A |
| | begin repeat | repeat_a| Section A |
| | begin repeat | repeat_b| Section B |
| | text | e | Text E |
| | begin group | group_b | Group B |
| | text | f | Text F |
| | text | g | Text G |
| | note | h | Note H |
| | end group | | |
| | note | i | Note I |
| | end repeat | | |
| | end repeat | | |
| | end group | | |
"""

survey = self.md_to_pyxform_survey(md_raw=md)
survey_xml = survey._to_pretty_xml()

section_template = '<section jr:template="">'
self.assertEqual(1, survey_xml.count(section_template))
repeat_a_template = '<repeat_a jr:template="">'
self.assertEqual(1, survey_xml.count(repeat_a_template))
repeat_b_template = '<repeat_b jr:template="">'
self.assertEqual(1, survey_xml.count(repeat_b_template))

section_instance = "<section>"
self.assertEqual(1, survey_xml.count(section_instance))
repeat_a_instance = "<repeat_a>"
self.assertEqual(1, survey_xml.count(repeat_a_instance))
repeat_b_instance = "<repeat_b>"
self.assertEqual(1, survey_xml.count(repeat_b_instance))

self.assertPyxformXform(
md=md,
instance__contains=[
'<section jr:template="">',
'<repeat_a jr:template="">',
'<repeat_b jr:template="">',
"<section>",
"<repeat_a>",
"<repeat_b>",
],
)
15 changes: 15 additions & 0 deletions pyxform/xls2json.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ def workbook_to_json(
# Rows from the survey sheet that should be nested in meta
survey_meta = []

repeat_behavior_warning_added = False
dynamic_default_warning_added = False
for row in survey_sheet:
row_number += 1
Expand Down Expand Up @@ -910,6 +911,20 @@ def workbook_to_json(
# until an end command is encountered.
control_type = aliases.control[parse_dict["type"]]
control_name = question_name

if (
control_type == constants.REPEAT
and not repeat_behavior_warning_added
):
warnings.append(
"Repeat behavior has changed. Previously, some clients like "
"ODK Collect prompted users to add the first repeat. Now, "
"the user will only be prompted to add repeats after the first "
"one. Representing 0 repetitions will require changing the form "
"design. Read more at http://xlsform.org#representing-zero-repeats."
)
repeat_behavior_warning_added = True

new_json_dict = row.copy()
new_json_dict[constants.TYPE] = control_type
child_list = list()
Expand Down

0 comments on commit 154ae78

Please sign in to comment.