def post_household_composition(eq_id, form_type, collection_id, group_id): questionnaire_manager = get_questionnaire_manager(g.schema, g.schema_json) answer_store = get_answer_store(current_user) this_location = Location(group_id, group_instance=0, block_id='household-composition') if 'action[save_continue]' in request.form: _remove_repeating_on_household_answers(answer_store, group_id) valid = questionnaire_manager.process_incoming_answers(this_location, post_data=request.form) if 'action[add_answer]' in request.form: questionnaire_manager.add_answer(this_location, answer_store, question_id='household-composition-question') return get_block(eq_id, form_type, collection_id, group_id, group_instance=0, block_id='household-composition') elif 'action[remove_answer]' in request.form: index_to_remove = int(request.form.get('action[remove_answer]')) questionnaire_manager.remove_answer(this_location, answer_store, index_to_remove) return get_block(eq_id, form_type, collection_id, group_id, group_instance=0, block_id='household-composition') elif 'action[save_sign_out]' in request.form: return _save_sign_out(collection_id, eq_id, form_type, questionnaire_manager, this_location) if not valid: _render_schema(this_location) return _build_template(current_location=this_location, context=questionnaire_manager.block_state, template='questionnaire') path_finder = PathFinder(g.schema_json, get_answer_store(current_user), get_metadata(current_user)) next_location = path_finder.get_next_location(current_location=this_location) metadata = get_metadata(current_user) return redirect(next_location.url(metadata))
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 check_survey_state(): metadata = get_metadata(current_user) logger.new(tx_id=metadata['tx_id']) g.schema_json = get_schema(get_metadata(current_user)) values = request.view_args logger.debug('questionnaire request', eq_id=values['eq_id'], form_type=values['form_type'], ce_id=values['collection_id'], method=request.method, url_path=request.full_path) _check_same_survey(values['eq_id'], values['form_type'], values['collection_id'])
def get_confirmation(eq_id, form_type, collection_id): # pylint: disable=unused-argument answer_store = get_answer_store(current_user) path_finder = PathFinder(g.schema_json, answer_store, get_metadata(current_user)) latest_location = path_finder.get_latest_location(get_completed_blocks(current_user)) if latest_location.block_id == 'confirmation': block = _render_schema(latest_location) return _build_template(current_location=latest_location, context={"block": block}) metadata = get_metadata(current_user) return redirect(latest_location.url(metadata))
def get_summary(eq_id, form_type, collection_id): # pylint: disable=unused-argument answer_store = get_answer_store(current_user) path_finder = PathFinder(g.schema_json, answer_store, get_metadata(current_user)) latest_location = path_finder.get_latest_location(get_completed_blocks(current_user)) metadata = get_metadata(current_user) if latest_location.block_id is 'summary': answers = get_answer_store(current_user) schema_context = build_schema_context(metadata, g.schema.aliases, answers) rendered_schema_json = renderer.render(g.schema_json, **schema_context) summary_context = build_summary_rendering_context(rendered_schema_json, answer_store, metadata) return _build_template(current_location=latest_location, context=summary_context) return redirect(latest_location.url(metadata))
def _check_same_survey(eq_id, form_type, collection_id): metadata = get_metadata(current_user) current_survey = eq_id + form_type + collection_id metadata_survey = metadata["eq_id"] + metadata["form_type"] + metadata[ "collection_exercise_sid"] if current_survey != metadata_survey: raise MultipleSurveyError
def _submit_data(user): answer_store = get_answer_store(user) if answer_store.answers: metadata = get_metadata(user) collection_metadata = get_collection_metadata(user) schema = load_schema_from_metadata(metadata) completed_blocks = get_completed_blocks(user) routing_path = PathFinder(schema, answer_store, metadata, completed_blocks).get_full_routing_path() message = convert_answers(metadata, collection_metadata, schema, answer_store, routing_path, flushed=True) encrypted_message = encrypt(message, current_app.eq['key_store'], KEY_PURPOSE_SUBMISSION) sent = current_app.eq['submitter'].send_message( encrypted_message, current_app.config['EQ_RABBITMQ_QUEUE_NAME'], metadata['tx_id']) if not sent: raise SubmissionFailedException() get_questionnaire_store(user.user_id, user.user_ik).delete() return True return False
def _submit_data(user): answer_store = get_answer_store(user) if answer_store.answers: metadata = get_metadata(user) schema = load_schema_from_metadata(metadata) routing_path = PathFinder(schema, answer_store, metadata).get_routing_path() message = convert_answers(metadata, schema, answer_store, routing_path, flushed=True) 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() get_questionnaire_store(user.user_id, user.user_ik).delete() return True else: return False
def role_required_wrapper(*args, **kwargs): metadata = get_metadata(current_user) roles = metadata and metadata.get('roles', []) or [] if current_user.is_authenticated and role in roles: return func(*args, **kwargs) else: raise Forbidden
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 before_questionnaire_request(): if request.method == "OPTIONS": return None if cookie_session.get("submitted"): raise PreviouslySubmittedException( "The Questionnaire has been previously submitted" ) metadata = get_metadata(current_user) if not metadata: raise NoQuestionnaireStateException(401) questionnaire_store = get_questionnaire_store( current_user.user_id, current_user.user_ik ) if questionnaire_store.submitted_at: return redirect(url_for("post_submission.get_thank_you")) logger.bind( tx_id=metadata["tx_id"], schema_name=metadata["schema_name"], ce_id=metadata["collection_exercise_sid"], ) logger.info( "questionnaire request", method=request.method, url_path=request.full_path ) handle_language() session_store = get_session_store() # pylint: disable=assigning-non-slot g.schema = load_schema_from_session_data(session_store.session_data)
def before_questionnaire_request(): metadata = get_metadata(current_user) if not metadata: raise NoTokenException(401) logger.bind(tx_id=metadata['tx_id']) values = request.view_args if check_multiple_survey(metadata, values): raise MultipleSurveyError logger.bind(eq_id=values['eq_id'], form_type=values['form_type'], ce_id=values['collection_id']) logger.info('questionnaire request', method=request.method, url_path=request.full_path) session_store = get_session_store() session_data = session_store.session_data language_code = request.args.get('language_code') if language_code: session_data.language_code = language_code session_store.save() g.schema = load_schema_from_session_data(session_data)
def before_post_submission_request(): if request.method == "OPTIONS": return None metadata = get_metadata(current_user) if not metadata: raise NoQuestionnaireStateException(401) questionnaire_store = get_questionnaire_store( current_user.user_id, current_user.user_ik ) if not questionnaire_store.submitted_at: raise NotFound handle_language() session_store = get_session_store() session_data = session_store.session_data # pylint: disable=assigning-non-slot g.schema = load_schema_from_session_data(session_data) logger.bind(tx_id=session_data.tx_id, schema_name=session_data.schema_name) logger.info( "questionnaire request", method=request.method, url_path=request.full_path )
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 """ logger.new() # logging in again clears any session state if session: session.clear() logger.debug("attempting token authentication") authenticator.jwt_login(request) logger.debug("token authenticated - linking to session") metadata = get_metadata(current_user) eq_id = metadata["eq_id"] form_type = metadata["form_type"] logger.bind(eq_id=eq_id, form_type=form_type) if not eq_id or not form_type: logger.error("missing eq id or form type in jwt") raise NotFound json = get_schema(metadata) navigator = PathFinder(json, get_answer_store(current_user), metadata) current_location = navigator.get_latest_location( get_completed_blocks(current_user)) return redirect(current_location.url(metadata))
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 flush_data(): if session: session.clear() encrypted_token = request.args.get("token") if not encrypted_token or encrypted_token is None: return Response(status=403) decrypted_token = decrypt( token=encrypted_token, key_store=current_app.eq["key_store"], key_purpose=KEY_PURPOSE_AUTHENTICATION, leeway=current_app.config["EQ_JWT_LEEWAY_IN_SECONDS"], ) roles = decrypted_token.get("roles") if roles and "flusher" in roles: user = _get_user(decrypted_token["response_id"]) metadata = get_metadata(user) if "tx_id" in metadata: logger.bind(tx_id=metadata["tx_id"]) if _submit_data(user): return Response(status=200) return Response(status=404) return Response(status=403)
def before_questionnaire_request(): metadata = get_metadata(current_user) if not metadata: raise NoTokenException(401) logger.bind(tx_id=metadata['tx_id']) values = request.view_args logger.bind(eq_id=values['eq_id'], form_type=values['form_type'], ce_id=values['collection_id']) logger.info('questionnaire request', method=request.method, url_path=request.full_path) _check_same_survey( url_eq_id=values['eq_id'], url_form_type=values['form_type'], url_collection_id=values['collection_id'], session_eq_id=metadata['eq_id'], session_form_type=metadata['form_type'], session_collection_id=metadata['collection_exercise_sid']) session_data = get_session_store().session_data g.schema = load_schema_from_session_data(session_data)
def send_feedback(): if 'action[sign_out]' in request.form: return redirect(url_for('session.get_sign_out')) metadata = get_metadata(current_user) if not metadata: raise NoTokenException(401) form = FeedbackForm() if form.validate(): message = convert_feedback( escape(request.form.get('message')), escape(request.form.get('name')), escape(request.form.get('email')), request.referrer or '', metadata, g.schema.json['survey_id'], ) encrypted_message = encrypt(message, current_app.eq['key_store'], key_purpose=KEY_PURPOSE_SUBMISSION) sent = current_app.eq['submitter'].send_message(encrypted_message, current_app.config['EQ_RABBITMQ_QUEUE_NAME'], metadata['tx_id']) if not sent: raise SubmissionFailedException() if request.form.get('redirect', 'true') == 'true': return redirect(url_for('feedback.thank_you')) return '', 200
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 metadata_context_wrapper(*args, **kwargs): metadata = get_metadata(current_user) if not metadata: raise NoTokenException(401) metadata_context = build_metadata_context(metadata) return func(*args, metadata_context=metadata_context, **kwargs)
def csrf_error(error=None): metadata = get_metadata(current_user) if metadata and check_multiple_survey(metadata, request.view_args): log_exception(error, 200) return render_template('multiple_survey.html') log_exception(error, 401) return render_template('session-expired.html'), 401
def log_exception(error, status_code): metadata = get_metadata(current_user) if metadata: logger.bind(tx_id=metadata['tx_id']) logger.error('an error has occurred', exc_info=error, url=request.url, status_code=status_code)
def _render_error_page(status_code): tx_id = None metadata = get_metadata(current_user) if metadata: tx_id = convert_tx_id(metadata["tx_id"]) user_agent = user_agent_parser.Parse(request.headers.get('User-Agent', '')) return render_theme_template('default', 'errors/error.html', status_code=status_code, ua=user_agent, tx_id=tx_id), status_code
def _generate_wtf_form(form, block, location, schema): disable_mandatory = 'action[save_sign_out]' in form wtf_form = post_form_for_location(schema, block, location, get_answer_store(current_user), get_metadata(current_user), request.form, disable_mandatory) return wtf_form
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 post_household_composition(eq_id, form_type, collection_id, group_id): path_finder = PathFinder(g.schema_json, get_answer_store(current_user), get_metadata(current_user)) answer_store = get_answer_store(current_user) questionnaire_store = get_questionnaire_store(current_user.user_id, current_user.user_ik) current_location = Location(group_id, 0, 'household-composition') block = _render_schema(current_location) if _household_answers_changed(answer_store): _remove_repeating_on_household_answers(answer_store, group_id) error_messages = SchemaHelper.get_messages(g.schema_json) if any(x in request.form for x in ['action[add_answer]', 'action[remove_answer]', 'action[save_sign_out]']): disable_mandatory = True else: disable_mandatory = False form, _ = post_form_for_location(block, current_location, answer_store, request.form, error_messages, disable_mandatory=disable_mandatory) if 'action[add_answer]' in request.form: form.household.append_entry() elif 'action[remove_answer]' in request.form: index_to_remove = int(request.form.get('action[remove_answer]')) form.remove_person(index_to_remove) elif 'action[save_sign_out]' in request.form: return _save_sign_out(collection_id, eq_id, form_type, current_location, form) if not form.validate() or 'action[add_answer]' in request.form or 'action[remove_answer]' in request.form: return _render_template({ 'form': form, 'block': block, }, current_location.block_id, current_location=current_location, template='questionnaire') update_questionnaire_store_with_answer_data(questionnaire_store, current_location, form.serialise(current_location)) next_location = path_finder.get_next_location(current_location=current_location) metadata = get_metadata(current_user) return redirect(next_location.url(metadata))
def post_questionnaire(eq_id, form_type, period_id, collection_id, location): valid = g.questionnaire_manager.process_incoming_answers(location, request.form) if not valid: return render_page(location, False) navigator = g.questionnaire_manager.navigator next_location = navigator.get_next_location(get_answers(current_user), location) metadata = get_metadata(current_user) logger.info("Redirecting user to next location %s with tx_id=%s", next_location, metadata["tx_id"]) return redirect_to_questionnaire_page(eq_id, form_type, period_id, collection_id, next_location)
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 get_path_finder(): finder = getattr(g, 'path_finder', None) if finder is None: metadata = get_metadata(current_user) answer_store = get_answer_store(current_user) completed_blocks = get_completed_blocks(current_user) finder = PathFinder(g.schema, answer_store, metadata, completed_blocks) g.path_finder = finder return finder
def dump_submission(): answer_store = get_answer_store(current_user) metadata = get_metadata(current_user) schema = load_schema_from_metadata(metadata) routing_path = PathFinder(schema, answer_store, metadata).get_routing_path() response = { 'submission': convert_answers(metadata, schema, answer_store, routing_path) } return jsonify(response), 200
def url_prefix_wrapper(*args, **kwargs): metadata = get_metadata(current_user) metadata_context = build_metadata_context(metadata) url_prefix = '/questionnaire/{}/{}/{}'.format( metadata_context['eq_id'], metadata_context['form_type'], metadata_context['collection_id'], ) return func(*args, url_prefix=url_prefix, **kwargs)
def post_interstitial(eq_id, form_type, collection_id, block_id): # pylint: disable=unused-argument 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(SchemaHelper.get_first_group_id(g.schema_json), group_instance=0, block_id=block_id) q_manager.update_questionnaire_store(this_location) # Don't care if data is valid because there isn't any for interstitial if this_location not in path_finder.get_location_path(): _render_schema(this_location) return _build_template(current_location=this_location, context=q_manager.block_state, template='questionnaire') next_location = path_finder.get_next_location(current_location=this_location) if next_location is None: raise NotFound metadata = get_metadata(current_user) logger.info("Redirecting user to next location %s with tx_id=%s", str(next_location), metadata["tx_id"]) return redirect(next_location.url(metadata))
def submit_answers(eq_id, form_type, period_id, collection_id): answers = get_answers(current_user) # check that all the answers we have are valid before submitting the data is_valid, invalid_location = g.questionnaire_manager.validate_all_answers() if is_valid: submitter = SubmitterFactory.get_submitter() submitter.send_answers(get_metadata(current_user), g.questionnaire_manager.get_schema(), answers) return redirect_to_questionnaire_page(eq_id, form_type, period_id, collection_id, 'thank-you') else: return redirect_to_questionnaire_page(eq_id, form_type, period_id, collection_id, invalid_location)
def _build_exercise(): """ Build the exercise data from the survey metadata """ metadata = get_metadata(current_user) return { "start_date": to_date(metadata["ref_p_start_date"]), "end_date": to_date(metadata["ref_p_end_date"]), "employment_date": to_date(metadata["employment_date"]), "return_by": to_date(metadata["return_by"]), }
def get_schema(): """ Get the schema for the current user :return: (json, schema) # Tuple of json and schema object from schema file """ metadata = get_metadata(current_user) eq_id = metadata["eq_id"] form_type = metadata["form_type"] logger.debug("Requested questionnaire %s for form type %s", eq_id, form_type) json_schema, schema = load_and_parse_schema(eq_id, form_type) if not json_schema: raise ValueError("No schema available") logger.debug("Constructing brand new User Journey Manager") return json_schema, schema
def _build_survey_meta(self, questionnaire_schema): introduction = questionnaire_schema['introduction'] metadata = get_metadata(current_user) survey_meta = { "title": questionnaire_schema['title'], "survey_code": questionnaire_schema['survey_id'], "description": self._get_description(introduction), "information_to_provide": self._get_info_to_provide(introduction), "theme": questionnaire_schema['theme'], "return_by": self._format_date(metadata["return_by"]), "start_date": self._format_date(metadata["ref_p_start_date"]), "end_date": self._format_date(metadata["ref_p_end_date"]), "employment_date": self._format_date(metadata["employment_date"]), "period_str": metadata["period_str"], } return survey_meta
def _build_respondent_meta(): metadata = get_metadata(current_user) if metadata: respondent_id = metadata["ru_ref"] name = metadata["ru_name"] trading_as = metadata["trad_as"] else: respondent_id = None name = None trading_as = None respondent_meta = { "tx_id": convert_tx_id(metadata["tx_id"]), "respondent_id": respondent_id, "address": { "name": name, "trading_as": trading_as, }, } return respondent_meta
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 session: session.clear() authenticator = Authenticator() logger.debug("Attempting token authentication") authenticator.jwt_login(request) logger.debug("Token authenticated - linking to session") metadata = get_metadata(current_user) eq_id = metadata["eq_id"] collection_id = metadata["collection_exercise_sid"] form_type = metadata["form_type"] period_id = metadata["period_id"] logger.debug("Requested questionnaire %s for form type %s", eq_id, form_type) if not eq_id or not form_type: logger.error("Missing EQ id %s or form type %s in JWT", eq_id, form_type) raise NotFound json, schema = get_schema() questionnaire_manager = QuestionnaireManager(schema, json=json) navigator = questionnaire_manager.navigator current_location = navigator.get_latest_location(get_answers(current_user), get_completed_blocks(current_user)) return redirect('/questionnaire/' + eq_id + '/' + form_type + '/' + period_id + '/' + collection_id + '/' + current_location)
def same_survey(eq_id, form_type, period_id, collection_id): metadata = get_metadata(current_user) current_survey = eq_id + form_type + period_id + collection_id metadata_survey = metadata["eq_id"] + metadata["form_type"] + metadata["period_id"] + metadata["collection_exercise_sid"] return current_survey == metadata_survey