def test_is_not_valid_location(self): schema = load_schema_from_params('test', 'is_skipping_to_end') current_location = Location('not-in-path', 0, 'not-in-path') routing_path = [ Location('test-skipping-group', 0, 'test-skipping-forced'), Location('test-skipping-section-summary-group', 0, 'test-skipping-section-summary'), Location('summary-group', 0, 'summary') ] completed_blocks = [ Location('test-skipping-group', 0, 'test-skipping-forced') ] completeness = Completeness(schema, answer_store=MagicMock(), completed_blocks=completed_blocks, routing_path=routing_path, metadata={}) router = Router(schema, routing_path, completeness, current_location) self.assertFalse(router.can_access_location()) router = Router(schema, routing_path, completeness) self.assertFalse(router.can_access_location()) # Currently, section summary is not added to completed_blocks without POST, ie when using nav bar. self.assertEqual(routing_path[2], router.get_next_location())
def test_section_summary_accessible_when_section_complete(self): schema = load_schema_from_params('test', 'is_skipping_to_end') current_location = Location('test-skipping-section-summary-group-2', 0, 'test-skipping-section-summary-2') routing_path = [ Location('test-skipping-group', 0, 'test-skipping-forced'), Location('test-skipping-group', 0, 'test-skipping-optional'), Location('test-skipping-section-summary-group', 0, 'test-skipping-section-summary'), Location('test-skipping-group-2', 0, 'test-skipping-forced-2'), Location('test-skipping-group-2', 0, 'test-skipping-optional-2'), Location('test-skipping-section-summary-group-2', 0, 'test-skipping-section-summary-2'), Location('summary-group', 0, 'summary') ] completed_blocks = [ Location('test-skipping-group', 0, 'test-skipping-forced'), Location('test-skipping-group-2', 0, 'test-skipping-forced-2'), Location('test-skipping-group-2', 0, 'test-skipping-optional-2'), Location('test-skipping-section-summary-group-2', 0, 'test-skipping-section-summary-2') ] completeness = Completeness(schema, answer_store=MagicMock(), completed_blocks=completed_blocks, routing_path=routing_path, metadata={}) router = Router(schema, routing_path, completeness, current_location) self.assertTrue(router.can_access_location()) self.assertEqual(routing_path[1], router.get_next_location())
def test_cant_access_location_not_on_allowable_path(self): schema = load_schema_from_name("test_unit_patterns") router = Router( schema, self.answer_store, self.list_store, self.progress_store, self.metadata, ) current_location = Location(section_id="default-section", block_id="set-duration-units-block") routing_path = RoutingPath( [ "set-length-units-block", "set-duration-units-block", "set-area-units-block", "set-volume-units-block", "summary", ], section_id="default-section", ) can_access_location = router.can_access_location( current_location, routing_path) self.assertFalse(can_access_location)
def get_section(schema, questionnaire_store, section_id, list_item_id=None): router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, ) if not schema.is_hub_enabled(): redirect_location = router.get_first_incomplete_location_in_survey() return redirect(redirect_location.url()) if section_id not in router.enabled_section_ids: return redirect(url_for(".get_questionnaire")) routing_path = router.routing_path(section_id=section_id, list_item_id=list_item_id) section_status = questionnaire_store.progress_store.get_section_status( section_id=section_id, list_item_id=list_item_id) if section_status == CompletionStatus.COMPLETED: return redirect( router.get_section_return_location_when_section_complete( routing_path).url()) if section_status == CompletionStatus.NOT_STARTED: return redirect( router.get_first_incomplete_location_for_section( routing_path).url()) return redirect( router.get_first_incomplete_location_for_section( routing_path=routing_path).url())
def get_block( routing_path, schema, metadata, answer_store, eq_id, form_type, collection_id, group_id, # pylint: disable=too-many-locals group_instance, block_id): current_location = Location(group_id, group_instance, block_id) completeness = get_completeness(current_user) router = Router(schema, routing_path, completeness, current_location) if not router.can_access_location(): next_location = router.get_next_location() return _redirect_to_location(collection_id, eq_id, form_type, next_location) block = _get_block_json(current_location, schema, answer_store, metadata) context = _get_context(routing_path, block, current_location, schema) return _render_page(block['type'], context, current_location, schema, answer_store, metadata, routing_path)
def __init__(self, schema, questionnaire_store, section_id, list_item_id, language): self._schema = schema self._questionnaire_store = questionnaire_store self._section_id = section_id self._list_item_id = list_item_id self._language = language self._router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, questionnaire_store.response_metadata, ) if not self._is_valid_location(): raise InvalidLocationException(f"location {self._section_id} is not valid") self.current_location = Location( section_id=self._section_id, list_name=self._schema.get_repeating_list_for_section(self._section_id), list_item_id=self._list_item_id, ) self._routing_path = self._router.routing_path( section_id=self._section_id, list_item_id=self._list_item_id )
def test_get_section_return_location_when_section_complete_section_summary( self): schema = load_schema_from_name("test_hub_and_spoke") router = Router( schema, self.answer_store, self.list_store, self.progress_store, self.metadata, ) routing_path = RoutingPath( ["proxy", "accommodation-details-summary"], section_id="accommodation-section", ) location_when_section_complete = router.get_section_return_location_when_section_complete( routing_path=routing_path) self.assertEqual( location_when_section_complete, Location( section_id="accommodation-section", block_id="accommodation-details-summary", ), )
def get_questionnaire(schema, questionnaire_store): router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, ) if not router.can_access_hub(): redirect_location = router.get_first_incomplete_location_in_survey() return redirect(redirect_location.url()) language_code = get_session_store().session_data.language_code hub = HubContext( language=language_code, schema=schema, answer_store=questionnaire_store.answer_store, list_store=questionnaire_store.list_store, progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, ) hub_context = hub.get_context( router.is_survey_complete(), router.enabled_section_ids ) return render_template("hub", content=hub_context)
def test_get_first_incomplete_location_in_section(self): schema = load_schema_from_name("test_section_summary") progress_store = ProgressStore([{ "section_id": "property-details-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": ["insurance-type"], }]) router = Router(schema, self.answer_store, self.list_store, progress_store, self.metadata) section_routing_path = RoutingPath( ["insurance-type", "insurance-address"], section_id="property-details-section", ) incomplete = router.get_first_incomplete_location_for_section( routing_path=section_routing_path) self.assertEqual( incomplete, Location(section_id="property-details-section", block_id="insurance-address"), )
def __init__( self, language: str, schema: QuestionnaireSchema, answer_store: AnswerStore, list_store: ListStore, progress_store: ProgressStore, metadata: Mapping[str, Any], response_metadata: Mapping, ): self._language = language self._schema = schema self._answer_store = answer_store self._list_store = list_store self._progress_store = progress_store self._metadata = metadata self._response_metadata = response_metadata self._router = Router( self._schema, self._answer_store, self._list_store, self._progress_store, self._metadata, self._response_metadata, ) self._placeholder_renderer = PlaceholderRenderer( language=self._language, answer_store=self._answer_store, list_store=self._list_store, metadata=self._metadata, response_metadata=self._response_metadata, schema=self._schema, )
def router(self): return Router( schema=self._schema, answer_store=self._questionnaire_store.answer_store, list_store=self._questionnaire_store.list_store, progress_store=self._questionnaire_store.progress_store, metadata=self._questionnaire_store.metadata, )
def router(self): return Router( self.schema, self.answer_store, self.list_store, self.progress_store, self.metadata, )
def router(schema, answer_store, list_store, progress_store): return Router( schema, answer_store, list_store, progress_store, metadata={}, response_metadata={}, )
def post_block( routing_path, schema, metadata, collection_metadata, answer_store, eq_id, form_type, collection_id, group_id, # pylint: disable=too-many-locals group_instance, block_id): current_location = Location(group_id, group_instance, block_id) completeness = get_completeness(current_user) router = Router(schema, routing_path, completeness, current_location) if not router.can_access_location(): next_location = router.get_next_location() return _redirect_to_location(collection_id, eq_id, form_type, next_location) block = _get_block_json(current_location, schema, answer_store, metadata) schema_context = _get_schema_context(routing_path, current_location, metadata, collection_metadata, answer_store, schema) rendered_block = renderer.render(block, **schema_context) form = _generate_wtf_form(request.form, rendered_block, current_location, schema) if 'action[save_sign_out]' in request.form: return _save_sign_out(routing_path, current_location, form, schema, answer_store, metadata) if form.validate(): _set_started_at_metadata_if_required(form, collection_metadata) _update_questionnaire_store(current_location, form, schema) next_location = path_finder.get_next_location( current_location=current_location) if _is_end_of_questionnaire(block, next_location): return submit_answers(routing_path, eq_id, form_type, schema) return redirect(_next_location_url(next_location)) context = build_view_context(block['type'], metadata, schema, answer_store, schema_context, rendered_block, current_location, form) return _render_page(block['type'], context, current_location, schema, answer_store, metadata, routing_path)
def _submit_data(user): answer_store = get_answer_store(user) if answer_store: questionnaire_store = get_questionnaire_store(user.user_id, user.user_ik) answer_store = questionnaire_store.answer_store metadata = questionnaire_store.metadata response_metadata = questionnaire_store.response_metadata progress_store = questionnaire_store.progress_store list_store = questionnaire_store.list_store submitted_at = datetime.now(timezone.utc) schema = load_schema_from_metadata(metadata) router = Router( schema, answer_store, list_store, progress_store, metadata, response_metadata, ) full_routing_path = router.full_routing_path() message = json_dumps( convert_answers( schema, questionnaire_store, full_routing_path, submitted_at, flushed=True, )) encrypted_message = encrypt(message, current_app.eq["key_store"], KEY_PURPOSE_SUBMISSION) sent = current_app.eq["submitter"].send_message( encrypted_message, tx_id=metadata.get("tx_id"), case_id=metadata["case_id"], ) if not sent: raise SubmissionFailedException() get_questionnaire_store(user.user_id, user.user_ik).delete() logger.info("successfully flushed answers") return True logger.info("no answers found to flush") return False
def test_is_survey_not_complete(self): schema = load_schema_from_name("test_textfield") router = Router( schema, self.answer_store, self.list_store, self.progress_store, self.metadata, ) is_survey_complete = router.is_survey_complete() self.assertFalse(is_survey_complete)
def test_full_routing_path_with_repeating_sections(self): schema = load_schema_from_name( "test_repeating_sections_with_hub_and_spoke") list_store = ListStore([{ "items": ["abc123", "123abc"], "name": "people", "primary_person": "abc123", }]) router = Router(schema, self.answer_store, list_store, self.progress_store, self.metadata) routing_path = router.full_routing_path() expected_path = [ RoutingPath( [ "primary-person-list-collector", "list-collector", "next-interstitial", "another-list-collector-block", "visitors-block", ], section_id="section", list_name=None, list_item_id=None, ), RoutingPath( [ "proxy", "date-of-birth", "confirm-dob", "sex", "personal-summary" ], section_id="personal-details-section", list_name="people", list_item_id="abc123", ), RoutingPath( [ "proxy", "date-of-birth", "confirm-dob", "sex", "personal-summary" ], section_id="personal-details-section", list_name="people", list_item_id="123abc", ), ] self.assertEqual(routing_path, expected_path)
def test_is_survey_complete(self): schema = load_schema_from_name("test_textfield") progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": ["name-block"], }]) router = Router(schema, self.answer_store, self.list_store, progress_store, self.metadata) is_survey_complete = router.is_survey_complete() self.assertTrue(is_survey_complete)
def login(): """ Initial url processing - expects a token parameter and then will authenticate this token. Once authenticated it will be placed in the users session :return: a 302 redirect to the next location for the user """ # logging in again clears any session state if cookie_session: cookie_session.clear() decrypted_token = decrypt_token(request.args.get('token')) validate_jti(decrypted_token) claims = parse_runner_claims(decrypted_token) g.schema = load_schema_from_metadata(claims) schema_metadata = g.schema.json['metadata'] validate_metadata(claims, schema_metadata) eq_id = claims['eq_id'] form_type = claims['form_type'] tx_id = claims['tx_id'] ru_ref = claims['ru_ref'] logger.bind(eq_id=eq_id, form_type=form_type, tx_id=tx_id, ru_ref=ru_ref) logger.info('decrypted token and parsed metadata') store_session(claims) cookie_session['theme'] = g.schema.json['theme'] cookie_session['survey_title'] = g.schema.json['title'] cookie_session['expires_in'] = get_session_timeout_in_seconds(g.schema) if claims.get('account_service_url'): cookie_session['account_service_url'] = claims.get( 'account_service_url') if claims.get('account_service_log_out_url'): cookie_session['account_service_log_out_url'] = claims.get( 'account_service_log_out_url') routing_path = path_finder.get_full_routing_path() completeness = get_completeness(current_user) router = Router(g.schema, routing_path, completeness) next_location = router.get_next_location() return redirect(next_location.url(claims))
def dump_routing(schema, questionnaire_store): router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, ) response = [{ "section_id": routing_path.section_id, "list_item_id": routing_path.list_item_id, "routing_path": routing_path.block_ids, } for routing_path in router.full_routing_path()] return json_dumps(response), 200
def test_is_path_not_complete(self): schema = load_schema_from_name("test_textfield") router = Router( schema, self.answer_store, self.list_store, self.progress_store, self.metadata, ) routing_path = router.routing_path(section_id="default-section") is_path_complete = router.is_path_complete(routing_path) self.assertFalse(is_path_complete)
def get_thank_you(schema, metadata, eq_id, form_type): session_data = get_session_store().session_data completeness = get_completeness(current_user) if session_data.submitted_time: metadata_context = build_metadata_context_for_survey_completed( session_data) view_submission_url = None view_submission_duration = 0 if _is_submission_viewable(schema.json, session_data.submitted_time): view_submission_url = url_for('.get_view_submission', eq_id=eq_id, form_type=form_type) view_submission_duration = humanize.naturaldelta( timedelta(seconds=schema.json['view_submitted_response'] ['duration'])) cookie_message = request.cookies.get('ons_cookie_message_displayed') allow_analytics = analytics_allowed(request) return render_theme_template( schema.json['theme'], template_name='thank-you.html', metadata=metadata_context, analytics_gtm_id=current_app.config['EQ_GTM_ID'], analytics_gtm_env_id=current_app.config['EQ_GTM_ENV_ID'], survey_id=schema.json['survey_id'], survey_title=TemplateRenderer.safe_content(schema.json['title']), is_view_submitted_response_enabled= is_view_submitted_response_enabled(schema.json), view_submission_url=view_submission_url, account_service_url=cookie_session.get('account_service_url'), account_service_log_out_url=cookie_session.get( 'account_service_log_out_url'), view_submission_duration=view_submission_duration, cookie_message=cookie_message, allow_analytics=allow_analytics) routing_path = path_finder.get_full_routing_path() router = Router(schema, routing_path, completeness) next_location = router.get_next_location() return _redirect_to_location(metadata['collection_exercise_sid'], metadata.get('eq_id'), metadata.get('form_type'), next_location)
def test_is_path_complete(self): schema = load_schema_from_name("test_textfield") progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.IN_PROGRESS, "block_ids": ["name-block"], }]) router = Router(schema, self.answer_store, self.list_store, progress_store, self.metadata) routing_path = router.routing_path(section_id="default-section") is_path_complete = router.is_path_complete(routing_path) self.assertTrue(is_path_complete)
def test_cant_access_location_invalid_list_item_id(self): schema = load_schema_from_name("test_textfield") router = Router( schema, self.answer_store, self.list_store, self.progress_store, self.metadata, ) current_location = Location(section_id="default-section", block_id="name-block") routing_path = [] can_access_location = router.can_access_location( current_location, routing_path) self.assertFalse(can_access_location)
def dump_submission(schema, questionnaire_store): router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, ) routing_path = router.full_routing_path() questionnaire_store = get_questionnaire_store( current_user.user_id, current_user.user_ik ) response = { "submission": convert_answers(schema, questionnaire_store, routing_path) } return json.dumps(response, for_json=True), 200
def test_cant_access_location_section_disabled(self): schema = load_schema_from_name("test_section_enabled_checkbox") router = Router( schema, self.answer_store, self.list_store, self.progress_store, self.metadata, ) current_location = Location(section_id="section-2", block_id="section-2-block", list_item_id=None) can_access_location = router.can_access_location(current_location, routing_path=[]) self.assertFalse(can_access_location)
def test_get_next_location_no_completed_blocks(self): schema = load_schema_from_params('test', 'is_skipping_to_end') current_location = Location('test-skipping-group', 0, 'test-skipping-forced') routing_path = [ Location('test-skipping-group', 0, 'test-skipping-forced') ] completeness = Completeness(schema, answer_store=MagicMock(), completed_blocks=[], routing_path=routing_path, metadata={}) router = Router(schema, routing_path, completeness, current_location) self.assertEqual(routing_path[0], router.get_next_location())
def dump_submission(schema, questionnaire_store): router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, ) routing_path = router.full_routing_path() questionnaire_store = get_questionnaire_store(current_user.user_id, current_user.user_ik) submission_handler = SubmissionHandler(schema, questionnaire_store, routing_path) response = {"submission": submission_handler.get_payload()} return json_dumps(response), 200
def post_questionnaire(schema, questionnaire_store): if any(action in request.form for action in ("action[save_sign_out]", "action[sign_out]")): return redirect(url_for("session.get_sign_out")) router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, ) if schema.is_hub_enabled() and router.is_survey_complete(): return submit_answers(schema, questionnaire_store, router.full_routing_path()) return redirect(router.get_first_incomplete_location_in_survey().url())
def get_questionnaire(schema, questionnaire_store): router = Router( schema, questionnaire_store.answer_store, questionnaire_store.list_store, questionnaire_store.progress_store, questionnaire_store.metadata, questionnaire_store.response_metadata, ) if not router.can_access_hub(): redirect_location_url = ( router.get_first_incomplete_location_in_questionnaire_url() ) return redirect(redirect_location_url) if request.method == "POST": if router.is_questionnaire_complete: submission_handler = SubmissionHandler( schema, questionnaire_store, router.full_routing_path() ) submission_handler.submit_questionnaire() return redirect(url_for("post_submission.get_thank_you")) return redirect(router.get_first_incomplete_location_in_questionnaire_url()) hub_context = HubContext( language=flask_babel.get_locale().language, schema=schema, answer_store=questionnaire_store.answer_store, list_store=questionnaire_store.list_store, progress_store=questionnaire_store.progress_store, metadata=questionnaire_store.metadata, response_metadata=questionnaire_store.response_metadata, ) context = hub_context( survey_complete=router.is_questionnaire_complete, enabled_section_ids=router.enabled_section_ids, ) return render_template( "hub", content=context, page_title=context["title"], )