Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e605123
Initial changes to frontstage to accomodate changes to transferring o…
SteveScorfield Jan 17, 2025
60d579a
Moved test back into folder and have removed a redeundent one. Have a…
SteveScorfield Jan 17, 2025
2b8168d
Merged with survey-respondent-help-journey
SteveScorfield Jan 17, 2025
80fbcd5
Ran make
SteveScorfield Jan 17, 2025
d17c44b
Removed no longer used imports and var
SteveScorfield Jan 17, 2025
b08b74e
Fixed test
SteveScorfield Jan 17, 2025
9e1874b
Fixed lint issue again
SteveScorfield Jan 17, 2025
d17f864
Halfway house for updating code based on review
SteveScorfield Jan 20, 2025
6ca46b1
Have updated code based on comments
SteveScorfield Jan 21, 2025
c538d78
Merge branch 'survey-respondent-help-journey' into transfer-a-survey-…
SteveScorfield Jan 21, 2025
fcd894f
Remove breadcrumbs
LJBabbage Jan 23, 2025
b92ce88
restore accidental push to wrong branch
LJBabbage Jan 23, 2025
f5beb1f
Push of code based on comments, refactored a function in party and ha…
SteveScorfield Jan 23, 2025
ad0672b
Merged branch
SteveScorfield Jan 23, 2025
9d6c4cc
Have merged remote to branch
SteveScorfield Jan 23, 2025
92219c2
Revert "Have merged remote to branch"
SteveScorfield Jan 23, 2025
68cfebb
Have fixed merge issue
SteveScorfield Jan 23, 2025
296ae50
Merge branch 'survey-respondent-help-journey' into transfer-a-survey-…
SteveScorfield Jan 24, 2025
702f36a
Revert test command
SteveScorfield Jan 24, 2025
61b3bc8
Adding additional ignore in due to recently added vulnerability to th…
SteveScorfield Jan 24, 2025
a298ae1
refactor and simplify code
LJBabbage Jan 30, 2025
b2cc65d
Fix daft calls to the same service, rectify tests
LJBabbage Jan 30, 2025
1eb301c
Add transferred_surveys back in
LJBabbage Jan 30, 2025
f44643e
Correct todo transfered survey issue
LJBabbage Jan 30, 2025
37634f1
POC for validating against pending_surveys
pricem14pc Jan 31, 2025
5e80064
Minor change
pricem14pc Jan 31, 2025
e74561e
Complete the logic
pricem14pc Jan 31, 2025
dbd0153
Adding detail to error message
pricem14pc Feb 1, 2025
4c6481b
Update account_transfer_survey.py
pricem14pc Feb 2, 2025
d8fe3ca
Merging from survey-respondent-help-journey
pricem14pc Feb 5, 2025
8967463
Fixing after survey-help-journey merge and new tests
pricem14pc Feb 5, 2025
a15b933
Merge branch 'survey-respondent-help-journey' into transfer-a-survey-…
pricem14pc Feb 13, 2025
f7ebbca
Merge branch 'survey-respondent-help-journey' into transfer-a-survey-…
pricem14pc Feb 19, 2025
7d24b2c
Recent refactor means we change the way we get enrolment details
pricem14pc Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions frontstage/controllers/party_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,22 @@ def register_pending_surveys(payload: json, party_id: str) -> requests.Response:
return response


def get_existing_pending_surveys(party_id: str) -> dict:
"""
Returns a list of pending_surveys that already exist in the DB for this user
"""
url = f"{app.config['PARTY_URL']}/party-api/v1/pending-surveys/originator/{party_id}"
response = requests.get(url, auth=app.config["BASIC_AUTH"])
try:
response.raise_for_status()
except requests.exceptions.HTTPError:
logger.error("Failed to get pending surveys for respondent", party_id=party_id)
raise ApiError(logger, response)

logger.info("Successfully retrieved pending surveys for respondent", party_id=party_id)
return response.json()


def get_pending_surveys_batch_number(batch_no):
"""
Gets batch number for the shared survey
Expand Down
73 changes: 73 additions & 0 deletions frontstage/views/account/account_transfer_survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from frontstage.controllers import party_controller, survey_controller
from frontstage.controllers.party_controller import (
get_business_by_id,
get_existing_pending_surveys,
get_respondent_enrolments,
get_surveys_to_transfer_map,
register_pending_surveys,
Expand Down Expand Up @@ -92,11 +93,83 @@ def transfer_survey_email_entry(session):
return render_template("surveys/surveys-transfer/recipient-email-address.html", form=form, errors=errors)

flask_session["transfer_survey_recipient_email_address"] = form.data["email_address"]

existing_pending_surveys = get_existing_pending_surveys(party_id)

if existing_pending_surveys:
duplicate_transfers = _get_duplicate_transfers(existing_pending_surveys, form.data["email_address"])
if duplicate_transfers:
business_survey_enrolments = get_respondent_enrolments(party_id)
errors = {
"email_address": [
_build_duplicate_transfer_error_message(duplicate_transfers, business_survey_enrolments)
]
}
return render_template(
"surveys/surveys-transfer/recipient-email-address.html", form=form, errors=errors
)

return redirect(url_for("account_bp.send_transfer_instruction_get"))

return render_template("surveys/surveys-transfer/recipient-email-address.html", form=form)


def _get_duplicate_transfers(existing_pending_surveys, email_address):
duplicate_transfers = []
for existing_pending_survey in existing_pending_surveys:
if (
_existing_pending_survey_is_in_selection(
flask_session["surveys_to_transfer_map"],
existing_pending_survey["business_id"],
existing_pending_survey["survey_id"],
)
and existing_pending_survey["email_address"].lower() == email_address.lower()
):
duplicate_transfers.append(
{
"business_id": existing_pending_survey["business_id"],
"survey_id": existing_pending_survey["survey_id"],
"email_address": existing_pending_survey["email_address"].lower(),
}
)
return duplicate_transfers


def _build_duplicate_transfer_error_message(duplicate_transfers, business_survey_enrolments):
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can probably be improved as it's clunky chaining this and including the HTML. But it's explicit and serves its purpose.

error_message = "You have already shared or transferred the following surveys to this email address. "
error_message += "They have 72 hours to accept your request. "
error_message += "<br /><br />If you have made an error then wait for the share/transfer to expire or contact us."
error_message += "<ul>"
for transfer in duplicate_transfers:
business_name = next(
(
business["business_name"]
for business in business_survey_enrolments
if business["business_id"] == transfer["business_id"]
),
None,
)
survey_name = next(
(
survey["long_name"]
for business in business_survey_enrolments
if business["business_id"] == transfer["business_id"]
for survey in business["survey_details"]
if survey["id"] == transfer["survey_id"]
),
None,
)
error_message += "<li>" + business_name + " - " + survey_name + "</li>"
error_message += "</ul>"
return error_message


def _existing_pending_survey_is_in_selection(surveys_to_transfer_map, business_id, survey_id):
if business_id in surveys_to_transfer_map:
return survey_id in surveys_to_transfer_map[business_id]
return False


@account_bp.route("/transfer-surveys/send-instruction", methods=["GET"])
@jwt_authorization(request)
def send_transfer_instruction_get(_):
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/mocked_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
with open("tests/test_data/survey/bricks_survey.json") as fp:
survey = json.load(fp)

with open("tests/test_data/party/pending_surveys.json") as fp:
pending_surveys = json.load(fp)

with open("tests/test_data/survey/qbs_survey.json") as fp:
survey_eq = json.load(fp)

Expand Down Expand Up @@ -108,6 +111,7 @@
url_get_business_party = f"{app.config['PARTY_URL']}/party-api/v1/businesses/id/{business_party['id']}"
url_get_respondent_party = f"{app.config['PARTY_URL']}/party-api/v1/respondents/party_id/{party['id']}"
url_get_respondent_enrolments = f"{app.config['PARTY_URL']}/party-api/v1/enrolments/respondent/{party['id']}"
url_get_existing_pending_surveys = f"{app.config['PARTY_URL']}/party-api/v1/pending-surveys/originator/{party['id']}"
url_get_case = f"{app.config['CASE_URL']}/cases/{case['id']}"
url_get_case_by_enrolment_code = f"{app.config['CASE_URL']}/cases/iac/{enrolment_code}"
url_get_case_categories = f"{app.config['CASE_URL']}/categories"
Expand Down Expand Up @@ -137,6 +141,7 @@
url_auth_token = f"{app.config['AUTH_URL']}/api/v1/tokens/"
url_password_change = f"{app.config['PARTY_URL']}/party-api/v1/respondents/change_password"
url_post_add_survey = f"{app.config['PARTY_URL']}/party-api/v1/respondents/add_survey"
url_get_post_add_survey = f"{app.config['PARTY_URL']}/party-api/v1/respondents/add_survey"
url_post_case_event_uuid = f"{app.config['CASE_URL']}/cases/{case['id']}/events"
url_reset_password_request = f"{app.config['PARTY_URL']}/party-api/v1/respondents/request_password_change"
url_send_message = app.config["SECURE_MESSAGE_URL"] + "/messages"
Expand Down
31 changes: 31 additions & 0 deletions tests/integration/views/account/test_transfer_surveys.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
business_party,
encoded_jwt_token,
party,
pending_surveys,
respondent_enrolments,
respondent_party,
survey,
url_banner_api,
url_get_existing_pending_surveys,
url_get_respondent_enrolments,
url_get_respondent_party,
url_get_survey,
)
Expand Down Expand Up @@ -153,12 +156,40 @@ def test_transfer_survey_recipient_email_invalid(self, mock_request):
self.assertIn("Problem with the email address".encode(), response.data)
self.assertIn("Invalid email address".encode(), response.data)

@requests_mock.mock()
def test_transfer_survey_transfer_duplicates(self, mock_request):
mock_request.get(url_banner_api, status_code=404)
mock_request.get(url_get_respondent_party, status_code=200, json=respondent_party)
mock_request.get(url_get_business_details, status_code=200, json=[business_party])
mock_request.get(url_get_survey, status_code=200, json=survey)
mock_request.get(url_get_existing_pending_surveys, status_code=200, json=pending_surveys)
mock_request.get(url_get_respondent_enrolments, json=respondent_enrolments)
with self.app.session_transaction() as mock_session:
mock_session["surveys_to_transfer_map"] = {business_party["id"]: [survey["id"]]}
response = self.app.post(
"/my-account/transfer-surveys/recipient-email-address",
data={"email_address": "bob@here.com"},
follow_redirects=True,
)
self.assertEqual(response.status_code, 200)
self.assertIn(
"You have already shared or transferred the following surveys to this email address.".encode(),
response.data,
)
self.assertIn("They have 72 hours to accept your request.".encode(), response.data)
self.assertIn(
"<br /><br />If you have made an error then wait for the share/transfer to expire or contact us.".encode(),
response.data,
)
self.assertIn("<ul><li>Business 3 - Survey 2</li></ul>".encode(), response.data)
Comment on lines +175 to +184
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit naff, but again it tests the feature and message in one hit so serves its purpose. We might want to extend the test data to include multiple duplicates (e.g. more <li>..</li> items) or permutations of pending surveys to ensure all use cases are covered. This test is simply wiring in all the mocks etc to cover a basic use case for the /recipient-email-address page.


@requests_mock.mock()
def test_transfer_survey_transfer_instruction(self, mock_request):
mock_request.get(url_banner_api, status_code=404)
mock_request.get(url_get_respondent_party, status_code=200, json=respondent_party)
mock_request.get(url_get_business_details, status_code=200, json=[business_party])
mock_request.get(url_get_survey, status_code=200, json=survey)
mock_request.get(url_get_existing_pending_surveys, status_code=200, json=pending_surveys)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exiting implementation now requires a list of pending surveys originated by this respondent

with self.app.session_transaction() as mock_session:
mock_session["surveys_to_transfer_map"] = {business_party["id"]: [survey["id"]]}
response = self.app.post(
Expand Down
11 changes: 11 additions & 0 deletions tests/test_data/party/pending_surveys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"batch_no": "be34d7ca-1d06-4f6e-98e3-73b80c911d32",
"business_id": "be3483c3-f5c9-4b13-bdd7-244db78ff687",
"email_address": "bob@here.com",
"is_transfer": true,
"shared_by": "f956e8ae-6e0f-4414-b0cf-a07c1aa3e37b",
"survey_id": "cb8accda-6118-4d3b-85a3-149e28960c54",
"time_shared": "2025-02-05 16:08:58"
}
Comment on lines +2 to +10
Copy link
Copy Markdown
Contributor Author

@pricem14pc pricem14pc Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned, we could introduce more permutations of these to test various scenarios are covered. Fairly easy to do that.

]