Skip to content

Commit

Permalink
Merge pull request #55 from XLSForm/issue-54
Browse files Browse the repository at this point in the history
Allows "value" as an alias for "name"
  • Loading branch information
ukanga committed Dec 25, 2015
2 parents c4745e7 + 896956b commit 1192bb0
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 49 deletions.
2 changes: 1 addition & 1 deletion pyxform/aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
u"video": u"media::video"
}
# Note that most of the type aliasing happens in all.xls
type = {
_type = {
u"imei": u"deviceid",
u"image": u"photo",
u"add image prompt": u"photo",
Expand Down
Binary file not shown.
Binary file not shown.
18 changes: 0 additions & 18 deletions pyxform/tests/bug_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,24 +229,6 @@ def runTest(self):
"Found " + str(len(warnings)) + " warnings")


class MissingHeaderColumnsTest(unittest.TestCase):
def test_choices_headers_missing(self):
filename = "no_header_on_choices_sheet.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)
with self.assertRaises(pyxform.errors.PyXFormError):
pyxform.xls2json.workbook_to_json(workbook_dict)

def test_survey_headers_missing(self):
filename = "no_header_on_survey_sheet.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)
with self.assertRaises(pyxform.errors.PyXFormError):
pyxform.xls2json.workbook_to_json(workbook_dict)


class TestChoiceNameAsType(unittest.TestCase):
def test_choice_name_as_type(self):
filename = "choice_name_as_type.xls"
Expand Down
33 changes: 33 additions & 0 deletions pyxform/tests_v1/test_bug_missing_headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from pyxform_test_case import PyxformTestCase


class MissingHeaders(PyxformTestCase):
'''
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="""
| survey | | |
| | select_one list | S1 |
""",
errored=True,
error__contains=[
'missing important column headers',
])

def test_missing_choice_headers(self):
self.assertPyxformXform(
md="""
| survey | | | |
| | type | label | name |
| | select_one list | S1 | s1 |
| choices | | | |
| | list | option a | a |
| | list | option b | b |
""",
errored=True,
error__contains=[
"has columns 'list name', 'name', and 'label'",
])
134 changes: 134 additions & 0 deletions pyxform/tests_v1/test_sheet_columns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from pyxform_test_case import PyxformTestCase


class InvalidSurveyColumnsTests(PyxformTestCase):
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'}]},
errored=True,
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'}]},
errored=False,
)

def test_missing_label(self):
self.assertPyxformXform(
name="invalidcols",
ss_structure={'survey': [{'type': 'text',
'name': 'q1'}]},
errored=True,
error__contains=['no label or hint'],
)


class InvalidChoiceSheetColumnsTests(PyxformTestCase):
def _simple_choice_ss(self, 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):
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'}]),
errored=False,
)

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'},
]),
errored=True,
error__contains=['option with no name'],
)

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'},
]),
debug=True,
errored=True,
# some basic keywords that should be in the error:
error__contains=[
'choices',
'name',
'list name',
])


class AliasesTests(PyxformTestCase):
def test_value_and_name(self):
'''
confirm that both 'name' and 'value' both compile to xforms with the
correct output.
'''
for name_alias in ['name', 'value']:
self.assertPyxformXform(
name="aliases",
md="""
| survey | | | |
| | type | %(name_alias)s | label |
| | text | q1 | Question 1 |
""" % ({
u'name_alias': name_alias
}),
instance__contains=[
'<q1/>',
],
model__contains=[
'<bind nodeset="/aliases/q1" type="string"/>',
],
xml__contains=[
'<input ref="/aliases/q1">',
'<label>Question 1</label>',
'</input>',
])
''' # uncomment when re-implemented
# TODO: test that this fails for the correct reason
def test_conflicting_aliased_values_raises_error(self):
# example:
# an xlsform has {"name": "q_name", "value": "q_value"}
# should not compile because "name" and "value" columns are aliases
self.assertPyxformXform(
# debug=True,
name="aliases",
md="""
| survey | | | | |
| | type | name | value | label |
| | text | q_name | q_value | Question 1 |
""",
errored=True,
)
'''
40 changes: 10 additions & 30 deletions pyxform/xls2json.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ def dealias_types(dict_array):
"""
for row in dict_array:
found_type = row.get(constants.TYPE)
if found_type in aliases.type.keys():
row[constants.TYPE] = aliases.type[found_type]
if found_type in aliases._type.keys():
row[constants.TYPE] = aliases._type[found_type]
return dict_array


Expand Down Expand Up @@ -242,7 +242,7 @@ def add_flat_annotations(prompt_list, parent_relevant='', name_prefix=''):

def workbook_to_json(
workbook_dict, form_name=None,
default_language=u"default", warnings=None):
default_language=u"default", warnings=[]):
"""
workbook_dict -- nested dictionaries representing a spreadsheet.
should be similar to those returned by xls_to_dict
Expand All @@ -262,31 +262,10 @@ def workbook_to_json(
json form spec.
"""
# ensure required headers are present
survey_header_sheet = u'%s_header' % constants.SURVEY
if survey_header_sheet in workbook_dict:
survey_headers = workbook_dict.get(survey_header_sheet)
if not survey_headers:
raise PyXFormError(u"The survey sheet is missing column headers.")
tmp = [h for h in [u'type', u'name'] if h in survey_headers[0].keys()]
if tmp.__len__() is not 2:
raise PyXFormError(u"The survey sheet must have on the first row"
u" name and type columns.")
del workbook_dict[survey_header_sheet]
choices_header_sheet = u'%s_header' % constants.CHOICES
if choices_header_sheet in workbook_dict:
choices_headers = workbook_dict.get(choices_header_sheet)
if not choices_headers:
raise PyXFormError(u"The choices sheet is missing column headers.")
choices_header_list = [u'list name', u'list_name', u'name']
tmp = [
h for h in choices_header_list if h in choices_headers[0].keys()]
if tmp.__len__() is not 2:
raise PyXFormError(u"The choices sheet must have on the first row"
u" list_name and name.")
del workbook_dict[choices_header_sheet]
if warnings is None:
# Set warnings to a list that will be discarded.
warnings = []
survey_rows = workbook_dict.get('survey', [])
if len(survey_rows) is 0 or 'type' not in survey_rows[0]:
raise PyXFormError(u"The survey sheet is either empty or missing important "
u"column headers.")

rowFormatString = '[row : %s]'

Expand Down Expand Up @@ -681,7 +660,8 @@ def replace_prefix(d, prefix):
raise PyXFormError(
u"There should be a choices sheet in this xlsform."
u" Please ensure that the choices sheet name is "
u"all in small caps.")
u"all in small caps and has columns 'list name', "
u"'name', and 'label' (or aliased column names).")
raise PyXFormError(
rowFormatString % row_number +
" List name not in choices sheet: " + list_name)
Expand Down Expand Up @@ -876,7 +856,7 @@ def get_filename(path):


def parse_file_to_json(path, default_name=None, default_language=u"default",
warnings=None, file_object=None):
warnings=[], file_object=None):
"""
A wrapper for workbook_to_json
"""
Expand Down

0 comments on commit 1192bb0

Please sign in to comment.