def get_block(eq_id, form_type, collection_id, group_id, group_instance, block_id): # pylint: disable=unused-argument # Filter answers down to those we may need to render answer_store = get_answer_store(current_user) path_finder = PathFinder(g.schema_json, answer_store, get_metadata(current_user)) current_location = Location(group_id, group_instance, block_id) valid_group = group_id in SchemaHelper.get_group_ids(g.schema_json) if not valid_group or current_location not in path_finder.get_routing_path(group_id, group_instance): raise NotFound block = _render_schema(current_location) error_messages = SchemaHelper.get_messages(g.schema_json) form, template_params = get_form_for_location(block, current_location, answer_store, error_messages) content = {'form': form, 'block': block} if template_params: content.update(template_params) template = block['type'] if block and 'type' in block and block['type'] else 'questionnaire' return _build_template(current_location, content, template)
def test_child_answers_define_parent(self): for schema_file in self.all_schema_files(): with open(schema_file, encoding="utf8") as file: schema_json = load(file) for block in SchemaHelper.get_blocks(schema_json): answers_by_id = SchemaHelper.get_answers_by_id_for_block( block) for answer_id, answer in answers_by_id.items(): if answer['type'] in ['Radio', 'Checkbox']: child_answer_ids = (o['child_answer_id'] for o in answer['options'] if 'child_answer_id' in o) for child_answer_id in child_answer_ids: if child_answer_id not in answers_by_id: self.fail( "Child answer with id '%s' does not exist in schema %s" % (child_answer_id, schema_file)) if 'parent_answer_id' not in answers_by_id[ child_answer_id]: self.fail( "Child answer '%s' does not define parent_answer_id '%s' in schema %s" % (child_answer_id, answer_id, schema_file)) if answers_by_id[child_answer_id][ 'parent_answer_id'] != answer_id: self.fail( "Child answer '%s' defines incorrect parent_answer_id '%s' in schema %s: " "Should be '%s" % (child_answer_id, answers_by_id[child_answer_id] ['parent_answer_id'], schema_file, answer_id))
def convert_answers_to_data(answer_store, questionnaire_json, routing_path): """ Convert answers into the data format below "data": { "001": "01-01-2016", "002": "30-03-2016" } :param answer_store: questionnaire answers :param questionnaire_json: The survey json :param routing_path: the path followed in the questionnaire :return: data in a formatted form """ data = OrderedDict() for location in routing_path: answers_in_block = answer_store.filter(location=location) block_json = SchemaHelper.get_block(questionnaire_json, location.block_id) answer_schema_list = SchemaHelper.get_answers_by_id_for_block(block_json) for answer in answers_in_block: answer_schema = answer_schema_list[answer['answer_id']] value = answer['value'] if answer_schema is not None and value is not None: if answer_schema['type'] != 'Checkbox' or any('q_code' not in option for option in answer_schema['options']): data[answer_schema['q_code']] = _get_answer_data(data, answer_schema['q_code'], value) else: data.update(_get_checkbox_answer_data(answer_schema, value)) return data
def test_post_form_for_household_composition(self): survey = load_schema_file("census_household.json") block_json = SchemaHelper.get_block(survey, 'household-composition') location = Location('who-lives-here', 0, 'household-composition') error_messages = SchemaHelper.get_messages(survey) form, _ = post_form_for_location( block_json, location, AnswerStore(), { 'household-0-first-name': 'Joe', 'household-0-last-name': '', 'household-1-first-name': 'Bob', 'household-1-last-name': 'Seymour', }, error_messages) self.assertEqual(len(form.household.entries), 2) self.assertEqual(form.household.entries[0].data, { 'first-name': 'Joe', 'middle-names': '', 'last-name': '' }) self.assertEqual(form.household.entries[1].data, { 'first-name': 'Bob', 'middle-names': '', 'last-name': 'Seymour' })
def test_form_date_range_populates_data(self): with self.test_request_context(): survey = load_schema_file("1_0102.json") block_json = SchemaHelper.get_block(survey, "reporting-period") error_messages = SchemaHelper.get_messages(survey) data = { 'period-from-day': '01', 'period-from-month': '3', 'period-from-year': '2016', 'period-to-day': '31', 'period-to-month': '3', 'period-to-year': '2016' } expected_form_data = { 'csrf_token': '', 'period-from': { 'day': '01', 'month': '3', 'year': '2016' }, 'period-to': { 'day': '31', 'month': '3', 'year': '2016' } } form = generate_form(block_json, data, error_messages) self.assertEqual(form.data, expected_form_data)
def setUp(self): super().setUp() survey = load_schema_file("census_household.json") self.block_json = SchemaHelper.get_block(survey, 'household-composition') self.error_messages = SchemaHelper.get_messages(survey)
def test_get_form_deserialises_month_year_dates(self): survey = load_schema_file("test_dates.json") block_json = SchemaHelper.get_block(survey, "date-block") location = SchemaHelper.get_first_location(survey) error_messages = SchemaHelper.get_messages(survey) form, _ = get_form_for_location( block_json, location, AnswerStore([{ 'answer_id': 'month-year-answer', 'group_id': 'a23d36db-6b07-4ce0-94b2-a843369511e3', 'group_instance': 0, 'block_id': 'date-block', 'value': '05/2015', 'answer_instance': 0, }]), error_messages) self.assertTrue(hasattr(form, "month-year-answer")) month_year_field = getattr(form, "month-year-answer") self.assertEquals(month_year_field.data, { 'month': '5', 'year': '2015', })
def test_post_form_for_radio_other_selected(self): with self.test_request_context(): survey = load_schema_file("test_radio.json") block_json = SchemaHelper.get_block(survey, 'radio-mandatory') location = Location('radio', 0, 'radio-mandatory') error_messages = SchemaHelper.get_messages(survey) answer_store = AnswerStore([{ 'answer_id': 'radio-mandatory-answer', 'block_id': 'radio-mandatory', 'value': 'Other', 'answer_instance': 0, }, { 'answer_id': 'other-answer-mandatory', 'block_id': 'block-1', 'value': 'Other text field value', 'answer_instance': 0, }]) radio_answer = SchemaHelper.get_first_answer_for_block(block_json) text_answer = 'other-answer-mandatory' form, _ = post_form_for_location( block_json, location, answer_store, MultiDict({ '{answer_id}'.format(answer_id=radio_answer['id']): 'Other', '{answer_id}'.format(answer_id=text_answer): 'Other text field value', }), error_messages) other_text_field = getattr(form, 'other-answer-mandatory') self.assertEqual(other_text_field.data, 'Other text field value')
def test_generate_relationship_form_errors_are_correctly_mapped(self): with self.test_request_context(): survey = load_schema_file("test_relationship_household.json") block_json = SchemaHelper.get_block(survey, 'relationships') error_messages = SchemaHelper.get_messages(survey) answer = SchemaHelper.get_first_answer_for_block(block_json) generated_form = generate_relationship_form( block_json, 3, None, error_messages=error_messages) form = generate_relationship_form( block_json, 3, { 'csrf_token': generated_form.csrf_token.current_token, '{answer_id}-0'.format(answer_id=answer['id']): '1', '{answer_id}-1'.format(answer_id=answer['id']): '3', }, error_messages=error_messages) form.validate() mapped_errors = form.map_errors() message = "Not a valid choice" self.assertTrue( self._error_exists(answer['id'], message, mapped_errors))
def test_generate_relationship_form_creates_form_from_data(self): with self.test_request_context(): survey = load_schema_file("test_relationship_household.json") block_json = SchemaHelper.get_block(survey, 'relationships') error_messages = SchemaHelper.get_messages(survey) answer = SchemaHelper.get_first_answer_for_block(block_json) form = generate_relationship_form( block_json, 3, { '{answer_id}-0'.format(answer_id=answer['id']): 'Husband or Wife', '{answer_id}-1'.format(answer_id=answer['id']): 'Brother or Sister', '{answer_id}-2'.format(answer_id=answer['id']): 'Relation - other', }, error_messages=error_messages) self.assertTrue(hasattr(form, answer['id'])) expected_form_data = [ 'Husband or Wife', 'Brother or Sister', 'Relation - other' ] self.assertEqual(form.data[answer['id']], expected_form_data)
def get_location_path(self, group_id=None, group_instance=0): """ Returns a list of url locations visited based on answers provided :return: List of block location dicts, with preceeding/closing interstitial pages included """ if group_id is None: group_id = SchemaHelper.get_first_group_id(self.survey_json) routing_path = self.get_routing_path(group_id, group_instance) can_reach_summary = self.can_reach_summary(routing_path) location_path = [ Location(group_id, 0, block_id) for block_id in self.preceeding_path ] location_path += routing_path if can_reach_summary: for block_id in PathFinder.CLOSING_INTERSTITIAL_PATH: location_path.append( Location(SchemaHelper.get_last_group_id(self.survey_json), 0, block_id)) return location_path
def get_blocks(self): blocks = [] for group in SchemaHelper.get_groups(self.survey_json): if 'skip_condition' in group: skip = evaluate_skip_condition(group['skip_condition'], self.metadata, self.answer_store) if skip: continue no_of_repeats = 1 repeating_rule = SchemaHelper.get_repeat_rule(group) if repeating_rule: no_of_repeats = evaluate_repeat(repeating_rule, self.answer_store) for i in range(0, no_of_repeats): for block in group['blocks']: if 'skip_condition' in block: skip = evaluate_skip_condition(block['skip_condition'], self.metadata, self.answer_store) if skip: continue blocks.append({ "group_id": group['id'], "group_instance": i, "block": block, }) return blocks
def _render_schema(current_location): metadata = get_metadata(current_user) answer_store = get_answer_store(current_user) block_json = SchemaHelper.get_block_for_location(g.schema_json, current_location) block_json = _evaluate_skip_conditions(block_json, current_location, answer_store, metadata) aliases = SchemaHelper.get_aliases(g.schema_json) block_context = build_schema_context(metadata, aliases, answer_store, current_location.group_instance) return renderer.render(block_json, **block_context)
def _remove_repeating_on_household_answers(answer_store, group_id): answer_store.remove(group_id=group_id, block_id='household-composition') questionnaire_store = get_questionnaire_store(current_user.user_id, current_user.user_ik) for answer in SchemaHelper.get_answers_that_repeat_in_block(g.schema_json, 'household-composition'): groups_to_delete = SchemaHelper.get_groups_that_repeat_with_answer_id(g.schema_json, answer['id']) for group in groups_to_delete: answer_store.remove(group_id=group['id']) questionnaire_store.completed_blocks[:] = [b for b in questionnaire_store.completed_blocks if b.group_id != group['id']]
def setUp(self): self.app = create_app() self.app.config['SERVER_NAME'] = "test" self.app_context = self.app.app_context() self.app_context.push() survey = load_schema_file("census_household.json") self.block_json = SchemaHelper.get_block(survey, 'household-composition') self.error_messages = SchemaHelper.get_messages(survey)
def build_navigation(self, current_group_id, current_group_instance): """ Build navigation based on the current group/instance selected :param current_group_id: :param current_group_instance: :return: """ if self.survey_json.get('navigation', False) is False: return None navigation = [] last_group_id = SchemaHelper.get_last_group_id(self.survey_json) for group in filter(lambda x: 'hide_in_navigation' not in x, self.survey_json['groups']): logger.debug("building frontend navigation", group_id=group['id']) first_location = Location(group['id'], 0, group['blocks'][0]['id']) last_block_in_group = SchemaHelper.get_last_block_in_group(group) is_summary_or_confirm_group = last_block_in_group and \ SchemaHelper.is_summary_or_confirmation(last_block_in_group) last_block_location = Location(group['id'], 0, last_block_in_group['id']) can_get_to_summary = is_summary_or_confirm_group and self.routing_path and \ last_block_location in self.routing_path skip_group = self._should_skip_group(current_group_instance, group) if not skip_group: repeating_rule = SchemaHelper.get_repeat_rule(group) if repeating_rule: logger.debug("building repeating navigation", group_id=group['id']) navigation.extend( self._build_repeating_navigation( repeating_rule, group, current_group_id, current_group_instance)) elif last_group_id == group['id'] and can_get_to_summary: logger.debug("building navigation", group_id=group['id']) if len(self.completed_blocks) > 0 and set( self.routing_path[:-1]).issubset( self.completed_blocks): navigation.append( self._build_single_navigation( group, current_group_id, first_location)) elif last_group_id != group[ 'id'] and not is_summary_or_confirm_group: logger.debug("building navigation", group_id=group['id']) navigation.append( self._build_single_navigation(group, current_group_id, first_location)) return navigation
def test_form_ids_match_block_answer_ids(self): survey = load_schema_file("1_0102.json") block_json = SchemaHelper.get_block(survey, "reporting-period") error_messages = SchemaHelper.get_messages(survey) form = generate_form(block_json, {}, error_messages) for answer in SchemaHelper.get_answers_for_block(block_json): self.assertTrue(hasattr(form, answer['id']))
def test_get_repeating_rule(self): survey = load_schema_file("test_repeating_household.json") groups = [group for group in SchemaHelper.get_groups(survey)] rule = SchemaHelper.get_repeat_rule(groups[1]) self.assertEqual( { 'type': 'answer_count', 'answer_id': 'first-name', 'navigation_label_answer_ids': ['first-name', 'last-name'], }, rule)
def test_option_has_other(self): with self.test_request_context(): survey = load_schema_file("test_checkbox.json") block_json = SchemaHelper.get_block(survey, "mandatory-checkbox") error_messages = SchemaHelper.get_messages(survey) form = generate_form(block_json, {}, error_messages) self.assertFalse( form.option_has_other("mandatory-checkbox-answer", 1)) self.assertTrue( form.option_has_other("mandatory-checkbox-answer", 6))
def get_routing_path(self, group_id=None, group_instance=0): """ Returns a list of the block ids visited based on answers provided :return: List of block location dicts """ if group_id is None: group_id = SchemaHelper.get_first_group_id(self.survey_json) first_block_in_group = SchemaHelper.get_first_block_id_for_group( self.survey_json, group_id) location = Location(group_id, group_instance, first_block_in_group) return self.build_path(self.get_blocks(), location)
def test_generate_relationship_form_creates_empty_form(self): survey = load_schema_file("test_relationship_household.json") block_json = SchemaHelper.get_block(survey, 'relationships') error_messages = SchemaHelper.get_messages(survey) answer = SchemaHelper.get_first_answer_for_block(block_json) form = generate_relationship_form(block_json, 3, {}, error_messages=error_messages) self.assertTrue(hasattr(form, answer['id'])) self.assertEqual(len(form.data[answer['id']]), 3)
def test_get_other_answer_invalid(self): with self.test_request_context(): survey = load_schema_file("test_checkbox.json") block_json = SchemaHelper.get_block(survey, "mandatory-checkbox") error_messages = SchemaHelper.get_messages(survey) form = generate_form(block_json, {"other-answer-mandatory": "Some data"}, error_messages) field = form.get_other_answer("mandatory-checkbox-answer", 4) self.assertEqual(None, field)
def test_generate_month_year_date_form_creates_empty_form(self): survey = load_schema_file("test_dates.json") block_json = SchemaHelper.get_block(survey, 'date-block') error_messages = SchemaHelper.get_messages(survey) answers = SchemaHelper.get_answers_by_id_for_block(block_json) form = get_month_year_form(answers['month-year-answer'], error_messages=error_messages) self.assertFalse(hasattr(form, 'day')) self.assertTrue(hasattr(form, 'month')) self.assertTrue(hasattr(form, 'year'))
def test_get_parent_options_for_block(self): survey = load_schema_file("test_checkbox.json") block_json = SchemaHelper.get_block(survey, 'mandatory-checkbox') parent_options = SchemaHelper.get_parent_options_for_block(block_json) expected = { 'mandatory-checkbox-answer': { 'index': 6, 'child_answer_id': 'other-answer-mandatory' } } self.assertEqual(parent_options, expected)
def test_get_form_for_household_relationship(self): with self.test_request_context(): survey = load_schema_file("census_household.json") block_json = SchemaHelper.get_block(survey, 'household-relationships') location = Location('who-lives-here-relationship', 0, 'household-relationships') error_messages = SchemaHelper.get_messages(survey) answer_store = AnswerStore([{ 'group_id': 'who-lives-here-relationship', 'group_instance': 0, 'answer_id': 'first-name', 'block_id': 'household-composition', 'value': 'Joe', 'answer_instance': 0, }, { 'group_id': 'who-lives-here-relationship', 'group_instance': 0, 'answer_id': 'last-name', 'block_id': 'household-composition', 'value': 'Bloggs', 'answer_instance': 0, }, { 'group_id': 'who-lives-here-relationship', 'group_instance': 1, 'answer_id': 'first-name', 'block_id': 'household-composition', 'value': 'Jane', 'answer_instance': 1, }, { 'group_id': 'who-lives-here-relationship', 'group_instance': 1, 'answer_id': 'last-name', 'block_id': 'household-composition', 'value': 'Bloggs', 'answer_instance': 1, }]) form, _ = get_form_for_location(block_json, location, answer_store, error_messages) answer = SchemaHelper.get_first_answer_for_block(block_json) self.assertTrue(hasattr(form, answer['id'])) field_list = getattr(form, answer['id']) # With two people, we need to define 1 relationship self.assertEqual(len(field_list.entries), 1)
def test_answer_with_child_inherits_mandatory_from_parent(self): with self.test_request_context(): survey = load_schema_file("test_radio.json") block_json = SchemaHelper.get_block(survey, "radio-mandatory") error_messages = SchemaHelper.get_messages(survey) form = generate_form(block_json, {'radio-mandatory-answer': 'Other'}, error_messages) child_field = getattr(form, 'other-answer-mandatory') self.assertIsInstance(child_field.validators[0], ResponseRequired)
def test_answer_with_child_inherits_mandatory_from_parent(self): survey = load_schema_file("test_radio.json") block_json = SchemaHelper.get_block(survey, "block-1") error_messages = SchemaHelper.get_messages(survey) form = generate_form(block_json, {'ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c': 'Other'}, error_messages) child_field = getattr(form, 'other-answer-mandatory') self.assertIsInstance(child_field.validators[0], validators.InputRequired)
def test_post_form_for_household_relationship(self): with self.test_request_context(): survey = load_schema_file("census_household.json") block_json = SchemaHelper.get_block(survey, 'household-relationships') location = Location('who-lives-here-relationship', 0, 'household-relationships') error_messages = SchemaHelper.get_messages(survey) answer_store = AnswerStore([{ 'answer_id': 'first-name', 'block_id': 'household-composition', 'value': 'Joe', 'answer_instance': 0, }, { 'answer_id': 'last-name', 'block_id': 'household-composition', 'value': 'Bloggs', 'answer_instance': 0, }, { 'answer_id': 'first-name', 'block_id': 'household-composition', 'value': 'Jane', 'answer_instance': 1, }, { 'answer_id': 'last-name', 'block_id': 'household-composition', 'value': 'Bloggs', 'answer_instance': 1, }]) answer = SchemaHelper.get_first_answer_for_block(block_json) form, _ = post_form_for_location( block_json, location, answer_store, MultiDict( {'{answer_id}-0'.format(answer_id=answer['id']): '3'}), error_messages) self.assertTrue(hasattr(form, answer['id'])) field_list = getattr(form, answer['id']) # With two people, we need to define 1 relationship self.assertEqual(len(field_list.entries), 1) # Check the data matches what was passed from request self.assertEqual(field_list.entries[0].data, "3")
def test_answer_errors_are_mapped(self): survey = load_schema_file("1_0112.json") block_json = SchemaHelper.get_block(survey, "total-retail-turnover") error_messages = SchemaHelper.get_messages(survey) form = generate_form(block_json, {'total-retail-turnover-answer': "-1"}, error_messages) form.validate() answer_errors = form.answer_errors('total-retail-turnover-answer') self.assertIn( "The value cannot be negative. Please correct your answer.", answer_errors)
def get_page_title_for_location(schema_json, current_location): block = SchemaHelper.get_block_for_location(schema_json, current_location) if block['type'] == 'Interstitial': group = SchemaHelper.get_group(schema_json, current_location.group_id) page_title = '{group_title} - {survey_title}'.format( group_title=group['title'], survey_title=schema_json['title']) elif block['type'] == 'Questionnaire': first_question = next(SchemaHelper.get_questions_for_block(block)) page_title = '{question_title} - {survey_title}'.format( question_title=first_question['title'], survey_title=schema_json['title']) else: page_title = schema_json['title'] return TemplateRenderer.safe_content(page_title)