diff --git a/.circleci/config.yml b/.circleci/config.yml index e8ef335ba..ac0fd2e02 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,6 +21,14 @@ jobs: command: | sudo apt-get install -y openjdk-8-jre-headless sudo sed -i -e '/^assistive_technologies=/s/^/#/' /etc/java-*-openjdk/accessibility.properties + - run: + name: Test code is well formatted + command: | + if ! type /home/circleci/.local/bin/black > /dev/null; then + echo "Black is not supported in this python version :(" + else + /home/circleci/.local/bin/black --target-version=py27 pyxform --check --quiet || (echo 'The source code could use a bit more black.' && exit 1) + fi - run: name: Run tests command: | diff --git a/.gitignore b/.gitignore index c280cd289..8faf9b55f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ env # ignore pypi manifest MANIFEST +pyxform/tests/test_output/ diff --git a/README.rst b/README.rst index aa7ef4877..e99cdf527 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,9 @@ pyxform v0.13.x .. |codecov| image:: https://codecov.io/github/XLSForm/pyxform/branch/master/graph/badge.svg :target: https://codecov.io/github/XLSForm/pyxform +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/python/black + pyxform is a Python library that makes writing XForms for ODK Collect and enketo easy by converting XLS(X) spreadsheets into XForms. It is used as a library in a number of tools including `the ODK online converter `_ and `Ona `_. diff --git a/pre-commit.sh b/pre-commit.sh index cf4f4cefe..915d99610 100755 --- a/pre-commit.sh +++ b/pre-commit.sh @@ -8,8 +8,8 @@ if [ -n "$FILES" ]; then fi if [ -n "$FILES" ]; then - if black --target-version=py36 $FILES; then - touch .commit + if black --target-version=py27 $FILES; then + touch .commit fi fi diff --git a/pyxform/__init__.py b/pyxform/__init__.py index caeebde99..3340937f1 100644 --- a/pyxform/__init__.py +++ b/pyxform/__init__.py @@ -1,19 +1,24 @@ +# -*- coding: utf-8 -*- """ pyxform is a Python library designed to make authoring XForms for ODK Collect easy. """ -__version__ = '0.13.1' +__version__ = "0.13.1" -from pyxform.builder import (SurveyElementBuilder, create_survey, # noqa - create_survey_element_from_dict, - create_survey_from_path, create_survey_from_xls) -from pyxform.instance import SurveyInstance # noqa -from pyxform.question import (InputQuestion, MultipleChoiceQuestion, # noqa - Question) -from pyxform.question_type_dictionary import QUESTION_TYPE_DICT # noqa -from pyxform.section import Section # noqa -from pyxform.survey import Survey # noqa -from pyxform.xls2json import SurveyReader as ExcelSurveyReader # noqa +from pyxform.builder import ( + SurveyElementBuilder, + create_survey, + create_survey_element_from_dict, + create_survey_from_path, + create_survey_from_xls, +) +from pyxform.instance import SurveyInstance +from pyxform.question import InputQuestion, MultipleChoiceQuestion, Question +from pyxform.question_type_dictionary import QUESTION_TYPE_DICT +from pyxform.section import Section +from pyxform.survey import Survey +from pyxform.xls2json import SurveyReader as ExcelSurveyReader # This is what gets imported when someone imports pyxform +# flake8: noqa diff --git a/pyxform/aliases.py b/pyxform/aliases.py index 4dde626b0..34a1f5bc4 100644 --- a/pyxform/aliases.py +++ b/pyxform/aliases.py @@ -1,3 +1,8 @@ +# -*- coding: utf-8 -*- +""" +Aliases for elements which could mean the same element in XForm but is represented +differently on the XLSForm. +""" from pyxform import constants # Aliases: @@ -7,118 +12,116 @@ # which is why self mapped keys are necessary. control = { - u"group": constants.GROUP, - u"lgroup": constants.REPEAT, - u"repeat": constants.REPEAT, - u"loop": constants.LOOP, - u"looped group": constants.REPEAT + "group": constants.GROUP, + "lgroup": constants.REPEAT, + "repeat": constants.REPEAT, + "loop": constants.LOOP, + "looped group": constants.REPEAT, } select = { - u"add select one prompt using": constants.SELECT_ONE, - u"add select multiple prompt using": constants.SELECT_ALL_THAT_APPLY, - u"select all that apply from": constants.SELECT_ALL_THAT_APPLY, - u"select one from": constants.SELECT_ONE, - u"select1": constants.SELECT_ONE, - u"select_one": constants.SELECT_ONE, - 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_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, - u"rank": constants.RANK, + "add select one prompt using": constants.SELECT_ONE, + "add select multiple prompt using": constants.SELECT_ALL_THAT_APPLY, + "select all that apply from": constants.SELECT_ALL_THAT_APPLY, + "select one from": constants.SELECT_ONE, + "select1": constants.SELECT_ONE, + "select_one": constants.SELECT_ONE, + "select one": constants.SELECT_ONE, + "select_multiple": constants.SELECT_ALL_THAT_APPLY, + "select all that apply": constants.SELECT_ALL_THAT_APPLY, + "select_one_external": "select one external", + "select_one_from_file": constants.SELECT_ONE, + "select_multiple_from_file": constants.SELECT_ALL_THAT_APPLY, + "select one from file": constants.SELECT_ONE, + "select multiple from file": constants.SELECT_ALL_THAT_APPLY, + "rank": constants.RANK, } cascading = { - u'cascading select': constants.CASCADING_SELECT, - u'cascading_select': constants.CASCADING_SELECT, + "cascading select": constants.CASCADING_SELECT, + "cascading_select": constants.CASCADING_SELECT, } settings_header = { - u"form_title": constants.TITLE, - u"set form title": constants.TITLE, - u"form_id": constants.ID_STRING, - u"sms_keyword": constants.SMS_KEYWORD, - u"sms_separator": constants.SMS_SEPARATOR, - u"sms_allow_media": constants.SMS_ALLOW_MEDIA, - u"sms_date_format": constants.SMS_DATE_FORMAT, - u"sms_datetime_format": constants.SMS_DATETIME_FORMAT, - - u"prefix": constants.COMPACT_PREFIX, - u"delimiter": constants.COMPACT_DELIMITER, - - u"set form id": constants.ID_STRING, - u"public_key": constants.PUBLIC_KEY, - u"submission_url": constants.SUBMISSION_URL, - u"auto_send": constants.AUTO_SEND, - u"auto_delete": constants.AUTO_DELETE + "form_title": constants.TITLE, + "set form title": constants.TITLE, + "form_id": constants.ID_STRING, + "sms_keyword": constants.SMS_KEYWORD, + "sms_separator": constants.SMS_SEPARATOR, + "sms_allow_media": constants.SMS_ALLOW_MEDIA, + "sms_date_format": constants.SMS_DATE_FORMAT, + "sms_datetime_format": constants.SMS_DATETIME_FORMAT, + "prefix": constants.COMPACT_PREFIX, + "delimiter": constants.COMPACT_DELIMITER, + "set form id": constants.ID_STRING, + "public_key": constants.PUBLIC_KEY, + "submission_url": constants.SUBMISSION_URL, + "auto_send": constants.AUTO_SEND, + "auto_delete": constants.AUTO_DELETE, } # TODO: Check on bind prefix approach in json. # Conversion dictionary from user friendly column names to meaningful values survey_header = { - u"Label": u"label", - u"Name": u"name", - u"SMS Field": constants.SMS_FIELD, - u"SMS Option": constants.SMS_OPTION, - u"SMS Sepatator": constants.SMS_SEPARATOR, - u"SMS Allow Media": constants.SMS_ALLOW_MEDIA, - u"SMS Date Format": constants.SMS_DATE_FORMAT, - u"SMS DateTime Format": constants.SMS_DATETIME_FORMAT, - u"SMS Response": constants.SMS_RESPONSE, - u"compact_tag": u"instance::odk:tag", # used for compact representation - u"Type": u"type", - u"List_name": u"list_name", + "Label": "label", + "Name": "name", + "SMS Field": constants.SMS_FIELD, + "SMS Option": constants.SMS_OPTION, + "SMS Sepatator": constants.SMS_SEPARATOR, + "SMS Allow Media": constants.SMS_ALLOW_MEDIA, + "SMS Date Format": constants.SMS_DATE_FORMAT, + "SMS DateTime Format": constants.SMS_DATETIME_FORMAT, + "SMS Response": constants.SMS_RESPONSE, + "compact_tag": "instance::odk:tag", # used for compact representation + "Type": "type", + "List_name": "list_name", # u"repeat_count": u"jr:count", duplicate key - u"read_only": u"bind::readonly", - u"readonly": u"bind::readonly", - u"relevant": u"bind::relevant", - u"caption": constants.LABEL, - u"appearance": u"control::appearance", # TODO: this is also an issue - u"relevance": u"bind::relevant", - u"required": u"bind::required", - u"constraint": u"bind::constraint", - u"constraining message": u"bind::jr:constraintMsg", - u"constraint message": u"bind::jr:constraintMsg", - u"constraint_message": u"bind::jr:constraintMsg", - u"calculation": u"bind::calculate", - u"calculate": u"bind::calculate", - u"command": constants.TYPE, - u"tag": constants.NAME, - u"value": constants.NAME, - u"image": u"media::image", - u"audio": u"media::audio", - u"video": u"media::video", - u"count": u"control::jr:count", - u"repeat_count": u"control::jr:count", - u"jr:count": u"control::jr:count", - u"autoplay": u"control::autoplay", - u"rows": u"control::rows", + "read_only": "bind::readonly", + "readonly": "bind::readonly", + "relevant": "bind::relevant", + "caption": constants.LABEL, + "appearance": "control::appearance", # TODO: this is also an issue + "relevance": "bind::relevant", + "required": "bind::required", + "constraint": "bind::constraint", + "constraining message": "bind::jr:constraintMsg", + "constraint message": "bind::jr:constraintMsg", + "constraint_message": "bind::jr:constraintMsg", + "calculation": "bind::calculate", + "calculate": "bind::calculate", + "command": constants.TYPE, + "tag": constants.NAME, + "value": constants.NAME, + "image": "media::image", + "audio": "media::audio", + "video": "media::video", + "count": "control::jr:count", + "repeat_count": "control::jr:count", + "jr:count": "control::jr:count", + "autoplay": "control::autoplay", + "rows": "control::rows", # New elements that have to go into itext elements: - u"noAppErrorString": u"bind::jr:noAppErrorString", - u"no_app_error_string": u"bind::jr:noAppErrorString", - u"requiredMsg": u"bind::jr:requiredMsg", - u"required_message": u"bind::jr:requiredMsg", - u"required message": u"bind::jr:requiredMsg", - u"body": u"control", - u"parameters": u"parameters", + "noAppErrorString": "bind::jr:noAppErrorString", + "no_app_error_string": "bind::jr:noAppErrorString", + "requiredMsg": "bind::jr:requiredMsg", + "required_message": "bind::jr:requiredMsg", + "required message": "bind::jr:requiredMsg", + "body": "control", + "parameters": "parameters", } list_header = { - u"caption": constants.LABEL, - u"list_name": constants.LIST_NAME, - u"value": constants.NAME, - u"image": u"media::image", - u"audio": u"media::audio", - u"video": u"media::video" + "caption": constants.LABEL, + "list_name": constants.LIST_NAME, + "value": constants.NAME, + "image": "media::image", + "audio": "media::audio", + "video": "media::video", } # Note that most of the type aliasing happens in all.xls _type = { - u"imei": u"deviceid", - u"image": u"photo", - u"add image prompt": u"photo", - u"add photo prompt": u"photo", - u"add audio prompt": u"audio", - u"add video prompt": u"video", - u"add file prompt": u"file" + "imei": "deviceid", + "image": "photo", + "add image prompt": "photo", + "add photo prompt": "photo", + "add audio prompt": "audio", + "add video prompt": "video", + "add file prompt": "file", } yes_no = { "yes": True, @@ -137,14 +140,12 @@ "false()": False, } label_optional_types = [ - u"deviceid", - u"phonenumber", - u"simserial", - u"calculate", - u"start", - u"end", - u"today" + "deviceid", + "phonenumber", + "simserial", + "calculate", + "start", + "end", + "today", ] -osm = { - u"osm": constants.OSM_TYPE -} +osm = {"osm": constants.OSM_TYPE} diff --git a/pyxform/builder.py b/pyxform/builder.py index eedb43224..844ef53b1 100644 --- a/pyxform/builder.py +++ b/pyxform/builder.py @@ -1,12 +1,22 @@ +# -*- coding: utf-8 -*- +""" +Survey builder functionality. +""" import copy import os from pyxform import file_utils, utils from pyxform.errors import PyXFormError from pyxform.external_instance import ExternalInstance -from pyxform.question import (InputQuestion, MultipleChoiceQuestion, - OsmUploadQuestion, Question, RangeQuestion, - TriggerQuestion, UploadQuestion) +from pyxform.question import ( + InputQuestion, + MultipleChoiceQuestion, + OsmUploadQuestion, + Question, + RangeQuestion, + TriggerQuestion, + UploadQuestion, +) from pyxform.question_type_dictionary import QUESTION_TYPE_DICT from pyxform.section import GroupedSection, RepeatingSection from pyxform.survey import Survey @@ -22,7 +32,7 @@ def copy_json_dict(json_dict): items = None if type(json_dict) is list: - json_dict_copy = [None]*len(json_dict) + json_dict_copy = [None] * len(json_dict) items = enumerate(json_dict) elif type(json_dict) is dict: json_dict_copy = {} @@ -40,30 +50,28 @@ def copy_json_dict(json_dict): class SurveyElementBuilder(object): # we use this CLASSES dict to create questions from dictionaries QUESTION_CLASSES = { - u"": Question, - u"input": InputQuestion, - u"trigger": TriggerQuestion, - u"select": MultipleChoiceQuestion, - u"select1": MultipleChoiceQuestion, - u"odk:rank": MultipleChoiceQuestion, - u"upload": UploadQuestion, - u"osm": OsmUploadQuestion, - u'range': RangeQuestion, - } + "": Question, + "input": InputQuestion, + "trigger": TriggerQuestion, + "select": MultipleChoiceQuestion, + "select1": MultipleChoiceQuestion, + "odk:rank": MultipleChoiceQuestion, + "upload": UploadQuestion, + "osm": OsmUploadQuestion, + "range": RangeQuestion, + } SECTION_CLASSES = { - u"group": GroupedSection, - u"repeat": RepeatingSection, - u"survey": Survey, - } + "group": GroupedSection, + "repeat": RepeatingSection, + "survey": Survey, + } def __init__(self, **kwargs): # I don't know why we would need an explicit none option for # select alls self._add_none_option = False - self.set_sections( - kwargs.get(u"sections", {}) - ) + self.set_sections(kwargs.get("sections", {})) def set_sections(self, sections): """ @@ -80,57 +88,60 @@ def create_survey_element_from_dict(self, d): call it because it corresponds directly with a json object) to a survey object """ - if u"add_none_option" in d: - self._add_none_option = d[u"add_none_option"] - if d[u"type"] in self.SECTION_CLASSES: + if "add_none_option" in d: + self._add_none_option = d["add_none_option"] + if d["type"] in self.SECTION_CLASSES: return self._create_section_from_dict(d) - elif d[u"type"] == u"loop": + elif d["type"] == "loop": return self._create_loop_from_dict(d) - elif d[u"type"] == u"include": - section_name = d[u"name"] + elif d["type"] == "include": + section_name = d["name"] if section_name not in self._sections: - raise PyXFormError("This section has not been included.", - section_name, self._sections.keys()) + raise PyXFormError( + "This section has not been included.", + section_name, + self._sections.keys(), + ) d = self._sections[section_name] full_survey = self.create_survey_element_from_dict(d) return full_survey.children - elif d[u"type"] == u"xml-external": + elif d["type"] == "xml-external": return ExternalInstance(**d) else: return self._create_question_from_dict( - d, copy_json_dict(QUESTION_TYPE_DICT), self._add_none_option) + d, copy_json_dict(QUESTION_TYPE_DICT), self._add_none_option + ) @staticmethod - def _create_question_from_dict(d, question_type_dictionary, - add_none_option=False): - question_type_str = d[u"type"] + def _create_question_from_dict(d, question_type_dictionary, add_none_option=False): + question_type_str = d["type"] d_copy = d.copy() # TODO: Keep add none option? - if add_none_option \ - and question_type_str.startswith(u"select all that apply"): - SurveyElementBuilder\ - ._add_none_option_to_select_all_that_apply(d_copy) + if add_none_option and question_type_str.startswith("select all that apply"): + SurveyElementBuilder._add_none_option_to_select_all_that_apply(d_copy) # Handle or_other on select type questions - or_other_str = u" or specify other" + or_other_str = " or specify other" if question_type_str.endswith(or_other_str): - question_type_str = \ - question_type_str[:len(question_type_str) - len(or_other_str)] + question_type_str = question_type_str[ + : len(question_type_str) - len(or_other_str) + ] d_copy["type"] = question_type_str - SurveyElementBuilder\ - ._add_other_option_to_multiple_choice_question(d_copy) + SurveyElementBuilder._add_other_option_to_multiple_choice_question(d_copy) return [ SurveyElementBuilder._create_question_from_dict( - d_copy, question_type_dictionary, add_none_option), - SurveyElementBuilder._create_specify_other_question_from_dict( - d_copy)] + d_copy, question_type_dictionary, add_none_option + ), + SurveyElementBuilder._create_specify_other_question_from_dict(d_copy), + ] question_class = SurveyElementBuilder._get_question_class( - question_type_str, question_type_dictionary) + question_type_str, question_type_dictionary + ) # todo: clean up this spaghetti code - d_copy[u"question_type_dictionary"] = question_type_dictionary + d_copy["question_type_dictionary"] = question_type_dictionary if question_class: return question_class(**d_copy) @@ -140,34 +151,28 @@ def _create_question_from_dict(d, question_type_dictionary, @staticmethod def _add_other_option_to_multiple_choice_question(d): # ideally, we'd just be pulling from children - choice_list = d.get(u"choices", d.get(u"children", [])) + choice_list = d.get("choices", d.get("children", [])) if len(choice_list) <= 0: raise PyXFormError("There should be choices for this question.") - other_choice = { - u"name": u"other", - u"label": u"Other", - } + other_choice = {"name": "other", "label": "Other"} if other_choice not in choice_list: choice_list.append(other_choice) @staticmethod def _add_none_option_to_select_all_that_apply(d_copy): - choice_list = d_copy.get(u"choices", d_copy.get(u"children", [])) + choice_list = d_copy.get("choices", d_copy.get("children", [])) if len(choice_list) <= 0: raise PyXFormError("There should be choices for this question.") - none_choice = { - u"name": u"none", - u"label": u"None", - } + none_choice = {"name": "none", "label": "None"} if none_choice not in choice_list: choice_list.append(none_choice) - none_constraint = u"(.='none' or not(selected(., 'none')))" - if u"bind" not in d_copy: - d_copy[u"bind"] = {} - if u"constraint" in d_copy[u"bind"]: - d_copy[u"bind"][u"constraint"] += " and " + none_constraint + none_constraint = "(.='none' or not(selected(., 'none')))" + if "bind" not in d_copy: + d_copy["bind"] = {} + if "constraint" in d_copy["bind"]: + d_copy["bind"]["constraint"] += " and " + none_constraint else: - d_copy[u"bind"][u"constraint"] = none_constraint + d_copy["bind"]["constraint"] = none_constraint @staticmethod def _get_question_class(question_type_str, question_type_dictionary): @@ -177,38 +182,36 @@ def _get_question_class(question_type_str, question_type_dictionary): type_dictionary -> QUESTION_CLASSES """ question_type = question_type_dictionary.get(question_type_str, {}) - control_dict = question_type.get(u"control", {}) - control_tag = control_dict.get(u"tag", u"") - if control_tag == u"upload" \ - and control_dict.get(u"mediatype") == "osm/*": - control_tag = u"osm" + control_dict = question_type.get("control", {}) + control_tag = control_dict.get("tag", "") + if control_tag == "upload" and control_dict.get("mediatype") == "osm/*": + control_tag = "osm" return SurveyElementBuilder.QUESTION_CLASSES[control_tag] @staticmethod def _create_specify_other_question_from_dict(d): kwargs = { - u"type": u"text", - u"name": u"%s_other" % d[u"name"], - u"label": u"Specify other.", - u"bind": {u"relevant": u"selected(../%s, 'other')" % d[u"name"]}, - } + "type": "text", + "name": "%s_other" % d["name"], + "label": "Specify other.", + "bind": {"relevant": "selected(../%s, 'other')" % d["name"]}, + } return InputQuestion(**kwargs) def _create_section_from_dict(self, d): d_copy = d.copy() - children = d_copy.pop(u"children", []) - section_class = self.SECTION_CLASSES[d_copy[u"type"]] - if d[u'type'] == u'survey' and u'title' not in d: - d_copy[u'title'] = d[u'name'] + children = d_copy.pop("children", []) + section_class = self.SECTION_CLASSES[d_copy["type"]] + if d["type"] == "survey" and "title" not in d: + d_copy["title"] = d["name"] result = section_class(**d_copy) for child in children: # Deep copying the child is a hacky solution to the or_other bug. # I don't know why it works. # And I hope it doesn't break something else. # I think the good solution would be to rewrite this class. - survey_element = self.create_survey_element_from_dict( - copy.deepcopy(child)) + survey_element = self.create_survey_element_from_dict(copy.deepcopy(child)) if survey_element: result.add_children(survey_element) @@ -220,8 +223,8 @@ def _create_loop_from_dict(self, d): Returns a GroupedSection """ d_copy = d.copy() - children = d_copy.pop(u"children", []) - columns = d_copy.pop(u"columns", []) + children = d_copy.pop("children", []) + columns = d_copy.pop("columns", []) result = GroupedSection(**d_copy) # columns is a left over from when this was @@ -229,17 +232,16 @@ def _create_loop_from_dict(self, d): for column_dict in columns: # If this is a none option for a select all that apply # question then we should skip adding it to the result - if column_dict[u"name"] == "none": + if column_dict["name"] == "none": continue column = GroupedSection(**column_dict) for child in children: - question_dict = self._name_and_label_substitutions( - child, column_dict) + question_dict = self._name_and_label_substitutions(child, column_dict) question = self.create_survey_element_from_dict(question_dict) column.add_child(question) result.add_child(column) - if result.name != u"": + if result.name != "": return result # TODO: Verify that nothing breaks if this returns a list @@ -249,12 +251,19 @@ def _name_and_label_substitutions(self, question_template, column_headers): # if the label in column_headers has multiple languages setup a # dictionary by language to do substitutions. info_by_lang = {} - if type(column_headers[u"label"]) == dict: + if type(column_headers["label"]) == dict: info_by_lang = dict( - [(lang, {u"name": column_headers[u"name"], - u"label": column_headers[u"label"][lang]}) - for lang in column_headers[u"label"].keys()] - ) + [ + ( + lang, + { + "name": column_headers["name"], + "label": column_headers["label"][lang], + }, + ) + for lang in column_headers["label"].keys() + ] + ) result = question_template.copy() for key in result.keys(): @@ -263,9 +272,8 @@ def _name_and_label_substitutions(self, question_template, column_headers): elif type(result[key]) == dict: result[key] = result[key].copy() for key2 in result[key].keys(): - if type(column_headers[u"label"]) == dict: - result[key][key2] %= info_by_lang.get( - key2, column_headers) + if type(column_headers["label"]) == dict: + result[key][key2] %= info_by_lang.get(key2, column_headers) else: result[key][key2] %= column_headers return result @@ -301,12 +309,13 @@ def create_survey_from_xls(path_or_file): def create_survey( - name_of_main_section=None, sections=None, - main_section=None, - id_string=None, - title=None, - default_language=None, - ): + name_of_main_section=None, + sections=None, + main_section=None, + id_string=None, + title=None, + default_language=None, +): """ name_of_main_section -- a string key used to find the main section in the sections dict if it is not supplied in the @@ -324,9 +333,10 @@ def create_survey( builder.set_sections(sections) # assert name_of_main_section in sections, name_of_main_section - if u"id_string" not in main_section: - main_section[u"id_string"] = name_of_main_section \ - if id_string is None else name_of_main_section + if "id_string" not in main_section: + main_section["id_string"] = ( + name_of_main_section if id_string is None else name_of_main_section + ) survey = builder.create_survey_element_from_dict(main_section) # not sure where to do this without repeating ourselves, @@ -360,9 +370,6 @@ def create_survey_from_path(path, include_directory=False): else: main_section_name, section = file_utils.load_file_to_dict(path) sections = {main_section_name: section} - pkg = { - u'name_of_main_section': main_section_name, - u'sections': sections - } + pkg = {"name_of_main_section": main_section_name, "sections": sections} return create_survey(**pkg) diff --git a/pyxform/constants.py b/pyxform/constants.py index 534d6ebfc..c240eebfb 100644 --- a/pyxform/constants.py +++ b/pyxform/constants.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ This file contains constants that correspond with the property names in the json survey format. (@see json_form_schema.json) These names are to be shared @@ -8,68 +9,72 @@ # TODO: Replace matching strings in the json2xforms code (builder.py, # survey.py, survey_element.py, question.py) with these constants -TYPE = u"type" -TITLE = u"title" -NAME = u"name" -ID_STRING = u"id_string" -SMS_KEYWORD = u"sms_keyword" -SMS_FIELD = u"sms_field" -SMS_OPTION = u"sms_option" -SMS_SEPARATOR = u"sms_separator" -SMS_ALLOW_MEDIA = u"sms_allow_media" -SMS_DATE_FORMAT = u"sms_date_format" -SMS_DATETIME_FORMAT = u"sms_datetime_format" -SMS_RESPONSE = u"sms_response" +TYPE = "type" +TITLE = "title" +NAME = "name" +ID_STRING = "id_string" +SMS_KEYWORD = "sms_keyword" +SMS_FIELD = "sms_field" +SMS_OPTION = "sms_option" +SMS_SEPARATOR = "sms_separator" +SMS_ALLOW_MEDIA = "sms_allow_media" +SMS_DATE_FORMAT = "sms_date_format" +SMS_DATETIME_FORMAT = "sms_datetime_format" +SMS_RESPONSE = "sms_response" # compact representation (https://opendatakit.github.io/xforms-spec/#compact-record-representation-(for-sms)) -COMPACT_PREFIX = u"prefix" -COMPACT_DELIMITER = u"delimiter" -COMPACT_TAG = u"compact_tag" +COMPACT_PREFIX = "prefix" +COMPACT_DELIMITER = "delimiter" +COMPACT_TAG = "compact_tag" -VERSION = u"version" -PUBLIC_KEY = u"public_key" -SUBMISSION_URL = u"submission_url" -AUTO_SEND = u"auto_send" -AUTO_DELETE = u"auto_delete" -DEFAULT_LANGUAGE = u"default_language" -LABEL = u"label" -HINT = u"hint" -STYLE = u"style" -ATTRIBUTE = u"attribute" +VERSION = "version" +PUBLIC_KEY = "public_key" +SUBMISSION_URL = "submission_url" +AUTO_SEND = "auto_send" +AUTO_DELETE = "auto_delete" +DEFAULT_LANGUAGE = "default_language" +LABEL = "label" +HINT = "hint" +STYLE = "style" +ATTRIBUTE = "attribute" -BIND = u"bind" # TODO: What should I do with the nested types? (readonly and relevant) # noqa -MEDIA = u"media" -CONTROL = u"control" -APPEARANCE = u"appearance" +BIND = ( + "bind" +) # TODO: What should I do with the nested types? (readonly and relevant) # noqa +MEDIA = "media" +CONTROL = "control" +APPEARANCE = "appearance" -LOOP = u"loop" -COLUMNS = u"columns" +LOOP = "loop" +COLUMNS = "columns" -REPEAT = u"repeat" -GROUP = u"group" -CHILDREN = u"children" +REPEAT = "repeat" +GROUP = "group" +CHILDREN = "children" -SELECT_ONE = u"select one" -SELECT_ALL_THAT_APPLY = u"select all that apply" -RANK = u"rank" -CHOICES = u"choices" +SELECT_ONE = "select one" +SELECT_ALL_THAT_APPLY = "select all that apply" +RANK = "rank" +CHOICES = "choices" # XLS Specific constants -LIST_NAME = u"list name" -CASCADING_SELECT = u"cascading_select" -TABLE_LIST = u"table-list" # hyphenated because it goes in appearance, and convention for appearance column is dashes # noqa +LIST_NAME = "list name" +CASCADING_SELECT = "cascading_select" +TABLE_LIST = ( + "table-list" +) # hyphenated because it goes in appearance, and convention for appearance column is dashes # noqa # The following are the possible sheet names: -SURVEY = u"survey" -SETTINGS = u"settings" +SURVEY = "survey" +SETTINGS = "settings" # These sheet names are for list sheets -CHOICES_AND_COLUMNS = u"choices and columns" -CASCADING_CHOICES = u"cascades" +CHOICES_AND_COLUMNS = "choices and columns" +CASCADING_CHOICES = "cascades" -OSM = u"osm" -OSM_TYPE = u"binary" +OSM = "osm" +OSM_TYPE = "binary" -NAMESPACES = u"namespaces" +NAMESPACES = "namespaces" SUPPORTED_SHEET_NAMES = [ SURVEY, @@ -80,8 +85,8 @@ SETTINGS, OSM, ] -SUPPORTED_FILE_EXTENSIONS = ['.xls', '.xlsx', '.xlsm'] +SUPPORTED_FILE_EXTENSIONS = [".xls", ".xlsx", ".xlsm"] -LOCATION_PRIORITY = u"location-priority" -LOCATION_MIN_INTERVAL = u"location-min-interval" -LOCATION_MAX_AGE = u"location-max-age" +LOCATION_PRIORITY = "location-priority" +LOCATION_MIN_INTERVAL = "location-min-interval" +LOCATION_MAX_AGE = "location-max-age" diff --git a/pyxform/errors.py b/pyxform/errors.py index 0d17c1271..606b50707 100644 --- a/pyxform/errors.py +++ b/pyxform/errors.py @@ -1,6 +1,16 @@ +# -*- coding: utf-8 -*- +""" +Common base classes for pyxform exceptions. +""" + + class PyXFormError(Exception): + """Common base class for pyxform exceptions.""" + pass class ValidationError(PyXFormError): + """Common base class for pyxform validation exceptions.""" + pass diff --git a/pyxform/external_instance.py b/pyxform/external_instance.py index c2c54caff..1f0838037 100644 --- a/pyxform/external_instance.py +++ b/pyxform/external_instance.py @@ -1,9 +1,11 @@ +# -*- coding: utf-8 -*- +""" +ExternalInstance class module +""" from pyxform.survey_element import SurveyElement -from pyxform.utils import node class ExternalInstance(SurveyElement): - def xml_control(self): """ No-op since there is no associated form control to place under . diff --git a/pyxform/file_utils.py b/pyxform/file_utils.py index b52cefc78..e864559de 100644 --- a/pyxform/file_utils.py +++ b/pyxform/file_utils.py @@ -1,7 +1,11 @@ -import os +# -*- coding: utf-8 -*- +""" +The pyxform file utility functions. +""" import glob -from pyxform import utils +import os +from pyxform import utils from pyxform.xls2json import SurveyReader @@ -32,7 +36,8 @@ def collect_compatible_files_in_directory(directory): create a giant dict out of all the spreadsheets and json forms in the given directory """ - available_files = glob.glob(os.path.join(directory, "*.xls")) + \ - glob.glob(os.path.join(directory, "*.json")) + available_files = glob.glob(os.path.join(directory, "*.xls")) + glob.glob( + os.path.join(directory, "*.json") + ) return dict([load_file_to_dict(f) for f in available_files]) diff --git a/pyxform/instance.py b/pyxform/instance.py index 73bfe8e43..962a842f8 100644 --- a/pyxform/instance.py +++ b/pyxform/instance.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +SurveyInstance class module. +""" from pyxform.xform_instance_parser import parse_xform_instance @@ -45,20 +49,15 @@ def answer(self, name=None, value=None): def to_json_dict(self): children = [] for k, v in self._answers.items(): - children.append({'node_name': k, 'value': v}) - return { - 'node_name': self._name, - 'id': self._id, - 'children': children - } + children.append({"node_name": k, "value": v}) + return {"node_name": self._name, "id": self._id, "children": children} def to_xml(self): """ A horrible way to do this, but it works (until we need the attributes pumped out in order, etc) """ - open_str = """<%s id="%s">""" % ( - self._name, self._id) + open_str = """<%s id="%s">""" % (self._name, self._id) close_str = """""" % self._name vals = "" for k, v in self._answers.items(): @@ -77,6 +76,7 @@ def answers(self): def import_from_xml(self, xml_string_or_filename): import os.path + if os.path.isfile(xml_string_or_filename): xml_str = open(xml_string_or_filename).read() else: @@ -90,4 +90,7 @@ def __unicode__(self): placed_count = len(self._answers.keys()) answer_count = orphan_count + placed_count return "" % ( - answer_count, placed_count, orphan_count) + answer_count, + placed_count, + orphan_count, + ) diff --git a/pyxform/instance_info.py b/pyxform/instance_info.py index b2d339c06..c9cc990d8 100644 --- a/pyxform/instance_info.py +++ b/pyxform/instance_info.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +InstanceInfo class module. +""" class InstanceInfo(object): diff --git a/pyxform/question.py b/pyxform/question.py index 4f8efd67b..8e083f512 100644 --- a/pyxform/question.py +++ b/pyxform/question.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +XForm Survey element classes for different question types. +""" import os.path from pyxform.errors import PyXFormError @@ -18,12 +22,12 @@ def validate(self): def xml_instance(self, **kwargs): survey = self.get_root() attributes = {} - attributes.update(self.get(u"instance", {})) + attributes.update(self.get("instance", {})) for key, value in attributes.items(): attributes[key] = survey.insert_xpaths(value, self) - if self.get(u"default"): - return node(self.name, unicode(self.get(u"default")), **attributes) + if self.get("default"): + return node(self.name, unicode(self.get("default")), **attributes) return node(self.name, **attributes) def xml_control(self): @@ -69,26 +73,26 @@ def xml_control(self): for key, value in control_dict.items(): control_dict[key] = survey.insert_xpaths(value, self) control_dict["ref"] = self.get_xpath() - return node(u"trigger", *self.xml_label_and_hint(), **control_dict) + return node("trigger", *self.xml_label_and_hint(), **control_dict) class UploadQuestion(Question): def _get_media_type(self): - return self.control[u"mediatype"] + return self.control["mediatype"] def xml_control(self): control_dict = self.control control_dict["ref"] = self.get_xpath() control_dict["mediatype"] = self._get_media_type() - return node(u"upload", *self.xml_label_and_hint(), **control_dict) + return node("upload", *self.xml_label_and_hint(), **control_dict) class Option(SurveyElement): def xml_value(self): - return node(u"value", self.name) + return node("value", self.name) def xml(self): - item = node(u"item") + item = node("item") self.xml_label() item.appendChild(self.xml_label()) item.appendChild(self.xml_value()) @@ -106,8 +110,8 @@ def __init__(self, **kwargs): # I'm going to try to stick to just choices. # Aliases in the json format will make it more difficult # to use going forward. - choices = list(kwargs_copy.pop(u"choices", [])) + list( - kwargs_copy.pop(u"children", []) + choices = list(kwargs_copy.pop("choices", [])) + list( + kwargs_copy.pop("children", []) ) Question.__init__(self, **kwargs_copy) for choice in choices: @@ -126,7 +130,7 @@ def validate(self): choice.validate() def xml_control(self): - assert self.bind[u"type"] in [u"select", u"select1", u"odk:rank"] + assert self.bind["type"] in ["select", "select1", "odk:rank"] survey = self.get_root() control_dict = self.control.copy() # Resolve field references in attributes @@ -164,9 +168,7 @@ def xml_control(self): nodeset = ( nodeset + ", " - + survey.insert_xpaths( - params["seed"], self - ).strip() + + survey.insert_xpaths(params["seed"], self).strip() ) else: nodeset = nodeset + ", " + params["seed"] @@ -177,9 +179,7 @@ def xml_control(self): node("value", ref="name"), node("label", ref=itemset_label_ref), ] - result.appendChild( - node("itemset", *itemset_children, nodeset=nodeset) - ) + result.appendChild(node("itemset", *itemset_children, nodeset=nodeset)) else: for n in [o.xml() for o in self.children]: result.appendChild(n) @@ -189,15 +189,13 @@ def xml_control(self): class SelectOneQuestion(MultipleChoiceQuestion): def __init__(self, **kwargs): super(SelectOneQuestion, self).__init__(**kwargs) - self._dict[self.TYPE] = u"select one" + self._dict[self.TYPE] = "select one" class Tag(SurveyElement): def __init__(self, **kwargs): kwargs_copy = kwargs.copy() - choices = kwargs_copy.pop(u"choices", []) + kwargs_copy.pop( - u"children", [] - ) + choices = kwargs_copy.pop("choices", []) + kwargs_copy.pop("children", []) super(Tag, self).__init__(**kwargs_copy) @@ -209,7 +207,7 @@ def __init__(self, **kwargs): self.add_child(option) def xml(self): - result = node(u"tag", key=self.name) + result = node("tag", key=self.name) self.xml_label() result.appendChild(self.xml_label()) for choice in self.children: @@ -224,7 +222,7 @@ def validate(self): class OsmUploadQuestion(UploadQuestion): def __init__(self, **kwargs): kwargs_copy = kwargs.copy() - tags = kwargs_copy.pop(u"tags", []) + kwargs_copy.pop(u"children", []) + tags = kwargs_copy.pop("tags", []) + kwargs_copy.pop("children", []) super(OsmUploadQuestion, self).__init__(**kwargs_copy) @@ -242,7 +240,7 @@ def xml_control(self): control_dict = self.control control_dict["ref"] = self.get_xpath() control_dict["mediatype"] = self._get_media_type() - result = node(u"upload", *self.xml_label_and_hint(), **control_dict) + result = node("upload", *self.xml_label_and_hint(), **control_dict) for osm_tag in self.children: result.appendChild(osm_tag.xml()) diff --git a/pyxform/question_type_dictionary.py b/pyxform/question_type_dictionary.py index ec85370a7..64731caa6 100644 --- a/pyxform/question_type_dictionary.py +++ b/pyxform/question_type_dictionary.py @@ -1,857 +1,374 @@ +# -*- coding: utf-8 -*- +""" +XForm survey question type mapping dictionary module. +""" from pyxform.xls2json import QuestionTypesReader, print_pyobj_to_json + def generate_new_dict(): """ This is just here incase there is ever any need to generate the question type dictionary from all.xls again. It shouldn't be called as part of any application. """ - path_to_question_types = "/home/nathan/aptana-workspace/pyxform"\ - "/pyxform/question_types/all.xls" + path_to_question_types = ( + "/home/nathan/aptana-workspace/pyxform" "/pyxform/question_types/all.xls" + ) json_dict = QuestionTypesReader(path_to_question_types).to_json_dict() - print_pyobj_to_json(json_dict, 'new_quesiton_type_dict.json') - + print_pyobj_to_json(json_dict, "new_quesiton_type_dict.json") -QUESTION_TYPE_DICT = \ - { - "q picture": { - "control": { - "tag": "upload", - "mediatype": "image/*" - }, - "bind": { - "type": "binary" - } - }, - "photo": { - "control": { - "tag": "upload", - "mediatype": "image/*" - }, - "bind": { - "type": "binary" - } - }, - "add date time prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "add audio prompt": { - "control": { - "tag": "upload", - "mediatype": "audio/*" - }, - "bind": { - "type": "binary" - } - }, - "q date time": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "phonenumber": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "phonenumber" - } - }, - "get start time": { - "bind": { - "jr:preload": "timestamp", - "type": "dateTime", - "jr:preloadParams": "start" - } - }, - "add select multiple prompt using": { - "control": { - "tag": "select" - }, - "bind": { - "type": "select" - } - }, - "add note prompt": { - "control": { - "tag": "input" - }, - "bind": { - "readonly": "true()", - "type": "string" - } - }, - "calculate": { - "bind": { - "type": "string" - } - }, - "acknowledge": { - "control": { - "tag": "trigger" - }, - "bind": { - "type": "string" - } - }, - "location": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geopoint" - } - }, - "text": { - "control": { - "tag": "input" - }, - "bind": { - "type": "string" - } - }, - "select all that apply from": { - "control": { - "tag": "select" - }, - "bind": { - "type": "select" - } - }, - "simserial": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "simserial" - } - }, - "string": { - "control": { - "tag": "input" - }, - "bind": { - "type": "string" - } - }, - "q string": { - "control": { - "tag": "input" - }, - "bind": { - "type": "string" - } - }, - "imei": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "deviceid" - } - }, - "integer": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int" - } - }, - "datetime": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "q note": { - "control": { - "tag": "input" - }, - "bind": { - "readonly": "true()", - "type": "string" - } - }, - "subscriber id": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "subscriberid" - } - }, - "decimal": { - "control": { - "tag": "input" - }, - "bind": { - "type": "decimal" - } - }, - "dateTime": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "q audio": { - "control": { - "tag": "upload", - "mediatype": "audio/*" - }, - "bind": { - "type": "binary" - } - }, - "q geopoint": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geopoint" - } - }, - "q geoshape": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geoshape" - } - }, - "q geotrace": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geotrace" - } - }, - "q image": { - "control": { - "tag": "upload", - "mediatype": "image/*" - }, - "bind": { - "type": "binary" - } - }, - "get today": { - "bind": { - "jr:preload": "date", - "type": "date", - "jr:preloadParams": "today" - } - }, - "video": { - "control": { - "tag": "upload", - "mediatype": "video/*" - }, - "bind": { - "type": "binary" - } - }, - "q acknowledge": { - "control": { - "tag": "trigger" - }, - "bind": { - "type": "string" - } - }, - "add video prompt": { - "control": { - "tag": "upload", - "mediatype": "video/*" - }, - "bind": { - "type": "binary" - } - }, - "number of days in last month": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int", - "constraint": "0 <= . and . <= 31" - }, - "hint": "Enter a number 0-31." - }, - "get sim id": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "simserial" - } - }, - "q location": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geopoint" - } - }, - "select one": { - "control": { - "tag": "select1" - }, - "bind": { - "type": "select1" - } - }, - "select one external": { - "control": { - "tag": "input" - }, - "bind": { - "type": "string" - } - }, - "add image prompt": { - "control": { - "tag": "upload", - "mediatype": "image/*" - }, - "bind": { - "type": "binary" - } - }, - "select all that apply": { - "control": { - "tag": "select" - }, - "bind": { - "type": "select" - } - }, - "get end time": { - "bind": { - "jr:preload": "timestamp", - "type": "dateTime", - "jr:preloadParams": "end" - } - }, - "barcode": { - "control": { - "tag": "input" - }, - "bind": { - "type": "barcode" - } - }, - "q video": { - "control": { - "tag": "upload", - "mediatype": "video/*" - }, - "bind": { - "type": "binary" - } - }, - "geopoint": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geopoint" - } - }, - "geoshape": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geoshape" - } - }, - "geotrace": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geotrace" - } - }, - "select multiple from": { - "control": { - "tag": "select" - }, - "bind": { - "type": "select" - } - }, - "end time": { - "bind": { - "jr:preload": "timestamp", - "type": "dateTime", - "jr:preloadParams": "end" - } - }, - "device id": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "deviceid" - } - }, - "subscriberid": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "subscriberid" - } - }, - "q barcode": { - "control": { - "tag": "input" - }, - "bind": { - "type": "barcode" - } - }, - "q select": { - "control": { - "tag": "select" - }, - "bind": { - "type": "select" - } - }, - "select one using": { - "control": { - "tag": "select1" - }, - "bind": { - "type": "select1" - } - }, - "rank": { - "control": { - "tag": "odk:rank" - }, - "bind": { - "type": "odk:rank" - } - }, - "image": { - "control": { - "tag": "upload", - "mediatype": "image/*" - }, - "bind": { - "type": "binary" - } - }, - "q int": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int" - } - }, - "add text prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "string" - } - }, - "add date prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "date" - } - }, - "q calculate": { - "bind": { - "type": "string" - } - }, - "start": { - "bind": { - "jr:preload": "timestamp", - "type": "dateTime", - "jr:preloadParams": "start" - } - }, - "trigger": { - "control": { - "tag": "trigger" - } - }, - "add acknowledge prompt": { - "control": { - "tag": "trigger" - }, - "bind": { - "type": "string" - } - }, - "percentage": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int", - "constraint": "0 <= . and . <= 100" - } - }, - "get phone number": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "phonenumber" - } - }, - "today": { - "bind": { - "jr:preload": "date", - "type": "date", - "jr:preloadParams": "today" - } - }, - "gps": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geopoint" - } - }, - "q date": { - "control": { - "tag": "input" - }, - "bind": { - "type": "date" - } - }, - "sim id": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "simserial" - } - }, - "add decimal prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "decimal" - } - }, - "number of days in last six months": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int", - "constraint": "0 <= . and . <= 183" - }, - "hint": "Enter a number 0-183." - }, - "deviceid": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "deviceid" - } - }, - "int": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int" - } - }, - "add barcode prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "barcode" - } - }, - "select multiple using": { - "control": { - "tag": "select" - }, - "bind": { - "type": "select" - } - }, - "q decimal": { - "control": { - "tag": "input" - }, - "bind": { - "type": "decimal" - } - }, - "end": { - "bind": { - "jr:preload": "timestamp", - "type": "dateTime", - "jr:preloadParams": "end" - } - }, - "add calculate prompt": { - "bind": { - "type": "string" - } - }, - "add dateTime prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "note": { - "control": { - "tag": "input" - }, - "bind": { - "readonly": "true()", - "type": "string" - } - }, - "add location prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "geopoint" - } - }, - "get subscriber id": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "subscriberid" - } - }, - "phone number": { - "control": { - "tag": "input" - }, - "bind": { - "type": "string", - "constraint": "regex(., '^\\d*$')" - }, - "hint": "Enter numbers only." - }, - "get device id": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "deviceid" - } - }, - "add integer prompt": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int" - } - }, - "q dateTime": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "date": { - "control": { - "tag": "input" - }, - "bind": { - "type": "date" - } - }, - "q select1": { - "control": { - "tag": "select1" - }, - "bind": { - "type": "select1" - } - }, - "start time": { - "bind": { - "jr:preload": "timestamp", - "type": "dateTime", - "jr:preloadParams": "start" - } - }, - "number of days in last year": { - "control": { - "tag": "input" - }, - "bind": { - "type": "int", - "constraint": "0 <= . and . <= 365" - }, - "hint": "Enter a number 0-365." - }, - "date time": { - "control": { - "tag": "input" - }, - "bind": { - "type": "dateTime" - } - }, - "time": { - "control": { - "tag": "input" - }, - "bind": { - "type": "time" - } - }, - "audio": { - "control": { - "tag": "upload", - "mediatype": "audio/*" - }, - "bind": { - "type": "binary" - } - }, - "add select one prompt using": { - "control": { - "tag": "select1" - }, - "bind": { - "type": "select1" - } - }, - "hidden": { - "bind": { - "type": "string" - } - }, - "uri:subscriberid": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "uri:subscriberid" - } - }, - "uri:phonenumber": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "uri:phonenumber" - } - }, - "uri:simserial": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "uri:simserial" - } - }, - "uri:deviceid": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "uri:deviceid" - } - }, - "username": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "username" - } - }, - "uri:username": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "uri:username" - } - }, - "email": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "email" - } - }, - "uri:email": { - "bind": { - "jr:preload": "property", - "type": "string", - "jr:preloadParams": "uri:email" - } - }, - "osm": { - "control": { - "tag": "upload", - "mediatype": "osm/*" - }, - "bind": { - "type": "binary" - } - }, - "file": { - "control": { - "tag": "upload", - "mediatype": "application/*" - }, - "bind": { - "type": "binary" - } - }, - "add file prompt": { - "control": { - "tag": "upload", - "mediatype": "application/*" - }, - "bind": { - "type": "binary" - } - }, - "range": { - "control": { - "tag": "range" - }, - "bind": { - "type": "int" - } - }, - "audit": { - "bind": { - "type": "binary" - } - }, - "xml-external": { - # Only effect is to add an external instance. +QUESTION_TYPE_DICT = { + "q picture": { + "control": {"tag": "upload", "mediatype": "image/*"}, + "bind": {"type": "binary"}, + }, + "photo": { + "control": {"tag": "upload", "mediatype": "image/*"}, + "bind": {"type": "binary"}, + }, + "add date time prompt": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "add audio prompt": { + "control": {"tag": "upload", "mediatype": "audio/*"}, + "bind": {"type": "binary"}, + }, + "q date time": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "phonenumber": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "phonenumber", + } + }, + "get start time": { + "bind": { + "jr:preload": "timestamp", + "type": "dateTime", + "jr:preloadParams": "start", + } + }, + "add select multiple prompt using": { + "control": {"tag": "select"}, + "bind": {"type": "select"}, + }, + "add note prompt": { + "control": {"tag": "input"}, + "bind": {"readonly": "true()", "type": "string"}, + }, + "calculate": {"bind": {"type": "string"}}, + "acknowledge": {"control": {"tag": "trigger"}, "bind": {"type": "string"}}, + "location": {"control": {"tag": "input"}, "bind": {"type": "geopoint"}}, + "text": {"control": {"tag": "input"}, "bind": {"type": "string"}}, + "select all that apply from": { + "control": {"tag": "select"}, + "bind": {"type": "select"}, + }, + "simserial": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "simserial", + } + }, + "string": {"control": {"tag": "input"}, "bind": {"type": "string"}}, + "q string": {"control": {"tag": "input"}, "bind": {"type": "string"}}, + "imei": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "deviceid", + } + }, + "integer": {"control": {"tag": "input"}, "bind": {"type": "int"}}, + "datetime": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "q note": { + "control": {"tag": "input"}, + "bind": {"readonly": "true()", "type": "string"}, + }, + "subscriber id": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "subscriberid", + } + }, + "decimal": {"control": {"tag": "input"}, "bind": {"type": "decimal"}}, + "dateTime": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "q audio": { + "control": {"tag": "upload", "mediatype": "audio/*"}, + "bind": {"type": "binary"}, + }, + "q geopoint": {"control": {"tag": "input"}, "bind": {"type": "geopoint"}}, + "q geoshape": {"control": {"tag": "input"}, "bind": {"type": "geoshape"}}, + "q geotrace": {"control": {"tag": "input"}, "bind": {"type": "geotrace"}}, + "q image": { + "control": {"tag": "upload", "mediatype": "image/*"}, + "bind": {"type": "binary"}, + }, + "get today": { + "bind": {"jr:preload": "date", "type": "date", "jr:preloadParams": "today"} + }, + "video": { + "control": {"tag": "upload", "mediatype": "video/*"}, + "bind": {"type": "binary"}, + }, + "q acknowledge": {"control": {"tag": "trigger"}, "bind": {"type": "string"}}, + "add video prompt": { + "control": {"tag": "upload", "mediatype": "video/*"}, + "bind": {"type": "binary"}, + }, + "number of days in last month": { + "control": {"tag": "input"}, + "bind": {"type": "int", "constraint": "0 <= . and . <= 31"}, + "hint": "Enter a number 0-31.", + }, + "get sim id": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "simserial", + } + }, + "q location": {"control": {"tag": "input"}, "bind": {"type": "geopoint"}}, + "select one": {"control": {"tag": "select1"}, "bind": {"type": "select1"}}, + "select one external": {"control": {"tag": "input"}, "bind": {"type": "string"}}, + "add image prompt": { + "control": {"tag": "upload", "mediatype": "image/*"}, + "bind": {"type": "binary"}, + }, + "select all that apply": {"control": {"tag": "select"}, "bind": {"type": "select"}}, + "get end time": { + "bind": { + "jr:preload": "timestamp", + "type": "dateTime", + "jr:preloadParams": "end", + } + }, + "barcode": {"control": {"tag": "input"}, "bind": {"type": "barcode"}}, + "q video": { + "control": {"tag": "upload", "mediatype": "video/*"}, + "bind": {"type": "binary"}, + }, + "geopoint": {"control": {"tag": "input"}, "bind": {"type": "geopoint"}}, + "geoshape": {"control": {"tag": "input"}, "bind": {"type": "geoshape"}}, + "geotrace": {"control": {"tag": "input"}, "bind": {"type": "geotrace"}}, + "select multiple from": {"control": {"tag": "select"}, "bind": {"type": "select"}}, + "end time": { + "bind": { + "jr:preload": "timestamp", + "type": "dateTime", + "jr:preloadParams": "end", + } + }, + "device id": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "deviceid", + } + }, + "subscriberid": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "subscriberid", + } + }, + "q barcode": {"control": {"tag": "input"}, "bind": {"type": "barcode"}}, + "q select": {"control": {"tag": "select"}, "bind": {"type": "select"}}, + "select one using": {"control": {"tag": "select1"}, "bind": {"type": "select1"}}, + "rank": {"control": {"tag": "odk:rank"}, "bind": {"type": "odk:rank"}}, + "image": { + "control": {"tag": "upload", "mediatype": "image/*"}, + "bind": {"type": "binary"}, + }, + "q int": {"control": {"tag": "input"}, "bind": {"type": "int"}}, + "add text prompt": {"control": {"tag": "input"}, "bind": {"type": "string"}}, + "add date prompt": {"control": {"tag": "input"}, "bind": {"type": "date"}}, + "q calculate": {"bind": {"type": "string"}}, + "start": { + "bind": { + "jr:preload": "timestamp", + "type": "dateTime", + "jr:preloadParams": "start", + } + }, + "trigger": {"control": {"tag": "trigger"}}, + "add acknowledge prompt": { + "control": {"tag": "trigger"}, + "bind": {"type": "string"}, + }, + "percentage": { + "control": {"tag": "input"}, + "bind": {"type": "int", "constraint": "0 <= . and . <= 100"}, + }, + "get phone number": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "phonenumber", + } + }, + "today": { + "bind": {"jr:preload": "date", "type": "date", "jr:preloadParams": "today"} + }, + "gps": {"control": {"tag": "input"}, "bind": {"type": "geopoint"}}, + "q date": {"control": {"tag": "input"}, "bind": {"type": "date"}}, + "sim id": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "simserial", + } + }, + "add decimal prompt": {"control": {"tag": "input"}, "bind": {"type": "decimal"}}, + "number of days in last six months": { + "control": {"tag": "input"}, + "bind": {"type": "int", "constraint": "0 <= . and . <= 183"}, + "hint": "Enter a number 0-183.", + }, + "deviceid": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "deviceid", + } + }, + "int": {"control": {"tag": "input"}, "bind": {"type": "int"}}, + "add barcode prompt": {"control": {"tag": "input"}, "bind": {"type": "barcode"}}, + "select multiple using": {"control": {"tag": "select"}, "bind": {"type": "select"}}, + "q decimal": {"control": {"tag": "input"}, "bind": {"type": "decimal"}}, + "end": { + "bind": { + "jr:preload": "timestamp", + "type": "dateTime", + "jr:preloadParams": "end", + } + }, + "add calculate prompt": {"bind": {"type": "string"}}, + "add dateTime prompt": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "note": { + "control": {"tag": "input"}, + "bind": {"readonly": "true()", "type": "string"}, + }, + "add location prompt": {"control": {"tag": "input"}, "bind": {"type": "geopoint"}}, + "get subscriber id": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "subscriberid", + } + }, + "phone number": { + "control": {"tag": "input"}, + "bind": {"type": "string", "constraint": "regex(., '^\\d*$')"}, + "hint": "Enter numbers only.", + }, + "get device id": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "deviceid", + } + }, + "add integer prompt": {"control": {"tag": "input"}, "bind": {"type": "int"}}, + "q dateTime": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "date": {"control": {"tag": "input"}, "bind": {"type": "date"}}, + "q select1": {"control": {"tag": "select1"}, "bind": {"type": "select1"}}, + "start time": { + "bind": { + "jr:preload": "timestamp", + "type": "dateTime", + "jr:preloadParams": "start", + } + }, + "number of days in last year": { + "control": {"tag": "input"}, + "bind": {"type": "int", "constraint": "0 <= . and . <= 365"}, + "hint": "Enter a number 0-365.", + }, + "date time": {"control": {"tag": "input"}, "bind": {"type": "dateTime"}}, + "time": {"control": {"tag": "input"}, "bind": {"type": "time"}}, + "audio": { + "control": {"tag": "upload", "mediatype": "audio/*"}, + "bind": {"type": "binary"}, + }, + "add select one prompt using": { + "control": {"tag": "select1"}, + "bind": {"type": "select1"}, + }, + "hidden": {"bind": {"type": "string"}}, + "uri:subscriberid": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "uri:subscriberid", + } + }, + "uri:phonenumber": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "uri:phonenumber", + } + }, + "uri:simserial": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "uri:simserial", + } + }, + "uri:deviceid": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "uri:deviceid", + } + }, + "username": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "username", + } + }, + "uri:username": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "uri:username", + } + }, + "email": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "email", + } + }, + "uri:email": { + "bind": { + "jr:preload": "property", + "type": "string", + "jr:preloadParams": "uri:email", } - } + }, + "osm": { + "control": {"tag": "upload", "mediatype": "osm/*"}, + "bind": {"type": "binary"}, + }, + "file": { + "control": {"tag": "upload", "mediatype": "application/*"}, + "bind": {"type": "binary"}, + }, + "add file prompt": { + "control": {"tag": "upload", "mediatype": "application/*"}, + "bind": {"type": "binary"}, + }, + "range": {"control": {"tag": "range"}, "bind": {"type": "int"}}, + "audit": {"bind": {"type": "binary"}}, + "xml-external": { + # Only effect is to add an external instance. + }, +} diff --git a/pyxform/section.py b/pyxform/section.py index 8e58b1bee..a8a93dc20 100644 --- a/pyxform/section.py +++ b/pyxform/section.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Section survey element module. +""" from pyxform.errors import PyXFormError from pyxform.external_instance import ExternalInstance from pyxform.question import SurveyElement @@ -19,8 +23,8 @@ def _validate_uniqueness_of_element_names(self): if any(element.name.lower() == s.lower() for s in element_slugs): raise PyXFormError( "There are more than one survey elements named '%s' " - "(case-insensitive) in the section named '%s'." % - (element.name.lower(), self.name) + "(case-insensitive) in the section named '%s'." + % (element.name.lower(), self.name) ) element_slugs.append(element.name) @@ -30,14 +34,14 @@ def xml_instance(self, **kwargs): """ attributes = {} attributes.update(kwargs) - attributes.update(self.get(u'instance', {})) + attributes.update(self.get("instance", {})) survey = self.get_root() # Resolve field references in attributes for key, value in attributes.items(): attributes[key] = survey.insert_xpaths(value, self) result = node(self.name, **attributes) for child in self.children: - if child.get(u"flat"): + if child.get("flat"): for grandchild in child.xml_instance_array(): result.appendChild(grandchild) elif isinstance(child, ExternalInstance): @@ -51,7 +55,7 @@ def xml_instance_array(self): This method is used for generating flat instances. """ for child in self.children: - if child.get(u"flat"): + if child.get("flat"): for grandchild in child.xml_instance_array(): yield grandchild else: @@ -88,19 +92,15 @@ def xml_control(self): # Resolve field references in attributes for key, value in control_dict.items(): control_dict[key] = survey.insert_xpaths(value, self) - repeat_node = node(u"repeat", nodeset=self.get_xpath(), **control_dict) + repeat_node = node("repeat", nodeset=self.get_xpath(), **control_dict) for n in Section.xml_control(self): repeat_node.appendChild(n) label = self.xml_label() if label: - return node( - u"group", self.xml_label(), repeat_node, - ref=self.get_xpath() - ) - return node(u"group", repeat_node, ref=self.get_xpath(), - **self.control) + return node("group", self.xml_label(), repeat_node, ref=self.get_xpath()) + return node("group", repeat_node, ref=self.get_xpath(), **self.control) # I'm anal about matching function signatures when overriding a function, # but there's no reason for kwargs to be an argument @@ -140,27 +140,26 @@ def xml_control(self): for key, value in attributes.items(): attributes[key] = survey.insert_xpaths(value, self) - if not self.get('flat'): - attributes['ref'] = self.get_xpath() + if not self.get("flat"): + attributes["ref"] = self.get_xpath() - if 'label' in self and len(self['label']) > 0: + if "label" in self and len(self["label"]) > 0: children.append(self.xml_label()) for n in Section.xml_control(self): children.append(n) - if u"appearance" in control_dict: - attributes['appearance'] = control_dict['appearance'] + if "appearance" in control_dict: + attributes["appearance"] = control_dict["appearance"] - if u"intent" in control_dict: + if "intent" in control_dict: survey = self.get_root() - attributes['intent'] = survey.insert_xpaths(control_dict['intent'], - self) + attributes["intent"] = survey.insert_xpaths(control_dict["intent"], self) - return node(u"group", *children, **attributes) + return node("group", *children, **attributes) def to_json_dict(self): # This is quite hacky, might want to think about a smart way # to approach this problem. result = super(GroupedSection, self).to_json_dict() - result[u"type"] = u"group" + result["type"] = "group" return result diff --git a/pyxform/survey.py b/pyxform/survey.py index bd9d9d944..2ccc68543 100644 --- a/pyxform/survey.py +++ b/pyxform/survey.py @@ -2,6 +2,8 @@ """ Survey module with XForm Survey objects and utility functions. """ +from __future__ import print_function + import codecs import os import re @@ -18,8 +20,14 @@ from pyxform.question import Question from pyxform.section import Section from pyxform.survey_element import SurveyElement -from pyxform.utils import (NSMAP, PatchedText, basestring, - get_languages_with_bad_tags, node, unicode) +from pyxform.utils import ( + NSMAP, + PatchedText, + basestring, + get_languages_with_bad_tags, + node, + unicode, +) from pyxform.validators import enketo_validate, odk_validate try: @@ -44,7 +52,7 @@ def is_parent_a_repeat(survey, xpath): Returns the XPATH of the first repeat of the given xpath in the survey, otherwise False will be returned. """ - parent_xpath = '/'.join(xpath.split('/')[:-1]) + parent_xpath = "/".join(xpath.split("/")[:-1]) if not parent_xpath: return False @@ -70,22 +78,22 @@ def share_same_repeat_parent(survey, xpath, context_xpath): context_parent = is_parent_a_repeat(survey, context_xpath) xpath_parent = is_parent_a_repeat(survey, xpath) if context_parent and xpath_parent and xpath_parent in context_parent: - context_parts = context_xpath[len(xpath_parent) + 1:].split('/') + context_parts = context_xpath[len(xpath_parent) + 1 :].split("/") parts = [] steps = 1 - remainder_xpath = xpath[len(xpath_parent):] - xpath_parts = xpath[len(xpath_parent) + 1:].split('/') + remainder_xpath = xpath[len(xpath_parent) :] + xpath_parts = xpath[len(xpath_parent) + 1 :].split("/") for index, item in enumerate(context_parts[:-1]): try: - if xpath[len(context_parent) + 1:].split('/')[index] != item: + if xpath[len(context_parent) + 1 :].split("/")[index] != item: steps = len(context_parts[index:]) parts = xpath_parts[index:] break else: - parts = remainder_xpath.split('/')[index + 2:] + parts = remainder_xpath.split("/")[index + 2 :] except IndexError: - steps = len(context_parts[index - 1:]) - parts = xpath_parts[index - 1:] + steps = len(context_parts[index - 1 :]) + parts = xpath_parts[index - 1 :] break return (steps, "/" + "/".join(parts) if parts else remainder_xpath) @@ -101,39 +109,37 @@ class Survey(Section): FIELDS = Section.FIELDS.copy() FIELDS.update( { - u"_xpath": dict, - u"_created": datetime.now, # This can't be dumped to json - u"title": unicode, - u"id_string": unicode, - u"sms_keyword": unicode, - u"sms_separator": unicode, - u"sms_allow_media": bool, - u"sms_date_format": unicode, - u"sms_datetime_format": unicode, - u"sms_response": unicode, - + "_xpath": dict, + "_created": datetime.now, # This can't be dumped to json + "title": unicode, + "id_string": unicode, + "sms_keyword": unicode, + "sms_separator": unicode, + "sms_allow_media": bool, + "sms_date_format": unicode, + "sms_datetime_format": unicode, + "sms_response": unicode, constants.COMPACT_PREFIX: unicode, constants.COMPACT_DELIMITER: unicode, - - u"file_name": unicode, - u"default_language": unicode, - u"_translations": dict, - u"submission_url": unicode, - u"auto_send": unicode, - u"auto_delete": unicode, - u"public_key": unicode, - u"instance_xmlns": unicode, - u"version": unicode, - u"choices": dict, - u"style": unicode, - u"attribute": dict, - u"namespaces": unicode, + "file_name": unicode, + "default_language": unicode, + "_translations": dict, + "submission_url": unicode, + "auto_send": unicode, + "auto_delete": unicode, + "public_key": unicode, + "instance_xmlns": unicode, + "version": unicode, + "choices": dict, + "style": unicode, + "attribute": dict, + "namespaces": unicode, } ) # yapf: disable def validate(self): - if self.id_string in [None, 'None']: - raise PyXFormError('Survey cannot have an empty id_string') + if self.id_string in [None, "None"]: + raise PyXFormError("Survey cannot have an empty id_string") super(Survey, self).validate() self._validate_uniqueness_of_section_names() @@ -143,8 +149,8 @@ def _validate_uniqueness_of_section_names(self): if isinstance(element, Section): if element.name in section_names: raise PyXFormError( - "There are two sections with the name %s." % - element.name) + "There are two sections with the name %s." % element.name + ) section_names.append(element.name) def get_nsmap(self): @@ -153,15 +159,21 @@ def get_nsmap(self): if namespaces and isinstance(namespaces, basestring): nslist = [ - ns.split('=') for ns in namespaces.split() - if len(ns.split('=')) == 2 and ns.split('=')[0] != '' + ns.split("=") + for ns in namespaces.split() + if len(ns.split("=")) == 2 and ns.split("=")[0] != "" ] - xmlns = u'xmlns:' + xmlns = "xmlns:" nsmap = NSMAP.copy() - nsmap.update(dict([ - (xmlns + k, v.replace('"', '').replace("'", "")) - for k, v in nslist if xmlns + k not in nsmap - ])) + nsmap.update( + dict( + [ + (xmlns + k, v.replace('"', "").replace("'", "")) + for k, v in nslist + if xmlns + k not in nsmap + ] + ) + ) return nsmap return NSMAP @@ -173,17 +185,16 @@ def xml(self): self.validate() self._setup_xpath_dictionary() body_kwargs = {} - if hasattr(self, constants.STYLE) and getattr( - self, constants.STYLE): - body_kwargs['class'] = getattr( - self, constants.STYLE) + if hasattr(self, constants.STYLE) and getattr(self, constants.STYLE): + body_kwargs["class"] = getattr(self, constants.STYLE) nsmap = self.get_nsmap() return node( - u"h:html", - node(u"h:head", node(u"h:title", self.title), self.xml_model()), - node(u"h:body", *self.xml_control(), **body_kwargs), - **nsmap) + "h:html", + node("h:head", node("h:title", self.title), self.xml_model()), + node("h:body", *self.xml_control(), **body_kwargs), + **nsmap + ) @staticmethod def _generate_static_instances(list_name, choice_list): @@ -201,52 +212,45 @@ def _generate_static_instances(list_name, choice_list): choice_element_list = [] # Add a unique id to the choice element in case there is itext # it references - itext_id = '-'.join(['static_instance', list_name, str(idx)]) + itext_id = "-".join(["static_instance", list_name, str(idx)]) choice_element_list.append(node("itextId", itext_id)) for name, value in choice.items(): - if isinstance(value, basestring) and name != 'label': + if isinstance(value, basestring) and name != "label": choice_element_list.append(node(name, unicode(value))) instance_element_list.append(node("item", *choice_element_list)) return InstanceInfo( - type=u"choice", - context=u"survey", + type="choice", + context="survey", name=list_name, src=None, instance=node( - "instance", - node("root", *instance_element_list), - id=list_name - ) + "instance", node("root", *instance_element_list), id=list_name + ), ) @staticmethod def _get_dummy_instance(): """Instance content required by ODK Validate for select inputs.""" - return node("root", - node("item", node("name", "_"), node("label", "_"))) + return node("root", node("item", node("name", "_"), node("label", "_"))) @staticmethod def _generate_external_instances(element): if isinstance(element, ExternalInstance): - name = element[u"name"] + name = element["name"] src = "jr://file/{}.xml".format(name) return InstanceInfo( - type=u"external", + type="external", context="[type: {t}, name: {n}]".format( - t=element[u"parent"][u"type"], - n=element[u"parent"][u"name"] + t=element["parent"]["type"], n=element["parent"]["name"] ), name=name, src=src, instance=node( - "instance", - Survey._get_dummy_instance(), - id=name, - src=src - ) + "instance", Survey._get_dummy_instance(), id=name, src=src + ), ) return None @@ -274,25 +278,27 @@ def _validate_external_instances(instances): "Instance names must be unique within a form. " "The name '{i}' was found {c} time(s), " "under these contexts: {contexts}".format( - i=element, c=len(copies), contexts=contexts)) + i=element, c=len(copies), contexts=contexts + ) + ) if errors: raise ValidationError("\n".join(errors)) @staticmethod def _generate_pulldata_instances(element): - if 'calculate' in element['bind']: - calculate = element['bind']['calculate'] - if calculate.startswith('pulldata('): - pieces = calculate.split('"') \ - if '"' in calculate else calculate.split("'") + if "calculate" in element["bind"]: + calculate = element["bind"]["calculate"] + if calculate.startswith("pulldata("): + pieces = ( + calculate.split('"') if '"' in calculate else calculate.split("'") + ) if len(pieces) > 1 and pieces[1]: file_id = pieces[1] uri = "jr://file-csv/{}.csv".format(file_id) return InstanceInfo( - type=u"pulldata", + type="pulldata", context="[type: {t}, name: {n}]".format( - t=element[u"parent"][u"type"], - n=element[u"parent"][u"name"] + t=element["parent"]["type"], n=element["parent"]["name"] ), name=file_id, src=uri, @@ -300,33 +306,31 @@ def _generate_pulldata_instances(element): "instance", Survey._get_dummy_instance(), id=file_id, - src=uri - ) + src=uri, + ), ) return None @staticmethod def _generate_from_file_instances(element): - itemset = element.get('itemset') - if itemset and (itemset.endswith('.csv') or itemset.endswith('.xml')): + itemset = element.get("itemset") + if itemset and (itemset.endswith(".csv") or itemset.endswith(".xml")): file_id, ext = os.path.splitext(itemset) - uri = 'jr://%s/%s' % ( - 'file' if ext == '.xml' else "file-%s" % ext[1:], itemset) + uri = "jr://%s/%s" % ( + "file" if ext == ".xml" else "file-%s" % ext[1:], + itemset, + ) return InstanceInfo( - type=u"file", + type="file", context="[type: {t}, name: {n}]".format( - t=element[u"parent"][u"type"], - n=element[u"parent"][u"name"] + t=element["parent"]["type"], n=element["parent"]["name"] ), name=file_id, src=uri, instance=node( - "instance", - Survey._get_dummy_instance(), - id=file_id, - src=uri - ) + "instance", Survey._get_dummy_instance(), id=file_id, src=uri + ), ) return None @@ -375,8 +379,8 @@ def _generate_instances(self): # Append last so the choice instance is excluded on a name clash. for name, value in self.choices.items(): instances += [ - self._generate_static_instances(list_name=name, - choice_list=value)] + self._generate_static_instances(list_name=name, choice_list=value) + ] # Check that external instances have unique names. if instances: @@ -387,15 +391,20 @@ def _generate_instances(self): for i in instances: if i.name in seen.keys() and seen[i.name].src != i.src: # Instance id exists with different src URI -> error. - msg = "The same instance id will be generated for different " \ - "external instance source URIs. Please check the form." \ - " Instance name: '{i}', Existing type: '{e}', " \ - "Existing URI: '{iu}', Duplicate type: '{d}', " \ - "Duplicate URI: '{du}', Duplicate context: '{c}'."\ - .format( - i=i.name, iu=seen[i.name].src, e=seen[i.name].type, - d=i.type, du=i.src, c=i.context - ) + msg = ( + "The same instance id will be generated for different " + "external instance source URIs. Please check the form." + " Instance name: '{i}', Existing type: '{e}', " + "Existing URI: '{iu}', Duplicate type: '{d}', " + "Duplicate URI: '{du}', Duplicate context: '{c}'.".format( + i=i.name, + iu=seen[i.name].src, + e=seen[i.name].type, + d=i.type, + du=i.src, + c=i.context, + ) + ) raise PyXFormError(msg) elif i.name in seen.keys() and seen[i.name].src == i.src: # Instance id exists with same src URI -> ok, don't duplicate. @@ -420,8 +429,7 @@ def xml_model(self): model_children += list(self._generate_instances()) model_children += self.xml_bindings() - if (self.submission_url or self.public_key or self.auto_send or - self.auto_delete): + if self.submission_url or self.public_key or self.auto_send or self.auto_delete: submission_attrs = dict() if self.submission_url: submission_attrs["action"] = self.submission_url @@ -431,8 +439,9 @@ def xml_model(self): submission_attrs["orx:auto-send"] = self.auto_send if self.auto_delete: submission_attrs["orx:auto-delete"] = self.auto_delete - submission_node = node("submission", method="form-data-post", - **submission_attrs) + submission_node = node( + "submission", method="form-data-post", **submission_attrs + ) model_children.insert(0, submission_node) return node("model", *model_children) @@ -444,20 +453,20 @@ def xml_instance(self, **kwargs): for key, value in self.attribute.items(): result.setAttribute(unicode(key), value) - result.setAttribute(u"id", self.id_string) + result.setAttribute("id", self.id_string) # add instance xmlns attribute to the instance node if self.instance_xmlns: - result.setAttribute(u"xmlns", self.instance_xmlns) + result.setAttribute("xmlns", self.instance_xmlns) if self.version: - result.setAttribute(u"version", self.version) + result.setAttribute("version", self.version) if self.prefix: - result.setAttribute(u"odk:prefix", self.prefix) + result.setAttribute("odk:prefix", self.prefix) if self.delimiter: - result.setAttribute(u"odk:delimiter", self.delimiter) + result.setAttribute("odk:delimiter", self.delimiter) return result @@ -474,54 +483,60 @@ def _setup_translations(self): set up the self._translations dict which will be referenced in the setup media and itext functions """ + def _setup_choice_translations(name, choice_value, itext_id): for media_type_or_language, value in choice_value.items(): # noqa if isinstance(value, dict): for language, val in value.items(): self._add_to_nested_dict( self._translations, - [language, itext_id, media_type_or_language], val) + [language, itext_id, media_type_or_language], + val, + ) else: - if name == 'media': + if name == "media": self._add_to_nested_dict( self._translations, - [self.default_language, itext_id, - media_type_or_language], - value) + [self.default_language, itext_id, media_type_or_language], + value, + ) else: self._add_to_nested_dict( self._translations, - [media_type_or_language, itext_id, 'long'], value) + [media_type_or_language, itext_id, "long"], + value, + ) self._translations = defaultdict(dict) # pylint: disable=W0201 for element in self.iter_descendants(): for d in element.get_translations(self.default_language): - if 'guidance_hint' in d['path']: - hint_path = d['path'].replace('guidance_hint', 'hint') - self._translations[d['lang']][hint_path] = \ - self._translations[d['lang']].get(hint_path, {}) - self._translations[d['lang']][hint_path].update( - {"guidance": d['text']}) + if "guidance_hint" in d["path"]: + hint_path = d["path"].replace("guidance_hint", "hint") + self._translations[d["lang"]][hint_path] = self._translations[ + d["lang"] + ].get(hint_path, {}) + self._translations[d["lang"]][hint_path].update( + {"guidance": d["text"]} + ) else: - self._translations[d['lang']][d['path']] = \ - self._translations[d['lang']].get(d['path'], {}) - self._translations[d['lang']][d['path']].update( - {"long": d['text']}) + self._translations[d["lang"]][d["path"]] = self._translations[ + d["lang"] + ].get(d["path"], {}) + self._translations[d["lang"]][d["path"]].update({"long": d["text"]}) # This code sets up translations for choices in filtered selects. for list_name, choice_list in self.choices.items(): for idx, choice in zip(range(len(choice_list)), choice_list): for name, choice_value in choice.items(): - itext_id = '-'.join(['static_instance', list_name, - str(idx)]) + itext_id = "-".join(["static_instance", list_name, str(idx)]) if isinstance(choice_value, dict): - _setup_choice_translations(name, choice_value, - itext_id) - elif name == 'label': + _setup_choice_translations(name, choice_value, itext_id) + elif name == "label": self._add_to_nested_dict( self._translations, - [self.default_language, itext_id, 'long'], - choice_value) + [self.default_language, itext_id, "long"], + choice_value, + ) def _add_empty_translations(self): """ @@ -541,7 +556,7 @@ def _add_empty_translations(self): self._translations[lang][path] = {} for content_type in content_types: if content_type not in self._translations[lang][path]: - self._translations[lang][path][content_type] = u"-" + self._translations[lang][path][content_type] = "-" def _setup_media(self): """ @@ -556,7 +571,7 @@ def _setup_media(self): for survey_element in self.iter_descendants(): translation_key = survey_element.get_xpath() + ":label" - media_dict = survey_element.get(u"media") + media_dict = survey_element.get("media") # This is probably papering over a real problem, but anyway, # in py3, sometimes if an item is on an xform with multiple @@ -571,8 +586,7 @@ def _setup_media(self): for media_type, possibly_localized_media in media_dict.items(): if media_type not in SurveyElement.SUPPORTED_MEDIA: - raise PyXFormError( - "Media type: " + media_type + " not supported") + raise PyXFormError("Media type: " + media_type + " not supported") if isinstance(possibly_localized_media, dict): # media is localized @@ -580,9 +594,7 @@ def _setup_media(self): else: # media is not localized so create a localized version # using the default language - localized_media = { - self.default_language: possibly_localized_media - } + localized_media = {self.default_language: possibly_localized_media} for language, media in localized_media.items(): @@ -597,8 +609,7 @@ def _setup_media(self): if translation_key not in translations_language: translations_language[translation_key] = {} - translations_trans_key = \ - translations_language[translation_key] + translations_trans_key = translations_language[translation_key] if media_type not in translations_trans_key: translations_trans_key[media_type] = {} @@ -615,8 +626,7 @@ def itext(self): result = [] for lang, translation in self._translations.items(): if lang == self.default_language: - result.append( - node("translation", lang=lang, default=u"true()")) + result.append(node("translation", lang=lang, default="true()")) else: result.append(node("translation", lang=lang)) @@ -631,49 +641,53 @@ def itext(self): # There is a odk/jr bug where hints can't have a value # for the "form" attribute. # This is my workaround. - if label_type == u"hint": - value, output_inserted = \ - self.insert_output_values(media_value) + if label_type == "hint": + value, output_inserted = self.insert_output_values(media_value) if media_type == "guidance": itext_nodes.append( - node("value", value, - form='guidance', - toParseString=output_inserted) + node( + "value", + value, + form="guidance", + toParseString=output_inserted, + ) ) else: itext_nodes.append( - node("value", value, - toParseString=output_inserted) + node("value", value, toParseString=output_inserted) ) continue if media_type == "long": - value, output_inserted = \ - self.insert_output_values(media_value) + value, output_inserted = self.insert_output_values(media_value) # I'm ignoring long types for now because I don't know # how they are supposed to work. itext_nodes.append( node("value", value, toParseString=output_inserted) ) elif media_type == "image": - value, output_inserted = \ - self.insert_output_values(media_value) + value, output_inserted = self.insert_output_values(media_value) itext_nodes.append( - node("value", "jr://images/" + value, - form=media_type, - toParseString=output_inserted) + node( + "value", + "jr://images/" + value, + form=media_type, + toParseString=output_inserted, + ) ) else: - value, output_inserted = \ - self.insert_output_values(media_value) + value, output_inserted = self.insert_output_values(media_value) itext_nodes.append( - node("value", "jr://" + media_type + "/" + value, - form=media_type, - toParseString=output_inserted)) + node( + "value", + "jr://" + media_type + "/" + value, + form=media_type, + toParseString=output_inserted, + ) + ) - result[-1].appendChild( - node("text", *itext_nodes, id=label_name)) + result[-1].appendChild(node("text", *itext_nodes, id=label_name)) return node("itext", *result) @@ -693,12 +707,13 @@ def _to_pretty_xml(self): # space to text # TODO: check out pyxml # http://ronrothman.com/public/leftbraned/xml-dom-minidom-toprettyxml-and-silly-whitespace/ - xml_with_linebreaks = self.xml().toprettyxml(indent=' ') - text_re = re.compile(r'(>)\n\s*(\s[^<>\s].*?)\n\s*(\s)\n(\s\s)*') - pretty_xml = text_re.sub(lambda m: ''.join(m.group(1, 2, 3)), - xml_with_linebreaks) - inline_output = output_re.sub(r'\g<1>', pretty_xml) + xml_with_linebreaks = self.xml().toprettyxml(indent=" ") + text_re = re.compile(r"(>)\n\s*(\s[^<>\s].*?)\n\s*(\s)\n(\s\s)*") + pretty_xml = text_re.sub( + lambda m: "".join(m.group(1, 2, 3)), xml_with_linebreaks + ) + inline_output = output_re.sub(r"\g<1>", pretty_xml) return '\n' + inline_output def __repr__(self): @@ -722,26 +737,28 @@ def _var_repl_function(self, matchobj, context, use_current=False): replace ${varname} with the xpath to varname. """ name = matchobj.group(1) - intro = "There has been a problem trying to replace ${%s} with the "\ + intro = ( + "There has been a problem trying to replace ${%s} with the " "XPath to the survey element named '%s'." % (name, name) + ) if name not in self._xpath: - raise PyXFormError( - intro + " There is no survey element with this name.") + raise PyXFormError(intro + " There is no survey element with this name.") if self._xpath[name] is None: - raise PyXFormError(intro + " There are multiple survey elements" - " with this name.") - if context and not (context['type'] == 'calculate' and - 'indexed-repeat' in context['bind']['calculate']): + raise PyXFormError( + intro + " There are multiple survey elements" " with this name." + ) + if context and not ( + context["type"] == "calculate" + and "indexed-repeat" in context["bind"]["calculate"] + ): xpath, context_xpath = self._xpath[name], context.get_xpath() # share same root i.e repeat_a from /data/repeat_a/... - if xpath.split('/')[2] == context_xpath.split('/')[2]: + if xpath.split("/")[2] == context_xpath.split("/")[2]: # if context xpath and target xpath fall under the same # repeat use relative xpath referencing. - steps, ref_path = share_same_repeat_parent(self, xpath, - context_xpath) + steps, ref_path = share_same_repeat_parent(self, xpath, context_xpath) if steps: - ref_path = ref_path \ - if ref_path.endswith(name) else "/%s" % name + ref_path = ref_path if ref_path.endswith(name) else "/%s" % name prefix = " current()/" if use_current else " " return prefix + "/".join([".."] * steps) + ref_path + " " @@ -752,6 +769,7 @@ def insert_xpaths(self, text, context, use_current=False): """ Replace all instances of ${var} with the xpath to var. """ + def _var_repl_function(matchobj): return self._var_repl_function(matchobj, context, use_current) @@ -764,8 +782,7 @@ def _var_repl_output_function(self, matchobj, context): A regex substitution function that will replace ${varname} with an output element that has the xpath to varname. """ - return ('') + return '' def insert_output_values(self, text, context=None): """ @@ -773,8 +790,10 @@ def insert_output_values(self, text, context=None): Returns that and a boolean indicating if there were any ${variables} present. """ + def _var_repl_output_function(matchobj): return self._var_repl_output_function(matchobj, context) + # There was a bug where escaping is completely turned off in labels # where variable replacement is used. # For exampke, `${name} < 3` causes an error but `< 3` does not. @@ -788,16 +807,15 @@ def _var_repl_output_function(matchobj): # need to make sure we have reason to replace # since at this point < is <, # the net effect < gets translated again to &lt; - if unicode(xml_text).find('{') != -1: - result = re.sub( - bracketed_tag, _var_repl_output_function, - unicode(xml_text)) + if unicode(xml_text).find("{") != -1: + result = re.sub(bracketed_tag, _var_repl_output_function, unicode(xml_text)) return result, not result == xml_text return text, False # pylint: disable=too-many-arguments - def print_xform_to_file(self, path=None, validate=True, pretty_print=True, - warnings=None, enketo=False): + def print_xform_to_file( + self, path=None, validate=True, pretty_print=True, warnings=None, enketo=False + ): """ Print the xForm to a file and optionally validate it as well by throwing exceptions and adding warnings to the warnings array. @@ -828,12 +846,13 @@ def print_xform_to_file(self, path=None, validate=True, pretty_print=True, if bad_languages: warnings.append( "\tThe following language declarations do not contain " - "valid machine-readable codes: " + - ", ".join(bad_languages) + ". " + - "Learn more: http://xlsform.org#multiple-language-support") + "valid machine-readable codes: " + + ", ".join(bad_languages) + + ". " + + "Learn more: http://xlsform.org#multiple-language-support" + ) - def to_xml(self, validate=True, pretty_print=True, warnings=None, - enketo=False): + def to_xml(self, validate=True, pretty_print=True, warnings=None, enketo=False): """ Generates the XForm XML. validate is True by default - pass the XForm XML through ODK Validator. @@ -850,8 +869,12 @@ def to_xml(self, validate=True, pretty_print=True, warnings=None, try: # this will throw an exception if the xml is not valid self.print_xform_to_file( - path=tmp.name, validate=validate, pretty_print=pretty_print, - warnings=warnings, enketo=enketo) + path=tmp.name, + validate=validate, + pretty_print=pretty_print, + warnings=warnings, + enketo=enketo, + ) finally: if os.path.exists(tmp.name): os.remove(tmp.name) diff --git a/pyxform/survey_element.py b/pyxform/survey_element.py index 6a07bfaca..0c5aa6569 100644 --- a/pyxform/survey_element.py +++ b/pyxform/survey_element.py @@ -1,12 +1,14 @@ -import re - +# -*- coding: utf-8 -*- +""" +Survey Element base class for all survey elements. +""" import json +import re from pyxform import constants from pyxform.errors import PyXFormError from pyxform.question_type_dictionary import QUESTION_TYPE_DICT -from pyxform.utils import is_valid_xml_tag, node, unicode, \ - INVALID_XFORM_TAG_REGEXP +from pyxform.utils import INVALID_XFORM_TAG_REGEXP, is_valid_xml_tag, node, unicode from pyxform.xls2json import print_pyobj_to_json try: @@ -33,37 +35,37 @@ class SurveyElement(dict): # the following are important keys for the underlying dict that # describes this survey element FIELDS = { - u"name": unicode, - constants.COMPACT_TAG: unicode, # used for compact (sms) representation - u"sms_field": unicode, - u"sms_option": unicode, - u"label": unicode, - u"hint": unicode, - u"guidance_hint": unicode, - u"default": unicode, - u"type": unicode, - u"appearance": unicode, - u"parameters": unicode, - u"intent": unicode, - u"jr:count": unicode, - u"bind": dict, - u"instance": dict, - u"control": dict, - u"media": dict, + "name": unicode, + constants.COMPACT_TAG: unicode, # used for compact (sms) representation + "sms_field": unicode, + "sms_option": unicode, + "label": unicode, + "hint": unicode, + "guidance_hint": unicode, + "default": unicode, + "type": unicode, + "appearance": unicode, + "parameters": unicode, + "intent": unicode, + "jr:count": unicode, + "bind": dict, + "instance": dict, + "control": dict, + "media": dict, # this node will also have a parent and children, like a tree! - u"parent": lambda: None, - u"children": list, - u"itemset": unicode, - u"choice_filter": unicode, - u"query": unicode, - u"autoplay": unicode, - u"flat": lambda: False, + "parent": lambda: None, + "children": list, + "itemset": unicode, + "choice_filter": unicode, + "query": unicode, + "autoplay": unicode, + "flat": lambda: False, } def _default(self): # TODO: need way to override question type dictionary defaults = QUESTION_TYPE_DICT - return defaults.get(self.get(u"type"), {}) + return defaults.get(self.get("type"), {}) def __getattr__(self, key): """ @@ -97,8 +99,8 @@ def __init__(self, **kwargs): # appearance tag. # This is because such elements are used to label the # options for selects in a field-list and might want blank labels for # themselves. - if self.control.get(u"appearance") == u"label" and not self.label: - self[u"label"] = u" " + if self.control.get("appearance") == "label" and not self.label: + self["label"] = " " def _link_children(self): for child in self.children: @@ -127,24 +129,21 @@ def add_children(self, children): "NO": "false()", "false": "false()", "False": "false()", - "FALSE": "false()" + "FALSE": "false()", } # Supported media types for attaching to questions - SUPPORTED_MEDIA = [ - "image", - "audio", - "video" - ] + SUPPORTED_MEDIA = ["image", "audio", "video"] def validate(self): if not is_valid_xml_tag(self.name): invalid_char = re.search(INVALID_XFORM_TAG_REGEXP, self.name) - msg = "The name '{}' is an invalid XML tag, it contains an " \ - "invalid character '{}'. Names must begin with a letter, " \ - "colon, or underscore, subsequent characters can include " \ - "numbers, dashes, and periods".format( - self.name, invalid_char.group(0)) + msg = ( + "The name '{}' is an invalid XML tag, it contains an " + "invalid character '{}'. Names must begin with a letter, " + "colon, or underscore, subsequent characters can include " + "numbers, dashes, and periods".format(self.name, invalid_char.group(0)) + ) raise PyXFormError(msg) # TODO: Make sure renaming this doesn't cause any problems @@ -165,8 +164,7 @@ def iter_descendants(self): def any_repeat(self, parent_xpath): """Return True if there ia any repeat in `parent_xpath`.""" for item in self.iter_descendants(): - if item.get_xpath() == parent_xpath and \ - item.type == constants.REPEAT: + if item.get_xpath() == parent_xpath and item.type == constants.REPEAT: return True return False @@ -183,7 +181,7 @@ def get_lineage(self): # For some reason the root element has a True flat property... output = [result[0]] for item in result[1:]: - if not item.get(u"flat"): + if not item.get("flat"): output.append(item) return output @@ -194,12 +192,12 @@ def get_xpath(self): """ Return the xpath of this survey element. """ - return u"/".join([u""] + [n.name for n in self.get_lineage()]) + return "/".join([""] + [n.name for n in self.get_lineage()]) def get_abbreviated_xpath(self): lineage = self.get_lineage() if len(lineage) >= 2: - return u"/".join([unicode(n.name) for n in lineage[1:]]) + return "/".join([unicode(n.name) for n in lineage[1:]]) else: return lineage[0].name @@ -210,14 +208,14 @@ def to_json_dict(self): """ self.validate() result = self.copy() - to_delete = [u"parent", u"question_type_dictionary", u"_created"] + to_delete = ["parent", "question_type_dictionary", "_created"] for key in to_delete: if key in result: del result[key] - children = result.pop(u"children") - result[u"children"] = [] + children = result.pop("children") + result["children"] = [] for child in children: - result[u"children"].append(child.to_json_dict()) + result["children"].append(child.to_json_dict()) # remove any keys with empty values for k, v in list(result.items()): if not v: @@ -234,8 +232,11 @@ def json_dump(self, path=""): print_pyobj_to_json(self.to_json_dict(), path) def __eq__(self, y): - return hasattr(y, 'to_json_dict') and callable(y.to_json_dict) and \ - self.to_json_dict() == y.to_json_dict() + return ( + hasattr(y, "to_json_dict") + and callable(y.to_json_dict) + and self.to_json_dict() == y.to_json_dict() + ) def _translation_path(self, display_element): return self.get_xpath() + ":" + display_element @@ -245,65 +246,71 @@ def get_translations(self, default_language): Returns translations used by this element so they can be included in the block. @see survey._setup_translations """ - bind_dict = self.get(u'bind') + bind_dict = self.get("bind") if bind_dict and type(bind_dict) is dict: - constraint_msg = bind_dict.get(u'jr:constraintMsg') + constraint_msg = bind_dict.get("jr:constraintMsg") if type(constraint_msg) is dict: for lang, text in constraint_msg.items(): yield { - 'path': self._translation_path(u'jr:constraintMsg'), - 'lang': lang, - 'text': text + "path": self._translation_path("jr:constraintMsg"), + "lang": lang, + "text": text, } - required_msg = bind_dict.get(u'jr:requiredMsg') + required_msg = bind_dict.get("jr:requiredMsg") if type(required_msg) is dict: for lang, text in required_msg.items(): yield { - 'path': self._translation_path(u'jr:requiredMsg'), - 'lang': lang, - 'text': text + "path": self._translation_path("jr:requiredMsg"), + "lang": lang, + "text": text, } - no_app_error_string = bind_dict.get(u'jr:noAppErrorString') + no_app_error_string = bind_dict.get("jr:noAppErrorString") if type(no_app_error_string) is dict: for lang, text in no_app_error_string.items(): yield { - 'path': self._translation_path(u'jr:noAppErrorString'), - 'lang': lang, - 'text': text + "path": self._translation_path("jr:noAppErrorString"), + "lang": lang, + "text": text, } - for display_element in [u'label', u'hint', u'guidance_hint']: + for display_element in ["label", "hint", "guidance_hint"]: label_or_hint = self[display_element] - if display_element is u'label' \ - and self.needs_itext_ref() \ - and type(label_or_hint) is not dict \ - and label_or_hint: + if ( + display_element == "label" + and self.needs_itext_ref() + and type(label_or_hint) is not dict + and label_or_hint + ): label_or_hint = {default_language: label_or_hint} # always use itext for guidance hints because that's # how they're defined - https://opendatakit.github.io/xforms-spec/#languages - if display_element is u'guidance_hint' \ - and not(isinstance(label_or_hint, dict)) \ - and len(label_or_hint) > 0: + if ( + display_element == "guidance_hint" + and not (isinstance(label_or_hint, dict)) + and len(label_or_hint) > 0 + ): label_or_hint = {default_language: label_or_hint} # always use itext for hint if there's a guidance hint - if display_element is u'hint' \ - and not(isinstance(label_or_hint, dict)) \ - and len(label_or_hint) > 0 \ - and "guidance_hint" in self.keys() \ - and len(self["guidance_hint"]) > 0: + if ( + display_element == "hint" + and not (isinstance(label_or_hint, dict)) + and len(label_or_hint) > 0 + and "guidance_hint" in self.keys() + and len(self["guidance_hint"]) > 0 + ): label_or_hint = {default_language: label_or_hint} if type(label_or_hint) is dict: for lang, text in label_or_hint.items(): yield { - 'display_element': display_element, # Not used - 'path': self._translation_path(display_element), - 'element': self, # Not used - 'lang': lang, - 'text': text, + "display_element": display_element, # Not used + "path": self._translation_path(display_element), + "element": self, # Not used + "lang": lang, + "text": text, } def get_media_keys(self): @@ -311,35 +318,34 @@ def get_media_keys(self): @deprected I'm leaving this in just in case it has outside references. """ - return { - u"media": u"%s:media" % self.get_xpath() - } + return {"media": "%s:media" % self.get_xpath()} def needs_itext_ref(self): return type(self.label) is dict or ( - type(self.media) is dict and len(self.media) > 0) + type(self.media) is dict and len(self.media) > 0 + ) # XML generating functions, these probably need to be moved around. def xml_label(self): if self.needs_itext_ref(): # If there is a dictionary label, or non-empty media dict, # then we need to make a label with an itext ref - ref = "jr:itext('%s')" % self._translation_path(u"label") - return node(u"label", ref=ref) + ref = "jr:itext('%s')" % self._translation_path("label") + return node("label", ref=ref) else: survey = self.get_root() - label, output_inserted = survey.insert_output_values(self.label, - self) - return node(u"label", label, toParseString=output_inserted) + label, output_inserted = survey.insert_output_values(self.label, self) + return node("label", label, toParseString=output_inserted) def xml_hint(self): if isinstance(self.hint, dict) or self.guidance_hint: path = self._translation_path("hint") - return node(u"hint", ref="jr:itext('%s')" % path) + return node("hint", ref="jr:itext('%s')" % path) else: hint, output_inserted = self.get_root().insert_output_values( - self.hint, self) - return node(u"hint", hint, toParseString=output_inserted) + self.hint, self + ) + return node("hint", hint, toParseString=output_inserted) def xml_label_and_hint(self): """ @@ -353,8 +359,7 @@ def xml_label_and_hint(self): result.append(self.xml_hint()) if len(result) == 0 or self.guidance_hint and len(result) == 1: - msg = "The survey element named '%s' " \ - "has no label or hint." % self.name + msg = "The survey element named '%s' " "has no label or hint." % self.name raise PyXFormError(msg) return result @@ -365,7 +370,7 @@ def xml_binding(self): """ survey = self.get_root() bind_dict = self.bind.copy() - if self.get('flat'): + if self.get("flat"): # Don't generate bind element for flat groups. return None if bind_dict: @@ -374,17 +379,14 @@ def xml_binding(self): # the xls2json side. if hashable(v) and v in self.binding_conversions: v = self.binding_conversions[v] - if k == u'jr:constraintMsg' and type(v) is dict: - v = "jr:itext('%s')" % self._translation_path( - u'jr:constraintMsg') - if k == u'jr:requiredMsg' and type(v) is dict: - v = "jr:itext('%s')" % self._translation_path( - u'jr:requiredMsg') - if k == u'jr:noAppErrorString' and type(v) is dict: - v = "jr:itext('%s')" % self._translation_path( - u'jr:noAppErrorString') + if k == "jr:constraintMsg" and type(v) is dict: + v = "jr:itext('%s')" % self._translation_path("jr:constraintMsg") + if k == "jr:requiredMsg" and type(v) is dict: + v = "jr:itext('%s')" % self._translation_path("jr:requiredMsg") + if k == "jr:noAppErrorString" and type(v) is dict: + v = "jr:itext('%s')" % self._translation_path("jr:noAppErrorString") bind_dict[k] = survey.insert_xpaths(v, context=self) - return node(u"bind", nodeset=self.get_xpath(), **bind_dict) + return node("bind", nodeset=self.get_xpath(), **bind_dict) return None def xml_bindings(self): diff --git a/pyxform/tests/__init__.py b/pyxform/tests/__init__.py index 6cddaf9e7..e69de29bb 100644 --- a/pyxform/tests/__init__.py +++ b/pyxform/tests/__init__.py @@ -1,12 +0,0 @@ -# These tests were being run twice by nose. -# commenting out the imports prevents this. -# from j2x_question_tests import * -# from j2x_test_creation import * -# #from j2x_test_xform_build_preparation import * -# from js2x_test_import_from_json import * -# from j2x_test_instantiation import * -# from json2xform_test import * -# from group_test import * -# from builder_tests import * -# from xls2json_tests import * -# from dump_and_load_tests import * diff --git a/pyxform/tests/attributecolumnstest.py b/pyxform/tests/attributecolumnstest.py index b1fa1f1ee..d7a41a97d 100644 --- a/pyxform/tests/attributecolumnstest.py +++ b/pyxform/tests/attributecolumnstest.py @@ -1,9 +1,12 @@ +# -*- coding: utf-8 -*- """ Some tests for the new (v0.9) spec is properly implemented. """ -import unittest2 as unittest import codecs import os + +import unittest2 as unittest + import pyxform from pyxform.tests.utils import XFormTestCase @@ -17,24 +20,23 @@ class AttributeColumnsTest(XFormTestCase): def runTest(self): filename = "attribute_columns_test.xlsx" self.get_file_path(filename) - expected_output_path = os.path.join(DIR, "test_expected_output", - self.root_filename + ".xml") + expected_output_path = os.path.join( + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") \ - as expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") \ - as actual_file: - self.assertXFormEqual( - expected_file.read(), actual_file.read()) + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: + self.assertXFormEqual(expected_file.read(), actual_file.read()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/pyxform/tests/bug_tests.py b/pyxform/tests/bug_tests.py index 7ac40b732..0cbd6d6f2 100644 --- a/pyxform/tests/bug_tests.py +++ b/pyxform/tests/bug_tests.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Some tests for the new (v0.9) spec is properly implemented. """ @@ -11,8 +12,6 @@ from pyxform.tests.utils import XFormTestCase from pyxform.utils import has_external_choices from pyxform.xls2json import SurveyReader, parse_file_to_workbook_dict -from pyxform.tests.utils import XFormTestCase -from pyxform.errors import PyXFormError from pyxform.xls2json_backends import xls_to_dict DIR = os.path.dirname(__file__) @@ -31,7 +30,8 @@ def runTest(self): warnings = [] with self.assertRaises(Exception): json_survey = pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(output_path, warnings=warnings) @@ -49,7 +49,8 @@ def runTest(self): warnings = [] with self.assertRaises(Exception): json_survey = pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(output_path, warnings=warnings) @@ -61,20 +62,20 @@ def runTest(self): filename = "repeat_date_test.xls" self.get_file_path(filename) expected_output_path = os.path.join( - DIR, "test_expected_output", self.root_filename + ".xml") + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open( - expected_output_path, 'rb', encoding="utf-8") as expected_file: - with codecs.open( - self.output_path, 'rb', encoding="utf-8") as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) @@ -85,20 +86,20 @@ def runTest(self): filename = "xml_escaping.xls" self.get_file_path(filename) expected_output_path = os.path.join( - DIR, "test_expected_output", self.root_filename + ".xml") + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open( - expected_output_path, 'rb', encoding="utf-8") as expected_file: - with codecs.open( - self.output_path, 'rb', encoding="utf-8") as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) @@ -112,19 +113,19 @@ def runTest(self): root_filename, ext = os.path.splitext(filename) output_path = os.path.join(DIR, "test_output", root_filename + ".xml") expected_output_path = os.path.join( - DIR, "test_expected_output", root_filename + ".xml") + DIR, "test_expected_output", root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open( - expected_output_path, 'rb', encoding="utf-8") as expected_file: - with codecs.open( - output_path, 'rb', encoding="utf-8") as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) @@ -140,7 +141,8 @@ def runTest(self): # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(output_path, warnings=warnings) @@ -157,7 +159,8 @@ def runTest(self): # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(output_path, warnings=warnings) @@ -175,7 +178,8 @@ def runTest(self): warnings = [] with self.assertRaises(PyXFormError): json_survey = pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(output_path, warnings=warnings) @@ -184,11 +188,10 @@ class EmptyStringOnRelevantColumnTest(unittest.TestCase): def runTest(self): filename = "ict_survey_fails.xls" path_to_excel_file = os.path.join(DIR, "bug_example_xls", filename) - workbook_dict = pyxform.xls2json.parse_file_to_workbook_dict( - path_to_excel_file) + workbook_dict = pyxform.xls2json.parse_file_to_workbook_dict(path_to_excel_file) with self.assertRaises(KeyError): # bind:relevant should not be part of workbook_dict - workbook_dict['survey'][0][u'bind: relevant'].strip() + workbook_dict["survey"][0]["bind: relevant"].strip() class BadChoicesSheetHeaders(unittest.TestCase): @@ -196,10 +199,8 @@ def runTest(self): filename = "spaces_in_choices_header.xls" path_to_excel_file = os.path.join(DIR, "bug_example_xls", filename) warnings = [] - pyxform.xls2json.parse_file_to_json(path_to_excel_file, - warnings=warnings) - self.assertEquals(len(warnings), 3, - "Found " + str(len(warnings)) + " warnings") + pyxform.xls2json.parse_file_to_json(path_to_excel_file, warnings=warnings) + self.assertEquals(len(warnings), 3, "Found " + str(len(warnings)) + " warnings") class TestChoiceNameAsType(unittest.TestCase): @@ -222,6 +223,7 @@ def test_blank_second_row(self): class TestXLDateAmbigous(unittest.TestCase): """Test non standard sheet with exception is processed successfully.""" + def test_xl_date_ambigous(self): """Test non standard sheet with exception is processed successfully.""" filename = "xl_date_ambiguous.xlsx" @@ -235,20 +237,24 @@ class TestXLDateAmbigousWithException(unittest.TestCase): """Test non standard sheet date values to raise an exception. This exception is raised if the date values exceed the datemode value accepted by that workbook.""" + def test_xl_date_ambigous_with_exception(self): """Test non standard sheet with exception is processed successfully.""" filename = "xl_date_ambiguous_v1.xlsx" path_to_excel_file = os.path.join(DIR, "bug_example_xls", filename) with self.assertRaises(PyXFormError) as e: xls_to_dict(path_to_excel_file) - msg = 'The xls file provided has an invalid date on the'\ - ' survey sheet, under the default column on row number 5' + msg = ( + "The xls file provided has an invalid date on the" + " survey sheet, under the default column on row number 5" + ) self.assertEqual(msg, str(e.exception)) class TestSpreadSheetFilesWithMacrosAreAllowed(unittest.TestCase): """Test that spreadsheets with .xlsm extension are allowed""" + def test_xlsm_files_are_allowed(self): filename = "excel_with_macros.xlsm" path_to_excel_file = os.path.join(DIR, "bug_example_xls", filename) diff --git a/pyxform/tests/builder_tests.py b/pyxform/tests/builder_tests.py index 3b06148fb..0aaa6a31a 100644 --- a/pyxform/tests/builder_tests.py +++ b/pyxform/tests/builder_tests.py @@ -1,12 +1,17 @@ +# -*- coding: utf-8 -*- +""" +Test builder module functionality. +""" +import os import re import xml.etree.ElementTree as ETree from unittest import TestCase + +from pyxform import InputQuestion, Survey from pyxform.builder import SurveyElementBuilder, create_survey_from_xls -from pyxform.xls2json import print_pyobj_to_json -from pyxform import Survey, InputQuestion from pyxform.errors import PyXFormError from pyxform.tests import utils -import os +from pyxform.xls2json import print_pyobj_to_json FIXTURE_FILETYPE = "xls" @@ -14,49 +19,40 @@ class BuilderTests(TestCase): maxDiff = None -# Moving to spec tests -# def test_new_widgets(self): -# survey = utils.build_survey('widgets.xls') -# path = utils.path_to_text_fixture('widgets.xml') -# survey.to_xml -# with open(path) as f: -# expected = ETree.fromstring(survey.to_xml()) -# result = ETree.fromstring(f.read()) -# self.assertTrue(xml_compare(expected, result)) + # Moving to spec tests + # def test_new_widgets(self): + # survey = utils.build_survey('widgets.xls') + # path = utils.path_to_text_fixture('widgets.xml') + # survey.to_xml + # with open(path) as f: + # expected = ETree.fromstring(survey.to_xml()) + # result = ETree.fromstring(f.read()) + # self.assertTrue(xml_compare(expected, result)) def test_unknown_question_type(self): - survey = utils.build_survey('unknown_question_type.xls') - self.assertRaises( - PyXFormError, - survey.to_xml - ) + survey = utils.build_survey("unknown_question_type.xls") + self.assertRaises(PyXFormError, survey.to_xml) def test_uniqueness_of_section_names(self): # Looking at the xls file, I think this test might be broken. - survey = utils.build_survey('group_names_must_be_unique.xls') - self.assertRaises( - Exception, - survey.to_xml - ) + survey = utils.build_survey("group_names_must_be_unique.xls") + self.assertRaises(Exception, survey.to_xml) def setUp(self): self.this_directory = os.path.dirname(__file__) - survey_out = Survey( - name=u"age", - sms_keyword=u"age", - type=u"survey" - ) - question = InputQuestion(name=u"age") - question.type = u"integer" - question.label = u"How old are you?" + survey_out = Survey(name="age", sms_keyword="age", type="survey") + question = InputQuestion(name="age") + question.type = "integer" + question.label = "How old are you?" survey_out.add_child(question) self.survey_out_dict = survey_out.to_json_dict() - print_pyobj_to_json(self.survey_out_dict, - utils.path_to_text_fixture("how_old_are_you.json")) + print_pyobj_to_json( + self.survey_out_dict, utils.path_to_text_fixture("how_old_are_you.json") + ) def test_create_from_file_object(self): - path = utils.path_to_text_fixture('yes_or_no_question.xls') - with open(path, 'rb') as f: + path = utils.path_to_text_fixture("yes_or_no_question.xls") + with open(path, "rb") as f: create_survey_from_xls(f) def tearDown(self): @@ -66,127 +62,104 @@ def tearDown(self): def test_create_table_from_dict(self): d = { - u"type": u"loop", - u"name": u"my_loop", - u"label": {u"English": u"My Loop"}, - u"columns": [ - { - u"name": u"col1", - u"label": {u"English": u"column 1"}, - }, - { - u"name": u"col2", - u"label": {u"English": u"column 2"}, - }, + "type": "loop", + "name": "my_loop", + "label": {"English": "My Loop"}, + "columns": [ + {"name": "col1", "label": {"English": "column 1"}}, + {"name": "col2", "label": {"English": "column 2"}}, ], - u"children": [ + "children": [ { - u"type": u"integer", - u"name": u"count", - u"label": { - u"English": u"How many are there in this group?" - } - }, - ] + "type": "integer", + "name": "count", + "label": {"English": "How many are there in this group?"}, + } + ], } builder = SurveyElementBuilder() g = builder.create_survey_element_from_dict(d) expected_dict = { - u'name': u'my_loop', - u'label': {u'English': u'My Loop'}, - u'type': u'group', - u'children': [ + "name": "my_loop", + "label": {"English": "My Loop"}, + "type": "group", + "children": [ { - u'name': u'col1', - u'label': {u'English': u'column 1'}, - u'type': u'group', - u'children': [ + "name": "col1", + "label": {"English": "column 1"}, + "type": "group", + "children": [ { - u'name': u'count', - u'label': { - u'English': - u'How many are there in this group?' - }, - u'type': u'integer' + "name": "count", + "label": {"English": "How many are there in this group?"}, + "type": "integer", } - ] + ], }, { - u'name': u'col2', - u'label': {u'English': u'column 2'}, - u'type': u'group', - u'children': [ + "name": "col2", + "label": {"English": "column 2"}, + "type": "group", + "children": [ { - u'name': u'count', - u'label': { - u'English': - u'How many are there in this group?' - }, - u'type': u'integer' + "name": "count", + "label": {"English": "How many are there in this group?"}, + "type": "integer", } - ] - } - ] + ], + }, + ], } self.assertEqual(g.to_json_dict(), expected_dict) def test_specify_other(self): - survey = utils.create_survey_from_fixture("specify_other", - filetype=FIXTURE_FILETYPE) + survey = utils.create_survey_from_fixture( + "specify_other", filetype=FIXTURE_FILETYPE + ) expected_dict = { - u'name': u'specify_other', - u'type': u'survey', - u'title': u'specify_other', - u'default_language': u'default', - u'id_string': u'specify_other', - u'sms_keyword': u'specify_other', - u'children': [ + "name": "specify_other", + "type": "survey", + "title": "specify_other", + "default_language": "default", + "id_string": "specify_other", + "sms_keyword": "specify_other", + "children": [ { - u'name': u'sex', - u'label': {u'English': u'What sex are you?'}, - u'type': u'select one', - u'children': [ + "name": "sex", + "label": {"English": "What sex are you?"}, + "type": "select one", + "children": [ # TODO Change to choices (there is stuff in the # json2xform half that will need to change) - { - u'name': u'male', - u'label': {u'English': u'Male'} - }, - { - u'name': u'female', - u'label': {u'English': u'Female'} - }, - { - u'name': u'other', - u'label': u'Other' - } - ] + {"name": "male", "label": {"English": "Male"}}, + {"name": "female", "label": {"English": "Female"}}, + {"name": "other", "label": "Other"}, + ], }, { - u'name': u'sex_other', - u'bind': {u'relevant': u"selected(../sex, 'other')"}, - u'label': u'Specify other.', - u'type': u'text'}, + "name": "sex_other", + "bind": {"relevant": "selected(../sex, 'other')"}, + "label": "Specify other.", + "type": "text", + }, { - u'children': [ + "children": [ { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - u'name': 'instanceID', - u'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - u'control': { - 'bodyless': True - }, - u'name': 'meta', - u'type': u'group' - } - ] + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, + ], } self.maxDiff = None self.assertEqual(survey.to_json_dict(), expected_dict) @@ -197,80 +170,68 @@ def test_select_one_question_with_identical_choice_name(self): as the name of the select one get compiled. """ survey = utils.create_survey_from_fixture( - "choice_name_same_as_select_name", filetype=FIXTURE_FILETYPE) + "choice_name_same_as_select_name", filetype=FIXTURE_FILETYPE + ) expected_dict = { - u'name': u'choice_name_same_as_select_name', - u'title': u'choice_name_same_as_select_name', - u'sms_keyword': u'choice_name_same_as_select_name', - u'default_language': u'default', - u'id_string': u'choice_name_same_as_select_name', - u'type': u'survey', - u'children': [ + "name": "choice_name_same_as_select_name", + "title": "choice_name_same_as_select_name", + "sms_keyword": "choice_name_same_as_select_name", + "default_language": "default", + "id_string": "choice_name_same_as_select_name", + "type": "survey", + "children": [ { - u'children': [ + "children": [{"name": "zone", "label": "Zone"}], + "type": "select one", + "name": "zone", + "label": "Zone", + }, + { + "children": [ { - u'name': u'zone', - u'label': u'Zone' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", + }, + "name": "instanceID", + "type": "calculate", } ], - u'type': u'select one', - u'name': u'zone', - u'label': u'Zone', + "control": {"bodyless": True}, + "name": "meta", + "type": "group", }, - {u'children': [ - { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' - }, - u'name': 'instanceID', - u'type': 'calculate' - } - ], - u'control': { - 'bodyless': True - }, - u'name': 'meta', - u'type': u'group' - } - ] + ], } self.maxDiff = None self.assertEqual(survey.to_json_dict(), expected_dict) def test_loop(self): - survey = utils.create_survey_from_fixture( - "loop", filetype=FIXTURE_FILETYPE) + survey = utils.create_survey_from_fixture("loop", filetype=FIXTURE_FILETYPE) expected_dict = { - u'name': u'loop', - u'id_string': u'loop', - u'sms_keyword': u'loop', - u'title': u'loop', - u'type': u'survey', - u'default_language': u'default', - u'children': [ + "name": "loop", + "id_string": "loop", + "sms_keyword": "loop", + "title": "loop", + "type": "survey", + "default_language": "default", + "children": [ { - u'name': u'available_toilet_types', - u'label': { - u'english': - u'What type of toilets are on the premises?' - }, - u'type': u'select all that apply', - u'children': [ + "name": "available_toilet_types", + "label": {"english": "What type of toilets are on the premises?"}, + "type": "select all that apply", + "children": [ { - u'name': u'pit_latrine_with_slab', - u'label': {u'english': u'Pit latrine with slab'} + "name": "pit_latrine_with_slab", + "label": {"english": "Pit latrine with slab"}, }, { - u'name': u'open_pit_latrine', - u'label': { - u'english': - u'Pit latrine without slab/open pit' - } + "name": "open_pit_latrine", + "label": {"english": "Pit latrine without slab/open pit"}, }, { - u'name': u'bucket_system', - u'label': {u'english': u'Bucket system'} + "name": "bucket_system", + "label": {"english": "Bucket system"}, }, # Removing this because select alls shouldn't need # an explicit none option @@ -278,311 +239,295 @@ def test_loop(self): # u'name': u'none', # u'label': u'None', # }, - { - u'name': u'other', - u'label': u'Other' - }, - ] + {"name": "other", "label": "Other"}, + ], }, - { - u'name': u'available_toilet_types_other', - u'bind': { - u'relevant': - u"selected(../available_toilet_types, 'other')" + "name": "available_toilet_types_other", + "bind": { + "relevant": "selected(../available_toilet_types, 'other')" }, - u'label': u'Specify other.', - u'type': u'text' + "label": "Specify other.", + "type": "text", }, { - u'name': u'loop_toilet_types', - u'type': u'group', - u'children': [ + "name": "loop_toilet_types", + "type": "group", + "children": [ { - u'name': u'pit_latrine_with_slab', - u'label': {u'english': u'Pit latrine with slab'}, - u'type': u'group', - u'children': [ + "name": "pit_latrine_with_slab", + "label": {"english": "Pit latrine with slab"}, + "type": "group", + "children": [ { - u'name': u'number', - u'label': { - u'english': - u'How many Pit latrine with slab are' - u' on the premises?' + "name": "number", + "label": { + "english": "How many Pit latrine with slab are" + " on the premises?" }, - u'type': u'integer' - }]}, + "type": "integer", + } + ], + }, { - u'name': u'open_pit_latrine', - u'label': { - u'english': - u'Pit latrine without slab/open pit' - }, - u'type': u'group', - u'children': [ + "name": "open_pit_latrine", + "label": {"english": "Pit latrine without slab/open pit"}, + "type": "group", + "children": [ { - u'name': u'number', - u'label': { - u'english': - u'How many Pit latrine without ' - u'slab/open pit are on the premises?' + "name": "number", + "label": { + "english": "How many Pit latrine without " + "slab/open pit are on the premises?" }, - u'type': u'integer' + "type": "integer", } - ] + ], }, { - u'name': u'bucket_system', - u'label': {u'english': u'Bucket system'}, - u'type': u'group', - u'children': [ + "name": "bucket_system", + "label": {"english": "Bucket system"}, + "type": "group", + "children": [ { - u'name': u'number', - u'label': { - u'english': - u'How many Bucket system are on the' - u' premises?'}, - u'type': u'integer' + "name": "number", + "label": { + "english": "How many Bucket system are on the" + " premises?" + }, + "type": "integer", } - ] - }]}, + ], + }, + ], + }, { - u'children': [ + "children": [ { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - u'name': 'instanceID', - u'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - u'control': { - 'bodyless': True - }, - u'name': 'meta', - u'type': u'group' - }]} + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, + ], + } self.maxDiff = None self.assertEqual(survey.to_json_dict(), expected_dict) def test_sms_columns(self): - survey = utils.create_survey_from_fixture( - "sms_info", filetype=FIXTURE_FILETYPE) + survey = utils.create_survey_from_fixture("sms_info", filetype=FIXTURE_FILETYPE) expected_dict = { - u'children': [{ - u'children': [ - { - u'label': u'How old are you?', - u'name': u'age', - u'sms_field': u'q1', - u'type': u'integer' - }, - { - u'children': [ - { - u'label': u'no', - u'name': u'0', - u'sms_option': u'n' - }, - { - u'label': u'yes', - u'name': u'1', - u'sms_option': u'y' - }], - u'label': u'Do you have any children?', - u'name': u'has_children', - u'sms_field': u'q2', - u'type': u'select one' - }, - { - u'label': u"What's your birth day?", - u'name': u'bday', - u'sms_field': u'q3', - u'type': u'date' - }, - { - u'label': u'What is your name?', - u'name': u'name', - u'sms_field': u'q4', - u'type': u'text' - } - ], - u'name': u'section1', - u'sms_field': u'a', - u'type': u'group' - }, + "children": [ { - u'children': [ + "children": [ { - u'label': u'May I take your picture?', - u'name': u'picture', - u'type': u'photo' + "label": "How old are you?", + "name": "age", + "sms_field": "q1", + "type": "integer", }, { - u'label': - u'Record your GPS coordinates.', - u'name': u'gps', - u'type': u'geopoint' - } + "children": [ + {"label": "no", "name": "0", "sms_option": "n"}, + {"label": "yes", "name": "1", "sms_option": "y"}, + ], + "label": "Do you have any children?", + "name": "has_children", + "sms_field": "q2", + "type": "select one", + }, + { + "label": "What's your birth day?", + "name": "bday", + "sms_field": "q3", + "type": "date", + }, + { + "label": "What is your name?", + "name": "name", + "sms_field": "q4", + "type": "text", + }, ], - u'name': u'medias', - u'sms_field': u'c', - u'type': u'group' - }, { - u'children': [ + "name": "section1", + "sms_field": "a", + "type": "group", + }, + { + "children": [ { - u'children': [{ - u'label': u'Mozilla Firefox', - u'name': u'firefox', - u'sms_option': u'ff' - }, + "label": "May I take your picture?", + "name": "picture", + "type": "photo", + }, + { + "label": "Record your GPS coordinates.", + "name": "gps", + "type": "geopoint", + }, + ], + "name": "medias", + "sms_field": "c", + "type": "group", + }, + { + "children": [ + { + "children": [ { - u'label': u'Google Chrome', - u'name': u'chrome', - u'sms_option': u'gc' + "label": "Mozilla Firefox", + "name": "firefox", + "sms_option": "ff", }, { - u'label': - u'Internet Explorer', - u'name': u'ie', - u'sms_option': u'ie' + "label": "Google Chrome", + "name": "chrome", + "sms_option": "gc", }, { - u'label': u'Safari', - u'name': u'safari', - u'sms_option': u'saf' - } + "label": "Internet Explorer", + "name": "ie", + "sms_option": "ie", + }, + { + "label": "Safari", + "name": "safari", + "sms_option": "saf", + }, ], - u'label': - u'What web browsers do you use?', - u'name': u'web_browsers', - u'sms_field': u'q5', - u'type': u'select all that apply' + "label": "What web browsers do you use?", + "name": "web_browsers", + "sms_field": "q5", + "type": "select all that apply", } ], - u'name': u'browsers', - u'sms_field': u'b', - u'type': u'group' - }, { - u'children': [{ - u'label': u'Phone Number', - u'name': u'phone', - u'type': u'phonenumber' - }, { - u'label': u'Start DT', - u'name': u'start', - u'type': u'start' - }, { - u'label': u'End DT', - u'name': u'end', - u'type': u'end' - }, { - u'label': u'Send Day', - u'name': u'today', - u'type': u'today' - }, { - u'label': u'IMEI', - u'name': u'imei', - u'type': u'deviceid' - }, { - u'label': u'Hey!', - u'name': u'nope', - u'type': u'note' - }], - u'name': u'metadata', - u'sms_field': u'meta', - u'type': u'group' + "name": "browsers", + "sms_field": "b", + "type": "group", }, { - u'children': [{ - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "children": [ + { + "label": "Phone Number", + "name": "phone", + "type": "phonenumber", }, - u'name': 'instanceID', - u'type': 'calculate' - }], - u'control': {'bodyless': True}, - u'name': 'meta', - u'type': u'group' - }], - u'default_language': u'default', - u'id_string': u'sms_info_form', - u'name': u'sms_info', - u'sms_allow_media': u'TRUE', - u'sms_date_format': u'%Y-%m-%d', - u'sms_datetime_format': u'%Y-%m-%d-%H:%M', - u'sms_keyword': u'inf', - u'sms_separator': u'+', - u'title': u'SMS Example', - u'type': u'survey' + {"label": "Start DT", "name": "start", "type": "start"}, + {"label": "End DT", "name": "end", "type": "end"}, + {"label": "Send Day", "name": "today", "type": "today"}, + {"label": "IMEI", "name": "imei", "type": "deviceid"}, + {"label": "Hey!", "name": "nope", "type": "note"}, + ], + "name": "metadata", + "sms_field": "meta", + "type": "group", + }, + { + "children": [ + { + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", + }, + "name": "instanceID", + "type": "calculate", + } + ], + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, + ], + "default_language": "default", + "id_string": "sms_info_form", + "name": "sms_info", + "sms_allow_media": "TRUE", + "sms_date_format": "%Y-%m-%d", + "sms_datetime_format": "%Y-%m-%d-%H:%M", + "sms_keyword": "inf", + "sms_separator": "+", + "title": "SMS Example", + "type": "survey", } self.assertEqual(survey.to_json_dict(), expected_dict) def test_style_column(self): survey = utils.create_survey_from_fixture( - "style_settings", filetype=FIXTURE_FILETYPE) + "style_settings", filetype=FIXTURE_FILETYPE + ) expected_dict = { - u'children': [ + "children": [ { - u'label': {u'english': u'What is your name?'}, - u'name': u'your_name', - u'type': u'text' + "label": {"english": "What is your name?"}, + "name": "your_name", + "type": "text", }, { - u'label': {u'english': u'How many years old are you?'}, - u'name': u'your_age', - u'type': u'integer' + "label": {"english": "How many years old are you?"}, + "name": "your_age", + "type": "integer", }, { - u'children': [ + "children": [ { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - u'name': 'instanceID', - u'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - u'control': {'bodyless': True}, - u'name': 'meta', - u'type': u'group' - } + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, ], - u'default_language': u'default', - u'id_string': u'new_id', - u'name': u'style_settings', - u'sms_keyword': u'new_id', - u'style': u'ltr', - u'title': u'My Survey', - u'type': u'survey', + "default_language": "default", + "id_string": "new_id", + "name": "style_settings", + "sms_keyword": "new_id", + "style": "ltr", + "title": "My Survey", + "type": "survey", } self.assertEqual(survey.to_json_dict(), expected_dict) - STRIP_NS_FROM_TAG_RE = re.compile(r'\{.+\}') + STRIP_NS_FROM_TAG_RE = re.compile(r"\{.+\}") def test_style_not_added_to_body_if_not_present(self): - survey = utils.create_survey_from_fixture( - "settings", filetype=FIXTURE_FILETYPE) + survey = utils.create_survey_from_fixture("settings", filetype=FIXTURE_FILETYPE) xml = survey.to_xml() # find the body tag - root_elm = ETree.fromstring(xml.encode('utf-8')) - body_elms = list(filter( - lambda e: self.STRIP_NS_FROM_TAG_RE.sub('', e.tag) == 'body', - [c for c in root_elm.getchildren()])) + root_elm = ETree.fromstring(xml.encode("utf-8")) + body_elms = list( + filter( + lambda e: self.STRIP_NS_FROM_TAG_RE.sub("", e.tag) == "body", + [c for c in root_elm.getchildren()], + ) + ) self.assertEqual(len(body_elms), 1) - self.assertIsNone(body_elms[0].get('class')) + self.assertIsNone(body_elms[0].get("class")) def test_style_added_to_body_if_present(self): survey = utils.create_survey_from_fixture( - "style_settings", filetype=FIXTURE_FILETYPE) + "style_settings", filetype=FIXTURE_FILETYPE + ) xml = survey.to_xml() # find the body tag - root_elm = ETree.fromstring(xml.encode('utf-8')) - body_elms = list(filter( - lambda e: self.STRIP_NS_FROM_TAG_RE.sub('', e.tag) == 'body', - [c for c in root_elm.getchildren()])) + root_elm = ETree.fromstring(xml.encode("utf-8")) + body_elms = list( + filter( + lambda e: self.STRIP_NS_FROM_TAG_RE.sub("", e.tag) == "body", + [c for c in root_elm.getchildren()], + ) + ) self.assertEqual(len(body_elms), 1) - self.assertEqual(body_elms[0].get('class'), 'ltr') + self.assertEqual(body_elms[0].get("class"), "ltr") diff --git a/pyxform/tests/dump_and_load_tests.py b/pyxform/tests/dump_and_load_tests.py index ec0ceb7f2..c78d72ec2 100644 --- a/pyxform/tests/dump_and_load_tests.py +++ b/pyxform/tests/dump_and_load_tests.py @@ -1,11 +1,15 @@ +# -*- coding: utf-8 -*- +""" +Test multiple XLSForm can be generated successfully. +""" +import os from unittest import TestCase + from pyxform.builder import create_survey_from_path -import os from pyxform.tests import utils class DumpAndLoadTests(TestCase): - def setUp(self): self.excel_files = [ "gps.xls", @@ -20,24 +24,19 @@ def setUp(self): # "include_json.xls", "simple_loop.xls", "yes_or_no_question.xls", - ] + ] self.surveys = {} self.this_directory = os.path.dirname(__file__) for filename in self.excel_files: path = utils.path_to_text_fixture(filename) - try: - self.surveys[filename] = create_survey_from_path(path) - except Exception as e: - print("Error on : " + filename) - raise e + self.surveys[filename] = create_survey_from_path(path) def test_load_from_dump(self): for filename, survey in self.surveys.items(): survey.json_dump() path = survey.name + ".json" survey_from_dump = create_survey_from_path(path) - self.assertEqual(survey.to_json_dict(), - survey_from_dump.to_json_dict()) + self.assertEqual(survey.to_json_dict(), survey_from_dump.to_json_dict()) def tearDown(self): for filename, survey in self.surveys.items(): diff --git a/pyxform/tests/file_utils_test.py b/pyxform/tests/file_utils_test.py index 5d67ad141..ee21521b3 100644 --- a/pyxform/tests/file_utils_test.py +++ b/pyxform/tests/file_utils_test.py @@ -1,7 +1,11 @@ -from __future__ import print_function +# -*- coding: utf-8 -*- +""" +Test xls2json_backends util functions. +""" from unittest import TestCase -from pyxform.xls2json_backends import convert_file_to_csv_string + from pyxform.tests import utils +from pyxform.xls2json_backends import convert_file_to_csv_string class BackendUtilsTests(TestCase): @@ -10,9 +14,4 @@ def test_xls_to_csv(self): converted_xls = convert_file_to_csv_string(specify_other_xls) specify_other_csv = utils.path_to_text_fixture("specify_other.csv") converted_csv = convert_file_to_csv_string(specify_other_csv) - print("csv:") - print(converted_csv) - print("xls:") - print(converted_xls) self.assertEqual(converted_csv, converted_xls) - diff --git a/pyxform/tests/group_test.py b/pyxform/tests/group_test.py index 7c9ea86f3..c57d5f4d6 100644 --- a/pyxform/tests/group_test.py +++ b/pyxform/tests/group_test.py @@ -1,68 +1,65 @@ +# -*- coding: utf-8 -*- """ Testing simple cases for Xls2Json """ from unittest import TestCase -from pyxform.xls2json import SurveyReader + from pyxform.builder import create_survey_element_from_dict from pyxform.tests import utils +from pyxform.xls2json import SurveyReader class GroupTests(TestCase): - def test_json(self): x = SurveyReader(utils.path_to_text_fixture("group.xls")) x_results = x.to_json_dict() expected_dict = { - u'name': u'group', - u'title': u'group', - u'id_string': u'group', - u'sms_keyword': u'group', - u'default_language': u'default', - u'type': u'survey', - u'children': [ + "name": "group", + "title": "group", + "id_string": "group", + "sms_keyword": "group", + "default_language": "default", + "type": "survey", + "children": [ { - u'name': u'family_name', - u'type': u'text', - u'label': {u'English': u"What's your family name?"} - }, + "name": "family_name", + "type": "text", + "label": {"English": "What's your family name?"}, + }, { - u'name': u'father', - u'type': u'group', - u'label': {u'English': u'Father'}, - u'children': [ + "name": "father", + "type": "group", + "label": {"English": "Father"}, + "children": [ { - u'name': u'phone_number', - u'type': u'phone number', - u'label': { - u'English': - u"What's your father's phone number?"} - }, + "name": "phone_number", + "type": "phone number", + "label": {"English": "What's your father's phone number?"}, + }, { - u'name': u'age', - u'type': u'integer', - u'label': {u'English': u'How old is your father?'} - } - ], - }, + "name": "age", + "type": "integer", + "label": {"English": "How old is your father?"}, + }, + ], + }, { - u'children': [ + "children": [ { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - u'name': 'instanceID', - u'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - u'control': { - 'bodyless': True - }, - u'name': 'meta', - u'type': u'group' - } - ], - } + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, + ], + } self.maxDiff = None self.assertEqual(x_results, expected_dict) diff --git a/pyxform/tests/j2x_question_tests.py b/pyxform/tests/j2x_question_tests.py index f880339e0..87987e068 100644 --- a/pyxform/tests/j2x_question_tests.py +++ b/pyxform/tests/j2x_question_tests.py @@ -1,9 +1,10 @@ +# -*- coding: utf-8 -*- """ Testing creation of Surveys using verbose methods """ from unittest import TestCase -from pyxform import * +from pyxform import Survey from pyxform.builder import create_survey_element_from_dict from pyxform.tests.utils import prep_class_config @@ -26,25 +27,27 @@ def setUpClass(cls): prep_class_config(cls=cls) def setUp(self): - self.s = Survey(name=u"test") + self.s = Survey(name="test") def test_question_type_string(self): simple_string_json = { - u"label": { - u"French": u"Nom du travailleur agricole:", - u"English": u"Name of Community Agricultural Worker" + "label": { + "French": "Nom du travailleur agricole:", + "English": "Name of Community Agricultural Worker", }, - u"type": u"text", - u"name": u"enumerator_name" + "type": "text", + "name": "enumerator_name", } q = create_survey_element_from_dict(simple_string_json) expected_string_control_xml = self.config.get( - self.cls_name, "test_question_type_string_control") + self.cls_name, "test_question_type_string_control" + ) expected_string_binding_xml = self.config.get( - self.cls_name, "test_question_type_string_binding") + self.cls_name, "test_question_type_string_binding" + ) self.s.add_child(q) self.assertEqual(ctw(q.xml_control()), expected_string_control_xml) @@ -57,49 +60,50 @@ def test_select_one_question_multilingual(self): Test the lowest common denominator of question types. """ simple_select_one_json = { - u"label": {u"f": u"ftext", u"e": u"etext"}, - u"type": u"select one", - u"name": u"qname", - u"choices": [ - {u"label": {u"f": u"fa", u"e": u"ea"}, u"name": u"a"}, - {u"label": {u"f": u"fb", u"e": u"eb"}, u"name": u"b"} - ] + "label": {"f": "ftext", "e": "etext"}, + "type": "select one", + "name": "qname", + "choices": [ + {"label": {"f": "fa", "e": "ea"}, "name": "a"}, + {"label": {"f": "fb", "e": "eb"}, "name": "b"}, + ], } # I copied the response in, since this is not our method of testing # valid return values. expected_select_one_control_xml = self.config.get( - self.cls_name, "test_select_one_question_multilingual_control") + self.cls_name, "test_select_one_question_multilingual_control" + ) expected_select_one_binding_xml = self.config.get( - self.cls_name, "test_select_one_question_multilingual_binding") + self.cls_name, "test_select_one_question_multilingual_binding" + ) q = create_survey_element_from_dict(simple_select_one_json) self.s.add_child(q) self.assertEqual(ctw(q.xml_control()), expected_select_one_control_xml) if TESTING_BINDINGS: - self.assertEqual(ctw(q.xml_binding()), - expected_select_one_binding_xml) + self.assertEqual(ctw(q.xml_binding()), expected_select_one_binding_xml) def test_simple_integer_question_type_multilingual(self): """ not sure how integer questions should show up. """ simple_integer_question = { - u"label": {u"f": u"fc", u"e": u"ec"}, - u"type": u"integer", - u"name": u"integer_q", - u"attributes": {} + "label": {"f": "fc", "e": "ec"}, + "type": "integer", + "name": "integer_q", + "attributes": {}, } expected_integer_control_xml = self.config.get( - self.cls_name, - "test_simple_integer_question_type_multilingual_control") + self.cls_name, "test_simple_integer_question_type_multilingual_control" + ) expected_integer_binding_xml = self.config.get( - self.cls_name, - "test_simple_integer_question_type_multilingual_binding") + self.cls_name, "test_simple_integer_question_type_multilingual_binding" + ) q = create_survey_element_from_dict(simple_integer_question) @@ -114,17 +118,20 @@ def test_simple_date_question_type_multilingual(self): """ not sure how date questions should show up. """ - simple_date_question = {u"label": {u"f": u"fd", u"e": u"ed"}, - u"type": u"date", u"name": u"date_q", - u"attributes": {}} + simple_date_question = { + "label": {"f": "fd", "e": "ed"}, + "type": "date", + "name": "date_q", + "attributes": {}, + } expected_date_control_xml = self.config.get( - self.cls_name, - "test_simple_date_question_type_multilingual_control") + self.cls_name, "test_simple_date_question_type_multilingual_control" + ) expected_date_binding_xml = self.config.get( - self.cls_name, - "test_simple_date_question_type_multilingual_binding") + self.cls_name, "test_simple_date_question_type_multilingual_binding" + ) q = create_survey_element_from_dict(simple_date_question) self.s.add_child(q) @@ -138,72 +145,74 @@ def test_simple_phone_number_question_type_multilingual(self): not sure how phone number questions should show up. """ simple_phone_number_question = { - u"label": {u"f": u"fe", u"e": u"ee"}, - u"type": u"phone number", - u"name": u"phone_number_q", + "label": {"f": "fe", "e": "ee"}, + "type": "phone number", + "name": "phone_number_q", } expected_phone_number_control_xml = self.config.get( - self.cls_name, - "test_simple_phone_number_question_type_multilingual_control") + self.cls_name, "test_simple_phone_number_question_type_multilingual_control" + ) expected_phone_number_binding_xml = self.config.get( - self.cls_name, - "test_simple_phone_number_question_type_multilingual_binding") + self.cls_name, "test_simple_phone_number_question_type_multilingual_binding" + ) q = create_survey_element_from_dict(simple_phone_number_question) self.s.add_child(q) - self.assertEqual(ctw(q.xml_control()), - expected_phone_number_control_xml) + self.assertEqual(ctw(q.xml_control()), expected_phone_number_control_xml) if TESTING_BINDINGS: - self.assertEqual(ctw(q.xml_binding()), - expected_phone_number_binding_xml) + self.assertEqual(ctw(q.xml_binding()), expected_phone_number_binding_xml) def test_simple_select_all_question_multilingual(self): """ not sure how select all questions should show up... """ simple_select_all_question = { - u"label": {u"f": u"f choisit", u"e": u"e choose"}, - u"type": u"select all that apply", - u"name": u"select_all_q", - u"choices": [ - {u"label": {u"f": u"ff", u"e": u"ef"}, u"name": u"f"}, - {u"label": {u"f": u"fg", u"e": u"eg"}, u"name": u"g"}, - {u"label": {u"f": u"fh", u"e": u"eh"}, u"name": u"h"} - ] + "label": {"f": "f choisit", "e": "e choose"}, + "type": "select all that apply", + "name": "select_all_q", + "choices": [ + {"label": {"f": "ff", "e": "ef"}, "name": "f"}, + {"label": {"f": "fg", "e": "eg"}, "name": "g"}, + {"label": {"f": "fh", "e": "eh"}, "name": "h"}, + ], } expected_select_all_control_xml = self.config.get( - self.cls_name, - "test_simple_select_all_question_multilingual_control") + self.cls_name, "test_simple_select_all_question_multilingual_control" + ) expected_select_all_binding_xml = self.config.get( - self.cls_name, - "test_simple_select_all_question_multilingual_binding") + self.cls_name, "test_simple_select_all_question_multilingual_binding" + ) q = create_survey_element_from_dict(simple_select_all_question) self.s.add_child(q) self.assertEqual(ctw(q.xml_control()), expected_select_all_control_xml) if TESTING_BINDINGS: - self.assertEqual(ctw(q.xml_binding()), - expected_select_all_binding_xml) + self.assertEqual(ctw(q.xml_binding()), expected_select_all_binding_xml) def test_simple_decimal_question_multilingual(self): """ not sure how decimal should show up. """ - simple_decimal_question = {u"label": {u"f": u"f text", u"e": u"e text"}, - u"type": u"decimal", u"name": u"decimal_q", - u"attributes": {}} + simple_decimal_question = { + "label": {"f": "f text", "e": "e text"}, + "type": "decimal", + "name": "decimal_q", + "attributes": {}, + } expected_decimal_control_xml = self.config.get( - self.cls_name, "test_simple_decimal_question_multilingual_control") + self.cls_name, "test_simple_decimal_question_multilingual_control" + ) expected_decimal_binding_xml = self.config.get( - self.cls_name, "test_simple_decimal_question_multilingual_binding") + self.cls_name, "test_simple_decimal_question_multilingual_binding" + ) q = create_survey_element_from_dict(simple_decimal_question) self.s.add_child(q) diff --git a/pyxform/tests/j2x_test_creation.py b/pyxform/tests/j2x_test_creation.py index 2ed3ddea7..ea8d2718a 100644 --- a/pyxform/tests/j2x_test_creation.py +++ b/pyxform/tests/j2x_test_creation.py @@ -1,8 +1,15 @@ +# -*- coding: utf-8 -*- """ Testing creation of Surveys using verbose methods """ from unittest import TestCase -from pyxform import * + +from pyxform import ( + InputQuestion, + MultipleChoiceQuestion, + Survey, + create_survey_from_xls, +) from pyxform.tests import utils @@ -19,17 +26,12 @@ def test_survey_can_be_created_in_a_verbose_manner(self): s.add_child(q) expected_dict = { - u'name': 'simple_survey', - u'children': [ + "name": "simple_survey", + "children": [ { - u'name': 'cow_color', - u'type': 'select one', - u'children': [ - { - u'label': 'Green', - u'name': 'green', - } - ], + "name": "cow_color", + "type": "select one", + "children": [{"label": "Green", "name": "green"}], } ], } @@ -38,24 +40,23 @@ def test_survey_can_be_created_in_a_verbose_manner(self): def test_survey_can_be_created_in_a_slightly_less_verbose_manner(self): option_dict_array = [ - {'name': 'red', 'label': 'Red'}, - {'name': 'blue', 'label': 'Blue'} + {"name": "red", "label": "Red"}, + {"name": "blue", "label": "Blue"}, ] - q = MultipleChoiceQuestion(name="Favorite_Color", - choices=option_dict_array) - q.type = u"select one" + q = MultipleChoiceQuestion(name="Favorite_Color", choices=option_dict_array) + q.type = "select one" s = Survey(name="Roses_are_Red", children=[q]) expected_dict = { - u'name': 'Roses_are_Red', - u'children': [ + "name": "Roses_are_Red", + "children": [ { - u'name': 'Favorite_Color', - u'type': u'select one', - u'children': [ - {u'label': 'Red', u'name': 'red'}, - {u'label': 'Blue', u'name': 'blue'} + "name": "Favorite_Color", + "type": "select one", + "children": [ + {"label": "Red", "name": "red"}, + {"label": "Blue", "name": "blue"}, ], } ], @@ -67,18 +68,18 @@ def test_two_options_cannot_have_the_same_value(self): q = MultipleChoiceQuestion(name="Favorite Color") q.add_choice(name="grey", label="Gray") q.add_choice(name="grey", label="Grey") - self.assertRaises(Exception, q, 'validate') + self.assertRaises(Exception, q, "validate") def test_one_section_cannot_have_two_conflicting_slugs(self): q1 = InputQuestion(name="YourName") q2 = InputQuestion(name="YourName") s = Survey(name="Roses are Red", children=[q1, q2]) - self.assertRaises(Exception, s, 'validate') + self.assertRaises(Exception, s, "validate") def allow_surveys_with_comment_rows(self): """assume that a survey with rows that don't have name, type, or label headings raise warning only""" - path = utils.path_to_text_fixture('allow_comment_rows_test.xls') + path = utils.path_to_text_fixture("allow_comment_rows_test.xls") survey = create_survey_from_xls(path) expected_dict = { "default_language": "default", @@ -86,10 +87,8 @@ def allow_surveys_with_comment_rows(self): "children": [ { "name": "farmer_name", - "label": { - "English": "First and last name of farmer" - }, - "type": "text" + "label": {"English": "First and last name of farmer"}, + "type": "text", } ], "name": "allow_comment_rows_test", @@ -103,8 +102,8 @@ def allow_surveys_with_comment_rows(self): "title": "allow_comment_rows_test", "_xpath": { "allow_comment_rows_test": "/allow_comment_rows_test", - "farmer_name": "/allow_comment_rows_test/farmer_name" + "farmer_name": "/allow_comment_rows_test/farmer_name", }, - "type": "survey" + "type": "survey", } self.assertEquals(survey.to_json_dict(), expected_dict) diff --git a/pyxform/tests/j2x_test_instantiation.py b/pyxform/tests/j2x_test_instantiation.py index 74de9ddd2..e779905b0 100644 --- a/pyxform/tests/j2x_test_instantiation.py +++ b/pyxform/tests/j2x_test_instantiation.py @@ -1,81 +1,93 @@ +# -*- coding: utf-8 -*- """ Testing the instance object for pyxform. """ from unittest import TestCase -from pyxform import * + +from pyxform import Survey, SurveyInstance from pyxform.builder import create_survey_element_from_dict from pyxform.tests.utils import prep_class_config class Json2XformExportingPrepTests(TestCase): - @classmethod def setUpClass(cls): prep_class_config(cls=cls) def test_simple_survey_instantiation(self): - surv = Survey(name=u"Simple") + surv = Survey(name="Simple") q = create_survey_element_from_dict( - {u"type": u"text", - u"name": u"survey_question", - u"label": u"Question"}) + {"type": "text", "name": "survey_question", "label": "Question"} + ) surv.add_child(q) - + i = surv.instantiate() - - self.assertEquals(i.keys(), [u"survey_question"]) - self.assertEquals(set(i.xpaths()), - {u"/Simple", u"/Simple/survey_question"}) - + + self.assertEquals(i.keys(), ["survey_question"]) + self.assertEquals(set(i.xpaths()), {"/Simple", "/Simple/survey_question"}) + def test_simple_survey_answering(self): - surv = Survey(name=u"Water") - q = create_survey_element_from_dict({ - u"type": u"text", - u"name": u"color", - u"label": u"Color"}) - q2 = create_survey_element_from_dict({ - u"type": u"text", - u"name": u"feeling", - u"label": u"Feeling"}) - + surv = Survey(name="Water") + q = create_survey_element_from_dict( + {"type": "text", "name": "color", "label": "Color"} + ) + q2 = create_survey_element_from_dict( + {"type": "text", "name": "feeling", "label": "Feeling"} + ) + surv.add_child(q) surv.add_child(q2) i = SurveyInstance(surv) - - i.answer(name=u"color", value=u"blue") - self.assertEquals(i.answers()[u'color'], u"blue") - - i.answer(name=u"feeling", value=u"liquidy") - self.assertEquals(i.answers()[u'feeling'], u"liquidy") - + + i.answer(name="color", value="blue") + self.assertEquals(i.answers()["color"], "blue") + + i.answer(name="feeling", value="liquidy") + self.assertEquals(i.answers()["feeling"], "liquidy") + def test_answers_can_be_imported_from_xml(self): - surv = Survey(name=u"data") - - surv.add_child(create_survey_element_from_dict({ - u'type': u'text', u'name': u'name', u"label": u"Name"})) - surv.add_child(create_survey_element_from_dict({ - u'type': u'integer', u'name': u'users_per_month', - u"label": u"Users per month"})) - surv.add_child(create_survey_element_from_dict({ - u'type': u'gps', u'name': u'geopoint', u'label': u'gps'})) - surv.add_child(create_survey_element_from_dict({ - u'type': u'imei', u'name': u'device_id'})) - + surv = Survey(name="data") + + surv.add_child( + create_survey_element_from_dict( + {"type": "text", "name": "name", "label": "Name"} + ) + ) + surv.add_child( + create_survey_element_from_dict( + { + "type": "integer", + "name": "users_per_month", + "label": "Users per month", + } + ) + ) + surv.add_child( + create_survey_element_from_dict( + {"type": "gps", "name": "geopoint", "label": "gps"} + ) + ) + surv.add_child( + create_survey_element_from_dict({"type": "imei", "name": "device_id"}) + ) + instance = surv.instantiate() import_xml = self.config.get( - self.cls_name, "test_answers_can_be_imported_from_xml") + self.cls_name, "test_answers_can_be_imported_from_xml" + ) instance.import_from_xml(import_xml) - + def test_simple_registration_xml(self): - reg_xform = Survey(name=u"Registration") - name_question = create_survey_element_from_dict({ - u'type': u'text', u'name': u'name', u"label": u"Name"}) + reg_xform = Survey(name="Registration") + name_question = create_survey_element_from_dict( + {"type": "text", "name": "name", "label": "Name"} + ) reg_xform.add_child(name_question) - + reg_instance = reg_xform.instantiate() - - reg_instance.answer(name=u"name", value=u"bob") - + + reg_instance.answer(name="name", value="bob") + rx = reg_instance.to_xml() expected_xml = self.config.get( self.cls_name, "test_simple_registration_xml" diff --git a/pyxform/tests/j2x_test_xform_build_preparation.py b/pyxform/tests/j2x_test_xform_build_preparation.py index 9228a6a62..cec5f4f79 100644 --- a/pyxform/tests/j2x_test_xform_build_preparation.py +++ b/pyxform/tests/j2x_test_xform_build_preparation.py @@ -1,37 +1,39 @@ +# -*- coding: utf-8 -*- """ Testing preparation of values for XForm exporting """ from unittest import TestCase -from pyxform import * +from pyxform import MultipleChoiceQuestion, Survey class Json2XformExportingPrepTests(TestCase): - def test_dictionary_consolidates_duplicate_entries(self): - + yes_or_no_dict_array = [ {"label": {"French": "Oui", "English": "Yes"}, "name": "yes"}, - {"label": {"French": "Non", "English": "No"}, "name": "no"} - ] + {"label": {"French": "Non", "English": "No"}, "name": "no"}, + ] first_yesno_question = MultipleChoiceQuestion( - name="yn_q1", options=yes_or_no_dict_array, type="select one") + name="yn_q1", options=yes_or_no_dict_array, type="select one" + ) second_yesno_question = MultipleChoiceQuestion( - name="yn_q2", options=yes_or_no_dict_array, type="select one") - + name="yn_q2", options=yes_or_no_dict_array, type="select one" + ) + s = Survey(name="yes_or_no_tests") s.add_child(first_yesno_question) s.add_child(second_yesno_question) - + # begin the processes in survey.to_xml() # 1. validate() s.validate() - + # 2. survey._build_options_list_from_descendants() # options_list = s._build_options_list_from_descendants() # Is this method called somewhere else now? - + # desired_options_list = [first_yesno_question.children] # todo: we need to think about whether we care about diff --git a/pyxform/tests/js2x_test_import_from_json.py b/pyxform/tests/js2x_test_import_from_json.py index ab28d5b04..064ea282c 100644 --- a/pyxform/tests/js2x_test_import_from_json.py +++ b/pyxform/tests/js2x_test_import_from_json.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Testing our ability to import from a JSON text file. """ @@ -15,9 +16,9 @@ def test_simple_questions_can_be_imported_from_json(self): { "label": {"French": "Combien?", "English": "How many?"}, "type": "decimal", - "name": "exchange_rate" + "name": "exchange_rate", } - ] + ], } s = create_survey_element_from_dict(json_text) diff --git a/pyxform/tests/json2xform_test.py b/pyxform/tests/json2xform_test.py index 9325cb8f9..bab2d9040 100644 --- a/pyxform/tests/json2xform_test.py +++ b/pyxform/tests/json2xform_test.py @@ -1,30 +1,32 @@ +# -*- coding: utf-8 -*- """ Testing simple cases for pyxform """ from unittest import TestCase -from pyxform.survey import Survey from pyxform.builder import create_survey_element_from_dict - +from pyxform.survey import Survey # TODO: # * test_two_questions_with_same_id_fails # (get this working in json2xform) + class BasicJson2XFormTests(TestCase): def test_survey_can_have_to_xml_called_twice(self): """ Test: Survey can have "to_xml" called multiple times - + (This was not being allowed before.) - + It would be good to know (with confidence) that a survey object can be exported to_xml twice, and the same thing will be returned both times. """ - survey = Survey(name=u"SampleSurvey") + survey = Survey(name="SampleSurvey") q = create_survey_element_from_dict( - {u'type': u'text', u'name': u'name', u'label': u'label'}) + {"type": "text", "name": "name", "label": "label"} + ) survey.add_child(q) str1 = survey.to_xml() diff --git a/pyxform/tests/loop_tests.py b/pyxform/tests/loop_tests.py index bc5cffb0b..54cf9da24 100644 --- a/pyxform/tests/loop_tests.py +++ b/pyxform/tests/loop_tests.py @@ -1,92 +1,95 @@ +# -*- coding: utf-8 -*- +""" +Test loop syntax. +""" from unittest import TestCase + from pyxform.builder import create_survey_from_xls from pyxform.tests import utils class LoopTests(TestCase): def test_loop(self): - path = utils.path_to_text_fixture('another_loop.xls') + path = utils.path_to_text_fixture("another_loop.xls") survey = create_survey_from_xls(path) self.maxDiff = None expected_dict = { - u'name': u'another_loop', - u'id_string': u'another_loop', - u'sms_keyword': u'another_loop', - u'default_language': u'default', - u'title': u'another_loop', - u'type': u'survey', - u'children': [ + "name": "another_loop", + "id_string": "another_loop", + "sms_keyword": "another_loop", + "default_language": "default", + "title": "another_loop", + "type": "survey", + "children": [ { - u'name': u'loop_vehicle_types', - u'type': u'group', - u'children': [ + "name": "loop_vehicle_types", + "type": "group", + "children": [ { - u'label': {u'English': u'Car', - u'French': u'Voiture'}, - u'name': u'car', - u'type': u'group', - u'children': [ + "label": {"English": "Car", "French": "Voiture"}, + "name": "car", + "type": "group", + "children": [ { - u'label': { - u'English': u'How many do you have?', - u'French': u'Combien avoir?' + "label": { + "English": "How many do you have?", + "French": "Combien avoir?", }, - u'name': u'total', - u'type': u'integer' + "name": "total", + "type": "integer", }, { - u'bind': {u'constraint': u'. <= ../total'}, - u'label': { - u'English': u'How many are working?', - u'French': u'Combien marcher?' + "bind": {"constraint": ". <= ../total"}, + "label": { + "English": "How many are working?", + "French": "Combien marcher?", }, - u'name': u'working', - u'type': u'integer' - } + "name": "working", + "type": "integer", + }, ], }, { - u'label': {u'English': u'Motorcycle', - u'French': u'Moto'}, - u'name': u'motor_cycle', - u'type': u'group', - u'children': [ + "label": {"English": "Motorcycle", "French": "Moto"}, + "name": "motor_cycle", + "type": "group", + "children": [ { - u'label': { - u'English': u'How many do you have?', - u'French': u'Combien avoir?' + "label": { + "English": "How many do you have?", + "French": "Combien avoir?", }, - u'name': u'total', - u'type': u'integer' + "name": "total", + "type": "integer", }, { - u'bind': {u'constraint': u'. <= ../total'}, - u'label': { - u'English': u'How many are working?', - u'French': u'Combien marcher?' + "bind": {"constraint": ". <= ../total"}, + "label": { + "English": "How many are working?", + "French": "Combien marcher?", }, - u'name': u'working', - u'type': u'integer' - } + "name": "working", + "type": "integer", + }, ], - }]}, + }, + ], + }, { - u'children': [ + "children": [ { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - u'name': 'instanceID', - u'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - u'control': { - 'bodyless': True - }, - u'name': 'meta', - u'type': u'group' - } + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, ], } self.assertEquals(survey.to_json_dict(), expected_dict) diff --git a/pyxform/tests/new_cascading_select_test.py b/pyxform/tests/new_cascading_select_test.py index f94b20815..d92115f1c 100644 --- a/pyxform/tests/new_cascading_select_test.py +++ b/pyxform/tests/new_cascading_select_test.py @@ -1,6 +1,12 @@ -import unittest2 as unittest +# -*- coding: utf-8 -*- +""" +Test cascading select syntax. +""" import codecs import os + +import unittest2 as unittest + import pyxform from pyxform.tests.utils import XFormTestCase @@ -8,31 +14,36 @@ class MainTest(XFormTestCase): - + maxDiff = None - + def runTest(self): - for filename in ["new_cascading_select.xls", "old_cascades.xls", - "cascading_select_test.xls"]: + for filename in [ + "new_cascading_select.xls", + "old_cascades.xls", + "cascading_select_test.xls", + ]: self.get_file_path(filename) expected_output_path = os.path.join( - DIR, "test_expected_output", self.root_filename + ".xml") + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: - json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file) + json_survey = pyxform.xls2json.parse_file_to_json(self.path_to_excel_file) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path) # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") as\ - expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") as \ - actual_file: - self.assertXFormEqual(expected_file.read(), - actual_file.read()) - -if __name__ == '__main__': + with codecs.open( + expected_output_path, "rb", encoding="utf-8" + ) as expected_file: + with codecs.open( + self.output_path, "rb", encoding="utf-8" + ) as actual_file: + self.assertXFormEqual(expected_file.read(), actual_file.read()) + + +if __name__ == "__main__": unittest.main() diff --git a/pyxform/tests/or_other_test.py b/pyxform/tests/or_other_test.py index 60ba5b899..ff073a30f 100644 --- a/pyxform/tests/or_other_test.py +++ b/pyxform/tests/or_other_test.py @@ -1,9 +1,12 @@ +# -*- coding: utf-8 -*- """ -Some tests for the new (v0.9) spec is properly implemented. +Some tests for the new (v0.9) spec is properly implemented. """ -import unittest2 as unittest import codecs import os + +import unittest2 as unittest + import pyxform from pyxform.tests.utils import XFormTestCase @@ -16,23 +19,23 @@ class MainTest(XFormTestCase): def runTest(self): filename = "or_other.xlsx" self.get_file_path(filename) - expected_output_path = os.path.join(DIR, "test_expected_output", - self.root_filename + ".xml") + expected_output_path = os.path.join( + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] - json_survey = pyxform.xls2json.parse_file_to_json(self.path_to_excel_file, - warnings=warnings) + json_survey = pyxform.xls2json.parse_file_to_json( + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', - encoding="utf-8") as expected_file: - with codecs.open(self.output_path, 'rb', - encoding="utf-8") as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/pyxform/tests/select_one_external_test.py b/pyxform/tests/select_one_external_test.py index b9edc0266..85a7df656 100644 --- a/pyxform/tests/select_one_external_test.py +++ b/pyxform/tests/select_one_external_test.py @@ -1,45 +1,54 @@ -import unittest2 as unittest +# -*- coding: utf-8 -*- +""" +Test select one external syntax. +""" import codecs import os + +import unittest2 as unittest + import pyxform -from pyxform.utils import sheet_to_csv from pyxform.tests.utils import XFormTestCase +from pyxform.utils import sheet_to_csv DIR = os.path.dirname(__file__) class MainTest(XFormTestCase): - + maxDiff = None - + def runTest(self): for filename in ["select_one_external.xlsx"]: self.get_file_path(filename) expected_output_path = os.path.join( - DIR, "test_expected_output", self.root_filename + ".xml") + DIR, "test_expected_output", self.root_filename + ".xml" + ) - output_csv = os.path.join( - DIR, "test_output", self.root_filename + ".csv") + output_csv = os.path.join(DIR, "test_output", self.root_filename + ".csv") # Do the conversion: - json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file) + json_survey = pyxform.xls2json.parse_file_to_json(self.path_to_excel_file) - self.assertTrue(sheet_to_csv( - self.path_to_excel_file, output_csv, "external_choices")) - self.assertFalse(sheet_to_csv( - self.path_to_excel_file, output_csv, "non-existant sheet")) + self.assertTrue( + sheet_to_csv(self.path_to_excel_file, output_csv, "external_choices") + ) + self.assertFalse( + sheet_to_csv(self.path_to_excel_file, output_csv, "non-existant sheet") + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path) # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") as\ - expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") as \ - actual_file: - self.assertXFormEqual( - expected_file.read(), actual_file.read()) - -if __name__ == '__main__': + with codecs.open( + expected_output_path, "rb", encoding="utf-8" + ) as expected_file: + with codecs.open( + self.output_path, "rb", encoding="utf-8" + ) as actual_file: + self.assertXFormEqual(expected_file.read(), actual_file.read()) + + +if __name__ == "__main__": unittest.main() diff --git a/pyxform/tests/settings_test.py b/pyxform/tests/settings_test.py index d63321415..3fcae2ff3 100644 --- a/pyxform/tests/settings_test.py +++ b/pyxform/tests/settings_test.py @@ -1,7 +1,13 @@ +# -*- coding: utf-8 -*- +""" +Test settings sheet syntax. +""" + from unittest import TestCase + from pyxform.builder import create_survey_from_path -from pyxform.xls2json import SurveyReader from pyxform.tests import utils +from pyxform.xls2json import SurveyReader class SettingsTests(TestCase): @@ -14,44 +20,42 @@ def setUp(self): def test_survey_reader(self): survey_reader = SurveyReader(self.path) expected_dict = { - u'id_string': u'new_id', - u'sms_keyword': u'new_id', - u'default_language': u'default', - u'name': u'settings', - u'title': u'My Survey', - u'type': u'survey', - u'attribute': { - u'my_number': u'1234567890', - u'my_string': u'lor\xe9m ipsum' + u"id_string": u"new_id", + u"sms_keyword": u"new_id", + u"default_language": u"default", + u"name": u"settings", + u"title": u"My Survey", + u"type": u"survey", + u"attribute": { + u"my_number": u"1234567890", + u"my_string": u"lor\xe9m ipsum", }, - u'children': [ + u"children": [ { - u'name': u'your_name', - u'label': {u'english': u'What is your name?'}, - u'type': u'text' + u"name": u"your_name", + u"label": {u"english": u"What is your name?"}, + u"type": u"text", }, { - u'name': u'your_age', - u'label': {u'english': u'How many years old are you?'}, - u'type': u'integer' + u"name": u"your_age", + u"label": {u"english": u"How many years old are you?"}, + u"type": u"integer", }, { - 'children': [ + "children": [ { - 'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - 'name': 'instanceID', - 'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - 'control': { - 'bodyless': True - }, - 'name': 'meta', - 'type': 'group' - } + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, ], } self.assertEqual(survey_reader.to_json_dict(), expected_dict) diff --git a/pyxform/tests/test_output/ODKValidateWarnings.xml b/pyxform/tests/test_output/ODKValidateWarnings.xml deleted file mode 100644 index 741c494f8..000000000 --- a/pyxform/tests/test_output/ODKValidateWarnings.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - ODKValidateWarnings - - - - - What's your father's phone number? - - - What's your family name? - - - asdf - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/cascades_old.xml b/pyxform/tests/test_output/cascades_old.xml index 28d61b1d6..35cf77491 100644 --- a/pyxform/tests/test_output/cascades_old.xml +++ b/pyxform/tests/test_output/cascades_old.xml @@ -5,260 +5,212 @@ - - Thaba-Tseka_C - - - Qacha's Nek_C - - - Quthing_C - - - Ha Ts'oeu-Khala (TT-049) - - - Raporong (MH-088) - - - Ha Ralengoele (QN-068) - - - Ha Monyake (MH-089) - - - Ha Phatalla (MH-097) - - - Ha Makhabane (MH-186) - - - Ha Mokotane (MH-223) - - - Phatlalla (MH-097) - - - Rakoloi (MH-149) - - - Ha Tumo (MH-384) - - - Mataoeng (MH-219) - - - Limapong (MK-268) - - - Ha Nthimolane (MK-382) - - - Ha Leaooa (LR-299) + + Name 1 - - Ha Ralikuku (LR-240) + + Name 2 - - Tsoenene (MK-002) + + Name 3 - - T'sila-nt'so (MK-233) + + Other - - Moteetee (MK-037) + + Yes - - Lithoteng (MK-131) + + No - - Kopanong (BB-250) + + Male - - Mantlakala (BB-221) + + Female - - Community (BB-136) + + A - - Ha 'Masekh'ou (BB-130) + + A1 - - Mphale (BB-156) + + B - - Thoteng (BB-251) + + C - - Masianokeng (BB-259) + + Berea_A - - Motabola (BB-214) + + Botha Bothe_A - - Ha Matala (LR-124) + + Leribe_A - - Ha Mukemane (LR-507) + + Mafeteng_A - - Ha Makhaketsa (LR-045) + + Maseru_A - - Ha Manamolela (LR-429) + + Mohale's Hoek_A - - Rankhelepe (LR-210) + + Qacha's Nek_A - - Thaba-Phatsoa (LR-049) + + Quthing_A - - Ha Ramapepe (LR-183) + + Thaba-Tseka_A - - Ha Tsepe (LR-269) + + Mokhotlong_A1 - - Sekakeng (QT-335) + + Qacha's Nek_A1 - - Tiping (LR-428) + + Thaba-Tseka_A1 - - Ha Joele (MF-419) + + Berea_B - - Mokomahatsi (BR-210) + + Botha Bothe_B - - Rat'sit'so (QT-074) + + Leribe_B - - Setibing (MS-581) + + Mafeteng_B - - Polateng (MS-489) + + Maseru_B - - Nazareth (MS-077) + + Mohale's Hoek_B - - Mahlabatheng (MS-258) + + Mokhotlong_B - - Joala Boholo (MS-155) + + Qacha's Nek_B - - Ha Phallang (MS-488) + + Quthing_B - - Ha Moruthoane (MS-191) + + Thaba-Tseka_B - - Ha Leutsoa (MS-133) + + Berea_C - - Salae (MF-529) + + Botha Bothe_C - - Mathebe (MF-083) + + Leribe_C - - Tlokotsane (MS-490) + + Mafeteng_C - - B + + Maseru_C - - Ha Bereng Matsoho (MH-115) + + Maseru _C - - Ha Mohohlo (MH-404) + + Mohale's Hoek_C - - Ha Senekane (MH-321) + + Mokhotlong_C - - Malahleha (MH-036) + + Qacha's Nek_C - - Ha Nkau (MH-293) + + Quthing_C - - Ha Raboroko (MH-156) + + Thaba-Tseka_C - - Ha Tebelo (MF- 679) + + Ha Koone (BR-329) - - Ha Sebeli-Lehananeng (MF-054) + + Ha Makebe (BR-011) - - Kubake Moreneng (MS-422) + + Ha Mohatlane (BR-113) - - Machekoaneng (MS-104) + + Ha Motloang (BR-103) - - Tale (LR-223) + + Ha Ntlama (BR-048) - - Nkoeng (LR-361) + + Ha Polaki/Matheneng (BR-141/2) - - Ha Manehella (MF-669) + + Lekhalong (BR-400) - - Ha Likupa (MF-008) + + Lifotholeng (BR-070) - - Ha Tobolela (LR-257) + + Ha Lemphane (BB-195) - - Ha Tente (LR-258) + + Kepile (BB-209) - - Metolong (LR-227) + + Liteleng (Ha moluoane) (BB-248) - - Maqasane (LR-128) + + Luma (BB-235) - - Khalimane/Lijoetsa (MF-415) + + Motete (BB-146) - - Ha-Hlehlisi (MF-319) + + Patuoe/Moepanyane (BB-201) - - Molleloa/Ha Patsa (MF-393) + + Pokojoe-Khoaba (BB-095) - - Lithipeng/Mosuoane (MF-651) + + Solane (BB-094) - - Ha- Ralintoane (MF-698) + + Ha Mahlomola (LR-337) - - Ha Mashapha (MF-676) + + Ha Matoli (LR-152) - - Ha Seoli (MF-816) + + Ha Rantuba (LR-044) - - Ha Ramatima (MF-107) + + Ha Senyenyane (LR-423) - - Ha Sechache (MS-497) + + Ha Tsae (LR-256) - - Ha Mashenephe (MS-414) + + Mapheaneng (LR-334) - - Maieaneng (MF-009) + + Phahameng (LR-061) - - Makokotoaneng (MF-564) + + Phelandaba (LR-164) Ha Lepolesa (MF-098) @@ -272,560 +224,611 @@ Ha Tjale (MF-583) - - Ha Tsae (LR-256) + + Maieaneng (MF-009) - - Mapheaneng (LR-334) + + Makokotoaneng (MF-564) - - Phahameng (LR-061) + + Mathebe (MF-083) - - Phelandaba (LR-164) + + Salae (MF-529) - - Ha Masasane (MK-381) + + Ha Leutsoa (MS-133) - - Maluke (MH-473) + + Ha Moruthoane (MS-191) - - Boranta (BR-327) + + Ha Phallang (MS-488) - - No + + Joala Boholo (MS-155) - - Setotoma (MH-110) + + Mahlabatheng (MS-258) - - Yes + + Nazareth (MS-077) - - Photha-Photha (QT-401) + + Polateng (MS-489) - - Raseeng (QT-076) + + Setibing (MS-581) - - Thaba-Chitja (QT-292) + + Ha Makhabane (MH-186) - - Ha Long (TT-298) + + Ha Mokotane (MH-223) - - Ha Moriana (TT-297) + + Ha Monyake (MH-089) - - Ha Motake (TT-303) + + Ha Phatalla (MH-097) - - Ha Nkune (TT-212) + + Ha Tumo (MH-384) - - Ha Poko (TT-225) + + Mataoeng (MH-219) - - Ha Ratau (TT-165) + + Phatlalla (MH-097) - - Ha Sekhaupane (TT-344) + + Rakoloi (MH-149) - - Khubetsoana & Sekoting (MS-508) + + Raporong (MH-088) - - Ha Sofonia (MS-021) + + Ha Ralengoele (QN-068) - - Letsoela (BR-008) + + Ha Ramokakatela (QN-048) - - Lihlookong (MF-221) + + Ha-Isaac (QN-059) - - Khasapane (MF-219) + + Mosenekeng (QN-064) - - T'soeute (MF-750) + + Sekoti (QN-069) - - Sekiring (MF-292) + + Filoane (QT-358) - - Ha Nkoankoa (MS-447) + + Kelebone (QT-034) - - Ha Lekhafola (MS-215/426) + + Matebeleng (QT-078) - - Ha Salemone Morainyane (MS-475) + + Mofetoli (QT-366) - - Ha Pita (MS-025) + + Mots'oane (QT-075) - - Ha Senyenyane (LR-423) + + Nosi (QT-355) - - Ha Rantuba (LR-044) + + Seputeng (QT-354) - - Luma (BB-235) + + Swatsi (QT-238) - - Liteleng (Ha moluoane) (BB-248) + + Lihlabaneng Ha Morapeli (TT-170) - - Patuoe/Moepanyane (BB-201) + + Mohlakeng (TT-011) - - Motete (BB-146) + + Motsitseng (TT-160) - - Solane (BB-094) + + Bokhina Pere (MK-304) - - Pokojoe-Khoaba (BB-095) + + Ha Ralit'sepe (MK-393) - - Ha Matoli (LR-152) + + Ha Senepi (MK-404) - - Ha Mahlomola (LR-337) + + Makorong (MK-232) - - Bloodberg (MK-048) + + Mpakatheng (MK-265) - - Ha Mokhati (BR-025) + + Sekhutlong (MK-356) - - Ha Matjotjo (BR-175) + + Sepatleng (MK-343) - - Tholanyane (TT-299) + + Tseko (MK-154) - - Sekiring (QN-094) + + Ha Isaac (QT-059) - - Ha Tumo (BR-043) + + Ha Katela (QN-219) - - Ha Telukhunoana (BR-166) + + Ha-Makhoa (QN-073) - - Ha Phoofolo (BR-090) + + Maphotong (QN-216) - - Ha Phalatsane (BR-075) + + Ha Laka (TT-009) - - Mokhethoaneng (BR-16) + + Ha Sekhohola (TT-138) - - Kolojane (BR-036) + + Mantsonyane (TT-051) + + + Ha Labane (TT-363) + + + Maholi-a Llang (TT-210) + + + Boranta (BR-327) + + + Letsoela (BR-008) + + + Maholong (BR-328) + + + Masaleng Ha Janki (BR-238) + + + Mokhachane (BR-330) + + + Mokomahatsi (BR-210) + + + Nokong (BR-060) + + + Qalaheng/Mafotholeng (BR-45/84) + + + Tsenoli (BR-001) Ha Lesia (BB-258) - - Tsenoli (BR-001) + + Ha Lishobana (BB-217) Ha Rampai (BB-045) - - Ha Lishobana (BB-217) + + Ha-Rakotoane (BB-185) Hlakacha (BB-029) - - Ha-Rakotoane (BB-185) + + Khutlo-sea-ja (BB-188) Lekanyane (BB-212) - - Khutlo-sea-ja (BB-188) + + Maphepheng (BB-147) Setenong (BB-246) - - Maphepheng (BB-147) + + Ha Makhaketsa (LR-045) - - Ha Sephelane (QN-154) + + Ha Manamolela (LR-429) - - Ha Nkofo (QN-120) + + Ha Matala (LR-124) - - Masakoane (TT-304) + + Ha Mukemane (LR-507) - - Matsaile Manganeng (TT-411) + + Ha Ramapepe (LR-183) - - Ha Molomo (QN-206) + + Ha Tsepe (LR-269) - - Ha Ranqhongoana (QN-007) + + Rankhelepe (LR-210) - - Mosenekeng (MK-341) + + Thaba-Phatsoa (LR-049) - - Ha Lehata (QN-088) + + Tiping (LR-428) - - Matebeleng (MK-334) + + Ha Joele (MF-419) - - Matlong (MK-387) + + Ha Mashapha (MF-676) - - Mabuleng (MK-269) + + Ha- Ralintoane (MF-698) - - Maheneng (MK-342) + + Ha Ramatima (MF-107) - - Ha Seholoholo (QN-055) + + Ha Seoli (MF-816) - - Liboteng (QN-096) + + Ha-Hlehlisi (MF-319) - - Thaba-Tseka_A + + Khalimane/Lijoetsa (MF-415) - - Mokhotlong_A1 + + Lithipeng/Mosuoane (MF-651) - - Nokong (BR-060) + + Molleloa/Ha Patsa (MF-393) - - Qalaheng/Mafotholeng (BR-45/84) + + Ha Mashenephe (MS-414) - - Leribe_A + + Ha Sechache (MS-497) - - Mafeteng_A + + Mokotleng (MS-462) - - Berea_A + + Mphephee/Moeaneng (MS-521) - - Botha Bothe_A + + Ramakhaleng (MS-474) - - Qacha's Nek_A + + Rothe (MS-001) - - Quthing_A + + Tlokotsane (MS-490) - - Maseru_A + + Tsutsulupa (MS-516) - - Mohale's Hoek_A + + Keleke/Moleko (MS-465) - - Ha Isaac (QT-059) + + Ha Boroko (MH-009) - - Tseko (MK-154) + + Ha Hamo/Moko (MH-142) - - Sepatleng (MK-343) + + Ha Mokhatla (MH-158) - - Sekhutlong (MK-356) + + Ha Nthamaha (MH-478) - - Ha Laka (TT-009) + + Ha Ranti (MH-538) - - Maphotong (QN-216) + + Lecheche (MH-557) - - Ha-Makhoa (QN-073) + + Matsoareng (MH-073) - - Ha Katela (QN-219) + + Moru Motso (MH-507) - - Mantsonyane (TT-051) + + Pontseng (MH-027) - - Ha Sekhohola (TT-138) + + Draaihoek (MK-384) - - Maholong (BR-328) + + Ha Tlenyane (MK-212) - - Masaleng Ha Janki (BR-238) + + Khohlong (MK-312) - - Kepile (BB-209) + + Liotloaneng (MK-379) - - Ha Lemphane (BB-195) + + Mabuleng (MK-269) - - Ha Polaki/Matheneng (BR-141/2) + + Maheneng (MK-342) - - Ha Ntlama (BR-048) + + Matebeleng (MK-334) - - Lifotholeng (BR-070) + + Matlong (MK-387) - - Lekhalong (BR-400) + + Mosenekeng (MK-341) - - Ha Makebe (BR-011) + + Ha Lehata (QN-088) - - Ha Koone (BR-329) + + Ha Molomo (QN-206) - - Ha Motloang (BR-103) + + Ha Ranqhongoana (QN-007) - - Ha Mohatlane (BR-113) + + Ha Seholoholo (QN-055) - - Nkoto-Silase (QT-334) + + Liboteng (QN-096) - - Ngoae & Sekokoaneng (QT-067) + + Mankoe (QN-091) - - Matsaile Moreneng (TT-038) + + Qenehellong (QN-070) - - Moeling (TT-268) + + Sekiring (QN-094) - - Ha Ntsokoane (TT-301) + + Tsolo (QN-104) - - Ha Labane (TT-363) + + Mamokeli (QT-337) - - Qenehellong (QN-070) + + Marakabei (QT-398) + + + Masuoaneng (QT-392) + + + Mats'ela-Habeli (QT-246) + + + Ngoae & Sekokoaneng (QT-067) + + + Nkoto-Silase (QT-334) + + + Photha-Photha (QT-401) - - Mankoe (QN-091) + + Raseeng (QT-076) - - Tsolo (QN-104) + + Thaba-Chitja (QT-292) - - Maholi-a Llang (TT-210) + + Ha Long (TT-298) - - Marakabei (QT-398) + + Ha Moriana (TT-297) - - Mamokeli (QT-337) + + Ha Motake (TT-303) - - Mats'ela-Habeli (QT-246) + + Ha Nkune (TT-212) - - Masuoaneng (QT-392) + + Ha Poko (TT-225) - - Mokhotlong_B + + Ha Ratau (TT-165) - - Qacha's Nek_B + + Ha Sekhaupane (TT-344) - - Makunyapane (TT-083) + + Masakoane (TT-304) - - Qacha's Nek_A1 + + Tholanyane (TT-299) - - Thaba-Tseka_A1 + + Ha Matjotjo (BR-175) - - Berea_B + + Ha Mokhati (BR-025) - - Botha Bothe_B + + Ha Phalatsane (BR-075) - - Leribe_B + + Ha Phoofolo (BR-090) - - Mafeteng_B + + Ha Telukhunoana (BR-166) - - Maseru_B + + Ha Tumo (BR-043) - - Mohale's Hoek_B + + Kolojane (BR-036) - - Seputeng (QT-354) + + Mokhethoaneng (BR-16) - - Swatsi (QT-238) + + Community (BB-136) - - Lihlabaneng Ha Morapeli (TT-170) + + Ha 'Masekh'ou (BB-130) - - Mohlakeng (TT-011) + + Kopanong (BB-250) - - Motsitseng (TT-160) + + Mantlakala (BB-221) - - Bokhina Pere (MK-304) + + Masianokeng (BB-259) - - Ha Ralit'sepe (MK-393) + + Motabola (BB-214) - - Ha Senepi (MK-404) + + Mphale (BB-156) - - Makorong (MK-232) + + Thoteng (BB-251) - - Mpakatheng (MK-265) + + Ha Leaooa (LR-299) - - Female + + Ha Ralikuku (LR-240) - - Male + + Ha Tente (LR-258) - - C + + Ha Tobolela (LR-257) - - Other + + Maqasane (LR-128) - - Name 3 + + Metolong (LR-227) - - Name 2 + + Nkoeng (LR-361) - - Name 1 + + Tale (LR-223) - - Ha Hamo/Moko (MH-142) + + Ha Likupa (MF-008) - - Ha Mokhatla (MH-158) + + Ha Manehella (MF-669) - - Thoteng (QT-034) + + Ha Sebeli-Lehananeng (MF-054) - - Sello (QT-283) + + Ha Tebelo (MF- 679) - - Mokhachane (BR-330) + + Khasapane (MF-219) - - Tsutsulupa (MS-516) + + Lihlookong (MF-221) - - Keleke/Moleko (MS-465) + + Sekiring (MF-292) - - Ha Boroko (MH-009) + + T'soeute (MF-750) - - Mokotleng (MS-462) + + Ha Lekhafola (MS-215/426) - - Mphephee/Moeaneng (MS-521) + + Ha Nkoankoa (MS-447) - - Ramakhaleng (MS-474) + + Ha Pita (MS-025) - - Rothe (MS-001) + + Ha Salemone Morainyane (MS-475) - - Mokhotlong_C + + Ha Sofonia (MS-021) - - Mohale's Hoek_C + + Khubetsoana & Sekoting (MS-508) - - Mafeteng_C + + Kubake Moreneng (MS-422) - - Leribe_C + + Machekoaneng (MS-104) - - Maseru _C + + Ha Bereng Matsoho (MH-115) - - Maseru_C + + Ha Mohohlo (MH-404) - - Thaba-Tseka_B + + Ha Nkau (MH-293) - - Quthing_B + + Ha Raboroko (MH-156) - - Botha Bothe_C + + Ha Senekane (MH-321) - - Berea_C + + Malahleha (MH-036) - - Ha Chooko (TT-312) + + Maluke (MH-473) - - Nosi (QT-355) + + Setotoma (MH-110) - - Mots'oane (QT-075) + + Bloodberg (MK-048) - - Kelebone (QT-034) + + Ha Masasane (MK-381) - - Filoane (QT-358) + + Ha Nthimolane (MK-382) - - Mofetoli (QT-366) + + Limapong (MK-268) - - Matebeleng (QT-078) + + Lithoteng (MK-131) - - Ha-Isaac (QN-059) + + Moteetee (MK-037) - - Ha Ramokakatela (QN-048) + + T'sila-nt'so (MK-233) - - Sekoti (QN-069) + + Tsoenene (MK-002) - - Mosenekeng (QN-064) + + Ha Nkofo (QN-120) + + + Ha Sephelane (QN-154) Ha Tamose (QN-051) @@ -857,44 +860,41 @@ Paballong (QT-117) - - A - - - Makhuleng (TT-239) + + Rat'sit'so (QT-074) - - A1 + + Sekakeng (QT-335) - - Liotloaneng (MK-379) + + Sello (QT-283) - - Khohlong (MK-312) + + Thoteng (QT-034) - - Ha Tlenyane (MK-212) + + Ha Chooko (TT-312) - - Draaihoek (MK-384) + + Ha Ntsokoane (TT-301) - - Pontseng (MH-027) + + Ha Ts'oeu-Khala (TT-049) - - Moru Motso (MH-507) + + Makhuleng (TT-239) - - Matsoareng (MH-073) + + Makunyapane (TT-083) - - Lecheche (MH-557) + + Matsaile Manganeng (TT-411) - - Ha Ranti (MH-538) + + Matsaile Moreneng (TT-038) - - Ha Nthamaha (MH-478) + + Moeling (TT-268) @@ -989,204 +989,236 @@ + + + + static_instance-name_list-0 + name1 + + + static_instance-name_list-1 + name2 + + + static_instance-name_list-2 + name3 + + + static_instance-name_list-3 + othername + + + + + + + static_instance-yes_no_list-0 + yes + + + static_instance-yes_no_list-1 + no + + + + + + + static_instance-gender_list-0 + male + + + static_instance-gender_list-1 + female + + + + + + + static_instance-phase-0 + a + + + static_instance-phase-1 + a1 + + + static_instance-phase-2 + b + + + static_instance-phase-3 + c + + + static_instance-district-0 - a berea_a + a static_instance-district-1 - a botha_bothe_a + a static_instance-district-2 - a leribe_a + a static_instance-district-3 - a mafeteng_a + a static_instance-district-4 - a maseru_a + a static_instance-district-5 - a mohale_s_hoek_a + a static_instance-district-6 - a qacha_s_nek_a + a static_instance-district-7 - a quthing_a + a static_instance-district-8 - a thaba_tseka_a + a static_instance-district-9 - a1 mokhotlong_a1 + a1 static_instance-district-10 - a1 qacha_s_nek_a1 + a1 static_instance-district-11 - a1 thaba_tseka_a1 + a1 static_instance-district-12 - b berea_b + b static_instance-district-13 - b botha_bothe_b + b static_instance-district-14 - b leribe_b + b static_instance-district-15 - b mafeteng_b + b static_instance-district-16 - b maseru_b + b static_instance-district-17 - b mohale_s_hoek_b + b static_instance-district-18 - b mokhotlong_b + b static_instance-district-19 - b qacha_s_nek_b + b static_instance-district-20 - b quthing_b + b static_instance-district-21 - b thaba_tseka_b + b static_instance-district-22 - c berea_c + c static_instance-district-23 - c botha_bothe_c + c static_instance-district-24 - c leribe_c + c static_instance-district-25 - c mafeteng_c + c static_instance-district-26 - c maseru_c + c static_instance-district-27 - c maseru__c + c static_instance-district-28 - c mohale_s_hoek_c + c static_instance-district-29 - c mokhotlong_c + c static_instance-district-30 - c qacha_s_nek_c + c static_instance-district-31 - c quthing_c + c static_instance-district-32 - c thaba_tseka_c - - - - - - - static_instance-name_list-0 - name1 - - - static_instance-name_list-1 - name2 - - - static_instance-name_list-2 - name3 - - - static_instance-name_list-3 - othername - - - - - - - static_instance-gender_list-0 - male - - - static_instance-gender_list-1 - female + c @@ -1194,1550 +1226,1518 @@ static_instance-site-0 - a ha_koone_br_329_ + a berea_a static_instance-site-1 - a ha_makebe_br_011_ + a berea_a static_instance-site-2 - a ha_mohatlane_br_113_ + a berea_a static_instance-site-3 - a ha_motloang_br_103_ + a berea_a static_instance-site-4 - a ha_ntlama_br_048_ + a berea_a static_instance-site-5 - a ha_polaki_matheneng_br_141_2_ + a berea_a static_instance-site-6 - a lekhalong_br_400_ + a berea_a static_instance-site-7 - a lifotholeng_br_070_ + a berea_a static_instance-site-8 - a ha_lemphane_bb_195_ + a botha_bothe_a static_instance-site-9 - a kepile_bb_209_ + a botha_bothe_a static_instance-site-10 - a liteleng_ha_moluoane_bb_248_ + a botha_bothe_a static_instance-site-11 - a luma_bb_235_ + a botha_bothe_a static_instance-site-12 - a motete_bb_146_ + a botha_bothe_a static_instance-site-13 - a patuoe_moepanyane_bb_201_ + a botha_bothe_a static_instance-site-14 - a pokojoe_khoaba_bb_095_ + a botha_bothe_a static_instance-site-15 - a solane_bb_094_ + a botha_bothe_a static_instance-site-16 - a ha_mahlomola_lr_337_ + a leribe_a static_instance-site-17 - a ha_matoli_lr_152_ + a leribe_a static_instance-site-18 - a ha_rantuba_lr_044_ + a leribe_a static_instance-site-19 - a ha_senyenyane_lr_423_ + a leribe_a static_instance-site-20 - a ha_tsae_lr_256_ + a leribe_a static_instance-site-21 - a mapheaneng_lr_334_ + a leribe_a static_instance-site-22 - a phahameng_lr_061_ + a leribe_a static_instance-site-23 - a phelandaba_lr_164_ + a leribe_a static_instance-site-24 - a ha_lepolesa_mf_098_ + a mafeteng_a static_instance-site-25 - a ha_mathabang_lifajaneng_mf_472_ + a mafeteng_a static_instance-site-26 - a ha_ranteme_mf_039_ + a mafeteng_a static_instance-site-27 - a ha_tjale_mf_583_ + a mafeteng_a static_instance-site-28 - a maieaneng_mf_009_ + a mafeteng_a static_instance-site-29 - a makokotoaneng_mf_564_ + a mafeteng_a static_instance-site-30 - a mathebe_mf_083_ + a mafeteng_a static_instance-site-31 - a salae_mf_529_ + a mafeteng_a static_instance-site-32 - a ha_leutsoa_ms_133_ + a maseru_a static_instance-site-33 - a ha_moruthoane_ms_191_ + a maseru_a static_instance-site-34 - a ha_phallang_ms_488_ + a maseru_a static_instance-site-35 - a joala_boholo_ms_155_ + a maseru_a static_instance-site-36 - a mahlabatheng_ms_258_ + a maseru_a static_instance-site-37 - a nazareth_ms_077_ + a maseru_a static_instance-site-38 - a polateng_ms_489_ + a maseru_a static_instance-site-39 - a setibing_ms_581_ + a maseru_a static_instance-site-40 - a ha_makhabane_mh_186_ + a mohale_s_hoek_a static_instance-site-41 - a ha_mokotane_mh_223_ + a mohale_s_hoek_a static_instance-site-42 - a ha_monyake_mh_089_ + a mohale_s_hoek_a static_instance-site-43 - a ha_phatalla_mh_097_ + a mohale_s_hoek_a static_instance-site-44 - a ha_tumo_mh_384_ + a mohale_s_hoek_a static_instance-site-45 - a mataoeng_mh_219_ + a mohale_s_hoek_a static_instance-site-46 - a phatlalla_mh_097_ + a mohale_s_hoek_a static_instance-site-47 - a rakoloi_mh_149_ + a mohale_s_hoek_a static_instance-site-48 - a raporong_mh_088_ + a mohale_s_hoek_a static_instance-site-49 - a ha_ralengoele_qn_068_ + a qacha_s_nek_a static_instance-site-50 - a ha_ramokakatela_qn_048_ + a qacha_s_nek_a static_instance-site-51 - a ha_isaac_qn_059_ + a qacha_s_nek_a static_instance-site-52 - a mosenekeng_qn_064_ + a qacha_s_nek_a static_instance-site-53 - a sekoti_qn_069_ + a qacha_s_nek_a static_instance-site-54 - a filoane_qt_358_ + a quthing_a static_instance-site-55 - a kelebone_qt_034_ + a quthing_a static_instance-site-56 - a matebeleng_qt_078_ + a quthing_a static_instance-site-57 - a mofetoli_qt_366_ + a quthing_a static_instance-site-58 - a mots_oane_qt_075_ + a quthing_a static_instance-site-59 - a nosi_qt_355_ + a quthing_a static_instance-site-60 - a seputeng_qt_354_ + a quthing_a static_instance-site-61 - a swatsi_qt_238_ + a quthing_a static_instance-site-62 - a lihlabaneng_ha_morapeli_tt_170_ + a thaba_tseka_a static_instance-site-63 - a mohlakeng_tt_011_ + a thaba_tseka_a static_instance-site-64 - a motsitseng_tt_160_ + a thaba_tseka_a static_instance-site-65 - a1 bokhina_pere_mk_304_ + a1 mokhotlong_a1 static_instance-site-66 - a1 ha_ralit_sepe_mk_393_ + a1 mokhotlong_a1 static_instance-site-67 - a1 ha_senepi_mk_404_ + a1 mokhotlong_a1 static_instance-site-68 - a1 makorong_mk_232_ + a1 mokhotlong_a1 static_instance-site-69 - a1 mpakatheng_mk_265_ + a1 mokhotlong_a1 static_instance-site-70 - a1 sekhutlong_mk_356_ + a1 mokhotlong_a1 static_instance-site-71 - a1 sepatleng_mk_343_ + a1 mokhotlong_a1 static_instance-site-72 - a1 tseko_mk_154_ + a1 mokhotlong_a1 static_instance-site-73 - a1 ha_isaac_qt_059_ + a1 qacha_s_nek_a1 static_instance-site-74 - a1 ha_katela_qn_219_ + a1 qacha_s_nek_a1 static_instance-site-75 - a1 ha_makhoa_qn_073_ + a1 qacha_s_nek_a1 static_instance-site-76 - a1 maphotong_qn_216_ + a1 qacha_s_nek_a1 static_instance-site-77 - a1 ha_laka_tt_009_ + a1 thaba_tseka_a1 static_instance-site-78 - a1 ha_sekhohola_tt_138_ + a1 thaba_tseka_a1 static_instance-site-79 - a1 mantsonyane_tt_051_ + a1 thaba_tseka_a1 static_instance-site-80 - a1 ha_labane_tt_363_ + a1 thaba_tseka_a1 static_instance-site-81 - a1 maholi_a_llang_tt_210_ + a1 thaba_tseka_a1 static_instance-site-82 - b boranta_br_327_ + b berea_b static_instance-site-83 - b letsoela_br_008_ + b berea_b static_instance-site-84 - b maholong_br_328_ + b berea_b static_instance-site-85 - b masaleng_ha_janki_br_238_ + b berea_b static_instance-site-86 - b mokhachane_br_330_ + b berea_b static_instance-site-87 - b mokomahatsi_br_210_ + b berea_b static_instance-site-88 - b nokong_br_060_ + b berea_b static_instance-site-89 - b qalaheng_mafotholeng_br_45_84_ + b berea_b static_instance-site-90 - b tsenoli_br_001_ + b berea_b static_instance-site-91 - b ha_lesia_bb_258_ + b botha_bothe_b static_instance-site-92 - b ha_lishobana_bb_217_ + b botha_bothe_b static_instance-site-93 - b ha_rampai_bb_045_ + b botha_bothe_b static_instance-site-94 - b ha_rakotoane_bb_185_ + b botha_bothe_b static_instance-site-95 - b hlakacha_bb_029_ + b botha_bothe_b static_instance-site-96 - b khutlo_sea_ja_bb_188_ + b botha_bothe_b static_instance-site-97 - b lekanyane_bb_212_ + b botha_bothe_b static_instance-site-98 - b maphepheng_bb_147_ + b botha_bothe_b static_instance-site-99 - b setenong_bb_246_ + b botha_bothe_b static_instance-site-100 - b ha_makhaketsa_lr_045_ + b leribe_b static_instance-site-101 - b ha_manamolela_lr_429_ + b leribe_b static_instance-site-102 - b ha_matala_lr_124_ + b leribe_b static_instance-site-103 - b ha_mukemane_lr_507_ + b leribe_b static_instance-site-104 - b ha_ramapepe_lr_183_ + b leribe_b static_instance-site-105 - b ha_tsepe_lr_269_ + b leribe_b static_instance-site-106 - b rankhelepe_lr_210_ + b leribe_b static_instance-site-107 - b thaba_phatsoa_lr_049_ + b leribe_b static_instance-site-108 - b tiping_lr_428_ + b leribe_b static_instance-site-109 - b ha_joele_mf_419_ + b mafeteng_b static_instance-site-110 - b ha_mashapha_mf_676_ + b mafeteng_b static_instance-site-111 - b ha_ralintoane_mf_698_ + b mafeteng_b static_instance-site-112 - b ha_ramatima_mf_107_ + b mafeteng_b static_instance-site-113 - b ha_seoli_mf_816_ + b mafeteng_b static_instance-site-114 - b ha_hlehlisi_mf_319_ + b mafeteng_b static_instance-site-115 - b khalimane_lijoetsa_mf_415_ + b mafeteng_b static_instance-site-116 - b lithipeng_mosuoane_mf_651_ + b mafeteng_b static_instance-site-117 - b molleloa_ha_patsa_mf_393_ + b mafeteng_b static_instance-site-118 - b ha_mashenephe_ms_414_ + b maseru_b static_instance-site-119 - b ha_sechache_ms_497_ + b maseru_b static_instance-site-120 - b mokotleng_ms_462_ + b maseru_b static_instance-site-121 - b mphephee_moeaneng_ms_521_ + b maseru_b static_instance-site-122 - b ramakhaleng_ms_474_ + b maseru_b static_instance-site-123 - b rothe_ms_001_ + b maseru_b static_instance-site-124 - b tlokotsane_ms_490_ + b maseru_b static_instance-site-125 - b tsutsulupa_ms_516_ + b maseru_b static_instance-site-126 - b keleke_moleko_ms_465_ + b maseru_b static_instance-site-127 - b ha_boroko_mh_009_ + b mohale_s_hoek_b static_instance-site-128 - b ha_hamo_moko_mh_142_ + b mohale_s_hoek_b static_instance-site-129 - b ha_mokhatla_mh_158_ + b mohale_s_hoek_b static_instance-site-130 - b ha_nthamaha_mh_478_ + b mohale_s_hoek_b static_instance-site-131 - b ha_ranti_mh_538_ + b mohale_s_hoek_b static_instance-site-132 - b lecheche_mh_557_ + b mohale_s_hoek_b static_instance-site-133 - b matsoareng_mh_073_ + b mohale_s_hoek_b static_instance-site-134 - b moru_motso_mh_507_ + b mohale_s_hoek_b static_instance-site-135 - b pontseng_mh_027_ + b mohale_s_hoek_b static_instance-site-136 - b draaihoek_mk_384_ + b mokhotlong_b static_instance-site-137 - b ha_tlenyane_mk_212_ + b mokhotlong_b static_instance-site-138 - b khohlong_mk_312_ + b mokhotlong_b static_instance-site-139 - b liotloaneng_mk_379_ + b mokhotlong_b static_instance-site-140 - b mabuleng_mk_269_ + b mokhotlong_b static_instance-site-141 - b maheneng_mk_342_ + b mokhotlong_b static_instance-site-142 - b matebeleng_mk_334_ + b mokhotlong_b static_instance-site-143 - b matlong_mk_387_ + b mokhotlong_b static_instance-site-144 - b mosenekeng_mk_341_ + b mokhotlong_b static_instance-site-145 - b ha_lehata_qn_088_ + b qacha_s_nek_b static_instance-site-146 - b ha_molomo_qn_206_ + b qacha_s_nek_b static_instance-site-147 - b ha_ranqhongoana_qn_007_ + b qacha_s_nek_b static_instance-site-148 - b ha_seholoholo_qn_055_ + b qacha_s_nek_b static_instance-site-149 - b liboteng_qn_096_ + b qacha_s_nek_b static_instance-site-150 - b mankoe_qn_091_ + b qacha_s_nek_b static_instance-site-151 - b qenehellong_qn_070_ + b qacha_s_nek_b static_instance-site-152 - b sekiring_qn_094_ + b qacha_s_nek_b static_instance-site-153 - b tsolo_qn_104_ + b qacha_s_nek_b static_instance-site-154 - b mamokeli_qt_337_ + b quthing_b static_instance-site-155 - b marakabei_qt_398_ + b quthing_b static_instance-site-156 - b masuoaneng_qt_392_ + b quthing_b static_instance-site-157 - b mats_ela_habeli_qt_246_ + b quthing_b static_instance-site-158 - b ngoae_sekokoaneng_qt_067_ + b quthing_b static_instance-site-159 - b nkoto_silase_qt_334_ + b quthing_b static_instance-site-160 - b photha_photha_qt_401_ + b quthing_b static_instance-site-161 - b raseeng_qt_076_ + b quthing_b static_instance-site-162 - b thaba_chitja_qt_292_ + b quthing_b static_instance-site-163 - b ha_long_tt_298_ + b thaba_tseka_b static_instance-site-164 - b ha_moriana_tt_297_ + b thaba_tseka_b static_instance-site-165 - b ha_motake_tt_303_ + b thaba_tseka_b static_instance-site-166 - b ha_nkune_tt_212_ + b thaba_tseka_b static_instance-site-167 - b ha_poko_tt_225_ + b thaba_tseka_b static_instance-site-168 - b ha_ratau_tt_165_ + b thaba_tseka_b static_instance-site-169 - b ha_sekhaupane_tt_344_ + b thaba_tseka_b static_instance-site-170 - b masakoane_tt_304_ + b thaba_tseka_b static_instance-site-171 - b tholanyane_tt_299_ + b thaba_tseka_b static_instance-site-172 - c ha_matjotjo_br_175_ + c berea_c static_instance-site-173 - c ha_mokhati_br_025_ + c berea_c static_instance-site-174 - c ha_phalatsane_br_075_ + c berea_c static_instance-site-175 - c ha_phoofolo_br_090_ + c berea_c static_instance-site-176 - c ha_telukhunoana_br_166_ + c berea_c static_instance-site-177 - c ha_tumo_br_043_ + c berea_c static_instance-site-178 - c kolojane_br_036_ + c berea_c static_instance-site-179 - c mokhethoaneng_br_16_ + c berea_c static_instance-site-180 - c community_bb_136_ + c botha_bothe_c static_instance-site-181 - c ha_masekh_ou_bb_130_ + c botha_bothe_c static_instance-site-182 - c kopanong_bb_250_ + c botha_bothe_c static_instance-site-183 - c mantlakala_bb_221_ + c botha_bothe_c static_instance-site-184 - c masianokeng_bb_259_ + c botha_bothe_c static_instance-site-185 - c motabola_bb_214_ + c botha_bothe_c static_instance-site-186 - c mphale_bb_156_ + c botha_bothe_c static_instance-site-187 - c thoteng_bb_251_ + c botha_bothe_c static_instance-site-188 - c ha_leaooa_lr_299_ + c leribe_c static_instance-site-189 - c ha_ralikuku_lr_240_ + c leribe_c static_instance-site-190 - c ha_tente_lr_258_ + c leribe_c static_instance-site-191 - c ha_tobolela_lr_257_ + c leribe_c static_instance-site-192 - c maqasane_lr_128_ + c leribe_c static_instance-site-193 - c metolong_lr_227_ + c leribe_c static_instance-site-194 - c nkoeng_lr_361_ + c leribe_c static_instance-site-195 - c tale_lr_223_ + c leribe_c static_instance-site-196 - c ha_likupa_mf_008_ + c mafeteng_c static_instance-site-197 - c ha_manehella_mf_669_ + c mafeteng_c static_instance-site-198 - c ha_sebeli_lehananeng_mf_054_ + c mafeteng_c static_instance-site-199 - c ha_tebelo_mf_679_ + c mafeteng_c static_instance-site-200 - c khasapane_mf_219_ + c mafeteng_c static_instance-site-201 - c lihlookong_mf_221_ + c mafeteng_c static_instance-site-202 - c sekiring_mf_292_ + c mafeteng_c static_instance-site-203 - c t_soeute_mf_750_ + c mafeteng_c static_instance-site-204 - c ha_lekhafola_ms_215_426_ + c maseru_c static_instance-site-205 - c ha_nkoankoa_ms_447_ + c maseru_c static_instance-site-206 - c ha_pita_ms_025_ + c maseru_c static_instance-site-207 - c ha_salemone_morainyane_ms_475_ + c maseru_c static_instance-site-208 - c ha_sofonia_ms_021_ + c maseru_c static_instance-site-209 - c khubetsoana_sekoting_ms_508_ + c maseru_c static_instance-site-210 - c kubake_moreneng_ms_422_ + c maseru__c static_instance-site-211 - c machekoaneng_ms_104_ + c maseru__c static_instance-site-212 - c ha_bereng_matsoho_mh_115_ + c mohale_s_hoek_c static_instance-site-213 - c ha_mohohlo_mh_404_ + c mohale_s_hoek_c static_instance-site-214 - c ha_nkau_mh_293_ + c mohale_s_hoek_c static_instance-site-215 - c ha_raboroko_mh_156_ + c mohale_s_hoek_c static_instance-site-216 - c ha_senekane_mh_321_ + c mohale_s_hoek_c static_instance-site-217 - c malahleha_mh_036_ + c mohale_s_hoek_c static_instance-site-218 - c maluke_mh_473_ + c mohale_s_hoek_c static_instance-site-219 - c setotoma_mh_110_ + c mohale_s_hoek_c static_instance-site-220 - c bloodberg_mk_048_ + c mokhotlong_c static_instance-site-221 - c ha_masasane_mk_381_ + c mokhotlong_c static_instance-site-222 - c ha_nthimolane_mk_382_ + c mokhotlong_c static_instance-site-223 - c limapong_mk_268_ + c mokhotlong_c static_instance-site-224 - c lithoteng_mk_131_ + c mokhotlong_c static_instance-site-225 - c moteetee_mk_037_ + c mokhotlong_c static_instance-site-226 - c t_sila_nt_so_mk_233_ + c mokhotlong_c static_instance-site-227 - c tsoenene_mk_002_ + c mokhotlong_c static_instance-site-228 - c ha_nkofo_qn_120_ + c qacha_s_nek_c static_instance-site-229 - c ha_sephelane_qn_154_ + c qacha_s_nek_c static_instance-site-230 - c ha_tamose_qn_051_ + c qacha_s_nek_c static_instance-site-231 - c ha_t_sita_qn_009_ + c qacha_s_nek_c static_instance-site-232 - c khubetsoana_qn_101_ + c qacha_s_nek_c static_instance-site-233 - c lepapaneng_qn_121_ + c qacha_s_nek_c static_instance-site-234 - c rasekoele_qn_100_ + c qacha_s_nek_c static_instance-site-235 - c sefaha_qn_003_ + c qacha_s_nek_c static_instance-site-236 - c mohale_qt_196_ + c quthing_c static_instance-site-237 - c mokokoane_qt_273_ + c quthing_c static_instance-site-238 - c mpharane_qt_287_ + c quthing_c static_instance-site-239 - c paballong_qt_117_ + c quthing_c static_instance-site-240 - c rat_sit_so_qt_074_ + c quthing_c static_instance-site-241 - c sekakeng_qt_335_ + c quthing_c static_instance-site-242 - c sello_qt_283_ + c quthing_c static_instance-site-243 - c thoteng_qt_034_ + c quthing_c static_instance-site-244 - c ha_chooko_tt_312_ + c thaba_tseka_c static_instance-site-245 - c ha_ntsokoane_tt_301_ + c thaba_tseka_c static_instance-site-246 - c ha_ts_oeu_khala_tt_049_ + c thaba_tseka_c static_instance-site-247 - c makhuleng_tt_239_ + c thaba_tseka_c static_instance-site-248 - c makunyapane_tt_083_ + c thaba_tseka_c static_instance-site-249 - c matsaile_manganeng_tt_411_ + c thaba_tseka_c static_instance-site-250 - c matsaile_moreneng_tt_038_ + c thaba_tseka_c static_instance-site-251 - c moeling_tt_268_ + c thaba_tseka_c - - - - static_instance-yes_no_list-0 - yes - - - static_instance-yes_no_list-1 - no - - - - - - - static_instance-phase-0 - a - - - static_instance-phase-1 - a1 - - - static_instance-phase-2 - b - - - static_instance-phase-3 - c - - - diff --git a/pyxform/tests/test_output/cascading_select_test.xml b/pyxform/tests/test_output/cascading_select_test.xml deleted file mode 100644 index 0e02931e9..000000000 --- a/pyxform/tests/test_output/cascading_select_test.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - - cascading_select_test - - - - - LGA 2 - - - LGA 2 - - - Zone 1 - - - LGA 1 - - - Zone 2 - - - LGA 4 - - - State 1 - - - State 2 - - - State 3 - - - State 4 - - - LGA 6 - - - LGA 5 - - - LGA 3 - - - LGA 1 - - - - - - - - - - - - - - - - - static_instance-state-0 - state_1 - zone_1 - - - static_instance-state-1 - state_2 - zone_1 - - - static_instance-state-2 - state_3 - zone_2 - - - static_instance-state-3 - state_4 - zone_2 - - - - - - - static_instance-lga-0 - state_1 - lga_1 - zone_1 - - - static_instance-lga-1 - state_1 - lga_2 - zone_1 - - - static_instance-lga-2 - state_2 - lga_3 - zone_1 - - - static_instance-lga-3 - state_2 - lga_4 - zone_1 - - - static_instance-lga-4 - state_3 - lga_5 - zone_2 - - - static_instance-lga-5 - state_3 - lga_6 - zone_2 - - - static_instance-lga-6 - state_4 - lga_1 - zone_2 - - - static_instance-lga-7 - state_4 - lga_2 - zone_2 - - - - - - - static_instance-zone-0 - zone_1 - - - static_instance-zone-1 - zone_2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/default_time_demo.xml b/pyxform/tests/test_output/default_time_demo.xml deleted file mode 100644 index 69f38352c..000000000 --- a/pyxform/tests/test_output/default_time_demo.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Default TIme Demo - - - - 09:30:00 - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/flat_xlsform_test.xml b/pyxform/tests/test_output/flat_xlsform_test.xml deleted file mode 100644 index 8e0c6fe14..000000000 --- a/pyxform/tests/test_output/flat_xlsform_test.xml +++ /dev/null @@ -1,930 +0,0 @@ - - - - Flat test - - - - - - - - - - - - - - Yes - - - No - - - - - - - No - - - - - - - - - - - Yes - - - - - - - No - - - - - - - jr://images/a.jpg - - - - - - - - - - - - - - - Yes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Yes - - - No - - - Yes - - - - - - - Yes - - - - - - - jr://images/b.jpg - - - - - - - - - - - - - - - - - - - - - - - jr://images/img_test.jpg - jr://audio/audio_test.wav - jr://video/test.mov - - - - - - - - - No - - - Yes - - - jr://images/b.jpg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No - - - - - - - a note - - - The goal of this test is to try out all the different media types in many languages to see if there are any bugs inserting media. - - - - - - - - - - - jr://images/a.jpg - - - - - - - No - - - - - - - - - - - - - - - jr://images/img_test.jpg - - - Yes - - - No - - - - - - - - - - - - - - - - - - - - 没有 - - - - - - - 没有 - - - - - - - - - - - - - - - - - - 没有 - - - - - - - jr://images/- - - - 您好 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 没有 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 對不起 ,你可以不選擇"是"和"否"。 - - - - - - - ni hao - - - jr://images/- - jr://audio/chinese_audio.wav - jr://video/- - 您好 - - - - - - - 没有 - - - - - - jr://images/- - - - - - - - - - - - - - - - - - - - - - - - - - - - jr://images/- - - - - - - - jr://images/- - - - 没有 - - - - - - - - - - - ni hao - - - - - - - - - - - - - - - - - - - 没有 - - - - - - - - - - - - - - - - - - - - - - - - - - 没有 - - - jr://images/- - - - - - deviceid_test_output: - - geopoint_test - - - list-nolabel-test - - - - - - - Enter your name - - - - - - - This launches a fictional application to get an integer result. - - - boolean name test - - - - - - - acknowledge_test - - - - - - - jr://images/- - - - - - - - Enter an address - - - compact-test - - - - - - - - - - - today_test_output: - - table list question - - - image_test - - - simserial_test_output: - - - - - - - - - - start test output: - - 1 - - - Your name is - - numerical name test - - - required_text - - - barcode_test - - - - - - Sorry , you can't select yes and no. - - - jr://images/- - - - - - - - autocomplete_chars_test - - - - - - - jr://images/img_test_2.jpg - jr://audio/- - jr://video/- - text_image_audio_video_test - - - date_test - - - - - - - time_test - - - note_test - - - compact-2-test - - - - - - - select multiple test - - - - - - - a integer - - - - - - - label-test - - - autocomplete_test - - - labeled select group test - - - - - - - jr://images/- - - - - - - - deviceid_test_output: - - - - - - jr://images/- - - - datetime_test - - - Skip to end - - - constrained decimal - - - end test output: - - jr://images/- - - - audio_test - - - You entered an email address - - - - - - - phonenumber_test_output: - - video_test - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_1/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/new_cascading_select.xml b/pyxform/tests/test_output/new_cascading_select.xml deleted file mode 100644 index 47e7b3532..000000000 --- a/pyxform/tests/test_output/new_cascading_select.xml +++ /dev/null @@ -1,183 +0,0 @@ - - - - cascading select test - - - - - harlingen - - - brownsville - - - Finney - - - Dumont - - - Texas - - - Washington - - - Redmond - - - Seattle - - - Puyallup - - - Pierce - - - King - - - Cameron - - - King - - - Tacoma - - - - - - - - - - - - - - - - - static_instance-states-0 - texas - - - static_instance-states-1 - washington - - - - - - - static_instance-cities-0 - king - texas - dumont - - - static_instance-cities-1 - king - texas - finney - - - static_instance-cities-2 - cameron - texas - brownsville - - - static_instance-cities-3 - cameron - texas - harlingen - - - static_instance-cities-4 - king - washington - seattle - - - static_instance-cities-5 - king - washington - redmond - - - static_instance-cities-6 - pierce - washington - tacoma - - - static_instance-cities-7 - pierce - washington - puyallup - - - - - - - static_instance-counties-0 - washington - king - - - static_instance-counties-1 - washington - pierce - - - static_instance-counties-2 - texas - king - - - static_instance-counties-3 - texas - cameron - - - - - - - - - - - - - - - texas - - - - washington - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/old_cascades.xml b/pyxform/tests/test_output/old_cascades.xml deleted file mode 100644 index 32a82ae19..000000000 --- a/pyxform/tests/test_output/old_cascades.xml +++ /dev/null @@ -1,179 +0,0 @@ - - - - old_cascades - - - - - harlingen - - - brownsville - - - finney - - - dumont - - - texas - - - washington - - - redmond - - - seattle - - - puyallup - - - cameron - - - king - - - pierce - - - king - - - tacoma - - - - - - - - - - - - - - - - - static_instance-states-0 - texas - - - static_instance-states-1 - washington - - - - - - - static_instance-cities-0 - texas - dumont - king - - - static_instance-cities-1 - texas - finney - king - - - static_instance-cities-2 - texas - brownsville - cameron - - - static_instance-cities-3 - texas - harlingen - cameron - - - static_instance-cities-4 - washington - seattle - king - - - static_instance-cities-5 - washington - redmond - king - - - static_instance-cities-6 - washington - tacoma - pierce - - - static_instance-cities-7 - washington - puyallup - pierce - - - - - - - static_instance-counties-0 - texas - king - - - static_instance-counties-1 - texas - cameron - - - static_instance-counties-2 - washington - king - - - static_instance-counties-3 - washington - pierce - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/or_other.xml b/pyxform/tests/test_output/or_other.xml deleted file mode 100644 index 8bf4b0428..000000000 --- a/pyxform/tests/test_output/or_other.xml +++ /dev/null @@ -1,109 +0,0 @@ - - - - or_other - - - - - apricot - - - blue - - - mauve - - - red - - - green - - - - - - - - - - - - - - - - - static_instance-colors-0 - no - red - - - static_instance-colors-1 - no - green - - - static_instance-colors-2 - no - blue - - - static_instance-colors-3 - yes - mauve - - - static_instance-colors-4 - yes - apricot - - - - - - - - - - - - - - - red - - - - green - - - - blue - - - - mauve - - - - apricot - - - - other - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/repeat_date_test.xml b/pyxform/tests/test_output/repeat_date_test.xml deleted file mode 100644 index 4555fd870..000000000 --- a/pyxform/tests/test_output/repeat_date_test.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - repeat_date_test - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - yes - - - - no - - - - - - - yes - - - - no - - - - - - - - - diff --git a/pyxform/tests/test_output/select_one_external.csv b/pyxform/tests/test_output/select_one_external.csv deleted file mode 100644 index 117ceafa5..000000000 --- a/pyxform/tests/test_output/select_one_external.csv +++ /dev/null @@ -1,15 +0,0 @@ -"list_name","name","label","state","county" -"states","texas","Texas","","" -"states","washington","Washington","","" -"counties","king","King","washington","" -"counties","pierce","Pierce","washington","" -"counties","king","King","texas","" -"counties","cameron","Cameron","texas","" -"cities","dumont","Dumont","texas","king" -"cities","finney","Finney","texas","king" -"cities","brownsville","brownsville","texas","cameron" -"cities","harlingen","harlingen","texas","cameron" -"cities","seattle","Seattle","washington","king" -"cities","redmond","Redmond","washington","king" -"cities","tacoma","Tacoma","washington","pierce" -"cities","puyallup","Puyallup","washington","pierce" diff --git a/pyxform/tests/test_output/select_one_external.xml b/pyxform/tests/test_output/select_one_external.xml deleted file mode 100644 index 409768fe4..000000000 --- a/pyxform/tests/test_output/select_one_external.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - cascading select test - - - - - - - - - - - - - - - - - - - - - - - texas - - - - washington - - - - - - - - - - diff --git a/pyxform/tests/test_output/simple_loop.json b/pyxform/tests/test_output/simple_loop.json deleted file mode 100644 index 6e2eaf511..000000000 --- a/pyxform/tests/test_output/simple_loop.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "simple_loop", - "title": "simple_loop", - "sms_keyword": "simple_loop", - "default_language": "default", - "id_string": "simple_loop", - "type": "survey", - "children": [ - { - "children": [ - { - "type": "integer", - "name": "count", - "label": { - "English": "How many are there in this group?" - } - } - ], - "type": "loop", - "name": "my_table", - "columns": [ - { - "name": "col1", - "label": { - "English": "Column 1" - } - }, - { - "name": "col2", - "label": { - "English": "Column 2" - } - } - ], - "label": { - "English": "My Table" - } - }, - { - "control": { - "bodyless": true - }, - "type": "group", - "name": "meta", - "children": [ - { - "bind": { - "readonly": "true()", - "calculate": "concat('uuid:', uuid())" - }, - "type": "calculate", - "name": "instanceID" - } - ] - } - ] -} \ No newline at end of file diff --git a/pyxform/tests/test_output/widgets.xml b/pyxform/tests/test_output/widgets.xml deleted file mode 100644 index f20aa7ad2..000000000 --- a/pyxform/tests/test_output/widgets.xml +++ /dev/null @@ -1,497 +0,0 @@ - - - - widgets - - - - - jr://images/happy.jpg - - - jr://images/sad.jpg - - - jr://images/happy.jpg - - - jr://images/sad.jpg - - - jr://images/sad.jpg - - - jr://images/happy.jpg - - - jr://images/sad.jpg - - - jr://images/happy.jpg - - - jr://images/a.jpg - a - - - jr://images/b.jpg - b - - - jr://images/b.jpg - b - - - jr://images/sad.jpg - - - jr://images/a.jpg - a - - - jr://images/happy.jpg - - - jr://images/happy.jpg - - - jr://images/sad.jpg - - - - - - - - 18.31 - 2010-06-15 - - a c - 8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - can be short or very long - - - - try entering a number < 10 - - - - only numbers > 10.51 and < 18.39 - - - - only future dates allowed - - - - testing time - - - - - scroll down to see default selection - - - 1 - - - - 2 - - - - 3 - - - - 4 - - - - 5 - - - - 6 - - - - 7 - - - - 8 - - - - - need to push button - - - - long hint: there is an upcoming section. - - - - this will get gps location - - - - scans multi-format 1d/2d barcodes - - - - this will launch the camera - - - - this will launch the audio recorder - - - - this will launch the video recorder - - - - Takes 0-9, -, +, ., space, and comma - - - - Note: this uses DATA and requires a connection - - - - - - - - - a - - - - b - - - - c - - - - d - - - - - - - - a - - - - b - - - - c - - - - d - - - - - make sure to put a.jpg and b.jpg in the form-media folder - - - - - - - - make sure to put a.jpg and b.jpg in the form-media folder - - - - - - - - - - - - yes - - - - no - - - - - - - yes - - - - no - - - - - - - yes - - - - no - - - - - - - - - - - - yes - - - - no - - - - - - - yes - - - - no - - - - - - - yes - - - - no - - - - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/xlsform_spec_test.xml b/pyxform/tests/test_output/xlsform_spec_test.xml deleted file mode 100644 index 4a47c19aa..000000000 --- a/pyxform/tests/test_output/xlsform_spec_test.xml +++ /dev/null @@ -1,957 +0,0 @@ - - - - Spec test - - - - - - Yes - - - - - - - - - - - No - - - - - - - No - - - - - - - - - - - - - - - The goal of this test is to try out all the different media types in many languages to see if there are any bugs inserting media. - - - No - - - Yes - - - - - - - - - - - - - - - jr://images/a.jpg - - - jr://images/b.jpg - - - jr://images/b.jpg - - - - - - - - - - - Yes - - - jr://images/img_test.jpg - jr://audio/audio_test.wav - jr://video/test.mov - - - - - - - - - - - - - No - - - jr://images/img_test.jpg - - - - - - - - - - - - - - - Yes - - - - - - - jr://images/a.jpg - - - a note - - - - - - - No - - - Yes - - - No - - - No - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No - - - - - - - - - - - - - - - - - - - - - - - - - - - Yes - - - - - - - - - - - - - - - Yes - - - - - - - Yes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 没有 - - - - - - - 没有 - - - - - - - - - - - - - - - ni hao - - - 没有 - - - - - - - - - - jr://images/- - - - - - - - - - - - jr://images/- - - - jr://images/- - - - - - - - - - - - - - - jr://images/- - jr://audio/chinese_audio.wav - jr://video/- - 您好 - - - - - - - - - - - 没有 - - - - - - - - - - - - - - - - - - - - - - - - - - jr://images/- - - - - - - - - - - - 没有 - - - - - - 没有 - - - 没有 - - - 您好 - - - - - - - 對不起 ,你可以不選擇"是"和"否"。 - - - - - - - - - - - - - - - - - - - 没有 - - - - - - - - - - - - - - - jr://images/- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ni hao - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - numerical name test - - - - - - - deviceid_test_output: - - - - - - end test output: - - today_test_output: - - - - - - - - - - - - - - - - - - - - - - - - - image_test - - - autocomplete_test - - - list-nolabel-test - - - jr://images/- - - - note_test - - - time_test - - - deviceid_test_output: - - - - - - jr://images/img_test_2.jpg - jr://audio/- - jr://video/- - text_image_audio_video_test - - - Skip to end - - - audio_test - - - You entered an email address - - - date_test - - - select multiple test - - - barcode_test - - - This launches a fictional application to get an integer result. - - - jr://images/- - - - - - - - video_test - - - jr://images/- - - - - - - - - - - - - - - - datetime_test - - - - - - - - - - - autocomplete_chars_test - - - Sorry , you can't select yes and no. - - - 1 - - - table list question - - - repeat_test - - - boolean name test - - - compact-2-test - - - geopoint_test - - - constrained decimal - - - required_text - - - Enter your name - - - - - - - phonenumber_test_output: - - label-test - - - Enter an address - - - labeled select group test - - - - - - - - - - - - - - - acknowledge_test - - - jr://images/- - - - Your name is - - jr://images/- - - - a integer - - - start test output: - - simserial_test_output: - - compact-test - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_1/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pyxform/tests/test_output/xml_escaping.xml b/pyxform/tests/test_output/xml_escaping.xml deleted file mode 100644 index 7d5455212..000000000 --- a/pyxform/tests/test_output/xml_escaping.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - xml_escaping - - - - - - - - - - - - - - - - - - - - - - yes - - - - no - - - - diff --git a/pyxform/tests/test_output/yes_or_no_question.json b/pyxform/tests/test_output/yes_or_no_question.json deleted file mode 100644 index e95631377..000000000 --- a/pyxform/tests/test_output/yes_or_no_question.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "yes_or_no_question", - "title": "yes_or_no_question", - "sms_keyword": "yes_or_no_question", - "default_language": "default", - "id_string": "yes_or_no_question", - "type": "survey", - "children": [ - { - "choices": [ - { - "name": "yes", - "label": { - "english": "yes" - } - }, - { - "name": "no", - "label": { - "english": "no" - } - } - ], - "type": "select one", - "name": "good_day", - "parameters": {}, - "label": { - "english": "have you had a good day today?" - } - }, - { - "control": { - "bodyless": true - }, - "type": "group", - "name": "meta", - "children": [ - { - "bind": { - "readonly": "true()", - "calculate": "concat('uuid:', uuid())" - }, - "type": "calculate", - "name": "instanceID" - } - ] - } - ] -} \ No newline at end of file diff --git a/pyxform/tests/test_validator_update.py b/pyxform/tests/test_validator_update.py index 74ff47308..0c25794bb 100644 --- a/pyxform/tests/test_validator_update.py +++ b/pyxform/tests/test_validator_update.py @@ -1,22 +1,33 @@ -from contextlib import contextmanager -from datetime import datetime, timedelta +# -*- coding: utf-8 -*- +""" +Test validator update cli command. +""" import os import platform import shutil -from stat import S_IXUSR, S_IXGRP import tempfile +from contextlib import contextmanager +from datetime import datetime, timedelta +from stat import S_IXGRP, S_IXUSR from zipfile import ZipFile -try: - from zipfile import BadZipFile -except ImportError: - from zipfile import BadZipfile as BadZipFile + from unittest2 import TestCase, skipIf + from pyxform.errors import PyXFormError from pyxform.tests import validators from pyxform.tests.validators.server import ThreadingServerInThread from pyxform.utils import unicode -from pyxform.validators.updater import _UpdateInfo, _UpdateHandler, \ - capture_handler, EnketoValidateUpdater +from pyxform.validators.updater import ( + EnketoValidateUpdater, + _UpdateHandler, + _UpdateInfo, + capture_handler, +) + +try: + from zipfile import BadZipFile +except ImportError: + from zipfile import BadZipfile as BadZipFile TEST_PATH = validators.HERE @@ -43,9 +54,13 @@ def get_update_info(check_ok, mod_root=None): else: install_check = install_check_fail return _UpdateInfo( - api_url="", repo_url="", validate_subfolder="", - install_check=install_check, validator_basename="validate", - mod_root=mod_root) + api_url="", + repo_url="", + validate_subfolder="", + install_check=install_check, + validator_basename="validate", + mod_root=mod_root, + ) @contextmanager @@ -71,7 +86,6 @@ def get_temp_dir(): class TestTempUtils(TestCase): - def test_get_temp_file(self): """Should provide a temp file that's cleared on exit.""" with get_temp_file() as temp_file: @@ -146,8 +160,7 @@ def test_write_json(self): """Should write the supplied dict to a file.""" with get_temp_file() as temp_file: self.assertEqual(0, os.path.getsize(temp_file)) - self.updater._write_json( - file_path=temp_file, content={"some": "data"}) + self.updater._write_json(file_path=temp_file, content={"some": "data"}) self.assertTrue(20 <= os.path.getsize(temp_file)) def test_read_last_check(self): @@ -161,37 +174,47 @@ def test_write_last_check(self): with get_temp_file() as temp_file: self.assertEqual(0, os.path.getsize(temp_file)) self.updater._write_last_check( - file_path=temp_file, content=datetime.utcnow()) + file_path=temp_file, content=datetime.utcnow() + ) self.assertEqual(20, os.path.getsize(temp_file)) def test_check_necessary__true_if_last_check_not_found(self): """Should return true if the last check file wasn't found.""" self.update_info.last_check_path = self.phantom_file - self.assertTrue(self.updater._check_necessary( - update_info=self.update_info, utc_now=self.utc_now)) + self.assertTrue( + self.updater._check_necessary( + update_info=self.update_info, utc_now=self.utc_now + ) + ) def test_check_necessary__true_if_latest_json_not_found(self): """Should return true if the latest.json file wasn't found.""" self.update_info.last_check_path = self.last_check self.update_info.latest_path = self.phantom_file - self.assertTrue(self.updater._check_necessary( - update_info=self.update_info, utc_now=self.utc_now)) + self.assertTrue( + self.updater._check_necessary( + update_info=self.update_info, utc_now=self.utc_now + ) + ) def test_check_necessary__true_if_last_check_empty(self): """Should return true if the last check file was empty.""" - self.update_info.last_check_path = os.path.join( - TEST_PATH, ".last_check_none") + self.update_info.last_check_path = os.path.join(TEST_PATH, ".last_check_none") self.update_info.latest_path = self.latest_enketo - self.assertTrue(self.updater._check_necessary( - update_info=self.update_info, utc_now=self.utc_now)) + self.assertTrue( + self.updater._check_necessary( + update_info=self.update_info, utc_now=self.utc_now + ) + ) def test_check_necessary__true_if_last_check_too_old(self): """Should return true if the last check was too long ago.""" self.update_info.last_check_path = self.last_check self.update_info.latest_path = self.latest_enketo old = self.utc_now - timedelta(minutes=45.0) - self.assertTrue(self.updater._check_necessary( - update_info=self.update_info, utc_now=old)) + self.assertTrue( + self.updater._check_necessary(update_info=self.update_info, utc_now=old) + ) def test_check_necessary__false_last_check_very_recent(self): """Should return false if the last check was very recent.""" @@ -203,7 +226,9 @@ def test_check_necessary__false_last_check_very_recent(self): self.update_info.latest_path = self.latest_enketo self.assertFalse( self.updater._check_necessary( - update_info=self.update_info, utc_now=self.utc_now)) + update_info=self.update_info, utc_now=self.utc_now + ) + ) def test_get_latest__if_check_necessary_true(self): """Should get latest from remote, rather than file.""" @@ -211,8 +236,7 @@ def test_get_latest__if_check_necessary_true(self): old = self.utc_now - timedelta(minutes=45.0) with get_temp_file() as temp_check, get_temp_file() as temp_json: - self.updater._write_last_check( - file_path=temp_check, content=old) + self.updater._write_last_check(file_path=temp_check, content=old) self.update_info.last_check_path = temp_check self.update_info.latest_path = temp_json latest = self.updater._get_latest(update_info=self.update_info) @@ -235,8 +259,7 @@ def test_list__not_installed_no_files(self): self.update_info.latest_path = self.latest_odk with get_temp_file() as temp_check: - self.updater._write_last_check( - file_path=temp_check, content=self.utc_now) + self.updater._write_last_check(file_path=temp_check, content=self.utc_now) self.update_info.last_check_path = temp_check self.updater.list(update_info=self.update_info) info = capture_handler.watcher.output["INFO"][0] @@ -249,8 +272,7 @@ def test_list__not_installed_with_files(self): self.update_info.latest_path = self.latest_enketo with get_temp_file() as temp_check: - self.updater._write_last_check( - file_path=temp_check, content=self.utc_now) + self.updater._write_last_check(file_path=temp_check, content=self.utc_now) self.update_info.last_check_path = temp_check self.updater.list(update_info=self.update_info) info = capture_handler.watcher.output["INFO"][0] @@ -263,8 +285,7 @@ def test_list__installed_no_files(self): self.update_info.latest_path = self.latest_odk with get_temp_file() as temp_check: - self.updater._write_last_check( - file_path=temp_check, content=self.utc_now) + self.updater._write_last_check(file_path=temp_check, content=self.utc_now) self.update_info.last_check_path = temp_check self.updater.list(update_info=self.update_info) info = capture_handler.watcher.output["INFO"][0] @@ -277,8 +298,7 @@ def test_list__installed_with_files(self): self.update_info.latest_path = self.latest_enketo with get_temp_file() as temp_check: - self.updater._write_last_check( - file_path=temp_check, content=self.utc_now) + self.updater._write_last_check(file_path=temp_check, content=self.utc_now) self.update_info.last_check_path = temp_check self.updater.list(update_info=self.update_info) info = capture_handler.watcher.output["INFO"][0] @@ -292,23 +312,20 @@ def test_find_download_url__no_files(self): with self.assertRaises(PyXFormError) as ctx: self.updater._find_download_url( - update_info=self.update_info, - json_data=json_data, - file_name=file_name) + update_info=self.update_info, json_data=json_data, file_name=file_name + ) self.assertIn("No files attached", unicode(ctx.exception)) def test_find_download_url__not_found(self): """Should raise an error if the file was not found.""" file_name = "windows.zip" json_data = self.updater._read_json(file_path=self.latest_enketo) - json_data["assets"] = [x for x in json_data["assets"] - if x["name"] != file_name] + json_data["assets"] = [x for x in json_data["assets"] if x["name"] != file_name] with self.assertRaises(PyXFormError) as ctx: self.updater._find_download_url( - update_info=self.update_info, - json_data=json_data, - file_name=file_name) + update_info=self.update_info, json_data=json_data, file_name=file_name + ) self.assertIn("No files with the name", unicode(ctx.exception)) def test_find_download_url__duplicates(self): @@ -320,22 +337,22 @@ def test_find_download_url__duplicates(self): with self.assertRaises(PyXFormError) as ctx: self.updater._find_download_url( - update_info=self.update_info, - json_data=json_data, - file_name=file_name) + update_info=self.update_info, json_data=json_data, file_name=file_name + ) self.assertIn("2 files with the name", unicode(ctx.exception)) def test_find_download_url__ok(self): """Should return the url for the matching file name.""" file_name = "windows.zip" json_data = self.updater._read_json(file_path=self.latest_enketo) - expected = "https://github.com/enketo/enketo-validate/releases/" \ - "download/1.0.3/windows.zip" + expected = ( + "https://github.com/enketo/enketo-validate/releases/" + "download/1.0.3/windows.zip" + ) observed = self.updater._find_download_url( - update_info=self.update_info, - json_data=json_data, - file_name=file_name) + update_info=self.update_info, json_data=json_data, file_name=file_name + ) self.assertEqual(expected, observed) def test_download_file(self): @@ -344,14 +361,16 @@ def test_download_file(self): with get_temp_file() as temp_file: self.assertEqual(0, os.path.getsize(temp_file)) self.updater._download_file( - url=self.update_info.api_url, file_path=temp_file) + url=self.update_info.api_url, file_path=temp_file + ) self.assertEqual(13, os.path.getsize(temp_file)) def test_get_bin_paths__ok(self): """Should return the path mappings.""" file_path = os.path.join(TEST_PATH, "linux.zip") observed = self.updater._get_bin_paths( - update_info=self.update_info, file_path=file_path) + update_info=self.update_info, file_path=file_path + ) self.assertEqual(3, len(observed)) def test_get_bin_paths__unsupported_raises(self): @@ -359,39 +378,47 @@ def test_get_bin_paths__unsupported_raises(self): file_path = self.last_check = os.path.join(TEST_PATH, "bacon.zip") with self.assertRaises(PyXFormError) as ctx: self.updater._get_bin_paths( - update_info=self.update_info, file_path=file_path) + update_info=self.update_info, file_path=file_path + ) self.assertIn("Did not find", unicode(ctx.exception)) def test_unzip_find_zip_jobs__ok_real_current(self): """Should return a list of zip jobs same length as search.""" - with get_temp_dir() as temp_dir, \ - ZipFile(self.zip_file, mode="r") as zip_file: + with get_temp_dir() as temp_dir, ZipFile(self.zip_file, mode="r") as zip_file: bin_paths = self.updater._get_bin_paths( - update_info=self.update_info, file_path=self.zip_file) + update_info=self.update_info, file_path=self.zip_file + ) jobs = self.updater._unzip_find_jobs( - open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir) + open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir + ) self.assertEqual(3, len(jobs.keys())) self.assertTrue(list(jobs.keys())[0].startswith(temp_dir)) def test_unzip_find_zip_jobs__ok_real_ideal(self): """Should return a list of zip jobs same length as search.""" - with get_temp_dir() as temp_dir, \ - ZipFile(self.zip_file_ideal, mode="r") as zip_file: + with get_temp_dir() as temp_dir, ZipFile( + self.zip_file_ideal, mode="r" + ) as zip_file: bin_paths = self.updater._get_bin_paths( - update_info=self.update_info, file_path=self.zip_file_ideal) + update_info=self.update_info, file_path=self.zip_file_ideal + ) jobs = self.updater._unzip_find_jobs( - open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir) + open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir + ) self.assertEqual(3, len(jobs.keys())) self.assertTrue(list(jobs.keys())[0].startswith(temp_dir)) def test_unzip_find_zip_jobs__ok_real_dupes(self): """Should return a list of zip jobs same length as search.""" - with get_temp_dir() as temp_dir, \ - ZipFile(self.zip_file_dupes, mode="r") as zip_file: + with get_temp_dir() as temp_dir, ZipFile( + self.zip_file_dupes, mode="r" + ) as zip_file: bin_paths = self.updater._get_bin_paths( - update_info=self.update_info, file_path=self.zip_file_dupes) + update_info=self.update_info, file_path=self.zip_file_dupes + ) jobs = self.updater._unzip_find_jobs( - open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir) + open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir + ) self.assertEqual(3, len(jobs.keys())) self.assertTrue(list(jobs.keys())[0].startswith(temp_dir)) @@ -399,49 +426,48 @@ def test_unzip_find_zip_jobs__not_found_raises(self): """Should raise an error if zip jobs isn't same length as search.""" bin_paths = [(".non_existent", ".non_existent")] - with get_temp_dir() as temp_dir, \ - ZipFile(self.zip_file, mode="r") as zip_file, \ - self.assertRaises(PyXFormError) as ctx: + with get_temp_dir() as temp_dir, ZipFile( + self.zip_file, mode="r" + ) as zip_file, self.assertRaises(PyXFormError) as ctx: self.updater._unzip_find_jobs( - open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir) + open_zip_file=zip_file, bin_paths=bin_paths, out_path=temp_dir + ) self.assertIn("1 zip job files, found: 0", unicode(ctx.exception)) def test_unzip_extract_file__ok(self): """Should extract the specified item to the target output path.""" - with get_temp_dir() as temp_dir, \ - ZipFile(self.zip_file, mode="r") as zip_file: + with get_temp_dir() as temp_dir, ZipFile(self.zip_file, mode="r") as zip_file: zip_item = zip_file.infolist()[0] file_out_path = os.path.join(temp_dir, "validate") self.updater._unzip_extract_file( - open_zip_file=zip_file, - zip_item=zip_item, - file_out_path=file_out_path) + open_zip_file=zip_file, zip_item=zip_item, file_out_path=file_out_path + ) self.assertTrue(os.path.exists(file_out_path)) def test_unzip_extract_file__bad_crc_raises(self): """Should raise an error if the zip file CRC doesn't match.""" - with get_temp_dir() as temp_dir, \ - ZipFile(self.zip_file, mode="r") as zip_file,\ - self.assertRaises(BadZipFile) as ctx: - zip_item = [x for x in zip_file.infolist() - if x.filename.endswith("validate")][0] + with get_temp_dir() as temp_dir, ZipFile( + self.zip_file, mode="r" + ) as zip_file, self.assertRaises(BadZipFile) as ctx: + zip_item = [ + x for x in zip_file.infolist() if x.filename.endswith("validate") + ][0] zip_item.CRC = 12345 file_out_path = os.path.join(temp_dir, "validate") self.updater._unzip_extract_file( - open_zip_file=zip_file, - zip_item=zip_item, - file_out_path=file_out_path) + open_zip_file=zip_file, zip_item=zip_item, file_out_path=file_out_path + ) self.assertIn("Bad CRC-32 for file", unicode(ctx.exception)) def test_unzip(self): """Should unzip the file to the locations in the bin_path map.""" with get_temp_dir() as temp_dir: self.updater._unzip( - update_info=self.update_info, - file_path=self.zip_file, - out_path=temp_dir) - dir_list = [os.path.join(r, f) - for r, _, fs in os.walk(temp_dir) for f in fs] + update_info=self.update_info, file_path=self.zip_file, out_path=temp_dir + ) + dir_list = [ + os.path.join(r, f) for r, _, fs in os.walk(temp_dir) for f in fs + ] self.assertEqual(3, len(dir_list)) def test_install__ok(self): @@ -450,15 +476,15 @@ def test_install__ok(self): new = self.utc_now - timedelta(minutes=15.0) with get_temp_file() as temp_check, get_temp_dir() as temp_dir: - self.updater._write_last_check( - file_path=temp_check, content=new) + self.updater._write_last_check(file_path=temp_check, content=new) self.update_info.last_check_path = temp_check self.update_info.bin_new_path = temp_dir installed = self.updater._install( - update_info=self.update_info, - file_name="linux.zip") - dir_list = [os.path.join(r, f) - for r, _, fs in os.walk(temp_dir) for f in fs] + update_info=self.update_info, file_name="linux.zip" + ) + dir_list = [ + os.path.join(r, f) for r, _, fs in os.walk(temp_dir) for f in fs + ] self.assertEqual(5, len(dir_list)) latest = self.updater._read_json(file_path=self.install_fake) @@ -471,15 +497,11 @@ def test_install__add_executable_mode(self): new = self.utc_now - timedelta(minutes=15.0) with get_temp_file() as temp_check, get_temp_dir() as temp_dir: - self.updater._write_last_check( - file_path=temp_check, content=new) + self.updater._write_last_check(file_path=temp_check, content=new) self.update_info.last_check_path = temp_check self.update_info.bin_new_path = temp_dir - self.updater._install( - update_info=self.update_info, - file_name="linux.zip") - bin_new = os.path.join( - temp_dir, self.update_info.validator_basename) + self.updater._install(update_info=self.update_info, file_name="linux.zip") + bin_new = os.path.join(temp_dir, self.update_info.validator_basename) bin_new_stat_mode = os.stat(bin_new).st_mode self.assertEqual(bin_new_stat_mode & S_IXUSR, S_IXUSR) self.assertEqual(bin_new_stat_mode & S_IXGRP, S_IXGRP) @@ -505,7 +527,8 @@ def test_update__not_installed__ok(self): update_info = get_update_info(check_ok=True, mod_root=mod_root) update_info.latest_path = self.install_fake self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) expected_path = os.path.join(update_info.bin_path, "validate") self.assertFalse(os.path.exists(expected_path)) @@ -523,7 +546,8 @@ def test_update__not_installed__fail__install_check(self): update_info = get_update_info(check_ok=False, mod_root=mod_root) update_info.latest_path = self.install_fake self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) self.assertFalse(os.path.exists(update_info.bin_path)) self.updater.update(update_info=update_info, file_name="linux.zip") @@ -542,7 +566,8 @@ def test_update__installed__ok(self): update_info = get_update_info(check_ok=True, mod_root=mod_root) update_info.latest_path = self.install_fake_old self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) self.updater.update(update_info=update_info, file_name="linux.zip") update_info.latest_path = self.install_fake @@ -560,7 +585,8 @@ def test_update__installed__fail__already_latest(self): update_info = get_update_info(check_ok=True, mod_root=mod_root) update_info.latest_path = self.install_fake self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) self.updater.update(update_info=update_info, file_name="linux.zip") update_info.latest_path = self.install_fake @@ -578,7 +604,8 @@ def test_update__installed__fail__install_check(self): update_info = get_update_info(check_ok=False, mod_root=mod_root) update_info.latest_path = self.install_fake self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) self.updater.update(update_info=update_info, file_name="linux.zip") update_info.latest_path = self.install_fake @@ -609,7 +636,8 @@ def test_check__ok(self): update_info = get_update_info(check_ok=True, mod_root=mod_root) update_info.latest_path = self.install_fake_old self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) self.updater.update(update_info=update_info, file_name="linux.zip") self.updater.check(update_info=update_info) @@ -626,7 +654,8 @@ def test_check__fail__install_check(self): update_info = get_update_info(check_ok=True, mod_root=mod_root) update_info.latest_path = self.install_fake_old self.updater._write_last_check( - file_path=update_info.last_check_path, content=new) + file_path=update_info.last_check_path, content=new + ) self.updater.update(update_info=update_info, file_name="linux.zip") update_info.install_check = install_check_fail diff --git a/pyxform/tests/test_validator_util.py b/pyxform/tests/test_validator_util.py index 2c2028c0d..841a5920a 100644 --- a/pyxform/tests/test_validator_util.py +++ b/pyxform/tests/test_validator_util.py @@ -1,5 +1,11 @@ +# -*- coding: utf-8 -*- +""" +Test pyxform.validators.utils module. +""" import os + from unittest2 import TestCase + from pyxform.tests.utils import prep_class_config from pyxform.validators.error_cleaner import ErrorCleaner from pyxform.validators.util import XFORM_SPEC_PATH, check_readable @@ -13,44 +19,45 @@ def setUpClass(cls): prep_class_config(cls=cls) def test_cleanup_error_message(self): - test_str = self.config.get( - self.cls_name, "test_cleanup_error_message_test") + test_str = self.config.get(self.cls_name, "test_cleanup_error_message_test") expected_str = self.config.get( - self.cls_name, "test_cleanup_error_message_expected") - self.assertEqual( - ErrorCleaner.odk_validate(test_str), expected_str.strip()) + self.cls_name, "test_cleanup_error_message_expected" + ) + self.assertEqual(ErrorCleaner.odk_validate(test_str), expected_str.strip()) def test_do_not_over_trim_javarosa_errors(self): test_str = self.config.get( - self.cls_name, "test_do_not_over_trim_javarosa_errors_test") + self.cls_name, "test_do_not_over_trim_javarosa_errors_test" + ) # Unescape tabs in string test_str = test_str.replace("\n\\t", "\n\t") expected_str = self.config.get( - self.cls_name, "test_do_not_over_trim_javarosa_errors_expected") - self.assertEqual( - ErrorCleaner.odk_validate(test_str), expected_str.strip()) + self.cls_name, "test_do_not_over_trim_javarosa_errors_expected" + ) + self.assertEqual(ErrorCleaner.odk_validate(test_str), expected_str.strip()) def test_single_line_error_still_output(self): """Should emit errors that are a single line of text.""" test_str = self.config.get( - self.cls_name, "test_single_line_error_still_output_test") + self.cls_name, "test_single_line_error_still_output_test" + ) expected_str = self.config.get( - self.cls_name, "test_single_line_error_still_output_expected") - self.assertEqual( - ErrorCleaner.odk_validate(test_str), expected_str.strip()) + self.cls_name, "test_single_line_error_still_output_expected" + ) + self.assertEqual(ErrorCleaner.odk_validate(test_str), expected_str.strip()) def test_jarfile_error_returned_asis(self): """Should return a jarfile error as-is, to avoid tokenising the path.""" test_str = self.config.get( - self.cls_name, "test_jarfile_error_returned_asis_test") + self.cls_name, "test_jarfile_error_returned_asis_test" + ) expected_str = self.config.get( - self.cls_name, "test_jarfile_error_returned_asis_expected") - self.assertEqual( - ErrorCleaner.odk_validate(test_str), expected_str.strip()) + self.cls_name, "test_jarfile_error_returned_asis_expected" + ) + self.assertEqual(ErrorCleaner.odk_validate(test_str), expected_str.strip()) class TestCheckReadable(TestCase): - def test_check_readable__real_file_ok(self): """Should return True if the file is readable.""" self.assertTrue(check_readable(file_path=XFORM_SPEC_PATH)) diff --git a/pyxform/tests/tests_by_file.py b/pyxform/tests/tests_by_file.py index ac6a0ad14..507c4c99b 100644 --- a/pyxform/tests/tests_by_file.py +++ b/pyxform/tests/tests_by_file.py @@ -1,48 +1,52 @@ +# -*- coding: utf-8 -*- """ Tests by file. Runs through a list of *.xls files, and expects that the output for a *.xml with a matching prefix before the . is as expected. Possibly risky: all tests in this file are defined according to matching files. """ +import codecs +import os +import sys import xml.etree.ElementTree as ETree -from formencode.doctest_xml_compare import xml_compare from unittest import TestCase + +from formencode.doctest_xml_compare import xml_compare + import pyxform from pyxform import xls2json -import os from pyxform.tests import utils -import codecs -import sys class MainTest(TestCase): - def runTest(self): files_to_test = ["instance_xmlns_test.xls"] for file_to_test in files_to_test: path_to_excel_file = utils.path_to_text_fixture(file_to_test) - + # Get the xform output path: directory, filename = os.path.split(path_to_excel_file) root_filename, ext = os.path.splitext(filename) path_to_output_xform = os.path.join( - directory, root_filename + "_output.xml") - path_to_expected_xform = os.path.join( - directory, root_filename + ".xml") + directory, root_filename + "_output.xml" + ) + path_to_expected_xform = os.path.join(directory, root_filename + ".xml") # Do the conversion: json_survey = xls2json.parse_file_to_json(path_to_excel_file) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(path_to_output_xform) - + # Compare with the expected output: - with codecs.open(path_to_expected_xform, 'rb', encoding="utf-8" - ) as expected_file: + with codecs.open( + path_to_expected_xform, "rb", encoding="utf-8" + ) as expected_file: expected = ETree.fromstring(expected_file.read()) result = ETree.fromstring(survey.to_xml()) - def write_line(x): sys.stdout.write(x + "\n") + def write_line(x): + sys.stdout.write(x + "\n") + reporter = write_line - self.assertTrue(xml_compare( - expected, result, reporter=reporter)) + self.assertTrue(xml_compare(expected, result, reporter=reporter)) os.remove(path_to_output_xform) diff --git a/pyxform/tests/tutorial_test.py b/pyxform/tests/tutorial_test.py index cb3b521ef..216dabb51 100644 --- a/pyxform/tests/tutorial_test.py +++ b/pyxform/tests/tutorial_test.py @@ -1,4 +1,9 @@ +# -*- coding: utf-8 -*- +""" +Test tutorial XLSForm. +""" from unittest import TestCase + from pyxform.builder import create_survey_from_path from pyxform.tests import utils @@ -6,5 +11,4 @@ class TutorialTests(TestCase): def test_create_from_path(self): path = utils.path_to_text_fixture("tutorial.xls") - survey = create_survey_from_path(path) - # print survey.to_xml() + create_survey_from_path(path) diff --git a/pyxform/tests/utils.py b/pyxform/tests/utils.py index 3510a084e..32eed2caf 100644 --- a/pyxform/tests/utils.py +++ b/pyxform/tests/utils.py @@ -1,11 +1,19 @@ +# -*- coding: utf-8 -*- +""" +The tests utils module functionality. +""" import os -from pyxform import file_utils -from pyxform.builder import create_survey, create_survey_from_path -from unittest2 import TestCase import xml.etree.ElementTree as ETree + from formencode.doctest_xml_compare import xml_compare +from unittest2 import TestCase + +from pyxform import file_utils +from pyxform.builder import create_survey, create_survey_from_path + try: import ConfigParser + configparser = ConfigParser except ImportError: import configparser @@ -23,15 +31,13 @@ def build_survey(filename): return create_survey_from_path(path) -def create_survey_from_fixture(fixture_name, filetype="xls", - include_directory=False): +def create_survey_from_fixture(fixture_name, filetype="xls", include_directory=False): fixture_path = path_to_text_fixture("%s.%s" % (fixture_name, filetype)) noop, section_dict = file_utils.load_file_to_dict(fixture_path) - pkg = {u'main_section': section_dict} + pkg = {"main_section": section_dict} if include_directory: directory, noop = os.path.split(fixture_path) - pkg[u'sections'] = file_utils.collect_compatible_files_in_directory( - directory) + pkg["sections"] = file_utils.collect_compatible_files_in_directory(directory) return create_survey(**pkg) @@ -55,8 +61,8 @@ def get_file_path(self, filename): self.output_path = os.path.join(DIR, "test_output", self.root_filename + ".xml") def assertXFormEqual(self, xform1, xform2): - xform1 = ETree.fromstring(xform1.encode('utf-8')) - xform2 = ETree.fromstring(xform2.encode('utf-8')) + xform1 = ETree.fromstring(xform1.encode("utf-8")) + xform2 = ETree.fromstring(xform2.encode("utf-8")) # Sort tags under section in each form self.sort_model(xform1) @@ -69,44 +75,49 @@ def reporter(msg): errs.append(msg) self.assertTrue( - xml_compare(xform1, xform2, reporter), - "\n\n" + "\n".join(reversed(errs)) + xml_compare(xform1, xform2, reporter), "\n\n" + "\n".join(reversed(errs)) ) def sort_elems(self, elems, attr=None): if attr: - def elem_get_attr(elem): return elem.get(attr, '') + + def elem_get_attr(elem): + return elem.get(attr, "") + key = elem_get_attr else: - def elem_get_tag(elem): return elem.tag + + def elem_get_tag(elem): + return elem.tag + key = elem_get_tag elems[:] = sorted(elems, key=key) def sort_model(self, xform): ns = "{http://www.w3.org/2002/xforms}" - model = xform.find('.//' + ns + 'model') + model = xform.find(".//" + ns + "model") # Sort multiple tags, if any - self.sort_elems(model, 'id') + self.sort_elems(model, "id") # Sort any tags in each - for instance in model.findall(ns + 'instance'): - if instance.get('id', None): - root = instance.find(ns + 'root') + for instance in model.findall(ns + "instance"): + if instance.get("id", None): + root = instance.find(ns + "root") if root is not None: for item in root: self.sort_elems(item) # Sort tags as well as inner and tags. - itext = model.find(ns + 'itext') + itext = model.find(ns + "itext") if itext is None: return - self.sort_elems(itext, 'lang') + self.sort_elems(itext, "lang") for translation in itext: - self.sort_elems(translation, 'id') + self.sort_elems(translation, "id") for text in translation: - self.sort_elems(text, 'form') + self.sort_elems(text, "form") def prep_class_config(cls, test_dir="tests"): diff --git a/pyxform/tests/validators/__init__.py b/pyxform/tests/validators/__init__.py index 1b011eb96..abbc7fd26 100644 --- a/pyxform/tests/validators/__init__.py +++ b/pyxform/tests/validators/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import os HERE = os.path.dirname(__file__) diff --git a/pyxform/tests/validators/server.py b/pyxform/tests/validators/server.py index a3e0d8fa7..6ff4b9b08 100644 --- a/pyxform/tests/validators/server.py +++ b/pyxform/tests/validators/server.py @@ -1,6 +1,8 @@ +# -*- coding: utf-8 -*- import os import posixpath import threading + try: from http.server import SimpleHTTPRequestHandler from socketserver import ThreadingTCPServer @@ -15,11 +17,11 @@ class SimpleHTTPRequestHandlerHere(SimpleHTTPRequestHandler, object): - def send_head(self): if self.client_address[0] != "127.0.0.1": - self.send_error(401, "Unauthorized", - "No permission -- see authorization schemes") + self.send_error( + 401, "Unauthorized", "No permission -- see authorization schemes" + ) return None else: return super(SimpleHTTPRequestHandlerHere, self).send_head() @@ -72,7 +74,8 @@ def __init__(self, port=8000): self._server_address = ("127.0.0.1", port) self._handler = SimpleHTTPRequestHandlerHere self.httpd = ThreadingTCPServer( - self._server_address, self._handler, bind_and_activate=False) + self._server_address, self._handler, bind_and_activate=False + ) def _bind_and_activate(self): try: diff --git a/pyxform/tests/xform2json_test.py b/pyxform/tests/xform2json_test.py index 35873265f..5af0eaa5f 100644 --- a/pyxform/tests/xform2json_test.py +++ b/pyxform/tests/xform2json_test.py @@ -1,11 +1,16 @@ -from __future__ import print_function -from pyxform.builder import create_survey_from_path -from pyxform.xform2json import create_survey_element_from_xml, _try_parse +# -*- coding: utf-8 -*- +""" +Test xform2json module. +""" import os -from pyxform.tests import utils -from unittest2 import TestCase from xml.etree.ElementTree import ParseError +from unittest2 import TestCase + +from pyxform.builder import create_survey_from_path +from pyxform.tests import utils +from pyxform.xform2json import _try_parse, create_survey_element_from_xml + class DumpAndLoadXForm2JsonTests(utils.XFormTestCase): @@ -31,18 +36,13 @@ def setUp(self): self.this_directory = os.path.dirname(__file__) for filename in self.excel_files: path = utils.path_to_text_fixture(filename) - try: - self.surveys[filename] = create_survey_from_path(path) - except Exception as e: - print("Error on : " + filename) - raise e + self.surveys[filename] = create_survey_from_path(path) def test_load_from_dump(self): for filename, survey in iter(self.surveys.items()): survey.json_dump() survey_from_dump = create_survey_element_from_xml(survey.to_xml()) - self.assertXFormEqual( - survey.to_xml(), survey_from_dump.to_xml()) + self.assertXFormEqual(survey.to_xml(), survey_from_dump.to_xml()) def tearDown(self): for filename, survey in self.surveys.items(): @@ -52,7 +52,6 @@ def tearDown(self): class TestXMLParse(TestCase): - @classmethod def setUpClass(cls): cls.cwd = os.path.dirname(__file__) diff --git a/pyxform/tests/xls2json_tests.py b/pyxform/tests/xls2json_tests.py index 2119b5fa5..09b723fd6 100644 --- a/pyxform/tests/xls2json_tests.py +++ b/pyxform/tests/xls2json_tests.py @@ -1,15 +1,16 @@ +# -*- coding: utf-8 -*- """ Testing simple cases for Xls2Json """ import codecs +import json import os -import json from unittest2 import TestCase from pyxform.tests import utils from pyxform.xls2json import SurveyReader, parse_file_to_json -from pyxform.xls2json_backends import xls_to_dict, csv_to_dict +from pyxform.xls2json_backends import csv_to_dict, xls_to_dict # Nothing calls this AFAICT @@ -30,17 +31,16 @@ def test_simple_yes_or_no_question(self): # Get the xform output path: root_filename, ext = os.path.splitext(filename) output_path = os.path.join(DIR, "test_output", root_filename + ".json") - expected_output_path = os.path.join(DIR, "test_expected_output", - root_filename + ".json") + expected_output_path = os.path.join( + DIR, "test_expected_output", root_filename + ".json" + ) x = SurveyReader(path_to_excel_file) x_results = x.to_json_dict() with codecs.open(output_path, mode="w", encoding="utf-8") as fp: json.dump(x_results, fp=fp, ensure_ascii=False, indent=4) # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', - encoding="utf-8") as expected_file: - with codecs.open(output_path, 'rb', - encoding="utf-8") as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(output_path, "rb", encoding="utf-8") as actual_file: expected_json = json.load(expected_file) actual_json = json.load(actual_file) self.assertEqual(expected_json, actual_json) @@ -50,94 +50,81 @@ def test_hidden(self): x_results = x.to_json_dict() expected_dict = [ + {"type": "hidden", "name": "hidden_test"}, { - u'type': u'hidden', - u'name': u'hidden_test' - }, - { - 'children': [ + "children": [ { - 'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - 'name': 'instanceID', - 'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - 'control': { - 'bodyless': True - }, - 'name': 'meta', - 'type': 'group' - } + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, ] - self.assertEqual(x_results[u"children"], expected_dict) + self.assertEqual(x_results["children"], expected_dict) def test_gps(self): x = SurveyReader(utils.path_to_text_fixture("gps.xls")) expected_dict = [ + {"type": "gps", "name": "location", "label": "GPS"}, { - u'type': u'gps', u'name': u'location', u'label': u'GPS'}, - { - 'children': [ + "children": [ { - 'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - 'name': 'instanceID', - 'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - 'control': { - 'bodyless': True - }, - 'name': 'meta', - 'type': 'group' - }] + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, + ] - self.assertEqual(x.to_json_dict()[u"children"], expected_dict) + self.assertEqual(x.to_json_dict()["children"], expected_dict) def test_text_and_integer(self): x = SurveyReader(utils.path_to_text_fixture("text_and_integer.xls")) expected_dict = [ { - u'label': { - u'english': u'What is your name?' - }, - u'type': u'text', - u'name': u'your_name' + "label": {"english": "What is your name?"}, + "type": "text", + "name": "your_name", }, { - u'label': { - u'english': u'How many years old are you?' - }, - u'type': u'integer', - u'name': u'your_age' + "label": {"english": "How many years old are you?"}, + "type": "integer", + "name": "your_age", }, { - u'children': [ + "children": [ { - u'bind': { - 'calculate': "concat('uuid:', uuid())", - 'readonly': 'true()' + "bind": { + "calculate": "concat('uuid:', uuid())", + "readonly": "true()", }, - u'name': 'instanceID', - u'type': 'calculate' + "name": "instanceID", + "type": "calculate", } ], - u'control': { - 'bodyless': True - }, - u'name': 'meta', - u'type': u'group' - } + "control": {"bodyless": True}, + "name": "meta", + "type": "group", + }, ] - self.assertEqual(x.to_json_dict()[u"children"], expected_dict) + self.assertEqual(x.to_json_dict()["children"], expected_dict) def test_table(self): filename = "simple_loop.xls" @@ -145,17 +132,16 @@ def test_table(self): # Get the xform output path: root_filename, ext = os.path.splitext(filename) output_path = os.path.join(DIR, "test_output", root_filename + ".json") - expected_output_path = os.path.join(DIR, "test_expected_output", - root_filename + ".json") + expected_output_path = os.path.join( + DIR, "test_expected_output", root_filename + ".json" + ) x = SurveyReader(path_to_excel_file) x_results = x.to_json_dict() with codecs.open(output_path, mode="w", encoding="utf-8") as fp: json.dump(x_results, fp=fp, ensure_ascii=False, indent=4) # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', - encoding="utf-8") as expected_file: - with codecs.open(output_path, 'rb', - encoding="utf-8") as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(output_path, "rb", encoding="utf-8") as actual_file: expected_json = json.load(expected_file) actual_json = json.load(actual_file) self.assertEqual(expected_json, actual_json) @@ -165,145 +151,95 @@ def test_choice_filter_choice_fields(self): Test that the choice filter fields appear on children field of json """ choice_filter_survey = SurveyReader( - utils.path_to_text_fixture("choice_filter_test.xlsx")) + utils.path_to_text_fixture("choice_filter_test.xlsx") + ) expected_dict = [ { - u'choices': [ - { - u'name': u'texas', - u'label': u'Texas' - }, - { - u'name': u'washington', - u'label': u'Washington' - } + "choices": [ + {"name": "texas", "label": "Texas"}, + {"name": "washington", "label": "Washington"}, ], - u'type': u'select one', - u'name': u'state', - u'parameters': {}, - u'label': u'state' + "type": "select one", + "name": "state", + "parameters": {}, + "label": "state", }, { - u'name': u'county', - u'parameters': {}, - u'choice_filter': u'${state}=cf', - u'label': u'county', - u'itemset': u'counties', - u'choices': [ - { - u'label': u'King', - u'cf': u'washington', - u'name': u'king' - }, - { - u'label': u'Pierce', - u'cf': u'washington', - u'name': u'pierce' - }, - { - u'label': u'King', - u'cf': u'texas', - u'name': u'king' - }, - { - u'label': u'Cameron', - u'cf': u'texas', - u'name': u'cameron' - } + "name": "county", + "parameters": {}, + "choice_filter": "${state}=cf", + "label": "county", + "itemset": "counties", + "choices": [ + {"label": "King", "cf": "washington", "name": "king"}, + {"label": "Pierce", "cf": "washington", "name": "pierce"}, + {"label": "King", "cf": "texas", "name": "king"}, + {"label": "Cameron", "cf": "texas", "name": "cameron"}, ], - u'type': u'select one' + "type": "select one", }, { - u'name': u'city', - u'parameters': {}, - u'choice_filter': u'${county}=cf', - u'label': u'city', - u'itemset': u'cities', - u'choices': [ - { - u'label': u'Dumont', - u'cf': u'king', - u'name': u'dumont' - }, - { - u'label': u'Finney', - u'cf': u'king', - u'name': u'finney' - }, - { - u'label': u'brownsville', - u'cf': u'cameron', - u'name': u'brownsville' - }, - { - u'label': u'harlingen', - u'cf': u'cameron', - u'name': u'harlingen' - }, - { - u'label': u'Seattle', - u'cf': u'king', - u'name': u'seattle' - }, - { - u'label': u'Redmond', - u'cf': u'king', - u'name': u'redmond' - }, - { - u'label': u'Tacoma', - u'cf': u'pierce', - u'name': u'tacoma' - }, - { - u'label': u'Puyallup', - u'cf': u'pierce', - u'name': u'puyallup' - } + "name": "city", + "parameters": {}, + "choice_filter": "${county}=cf", + "label": "city", + "itemset": "cities", + "choices": [ + {"label": "Dumont", "cf": "king", "name": "dumont"}, + {"label": "Finney", "cf": "king", "name": "finney"}, + {"label": "brownsville", "cf": "cameron", "name": "brownsville"}, + {"label": "harlingen", "cf": "cameron", "name": "harlingen"}, + {"label": "Seattle", "cf": "king", "name": "seattle"}, + {"label": "Redmond", "cf": "king", "name": "redmond"}, + {"label": "Tacoma", "cf": "pierce", "name": "tacoma"}, + {"label": "Puyallup", "cf": "pierce", "name": "puyallup"}, ], - u'type': u'select one' + "type": "select one", }, { - u'control': { - u'bodyless': True - }, - u'type': u'group', - u'name': u'meta', - u'children': [ + "control": {"bodyless": True}, + "type": "group", + "name": "meta", + "children": [ { - u'bind': { - u'readonly': u'true()', - u'calculate': u"concat('uuid:', uuid())" + "bind": { + "readonly": "true()", + "calculate": "concat('uuid:', uuid())", }, - u'type': u'calculate', - u'name': u'instanceID' + "type": "calculate", + "name": "instanceID", } - ] - } + ], + }, ] - self.assertEqual( - choice_filter_survey.to_json_dict()[u"children"], expected_dict) + self.assertEqual(choice_filter_survey.to_json_dict()["children"], expected_dict) def test_underscore_warnings(self): """Raise warnings incase there are underscores in column names""" warnings = [] - parse_file_to_json( - utils.path_to_text_fixture("hidden.xls"), - warnings=warnings) + parse_file_to_json(utils.path_to_text_fixture("hidden.xls"), warnings=warnings) self.assertGreater(len(warnings), 0) - warning = "Google Sheets submissions don't allow underscores in the " \ - "column name. If you intend to use Google Sheets " \ - "submissions, replace underscores with hyphens in the " \ - "following names: hidden_test" + warning = ( + "Google Sheets submissions don't allow underscores in the " + "column name. If you intend to use Google Sheets " + "submissions, replace underscores with hyphens in the " + "following names: hidden_test" + ) self.assertIn(warning, warnings) class CsvReaderEquivalencyTest(TestCase): def test_equivalency(self): - equivalent_fixtures = ['group', 'loop', # 'gps', - 'specify_other', 'include', 'text_and_integer', - 'include_json', 'yes_or_no_question'] + equivalent_fixtures = [ + "group", + "loop", # 'gps', + "specify_other", + "include", + "text_and_integer", + "include_json", + "yes_or_no_question", + ] for fixture in equivalent_fixtures: xls_path = utils.path_to_text_fixture("%s.xls" % fixture) csv_path = utils.path_to_text_fixture("%s.csv" % fixture) diff --git a/pyxform/tests/xls2xform_tests.py b/pyxform/tests/xls2xform_tests.py index 1e69d7386..2586ce067 100644 --- a/pyxform/tests/xls2xform_tests.py +++ b/pyxform/tests/xls2xform_tests.py @@ -1,34 +1,41 @@ +# -*- coding: utf-8 -*- +""" +Test xls2xform module. +""" # The Django application xls2xform uses the function # pyxform.create_survey. We have a test here to make sure no one # breaks that function. +import argparse from unittest import TestCase -import pyxform, argparse -from pyxform.xls2xform import _create_parser,\ - _validator_args_logic, main_cli, get_xml_path + +import pyxform +from pyxform.xls2xform import ( + _create_parser, + _validator_args_logic, + get_xml_path, + main_cli, +) + try: from unittest import mock except ImportError: import mock + class XLS2XFormTests(TestCase): survey_package = { - 'id_string': u'test_2011_08_29b', - 'name_of_main_section': u'gps', - 'sections': { - u'gps': { - u'children': [ - { - u'name': u'location', - u'type': u'gps' - } - ], - u'name': u'gps', - u'type': u'survey' - } - }, - 'title': u'test' - } + "id_string": "test_2011_08_29b", + "name_of_main_section": "gps", + "sections": { + "gps": { + "children": [{"name": "location", "type": "gps"}], + "name": "gps", + "type": "survey", + } + }, + "title": "test", + } survey = pyxform.create_survey(**survey_package) def test_create_parser_without_args(self): @@ -42,16 +49,21 @@ def test_create_parser_optional_output_path(self): path to the xlsx file path, while the output path is left out """ try: - _create_parser().parse_args(['/some/path/tofile.xlsx']) + _create_parser().parse_args(["/some/path/tofile.xlsx"]) except SystemExit: self.fail() def test_create_parser_with_args(self): """Should parse the provided arguments.""" - arg_xlsform = 'xlsform.xlsx' - arg_output = '.' - arg_list = ['--json', '--skip_validate', '--no_pretty_print', - arg_xlsform, arg_output] + arg_xlsform = "xlsform.xlsx" + arg_output = "." + arg_list = [ + "--json", + "--skip_validate", + "--no_pretty_print", + arg_xlsform, + arg_output, + ] args = _create_parser().parse_args(arg_list) self.assertEqual(arg_xlsform, args.path_to_XLSForm) self.assertEqual(arg_output, args.output_path) @@ -61,155 +73,160 @@ def test_create_parser_with_args(self): def test_create_parser_file_name_with_space(self): """Should interpret the path correctly.""" - arg_xlsform = 'some/path/my xlsform.xlsx' - arg_output = '.' + arg_xlsform = "some/path/my xlsform.xlsx" + arg_output = "." arg_list = [arg_xlsform, arg_output] args = _create_parser().parse_args(arg_list) self.assertEqual(arg_xlsform, args.path_to_XLSForm) def test_create_parser_json_default_false(self): """Should have json=False if not specified.""" - arg_xlsform = 'xlsform.xlsx' - arg_output = '.' + arg_xlsform = "xlsform.xlsx" + arg_output = "." arg_list = [arg_xlsform, arg_output] args = _create_parser().parse_args(arg_list) self.assertEqual(False, args.json) def test_create_parser_skip_validate_default_true(self): """Should have skip_validate=True if not specified.""" - arg_xlsform = 'xlsform.xlsx' - arg_output = '.' + arg_xlsform = "xlsform.xlsx" + arg_output = "." arg_list = [arg_xlsform, arg_output] args = _create_parser().parse_args(arg_list) self.assertEqual(True, args.skip_validate) def test_create_parser_no_enketo_default_false(self): """Should have enketo_validate=False if not specified.""" - arg_xlsform = 'xlsform.xlsx' - arg_output = '.' + arg_xlsform = "xlsform.xlsx" + arg_output = "." arg_list = [arg_xlsform, arg_output] args = _create_parser().parse_args(arg_list) self.assertEqual(False, args.enketo_validate) def test_create_parser_no_pretty_print_default_true(self): """Should have no_pretty_print=True if not specified.""" - args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - ]) + args = _create_parser().parse_args(["xlsform.xlsx", "."]) self.assertEqual(True, args.no_pretty_print) def test_validator_args_logic_skip_validate_alone(self): """Should deactivate both validators.""" - raw_args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - '--skip_validate' - ]) + raw_args = _create_parser().parse_args(["xlsform.xlsx", ".", "--skip_validate"]) args = _validator_args_logic(args=raw_args) self.assertEqual(False, args.odk_validate) self.assertEqual(False, args.enketo_validate) def test_validator_args_logic_odk_default(self): """Should activate ODK only.""" - raw_args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - ]) + raw_args = _create_parser().parse_args(["xlsform.xlsx", "."]) args = _validator_args_logic(args=raw_args) self.assertEqual(True, args.odk_validate) self.assertEqual(False, args.enketo_validate) def test_validator_args_logic_enketo_only(self): """Should activate Enketo only.""" - raw_args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - '--enketo_validate' - ]) + raw_args = _create_parser().parse_args( + ["xlsform.xlsx", ".", "--enketo_validate"] + ) args = _validator_args_logic(args=raw_args) self.assertEqual(False, args.odk_validate) self.assertEqual(True, args.enketo_validate) def test_validator_args_logic_odk_only(self): """Should activate ODK only.""" - raw_args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - '--odk_validate' - ]) + raw_args = _create_parser().parse_args(["xlsform.xlsx", ".", "--odk_validate"]) args = _validator_args_logic(args=raw_args) self.assertEqual(True, args.odk_validate) self.assertEqual(False, args.enketo_validate) def test_validator_args_logic_odk_and_enketo(self): """Should activate ODK and Enketo.""" - raw_args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - '--odk_validate', - '--enketo_validate' - ]) + raw_args = _create_parser().parse_args( + ["xlsform.xlsx", ".", "--odk_validate", "--enketo_validate"] + ) args = _validator_args_logic(args=raw_args) self.assertEqual(True, args.odk_validate) self.assertEqual(True, args.enketo_validate) def test_validator_args_logic_skip_validate_override(self): """Should deactivate both validators""" - raw_args = _create_parser().parse_args([ - 'xlsform.xlsx', - '.', - '--skip_validate', - '--odk_validate', - '--enketo_validate', - ]) + raw_args = _create_parser().parse_args( + [ + "xlsform.xlsx", + ".", + "--skip_validate", + "--odk_validate", + "--enketo_validate", + ] + ) args = _validator_args_logic(args=raw_args) self.assertEqual(False, args.odk_validate) self.assertEqual(False, args.enketo_validate) - @mock.patch('argparse.ArgumentParser.parse_args', - return_value=argparse.Namespace(path_to_XLSForm='xlsform.xlsx', output_path=None, - json=False, skip_validate=False, odk_validate=False, - enketo_validate=False, no_pretty_print=False)) - @mock.patch('pyxform.xls2xform.xls2xform_convert') - def test_xls2form_convert_parameters( - self, converter_mock, parser_mock_args): + @mock.patch( + "argparse.ArgumentParser.parse_args", + return_value=argparse.Namespace( + path_to_XLSForm="xlsform.xlsx", + output_path=None, + json=False, + skip_validate=False, + odk_validate=False, + enketo_validate=False, + no_pretty_print=False, + ), + ) + @mock.patch("pyxform.xls2xform.xls2xform_convert") + def test_xls2form_convert_parameters(self, converter_mock, parser_mock_args): """ Checks that xls2xform_convert is given the right arguments, when the output-path is not given """ - converter_mock.return_value = '{}' + converter_mock.return_value = "{}" main_cli() converter_mock.assert_called_once_with( - xlsform_path='xlsform.xlsx', xform_path='xlsform.xml', - validate=False, pretty_print=False, - enketo=False) - - @mock.patch('argparse.ArgumentParser.parse_args', - return_value=argparse.Namespace(path_to_XLSForm='xlsform.xlsx', output_path=None, - json=True, skip_validate=False, odk_validate=False, - enketo_validate=False, no_pretty_print=False)) - @mock.patch('pyxform.xls2xform.xls2xform_convert') + xlsform_path="xlsform.xlsx", + xform_path="xlsform.xml", + validate=False, + pretty_print=False, + enketo=False, + ) + + @mock.patch( + "argparse.ArgumentParser.parse_args", + return_value=argparse.Namespace( + path_to_XLSForm="xlsform.xlsx", + output_path=None, + json=True, + skip_validate=False, + odk_validate=False, + enketo_validate=False, + no_pretty_print=False, + ), + ) + @mock.patch("pyxform.xls2xform.xls2xform_convert") def test_xls2xform_convert_params_with_flags( - self, converter_mock, parser_mock_args): + self, converter_mock, parser_mock_args + ): """ Should call xlsform_convert with the correct input for output path where only the xlsform input path and json flag were provided, since the xlsform-convert can be called if json flag was set or when not """ - converter_mock.return_value = '{}' + converter_mock.return_value = "{}" main_cli() converter_mock.assert_called_once_with( - xlsform_path='xlsform.xlsx', xform_path='xlsform.xml', - validate=False, pretty_print=False, - enketo=False) + xlsform_path="xlsform.xlsx", + xform_path="xlsform.xml", + validate=False, + pretty_print=False, + enketo=False, + ) def test_get_xml_path_function(self): """Should return an xml path in the same directory as the xlsx file""" - xlsx_path = '/home/user/Desktop/xlsform.xlsx' - expected = '/home/user/Desktop/xlsform.xml' + xlsx_path = "/home/user/Desktop/xlsform.xlsx" + expected = "/home/user/Desktop/xlsform.xml" assert expected == get_xml_path(xlsx_path) # check that it also handles spaced routes - xlsx_path = '/home/user/Desktop/my xlsform.xlsx' - expected = '/home/user/Desktop/my xlsform.xml' + xlsx_path = "/home/user/Desktop/my xlsform.xlsx" + expected = "/home/user/Desktop/my xlsform.xml" assert expected == get_xml_path(xlsx_path) diff --git a/pyxform/tests/xlsform_spec_test.py b/pyxform/tests/xlsform_spec_test.py index 37648643f..4e391f062 100644 --- a/pyxform/tests/xlsform_spec_test.py +++ b/pyxform/tests/xlsform_spec_test.py @@ -1,9 +1,12 @@ +# -*- coding: utf-8 -*- """ Some tests for the new (v0.9) spec is properly implemented. """ -import unittest2 as unittest import codecs import os + +import unittest2 as unittest + import pyxform from pyxform.errors import PyXFormError from pyxform.tests.utils import XFormTestCase @@ -17,21 +20,21 @@ class MainTest(XFormTestCase): def runTest(self): filename = "xlsform_spec_test.xlsx" self.get_file_path(filename) - expected_output_path = os.path.join(DIR, "test_expected_output", - self.root_filename + ".xml") + expected_output_path = os.path.join( + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") \ - as expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") \ - as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) @@ -42,20 +45,20 @@ def runTest(self): filename = "flat_xlsform_test.xlsx" self.get_file_path(filename) expected_output_path = os.path.join( - DIR, "test_expected_output", self.root_filename + ".xml") + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") \ - as expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") \ - as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) @@ -66,20 +69,20 @@ def runTest(self): filename = "widgets.xls" self.get_file_path(filename) expected_output_path = os.path.join( - DIR, "test_expected_output", self.root_filename + ".xml") + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] - json_survey = pyxform.xls2json.parse_file_to_json(self.path_to_excel_file, - warnings=warnings) + json_survey = pyxform.xls2json.parse_file_to_json( + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # print warnings # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") \ - as expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") \ - as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) @@ -93,10 +96,10 @@ def runTest(self): filename = "warnings.xls" path_to_excel_file = os.path.join(DIR, "example_xls", filename) warnings = [] - pyxform.xls2json.parse_file_to_json( - path_to_excel_file, warnings=warnings) + pyxform.xls2json.parse_file_to_json(path_to_excel_file, warnings=warnings) self.assertEquals( - len(warnings), 22, "Found " + str(len(warnings)) + " warnings") + len(warnings), 22, "Found " + str(len(warnings)) + " warnings" + ) class CalculateWithoutCalculationTest(unittest.TestCase): @@ -107,8 +110,9 @@ class CalculateWithoutCalculationTest(unittest.TestCase): def runTest(self): filename = "calculate_without_calculation.xls" path_to_excel_file = os.path.join(DIR, "example_xls", filename) - self.assertRaises(PyXFormError, pyxform.xls2json.parse_file_to_json, - path_to_excel_file) + self.assertRaises( + PyXFormError, pyxform.xls2json.parse_file_to_json, path_to_excel_file + ) class PullDataTest(XFormTestCase): @@ -117,21 +121,21 @@ class PullDataTest(XFormTestCase): def runTest(self): filename = "pull_data.xlsx" self.get_file_path(filename) - expected_output_path = os.path.join(DIR, "test_expected_output", - self.root_filename + ".xml") + expected_output_path = os.path.join( + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") \ - as expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") \ - as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) # cleanup @@ -144,26 +148,26 @@ class SeachAndSelectTest(XFormTestCase): def runTest(self): filename = "search_and_select.xlsx" self.get_file_path(filename) - expected_output_path = os.path.join(DIR, "test_expected_output", - self.root_filename + ".xml") + expected_output_path = os.path.join( + DIR, "test_expected_output", self.root_filename + ".xml" + ) # Do the conversion: warnings = [] json_survey = pyxform.xls2json.parse_file_to_json( - self.path_to_excel_file, warnings=warnings) + self.path_to_excel_file, warnings=warnings + ) survey = pyxform.create_survey_element_from_dict(json_survey) survey.print_xform_to_file(self.output_path, warnings=warnings) # Compare with the expected output: - with codecs.open(expected_output_path, 'rb', encoding="utf-8") \ - as expected_file: - with codecs.open(self.output_path, 'rb', encoding="utf-8") \ - as actual_file: + with codecs.open(expected_output_path, "rb", encoding="utf-8") as expected_file: + with codecs.open(self.output_path, "rb", encoding="utf-8") as actual_file: self.assertXFormEqual(expected_file.read(), actual_file.read()) # cleanup os.remove(self.output_path) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/pyxform/tests/xml_tests.py b/pyxform/tests/xml_tests.py index a9e2376a9..62894c4af 100644 --- a/pyxform/tests/xml_tests.py +++ b/pyxform/tests/xml_tests.py @@ -1,18 +1,25 @@ -from pyxform import create_survey_from_xls +# -*- coding: utf-8 -*- +""" +Test XForm XML syntax. +""" import re -from pyxform.tests import utils -from unittest2 import TestCase from xml.dom.minidom import getDOMImplementation + +from unittest2 import TestCase + +from pyxform import create_survey_from_xls +from pyxform.tests import utils from pyxform.utils import node class XMLTests(utils.XFormTestCase): def setUp(self): self.survey = create_survey_from_xls( - utils.path_to_text_fixture("yes_or_no_question.xls")) + utils.path_to_text_fixture("yes_or_no_question.xls") + ) def test_to_xml(self): - xml_str = u''' + xml_str = """ -''' - xml_str = re.sub(r"yes_or_no_question_2011_04_22", - self.survey.id_string, xml_str) +""" + xml_str = re.sub( + r"yes_or_no_question_2011_04_22", self.survey.id_string, xml_str + ) self.maxDiff = None self.assertXFormEqual(xml_str, self.survey.to_xml()) class MinidomTextWriterMonkeyPatchTest(TestCase): - def test_patch_lets_node_func_escape_only_necessary(self): """Should only escape text chars that should be: ["<", ">", "&"].""" - text = u"' \" & < >" - expected = u"' \" & < >".format(text) - observed = node(u"root", text).toprettyxml(indent="", newl="") + text = "' \" & < >" + expected = "' \" & < >".format(text) + observed = node("root", text).toprettyxml(indent="", newl="") self.assertEqual(expected, observed) def test_original_escape_escapes_more_than_necessary(self): """Should fail if the original is updated (the patch can be removed).""" - text = u"' \" & < >" - expected = u"' " & < >".format(text) - document = getDOMImplementation().createDocument(None, u"root", None) + text = "' \" & < >" + expected = "' " & < >".format(text) + document = getDOMImplementation().createDocument(None, "root", None) root = document.documentElement text_node = document.createTextNode(text) root.appendChild(text_node) diff --git a/pyxform/tests_v1/pyxform_test_case.py b/pyxform/tests_v1/pyxform_test_case.py index 2ca4f0b62..6b28e9fce 100644 --- a/pyxform/tests_v1/pyxform_test_case.py +++ b/pyxform/tests_v1/pyxform_test_case.py @@ -1,8 +1,11 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- +""" +PyxformTestCase base class using markdown to define the XLSForm. +""" from __future__ import print_function, unicode_literals import codecs +import logging import os import re import tempfile @@ -12,10 +15,14 @@ from pyxform.builder import create_survey_element_from_dict from pyxform.errors import PyXFormError from pyxform.tests_v1.test_utils.md_table import md_table_to_ss_structure -from pyxform.validators.odk_validate import ODKValidateError, check_xform from pyxform.utils import NSMAP, unicode +from pyxform.validators.odk_validate import ODKValidateError, check_xform from pyxform.xls2json import workbook_to_json +logger = logging.getLogger(__name__) +logger.addHandler(logging.StreamHandler()) +logger.setLevel(logging.DEBUG) + class PyxformTestError(Exception): pass @@ -30,21 +37,20 @@ def md_to_pyxform_survey(self, md_raw, kwargs=None, autoname=True): if autoname: kwargs = self._autoname_inputs(kwargs) _md = [] - for line in md_raw.split('\n'): - if re.match(r'^\s+#', line): + for line in md_raw.split("\n"): + if re.match(r"^\s+#", line): # ignore lines which start with pound sign continue - elif re.match(r'^(.*)(#[^|]+)$', line): + elif re.match(r"^(.*)(#[^|]+)$", line): # keep everything before the # outside of the last occurrence # of | - _md.append( - re.match(r'^(.*)(#[^|]+)$', line).groups()[0].strip()) + _md.append(re.match(r"^(.*)(#[^|]+)$", line).groups()[0].strip()) else: _md.append(line.strip()) - md = '\n'.join(_md) + md = "\n".join(_md) - if kwargs.get('debug'): - print(md) + if kwargs.get("debug"): + logger.debug(md) def list_to_dicts(arr): headers = arr[0] @@ -53,7 +59,7 @@ def _row_to_dict(row): out_dict = {} for i in range(0, len(row)): col = row[i] - if col not in [None, '']: + if col not in [None, ""]: out_dict[headers[i]] = col return out_dict @@ -72,9 +78,9 @@ def _ss_structure_to_pyxform_survey(ss_structure, kwargs): # ideally, when all these tests are working, this would be # refactored as well survey = create_survey_element_from_dict(imported_survey_json) - survey.name = kwargs.get('name') - survey.title = kwargs.get('title') - survey.id_string = kwargs.get('id_string') + survey.name = kwargs.get("name") + survey.title = kwargs.get("title") + survey.id_string = kwargs.get("id_string") return survey @@ -82,7 +88,7 @@ def _ss_structure_to_pyxform_survey(ss_structure, kwargs): def _run_odk_validate(xml): # On Windows, NamedTemporaryFile must be opened exclusively. # So it must be explicitly created, opened, closed, and removed - tmp = tempfile.NamedTemporaryFile(suffix='.xml', delete=False) + tmp = tempfile.NamedTemporaryFile(suffix=".xml", delete=False) tmp.close() try: with codecs.open(tmp.name, mode="w", encoding="utf-8") as fp: @@ -101,19 +107,18 @@ def _autoname_inputs(kwargs): include in test cases, so this will pull a default value from the stack trace. """ - test_name_root = 'pyxform' - if 'name' not in kwargs.keys(): - kwargs['name'] = test_name_root + '_autotestname' - if 'title' not in kwargs.keys(): - kwargs['title'] = test_name_root + "_autotesttitle" - if 'id_string' not in kwargs.keys(): - kwargs['id_string'] = test_name_root + "_autotest_id_string" + test_name_root = "pyxform" + if "name" not in kwargs.keys(): + kwargs["name"] = test_name_root + "_autotestname" + if "title" not in kwargs.keys(): + kwargs["title"] = test_name_root + "_autotesttitle" + if "id_string" not in kwargs.keys(): + kwargs["id_string"] = test_name_root + "_autotest_id_string" return kwargs class PyxformTestCase(PyxformMarkdown, TestCase): - def assertPyxformXform(self, **kwargs): """ PyxformTestCase.assertPyxformXform() named arguments: @@ -147,74 +152,82 @@ def assertPyxformXform(self, **kwargs): * run_odk_validate: (bool) when True, runs ODK Validate process Default value = False because it slows down tests """ - debug = kwargs.get('debug', False) - expecting_invalid_survey = kwargs.get('errored', False) + debug = kwargs.get("debug", False) + expecting_invalid_survey = kwargs.get("errored", False) errors = [] xml_nodes = {} - run_odk_validate = kwargs.get('run_odk_validate', False) - odk_validate_error__contains = \ - kwargs.get('odk_validate_error__contains', []) + run_odk_validate = kwargs.get("run_odk_validate", False) + odk_validate_error__contains = kwargs.get("odk_validate_error__contains", []) try: - if 'md' in kwargs.keys(): + if "md" in kwargs.keys(): kwargs = self._autoname_inputs(kwargs) - survey = self.md_to_pyxform_survey(kwargs.get('md'), kwargs) - elif 'ss_structure' in kwargs.keys(): + survey = self.md_to_pyxform_survey(kwargs.get("md"), kwargs) + elif "ss_structure" in kwargs.keys(): kwargs = self._autoname_inputs(kwargs) survey = self._ss_structure_to_pyxform_survey( - kwargs.get('ss_structure'), kwargs) + kwargs.get("ss_structure"), kwargs + ) else: survey = kwargs.get("survey") xml = survey._to_pretty_xml() - root = ETree.fromstring(xml.encode('utf-8')) + root = ETree.fromstring(xml.encode("utf-8")) # Ensure all namespaces are present, even if unused final_nsmap = NSMAP.copy() final_nsmap.update(survey.get_nsmap()) root.attrib.update(final_nsmap) - xml_nodes['xml'] = root + xml_nodes["xml"] = root def _pull_xml_node_from_root(element_selector): - ns = 'http://www.w3.org/2002/xforms' - _r = root.findall('.//n:%s' % element_selector, - namespaces={'n': ns}) - if len(_r) == 0: - return False - else: + _r = root.findall( + ".//n:%s" % element_selector, + namespaces={"n": "http://www.w3.org/2002/xforms"}, + ) + if _r: return _r[0] - for _n in ['model', 'instance', 'itext']: + return False + + for _n in ["model", "instance", "itext"]: xml_nodes[_n] = _pull_xml_node_from_root(_n) if debug: - print(xml) + logger.debug(xml) if run_odk_validate: self._run_odk_validate(xml=xml) - if len(odk_validate_error__contains) > 0: + if odk_validate_error__contains: raise PyxformTestError("ODKValidateError was not raised") except PyXFormError as e: survey = False errors = [unicode(e)] if debug: - print("") - print("ERROR: '%s'" % errors[0]) + logger.debug("") + logger.debug("ERROR: '%s'", errors[0]) except ODKValidateError as e: - if len(odk_validate_error__contains) is 0: - raise PyxformTestError("ODK Validate error was thrown but " + - "'odk_validate_error__contains'" + - " was empty:" + unicode(e)) + if not odk_validate_error__contains: + raise PyxformTestError( + "ODK Validate error was thrown but " + + "'odk_validate_error__contains'" + + " was empty:" + + unicode(e) + ) for v_err in odk_validate_error__contains: - self.assertContains(e.args[0].decode('utf-8'), v_err, - msg_prefix='odk_validate_error__contains') + self.assertContains( + e.args[0].decode("utf-8"), + v_err, + msg_prefix="odk_validate_error__contains", + ) else: survey = True if survey: + def _check_contains(keyword): - contains_str = '%s__contains' % keyword + contains_str = "%s__contains" % keyword def check_content(content): text_arr = kwargs[contains_str] @@ -223,21 +236,29 @@ def check_content(content): return contains_str, check_content - if 'body_contains' in kwargs or 'body__contains' in kwargs: - raise SyntaxError("Invalid parameter: 'body__contains'." - "Use 'xml__contains' instead") + if "body_contains" in kwargs or "body__contains" in kwargs: + raise SyntaxError( + "Invalid parameter: 'body__contains'." "Use 'xml__contains' instead" + ) - for code in ['xml', 'instance', 'model', 'itext']: + for code in ["xml", "instance", "model", "itext"]: (code__str, checker) = _check_contains(code) if kwargs.get(code__str): - checker(ETree.tostring( - xml_nodes[code], encoding='utf-8').decode('utf-8')) - bad_kwarg = '%s_contains' % code + checker( + ETree.tostring(xml_nodes[code], encoding="utf-8").decode( + "utf-8" + ) + ) + bad_kwarg = "%s_contains" % code if bad_kwarg in kwargs: - good_kwarg = '%s__contains' % code - raise SyntaxError(("'%s' is not a valid parameter. " - "Use double underscores: '%s'") % - (bad_kwarg, good_kwarg)) + good_kwarg = "%s__contains" % code + raise SyntaxError( + ( + "'%s' is not a valid parameter. " + "Use double underscores: '%s'" + ) + % (bad_kwarg, good_kwarg) + ) if survey is False and expecting_invalid_survey is False: raise PyxformTestError( @@ -250,11 +271,10 @@ def check_content(content): elif survey and expecting_invalid_survey: raise PyxformTestError("Expected survey to be invalid.") - if 'error__contains' in kwargs: - joined_error = '\n'.join(errors) - for text in kwargs['error__contains']: - self.assertContains(joined_error, text, - msg_prefix="error__contains") + if "error__contains" in kwargs: + joined_error = "\n".join(errors) + for text in kwargs["error__contains"]: + self.assertContains(joined_error, text, msg_prefix="error__contains") @staticmethod def _assert_contains(content, text, msg_prefix): @@ -268,7 +288,7 @@ def _assert_contains(content, text, msg_prefix): return text_repr, real_count, msg_prefix - def assertContains(self, content, text, count=None, msg_prefix=''): + def assertContains(self, content, text, count=None, msg_prefix=""): """ FROM: django source- testcases.py @@ -277,27 +297,31 @@ def assertContains(self, content, text, count=None, msg_prefix=''): true if the text occurs at least once in the content. """ text_repr, real_count, msg_prefix = self._assert_contains( - content, text, msg_prefix) + content, text, msg_prefix + ) if count is not None: self.assertEqual( - real_count, count, + real_count, + count, msg_prefix + "Found %d instances of %s in content" - " (expected %d)" % (real_count, text_repr, count)) + " (expected %d)" % (real_count, text_repr, count), + ) else: self.assertTrue( - real_count != 0, - msg_prefix + "Couldn't find %s in content" % text_repr) + real_count != 0, msg_prefix + "Couldn't find %s in content" % text_repr + ) - def assertNotContains(self, content, text, msg_prefix=''): + def assertNotContains(self, content, text, msg_prefix=""): """ Asserts that a content indicates that some content was retrieved successfully, (i.e., the HTTP status code was as expected), and that ``text`` doesn't occurs in the content of the content. """ text_repr, real_count, msg_prefix = self._assert_contains( - content, text, msg_prefix) + content, text, msg_prefix + ) self.assertEqual( - real_count, 0, - msg_prefix + "Response should not contain %s" % text_repr) + real_count, 0, msg_prefix + "Response should not contain %s" % text_repr + ) diff --git a/pyxform/tests_v1/test_area.py b/pyxform/tests_v1/test_area.py index 48733dee2..a9ded4c43 100644 --- a/pyxform/tests_v1/test_area.py +++ b/pyxform/tests_v1/test_area.py @@ -1,23 +1,27 @@ +# -*- coding: utf-8 -*- +""" +AreaTest - test enclosed-area(geo_shape) calculation. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase - -MD = ''' +MD = """ | survey | | | | | | | | type | name | label | calculation | default | | | geoshape | geoshape1 | Draw your shape here... | | 38.253094215699576 21.756382658677467;38.25021274773806 21.756382658677467;38.25007793942195 21.763892843919166;38.25290886154963 21.763935759263404;38.25146813817506 21.758421137528785 | | | calculate | result | | enclosed-area(${geoshape1}) | | -''' # nopep8 +""" # nopep8 -XML_CONTAINS = ''' +XML_CONTAINS = """ -'''.strip() # nopep8 +""".strip() # nopep8 class AreaTest(PyxformTestCase): + """ + AreaTest - test enclosed-area(geo_shape) calculation. + """ + def test_area(self): self.assertPyxformXform( - name="area", - md=MD, - xml__contains=[XML_CONTAINS], - debug=True + name="area", md=MD, xml__contains=[XML_CONTAINS], debug=True ) diff --git a/pyxform/tests_v1/test_audit.py b/pyxform/tests_v1/test_audit.py index abd219e6b..b7e996d16 100644 --- a/pyxform/tests_v1/test_audit.py +++ b/pyxform/tests_v1/test_audit.py @@ -1,7 +1,15 @@ +# -*- coding: utf-8 -*- +""" +AuditTest - test audit question type. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class AuditTest(PyxformTestCase): + """ + AuditTest - test audit question type. + """ + def test_audit(self): self.assertPyxformXform( name="meta_audit", @@ -11,10 +19,11 @@ def test_audit(self): | | audit | audit | | """, xml__contains=[ - '', - '', - '', - ''], + "", + "", + "", + '', + ], ) def test_audit_random_name(self): @@ -26,7 +35,7 @@ def test_audit_random_name(self): | | audit | bobby | | """, errored=True, - error__contains=['Audits must always be named \'audit.\''], + error__contains=["Audits must always be named 'audit.'"], ) def test_audit_blank_name(self): @@ -38,10 +47,11 @@ def test_audit_blank_name(self): | | audit | | | """, xml__contains=[ - '', - '', - '', - ''], + "", + "", + "", + '', + ], ) def test_audit_location_required_parameters(self): @@ -53,7 +63,9 @@ def test_audit_location_required_parameters(self): | | audit | audit | location-max-age=3, location-min-interval=1 | """, errored=True, - error__contains=['\'location-priority\', \'location-min-interval\', and \'location-max-age\' are required parameters'], + error__contains=[ + "'location-priority', 'location-min-interval', and 'location-max-age' are required parameters" + ], ) def test_audit_location_priority_values(self): @@ -65,7 +77,9 @@ def test_audit_location_priority_values(self): | | audit | audit | location-priority=foo, location-min-interval=1, location-max-age=2 | """, errored=True, - error__contains=['location-priority must be set to no-power, low-power, balanced, or high-accuracy'], + error__contains=[ + "location-priority must be set to no-power, low-power, balanced, or high-accuracy" + ], ) def test_audit_location_max_age_gt_min_interval(self): @@ -77,7 +91,9 @@ def test_audit_location_max_age_gt_min_interval(self): | | audit | audit | location-priority=balanced, location-min-interval=2, location-max-age=1 | """, errored=True, - error__contains=['location-max-age must be greater than or equal to location-min-interval'], + error__contains=[ + "location-max-age must be greater than or equal to location-min-interval" + ], ) def test_audit_location_min_interval_positive(self): @@ -89,7 +105,9 @@ def test_audit_location_min_interval_positive(self): | | audit | audit | location-priority=balanced, location-min-interval=-1, location-max-age=1 | """, errored=True, - error__contains=['location-min-interval must be greater than or equal to zero'], + error__contains=[ + "location-min-interval must be greater than or equal to zero" + ], ) def test_audit_location(self): @@ -101,8 +119,9 @@ def test_audit_location(self): | | audit | audit | location-priority=balanced, location-min-interval=60, location-max-age=300 | """, xml__contains=[ - '', - '', - '', - ''], + "", + "", + "", + '', + ], ) diff --git a/pyxform/tests_v1/test_bug_missing_headers.py b/pyxform/tests_v1/test_bug_missing_headers.py index f80933b9c..2309fefde 100644 --- a/pyxform/tests_v1/test_bug_missing_headers.py +++ b/pyxform/tests_v1/test_bug_missing_headers.py @@ -1,11 +1,17 @@ +# -*- coding: utf-8 -*- +""" +Test missing headers in XLSForm. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class MissingHeaders(PyxformTestCase): - """ - when survey and choices columns are missing headers, it is helpful to see + """Test missing headers in XLSForm + + When survey and choices columns are missing headers, it is helpful to see an error message that prompts the user to include necessary headers. """ + def test_missing_survey_headers(self): self.assertPyxformXform( md=""" @@ -13,9 +19,8 @@ def test_missing_survey_headers(self): | | select_one list | S1 | """, errored=True, - error__contains=[ - 'missing important column headers', - ]) + error__contains=["missing important column headers"], + ) def test_missing_choice_headers(self): self.assertPyxformXform( @@ -28,6 +33,5 @@ def test_missing_choice_headers(self): | | list | option b | b | """, errored=True, - error__contains=[ - "has columns 'list name', 'name', and 'label'", - ]) + error__contains=["has columns 'list name', 'name', and 'label'"], + ) diff --git a/pyxform/tests_v1/test_bug_round_calculation.py b/pyxform/tests_v1/test_bug_round_calculation.py index 34a1f0a54..080b1343a 100644 --- a/pyxform/tests_v1/test_bug_round_calculation.py +++ b/pyxform/tests_v1/test_bug_round_calculation.py @@ -1,6 +1,8 @@ +# -*- coding: utf-8 -*- +""" +Test round(number, precision) calculation. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase -from pyxform.xls2json_backends import xls_value_to_unicode -import xlrd class RoundCalculationTest(PyxformTestCase): @@ -13,33 +15,6 @@ def test_non_existent_itext_reference(self): | | decimal | amount | Counter | | | | calculate | rounded | Rounded | round(${amount}, 0) | """, # noqa - xml__contains=[ - """""" - ], - run_odk_validate=True) - - -class TestXLIntValueConversion(PyxformTestCase): - """ - Test external choices sheet with numeric values is processed successfully. - - The test ensures that the integer values within the external choices sheet - are returned as they were initially received. - """ - def test_xls_int_to_csv(self): - """Test that the float value obtained from xlrd module - is converted back to its int value.""" - value = 32.0 - value_type = xlrd.XL_CELL_NUMBER - datemode = 1 - csv_data = xls_value_to_unicode(value, value_type, datemode) - expected_output = "32" - self.assertEqual(csv_data, expected_output) - - """Test that the decimal value is not changed during conversion.""" - value = 46.9 - value_type = xlrd.XL_CELL_NUMBER - datemode = 1 - csv_data = xls_value_to_unicode(value, value_type, datemode) - expected_output = "46.9" - self.assertEqual(csv_data, expected_output) + xml__contains=[""""""], + run_odk_validate=True, + ) diff --git a/pyxform/tests_v1/test_custom_xml_namespaces.py b/pyxform/tests_v1/test_custom_xml_namespaces.py index 8c0cfa06b..550a7c108 100644 --- a/pyxform/tests_v1/test_custom_xml_namespaces.py +++ b/pyxform/tests_v1/test_custom_xml_namespaces.py @@ -1,17 +1,24 @@ +# -*- coding: utf-8 -*- +""" +Test custom namespaces. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase - -MD = ''' +MD = """ | survey | | | | | | type | name | label | | | note | q | Q | | settings | | | | | | namespaces | | | | | esri="http://esri.com/xforms" enk="http://enketo.org/xforms" naf="http://nafundi.com/xforms" | | | -''' # nopep8 +""" # nopep8 class CustomXMLNamespacesTest(PyxformTestCase): + """ + Test custom namespaces. + """ + def test_custom_xml_name_spaces(self): # re: https://github.com/XLSForm/pyxform/issues/65 self.assertPyxformXform( diff --git a/pyxform/tests_v1/test_fields.py b/pyxform/tests_v1/test_fields.py index 58c4277b1..2cc071ade 100644 --- a/pyxform/tests_v1/test_fields.py +++ b/pyxform/tests_v1/test_fields.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test duplicate survey question field name. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -19,8 +23,7 @@ def test_duplicate_fields(self): | | integer | age | the age | """, errored=True, - error__contains=[ - "There are more than one survey elements named 'age'"], + error__contains=["There are more than one survey elements named 'age'"], ) def test_duplicate_fields_diff_cases(self): @@ -36,6 +39,5 @@ def test_duplicate_fields_diff_cases(self): | | integer | Age | the age | """, errored=True, - error__contains=[ - "There are more than one survey elements named 'age'"], + error__contains=["There are more than one survey elements named 'age'"], ) diff --git a/pyxform/tests_v1/test_file.py b/pyxform/tests_v1/test_file.py index cd959528b..76414501a 100644 --- a/pyxform/tests_v1/test_file.py +++ b/pyxform/tests_v1/test_file.py @@ -1,8 +1,19 @@ +# -*- coding: utf-8 -*- +""" +Test file question type. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class FileWidgetTest(PyxformTestCase): + """ + Test file widget class. + """ + def test_file_type(self): + """ + Test file question type. + """ self.assertPyxformXform( name="data", md=""" @@ -10,6 +21,5 @@ def test_file_type(self): | | type | name | label | | | file | file | Attach a file | """, - xml__contains=[ - '', - '', - ], + instance__contains=['', ""], model__contains=[ """""" - ], + ], xml__contains=[ '', - '', - '', + "", + "", ], ) diff --git a/pyxform/tests_v1/test_geo.py b/pyxform/tests_v1/test_geo.py index 9d6fd0b34..b3448da47 100644 --- a/pyxform/tests_v1/test_geo.py +++ b/pyxform/tests_v1/test_geo.py @@ -1,7 +1,13 @@ +# -*- coding: utf-8 -*- +""" +Test geo widgets. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class GeoWidgetsTest(PyxformTestCase): + """Test geo widgets class.""" + def test_gps_type(self): self.assertPyxformXform( name="geo", @@ -10,7 +16,7 @@ def test_gps_type(self): | | type | name | label | | | gps | location | GPS | """, - xml__contains=['geopoint'], + xml__contains=["geopoint"], ) def test_gps_alias(self): @@ -21,7 +27,7 @@ def test_gps_alias(self): | | type | name | label | | | geopoint | location | GPS | """, - xml__contains=['geopoint'], + xml__contains=["geopoint"], ) def test_geo_widgets_types(self): @@ -43,25 +49,20 @@ def test_geo_widgets_types(self): | | end_repeat | | | """, xml__contains=[ - '', - '', - - '', - '', - - '', - '', - + "", + "", + "", + "", + "", + "", '', '', - '', '', - '', '', - ], - ) + ], + ) diff --git a/pyxform/tests_v1/test_groups.py b/pyxform/tests_v1/test_groups.py index b0a625450..454f04687 100644 --- a/pyxform/tests_v1/test_groups.py +++ b/pyxform/tests_v1/test_groups.py @@ -1,7 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Test XForm groups. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class GroupsTests(PyxformTestCase): + """ + Test XForm groups. + """ def test_group_type(self): self.assertPyxformXform( @@ -16,14 +23,15 @@ def test_group_type(self): | | note | postgrp | Post group note | """, model__contains=[ - '', - '', - '', # nopep8 - '', # nopep8 - '', # nopep8 - '', - '', - ]) + "", + "", + "", # nopep8 + "", # nopep8 + "", # nopep8 + "", + "", + ], + ) def test_group_intent(self): self.assertPyxformXform( @@ -39,5 +47,6 @@ def test_group_intent(self): | | note | postgrp | Post group note | | """, # nopep8 xml__contains=[ - '', # nopep8 - ]) + '' # nopep8 + ], + ) diff --git a/pyxform/tests_v1/test_guidance_hint.py b/pyxform/tests_v1/test_guidance_hint.py index a9de9296e..0812e6515 100644 --- a/pyxform/tests_v1/test_guidance_hint.py +++ b/pyxform/tests_v1/test_guidance_hint.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- -"""GUidnace hint test module.""" +""" +Guidance hint test module. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -79,9 +81,7 @@ def test_guidance_hint_only(self): | | type | name | guidance_hint | | | string | name | as shown on birth certificate| """, - error__contains=[ - "The survey element named 'name' has no label or hint." - ], + error__contains=["The survey element named 'name' has no label or hint."], ) def test_multi_language_guidance_only(self): # pylint:disable=C0103 @@ -94,9 +94,7 @@ def test_multi_language_guidance_only(self): # pylint:disable=C0103 | | type | name | guidance_hint | guidance_hint::French (fr) | | | string | name | as shown on birth certificate| comme sur le certificat de naissance| """, # noqa - error__contains=[ - "The survey element named 'name' has no label or hint." - ], + error__contains=["The survey element named 'name' has no label or hint."], ) def test_multi_language_hint(self): diff --git a/pyxform/tests_v1/test_language_warnings.py b/pyxform/tests_v1/test_language_warnings.py index cd46f8f64..762f9148d 100644 --- a/pyxform/tests_v1/test_language_warnings.py +++ b/pyxform/tests_v1/test_language_warnings.py @@ -1,17 +1,29 @@ -import tempfile +# -*- coding: utf-8 -*- +""" +Test language warnings. +""" import os +import tempfile + from pyxform.tests_v1.pyxform_test_case import PyxformTestCase + class LanguageWarningTest(PyxformTestCase): + """ + Test language warnings. + """ + def test_label_with_valid_subtag_should_not_warn(self): - survey = self.md_to_pyxform_survey(""" + survey = self.md_to_pyxform_survey( + """ | survey | | | | | | type | name | label::English (en) | | | note | my_note | My note | - """) + """ + ) warnings = [] - tmp = tempfile.NamedTemporaryFile(suffix='.xml', delete=False) + tmp = tempfile.NamedTemporaryFile(suffix=".xml", delete=False) tmp.close() survey.print_xform_to_file(tmp.name, warnings=warnings) @@ -19,52 +31,64 @@ def test_label_with_valid_subtag_should_not_warn(self): os.unlink(tmp.name) def test_label_with_no_subtag_should_warn(self): - survey = self.md_to_pyxform_survey(""" + survey = self.md_to_pyxform_survey( + """ | survey | | | | | | type | name | label::English | | | note | my_note | My note | - """) + """ + ) warnings = [] - tmp = tempfile.NamedTemporaryFile(suffix='.xml', delete=False) + tmp = tempfile.NamedTemporaryFile(suffix=".xml", delete=False) tmp.close() survey.print_xform_to_file(tmp.name, warnings=warnings) self.assertTrue(len(warnings) == 1) - self.assertTrue('do not contain valid machine-readable codes: English. Learn more' in warnings[0]) + self.assertTrue( + "do not contain valid machine-readable codes: English. Learn more" + in warnings[0] + ) os.unlink(tmp.name) def test_label_with_unknown_subtag_should_warn(self): - survey = self.md_to_pyxform_survey(""" + survey = self.md_to_pyxform_survey( + """ | survey | | | | | | type | name | label::English (schm) | | | note | my_note | My note | - """) + """ + ) warnings = [] - tmp = tempfile.NamedTemporaryFile(suffix='.xml', delete=False) + tmp = tempfile.NamedTemporaryFile(suffix=".xml", delete=False) tmp.close() survey.print_xform_to_file(tmp.name, warnings=warnings) self.assertTrue(len(warnings) == 1) - self.assertTrue('do not contain valid machine-readable codes: English (schm). Learn more' in warnings[0]) - os.unlink(tmp.name) + self.assertTrue( + "do not contain valid machine-readable codes: English (schm). Learn more" + in warnings[0] + ) + os.unlink(tmp.name) def test_default_language_only_should_not_warn(self): - survey = self.md_to_pyxform_survey(""" + survey = self.md_to_pyxform_survey( + """ | survey | | | | | | | type | name | label | choice_filter | | | select_one opts | opt | My opt | fake = 1 | | choices| | | | | | | list_name | name | label | fake | | | opts | opt1 | Opt1 | 1 | - | | opts | opt2 | Opt2 | 1 | - """) + | | opts | opt2 | Opt2 | 1 | + """ + ) warnings = [] - tmp = tempfile.NamedTemporaryFile(suffix='.xml', delete=False) + tmp = tempfile.NamedTemporaryFile(suffix=".xml", delete=False) tmp.close() survey.print_xform_to_file(tmp.name, warnings=warnings) self.assertTrue(len(warnings) == 0) - os.unlink(tmp.name) \ No newline at end of file + os.unlink(tmp.name) diff --git a/pyxform/tests_v1/test_max_pixels.py b/pyxform/tests_v1/test_max_pixels.py index 6f5c86442..7d362c94f 100644 --- a/pyxform/tests_v1/test_max_pixels.py +++ b/pyxform/tests_v1/test_max_pixels.py @@ -1,6 +1,15 @@ +# -*- coding: utf-8 -*- +""" +Test image max-pixel parameters. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase + class MaxPixelsTest(PyxformTestCase): + """ + Test image max-pixel parameters. + """ + def test_integer_max_pixels(self): self.assertPyxformXform( name="data", @@ -11,8 +20,8 @@ def test_integer_max_pixels(self): """, xml__contains=[ 'xmlns:orx="http://openrosa.org/xforms"', - '' - ] + '', + ], ) def test_string_max_pixels(self): @@ -24,9 +33,7 @@ def test_string_max_pixels(self): | | type | name | label | parameters | | | image | my_image | Image | max-pixels=foo | """, - error__contains=[ - "Parameter max-pixels must have an integer value." - ] + error__contains=["Parameter max-pixels must have an integer value."], ) def test_string_extra_params(self): @@ -40,5 +47,5 @@ def test_string_extra_params(self): """, error__contains=[ "Accepted parameters are 'max-pixels': 'foo' is an invalid parameter." - ] - ) \ No newline at end of file + ], + ) diff --git a/pyxform/tests_v1/test_osm.py b/pyxform/tests_v1/test_osm.py index 48fc795d6..11acce018 100644 --- a/pyxform/tests_v1/test_osm.py +++ b/pyxform/tests_v1/test_osm.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test OSM widgets. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase expected_xml_output = """ @@ -11,7 +15,12 @@ """ + class OSMWidgetsTest(PyxformTestCase): + """ + Test OSM widgets. + """ + def test_osm_type(self): self.assertPyxformXform( name="osm", diff --git a/pyxform/tests_v1/test_pyxformtestcase.py b/pyxform/tests_v1/test_pyxformtestcase.py index 452bc3aea..29192e4f0 100644 --- a/pyxform/tests_v1/test_pyxformtestcase.py +++ b/pyxform/tests_v1/test_pyxformtestcase.py @@ -1,10 +1,9 @@ -from pyxform.tests_v1.pyxform_test_case import PyxformTestCase - - -''' +# -*- coding: utf-8 -*- +""" Ensuring that the pyxform_test_case.PyxformTestCase class does some internal conversions correctly. -''' +""" +from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class PyxformTestCaseNonMarkdownSurveyAlternatives(PyxformTestCase): @@ -16,7 +15,7 @@ def test_tainted_vanilla_survey_failure(self): if "errored" parameter is not set to False, it should raise an exception """ - _invalid_ss_structure = {'survey': [{'type': 'note', 'name': 'n1'}]} + _invalid_ss_structure = {"survey": [{"type": "note", "name": "n1"}]} def _no_valid_flag(): """ @@ -34,9 +33,7 @@ def _no_valid_flag(): self.assertPyxformXform( ss_structure=_invalid_ss_structure, errored=True, - error__contains=[ - "The survey element named 'n1' has no label or hint.", - ], + error__contains=["The survey element named 'n1' has no label or hint."], ) def test_vanilla_survey(self): @@ -50,7 +47,7 @@ def test_vanilla_survey(self): """ self.assertPyxformXform( ss_structure={ - 'survey': [{'type': 'note', 'name': 'n1', 'label': 'Note 1'}] + "survey": [{"type": "note", "name": "n1", "label": "Note 1"}] }, errored=False, ) @@ -62,11 +59,15 @@ def test_formid_is_not_none(self): When the form id is not set, it should never use python's None. Fixing because this messes up other tests. """ - s1 = self.md_to_pyxform_survey(""" + s1 = self.md_to_pyxform_survey( + """ | survey | | | | | | type | name | label | | | note | q | Q | - """, {}, autoname=True) + """, + {}, + autoname=True, + ) - if s1.id_string in ['None', None]: + if s1.id_string in ["None", None]: self.assertRaises(Exception, lambda: s1.validate()) diff --git a/pyxform/tests_v1/test_randomize_itemsets.py b/pyxform/tests_v1/test_randomize_itemsets.py index 9e1656a6d..1c48dfb24 100644 --- a/pyxform/tests_v1/test_randomize_itemsets.py +++ b/pyxform/tests_v1/test_randomize_itemsets.py @@ -1,5 +1,10 @@ +# -*- coding: utf-8 -*- +""" +Test randomize itemsets. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase + class RandomizeItemsetsTest(PyxformTestCase): def test_randomized_select_one(self): self.assertPyxformXform( @@ -11,12 +16,12 @@ def test_randomized_select_one(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_seeded_select_one(self): @@ -29,12 +34,12 @@ def test_randomized_seeded_select_one(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_seeded_select_one_nameset_seed(self): @@ -52,8 +57,8 @@ def test_randomized_seeded_select_one_nameset_seed(self): """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_seeded_filtered_select_one(self): @@ -70,8 +75,8 @@ def test_randomized_seeded_filtered_select_one(self): """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_select_multiple(self): @@ -84,12 +89,12 @@ def test_randomized_select_multiple(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_seeded_select_multiple(self): @@ -102,12 +107,12 @@ def test_randomized_seeded_select_multiple(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_external_xml_instance(self): @@ -117,11 +122,11 @@ def test_randomized_external_xml_instance(self): | survey | | | | | | | type | name | label | parameters | | | select_one_from_file cities.xml | city | City | randomize=true | - + """, xml__contains=[ - "" - ] + "" + ], ) def test_randomized_select_one_bad_param(self): @@ -135,12 +140,12 @@ def test_randomized_select_one_bad_param(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, error__contains=[ "Accepted parameters are 'randomize, seed': 'step' is an invalid parameter." - ] + ], ) def test_randomized_select_one_bad_randomize(self): @@ -154,12 +159,12 @@ def test_randomized_select_one_bad_randomize(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, error__contains=[ "randomize must be set to true or false: 'ukanga' is an invalid value" - ] + ], ) def test_randomized_select_one_bad_seed(self): @@ -173,12 +178,12 @@ def test_randomized_select_one_bad_seed(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, error__contains=[ "seed value must be a number or a reference to another field." - ] + ], ) def test_randomized_select_one_seed_without_randomize(self): @@ -192,12 +197,8 @@ def test_randomized_select_one_seed_without_randomize(self): | choices| | | | | | | list_name | name | label | | | | choices | a | opt_a | | - | | choices | b | opt_b | | + | | choices | b | opt_b | | """, - error__contains=[ - "Parameters must include randomize=true to use a seed." - ] + error__contains=["Parameters must include randomize=true to use a seed."], ) - - diff --git a/pyxform/tests_v1/test_range.py b/pyxform/tests_v1/test_range.py index bbd931e1b..92039807e 100644 --- a/pyxform/tests_v1/test_range.py +++ b/pyxform/tests_v1/test_range.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test range widget. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -13,7 +17,8 @@ def test_range_type(self): """, xml__contains=[ '', - ''], + '', + ], ) # mixed case parameters @@ -26,7 +31,8 @@ def test_range_type(self): """, xml__contains=[ '', - ''], + '', + ], ) def test_range_type_defaults(self): @@ -39,7 +45,8 @@ def test_range_type_defaults(self): """, xml__contains=[ '', - ''], + '', + ], ) self.assertPyxformXform( @@ -51,7 +58,8 @@ def test_range_type_defaults(self): """, xml__contains=[ '', - ''], + '', + ], ) self.assertPyxformXform( @@ -63,7 +71,8 @@ def test_range_type_defaults(self): """, xml__contains=[ '', - ''], + '', + ], ) def test_range_type_float(self): @@ -76,7 +85,8 @@ def test_range_type_float(self): """, xml__contains=[ '', - ''], + '', + ], ) def test_range_type_invvalid_parameters(self): @@ -121,7 +131,8 @@ def test_range_semicolon_separator(self): """, xml__contains=[ '', - ''], + '', + ], ) def test_range_comma_separator(self): @@ -134,7 +145,8 @@ def test_range_comma_separator(self): """, xml__contains=[ '', - ''], + '', + ], ) self.assertPyxformXform( @@ -146,7 +158,8 @@ def test_range_comma_separator(self): """, xml__contains=[ '', - ''], + '', + ], ) self.assertPyxformXform( @@ -158,5 +171,6 @@ def test_range_comma_separator(self): """, # noqa xml__contains=[ '', - ''], + '', + ], ) diff --git a/pyxform/tests_v1/test_rank.py b/pyxform/tests_v1/test_rank.py index 5dc3c1234..f01a91ad0 100644 --- a/pyxform/tests_v1/test_rank.py +++ b/pyxform/tests_v1/test_rank.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test rank widget. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -18,16 +22,15 @@ def test_rank(self): 'xmlns:odk="http://www.opendatakit.org/xforms"', '', '', - '', - '', - 'a', - '', - 'b', - '' - ] + "", + "", + "a", + "", + "b", + "", + ], ) - def test_rank_filter(self): self.assertPyxformXform( name="data", @@ -44,21 +47,20 @@ def test_rank_filter(self): 'xmlns:odk="http://www.opendatakit.org/xforms"', '', '', - 'red', - 'a', - 'blue', - 'b', + "red", + "a", + "blue", + "b", """ - """ - ] + """, + ], ) - def test_rank_translations(self): self.assertPyxformXform( name="data", @@ -83,7 +85,7 @@ def test_rank_translations(self): """ BB """, - '', + "", """ """ - ] + """, + ], ) diff --git a/pyxform/tests_v1/test_repeat.py b/pyxform/tests_v1/test_repeat.py index bd139c9f9..691804a54 100644 --- a/pyxform/tests_v1/test_repeat.py +++ b/pyxform/tests_v1/test_repeat.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -test_repeat.py +Test reapeat structure. """ from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -9,6 +9,7 @@ class TestRepeat(PyxformTestCase): """ TestRepeat class. """ + def test_repeat_relative_reference(self): """ Test relative reference in repeats. @@ -66,13 +67,12 @@ def test_repeat_relative_reference(self): """, # noqa pylint: disable=line-too-long instance__contains=[ '', - ], + "", + "", + "", + ], model__contains=[ - """""", + """""", """""", @@ -85,16 +85,16 @@ def test_repeat_relative_reference(self): """type="string"/>""", """""" - ], + """type="string"/>""", + ], xml__contains=[ '', - '', - '', + "", + "", """""", """""", """""" + """ """, ], ) @@ -183,7 +183,7 @@ def test_indexed_repeat_relative_path(self): | | crop_list | kale | Kale | | """, # noqa pylint: disable=line-too-long model__contains=[ - """""", # noqa pylint: disable=line-too-long + """""" # noqa pylint: disable=line-too-long ], ) @@ -205,8 +205,7 @@ def test_hints_are_present_within_repeats(self): | | pet | fish | Fish | | """ - expected = \ -""" + expected = """ @@ -244,6 +243,5 @@ def test_hints_are_present_within_repeats(self): """ # noqa - self.assertPyxformXform( - md=md, model__contins=[expected], run_odk_validate=True) - survey = self.md_to_pyxform_survey(md_raw=md) + self.assertPyxformXform(md=md, model__contins=[expected], run_odk_validate=True) + self.md_to_pyxform_survey(md_raw=md) diff --git a/pyxform/tests_v1/test_settings_auto_send_delete.py b/pyxform/tests_v1/test_settings_auto_send_delete.py index 6abe020d5..52d9bc51a 100644 --- a/pyxform/tests_v1/test_settings_auto_send_delete.py +++ b/pyxform/tests_v1/test_settings_auto_send_delete.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test settins auto settings. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -54,4 +58,4 @@ def test_settings_auto_send_delete_false(self): xml__contains=[ '' ], - ) \ No newline at end of file + ) diff --git a/pyxform/tests_v1/test_sheet_columns.py b/pyxform/tests_v1/test_sheet_columns.py index 49f5ab1c5..26faf787a 100644 --- a/pyxform/tests_v1/test_sheet_columns.py +++ b/pyxform/tests_v1/test_sheet_columns.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test XLSForm sheet names. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -11,29 +15,27 @@ def test_missing_name(self): every question needs a name (or alias of name) """ self.assertPyxformXform( - name='invalidcols', - ss_structure={'survey': [{'type': 'text', - 'label': 'label'}]}, + name="invalidcols", + ss_structure={"survey": [{"type": "text", "label": "label"}]}, errored=True, - error__contains=['no name'], + error__contains=["no name"], ) def test_missing_name_but_has_alias_of_name(self): self.assertPyxformXform( - name='invalidcols', - ss_structure={'survey': [{'value': 'q1', - 'type': 'text', - 'label': 'label'}]}, + name="invalidcols", + ss_structure={ + "survey": [{"value": "q1", "type": "text", "label": "label"}] + }, errored=False, ) def test_missing_label(self): self.assertPyxformXform( name="invalidcols", - ss_structure={'survey': [{'type': 'text', - 'name': 'q1'}]}, + ss_structure={"survey": [{"type": "text", "name": "q1"}]}, errored=True, - error__contains=['no label or hint'], + error__contains=["no label or hint"], ) def test_column_case(self): @@ -50,7 +52,7 @@ def test_column_case(self): | | text | gender | the gender | """, errored=False, - debug=True + debug=True, ) @@ -66,10 +68,16 @@ def _simple_choice_ss(self, choice_sheet=None): if choice_sheet is None: choice_sheet = [] - return {'survey': [{'type': 'select_one l1', - 'name': 'l1choice', - 'label': 'select one from list l1'}], - 'choices': choice_sheet} + return { + "survey": [ + { + "type": "select_one l1", + "name": "l1choice", + "label": "select one from list l1", + } + ], + "choices": choice_sheet, + } def test_valid_choices_sheet_passes(self): """ @@ -77,16 +85,15 @@ def test_valid_choices_sheet_passes(self): """ self.assertPyxformXform( - name='valid_choices', - ss_structure=self._simple_choice_ss([ - {'list_name': 'l1', - 'name': 'c1', - 'label': 'choice 1'}, - {'list_name': 'l1', - 'name': 'c2', - 'label': 'choice 2'}]), + name="valid_choices", + ss_structure=self._simple_choice_ss( + [ + {"list_name": "l1", "name": "c1", "label": "choice 1"}, + {"list_name": "l1", "name": "c2", "label": "choice 2"}, + ] + ), errored=False, - ) + ) def test_invalid_choices_sheet_fails(self): """ @@ -94,16 +101,16 @@ def test_invalid_choices_sheet_fails(self): """ self.assertPyxformXform( - name='missing_name', - ss_structure=self._simple_choice_ss([ - {'list_name': 'l1', - 'label': 'choice 1'}, - {'list_name': 'l1', - 'label': 'choice 2'}, - ]), + name="missing_name", + ss_structure=self._simple_choice_ss( + [ + {"list_name": "l1", "label": "choice 1"}, + {"list_name": "l1", "label": "choice 2"}, + ] + ), errored=True, - error__contains=['option with no name'], - ) + error__contains=["option with no name"], + ) def test_missing_list_name(self): """ @@ -111,42 +118,38 @@ def test_missing_list_name(self): """ self.assertPyxformXform( - name='missing_list_name', - ss_structure=self._simple_choice_ss([ - {'bad_column': 'l1', - 'name': 'l1c1', - 'label': 'choice 1'}, - {'bad_column': 'l1', - 'name': 'l1c1', - 'label': 'choice 2'}, - ]), + name="missing_list_name", + ss_structure=self._simple_choice_ss( + [ + {"bad_column": "l1", "name": "l1c1", "label": "choice 1"}, + {"bad_column": "l1", "name": "l1c1", "label": "choice 2"}, + ] + ), debug=True, errored=True, # some basic keywords that should be in the error: - error__contains=[ - 'choices', - 'name', - 'list name', - ]) + error__contains=["choices", "name", "list name"], + ) def test_clear_filename_error_message(self): """Test clear filename error message""" - error_message = "The name 'bad@filename' is an invalid XML tag, it " \ - "contains an invalid character '@'. Names must begin" \ - " with a letter, colon, or underscore, subsequent " \ - "characters can include numbers, dashes, and periods" + error_message = ( + "The name 'bad@filename' is an invalid XML tag, it " + "contains an invalid character '@'. Names must begin" + " with a letter, colon, or underscore, subsequent " + "characters can include numbers, dashes, and periods" + ) self.assertPyxformXform( - name='bad@filename', - ss_structure=self._simple_choice_ss([ - {'list_name': 'l1', - 'name': 'c1', - 'label': 'choice 1'}, - {'list_name': 'l1', - 'name': 'c2', - 'label': 'choice 2'}]), + name="bad@filename", + ss_structure=self._simple_choice_ss( + [ + {"list_name": "l1", "name": "c1", "label": "choice 1"}, + {"list_name": "l1", "name": "c2", "label": "choice 2"}, + ] + ), errored=True, - error__contains=[error_message] - ) + error__contains=[error_message], + ) class AliasesTests(PyxformTestCase): @@ -155,10 +158,10 @@ class AliasesTests(PyxformTestCase): """ def test_value_and_name(self): - ''' + """ confirm that both 'name' and 'value' columns of choice list work - ''' - for name_alias in ['name', 'value']: + """ + for name_alias in ["name", "value"]: self.assertPyxformXform( name="aliases", md=""" @@ -169,21 +172,17 @@ def test_value_and_name(self): | | list name | %(name_alias)s | label | | | yn | yes | Yes | | | yn | no | No | - """ % ({ - u'name_alias': name_alias - }), - instance__contains=[ - '', - ], - model__contains=[ - '', - ], + """ + % ({"name_alias": name_alias}), + instance__contains=[""], + model__contains=[''], xml__contains=[ '', - 'yes', - 'no', - '', - ]) + "yes", + "no", + "", + ], + ) ''' # uncomment when re-implemented diff --git a/pyxform/tests_v1/test_sms.py b/pyxform/tests_v1/test_sms.py index fdf8e2627..0d16c12aa 100644 --- a/pyxform/tests_v1/test_sms.py +++ b/pyxform/tests_v1/test_sms.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test sms syntax. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -13,9 +17,7 @@ def test_prefix_only(self): | | prefix | | | | | | sms_test | | | | """, - xml__contains=[ - 'odk:prefix="sms_test"' - ] + xml__contains=['odk:prefix="sms_test"'], ) def test_delimiter_only(self): @@ -29,9 +31,7 @@ def test_delimiter_only(self): | | delimiter | | | | | | ~ | | | | """, - xml__contains=[ - 'odk:delimiter="~"' - ] + xml__contains=['odk:delimiter="~"'], ) def test_prefix_and_delimiter(self): @@ -45,10 +45,7 @@ def test_prefix_and_delimiter(self): | | delimiter | prefix | | | | | * | sms_test2| | | """, - xml__contains=[ - 'odk:delimiter="*"', - 'odk:prefix="sms_test2"' - ] + xml__contains=['odk:delimiter="*"', 'odk:prefix="sms_test2"'], ) def test_sms_tag(self): @@ -64,6 +61,6 @@ def test_sms_tag(self): xml__contains=[ '', '7', - '' - ] - ) \ No newline at end of file + "", + ], + ) diff --git a/pyxform/tests_v1/test_support_external_instances.py b/pyxform/tests_v1/test_support_external_instances.py index 6e34c0c67..1c3714f8b 100644 --- a/pyxform/tests_v1/test_support_external_instances.py +++ b/pyxform/tests_v1/test_support_external_instances.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +""" +Test external instance syntax +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase @@ -22,7 +26,7 @@ def test_external_csv_instances(self): """, # noqa '', - '', + "", """ @@ -32,7 +36,7 @@ def test_external_csv_instances(self): """, # noqa '', - '', # noqa + "", # noqa ], run_odk_validate=True, ) @@ -93,7 +97,7 @@ def test_external_xml_instances(self): """, # noqa '', - '', + "", """ @@ -103,7 +107,7 @@ def test_external_xml_instances(self): """, # noqa ' @@ -29,14 +32,14 @@ first row! -'''.strip() # nopep8 +""".strip() # nopep8 -class AreaTest(PyxformTestCase): - def test_area(self): +class TableListTest(PyxformTestCase): + def test_table_list(self): self.assertPyxformXform( name="table-list-appearance-mod", md=MD, xml__contains=[XML_CONTAINS], - debug=True + debug=True, ) diff --git a/pyxform/tests_v1/test_translations.py b/pyxform/tests_v1/test_translations.py index d8f571641..f5e3c974b 100644 --- a/pyxform/tests_v1/test_translations.py +++ b/pyxform/tests_v1/test_translations.py @@ -1,15 +1,19 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- +""" +Test translations syntax. +""" from pyxform.tests_v1.pyxform_test_case import PyxformTestCase class DoubleColonTranslations(PyxformTestCase): def test_langs(self): - model_contains = """""" + model_contains = ( + """""" + ) self.assertPyxformXform( - name='translations', - id_string='transl', + name="translations", + id_string="transl", md=""" | survey | | | | | | | type | name | label::english | label::french | @@ -19,17 +23,15 @@ def test_langs(self): itext__contains=[ '', '', - 'bonjour', - '', - '', + "bonjour", + "", + "", '', '', - 'hello', - '', - '', - ], - xml__contains=[ - """
', - '', - '', - '