From cd06aa67de6c484f226b3c6a0670441b111674f8 Mon Sep 17 00:00:00 2001 From: Farmer Paul Date: Tue, 5 Nov 2024 12:08:48 -0500 Subject: [PATCH] fix: Add respondent subject metadata to applet base info (M2-8164, M2-8026) (#1646) * fix: add `respondent_meta` to base info endpoint `respondent_meta` is already generated in two places, and now it's also returned by the `/applets/{applet_id}/base_info` endpoint. So ensure consistent structure, created a static method for generating it in `SubjectsService`. * test: add `respondentMeta` tests --- src/apps/activities/api/activities.py | 2 +- src/apps/activities/tests/test_activities.py | 3 ++- src/apps/applets/api/applets.py | 2 +- src/apps/applets/domain/applet.py | 1 + src/apps/applets/service/applet.py | 4 +++- src/apps/applets/tests/test_applet.py | 23 +++++++++++++++++--- src/apps/subjects/services/subjects.py | 8 +++++++ 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/apps/activities/api/activities.py b/src/apps/activities/api/activities.py index 1a2e807b62a..c8e7495f46e 100644 --- a/src/apps/activities/api/activities.py +++ b/src/apps/activities/api/activities.py @@ -90,7 +90,7 @@ async def applet_activities( activities_future, ) applet_detail = AppletSingleLanguageDetailMobilePublic.from_orm(applet) - respondent_meta = {"nickname": subject.nickname if subject else None, "tag": subject.tag if subject else None} + respondent_meta = SubjectsService.to_respondent_meta(subject) if filters.has_submitted or filters.has_score: activities = await __filter_activities( diff --git a/src/apps/activities/tests/test_activities.py b/src/apps/activities/tests/test_activities.py index 7265c2d351a..ffc6f65e878 100644 --- a/src/apps/activities/tests/test_activities.py +++ b/src/apps/activities/tests/test_activities.py @@ -403,7 +403,8 @@ async def test_activities_applet( assert result["appletDetail"]["activityFlows"] == [] assert result["respondentMeta"] == { - "nickname": f"{tom.first_name} {tom.last_name}", + "subjectId": str(tom_applet_one_subject.id), + "nickname": tom_applet_one_subject.nickname, "tag": tom_applet_one_subject.tag, } diff --git a/src/apps/applets/api/applets.py b/src/apps/applets/api/applets.py index c674b76827e..a1efdc78f50 100644 --- a/src/apps/applets/api/applets.py +++ b/src/apps/applets/api/applets.py @@ -103,7 +103,7 @@ async def applet_retrieve( applet.owner_id = applet_owner.owner_id return AppletRetrieveResponse( result=AppletSingleLanguageDetailPublic.from_orm(applet), - respondent_meta={"nickname": subject.nickname if subject else None, "tag": subject.tag if subject else None}, + respondent_meta=SubjectsService.to_respondent_meta(subject), applet_meta=AppletMeta(has_assessment=has_assessment), ) diff --git a/src/apps/applets/domain/applet.py b/src/apps/applets/domain/applet.py index 76e6d4e9dd7..d3f42552342 100644 --- a/src/apps/applets/domain/applet.py +++ b/src/apps/applets/domain/applet.py @@ -179,3 +179,4 @@ class AppletActivitiesBaseInfo(AppletMinimumInfo, PublicModel): updated_at: datetime.datetime | None activities: list[ActivityBaseInfo] activity_flows: list[FlowBaseInfo] + respondent_meta: dict | None = None diff --git a/src/apps/applets/service/applet.py b/src/apps/applets/service/applet.py index 5996b0d1a8f..6b6bb85ad37 100644 --- a/src/apps/applets/service/applet.py +++ b/src/apps/applets/service/applet.py @@ -766,9 +766,11 @@ async def _get_info_by_id(self, schema: AppletSchema, language: str) -> AppletAc ) activities = ActivityService(self.session, self.user_id).get_info_by_applet_id(schema.id, language) activity_flows = FlowService(self.session).get_info_by_applet_id(schema.id, language) - futures = await asyncio.gather(activities, activity_flows) + subject = SubjectsService(self.session, self.user_id).get_by_user_and_applet(self.user_id, schema.id) + futures = await asyncio.gather(activities, activity_flows, subject) applet.activities = futures[0] applet.activity_flows = futures[1] + applet.respondent_meta = SubjectsService.to_respondent_meta(futures[2]) return applet async def has_assessment(self, applet_id: uuid.UUID) -> bool: diff --git a/src/apps/applets/tests/test_applet.py b/src/apps/applets/tests/test_applet.py index 1d1e5befdca..03bffe47701 100644 --- a/src/apps/applets/tests/test_applet.py +++ b/src/apps/applets/tests/test_applet.py @@ -30,6 +30,7 @@ from apps.applets.service.applet import AppletService from apps.shared.exception import NotFoundError from apps.shared.test.client import TestClient +from apps.subjects.domain import Subject from apps.users.domain import User from apps.workspaces.domain.constants import Role from apps.workspaces.errors import AppletCreationAccessDenied, AppletEncryptionUpdateDenied @@ -468,16 +469,27 @@ async def test_applet_list_with_limit(self, client: TestClient, tom: User, apple assert len(response.json()["result"]) == 1 assert response.json()["result"][0]["id"] == str(applet_one.id) - async def test_applet_detail(self, client: TestClient, tom: User, applet_one_with_flow: AppletFull): + async def test_applet_detail( + self, + client: TestClient, + tom: User, + applet_one_with_flow: AppletFull, + tom_applet_one_subject: Subject, + ): client.login(tom) response = await client.get(self.applet_detail_url.format(pk=applet_one_with_flow.id)) assert response.status_code == http.HTTPStatus.OK - result = response.json()["result"] + responseJson = response.json() + result = responseJson["result"] assert result["displayName"] == applet_one_with_flow.display_name assert result["ownerId"] == str(tom.id) assert len(result["activities"]) == 1 assert len(result["activityFlows"]) == 1 - assert response.json()["respondentMeta"]["nickname"] == tom.get_full_name() + assert responseJson["respondentMeta"] == { + "subjectId": str(tom_applet_one_subject.id), + "nickname": tom_applet_one_subject.nickname, + "tag": tom_applet_one_subject.tag, + } async def test_public_applet_detail(self, client: TestClient, applet_one_with_public_link: AppletFull): response = await client.get(self.public_applet_detail_url.format(key=applet_one_with_public_link.link)) @@ -637,6 +649,11 @@ async def test_get_applet_activities_info( assert ResponseType.MULTISELECT not in response.json()["result"]["activities"][0]["containsResponseTypes"] assert response.json()["result"]["activities"][0]["itemCount"] == 1 + respondentMeta = response.json()["result"]["respondentMeta"] + assert respondentMeta["subjectId"] is not None + assert respondentMeta["nickname"] == tom.get_full_name() + assert respondentMeta["tag"] == "Team" + async def test_get_public_applet_activities_info( self, client: TestClient, diff --git a/src/apps/subjects/services/subjects.py b/src/apps/subjects/services/subjects.py index b46cd6f4a9a..a82236ad95e 100644 --- a/src/apps/subjects/services/subjects.py +++ b/src/apps/subjects/services/subjects.py @@ -35,6 +35,14 @@ def __to_db_model(schema: SubjectCreate): tag=schema.tag, ) + @staticmethod + def to_respondent_meta(subject: Subject | None): + return { + "subjectId": subject.id if subject else None, + "nickname": subject.nickname if subject else None, + "tag": subject.tag if subject else None, + } + async def create(self, schema: SubjectCreate) -> Subject: subject_with_secret = await self.get_by_secret_id(schema.applet_id, schema.secret_user_id) if subject_with_secret and not subject_with_secret.is_deleted: