def test_should_go_to_next_question_when_condition_is_meta_and_answer_type( self): # Given goto_rule = { 'id': 'next-question', 'when': [{ 'id': 'my_answer', 'condition': 'equals', 'value': 'Yes' }, { 'condition': 'equals', 'meta': 'sexual_identity', 'value': True }] } answer_store = AnswerStore() answer_store.add(Answer(answer_id='my_answer', value='Yes')) metadata = {'sexual_identity': True} # When goto = evaluate_goto(goto_rule, metadata, answer_store, 0) # Then self.assertTrue(goto)
def test_navigation_skip_condition_show_group(self): survey = load_schema_file("test_navigation.json") metadata = { "eq_id": '1', "collection_exercise_sid": '999', "form_type": "some_form" } completed_blocks = [] answer_store = AnswerStore() answer_1 = Answer(value="Buildings", group_instance=0, block_id='insurance-type', group_id='property-details', answer_instance=0, answer_id='insurance-type-answer') answer_store.add(answer_1) navigation = Navigation(survey, answer_store, metadata, completed_blocks=completed_blocks) user_navigation = navigation.build_navigation('property-details', 0) link_names = [d['link_name'] for d in user_navigation] self.assertIn('House Details', link_names)
def test_evaluate_skip_condition_returns_true_when_that_rule_true(self): skip_condition = [ { 'when': [ { 'id': 'this', 'condition': 'equals', 'value': 'value' } ] }, { 'when': [ { 'id': 'that', 'condition': 'equals', 'value': 'other value' } ] } ] answer_store = AnswerStore() answer_store.add(Answer(answer_id='that', value='other value')) self.assertTrue(evaluate_skip_condition(skip_condition, {}, answer_store))
def test_get_routing_path_when_first_block_in_group_skipped(self): # Given schema = load_schema_from_params('test', 'skip_condition_group') answer_store = AnswerStore() answer_store.add(Answer(answer_id='do-you-want-to-skip-answer', value='Yes')) # When path_finder = PathFinder(schema, answer_store=answer_store, metadata={}, completed_blocks=[]) # Then expected_route = [ { 'block_id': 'do-you-want-to-skip-block', 'group_id': 'do-you-want-to-skip-group', 'group_instance': 0 }, { 'block_id': 'summary', 'group_id': 'should-skip-group', 'group_instance': 0 } ] pytest.xfail(reason='Known bug when skipping last group due to summary bundled into it') self.assertEqual(path_finder.get_routing_path('should-skip-group'), expected_route)
def test_next_location_goto_summary(self): survey = load_schema_file("0_star_wars.json") expected_path = [ Location("14ba4707-321d-441d-8d21-b8367366e766", 0, 'introduction'), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, 'choose-your-side-block'), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, 'summary'), ] answer = Answer( group_id="14ba4707-321d-441d-8d21-b8367366e766", group_instance=0, block_id="choose-your-side-block", answer_id="ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c", value="I prefer Star Trek", ) answers = AnswerStore() answers.add(answer) path_finder = PathFinder(survey, answer_store=answers) current_location = expected_path[1] expected_next_location = expected_path[2] next_location = path_finder.get_next_location( current_location=current_location) self.assertEqual(next_location, expected_next_location)
def test_repeating_groups(self): schema = load_schema_from_params('test', 'repeating_household') schema.answer_is_in_repeating_group = MagicMock(return_value=False) # Default is to count answers, so switch to using value schema.json['sections'][-2]['groups'][0]['routing_rules'][0]['repeat']['type'] = 'answer_value' expected_path = [ Location('multiple-questions-group', 0, 'introduction'), Location('multiple-questions-group', 0, 'household-composition'), Location('repeating-group', 0, 'repeating-block-1'), Location('repeating-group', 0, 'repeating-block-2'), Location('repeating-group', 0, 'repeating-block-3'), Location('repeating-group', 1, 'repeating-block-1'), Location('repeating-group', 1, 'repeating-block-2'), Location('repeating-group', 1, 'repeating-block-3'), Location('summary-group', 0, 'summary'), ] answer = Answer( group_instance=0, answer_id='first-name', value='2' ) answers = AnswerStore() answers.add(answer) path_finder = PathFinder(schema, answer_store=answers, metadata={}, completed_blocks=[]) self.assertEqual(expected_path, path_finder.get_full_routing_path())
def test_minimum_and_maximum_offset_dates(self): test_metadata = {'date': '2018-02-20'} store = AnswerStore() test_answer_id = Answer( answer_id='date', answer_instance=1, group_instance=0, value='2018-03-20', ) store.add(test_answer_id) answer = { 'id': 'date_answer', 'type': 'Date', 'minimum': { 'meta': 'date', 'offset_by': { 'days': -10 } }, 'maximum': { 'answer_id': 'date', 'offset_by': { 'years': 1 } } } offset_dates = get_dates_for_single_date_period_validation( answer, store, metadata=test_metadata) self.assertEqual(offset_dates, (convert_to_datetime('2018-02-10'), convert_to_datetime('2019-03-20')))
def test_get_next_location_summary(self): schema = load_schema_from_params('0', 'star_wars') schema.answer_is_in_repeating_group = MagicMock(return_value=False) answer_1 = Answer( answer_id='choose-your-side-answer', value='Light Side' ) answer_2 = Answer( answer_id='light-side-pick-ship-answer', value='No', ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) path_finder = PathFinder(schema, answer_store=answers, metadata={}, completed_blocks=[]) current_location = Location('star-wars', 0, 'star-wars-trivia-part-2') next_location = path_finder.get_next_location(current_location=current_location) expected_next_location = Location('star-wars', 0, 'star-wars-trivia-part-3') self.assertEqual(expected_next_location, next_location) current_location = expected_next_location next_location = path_finder.get_next_location(current_location=current_location) expected_next_location = Location('star-wars', 0, 'summary') self.assertEqual(expected_next_location, next_location)
def test_next_location_goto_summary(self): schema = load_schema_from_params('0', 'star_wars') schema.answer_is_in_repeating_group = MagicMock(return_value=False) expected_path = [ Location('star-wars', 0, 'introduction'), Location('star-wars', 0, 'choose-your-side-block'), Location('star-wars', 0, 'summary'), ] answer = Answer( group_instance=0, answer_id='choose-your-side-answer', value='I prefer Star Trek', ) answers = AnswerStore() answers.add(answer) path_finder = PathFinder(schema, answer_store=answers, metadata={}, completed_blocks=[]) current_location = expected_path[1] expected_next_location = expected_path[2] next_location = path_finder.get_next_location(current_location=current_location) self.assertEqual(next_location, expected_next_location)
def test_routing_ignores_answers_not_on_path(self): when = { 'when': [{ 'id': 'some-answer', 'condition': 'equals', 'value': 'some value' }] } answer_store = AnswerStore({}) answer_store.add( Answer( answer_id='some-answer', value='some value', group_instance=0, )) routing_path = [Location('test', 0, 'test_block_id')] with patch('app.questionnaire.rules._get_answers_on_path', return_value=answer_store): self.assertTrue( evaluate_when_rules(when['when'], get_schema_mock(), {}, answer_store, 0, None)) with patch('app.questionnaire.rules._is_answer_on_path', return_value=False): self.assertFalse( evaluate_when_rules(when['when'], get_schema_mock(), {}, answer_store, 0, None, routing_path=routing_path))
def test_route_based_on_answer_count(self): schema = load_schema_from_params('test', 'routing_answer_count') answer_group_id = 'first-name' answer_store = AnswerStore({}) answer_store.add(Answer( answer_id=answer_group_id, answer_instance=0, value='alice', )) answer_store.add(Answer( answer_id=answer_group_id, answer_instance=1, value='bob', )) completed_blocks = [ Location('multiple-questions-group', 0, 'household-composition') ] expected_next_location = Location('group-equal-2', 0, 'group-equal-2-block') should_not_be_present = Location('group-less-than-2', 0, 'group-less-than-2-block') path_finder = PathFinder(schema, answer_store, metadata={}, completed_blocks=completed_blocks) path = path_finder.build_path() self.assertNotIn(should_not_be_present, path) self.assertIn(expected_next_location, path)
def test_next_location_empty_routing_rules(self): survey = load_schema_file("test_checkbox.json") expected_path = [ Location("checkboxes", 0, 'introduction'), Location("checkboxes", 0, 'mandatory-checkbox'), Location("checkboxes", 0, 'non-mandatory-checkbox'), Location("checkboxes", 0, 'summary') ] answer_1 = Answer( group_id="checkboxes", block_id="mandatory-checkbox", answer_id="mandatory-checkbox-answer", value="Cheese", ) answer_2 = Answer( group_id="checkboxes", block_id="non-mandatory-checkbox", answer_id="non-mandatory-checkbox-answer", value="deep pan", ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) path_finder = PathFinder(survey, answer_store=answers) current_location = expected_path[1] expected_next_location = expected_path[2] next_location = path_finder.get_next_location(current_location=current_location) self.assertEqual(next_location, expected_next_location)
def test_should_not_show_block_for_zero_repeats(self): survey = load_schema_file("test_repeating_household.json") # Default is to count answers, so switch to using value survey['groups'][-2]['routing_rules'][0]['repeat']['type'] = 'answer_value' expected_path = [ Location("multiple-questions-group", 0, "introduction"), Location("multiple-questions-group", 0, "household-composition"), Location("summary-group", 0, "summary") ] answer = Answer( group_id="multiple-questions-group", group_instance=0, answer_id="first-name", block_id="household-composition", value="0" ) answers = AnswerStore() answers.add(answer) path_finder = PathFinder(survey, answer_store=answers) self.assertEqual(expected_path, path_finder.get_routing_path())
def test_next_location_goto_summary(self): survey = load_schema_file("0_star_wars.json") expected_path = [ Location("star-wars", 0, 'introduction'), Location("star-wars", 0, 'choose-your-side-block'), Location("star-wars", 0, 'summary'), ] answer = Answer( group_id="star-wars", group_instance=0, block_id="choose-your-side-block", answer_id="choose-your-side-answer", value="I prefer Star Trek", ) answers = AnswerStore() answers.add(answer) path_finder = PathFinder(survey, answer_store=answers) current_location = expected_path[1] expected_next_location = expected_path[2] next_location = path_finder.get_next_location(current_location=current_location) self.assertEqual(next_location, expected_next_location)
def test_previous_with_conditional_path_alternative(self): survey = load_schema_file("0_star_wars.json") expected_path = [ Location("star-wars", 0, "choose-your-side-block"), Location("star-wars", 0, "light-side-pick-character-ship"), Location("star-wars", 0, "star-wars-trivia"), Location("star-wars", 0, "star-wars-trivia-part-2"), Location("star-wars", 0, "star-wars-trivia-part-3"), ] current_location = expected_path[2] expected_previous_location = expected_path[1] answer_1 = Answer( group_id="star-wars", block_id="choose-your-side-block", answer_id="choose-your-side-answer", value="Light Side" ) answer_2 = Answer( group_id="star-wars", block_id="light-side-pick-character-ship", answer_id="light-side-pick-ship-answer", value="No", ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) path_finder = PathFinder(survey, answer_store=answers) self.assertEqual(path_finder.get_previous_location(current_location=current_location), expected_previous_location)
def test_routing_basic_and_conditional_path(self): survey = load_schema_file("0_star_wars.json") expected_path = [ Location("star-wars", 0, "introduction"), Location("star-wars", 0, "choose-your-side-block"), Location("star-wars", 0, "dark-side-pick-character-ship"), Location("star-wars", 0, "light-side-ship-type"), Location("star-wars", 0, "star-wars-trivia"), Location("star-wars", 0, "star-wars-trivia-part-2"), Location("star-wars", 0, "star-wars-trivia-part-3"), Location("star-wars", 0, "summary"), ] answer_1 = Answer( group_id="star-wars", block_id="choose-your-side-block", answer_id="choose-your-side-answer", value="Dark Side" ) answer_2 = Answer( group_id="star-wars", block_id="dark-side-pick-character-ship", answer_id="dark-side-pick-ship-answer", value="Can I be a pain and have a goodies ship", ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) path_finder = PathFinder(survey, answer_store=answers) routing_path = path_finder.get_routing_path() self.assertEqual(routing_path, expected_path)
def test_repeating_groups_no_of_answers_minus_one(self): survey = load_schema_file("test_repeating_household.json") # Default is to count answers, so switch to using value survey['groups'][-1]['routing_rules'][0]['repeat'][ 'type'] = 'answer_count_minus_one' expected_path = [ Location("multiple-questions-group", 0, "household-composition"), Location("repeating-group", 0, "repeating-block-1"), Location("repeating-group", 0, "repeating-block-2"), ] answer = Answer(group_id="multiple-questions-group", group_instance=0, answer_instance=0, answer_id="first-name", block_id="household-composition", value="Joe Bloggs") answer_2 = Answer(group_id="multiple-questions-group", group_instance=0, answer_instance=1, answer_id="first-name", block_id="household-composition", value="Sophie Bloggs") answers = AnswerStore() answers.add(answer) answers.add(answer_2) path_finder = PathFinder(survey, answer_store=answers) self.assertEqual(expected_path, path_finder.get_routing_path())
def test_answer_count_when_rule_equal_2(self): """Assert that an `answer_count` can be used in a when block and the value is correctly matched """ answer_group_id = 'repeated-answer' when = [{ 'type': 'answer_count', 'answer_ids': [answer_group_id], 'condition': 'equals', 'value': 2, }] answer_store = AnswerStore({}) answer_store.add( Answer( answer_id=answer_group_id, group_instance=0, group_instance_id='group-1-0', value=10, )) answer_store.add( Answer( answer_id=answer_group_id, group_instance=1, group_instance_id='group-1-1', value=20, )) self.assertTrue( evaluate_when_rules(when, get_schema_mock(), {}, answer_store, 0, None))
def test_get_routing_path_when_first_block_in_group_skipped(self): # Given survey = load_schema_file('test_skip_condition_group.json') answer_store = AnswerStore() answer_store.add( Answer(group_id='do-you-want-to-skip-group', block_id='do-you-want-to-skip', answer_id='do-you-want-to-skip-answer', value='Yes')) # When path_finder = PathFinder(survey, answer_store=answer_store) # Then expected_route = [{ 'block_id': 'do-you-want-to-skip-block', 'group_id': 'do-you-want-to-skip-group', 'group_instance': 0 }, { 'block_id': 'summary', 'group_id': 'should-skip-group', 'group_instance': 0 }] self.assertEqual(path_finder.get_routing_path('should-skip-group'), expected_route)
def test_evaluate_skip_condition_returns_true_when_more_than_one_rule_is_true(self): # Given skip_conditions = [ { 'when': [ { 'id': 'this', 'condition': 'equals', 'value': 'value' } ] }, { 'when': [ { 'id': 'that', 'condition': 'equals', 'value': 'other value' } ] } ] answer_store = AnswerStore({}) answer_store.add(Answer(answer_id='this', value='value')) answer_store.add(Answer(answer_id='that', value='other value')) # When condition = evaluate_skip_conditions(skip_conditions, get_schema_mock(), {}, answer_store) # Then self.assertTrue(condition)
def test_routing_basic_and_conditional_path(self): # Given schema = load_schema_from_params('0', 'star_wars') expected_path = [ Location('star-wars', 0, 'introduction'), Location('star-wars', 0, 'choose-your-side-block'), Location('star-wars', 0, 'dark-side-pick-character-ship'), Location('star-wars', 0, 'light-side-ship-type'), Location('star-wars', 0, 'star-wars-trivia'), Location('star-wars', 0, 'star-wars-trivia-part-2'), Location('star-wars', 0, 'star-wars-trivia-part-3'), Location('star-wars', 0, 'summary'), ] answer_1 = Answer( answer_id='choose-your-side-answer', value='Dark Side' ) answer_2 = Answer( answer_id='dark-side-pick-ship-answer', value='Can I be a pain and have a goodies ship', ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) schema.answer_is_in_repeating_group = MagicMock(return_value=False) # When path_finder = PathFinder(schema, answer_store=answers, metadata={}, completed_blocks=[]) routing_path = path_finder.get_full_routing_path() # Then self.assertEqual(routing_path, expected_path)
def test_evaluate_skip_condition_returns_false_when_both_or_rules_false(self): # Given skip_conditions = [ { 'when': [ { 'id': 'this', 'condition': 'equals', 'value': 'value' } ] }, { 'when': [ { 'id': 'that', 'condition': 'equals', 'value': 'other value' } ] } ] answer_store = AnswerStore({}) answer_store.add(Answer(answer_id='this', value='not correct')) answer_store.add(Answer(answer_id='that', value='not correct')) # When condition = evaluate_skip_conditions(skip_conditions, get_schema_mock(), {}, answer_store) # Then self.assertFalse(condition)
def test_previous_with_conditional_path_alternative(self): schema = load_schema_from_params('0', 'star_wars') schema.answer_is_in_repeating_group = MagicMock(return_value=False) expected_path = [ Location('star-wars', 0, 'choose-your-side-block'), Location('star-wars', 0, 'light-side-pick-character-ship'), Location('star-wars', 0, 'star-wars-trivia'), Location('star-wars', 0, 'star-wars-trivia-part-2'), Location('star-wars', 0, 'star-wars-trivia-part-3'), ] current_location = expected_path[2] expected_previous_location = expected_path[1] answer_1 = Answer( answer_id='choose-your-side-answer', value='Light Side' ) answer_2 = Answer( answer_id='light-side-pick-ship-answer', value='No', ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) path_finder = PathFinder(schema, answer_store=answers, metadata={}, completed_blocks=[]) self.assertEqual(path_finder.get_previous_location(current_location=current_location), expected_previous_location)
def test_should_not_go_to_next_question_when_second_condition_fails(self): # Given goto_rule = { 'id': 'next-question', 'when': [ { 'id': 'my_answer', 'condition': 'equals', 'value': 'Yes' }, { 'condition': 'equals', 'meta': 'sexual_identity', 'value': False } ] } answer_store = AnswerStore({}) answer_store.add(Answer(answer_id='my_answer', value='Yes')) metadata = {'sexual_identity': True} # When goto = evaluate_goto(goto_rule, get_schema_mock(), metadata, answer_store, 0) # Then self.assertFalse(goto)
def test_next_location_empty_routing_rules(self): schema = load_schema_from_params('test', 'checkbox') schema.answer_is_in_repeating_group = MagicMock(return_value=False) expected_path = [ Location('checkboxes', 0, 'mandatory-checkbox'), Location('checkboxes', 0, 'non-mandatory-checkbox'), Location('checkboxes', 0, 'summary') ] answer_1 = Answer( answer_id='mandatory-checkbox-answer', value='Cheese', ) answer_2 = Answer( answer_id='non-mandatory-checkbox-answer', value='deep pan', ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) path_finder = PathFinder(schema, answer_store=answers, metadata={}, completed_blocks=[]) current_location = expected_path[0] expected_next_location = expected_path[1] next_location = path_finder.get_next_location(current_location=current_location) self.assertEqual(next_location, expected_next_location)
def test_evaluate_when_rules_condition_is_not_met(self): # Given answer_1 = Answer( answer_id='my_answers', answer_instance=0, group_instance=0, value=10, ) answer_2 = Answer( answer_id='my_answers', answer_instance=1, group_instance=0, value=20, ) answer_store = AnswerStore({}) answer_store.add(answer_1) answer_store.add(answer_2) when = { 'id': 'next-question', 'when': [ { 'id': 'my_answers', 'condition': 'not set', 'value': '2' } ] } # When with self.assertRaises(Exception) as err: evaluate_when_rules(when['when'], get_schema_mock(), None, answer_store, 0) self.assertEqual('Multiple answers (2) found evaluating when rule for answer (my_answers)', str(err.exception))
def test_should_repeat_for_answer_answer_value(self): # Given repeat = {'answer_id': 'my_answer', 'type': 'answer_value'} answer_store = AnswerStore() answer_store.add(Answer(answer_id='my_answer', value='3')) # When number_of_repeats = evaluate_repeat(repeat, answer_store) self.assertEqual(number_of_repeats, 3)
def test_should_minus_one_from_maximum_repeats(self): # Given repeat = {'answer_id': 'my_answer', 'type': 'answer_count_minus_one'} answer_store = AnswerStore() for i in range(27): answer_store.add( Answer(answer_id='my_answer', value='3', answer_instance=i)) # When number_of_repeats = evaluate_repeat(repeat, answer_store) self.assertEqual(number_of_repeats, 24)
def test_should_repeat_for_answer_until(self): questionnaire = { 'survey_id': '021', 'data_version': '0.0.1', 'sections': [{ 'id': 'section1', 'groups': [ { 'id': 'group-1', 'blocks': [ { 'id': 'block-1', 'questions': [{ 'id': 'question-2', 'answers': [ { 'id': 'my_answer', 'type': 'TextField' } ] }] } ] } ] }] } schema = QuestionnaireSchema(questionnaire) # Given repeat = { 'type': 'until', 'when': [ { 'id': 'my_answer', 'condition': 'equals', 'value': 'Done' } ] } answer_store = AnswerStore({}) current_path = [Location('group-1', 0, 'block-1')] answer_on_path = get_answer_ids_on_routing_path(schema, current_path) self.assertEqual(evaluate_repeat(repeat, answer_store, answer_on_path), 1) answer_store.add(Answer(answer_id='my_answer', value='Not Done', group_instance=0)) self.assertEqual(evaluate_repeat(repeat, answer_store, answer_on_path), 2) answer_store.add(Answer(answer_id='my_answer', value='Done', group_instance=1)) self.assertEqual(evaluate_repeat(repeat, answer_store, answer_on_path), 2)
def test_should_repeat_for_answer_answer_count_minus_one(self): # Given repeat = {'answer_id': 'my_answer', 'type': 'answer_count_minus_one'} answer_store = AnswerStore() answer_store.add(Answer(answer_id='my_answer', value='3')) answer_store.add( Answer(answer_id='my_answer', value='4', answer_instance=1)) # When number_of_repeats = evaluate_repeat(repeat, answer_store) self.assertEqual(number_of_repeats, 1)