From fab7fce8d22163a1c017c07d2c58b149050b96ca Mon Sep 17 00:00:00 2001 From: Ukang'a Dickson Date: Sun, 7 Aug 2016 18:16:23 +0300 Subject: [PATCH 1/5] add support for external instances, closes #30 --- pyxform/aliases.py | 6 +++++- pyxform/question.py | 7 ++++++- pyxform/survey.py | 14 ++++++++++++++ pyxform/xls2json.py | 11 ++++++++--- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/pyxform/aliases.py b/pyxform/aliases.py index 26bea9f54..fc1a78e0e 100644 --- a/pyxform/aliases.py +++ b/pyxform/aliases.py @@ -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, diff --git a/pyxform/question.py b/pyxform/question.py index 14406a267..6711eff84 100644 --- a/pyxform/question.py +++ b/pyxform/question.py @@ -1,3 +1,5 @@ +import os.path + from utils import node from survey_element import SurveyElement from question_type_dictionary import QUESTION_TYPE_DICT @@ -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 + ']' diff --git a/pyxform/survey.py b/pyxform/survey.py index df73f21e7..136434a8b 100644 --- a/pyxform/survey.py +++ b/pyxform/survey.py @@ -128,6 +128,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 element @@ -142,6 +155,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: diff --git a/pyxform/xls2json.py b/pyxform/xls2json.py index b4eba9f87..e793fdc48 100644 --- a/pyxform/xls2json.py +++ b/pyxform/xls2json.py @@ -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." @@ -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( @@ -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] From 40bdc3d94bc84b1e9a939ccbaa1f738436aeacf5 Mon Sep 17 00:00:00 2001 From: Ukang'a Dickson Date: Sun, 7 Aug 2016 18:21:59 +0300 Subject: [PATCH 2/5] add tests for support of external instances --- .../test_support_external_instances.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 pyxform/tests_v1/test_support_external_instances.py diff --git a/pyxform/tests_v1/test_support_external_instances.py b/pyxform/tests_v1/test_support_external_instances.py new file mode 100644 index 000000000..53e26e0d9 --- /dev/null +++ b/pyxform/tests_v1/test_support_external_instances.py @@ -0,0 +1,105 @@ +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=[ + """ + + + + + + """, # noqa + '', + """ + + + + + + """, # noqa + '', + ], debug=True + ) + + +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=["There. should be a choices sheet in this xlsform"], + debug=True + ) + + 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"], + debug=True + ) From 8d6b0f5b46035db0675a8951109d9561ab911106 Mon Sep 17 00:00:00 2001 From: Ukang'a Dickson Date: Sun, 7 Aug 2016 18:32:58 +0300 Subject: [PATCH 3/5] add tests for support of external instances with choices --- .../test_support_external_instances.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pyxform/tests_v1/test_support_external_instances.py b/pyxform/tests_v1/test_support_external_instances.py index 53e26e0d9..b196bb882 100644 --- a/pyxform/tests_v1/test_support_external_instances.py +++ b/pyxform/tests_v1/test_support_external_instances.py @@ -34,6 +34,39 @@ def test_external_csv_instances(self): ], debug=True ) + 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=[ + """ + + + + + + """, # noqa + '', + """ + + + + + + """, # noqa + '', + '', ], debug=True ) @@ -89,6 +91,7 @@ def test_external_xml_instances(self): """, # noqa '', + '', """ @@ -98,6 +101,7 @@ def test_external_xml_instances(self): """, # noqa '', '', - ], debug=True + ], ) def test_external_csv_instances_w_choice_filter(self): @@ -66,7 +66,7 @@ def test_external_csv_instances_w_choice_filter(self): """, # noqa '', '', - ], debug=True + ], ) @@ -118,8 +118,7 @@ def test_external_other_extension_instances(self): | | select_multiple_from_file neighbourhoods.pdf | neighbourhoods | Neighbourhoods | """, # noqa errored=True, - error_contains=["There. should be a choices sheet in this xlsform"], - debug=True + error_contains=["should be a choices sheet in this xlsform"], ) def test_external_choices_sheet_included_instances(self): @@ -138,5 +137,4 @@ def test_external_choices_sheet_included_instances(self): """, # noqa errored=True, error__contains=["List name not in choices sheet: cities.pdf"], - debug=True )