diff --git a/src/apps/activity_flows/crud/flow_history.py b/src/apps/activity_flows/crud/flow_history.py index 647e97852c7..6661ce44d5a 100644 --- a/src/apps/activity_flows/crud/flow_history.py +++ b/src/apps/activity_flows/crud/flow_history.py @@ -112,3 +112,11 @@ async def get_versions_data(self, flow_id: uuid.UUID) -> list[Version]: data = result.all() return parse_obj_as(list[Version], data) + + async def get_by_applet_id_version(self, applet_id_version: str) -> list[ActivityFlowHistoriesSchema]: + query: Query = select(ActivityFlowHistoriesSchema) + query = query.where(ActivityFlowHistoriesSchema.applet_id == applet_id_version) + query = query.join(AppletHistorySchema, AppletHistorySchema.id_version == ActivityFlowHistoriesSchema.applet_id) + query = query.order_by(AppletHistorySchema.created_at.desc()) + result = await self._execute(query) + return result.scalars().all() diff --git a/src/apps/answers/service.py b/src/apps/answers/service.py index 9f6d86a1c87..ec678bee2d2 100644 --- a/src/apps/answers/service.py +++ b/src/apps/answers/service.py @@ -71,7 +71,7 @@ ) from apps.answers.filters import AppletSubmitDateFilter, ReviewAppletItemFilter, SummaryActivityFilter from apps.answers.tasks import create_report -from apps.applets.crud import AppletsCRUD +from apps.applets.crud import AppletHistoriesCRUD, AppletsCRUD from apps.applets.domain.applet_history import Version from apps.applets.domain.base import Encryption from apps.applets.service import AppletHistoryService @@ -1030,6 +1030,7 @@ async def get_summary_activities( self, applet_id: uuid.UUID, filters: SummaryActivityFilter ) -> list[SummaryActivity]: assert self.user_id + current_applet_version = await AppletHistoriesCRUD(self.session).get_current_versions_by_applet_id(applet_id) act_hst_crud = ActivityHistoriesCRUD(self.session) activities = await act_hst_crud.get_last_histories_by_applet(applet_id=applet_id) activity_ver_ids = [activity.id_version for activity in activities] @@ -1043,11 +1044,21 @@ async def get_summary_activities( submitted_activities[activity_id] = max(submit_date, date) if date else submit_date submitted_activities[activity_history_id] = submit_date + current_activity_histories = await act_hst_crud.retrieve_by_applet_version(current_applet_version) + current_activities_map = {str(ah.id): ah for ah in current_activity_histories} results = [] for activity in activities: activity_history_answer_date = submitted_activities.get( activity.id_version, submitted_activities.get(str(activity.id)) ) + has_answer = bool(activity_history_answer_date) + if not has_answer: + activity_curr = current_activities_map.get(str(activity.id)) + if not activity_curr: + continue + elif activity_curr.is_reviewable: + continue + results.append( SummaryActivity( id=activity.id, @@ -1063,6 +1074,7 @@ async def get_summary_activity_flows( self, applet_id: uuid.UUID, target_subject_id: uuid.UUID | None ) -> list[SummaryActivityFlow]: assert self.user_id + current_applet_version = await AppletHistoriesCRUD(self.session).get_current_versions_by_applet_id(applet_id) flow_crud = FlowsHistoryCRUD(self.session) answer_crud = AnswersCRUD(self.answer_session) flow_history_ids_with_date = await answer_crud.get_submitted_flows_with_last_date(applet_id, target_subject_id) @@ -1075,11 +1087,17 @@ async def get_summary_activity_flows( submitted_activity_flows[flow_id] = max(submit_date, date) if date else submit_date submitted_activity_flows[version_id] = submit_date + flow_histories = await flow_crud.get_by_applet_id_version(current_applet_version) + flow_histories_curr = [flow_h.id for flow_h in flow_histories] results = [] for flow_history in activity_flow_histories: flow_history_answer_date = submitted_activity_flows.get( flow_history.id_version, submitted_activity_flows.get(str(flow_history.id)) ) + has_answer = bool(flow_history_answer_date) + if not has_answer and flow_history.id not in flow_histories_curr: + continue + results.append( SummaryActivityFlow( id=flow_history.id, diff --git a/src/apps/answers/tests/conftest.py b/src/apps/answers/tests/conftest.py index 5bc0e9e5111..bf1a1a72c76 100644 --- a/src/apps/answers/tests/conftest.py +++ b/src/apps/answers/tests/conftest.py @@ -474,3 +474,67 @@ async def editor_user_reviewer_applet_one(user: UserSchema, session: AsyncSessio applet_id = uuid.UUID("92917a56-d586-4613-b7aa-991f2c4b15b1") srv = UserAppletAccessService(session, user.id, applet_id) await srv.add_role(user.id, Role.EDITOR) + + +@pytest.fixture +async def applet__activity_turned_into_assessment( + session: AsyncSession, tom: User, applet_data: AppletCreate +) -> AppletFull: + """ + Applet with one Activity0 updated into applet with Activity0(is_reviewable=True) and Activity1 + All activities has no answers + """ + srv = AppletService(session, tom.id) + applet = await srv.create(applet_data) + data = AppletUpdate(**applet.dict()) + + activity_new = data.activities[0].copy(deep=True) + activity_new.id = uuid.uuid4() + activity_new.key = uuid.uuid4() + for i in range(len(activity_new.items)): + activity_new.items[i].id = uuid.uuid4() + activity_new.name = "New activity" + data.activities.append(activity_new) + + data.activities[0].is_reviewable = True + data.activities[0].name = "Reviewer assessment" + updated_applet = await srv.update(applet.id, data) + return updated_applet + + +@pytest.fixture +async def applet__deleted_activity_without_answers( + session: AsyncSession, tom: User, applet_data: AppletCreate +) -> AppletFull: + """ + Applet with one Activity0 updated into applet with Activity1 and deleted Activity1 + All activities has no answers + """ + srv = AppletService(session, tom.id) + applet = await srv.create(applet_data) + data = AppletUpdate(**applet.dict()) + + activity_new = data.activities[0].copy(deep=True) + activity_new.id = uuid.uuid4() + activity_new.key = uuid.uuid4() + for i in range(len(activity_new.items)): + activity_new.items[i].id = uuid.uuid4() + activity_new.name = "New activity" + data.activities = [activity_new] + updated_applet = await srv.update(applet.id, data) + return updated_applet + + +@pytest.fixture +async def applet__deleted_flow_without_answers( + session: AsyncSession, tom: User, applet_with_flow: AppletFull +) -> AppletFull: + srv = AppletService(session, tom.id) + data = applet_with_flow.dict() + activity_flow = data["activity_flows"][0] + for i in range(len(activity_flow["items"])): + activity_flow["items"][i]["activity_key"] = data["activities"][0]["key"] + data["activity_flows"] = [activity_flow] + update_data = AppletUpdate(**data) + updated_applet = await srv.update(applet_with_flow.id, update_data) + return updated_applet diff --git a/src/apps/answers/tests/test_answers.py b/src/apps/answers/tests/test_answers.py index aca099ee7ab..f8c13ce026c 100644 --- a/src/apps/answers/tests/test_answers.py +++ b/src/apps/answers/tests/test_answers.py @@ -1888,3 +1888,61 @@ async def test_access_to_activity_list( client.login(user) response = await client.get(url) assert response.status_code == expected + + async def test_get_summary_activities_no_answer_no_empty_deleted_history( + self, client: TestClient, tom: User, applet: AppletFull + ): + client.login(tom) + + response = await client.get( + self.summary_activities_url.format( + applet_id=str(applet.id), + ) + ) + + assert response.status_code == http.HTTPStatus.OK + assert response.json()["count"] == 1 + assert response.json()["result"][0]["name"] == applet.activities[0].name + assert response.json()["result"][0]["id"] == str(applet.activities[0].id) + assert not response.json()["result"][0]["isPerformanceTask"] + assert not response.json()["result"][0]["hasAnswer"] + + async def test_activity_turned_into_assessment_not_included_in_list( + self, client: TestClient, tom: User, applet__activity_turned_into_assessment: AppletFull + ): + client.login(tom) + response = await client.get( + self.summary_activities_url.format( + applet_id=str(applet__activity_turned_into_assessment.id), + ) + ) + + assert response.status_code == http.HTTPStatus.OK + assert response.json()["count"] == 1 + assert response.json()["result"][0]["id"] == str(applet__activity_turned_into_assessment.activities[1].id) + + async def test_deleted_activity_without_answers_not_included_in_list( + self, client: TestClient, tom: User, applet__deleted_activity_without_answers: AppletFull + ): + client.login(tom) + response = await client.get( + self.summary_activities_url.format( + applet_id=str(applet__deleted_activity_without_answers.id), + ) + ) + + assert response.status_code == http.HTTPStatus.OK + assert response.json()["count"] == 1 + assert response.json()["result"][0]["id"] == str(applet__deleted_activity_without_answers.activities[0].id) + + async def test_deleted_flow_not_included_in_submission_list( + self, client: TestClient, tom: User, applet__deleted_flow_without_answers: AppletFull + ): + client.login(tom) + url = self.summary_activity_flows_url.format(applet_id=applet__deleted_flow_without_answers.id) + response = await client.get(url) + assert response.status_code == 200 + payload = response.json() + assert applet__deleted_flow_without_answers.activity_flows[0].id + assert payload["count"] == 1 + assert payload["result"][0]["id"] == str(applet__deleted_flow_without_answers.activity_flows[0].id) diff --git a/src/apps/applets/crud/applets_history.py b/src/apps/applets/crud/applets_history.py index e208c19625b..702eb71515a 100644 --- a/src/apps/applets/crud/applets_history.py +++ b/src/apps/applets/crud/applets_history.py @@ -77,3 +77,11 @@ async def set_report_configuration( query = query.values(**schema.dict(by_alias=False)) await self._execute(query) + + async def get_current_versions_by_applet_id(self, applet_id: uuid.UUID) -> str: + query: Query = select(AppletHistorySchema.id_version) + query = query.where(AppletHistorySchema.id == applet_id) + query = query.order_by(AppletHistorySchema.created_at.desc()) + query = query.limit(1) + result = await self._execute(query) + return result.scalars().first()