Skip to content

Commit

Permalink
Merge pull request #76 from XLSForm/support_external_instances
Browse files Browse the repository at this point in the history
add support for external_instances
  • Loading branch information
dorey authored Aug 11, 2016
2 parents a8e2711 + 5fc4d46 commit 3f7bd1e
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 5 deletions.
6 changes: 5 additions & 1 deletion pyxform/aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
u"select one": constants.SELECT_ONE,
u"select_multiple": constants.SELECT_ALL_THAT_APPLY,
u"select all that apply": constants.SELECT_ALL_THAT_APPLY,
u"select_one_external": u"select one external"
u"select_one_external": u"select one external",
u"select_one_from_file": constants.SELECT_ONE,
u"select_multiple_from_file": constants.SELECT_ALL_THAT_APPLY,
u"select one from file": constants.SELECT_ONE,
u"select multiple from file": constants.SELECT_ALL_THAT_APPLY,
}
cascading = {
u'cascading select': constants.CASCADING_SELECT,
Expand Down
7 changes: 6 additions & 1 deletion pyxform/question.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os.path

from utils import node
from survey_element import SurveyElement
from question_type_dictionary import QUESTION_TYPE_DICT
Expand Down Expand Up @@ -152,7 +154,10 @@ def xml_control(self):
# check to prevent the rare dicts that show up
if self['itemset'] and isinstance(self['itemset'], basestring):
choice_filter = self.get('choice_filter')
nodeset = "instance('" + self['itemset'] + "')/root/item"
itemset, file_extension = os.path.splitext(self['itemset'])
itemset = itemset \
if file_extension in ['.csv', '.xml'] else self['itemset']
nodeset = "instance('" + itemset + "')/root/item"
choice_filter = survey.insert_xpaths(choice_filter)
if choice_filter:
nodeset += '[' + choice_filter + ']'
Expand Down
14 changes: 14 additions & 0 deletions pyxform/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,19 @@ def _generate_pulldata_instances(self):
src="jr://file-csv/{}.csv".format(csv_id)
)

def _generate_from_file_instances(self):
for i in self.iter_descendants():
itemset = i.get('itemset')
if itemset and \
(itemset.endswith('.csv') or itemset.endswith('.xml')):
file_id, file_extension = os.path.splitext(itemset)
yield node(
"instance",
node("root", node("item", node("name"), node("label"))),
id=file_id,
src="jr://file-%s/%s" % (file_extension[1:], itemset)
)

def xml_model(self):
"""
Generate the xform <model> element
Expand All @@ -162,6 +175,7 @@ def xml_model(self):
model_children += [node("instance", self.xml_instance())]
model_children += list(self._generate_static_instances())
model_children += list(self._generate_pulldata_instances())
model_children += list(self._generate_from_file_instances())
model_children += self.xml_bindings()

if self.submission_url or self.public_key:
Expand Down
140 changes: 140 additions & 0 deletions pyxform/tests_v1/test_support_external_instances.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from pyxform_test_case import PyxformTestCase


class ExternalCSVInstancesTest(PyxformTestCase):
def test_external_csv_instances(self):
# re: https://github.com/XLSForm/pyxform/issues/30
self.assertPyxformXform(
name="ecsv",
md="""
| survey | | | |
| | type | name | label |
| | select_one_from_file cities.csv | city | City |
| | select_multiple_from_file neighbourhoods.csv | neighbourhoods | Neighbourhoods |
""", # noqa
xml__contains=[
"""<instance id="cities" src="jr://file-csv/cities.csv">
<root>
<item>
<name/>
<label/>
</item>
</root>
</instance>""", # noqa
'<select1 ref="/ecsv/city">',
'<itemset nodeset="instance(\'cities\')/root/item">',
"""<instance id="neighbourhoods" src="jr://file-csv/neighbourhoods.csv">
<root>
<item>
<name/>
<label/>
</item>
</root>
</instance>""", # noqa
'<select ref="/ecsv/neighbourhoods">',
'<itemset nodeset="instance(\'neighbourhoods\')/root/item">',
],
)

def test_external_csv_instances_w_choice_filter(self):
# re: https://github.com/XLSForm/pyxform/issues/30
self.assertPyxformXform(
name="ecsv",
md="""
| survey | | | |
| | type | name | label | choice_filter |
| | select_one_from_file cities.csv | city | City | |
| | select_multiple_from_file neighbourhoods.csv | neighbourhoods | Neighbourhoods | city=${city} |
""", # noqa
xml__contains=[
"""<instance id="cities" src="jr://file-csv/cities.csv">
<root>
<item>
<name/>
<label/>
</item>
</root>
</instance>""", # noqa
'<select1 ref="/ecsv/city">',
"""<instance id="neighbourhoods" src="jr://file-csv/neighbourhoods.csv">
<root>
<item>
<name/>
<label/>
</item>
</root>
</instance>""", # noqa
'<select ref="/ecsv/neighbourhoods">',
'<itemset nodeset="instance(\'neighbourhoods\')/root/item[city= /ecsv/city ]">',
],
)


class ExternalXMLInstancesTest(PyxformTestCase):
def test_external_xml_instances(self):
# re: https://github.com/XLSForm/pyxform/issues/30
self.assertPyxformXform(
name="exml",
md="""
| survey | | | |
| | type | name | label |
| | select_one_from_file cities.xml | city | City |
| | select_multiple_from_file neighbourhoods.xml | neighbourhoods | Neighbourhoods |
""", # noqa
xml__contains=[
"""<instance id="cities" src="jr://file-xml/cities.xml">
<root>
<item>
<name/>
<label/>
</item>
</root>
</instance>""", # noqa
'<select1 ref="/exml/city">',
'<itemset nodeset="instance(\'cities\')/root/item">',
"""<instance id="neighbourhoods" src="jr://file-xml/neighbourhoods.xml">
<root>
<item>
<name/>
<label/>
</item>
</root>
</instance>""", # noqa
'<select ref="/exml/neighbourhoods">',
'<itemset nodeset="instance(\'neighbourhoods\')/root/item">',
],
)


class InvalidExternalFileInstancesTest(PyxformTestCase):
def test_external_other_extension_instances(self):
# re: https://github.com/XLSForm/pyxform/issues/30
self.assertPyxformXform(
name="epdf",
md="""
| survey | | | |
| | type | name | label |
| | select_one_from_file cities.pdf | city | City |
| | select_multiple_from_file neighbourhoods.pdf | neighbourhoods | Neighbourhoods |
""", # noqa
errored=True,
error_contains=["should be a choices sheet in this xlsform"],
)

def test_external_choices_sheet_included_instances(self):
# re: https://github.com/XLSForm/pyxform/issues/30
self.assertPyxformXform(
name="epdf",
md="""
| survey | | | |
| | type | name | label |
| | select_one_from_file cities.pdf | city | City |
| | select_multiple_from_file neighbourhoods.pdf | neighbourhoods | Neighbourhoods |
| choices |
| | list name | name | label |
| | fruits | apple | Apple |
""", # noqa
errored=True,
error__contains=["List name not in choices sheet: cities.pdf"],
)
11 changes: 8 additions & 3 deletions pyxform/xls2json.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,9 +657,11 @@ def replace_prefix(d, prefix):
u" filtered selects.")
select_type = aliases.select['select_one']
list_name = parse_dict["list_name"]
list_file_name, file_extension = os.path.splitext(list_name)

if list_name not in choices\
and select_type != 'select one external':
if list_name not in choices \
and select_type != 'select one external' \
and file_extension not in ['.csv', '.xml']:
if not choices:
raise PyXFormError(
u"There should be a choices sheet in this xlsform."
Expand All @@ -672,7 +674,8 @@ def replace_prefix(d, prefix):

# Validate select_multiple choice names by making sure
# they have no spaces (will cause errors in exports).
if select_type == constants.SELECT_ALL_THAT_APPLY:
if select_type == constants.SELECT_ALL_THAT_APPLY \
and file_extension not in ['.csv', '.xml']:
for choice in choices[list_name]:
if ' ' in choice[constants.NAME]:
raise PyXFormError(
Expand Down Expand Up @@ -719,6 +722,8 @@ def replace_prefix(d, prefix):
else:
new_json_dict['itemset'] = list_name
json_dict['choices'] = choices
elif file_extension in ['.csv', '.xml']:
new_json_dict['itemset'] = list_name
else:
new_json_dict[constants.CHOICES] = choices[list_name]

Expand Down

0 comments on commit 3f7bd1e

Please sign in to comment.