-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RAS-939 Survey Status Change #914
Conversation
…to change-status-to-not-started
{% set survey_link = ' <a href="' + url_for("case_bp.get_response_statuses", ru_ref=ru.sampleUnitRef, survey=survey.shortName, period=ce.exerciseRef) + '">Change</a>' %} | ||
{% elif ce.responseStatus in ['Completed', 'Completed by phone', 'No longer required'] and isReportingUnitPermission %} | ||
{% elif isReportingUnitPermission %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure about this since I added the permission check in the next page as well so this might be worth just being an else and then view
link
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to just make this an if else and show the view to all users as that is how it is used across the rest of the site. It just made more sense to me
@@ -67,12 +67,10 @@ <h3 id="COLLECTION_EXERCISES" class="ons-u-fs-m">Collection exercises</h3> | |||
{% set status_class = 'ons-status--error' %} | |||
{% endif %} | |||
|
|||
{% if ce.responseStatus in ['Not started', 'In progress'] and isReportingUnitPermission %} | |||
{% if ce.responseStatus in ['Not started', 'In progress','Completed', 'Completed by phone', 'No longer required'] and isReportingUnitPermission %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't going to work in conjunction with eQ, QuestionnaireState which holds the answers for eQ will still be present if Completed by phone or No longer required has been used and the respondent started the survey. eQ only deletes QuestionnaireState if it's completed by them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to take the opportunity to move to this to the backend, removing the logic from the template. It only needs the check and link generation, not everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't going to work in conjunction with eQ, QuestionnaireState which holds the answers for eQ will still be present if Completed by phone or No longer required has been used and the respondent started the survey. eQ only deletes QuestionnaireState if it's completed by them
The issue of EQ post submission state has also been raised when changing from COMPLETED to NOT STARTED. Depending on the time-bound post submission state, and the post-submission cookie state the respondent will not necessarily be presented with a new 'blank' questionnaire as the response_id may resolve to an existing state for a period of time after submission.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't changed the functionality of the Completed by Phone and No longer required rOps users were already able to change those to Not started just that before they were clicking a link that said View
and were then presented with options to change the status - I've also moved the logic to the backend
self.assertIn(b"Not started", data) | ||
|
||
@requests_mock.mock() | ||
def test_not_started_status_is_not_present_for_not_started_without_permission(self, mock_request): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this name difficult to understand, specially with 2 not_started in it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to be more clear
@@ -331,3 +356,43 @@ def test_not_started_status_is_present_for_no_longer_required(self, mock_request | |||
self.assertEqual(response.status_code, 200) | |||
self.assertIn(b"No longer required", data) | |||
self.assertIn(b"Not started", data) | |||
|
|||
@requests_mock.mock() | |||
def test_not_started_status_is_present_for_completed(self, mock_request): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We really should try to move away from this pattern or mass mocking at the highest level as it gets messy. The functionality should in the backend which then means we can unit test the link creation combinations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave this in but I have moved the link creation to the backend with additional tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can, but now you have done the work to test the dict being passed, you don't need to. The idea is you should only need to test the template logic
/deploy scorfs |
Deploying to dev cluster with following parameters:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested these changes and reviewed the code. All looks good to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking better and nice to see some pytest fixtures use. It needs a bit or refinement with function names, structure and reuse.
from flask import url_for | ||
|
||
|
||
def build_case_context( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function name and return, don't really align
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this
|
||
|
||
def build_case_context( | ||
ru_ref, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not fundamental, but I have been trying to do typing for anything new
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added typing where i can
return {"change_response_status": change_response_status} | ||
|
||
|
||
def _build_change_response_status( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite sure why this is split as it is single use, bar the dict assignment I can't see what else the parent function is doing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right I feel like I was on autopilot when writing this - now fixed
): | ||
if has_reporting_unit_permission: | ||
change_response_status = { | ||
"form_action": url_for( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor, but I would probably go with url as the key, just because it aligns with the flask function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated this
|
||
def _generate_radios(allowed_transitions_for_case): | ||
radios = [] | ||
for index, (event, status) in enumerate(allowed_transitions_for_case.items()): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
list comprehension instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swapped list comprehension in
tests/context/conftest.py
Outdated
def multiple_collection_exercises_with_details(): | ||
return [ | ||
{ | ||
"id": "57e06b27-a62b-4d8e-8d57-7b6b80067e4f", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels to me this chunk can be extracted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
tests/context/conftest.py
Outdated
}, | ||
], | ||
"attributes": { | ||
"birthdate": "01/09/1993", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This again feels like it can be extracted and used as a fixture import
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made into a fixture import
tests/context/conftest.py
Outdated
@pytest.fixture | ||
def multiple_cases(): | ||
return { | ||
"state": "ACTIONABLE", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again this looks like the same chunk bar an odd change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this
tests/context/test_case_context.py
Outdated
SURVEY_ID = "02b9c366-7397-42f7-942a-76dc5876d86d" | ||
|
||
|
||
def test_case_context_with_permissions(app, transitions_for_complete_case, expected_case_context_with_all_permissions): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be able to use fixture params here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Swapped for fixture params
@@ -331,3 +356,43 @@ def test_not_started_status_is_present_for_no_longer_required(self, mock_request | |||
self.assertEqual(response.status_code, 200) | |||
self.assertIn(b"No longer required", data) | |||
self.assertIn(b"Not started", data) | |||
|
|||
@requests_mock.mock() | |||
def test_not_started_status_is_present_for_completed(self, mock_request): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can, but now you have done the work to test the dict being passed, you don't need to. The idea is you should only need to test the template logic
# Conflicts: # Pipfile.lock # response_operations_ui/views/reporting_units.py
/deploy scorfs |
Deploying to dev cluster with following parameters:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tested this again and all works as expected. I'm also happy with the recommended code changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looking good, a few minor things, tests need a bit more work regarding naming and fixture use
survey_id: str, | ||
has_reporting_unit_permission: bool, | ||
) -> dict: | ||
if has_reporting_unit_permission: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have kind of doubled up here with the frontend and checked permissions twice ({% if isReportingUnitPermission %} in the template), so else is never reachable. However we shouldn't need to do isReportingUnitPermission in the template, as it's been done in the back end. Depends on what else uses it on how to pass it through, if it's just this then checking change_response_status is not null would work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed this permissions check from the frontend and made it just check for it existing
survey["surveyRef"], | ||
survey["shortName"], | ||
survey["id"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry my fault, I wasn't clear. If it's 1 or 2 elements in a list we should pass the value, more than that pass the list. It's a balance act between passing what it needs and how many vars for the function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh yeah that makes more sense - updated
return {"change_response_status": change_response_status} | ||
|
||
|
||
def _generate_radios(allowed_transitions_for_case: dict): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing the return type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added return type
), | ||
"hyperlink_text": "Change" if ru_permission else "View", | ||
"status_class": ( | ||
CE_STATUS_CLASS[response_status] if response_status in CE_STATUS_CLASS.keys() else "ons-status--error" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A get with default might be better here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to get with a default
for respondent in respondents: | ||
row = {} | ||
if permissions["reporting_unit_edit"]: | ||
if unused_iac: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see it, the logic is a bit unusual as the frontend is also checking off the iac key being passed to it and diverting it around. We should try not to do that as it kind of invalidates any unit tests in that area as there is the same logic both in the frontend and back.
tests/context/conftest.py
Outdated
|
||
|
||
@pytest.fixture() | ||
def collection_exercises_with_details(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has a lot of stuff we don't need. Notice up the top there is a slim line ce_details. The idea being that is the starting point, each fixture below extents depending on what it needs to test. It doesn't need anything that it is not testing, it's not going to the frontend so dates, events, samples etc aren't needed for everything. So if it's build_reporting_units_context your testing, call your fixture appropriately extent the CE base and just make sure the values are populated to test, everything else could be None.
It will reduce the code massively and make it easier to maintain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated and slimmed down
tests/context/conftest.py
Outdated
@pytest.fixture | ||
def transitions_for_complete_case(): | ||
return {"COMPLETED_TO_NOTSTARTED": "Not started"} | ||
|
||
|
||
@pytest.fixture | ||
def transitions_for_completed_by_phone_case(): | ||
return {"COMPLETED_TO_NOTSTARTED": "Not started"} | ||
|
||
|
||
@pytest.fixture | ||
def transitions_for_no_longer_required_case(): | ||
return {"COMPLETED_TO_NOTSTARTED": "Not started"} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why these are all the same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed the repeated ones
tests/context/conftest.py
Outdated
@pytest.fixture | ||
def transitions_for_in_progress_case(): | ||
return { | ||
"COMPLETED_BY_PHONE": "Completed by phone", | ||
"NO_LONGER_REQUIRED": "No longer required", | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def transitions_for_not_started_case(): | ||
return { | ||
"COMPLETED_BY_PHONE": "Completed by phone", | ||
"NO_LONGER_REQUIRED": "No longer required", | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above
tests/context/test_case_context.py
Outdated
from response_operations_ui.contexts.case import build_response_status_context | ||
|
||
|
||
def test_case_context_with_permissions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A number of test names needs a tweak, they need to reflect what we are testing here its not case but the response status
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from response_operations_ui.contexts.case import build_response_status_context | ||
|
||
|
||
def test_response_status_context_with_permissions( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are fine, and one for another time, but pytest.mark.parametrize would of worked well here and saved a lot of repetition, https://docs.pytest.org/en/7.1.x/how-to/parametrize.html#parametrize. You would of had to use something like getfixturevalue in the function for the compare
What and why?
Adds a new case group transition from
complete
tonot started
to allow business users a new status change in rOpsHow to test?
Jira
https://jira.ons.gov.uk/browse/RAS-939