def render_form(request, domain): # get session session_id = request.GET.get('session_id') session = get_object_or_404(EntrySession, session_id=session_id) try: raw_instance = get_raw_instance(session_id, domain) except Exception as e: return HttpResponse(e, status=500, content_type="text/plain") xmlns = raw_instance["xmlns"] form_data_xml = raw_instance["output"] _, form_data_json = xml2json(form_data_xml) pretty_questions = readable.get_questions(domain, session.app_id, xmlns) readable_form = readable.get_readable_form_data(form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form}) return json_response({ 'form_data': rendered_readable_form, 'instance_xml': indent_xml(form_data_xml) })
def _test_corpus(self, slug): xform_file = os.path.join(os.path.dirname(__file__), 'readable_forms', '{}.xform.xml'.format(slug)) submission_file = os.path.join(os.path.dirname(__file__), 'readable_forms', '{}.submission.json'.format(slug)) result_file = os.path.join(os.path.dirname(__file__), 'readable_forms', '{}.result.yaml'.format(slug)) with open(xform_file) as f: xform = f.read() with open(submission_file) as f: data = json.load(f) with open(result_file) as f: result = yaml.load(f) questions = get_questions_from_xform_node(XForm(xform), langs=['en']) questions = get_readable_form_data(data, questions) # Search for 'READABLE FORMS TEST' for more info # to bootstrap a test and have it print out your yaml result # uncomment this line. Ghetto but it works. # print yaml.safe_dump([json.loads(json.dumps(x.to_json())) # for x in questions]) self.assertJSONEqual( json.dumps([x.to_json() for x in questions]), json.dumps(result), msg= "Search for \"READABLE FORMS TEST\" for more info on fixing this test" )
def render_form(request, domain): # get session session_id = request.GET.get('session_id') session = get_object_or_404(EntrySession, session_id=session_id) response = requests.post("{base_url}/webforms/get-xml/{session_id}".format( base_url=get_url_base(), session_id=session_id)) if response.status_code is not 200: err = "Session XML could not be found" return HttpResponse(err, status=500, content_type="text/plain") response_json = json.loads(response.text) xmlns = response_json["xmlns"] form_data_xml = response_json["output"] _, form_data_json = xml2json(form_data_xml) pretty_questions = readable.get_questions(domain, session.app_id, xmlns) readable_form = readable.get_readable_form_data(form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form}) return json_response({ 'form_data': rendered_readable_form, 'instance_xml': render_pretty_xml(form_data_xml) })
def _test_corpus(self, slug): xform_file = os.path.join( os.path.dirname(__file__), 'readable_forms', '{}.xform.xml'.format(slug)) submission_file = os.path.join( os.path.dirname(__file__), 'readable_forms', '{}.submission.json'.format(slug)) result_file = os.path.join( os.path.dirname(__file__), 'readable_forms', '{}.result.yaml'.format(slug)) with open(xform_file) as f: xform = f.read() with open(submission_file) as f: data = json.load(f) with open(result_file) as f: result = yaml.load(f) questions = get_questions_from_xform_node(XForm(xform), langs=['en']) questions = get_readable_form_data(data, questions) # Search for 'READABLE FORMS TEST' for more info # to bootstrap a test and have it print out your yaml result # uncomment this line. Ghetto but it works. # print yaml.safe_dump([json.loads(json.dumps(x.to_json())) # for x in questions]) self.assertJSONEqual(json.dumps([x.to_json() for x in questions]), json.dumps(result), msg="Search for \"READABLE FORMS TEST\" for more info on fixing this test")
def render_form(request, domain): # get session session_id = request.GET.get('session_id') session = get_object_or_404(EntrySession, session_id=session_id) response = requests.post("{base_url}/webforms/get-xml/{session_id}".format( base_url=get_url_base(), session_id=session_id) ) if response.status_code is not 200: err = "Session XML could not be found" return HttpResponse(err, status=500, content_type="text/plain") response_json = json.loads(response.text) xmlns = response_json["xmlns"] form_data_xml = response_json["output"] _, form_data_json = xml2json(form_data_xml) pretty_questions = readable.get_questions(domain, session.app_id, xmlns) readable_form = readable.get_readable_form_data(form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form} ) return json_response({ 'form_data': rendered_readable_form, 'instance_xml': render_pretty_xml(form_data_xml) })
def test(self): questions_json = [{ "tag": "input", "repeat": None, "group": None, "value": "/data/question4", "label": "Question 4", "type": "Text", 'calculate': None, }] 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, }]) )
def test(self): questions_json = [{ "tag": "input", "repeat": None, "group": None, "value": "/data/question4", "label": "Question 4", "type": "Text", }] 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", }]))
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 post(self, request, domain): instance_xml = request.POST.get('instanceXml').encode('utf-8') app_id = request.POST.get('appId') xmlns = request.POST.get('xmlns') _, form_data_json = xml2json(instance_xml) pretty_questions = readable.get_questions(domain, app_id, xmlns) readable_form = readable.get_readable_form_data( form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form}) return json_response({ 'form_data': rendered_readable_form, })
def post(self, request, domain): instance_xml = request.POST.get('instanceXml').encode('utf-8') app_id = request.POST.get('appId') xmlns = request.POST.get('xmlns') _, form_data_json = xml2json(instance_xml) pretty_questions = readable.get_questions(domain, app_id, xmlns) readable_form = readable.get_readable_form_data(form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form} ) return json_response({ 'form_data': rendered_readable_form, 'form_questions': pretty_questions })
def test_corpus(self): slug = 'mismatched_group_hierarchy' xform_file = os.path.join( os.path.dirname(__file__), 'readable_forms', '{}.xform.xml'.format(slug)) submission_file = os.path.join( os.path.dirname(__file__), 'readable_forms', '{}.submission.json'.format(slug)) result_file = os.path.join( os.path.dirname(__file__), 'readable_forms', '{}.result.yaml'.format(slug)) with open(xform_file) as f: xform = f.read() with open(submission_file) as f: data = json.load(f) with open(result_file) as f: result = yaml.load(f) questions = get_questions_from_xform_node(XForm(xform), langs=['en']) questions = get_readable_form_data(data, questions) self.assertJSONEqual(json.dumps([x.to_json() for x in questions]), json.dumps(result))
def render_form(form, domain, options): """ Uses options since Django 1.3 doesn't seem to support templatetag kwargs. Change to kwargs when we're on a version of Django that does. """ # don't actually use the passed in timezone since we assume form submissions already come # in in local time. # todo: we should revisit this when we properly handle timezones in form processing. timezone = pytz.utc case_id = options.get('case_id') case_id_attr = "@%s" % const.CASE_TAG_ID _get_tables_as_columns = partial(get_tables_as_columns, timezone=timezone) # Form Data tab form_data, question_list_not_found = get_readable_form_data(form) # Case Changes tab case_blocks = extract_case_blocks(form) for i, block in enumerate(list(case_blocks)): if case_id and block.get(case_id_attr) == case_id: case_blocks.pop(i) case_blocks.insert(0, block) cases = [] for b in case_blocks: this_case_id = b.get(case_id_attr) try: this_case = CommCareCase.get( this_case_id) if this_case_id else None valid_case = True except ResourceNotFound: this_case = None valid_case = False if this_case and this_case._id: url = reverse('case_details', args=[domain, this_case._id]) else: url = "#" definition = get_definition(sorted_case_update_keys(b.keys())) cases.append({ "is_current_case": case_id and this_case_id == case_id, "name": case_inline_display(this_case), "table": _get_tables_as_columns(b, definition), "url": url, "valid_case": valid_case }) # Form Metadata tab meta = form.top_level_tags().get('meta', {}) definition = get_definition(sorted_form_metadata_keys(meta.keys())) form_meta_data = _get_tables_as_columns(meta, definition) if 'auth_context' in form: auth_context = AuthContext(form.auth_context) auth_context_user_id = auth_context.user_id auth_user_info = get_doc_info_by_id(domain, auth_context_user_id) else: auth_user_info = get_doc_info_by_id(domain, None) auth_context = AuthContext( user_id=None, authenticated=False, domain=domain, ) meta_userID = meta.get('userID') meta_username = meta.get('username') if meta_userID == 'demo_user': user_info = DocInfo( domain=domain, display='demo_user', ) elif meta_username == 'admin': user_info = DocInfo( domain=domain, display='admin', ) else: user_info = get_doc_info_by_id(domain, meta_userID) return render_to_string( "reports/form/partials/single_form.html", { "context_case_id": case_id, "instance": form, "is_archived": form.doc_type == "XFormArchived", "domain": domain, 'question_list_not_found': question_list_not_found, "form_data": form_data, "cases": cases, "form_table_options": { # todo: wells if display config has more than one column "put_loners_in_wells": False }, "form_meta_data": form_meta_data, "auth_context": auth_context, "auth_user_info": auth_user_info, "user_info": user_info, })
session_id = request.GET.get('session_id') session = get_object_or_404(EntrySession, session_id=session_id) try: raw_instance = get_raw_instance(session_id, domain) except Exception, e: return HttpResponse(e, status=500, content_type="text/plain") xmlns = raw_instance["xmlns"] form_data_xml = raw_instance["output"] _, form_data_json = xml2json(form_data_xml) pretty_questions = readable.get_questions(domain, session.app_id, xmlns) readable_form = readable.get_readable_form_data(form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form}) return json_response({ 'form_data': rendered_readable_form, 'instance_xml': indent_xml(form_data_xml) }) class HttpResponseConflict(HttpResponse): status_code = 409
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 render_form(form, domain, options): """ Uses options since Django 1.3 doesn't seem to support templatetag kwargs. Change to kwargs when we're on a version of Django that does. """ # don't actually use the passed in timezone since we assume form submissions already come # in in local time. # todo: we should revisit this when we properly handle timezones in form processing. timezone = pytz.utc case_id = options.get('case_id') case_id_attr = "@%s" % const.CASE_TAG_ID _get_tables_as_columns = partial(get_tables_as_columns, timezone=timezone) # Form Data tab form_data, question_list_not_found = get_readable_form_data(form) # Case Changes tab case_blocks = extract_case_blocks(form) for i, block in enumerate(list(case_blocks)): if case_id and block.get(case_id_attr) == case_id: case_blocks.pop(i) case_blocks.insert(0, block) cases = [] for b in case_blocks: this_case_id = b.get(case_id_attr) try: this_case = CommCareCase.get(this_case_id) if this_case_id else None valid_case = True except ResourceNotFound: this_case = None valid_case = False if this_case and this_case._id: url = reverse('case_details', args=[domain, this_case._id]) else: url = "#" definition = get_definition(sorted_case_update_keys(b.keys())) cases.append({ "is_current_case": case_id and this_case_id == case_id, "name": case_inline_display(this_case), "table": _get_tables_as_columns(b, definition), "url": url, "valid_case": valid_case }) # Form Metadata tab meta = form.top_level_tags().get('meta', {}) definition = get_definition(sorted_form_metadata_keys(meta.keys())) form_meta_data = _get_tables_as_columns(meta, definition) if 'auth_context' in form: auth_context = AuthContext(form.auth_context) auth_context_user_id = auth_context.user_id auth_user_info = get_doc_info_by_id(domain, auth_context_user_id) else: auth_user_info = get_doc_info_by_id(domain, None) auth_context = AuthContext( user_id=None, authenticated=False, domain=domain, ) meta_userID = meta.get('userID') meta_username = meta.get('username') if meta_userID == 'demo_user': user_info = DocInfo( domain=domain, display='demo_user', ) elif meta_username == 'admin': user_info = DocInfo( domain=domain, display='admin', ) else: user_info = get_doc_info_by_id(domain, meta_userID) return render_to_string("reports/form/partials/single_form.html", { "context_case_id": case_id, "instance": form, "is_archived": form.doc_type == "XFormArchived", "domain": domain, 'question_list_not_found': question_list_not_found, "form_data": form_data, "cases": cases, "form_table_options": { # todo: wells if display config has more than one column "put_loners_in_wells": False }, "form_meta_data": form_meta_data, "auth_context": auth_context, "auth_user_info": auth_user_info, "user_info": user_info, })
session_id = request.GET.get('session_id') session = get_object_or_404(EntrySession, session_id=session_id) try: raw_instance = get_raw_instance(session_id, domain) except Exception, e: return HttpResponse(e, status=500, content_type="text/plain") xmlns = raw_instance["xmlns"] form_data_xml = raw_instance["output"] _, form_data_json = xml2json(form_data_xml) pretty_questions = readable.get_questions(domain, session.app_id, xmlns) readable_form = readable.get_readable_form_data(form_data_json, pretty_questions) rendered_readable_form = render_to_string( 'reports/form/partials/readable_form.html', {'questions': readable_form} ) return json_response({ 'form_data': rendered_readable_form, 'instance_xml': indent_xml(form_data_xml) }) class HttpResponseConflict(HttpResponse): status_code = 409
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]) )