diff --git a/db/queries/statuses/queries.py b/db/queries/statuses/queries.py index aa3b3553..925bce50 100644 --- a/db/queries/statuses/queries.py +++ b/db/queries/statuses/queries.py @@ -7,55 +7,69 @@ from db.queries.form.queries import get_form from external_services import get_round from external_services.data import get_application_sections +from external_services.models.round import FeedbackSurveyConfig -def _is_all_feedback_complete(application_id, fund_id, round_id, language: str): +def _is_all_sections_feedback_complete( + application_id, fund_id, round_id, language: str +): sections = get_application_sections(fund_id, round_id, language) - sections_feedbacks_completed = all( + all_feedback_completed = all( get_feedback(application_id, str(s["id"])) for s in sections if s.get("requires_feedback") ) - end_of_feedback_survey_completed = all( + return all_feedback_completed + + +def _is_feedback_survey_complete(application_id): + is_survey_completed = all( retrieve_end_of_application_survey_data(application_id, pn) for pn in "1234" ) - all_feedback_completed = ( - sections_feedbacks_completed and end_of_feedback_survey_completed - ) - return all_feedback_completed + return is_survey_completed def update_application_status( - application_with_forms: Applications, round_requires_feedback: bool + application_with_forms: Applications, feedback_survey_config: FeedbackSurveyConfig ) -> str: """ Updates the status of the supplied application based on the status of all - forms with that application, and the status of feedback forms (if the round requires feedback) + forms with that application, and the status of all sections feedback and + survey status (if the round requires feedback) Parameters: application_with_forms (`Applications`): Application record to update, with the form jsons populated This object should be within a db context as the function updates it. - round_requires_feedback (`bool`): Whether or not this round needs feedback + feedback_survey_config (`FeedbackSurveyConfig`): feedback_survey_config of the round """ - all_feedback_completed = True - if round_requires_feedback: - all_feedback_completed = _is_all_feedback_complete( - application_with_forms.id, - application_with_forms.fund_id, - application_with_forms.round_id, - application_with_forms.language.name, + all_feedback_and_survey_completed = True + if feedback_survey_config.has_section_feedback: + all_feedback_and_survey_completed = ( + feedback_survey_config.is_section_feedback_optional + or _is_all_sections_feedback_complete( + application_with_forms.id, + application_with_forms.fund_id, + application_with_forms.round_id, + application_with_forms.language.name, + ) + ) + + if feedback_survey_config.has_feedback_survey: + all_feedback_and_survey_completed = all_feedback_and_survey_completed and ( + feedback_survey_config.is_feedback_survey_optional + or _is_feedback_survey_complete(application_with_forms.id) ) form_statuses = [form.status.name for form in application_with_forms.forms] if "IN_PROGRESS" in form_statuses: status = "IN_PROGRESS" elif "COMPLETED" in form_statuses and ( - "NOT_STARTED" in form_statuses or not all_feedback_completed + "NOT_STARTED" in form_statuses or not all_feedback_and_survey_completed ): status = "IN_PROGRESS" - elif "COMPLETED" in form_statuses and all_feedback_completed: + elif "COMPLETED" in form_statuses and all_feedback_and_survey_completed: status = "COMPLETED" elif "SUBMITTED" in form_statuses: status = "SUBMITTED" @@ -231,5 +245,5 @@ def update_statuses( ) db.session.commit() - update_application_status(application, round.requires_feedback) + update_application_status(application, round.feedback_survey_config) db.session.commit() diff --git a/external_services/models/round.py b/external_services/models/round.py index e7d9a9e4..31d637e3 100644 --- a/external_services/models/round.py +++ b/external_services/models/round.py @@ -1,7 +1,27 @@ +import inspect from dataclasses import dataclass from typing import Optional +@dataclass +class FeedbackSurveyConfig: + has_feedback_survey: bool = False + is_feedback_survey_optional: bool = True + has_section_feedback: bool = False + is_section_feedback_optional: bool = True + + @staticmethod + def from_json(d: dict): + # Filter unknown fields from JSON dictionary + return FeedbackSurveyConfig( + **{ + k: v + for k, v in d.items() + if k in inspect.signature(FeedbackSurveyConfig).parameters + } + ) + + @dataclass class Round: id: str @@ -12,9 +32,17 @@ class Round: title: str short_name: str contact_email: str - requires_feedback: bool = False project_name_field_id: Optional[str] = None mark_as_complete_enabled: bool = False + feedback_survey_config: FeedbackSurveyConfig = None + + def __post_init__(self): + if isinstance(self.feedback_survey_config, dict): + self.feedback_survey_config = FeedbackSurveyConfig.from_json( + self.feedback_survey_config + ) + elif self.feedback_survey_config is None: + self.feedback_survey_config = FeedbackSurveyConfig() @staticmethod def from_json(data: dict): @@ -28,6 +56,7 @@ def from_json(data: dict): assessment_deadline=data["assessment_deadline"], project_name_field_id=data.get("project_name_field_id", None), contact_email=data.get("contact_email", None), - requires_feedback=data.get("requires_feedback") or False, + feedback_survey_config=data.get("feedback_survey_config") + or FeedbackSurveyConfig(), mark_as_complete_enabled=data.get("mark_as_complete_enabled") or False, ) diff --git a/tests/test_application_status.py b/tests/test_application_status.py index a4e188ee..b955857d 100644 --- a/tests/test_application_status.py +++ b/tests/test_application_status.py @@ -2,11 +2,13 @@ import pytest from db.queries.statuses.queries import _determine_question_page_status_from_answers -from db.queries.statuses.queries import _is_all_feedback_complete +from db.queries.statuses.queries import _is_all_sections_feedback_complete +from db.queries.statuses.queries import _is_feedback_survey_complete from db.queries.statuses.queries import _is_field_answered from db.queries.statuses.queries import update_application_status from db.queries.statuses.queries import update_form_status from db.queries.statuses.queries import update_question_page_statuses +from external_services.models.round import FeedbackSurveyConfig @pytest.mark.parametrize( @@ -227,13 +229,12 @@ def test_update_form_status( @pytest.mark.parametrize( - "app_sections,feedback_for_sections,end_survey_data,exp_result", + "app_sections,feedback_for_sections,exp_result", [ ( [ [{"requires_feedback": True, "id": 0}], [None], - [None, None, None, None], False, ] ), @@ -244,7 +245,6 @@ def test_update_form_status( {"requires_feedback": True, "id": 1}, ], [None, True], - [None, None, None, None], False, ] ), @@ -255,8 +255,7 @@ def test_update_form_status( {"requires_feedback": True, "id": 1}, ], [True, True], - [True, None, None, None], - False, + True, ] ), ( @@ -266,14 +265,13 @@ def test_update_form_status( {"requires_feedback": True, "id": 1}, ], [True, True], - [True, True, True, True], True, ] ), ], ) -def test_is_all_feedback_complete( - mocker, app_sections, feedback_for_sections, end_survey_data, exp_result +def test_is_all_sections_feedback_complete( + mocker, app_sections, feedback_for_sections, exp_result ): mocker.patch( "db.queries.statuses.queries.get_application_sections", @@ -283,45 +281,209 @@ def test_is_all_feedback_complete( "db.queries.statuses.queries.get_feedback", new=lambda application_id, section_id: feedback_for_sections[int(section_id)], ) + result = _is_all_sections_feedback_complete("123", "123", "123", "en") + assert result == exp_result + + +@pytest.mark.parametrize( + "end_survey_data,exp_result", + [ + ( + [ + [True, None, None, None], + False, + ] + ), + ( + [ + [True, True, True, True], + True, + ] + ), + ], +) +def test_is_feedback_survey_complete(mocker, end_survey_data, exp_result): mocker.patch( "db.queries.statuses.queries.retrieve_end_of_application_survey_data", new=lambda application_id, page_number: end_survey_data[int(page_number) - 1], ) - result = _is_all_feedback_complete("123", "123", "123", "en") + result = _is_feedback_survey_complete("123") assert result == exp_result @pytest.mark.parametrize( - "form_statuses,feedback_complete,round_requires_feedback,exp_status", + "form_statuses,feedback_complete,survey_complete,feedback_survey_config,exp_status", [ - (["NOT_STARTED"], False, False, "NOT_STARTED"), - (["NOT_STARTED"], False, True, "NOT_STARTED"), - (["NOT_STARTED"], True, True, "NOT_STARTED"), - (["NOT_STARTED", "COMPLETED"], False, False, "IN_PROGRESS"), - (["NOT_STARTED", "COMPLETED"], False, True, "IN_PROGRESS"), - (["NOT_STARTED", "COMPLETED"], True, True, "IN_PROGRESS"), - (["COMPLETED", "COMPLETED"], True, True, "COMPLETED"), - (["COMPLETED", "COMPLETED"], False, True, "IN_PROGRESS"), - (["COMPLETED", "COMPLETED"], False, False, "COMPLETED"), - (["SUBMITTED"], True, True, "SUBMITTED"), - (["SUBMITTED"], False, True, "SUBMITTED"), + ( + ["NOT_STARTED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=False, + is_feedback_survey_optional=False, + has_section_feedback=False, + is_section_feedback_optional=False, + ), + "NOT_STARTED", + ), + ( + ["NOT_STARTED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "NOT_STARTED", + ), + ( + ["NOT_STARTED"], + True, + True, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "NOT_STARTED", + ), + ( + ["NOT_STARTED", "COMPLETED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=False, + is_feedback_survey_optional=False, + has_section_feedback=False, + is_section_feedback_optional=False, + ), + "IN_PROGRESS", + ), + ( + ["NOT_STARTED", "COMPLETED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "IN_PROGRESS", + ), + ( + ["NOT_STARTED", "COMPLETED"], + True, + True, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "IN_PROGRESS", + ), + ( + ["COMPLETED", "COMPLETED"], + True, + True, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "COMPLETED", + ), + ( + ["COMPLETED", "COMPLETED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "IN_PROGRESS", + ), + ( + ["COMPLETED", "COMPLETED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=False, + is_feedback_survey_optional=False, + has_section_feedback=False, + is_section_feedback_optional=False, + ), + "COMPLETED", + ), + ( + ["SUBMITTED"], + True, + True, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "SUBMITTED", + ), + ( + ["SUBMITTED"], + False, + False, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "SUBMITTED", + ), + ( + ["COMPLETED"], + True, + True, + FeedbackSurveyConfig( + has_feedback_survey=True, + is_feedback_survey_optional=False, + has_section_feedback=True, + is_section_feedback_optional=False, + ), + "COMPLETED", + ), ], ) def test_update_application_status( - mocker, form_statuses, feedback_complete, round_requires_feedback, exp_status + mocker, + form_statuses, + feedback_complete, + survey_complete, + feedback_survey_config, + exp_status, ): mock_fb = mocker.patch( - "db.queries.statuses.queries._is_all_feedback_complete", + "db.queries.statuses.queries._is_all_sections_feedback_complete", return_value=feedback_complete, ) + mocker.patch( + "db.queries.statuses.queries._is_feedback_survey_complete", + return_value=survey_complete, + ) app_with_forms = MagicMock() app_with_forms.forms = [] for status in form_statuses: form = MagicMock() form.status.name = status app_with_forms.forms.append(form) - update_application_status(app_with_forms, round_requires_feedback) + update_application_status(app_with_forms, feedback_survey_config) assert app_with_forms.status == exp_status - if round_requires_feedback: + if feedback_survey_config.has_section_feedback: mock_fb.assert_called_once()