def test_should_return_correct_date_format_for_year_or_monthyear_appearance_combined_With_other_appearance(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {'children': [ {u'type': u'date', u'name': u'birth_date', u'label': u'1. What is your date of birth?', u'control': {u'appearance': u'w2 year'}}, {u'type': u'date', u'name': u'birth_date2', u'label': u'2. What is your date of birth?', u'control': {u'appearance': u'w4 horizontal month-year'}}]} xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') self.assertEquals('yyyy', xls_form_parser._get_date_format(fields['children'][0])) self.assertEquals('mm.yyyy', xls_form_parser._get_date_format(fields['children'][1]))
def test_should_return_default_date_format_for_any_other_appearance(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {'children': [ {u'type': u'date', u'name': u'birth_date', u'label': u'1. What is your date of birth?', u'control': {u'appearance': u'w4'} }]} get_xform_dict.return_value = fields get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') self.assertEquals('dd.mm.yyyy', xls_form_parser._get_date_format(fields['children'][0]))
def test_sequence_of_the_mixed_type_fields_in_from_model_should_be_same_as_xlsform(self): parser = XlsFormParser(self.REPEAT, "My questionnaire") xls_parser_response = parser.parse() names = [f['code'] if f['type'] != 'field_set' else self._repeat_codes(f) for f in xls_parser_response.json_xform_data] expected_names = ['familyname', ['family',['name','age']], 'city', ['house',['name','room','numberofrooms']]] self.assertEqual(names, expected_names)
def test_should_populate_error_when_constraint_not_given_for_dw_idnr_question(self): with patch('datawinners.blue.xform_bridge.entity_type_already_defined') as is_entity_type_already_defined: with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') fields = [{u'name': u'my_unique', u'label': u'mu_uni', u'type': u'dw_idnr'}] get_xform_dict.return_value = fields is_entity_type_already_defined.return_value = True questions, errors, unique_id_errors = xls_form_parser._create_questions(fields) self.assertEqual(questions.__len__(), 0) self.assertEqual(errors, { u"The Identification Number Type (dw_idnr) is missing in the Constraints column. Please add the Identification Number Type and upload again. <a target='_blank'>Learn More</a> about how to manage Identification Numbers with XLSForms. "})
def test_should_populate_error_when_label_defined_in_single_explict_language(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') fields = {'children': [{u'bind': {u'required': u'yes'}, u'type': u'text', u'name': u'name', u'label': {u'english': u'1. What is your name?'}}, {u'bind': {u'required': u'yes'}, u'type': u'integer', u'name': u'age', u'label': {u'english': u'2. What is your age?'}}, {u'bind': {u'required': u'yes'}, u'type': u'text', u'name': u'new_loc', u'label': {u'english': u'1. What is your new loc?'}}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}]} get_xform_dict.return_value = fields actual_errors = xls_form_parser._validate_fields_are_recognised(fields['children']) self.assertEquals(actual_errors, {"XLSForm language settings."})
def test_should_convert_multi_select_question(self): xls_parser_response = XlsFormParser(self.MULTI_SELECT, u"My questionnairé").parse() mangroveService = MangroveService(self.mock_request, xls_parser_response=xls_parser_response) questionnaire_id = mangroveService.create_project() self.assertIsNotNone(questionnaire_id)
def test_all_fields_types_in_xlsform_is_converted_to_json(self): xls_parser_response = XlsFormParser(self.ALL_FIELDS, "My questionnaire").parse() expected_json = \ [{"code": "name", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a word", "name": "What is your name?", "parent_field_code": None, "title": "What is your name?", "default": None, "hint": None, "appearance": None, "required": True, "type": "text"}, # repeat {"code": "education", "is_entity_question": False, "relevant": None, "fieldset_type": "repeat", "name": "Education", "parent_field_code": None, "title": "Education", "default": None, "fields": [{"code": "degree", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a word", "name": "Degree name", "parent_field_code": "education", "title": "Degree name", "default": None, "hint": None, "appearance": None, "required": True, "type": "text"}, {"code": "completed_on", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a date in the following format: day.month.year. Example: 25.12.2011", "event_time_field_flag": False, "date_format": "dd.mm.yyyy", "name": "Degree completion year", "parent_field_code": "education", "title": "Degree completion year", "default": None, "hint": None, "appearance": None, "required": True, "type": "date"}], "instruction": "No answer required", "appearance": None, "required": False, "type": "field_set"}, # end repeat {"code": "age", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a number", "name": "What is your age?", "parent_field_code": None, "title": "What is your age?", "default": None, "hint": None, "appearance": None, "required": False, "type": "integer"}, {"code": "height", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a decimal or number", "name": "What is your height?", "parent_field_code": None, "title": "What is your height?", "default": None, "hint": None, "appearance": None, "required": False, "type": "integer"}, {"code": "fav_color", "constraint_message": None, "is_cascade": False, "has_other": False, "required": True, "xform_constraint": None, "relevant": None, "parent_field_code": None, "title": "Which colors you like?", "default": None, "hint": None, "appearance": None, "choices": [{"value": {"text": "Red", "val": "a"}}, {"value": {"text": "Blue", "val": "b"}}, {"value": {"text": "Green", "val": "c"}}], "is_entity_question": False, "type": "select"}, #group {"code": "pizza_test_group", "is_entity_question": False, "relevant": None, "fieldset_type": "group", "name": "Pizza fan", "parent_field_code": None, "title": "Pizza fan", "default": None, "fields": [{"code": "pizza_fan", "constraint_message": None, "is_cascade": False, "has_other": False, "required": True, "xform_constraint": None, "relevant": None, "parent_field_code": "pizza_test_group", "title": "Do you like pizza?", "default": None, "hint": None, "appearance": None, "choices": [{"value": {"text": "Yes", "val": "a"}}, {"value": {"text": "No", "val": "b"}}], "is_entity_question": False, "type": "select1"}, #group {"code": "like_group", "is_entity_question": False, "relevant": None, "fieldset_type": "group", "name": "Like group", "parent_field_code": "pizza_test_group", "title": "Like group", "default": None, "fields": [{"code": "other", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": "${pizza_fan} = 'b'", "instruction": "Answer must be a word", "name": "What else you like?", "parent_field_code": "like_group", "title": "What else you like?", "default": None, "hint": None, "appearance": None, "required": False, "type": "text"}, {"code": "pizza_type", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": "${pizza_fan} = 'a'", "instruction": "Answer must be a word", "name": "Which pizza type you like?", "parent_field_code": "like_group", "title": "Which pizza type you like?", "default": None, "hint": None, "appearance": None, "required": False, "type": "text"}], "instruction": "No answer required", "appearance": None, "required": False, "type": "field_set"}], "instruction": "No answer required", "appearance": None, "required": False, "type": "field_set"}, {"code": "location", "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a geopoint", "name": "Your location?", "parent_field_code": None, "title": "Your location?", "default": None, "hint": None, "appearance": None, "required": False, "type": "geocode"}, {"code": "add_age_height", "is_calculated": True, "constraint_message": None, "is_entity_question": False, "xform_constraint": None, "relevant": None, "instruction": "Answer must be a calculated field", "name": "Age and height", "parent_field_code": None, "title": "Age and height", "default": None, "hint": None, "appearance": None, "required": False, "type": "text"}, {"code": "ab", "constraint_message": None, "is_cascade": False, "has_other": False, "required": True, "xform_constraint": None, "relevant": None, "parent_field_code": None, "title": "A or B?", "default": None, "hint": None, "appearance": None, "choices": [{"value": {"text": "A", "val": "a"}}, {"value": {"text": "B", "val": "b"}}], "is_entity_question": False, "type": "select1"}] self.assertEqual(expected_json, xls_parser_response.json_xform_data) self.assertIsNotNone(xls_parser_response.xform_as_string)
def test_should_convert_skip_logic_question(self): xls_parser_response = XlsFormParser(self.SKIP, u"My questionnairé").parse() mangroveService = MangroveService(self.mock_request, xls_parser_response=xls_parser_response) questionnaire_id = mangroveService.create_project() self.assertIsNotNone(questionnaire_id)
def test_should_not_populate_errors_when_choice_answer_has_no_media_present(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {u'name': u'tmpGX1Ud_', u'title': u'tmpGX1Ud_', u'sms_keyword': u'tmpGX1Ud_', u'default_language': u'default', u'id_string': u'tmpGX1Ud_', u'type': u'survey', u'children': [ {u'choices': [{u'name': u'crow', u'label': u'crow'}, {u'name': u'eagle', u'label': u'Eagle'}], u'label': u'What bird did you see?', u'type': u'select one', u'name': u'bird', u'hint': u'Some birds have included images or audio.'}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}]} get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', u'questionnaire_name') xls_parser_response = xls_form_parser.parse() self.assertEquals(xls_parser_response.errors, [])
def test_should_create_project_using_xlsform_file(self): xls_parser_response = XlsFormParser(self.ALL_FIELDS, u"My questionnairé").parse() mangroveService = MangroveService(self.mock_request, xls_parser_response=xls_parser_response) quesionnaire_id = mangroveService.create_project() self.assertIsNotNone(quesionnaire_id)
def test_sequence_of_the_fields_in_form_model_should_be_same_as_in_xlsform(self): xls_parser_response = XlsFormParser(self.MANY_FIELD, "My questionnaire").parse() self.assertIsNotNone(xls_parser_response.xform_as_string) names = [f['code'] for f in xls_parser_response.json_xform_data] expected_names = ["a312name1312","a528name2528","a972name3972","a667name4667","a868name5868","a970name6970","a870name7870","a320name8320","a863name9863","a509name10509","a191name11191","a216name12216","a320name13320","a165name14165","a116name15116","a413name16413","a568name17568","a379name18379","a863name19863","a929name20929","a640name21640","a392name22392","a264name23264","a868name24868","a191name25191","a316name26316","a908name27908","a488name28488","a455name29455","a802name30802","a595name31595","a668name32668","a329name33329","a566name34566","a335name35335","a197name36197","a536name37536","a204name38204","a418name39418","a399name40399","a614name41614","a510name42510","a515name43515","a835name44835","a575name45575","a531name46531","a247name47247","a143name48143","a811name49811","a110name50110"] self.assertEqual(names, expected_names)
def test_should_populate_error_when_settings_sheet_present_with_default_language(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {'children': [{u'bind': {u'required': u'no'}, u'type': u'text', u'name': u'college', u'label': u'College Name'}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}], 'title': 'asdasx', 'name': 'asdasx', 'id_string': 'asdasx', 'default_language': 'default_something' } get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') xls_parser_response = xls_form_parser.parse() self.assertEquals(xls_parser_response.errors, {"XLSForm settings worksheet and the related values in survey sheet."})
def test_should_populate_error_when_calculate_field_with_prefetch_present(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {'children': [ {u'bind': {u'calculate': u'pulldata(fruit, "mangoes")'}, u'type': u'calculate', u'name': u'calc', u'label': u'1. Are you a student?'}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}], 'title': 'asdasx', 'name': 'asdasx', 'id_string': 'asdasx', 'default_language': 'default' } get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') xls_parser_response = xls_form_parser.parse() self.assertEquals(xls_parser_response.errors, {"preloading of CSV data (the PullData() function)."})
def test_xform_validation_for_nested_repeats_names(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') fields = { 'children': [{u'bind': {u'required': u'yes'}, u'type': u'text', u'name': u'name', u'label': u'Name'}, {u'children': [{u'children': [ {u'bind': {u'required': u'no'}, u'type': u'text', u'name': u'college', u'label': u'College Name'}], u'type': u'repeat', u'name': u'some', u'label': u'some'}], u'type': u'repeat', u'name': u'highest_degree', u'label': u'degree'}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}]} get_xform_dict.return_value = fields actual_errors = xls_form_parser._validate_fields_are_recognised(fields['children']) self.assertEqual(actual_errors, {"more than one level of repeated questions."})
def test_should_verify_repeat_field_added_to_questionnaire(self): xls_parser_response = XlsFormParser(self.REPEAT, u"My questionnairé").parse() mangroveService = MangroveService(self.mock_request, xls_parser_response=xls_parser_response) mangroveService.create_project() questionnaire_code = mangroveService.questionnaire_code mgr = mangroveService.manager from_model = get_form_model_by_code(mgr, questionnaire_code) self.assertNotEqual([], [f for f in from_model.fields if type(f) is FieldSet and f.fields])
def test_should_verify_field_is_not_mandatory_when_required_is_not_specified(self): xls_parser_response = XlsFormParser(self.REQUIRED, "My questionnaire").parse() root = ET.fromstring(xls_parser_response.xform_as_string) ET.register_namespace('', 'http://www.w3.org/2002/xforms') binds = [node.attrib.get('required') for node in root.iter('{http://www.w3.org/2002/xforms}bind')] self.assertEqual('true()', binds[0]) self.assertEqual(None, binds[1])
def test_should_not_populate_error_when_default_choice_for_multi_select_is_present_in_choice_list(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {'children': [{u'bind': {u'required': u'yes'}, u'type': u'select all that apply or specify other', 'default': "yes no", u'name': u'is_student', u'label': u'1. Are you a student?', u'choices': [{u'name': u'yes', u'label': 'yes'}, {u'name': u'no', u'label': u'No'}]}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}], 'title': 'asdasx', 'name': 'asdasx', 'id_string': 'asdasx', 'default_language': 'default' } xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') errors = xls_form_parser._validate_choice_names(fields['children']) self.assertEquals(errors, [])
def test_should_populate_error_when_choice_has_no_label(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {'children': [{u'bind': {u'required': u'yes'}, u'type': u'select one', u'name': u'is_student', u'label': u'1. Are you a student?', u'choices': [{u'name': u'yes'}, {u'name': u'no', u'label': u'No'}]}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}], 'title': 'asdasx', 'name': 'asdasx', 'id_string': 'asdasx', 'default_language': 'default' } get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') xls_parser_response = xls_form_parser.parse() self.assertEquals(xls_parser_response.errors, {"optional labels. Label is a mandatory field for choice option with name [yes]"})
def test_should_not_create_question_for_select_that_are_only_labels(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') fields = [{u'control': {u'appearance': u'label'}, u'name': u'table_list_test_label', u'hint': u'Show only the labels of these options and not the inputs (type=select_one yes_no, appearance=label)', u'choices': [{u'name': u'yes', u'label': u'Yes'}, {u'name': u'no', u'label': u'No'}, {u'name': u'dk', u'label': u"Don't Know"}, {u'name': u'na', u'label': u'Not Applicable'}], u'label': u'Table', u'type': u'select one'}, {u'control': {u'appearance': u'list-nolabel'}, u'name': u'table_list_1', u'hint': u'Show only the inputs of these options and not the labels (type=select_one yes_no, appearance=list-nolabel)', u'choices': [{u'name': u'yes', u'label': u'Yes'}, {u'name': u'no', u'label': u'No'}, {u'name': u'dk', u'label': u"Don't Know"}, {u'name': u'na', u'label': u'Not Applicable'}], u'label': u'Q1', u'type': u'select one'}, {u'control': {u'appearance': u'list-nolabel'}, u'name': u'table_list_2', u'hint': u'Show only the inputs of these options and not the labels (type=select_one yes_no, appearance=list-nolabel)', u'choices': [{u'name': u'yes', u'label': u'Yes'}, {u'name': u'no', u'label': u'No'}, {u'name': u'dk', u'label': u"Don't Know"}, {u'name': u'na', u'label': u'Not Applicable'}], u'label': u'Question 2', u'type': u'select one'}, {'control': {'bodyless': True}}] questions, errors, unique_id_errors = xls_form_parser._create_questions(fields) self.assertEqual(questions.__len__(), 2) self.assertDictEqual(questions[0], {'code': u'table_list_1', 'title': u'Q1', 'required': False, 'parent_field_code': None, 'has_other': False, 'choices': [{'value': {'text': u'Yes', 'val': u'yes'}}, {'value': {'text': u'No', 'val': u'no'}}, {'value': {'text': u"Don't Know", 'val': u'dk'}}, {'value': {'text': u'Not Applicable', 'val': u'na'}}], 'is_entity_question': False, 'type': 'select1'}) self.assertDictEqual(questions[1], {'code': u'table_list_2', 'title': u'Question 2', 'required': False, 'parent_field_code': None, 'has_other': False, 'choices': [{'value': {'text': u'Yes', 'val': u'yes'}}, {'value': {'text': u'No', 'val': u'no'}}, {'value': {'text': u"Don't Know", 'val': u'dk'}}, {'value': {'text': u'Not Applicable', 'val': u'na'}}], 'is_entity_question': False, 'type': 'select1'})
def test_should_create_entity_question_for_dw_idnr_question(self): with patch('datawinners.blue.xform_bridge.entity_type_already_defined') as is_entity_type_already_defined: with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: manager = Mock(DatabaseManager) manager.view = Mock(View) manager.view.count_non_voided_entities_by_type = Mock( return_value=[Row({'key': ['clinic'], 'value': 10})]) xls_form_parser = XlsFormParser('some_path', 'questionnaire_name', dbm=manager) fields = [{u'name': u'my_unique', u'bind': {u'constraint': u'clinic'}, u'label': u'mu_uni', u'type': u'dw_idnr'}] get_xform_dict.return_value = fields is_entity_type_already_defined.return_value = True questions, errors, unique_id_errors = xls_form_parser._create_questions(fields) self.assertEqual(questions.__len__(), 1) self.assertDictEqual(questions[0], {'instruction': 'Answer must be a Identification Number', 'code': u'my_unique', 'title': u'mu_uni', 'required': False, 'parent_field_code': None, 'name': u'mu_uni', 'is_entity_question': True, 'type': 'unique_id', 'uniqueIdType': u'clinic'})
def test_should_populate_error_when_media_type_present_as_a_data_type(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {u'children': [{u'name': u'bird', u'hint': u'Some birds have included images or audio.', u'media': {u'audio': u'question.wav'}, u'choices': [{u'name': u'eagle', u'label': u'Eagle'}], u'label': u'What bird did you see?', u'type': u'select one'}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}], u'type': u'repeat', u'name': u'myrepeat', u'label': u'My repeat', 'title': 'asdasx', 'name': 'asdasx', 'id_string': 'asdasx', 'default_language': 'default' } get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') xls_parser_response = xls_form_parser.parse() self.assertEquals(xls_parser_response.errors, {"XLSForm media type (audio) in survey sheet."})
def test_should_populate_error_when_unsupported_geoshape_question_present_within_a_repeat(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: fields = {u'children': [ {u'children': [ {u'bind': {u'required': u'yes'}, u'type': u'geoshape', u'name': u'timeq', u'label': u'Time q'}, {u'bind': {u'required': u'yes'}, u'type': u'datetime', u'name': u'datetq', u'label': u'Date time q'}], u'type': u'group', u'name': u'mygroup', u'label': u'My group' }], u'type': u'repeat', u'name': u'myrepeat', u'label': u'My repeat', 'title': 'asdasx', 'name': 'asdasx', 'id_string': 'asdasx', 'default_language': 'default' } get_xform_dict.return_value = fields xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') xls_parser_response = xls_form_parser.parse() self.assertEquals(xls_parser_response.errors, {"geoshape as a datatype"})
def test_should_verify_xform_is_stored_when_project_created(self): manager = get_database_manager(self.user) questionnaire_code = generate_questionnaire_code(manager) project_name = 'xform-' + questionnaire_code xls_parser_response = XlsFormParser(self.REPEAT, u"My questionnairé").parse() mangrove_service = MangroveService(self.mock_request, project_name=project_name, xls_parser_response=xls_parser_response) mangrove_service.create_project() questionnaire_code = mangrove_service.questionnaire_code mgr = mangrove_service.manager from_model = get_form_model_by_code(mgr, questionnaire_code) self.assertIsNotNone(from_model.xform)
def test_should_create_additional_text_question_for_single_select_or_other_question(self): with patch('datawinners.blue.xform_bridge.parse_file_to_json') as get_xform_dict: xls_form_parser = XlsFormParser('some_path', 'questionnaire_name') fields = [{u'choices': [{u'name': u'male', u'label': u'Male'}, {u'name': u'female', u'label': u'Female'}], u'type': u'select one or specify other', u'name': u'hh_user_gender', u'label': u'Sex'}, {'control': {'bodyless': True}, 'type': 'group', 'name': 'meta', 'children': [ {'bind': {'readonly': 'true()', 'calculate': "concat('uuid:', uuid())"}, 'type': 'calculate', 'name': 'instanceID'}]}] questions, errors, unique_id_errors = xls_form_parser._create_questions(fields) self.assertEqual(questions.__len__(), 2) self.assertDictEqual(questions[0], {'code': u'hh_user_gender', 'title': u'Sex', 'required': False, 'parent_field_code': None, 'has_other': True, 'choices': [{'value': {'text': u'Male', 'val': u'male'}}, {'value': {'text': u'Female', 'val': u'female'}}], 'is_entity_question': False, 'type': 'select1'}) self.assertDictEqual(questions[1], {'code': u'hh_user_gender_other', 'title': u'Sex_other', 'required': False, 'parent_field_code': None, 'name': u'Sex_other', 'instruction': 'Answer must be a word', 'is_entity_question': False, 'type': u'text'})
def test_should_convert_cascaded_select_field(self): xls_parser_response = XlsFormParser(self.CASCADE, "My questionnaire").parse() expected_json = [{'code': 'name', 'name': 'What is your name?', 'title': 'What is your name?', 'required': False, 'is_entity_question': False, 'instruction': 'Answer must be a word', 'parent_field_code': None, 'type': 'text'}, {'code': 'respondent_district_counties', 'parent_field_code': None, 'title': 'Please select the county', 'required': False, 'has_other': False, 'choices': [{'value': {'text': 'Bomi', 'val': 'bomi'}}, {'value': {'text': 'Grand Bassa', 'val': 'grand_bassa'}}], 'is_entity_question': False, 'type': 'select1'}, {'code': 'respondent_district', 'parent_field_code': None, 'title': 'Please select the district', 'required': False, 'has_other': False, 'choices': [{'value': {'text': 'Klay', 'val': 'klay'}}, {'value': {'text': 'Commonwealth 1', 'val': 'commonwealth_1'}}], 'is_entity_question': False, 'type': 'select1'}] self.assertEqual(expected_json, xls_parser_response.json_xform_data)
def test_all_fields_types_in_xlsform_is_converted_to_json(self): xls_parser_response = XlsFormParser(self.ALL_FIELDS, "My questionnaire").parse() expected_json = \ [{'code': 'name', 'parent_field_code': None, 'name': 'What is your name?', 'title': 'What is your name?', 'required': True, 'is_entity_question': False, 'instruction': 'Answer must be a word', 'type': 'text'}, # repeat {'code': 'education', 'parent_field_code': None, 'instruction': 'No answer required', 'name': 'Education', 'title': 'Education', 'fields': [{'code': 'degree', 'parent_field_code': u'education', 'name': 'Degree name', 'title': 'Degree name', 'required': True, 'is_entity_question': False, 'instruction': 'Answer must be a word', 'type': 'text'}, {'code': 'completed_on', 'parent_field_code': u'education', 'date_format': 'dd.mm.yyyy', 'name': 'Degree completion year', 'title': 'Degree completion year', 'required': True, 'is_entity_question': False, 'instruction': 'Answer must be a date in the following format: day.month.year. Example: 25.12.2011','event_time_field_flag': False, 'type': 'date'}], 'is_entity_question': False, 'type': 'field_set', 'required': False, 'fieldset_type': 'repeat'}, # end repeat {'code': 'age', 'parent_field_code': None, 'name': 'What is your age?', 'title': 'What is your age?', 'required': False, 'is_entity_question': False, 'instruction': 'Answer must be a number', 'type': 'integer'}, {'code': 'height', 'parent_field_code': None, 'name': 'What is your height?', 'title': 'What is your height?', 'required': False, 'is_entity_question': False, 'instruction': 'Answer must be a decimal or number', 'type': 'integer'}, {'code': 'fav_color', 'parent_field_code': None, 'title': 'Which colors you like?', 'required': True, 'has_other': False, 'choices': [{'value':{'text': 'Red', 'val': 'a'}}, {'value': {'text': 'Blue', 'val': 'b'}}, {'value':{'text': 'Green', 'val': 'c'}}], 'is_entity_question': False, 'type': 'select'}, #group {'code': u'pizza_test_group', 'parent_field_code': None, 'instruction': 'No answer required', 'name': u'Pizza fan', 'title': u'Pizza fan', 'fields': [{'code': u'pizza_fan', 'parent_field_code': u'pizza_test_group', 'title': u'Do you like pizza?', 'required': True, 'has_other': False, 'choices': [{'value': {'text': u'Yes', 'val': u'a'}}, {'value': {'text': u'No', 'val': u'b'}}], 'is_entity_question': False, 'type': 'select1'}, #group {'code': u'like_group', 'parent_field_code': u'pizza_test_group', 'instruction': 'No answer required', 'name': u'Like group', 'title': u'Like group', 'fields': [ {'code': u'other', 'parent_field_code': u'like_group', 'name': u'What else you like?', 'title': u'What else you like?', 'required': False, 'is_entity_question': False, 'instruction': 'Answer must be a word', 'type': u'text'}, {'code': u'pizza_type', 'parent_field_code': u'like_group', 'name': u'Which pizza type you like?', 'title': u'Which pizza type you like?', 'required': False, 'is_entity_question': False, 'instruction': 'Answer must be a word', 'type': u'text'}], 'is_entity_question': False, 'type': 'field_set', 'fieldset_type': 'group', 'required': False}], 'is_entity_question': False, 'type': 'field_set', 'fieldset_type': 'group', 'required': False}, {'code': 'location', 'parent_field_code': None, 'name': 'Your location?', 'title': 'Your location?', 'required': False, 'is_entity_question': False, 'instruction': 'Answer must be a geopoint', 'type': 'geocode'}, {'code': 'add_age_height', 'parent_field_code': None, 'name': 'Age and height', 'title': 'Age and height', 'required': False, 'is_calculated': True, 'is_entity_question': False, 'instruction': 'Answer must be a calculated field', 'type': 'text'}, {'code': 'ab','parent_field_code': None, 'title': 'A or B?', 'required': True, 'has_other': False, 'choices': [{'value':{'text': 'A', 'val': 'a'}}, {'value':{'text': 'B', 'val': 'b'}}], 'is_entity_question': False, 'type': 'select1'}] self.assertEqual(expected_json, xls_parser_response.json_xform_data) self.assertIsNotNone(xls_parser_response.xform_as_string)
def test_should_throw_error_for_invalid_field_type(self): xls_parser_response = XlsFormParser(self.INVALID_FIELDS, u"My questionnairé").parse() self.assertEqual(xls_parser_response.errors, set(["dfdfd as a datatype"]))
def test_should_throw_error_for_unsupported_valid_field_type(self): xls_parser_response = XlsFormParser(self.UNSUPPORTED_FIELDS, u"My questionnairé").parse() self.assertEqual(xls_parser_response.errors, set(["geoshape as a datatype"]))
def post(self, request): file_content = None tmp_file = None try: file_content = request.raw_post_data file_errors, file_extension = _perform_file_validations(request) tmp_file = NamedTemporaryFile(delete=True, suffix=file_extension) if file_errors: logger.info( "User: %s. Upload File validation failed: %s. File name: %s, size: %d", request.user.username, json.dumps(file_errors), request.GET.get("qqfile"), int(request.META.get('CONTENT_LENGTH'))) return HttpResponse(json.dumps({ 'success': False, 'error_msg': file_errors }), content_type='application/json') tmp_file.write(file_content) tmp_file.seek(0) project_name = request.GET['pname'].strip() manager = get_database_manager(request.user) questionnaire_code = generate_questionnaire_code(manager) xls_parser_response = XlsFormParser(tmp_file, project_name, manager).parse() send_email_if_unique_id_type_question_has_no_registered_unique_ids( xls_parser_response, request, project_name) profile = request.user.get_profile() organization = Organization.objects.get(org_id=profile.org_id) if xls_parser_response.is_multiple_languages: logger.info( "Creating Questionnaire %s with Multi Language support for organization : %s(%s) and email: %s", project_name, organization.name, profile.org_id, profile.user.email) if xls_parser_response.errors: error_list = list(xls_parser_response.errors) logger.info("User: %s. Upload Errors: %s", request.user.username, json.dumps(error_list)) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': error_list, 'message_prefix': _("Sorry! Current version of DataWinners does not support" ), 'message_suffix': _("Update your XLSForm and upload again.") })) tmp_file.seek(0) mangrove_service = MangroveService( request, questionnaire_code=questionnaire_code, project_name=project_name, xls_form=tmp_file, xls_parser_response=xls_parser_response) questionnaire_id, form_code = mangrove_service.create_project() except PyXFormError as e: logger.info("User: %s. Upload Error: %s", request.user.username, e.message) message = transform_error_message(e.message) if 'name_type_error' in message or 'choice_name_type_error' in message: if 'choice_name_type_error' in message: message_prefix = _( "On your \"choices\" sheet the first and second column must be \"list_name\" and \"name\". Possible errors:" ) else: message_prefix = _( "On your \"survey\" sheet the first and second column must be \"type\" and \"name\". Possible errors:" ) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ _("Columns are missing"), _("Column name is misspelled"), _("Additional space in column name") ], 'message_prefix': message_prefix, 'message_suffix': _("Update your XLSForm and upload again.") })) else: return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ message if message else ugettext( "all XLSForm features. Please check the list of unsupported features." ) ] })) except QuestionAlreadyExistsException as e: logger.info("User: %s. Upload Error: %s", request.user.username, e.message) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ _("Duplicate labels. All questions (labels) must be unique." ) ], 'message_prefix': _("Sorry! Current version of DataWinners does not support" ), 'message_suffix': _("Update your XLSForm and upload again.") })) except UnicodeDecodeError as e: logger.info("User: %s. Upload Error: %s", request.user.username, e.message) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ _("Check your columns for errors.<br>There are missing symbols (like $ for relevant or calculate) or incorrect characters<br>" ) + _("Update your XLSForm and upload again.") ], })) except Exception as e: message = e.message if e.message else _("Errors in excel") logger.info("User: %s. Upload Exception message: %s", request.user.username, e.message) odk_message = '' if not 'ODK Validate Errors:' in e.message: send_email_on_exception( request.user, "Questionnaire Create", traceback.format_exc(), additional_details={'file_contents': file_content}) else: odk_message = translate_odk_message(e.message) message = odk_message if odk_message else message return HttpResponse(content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [message], })) finally: if tmp_file: tmp_file.close() if not questionnaire_id: org = get_organization(request) if org.is_pro_sms: message = _( "Questionnaire or Poll with same name already exists.Upload was cancelled." ) else: message = _( "Questionnaire with same name already exists.Upload was cancelled." ) return HttpResponse(json.dumps({ 'success': False, 'duplicate_project_name': True, 'error_msg': [message] }), content_type='application/json') return HttpResponse(json.dumps({ "success": True, "project_name": project_name, "project_id": questionnaire_id, "form_code": form_code }), content_type='application/json')
def post(self, request, project_id): manager = get_database_manager(request.user) questionnaire = Project.get(manager, project_id) file_content = None tmp_file = None try: file_content = request.raw_post_data file_errors, file_extension = _perform_file_validations(request) tmp_file = NamedTemporaryFile(delete=True, suffix=file_extension) if file_errors: logger.info( "User: %s. Edit upload File validation failed: %s. File name: %s, size: %d", request.user.username, json.dumps(file_errors), request.GET.get("qqfile"), int(request.META.get('CONTENT_LENGTH'))) return HttpResponse(content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': file_errors })) tmp_file.write(file_content) tmp_file.seek(0) xls_parser_response = XlsFormParser(tmp_file, questionnaire.name, manager).parse() send_email_if_unique_id_type_question_has_no_registered_unique_ids( xls_parser_response, request, questionnaire.name) profile = request.user.get_profile() organization = Organization.objects.get(org_id=profile.org_id) if xls_parser_response.is_multiple_languages: logger.info( "Edit Questionnaire %s with Multi Language support for organization : %s(%s) and email: %s", questionnaire.name, organization.name, profile.org_id, profile.user.email) if xls_parser_response.errors: info_list = list(xls_parser_response.errors) logger.info("User: %s. Edit upload Errors: %s", request.user.username, json.dumps(info_list)) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': info_list, 'message_prefix': _("Sorry! Current version of DataWinners does not support" ), 'message_suffix': _("Update your XLSForm and upload again.") })) mangrove_service = MangroveService( request, questionnaire_code=questionnaire.form_code, project_name=questionnaire.name, xls_parser_response=xls_parser_response) questionnaire.xform = mangrove_service.xform_with_form_code QuestionnaireBuilder(questionnaire, manager).update_questionnaire_with_questions( xls_parser_response.json_xform_data) tmp_file.seek(0) questionnaire.update_media_field_flag() questionnaire.save(process_post_update=False) base_name, extension = os.path.splitext(tmp_file.name) questionnaire.update_attachments(tmp_file, 'questionnaire%s' % extension) self._purge_submissions(manager, questionnaire) self._purge_feed_documents(questionnaire, request) self._purge_media_details_documents(manager, questionnaire) self.recreate_submissions_mapping(manager, questionnaire) if xls_parser_response.info: info_list = list(xls_parser_response.info) logger.info("User: %s. Edit upload Errors: %s", request.user.username, json.dumps(info_list)) return HttpResponse(content_type='application/json', content=json.dumps({ 'success': True, 'information': info_list, })) except PyXFormError as e: logger.info("User: %s. Upload Error: %s", request.user.username, e.message) message = transform_error_message(e.message) if 'name_type_error' in message or 'choice_name_type_error' in message: if 'choice_name_type_error' in message: message_prefix = _( "On your \"choices\" sheet the first and second column must be \"list_name\" and \"name\". Possible errors:" ) else: message_prefix = _( "On your \"survey\" sheet the first and second column must be \"type\" and \"name\". Possible errors:" ) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ _("Columns are missing"), _("Column name is misspelled"), _("Additional space in column name") ], 'message_prefix': message_prefix, 'message_suffix': _("Update your XLSForm and upload again.") })) else: return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ message if message else ugettext( "all XLSForm features. Please check the list of unsupported features." ) ] })) except QuestionAlreadyExistsException as e: logger.info("User: %s. Upload Error: %s", request.user.username, e.message) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ _("Duplicate labels. All questions (labels) must be unique." ) ], 'message_prefix': _("Sorry! Current version of DataWinners does not support" ), 'message_suffix': _("Update your XLSForm and upload again.") })) except UnicodeDecodeError as e: logger.info("User: %s. Upload Error: %s", request.user.username, e.message) return HttpResponse( content_type='application/json', content=json.dumps({ 'success': False, 'error_msg': [ _("Check your columns for errors.<br>There are missing symbols (like $ for relevant or calculate) or incorrect characters<br>" ) + _("Update your XLSForm and upload again.") ], })) except Exception as e: logger.info("User: %s. Edit Upload Exception message: %s", request.user.username, e.message) message = e.message if e.message else _("Some error in excel") odk_message = '' if not 'ODK Validate Errors:' in e.message: send_email_on_exception( request.user, "Questionnaire Edit", traceback.format_exc(), additional_details={'file_contents': file_content}) else: odk_message = translate_odk_message(e.message) message = odk_message if odk_message else message return HttpResponse(content_type='application/json', content=json.dumps({ 'error_msg': [message], 'success': False, })) finally: if tmp_file: tmp_file.close() return HttpResponse( json.dumps({ "success": True, "project_name": questionnaire.name, "project_id": questionnaire.id, "file_name": "%s%s" % (slugify(questionnaire.name), extension), # "xls_dict": XlsProjectParser().parse(file_content) }), content_type='application/json')