def respondent_add_survey(): payload = request.get_json() or {} v = Validator(Exists('party_id', 'enrolment_code')) if not v.validate(payload): logger.debug(v.errors, url=request.url) raise BadRequest(v.errors) account_controller.add_new_survey_for_respondent(payload) return make_response(jsonify('OK'), 200)
def change_respondent_enrolment_status(): payload = request.get_json() or {} v = Validator( Exists('respondent_id', 'business_id', 'survey_id', 'change_flag')) if not v.validate(payload): logger.debug(v.errors, url=request.url) raise BadRequest(v.errors) account_controller.change_respondent_enrolment_status(payload) return make_response(jsonify('OK'), 200)
def change_respondent(payload, session): """ Modify an existing respondent's email address, identified by their current email address. """ v = Validator(Exists('email_address', 'new_email_address')) if not v.validate(payload): logger.info("Payload for change respondent was invalid", errors=v.errors) raise BadRequest(v.errors, 400) email_address = payload['email_address'] new_email_address = payload['new_email_address'] respondent = query_respondent_by_email(email_address, session) if not respondent: logger.info("Respondent does not exist") raise NotFound("Respondent does not exist") if new_email_address == email_address: return respondent.to_respondent_dict() respondent_with_new_email = query_respondent_by_email( new_email_address, session) if respondent_with_new_email: logger.info("Respondent with email already exists") raise Conflict("New email address already taken") respondent.pending_email_address = new_email_address # check if respondent has initiated this request if 'change_requested_by_respondent' in payload: verification_url = PublicWebsite().confirm_account_email_change_url( new_email_address) personalisation = { 'CONFIRM_EMAIL_URL': verification_url, 'FIRST_NAME': respondent.first_name } logger.info('Account change email URL for party_id', party_id=str(respondent.party_uuid), url=verification_url) _send_account_email_change_email( personalisation, template='verify_account_email_change', email=new_email_address, party_id=respondent.party_uuid) else: _send_email_verification(respondent.party_uuid, new_email_address) logger.info('Verification email sent for changing respondents email', respondent_id=str(respondent.party_uuid)) return respondent.to_respondent_dict()
def put_edit_account_status(party_id): # This is the party endpoint to lock and notify respondent when account is locked # This is also the end point for internal user to unlock a respondents account payload = request.get_json() or {} v = Validator(Exists('status_change')) if not v.validate(payload): logger.debug(v.errors, url=request.url) raise BadRequest(v.errors) response = account_controller.notify_change_account_status( payload=payload, party_id=party_id) return make_response(jsonify(response), 200)
def businesses_sample_ce_link(sample, ce_data, session): """ Update business versions to have the correct collection exercise and sample link. :param sample: the sample summary id to update. :param ce_data: dictionary containing the collectionExerciseId to link with sample. :param session: database session. """ v = Validator(Exists('collectionExerciseId')) if not v.validate(ce_data): logger.debug(v.errors) raise BadRequest(v.errors) collection_exercise_id = ce_data['collectionExerciseId'] session.query(BusinessAttributes).filter(BusinessAttributes.sample_summary_id == sample)\ .update({'collection_exercise': collection_exercise_id})
def post_pending_shares(): """ Creates new records for pending shares accepted payload example: { pending_shares: [{ "business_id": "business_id" "survey_id": "survey_id", "email_address": "email_address", "shared_by": "respondent_id" }, { "business_id": "business_id": "survey_id": "survey_id", "email_address": "email_address", "shared_by": "respondent_id" }] } """ payload = request.get_json() or {} try: pending_shares = payload['pending_shares'] if len(pending_shares) == 0: raise BadRequest('Payload Invalid - pending_shares list is empty') for share in pending_shares: # Validation, curation and checks expected = ('email_address', 'survey_id', 'business_id', 'shared_by') v = Validator(Exists(*expected)) if not v.validate(share): logger.debug(v.errors) raise BadRequest(v.errors) for pending_share in pending_shares: share_survey_controller.pending_share_create(business_id=pending_share['business_id'], survey_id=pending_share['survey_id'], email_address=pending_share['email_address'], shared_by=pending_share['shared_by']) # TODO: Add logic to send email return make_response(jsonify({"created": "success"}), 201) except KeyError: raise BadRequest('Payload Invalid - Pending share key missing') except SQLAlchemyError: raise BadRequest('This share is already in progress')
def post_respondent(party, session): """ Register respondent and set up pending enrolment before account verification :param party: respondent to be created details :param session :return: created respondent """ # Validation, curation and checks expected = ('emailAddress', 'firstName', 'lastName', 'password', 'telephone', 'enrolmentCode') v = Validator(Exists(*expected)) if 'id' in party: # Note: there's not strictly a requirement to be able to pass in a UUID, this is currently supported to # aid with testing. logger.info("'id' in respondent post message") try: uuid.UUID(party['id']) except ValueError: logger.info("Invalid respondent id type", respondent_id=party['id']) raise BadRequest( f"'{party['id']}' is not a valid UUID format for property 'id'" ) if not v.validate(party): logger.debug(v.errors) raise BadRequest(v.errors) iac = request_iac(party['enrolmentCode']) if not iac.get('active'): logger.info("Inactive enrolment code") raise BadRequest("Enrolment code is not active") existing = query_respondent_by_email(party['emailAddress'].lower(), session) if existing: logger.info("Email already exists", party_uuid=str(existing.party_uuid)) raise BadRequest("Email address already exists") case_context = request_case(party['enrolmentCode']) case_id = case_context['id'] business_id = case_context['partyId'] collection_exercise_id = case_context['caseGroup']['collectionExerciseId'] collection_exercise = request_collection_exercise(collection_exercise_id) survey_id = collection_exercise['surveyId'] business = query_business_by_party_uuid(business_id, session) if not business: logger.error( "Could not locate business when creating business association", business_id=business_id, case_id=case_id, collection_exercise_id=collection_exercise_id) raise InternalServerError( "Could not locate business when creating business association") # Chain of enrolment processes translated_party = { 'party_uuid': party.get('id') or str(uuid.uuid4()), 'email_address': party['emailAddress'].lower(), 'first_name': party['firstName'], 'last_name': party['lastName'], 'telephone': party['telephone'], 'status': RespondentStatus.CREATED } # This might look odd but it's done in the interest of keeping the code working in the same way. # If raise_for_status in the function raises an error, it would've been caught by @with_db_session, # rolled back the db and raised it. Whether that's something we want is another question. try: respondent = _add_enrolment_and_auth(business, business_id, case_id, party, session, survey_id, translated_party) except HTTPError: logger.error("add_enrolment_and_auth raised an HTTPError", exc_info=True) session.rollback() raise # If the disabling of the enrolment code fails we log an error and continue anyway. In the interest of keeping # the code working in the same way (which may itself be wrong...) we'll handle the ValueError that can be raised # in the same way as before (rollback the session and raise) but it's not clear whether this is the desired # behaviour. try: disable_iac(party['enrolmentCode'], case_id) except ValueError: logger.error("disable_iac didn't return json in its response", exc_info=True) session.rollback() raise _send_email_verification(respondent.party_uuid, party['emailAddress'].lower()) return respondent.to_respondent_dict()
def _is_valid(payload, attribute): v = Validator(Exists(attribute)) if v.validate(payload): return True logger.debug(v.errors) raise BadRequest(v.errors, 400)