def get_block(eq_id, form_type, collection_id, group_id, group_instance, block_id): # pylint: disable=unused-argument,too-many-locals current_location = Location(group_id, group_instance, block_id) metadata = get_metadata(current_user) answer_store = get_answer_store(current_user) path_finder = PathFinder(g.schema_json, answer_store, metadata) valid_group = group_id in SchemaHelper.get_group_ids(g.schema_json) full_routing_path = path_finder.get_routing_path() is_valid_location = valid_group and current_location in path_finder.get_routing_path( group_id, group_instance) latest_location = path_finder.get_latest_location( get_completed_blocks(current_user), routing_path=full_routing_path) if not is_valid_location: return _redirect_to_location(collection_id, eq_id, form_type, latest_location) block = _render_schema(current_location) block_type = block['type'] is_skipping_to_end = block_type in [ 'Summary', 'Confirmation' ] and current_location != latest_location if is_skipping_to_end: return _redirect_to_location(collection_id, eq_id, form_type, latest_location) context = _get_context(block, current_location, answer_store) return _build_template(current_location, context, template=block_type, routing_path=full_routing_path)
def post_block(eq_id, form_type, collection_id, group_id, group_instance, block_id): # pylint: disable=too-many-locals current_location = Location(group_id, group_instance, block_id) metadata = get_metadata(current_user) answer_store = get_answer_store(current_user) path_finder = PathFinder(g.schema_json, answer_store, metadata) valid_group = group_id in SchemaHelper.get_group_ids(g.schema_json) full_routing_path = path_finder.get_routing_path() is_valid_location = valid_group and current_location in path_finder.get_routing_path( group_id, group_instance) if not is_valid_location: latest_location = path_finder.get_latest_location( get_completed_blocks(current_user), routing_path=full_routing_path) return _redirect_to_location(collection_id, eq_id, form_type, latest_location) error_messages = SchemaHelper.get_messages(g.schema_json) block = _render_schema(current_location) disable_mandatory = 'action[save_sign_out]' in request.form form, _ = post_form_for_location(block, current_location, answer_store, request.form, error_messages, disable_mandatory=disable_mandatory) if 'action[save_sign_out]' in request.form: return _save_sign_out(collection_id, eq_id, form_type, current_location, form) elif _is_invalid_form(form): context = {'form': form, 'block': block} return _build_template(current_location, context, template=block['type'], routing_path=full_routing_path) else: _update_questionnaire_store(current_location, form) next_location = path_finder.get_next_location( current_location=current_location) if next_location is None and block['type'] in [ "Summary", "Confirmation" ]: return submit_answers(eq_id, form_type, collection_id, metadata, answer_store) return redirect(next_location.url(metadata))
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 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_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 post_block(eq_id, form_type, collection_id, group_id, group_instance, block_id): path_finder = PathFinder(g.schema_json, get_answer_store(current_user), get_metadata(current_user)) q_manager = get_questionnaire_manager(g.schema, g.schema_json) this_location = Location(group_id, group_instance, block_id) if 'action[save_sign_out]' in request.form: return _save_sign_out(collection_id, eq_id, form_type, q_manager, this_location) valid_location = this_location in path_finder.get_routing_path(group_id, group_instance) valid_data = q_manager.validate(this_location, request.form) if not valid_location or not valid_data: current_location = Location(group_id, group_instance, block_id) _render_schema(current_location) return _build_template(current_location, q_manager.block_state, template='questionnaire') else: q_manager.update_questionnaire_store(this_location) next_location = path_finder.get_next_location(current_location=this_location) if next_location is None: raise NotFound metadata = get_metadata(current_user) return redirect(next_location.url(metadata))
def build_summary_rendering_context(schema_json, answer_store, metadata): """ Build questionnaire summary context containing metadata and content from the answers of the questionnaire :param schema_json: schema of the current questionnaire :param answer_store: all of the answers to the questionnaire :param metadata: all of the metadata :return: questionnaire summary context """ navigator = PathFinder(schema_json, answer_store, metadata) path = navigator.get_routing_path() sections = [] for group in schema_json['groups']: for block in group['blocks']: if block['id'] in [location.block_id for location in path]: if "type" not in block or block['type'] != "interstitial": link = url_for( 'questionnaire.get_block', eq_id=metadata['eq_id'], form_type=metadata['form_type'], collection_id=metadata['collection_exercise_sid'], group_id=group['id'], group_instance=0, block_id=block['id']) sections.extend([ Section(section, answer_store.map(), link) for section in block['sections'] ]) return sections
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 submit_answers(eq_id, form_type, collection_id, metadata, answer_store): is_completed, invalid_location = is_survey_completed( answer_store, metadata) if is_completed: path_finder = PathFinder(g.schema_json, answer_store, metadata) message = convert_answers(metadata, g.schema_json, answer_store, path_finder.get_routing_path()) message = current_app.eq['encrypter'].encrypt(message) sent = current_app.eq['submitter'].send_message( message, current_app.config['EQ_RABBITMQ_QUEUE_NAME'], metadata['tx_id']) if not sent: raise SubmissionFailedException() current_app.eq['session_storage'].store_survey_completed_metadata( metadata['tx_id'], metadata['period_str'], metadata['ru_ref']) get_questionnaire_store(current_user.user_id, current_user.user_ik).delete() _remove_survey_session_data() return redirect( url_for('.get_thank_you', eq_id=eq_id, form_type=form_type, collection_id=collection_id)) else: return redirect(invalid_location.url(metadata))
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_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_introduction_not_in_path_when_not_in_schema(self): survey = load_schema_file("census_individual.json") path_finder = PathFinder(survey) blocks = [b.block_id for b in path_finder.get_routing_path()] self.assertNotIn('introduction', blocks)
def _redirect_to_latest_location(collection_id, eq_id, form_type, schema): metadata = get_metadata(current_user) answer_store = get_answer_store(current_user) path_finder = PathFinder(schema, answer_store, metadata) routing_path = path_finder.get_routing_path() latest_location = path_finder.get_latest_location( get_completed_blocks(current_user), routing_path=routing_path) return _redirect_to_location(collection_id, eq_id, form_type, latest_location)
def is_survey_completed(answer_store, metadata): path_finder = PathFinder(g.schema_json, answer_store, metadata) completed_blocks = get_completed_blocks(current_user) for location in path_finder.get_routing_path(): if location.block_id in ['thank-you', 'summary', 'confirmation']: continue if location not in completed_blocks: return False, location return True, None
def test_repeating_groups_no_of_answers(self): survey = load_schema_file("test_repeating_household.json") 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", 1, "repeating-block-1"), Location("repeating-group", 1, "repeating-block-2"), Location("repeating-group", 2, "repeating-block-1"), Location("repeating-group", 2, "repeating-block-2"), Location("summary-group", 0, "summary"), ] 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" ) answer_3 = Answer( group_id="multiple-questions-group", group_instance=0, answer_instance=2, answer_id="first-name", block_id="household-composition", value="Gregg Bloggs" ) answers = AnswerStore() answers.add(answer) answers.add(answer_2) answers.add(answer_3) path_finder = PathFinder(survey, answer_store=answers) self.assertEqual(expected_path, path_finder.get_routing_path())
def test_routing_basic_path(self): survey = load_schema_file("1_0112.json") expected_path = [ Location("rsi", 0, "reporting-period"), Location("rsi", 0, "total-retail-turnover"), Location("rsi", 0, "internet-sales"), Location("rsi", 0, "changes-in-retail-turnover"), Location("rsi", 0, "number-of-employees"), Location("rsi", 0, "changes-in-employees") ] path_finder = PathFinder(survey) routing_path = path_finder.get_routing_path() self.assertEqual(routing_path, expected_path)
def submit_answers(eq_id, form_type, collection_id): q_manager = get_questionnaire_manager(g.schema, g.schema_json) # check that all the answers we have are valid before submitting the data is_valid, invalid_location = q_manager.validate_all_answers() metadata = get_metadata(current_user) if is_valid: answer_store = get_answer_store(current_user) path_finder = PathFinder(g.schema_json, answer_store, metadata) submitter = SubmitterFactory.get_submitter() message = convert_answers(metadata, g.schema, answer_store, path_finder.get_routing_path()) submitter.send_answers(message, queue=settings.EQ_RABBITMQ_QUEUE_NAME) logger.info("Responses submitted tx_id=%s", metadata["tx_id"]) return redirect(url_for('.get_thank_you', eq_id=eq_id, form_type=form_type, collection_id=collection_id)) else: return redirect(invalid_location.url(metadata))
def test_interstitial_post_blocks(self): survey = load_schema_file("0_star_wars.json") answer = Answer( group_id="star-wars", block_id="choose-your-side-block", answer_id="choose-your-side-answer", value="Light Side" ) answers = AnswerStore() answers.add(answer) path_finder = PathFinder(survey, answer_store=answers) self.assertFalse(Location("star-wars", 0, 'summary') in path_finder.get_routing_path())
def submit_answers(eq_id, form_type, collection_id): metadata = get_metadata(current_user) answer_store = get_answer_store(current_user) is_valid, invalid_location = validate_all_answers(answer_store, metadata) if is_valid: path_finder = PathFinder(g.schema_json, answer_store, metadata) submitter = SubmitterFactory.get_submitter() message = convert_answers(metadata, g.schema_json, answer_store, path_finder.get_routing_path()) submitter.send_answers(message) return redirect(url_for('.get_thank_you', eq_id=eq_id, form_type=form_type, collection_id=collection_id)) else: return redirect(invalid_location.url(metadata))
def test_repeating_groups_conditional_location_path(self): survey = load_schema_file("test_repeating_and_conditional_routing.json") expected_path = [ Location("repeat-value-group", 0, "introduction"), Location("repeat-value-group", 0, "no-of-repeats"), Location("repeated-group", 0, "repeated-block"), Location("repeated-group", 0, "age-block"), Location("repeated-group", 0, "shoe-size-block"), Location("repeated-group", 1, "repeated-block"), Location("repeated-group", 1, "shoe-size-block"), Location("summary-group", 0, "summary") ] answer_1 = Answer( group_id="repeat-value-group", block_id="no-of-repeats", answer_id="no-of-repeats-answer", value="2" ) answer_2 = Answer( group_id="repeated-group", group_instance=0, block_id="repeated-block", answer_id="conditional-answer", value="Age and Shoe Size" ) answer_3 = Answer( group_id="repeated-group", group_instance=1, block_id="repeated-block", answer_id="conditional-answer", value="Shoe Size Only" ) answers = AnswerStore() answers.add(answer_1) answers.add(answer_2) answers.add(answer_3) path_finder = PathFinder(survey, answer_store=answers) self.assertEqual(expected_path, path_finder.get_routing_path())
def build_summary_rendering_context(schema_json, answer_store, metadata): """ Build questionnaire summary context containing metadata and content from the answers of the questionnaire :param schema_json: schema of the current questionnaire :param answer_store: all of the answers to the questionnaire :param metadata: all of the metadata :return: questionnaire summary context """ navigator = PathFinder(schema_json, answer_store, metadata) path = navigator.get_routing_path() groups = [] for group in schema_json['groups']: if SchemaHelper.group_has_questions(group) \ and group['id'] in [location.group_id for location in path]: groups.extend( [Group(group, answer_store.map(), path, metadata, url_for)]) return groups
def post_block(eq_id, form_type, collection_id, group_id, group_instance, block_id): path_finder = PathFinder(g.schema_json, get_answer_store(current_user), get_metadata(current_user)) current_location = Location(group_id, group_instance, block_id) valid_location = current_location in path_finder.get_routing_path(group_id, group_instance) block = _render_schema(current_location) error_messages = SchemaHelper.get_messages(g.schema_json) disable_mandatory = 'action[save_sign_out]' in request.form form, _ = post_form_for_location(block, current_location, get_answer_store(current_user), request.form, error_messages, disable_mandatory=disable_mandatory) if 'action[save_sign_out]' in request.form: return _save_sign_out(collection_id, eq_id, form_type, current_location, form) content = { 'form': form, 'block': block, } if not valid_location or not form.validate(): return _build_template(current_location, content, template='questionnaire') else: questionnaire_store = get_questionnaire_store(current_user.user_id, current_user.user_ik) if current_location.block_id in ['relationships', 'household-relationships']: update_questionnaire_store_with_answer_data(questionnaire_store, current_location, form.serialise(current_location)) else: update_questionnaire_store_with_form_data(questionnaire_store, current_location, form.data) next_location = path_finder.get_next_location(current_location=current_location) if next_location is None: raise NotFound metadata = get_metadata(current_user) return redirect(next_location.url(metadata))
def test_routing_basic_and_conditional_path(self): survey = load_schema_file("0_star_wars.json") expected_path = [ Location("14ba4707-321d-441d-8d21-b8367366e766", 0, "choose-your-side-block"), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, "923ccc84-9d47-4a02-8ebc-1e9d14fcf10b"), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, "26f2c4b3-28ac-4072-9f18-a6a6c6f660db"), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, "cd3b74d1-b687-4051-9634-a8f9ce10a27d"), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, "an3b74d1-b687-4051-9634-a8f9ce10ard"), Location("14ba4707-321d-441d-8d21-b8367366e766", 0, "846f8514-fed2-4bd7-8fb2-4b5fcb1622b1"), ] answer_1 = Answer(group_id="14ba4707-321d-441d-8d21-b8367366e766", block_id="choose-your-side-block", answer_id="ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c", value="Dark Side") answer_2 = Answer( group_id="14ba4707-321d-441d-8d21-b8367366e766", block_id="923ccc84-9d47-4a02-8ebc-1e9d14fcf10b", answer_id="pel989b8-5185-4ba6-b73f-c126e3a06ba7", 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_excessive_repeating_groups_conditional_location_path(self): survey = load_schema_file("test_repeating_and_conditional_routing.json") answers = AnswerStore() answers.add(Answer( group_id="repeat-value-group", block_id="no-of-repeats", answer_id="no-of-repeats-answer", value="10000" )) for i in range(50): answers.add(Answer( group_id="repeated-group", group_instance=i, block_id="repeated-block", answer_id="conditional-answer", value="Shoe Size Only" )) path_finder = PathFinder(survey, answer_store=answers) self.assertEqual("summary", path_finder.get_routing_path().pop().block_id)