def _save_to_case_root_node(self, form_unique_id, question): """Add an extra node with the root path of the save to case to attach case properties to """ question_path = self._save_to_case_root_path(question) response = FormQuestionResponse({ "label": question_path, "tag": question_path, "value": question_path, "repeat": question['repeat'], "group": question['group'], "type": 'SaveToCase', "hashtagValue": question['hashtagValue'], "relevant": None, "required": False, "comment": None, "constraint": None, }).to_json() response['load_properties'] = self._case_meta.get_load_properties( form_unique_id, question_path) response['save_properties'] = self._case_meta.get_save_properties( form_unique_id, question_path) self._seen_save_to_case[form_unique_id].append(question_path) return response
def test_repeat_empty(self): questions_json = [{ 'tag': 'repeat', 'type': 'Repeat', 'label': 'Repeat', 'value': '/data/question12', }] form_data = { "@uiVersion": "1", "@xmlns": "http://openrosa.org/formdesigner/432B3A7F-6EEE-4033-8740-ACCB0804C4FC", "@name": "Untitled Form", "#type": "data", "question12": ["", ""], "meta": { "@xmlns": "http://openrosa.org/jr/xforms", "username": "******", "instanceID": "172981b6-5eeb-4be8-bbc7-ad52f808e803", "userID": "a07d4bd967a9c205287f767509600931", "timeEnd": "2014-04-28T18:27:05Z", "appVersion": { "@xmlns": "http://commcarehq.org/xforms", "#text": "CommCare ODK, version \"2.11.0\"(29272). App v8. CommCare Version 2.11. Build 29272, built on: February-14-2014" }, "timeStart": "2014-04-28T18:26:38Z", "deviceID": "990004280784863" }, "@version": "8" } expected = [{ 'tag': 'repeat', 'type': 'Repeat', 'label': 'Repeat', 'value': '/data/question12', 'response': None, 'calculate': None, 'children': [], }] actual = get_readable_form_data( form_data, [FormQuestionResponse(q) for q in questions_json]) self.assertJSONEqual( json.dumps([q.to_json() for q in actual]), json.dumps([FormQuestionResponse(q).to_json() for q in expected]))
def _serialized_question(self, form_unique_id, question): response = FormQuestionResponse(question).to_json() response['load_properties'] = self._case_meta.get_load_properties( form_unique_id, question['value']) response['save_properties'] = self._case_meta.get_save_properties( form_unique_id, question['value']) if self._is_save_to_case(question): response['type'] = 'SaveToCase' return response
def test_get_readable_data_for_submission(self): formxml = FormSubmissionBuilder(form_id='123', form_properties={ 'dalmation_count': 'yes' }).as_xml_string() xform = submit_form_locally(formxml, self.domain).xform actual, _ = get_readable_data_for_submission(xform) expected = [{ 'value': '/data/dalmation_count', 'label': 'dalmation_count', 'response': 'yes' }] self.assertJSONEqual( json.dumps([q.to_json() for q in actual]), json.dumps([FormQuestionResponse(q).to_json() for q in expected]))
def _get_form_row(self, form, language, case_meta): form_summary_rows = [] for question in form.get_questions(self.app.langs, include_triggers=True, include_groups=True, include_translations=True): question_response = FormQuestionResponse(question) form_summary_rows.append( FormSummaryRow( question_id=question_response.value, label=_translate_name(question_response.translations, language), translations=question_response.translations, type=question_response.type, repeat=question_response.repeat, group=question_response.group, option_labels="\n".join([ _translate_name(option.translations, language) for option in question_response.options ]), option_values=", ".join([ option.value for option in question_response.options ]), calculate=question_response.calculate, relevant=question_response.relevant, constraint=question_response.constraint, required="true" if question_response.required else "false", comment=question_response.comment, default_value=question_response.setvalue, load_properties="\n".join([ "{} - {}".format(prop.case_type, prop.property) for prop in case_meta.get_load_properties( form.unique_id, question['value']) ]), save_properties="\n".join([ "{} - {}".format(prop.case_type, prop.property) for prop in case_meta.get_save_properties( form.unique_id, question['value']) ]), )) return tuple(form_summary_rows)
def get_questions_from_xform_node(xform, langs): questions = xform.get_questions(langs, include_triggers=True, include_groups=True) return [FormQuestionResponse(q) for q in questions]
def zip_form_data_and_questions(relative_data, questions, path_context='', output_context=None, process_label=None, absolute_data=None): """ The strategy here is to loop through the questions, and at every point pull in the corresponding piece of data, removing it from data and adding it to the question. At the end, any remain piece of data are added to the end as unknown questions. Repeats are matched up with their entry node in the data, and then this function is applied recursively to each of the elements in the list, using the repeat's children as the question list. """ assert path_context absolute_data = absolute_data or relative_data if not path_context.endswith('/'): path_context += '/' if not output_context: output_context = { '%s%s' % (path_context, '/'.join(map(str, key))): str(value) for key, value in _flatten_json(relative_data).items() } result = [] for question in questions: path = path_relative_to_context(question.value, path_context) absolute_path = absolute_path_from_context(question.value, path_context) node = pop_from_form_data(relative_data, absolute_data, path) # response=True on a question with children indicates that one or more # child has a response, i.e. that the entire group wasn't skipped question_data = dict(question) question_data.pop('response') if question.type in ('Group', 'FieldList'): children = question_data.pop('children') form_question = FormQuestionResponse( children=zip_form_data_and_questions( node, children, path_context=absolute_path, output_context=output_context, process_label=process_label, absolute_data=absolute_data, ), **question_data) if _group_question_has_response(form_question): form_question.response = True elif question.type == 'Repeat': if not isinstance(node, list): node = [node] children = question_data.pop('children') form_question = FormQuestionResponse(children=[ FormQuestionResponse(children=zip_form_data_and_questions( entry, children, path_context=absolute_path, output_context=output_context, process_label=process_label, absolute_data=absolute_data, ), ) for entry in node ], **question_data) for child in form_question.children: if _group_question_has_response(child): child.response = True if _group_question_has_response(form_question): form_question.response = True else: if (question.type == 'DataBindOnly' and question.label == question.value): question_data['label'] = '/'.join( question.value.split('/')[2:]) if process_label: question_data['label'] = process_label(question_data['label'], output_context) form_question = FormQuestionResponse(response=node, **question_data) result.append(form_question) if relative_data: for key, response in sorted(_flatten_json(relative_data).items()): joined_key = '/'.join(map(str, key)) result.append( FormQuestionResponse( label=joined_key, value='%s%s' % (path_context, joined_key), response=response, )) return result
def test(self): questions_json = [{ "tag": "input", "repeat": None, "group": None, "value": "/data/question4", "label": "Question 4", "type": "Text", 'calculate': None, 'required': False, 'relevant': None, 'setvalue': 'default', }] form_data = { "@uiVersion": "1", "@xmlns": "http://openrosa.org/formdesigner/D096EE34-DF37-466C-B6D9-950A36D570AD", "@name": "Untitled Form", "question4": "foo", "#type": "data", "case": { "@xmlns": "http://commcarehq.org/case/transaction/v2", "@date_modified": "2013-12-23T16:24:20Z", "create": { "case_type": "case", "case_name": "foo", "owner_id": "9ee0367ad4051f0fb33c75eae67d750e" }, "@user_id": "9ee0367ad4051f0fb33c75eae67d750e", "update": "", "@case_id": "6bc190f6-ddeb-4a42-b445-8fa348b50806" }, "meta": { "@xmlns": "http://openrosa.org/jr/xforms", "username": "******", "instanceID": "0398f186-c35f-4437-8b6b-41800807e485", "userID": "9ee0367ad4051f0fb33c75eae67d750e", "timeEnd": "2013-12-23T16:24:20Z", "appVersion": { "@xmlns": "http://commcarehq.org/xforms", "#text": "2.0" }, "timeStart": "2013-12-23T16:24:10Z", "deviceID": "cloudcare" }, "@version": "10" } questions = [FormQuestionResponse(q) for q in questions_json] actual = get_readable_form_data(form_data, questions) self.assertJSONEqual( json.dumps([q.to_json() for q in actual]), json.dumps([{ "tag": "input", "repeat": None, "group": None, "value": "/data/question4", "label": "Question 4", "response": "foo", "type": "Text", 'calculate': None, 'comment': None, 'required': False, 'relevant': None, 'setvalue': 'default', "options": [], "children": [], }]))
def test_repeat(self): questions_json = [{ 'tag': 'input', 'type': 'Text', 'label': 'Text', 'value': '/data/question1', }, { 'tag': 'input', 'type': 'Int', 'label': 'How many names?', 'value': '/data/question18', }, { 'tag': 'repeat', 'type': 'Repeat', 'label': 'Repeat', 'value': '/data/question12', }, { 'tag': 'input', 'type': 'Text', 'label': 'Name', 'value': '/data/question12/name', 'repeat': '/data/question12', 'group': '/data/question12', }, { 'tag': 'trigger', 'type': 'Trigger', 'label': 'Label', 'value': '/data/question2', }, { 'tag': 'select1', 'type': 'Select', 'label': 'Single Answer', 'value': '/data/question3', 'options': [{ 'value': 'item1', 'label': 'Item 1' }, { 'value': 'item2', 'label': 'Item 2' }] }] form_data = { "@uiVersion": "1", "@xmlns": "http://openrosa.org/formdesigner/432B3A7F-6EEE-4033-8740-ACCB0804C4FC", "@name": "Untitled Form", "question18": "3", "#type": "data", "question12": [{ "name": "Jack" }, { "name": "Jill" }, { "name": "Up the hill" }], "meta": { "@xmlns": "http://openrosa.org/jr/xforms", "username": "******", "instanceID": "172981b6-5eeb-4be8-bbc7-ad52f808e803", "userID": "a07d4bd967a9c205287f767509600931", "timeEnd": "2014-04-28T18:27:05Z", "appVersion": { "@xmlns": "http://commcarehq.org/xforms", "#text": "CommCare ODK, version \"2.11.0\"(29272). App v8. CommCare Version 2.11. Build 29272, built on: February-14-2014" }, "timeStart": "2014-04-28T18:26:38Z", "deviceID": "990004280784863" }, "question1": "T", "question3": "item2", "question2": "OK", "@version": "8" } expected = [{ 'tag': 'input', 'type': 'Text', 'label': 'Text', 'value': '/data/question1', 'response': 'T', 'calculate': None, }, { 'tag': 'input', 'type': 'Int', 'label': 'How many names?', 'value': '/data/question18', 'response': '3', 'calculate': None, }, { 'tag': 'repeat', 'type': 'Repeat', 'label': 'Repeat', 'value': '/data/question12', 'response': True, 'calculate': None, 'children': [{ 'children': [{ 'tag': 'input', 'type': 'Text', 'label': 'Name', 'value': '/data/question12/name', 'repeat': '/data/question12', 'group': '/data/question12', 'response': 'Jack', 'calculate': None, }], 'response': True, }, { 'children': [{ 'tag': 'input', 'type': 'Text', 'label': 'Name', 'value': '/data/question12/name', 'repeat': '/data/question12', 'group': '/data/question12', 'response': 'Jill', 'calculate': None, }], 'response': True }, { 'children': [{ 'tag': 'input', 'type': 'Text', 'label': 'Name', 'value': '/data/question12/name', 'repeat': '/data/question12', 'group': '/data/question12', 'response': 'Up the hill', 'calculate': None, }], 'response': True }], }, { 'tag': 'trigger', 'type': 'Trigger', 'label': 'Label', 'value': '/data/question2', 'response': 'OK', 'calculate': None, }, { 'tag': 'select1', 'type': 'Select', 'label': 'Single Answer', 'value': '/data/question3', 'options': [{ 'value': 'item1', 'label': 'Item 1' }, { 'value': 'item2', 'label': 'Item 2' }], 'response': 'item2', 'calculate': None, }] actual = get_readable_form_data( form_data, [FormQuestionResponse(q) for q in questions_json]) self.assertJSONEqual( json.dumps([q.to_json() for q in actual]), json.dumps([FormQuestionResponse(q).to_json() for q in expected]))
def zip_form_data_and_questions(relative_data, questions, path_context='', output_context=None, process_label=None, absolute_data=None): """ The strategy here is to loop through the questions, and at every point pull in the corresponding piece of data, removing it from data and adding it to the question. At the end, any remain piece of data are added to the end as unknown questions. Repeats are matched up with their entry node in the data, and then this function is applied recursively to each of the elements in the list, using the repeat's children as the question list. """ assert path_context absolute_data = absolute_data or relative_data if not path_context.endswith('/'): path_context += '/' if not output_context: output_context = { '%s%s' % (path_context, '/'.join(map(six.text_type, key))): six.text_type(value) for key, value in _flatten_json(relative_data).items() } result = [] for question in questions: path = path_relative_to_context(question.value, path_context) absolute_path = absolute_path_from_context(question.value, path_context) node = pop_from_form_data(relative_data, absolute_data, path) # response=True on a question with children indicates that one or more # child has a response, i.e. that the entire group wasn't skipped question_data = dict(question) question_data.pop('response') if question.type in ('Group', 'FieldList'): children = question_data.pop('children') form_question = FormQuestionResponse( children=zip_form_data_and_questions( node, children, path_context=absolute_path, output_context=output_context, process_label=process_label, absolute_data=absolute_data, ), **question_data ) if _group_question_has_response(form_question): form_question.response = True elif question.type == 'Repeat': if not isinstance(node, list): node = [node] children = question_data.pop('children') form_question = FormQuestionResponse( children=[ FormQuestionResponse( children=zip_form_data_and_questions( entry, children, path_context=absolute_path, output_context=output_context, process_label=process_label, absolute_data=absolute_data, ), ) for entry in node ], **question_data ) for child in form_question.children: if _group_question_has_response(child): child.response = True if _group_question_has_response(form_question): form_question.response = True else: if (question.type == 'DataBindOnly' and question.label == question.value): question_data['label'] = '/'.join( question.value.split('/')[2:]) if process_label: question_data['label'] = process_label(question_data['label'], output_context) form_question = FormQuestionResponse(response=node, **question_data) result.append(form_question) if relative_data: for key, response in sorted(_flatten_json(relative_data).items()): joined_key = '/'.join(map(six.text_type, key)) result.append( FormQuestionResponse( label=joined_key, value='%s%s' % (path_context, joined_key), response=response, ) ) return result