From b67ae3d5fd45f55044af06972e7a8008b4bada76 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Thu, 8 Feb 2024 15:24:13 +0000 Subject: [PATCH 01/12] Update view and view test --- api/cases/tests/test_case_ecju_queries.py | 23 ++++++++++++ api/cases/views/views.py | 45 ++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index d0815d47a9..bbe3b89abf 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -675,3 +675,26 @@ def test_exporter_cannot_delete_documents_of_closed_query(self): self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json() self.assertIsNotNone(response["document"]["id"]) + + @parameterized.expand(["this is some response text", ""]) + def test_exporter_responding_to_query_creates_case_note_mention_for_caseworker(self, response_text): + case = self.create_standard_application_case(self.organisation) + url = reverse("cases:case_ecju_queries", kwargs={"pk": case.id}) + question_text = "this is the question text" + data = {"question": question_text, "query_type": ECJUQueryType.ECJU} + + response = self.client.post(url, data, **self.gov_headers) + response_data = response.json() + ecju_query = EcjuQuery.objects.get(case=case) + + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + self.assertEqual(response_data["ecju_query_id"], str(ecju_query.id)) + self.assertEqual(question_text, ecju_query.question) + self.assertIsNone(ecju_query.response) + + url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) + data = {"response": response_text} + response = self.client.put(url, data, **self.exporter_headers) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(list(response.json().keys()), ["ecju_query", "case_note", "case_note_mentions"]) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 48803d4dd8..3a7d16b049 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -74,6 +74,8 @@ EcjuQueryUserResponseSerializer, EcjuQueryDocumentCreateSerializer, EcjuQueryDocumentViewSerializer, + CaseNoteSerializer, + CaseNoteMentionsSerializer, ) from api.cases.service import get_destinations from api.compliance.helpers import generate_compliance_site_case @@ -101,6 +103,8 @@ from api.users.libraries.get_user import get_user_by_pk from lite_content.lite_api import strings from lite_content.lite_api.strings import Documents, Cases +from api.users.enums import SystemUser +from api.users.models import BaseUser class CaseDetail(APIView): @@ -649,7 +653,46 @@ def put(self, request, pk, ecju_pk): target=serializer.instance.case, payload={"ecju_response": data.get("response")}, ) - return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_201_CREATED) + + # Create case note mention notification for case worker. + # LITE system is the user that creates the case note. + exporter_user_full_name = getattr(get_user_by_pk(request.user.pk), "full_name", "Exporter user") + case_note_data = { + "text": f"{exporter_user_full_name} has responded to a query.", + "case": serializer.instance.case.id, + "user": SystemUser.id, + } + case_note_serializer = CaseNoteSerializer(data=case_note_data) + if case_note_serializer.is_valid(): + case_note_serializer.save() + case_note_mentions_data = [ + {"user": ecju_query.raised_by_user.pk, "case_note": case_note_serializer.instance.id} + ] + case_note_mentions_serializer = CaseNoteMentionsSerializer( + data=case_note_mentions_data, + many=True, + ) + if case_note_mentions_serializer.is_valid(): + case_note_mentions_serializer.save() + audit_trail_service.create_system_user_audit( + verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, target=serializer.instance.case, payload={} + ) + return JsonResponse( + data={ + "ecju_query": serializer.data, + "case_note": case_note_serializer.data, + "case_note_mentions": case_note_mentions_serializer.data, + }, + status=status.HTTP_201_CREATED, + ) + else: + return JsonResponse( + data={"errors": case_note_mentions_serializer.errors}, status=status.HTTP_400_BAD_REQUEST + ) + else: + return JsonResponse( + data={"errors": case_note_serializer.errors}, status=status.HTTP_400_BAD_REQUEST + ) else: return JsonResponse(data={}, status=status.HTTP_200_OK) From 8fe625e3e2226c338416c8410eb0be92c2bc02c3 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Thu, 8 Feb 2024 16:53:59 +0000 Subject: [PATCH 02/12] Refactor mentions code into separate function --- api/cases/views/views.py | 79 +++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 3a7d16b049..6d3634ff63 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -104,7 +104,6 @@ from lite_content.lite_api import strings from lite_content.lite_api.strings import Documents, Cases from api.users.enums import SystemUser -from api.users.models import BaseUser class CaseDetail(APIView): @@ -656,48 +655,52 @@ def put(self, request, pk, ecju_pk): # Create case note mention notification for case worker. # LITE system is the user that creates the case note. - exporter_user_full_name = getattr(get_user_by_pk(request.user.pk), "full_name", "Exporter user") - case_note_data = { - "text": f"{exporter_user_full_name} has responded to a query.", - "case": serializer.instance.case.id, - "user": SystemUser.id, - } - case_note_serializer = CaseNoteSerializer(data=case_note_data) - if case_note_serializer.is_valid(): - case_note_serializer.save() - case_note_mentions_data = [ - {"user": ecju_query.raised_by_user.pk, "case_note": case_note_serializer.instance.id} - ] - case_note_mentions_serializer = CaseNoteMentionsSerializer( - data=case_note_mentions_data, - many=True, - ) - if case_note_mentions_serializer.is_valid(): - case_note_mentions_serializer.save() - audit_trail_service.create_system_user_audit( - verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, target=serializer.instance.case, payload={} - ) - return JsonResponse( - data={ - "ecju_query": serializer.data, - "case_note": case_note_serializer.data, - "case_note_mentions": case_note_mentions_serializer.data, - }, - status=status.HTTP_201_CREATED, - ) - else: - return JsonResponse( - data={"errors": case_note_mentions_serializer.errors}, status=status.HTTP_400_BAD_REQUEST - ) - else: - return JsonResponse( - data={"errors": case_note_serializer.errors}, status=status.HTTP_400_BAD_REQUEST - ) + mentions_data = self._create_case_note_mention(request, ecju_query, serializer) + if "errors" in mentions_data.keys(): + return JsonResponse(data=mentions_data) + + return JsonResponse( + data={"ecju_query": serializer.data, **mentions_data}, + status=status.HTTP_201_CREATED, + ) else: return JsonResponse(data={}, status=status.HTTP_200_OK) return JsonResponse(data={"errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) + def _create_case_note_mention(self, request, ecju_query, ecju_query_serializer): + exporter_user_full_name = getattr(get_user_by_pk(request.user.pk), "full_name", "Exporter user") + case_note_data = { + "text": f"{exporter_user_full_name} has responded to a query.", + "case": ecju_query_serializer.instance.case.id, + "user": SystemUser.id, + } + case_note_serializer = CaseNoteSerializer(data=case_note_data) + if case_note_serializer.is_valid(): + case_note_serializer.save() + case_note_mentions_data = [ + {"user": ecju_query.raised_by_user.pk, "case_note": case_note_serializer.instance.id} + ] + case_note_mentions_serializer = CaseNoteMentionsSerializer( + data=case_note_mentions_data, + many=True, + ) + if case_note_mentions_serializer.is_valid(): + case_note_mentions_serializer.save() + audit_trail_service.create_system_user_audit( + verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, + target=ecju_query_serializer.instance.case, + payload={}, + ) + return { + "case_note": case_note_serializer.data, + "case_note_mentions": case_note_mentions_serializer.data, + } + else: + return {"errors": case_note_mentions_serializer.errors} + else: + return {"errors": case_note_serializer.errors} + class EcjuQueryAddDocument(APIView): authentication_classes = (ExporterAuthentication,) From e4ac6d37331e7732007138e497d28efe7907a905 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Thu, 8 Feb 2024 17:19:53 +0000 Subject: [PATCH 03/12] Fix audit message --- api/cases/views/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 6d3634ff63..964e4caa94 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -689,8 +689,9 @@ def _create_case_note_mention(self, request, ecju_query, ecju_query_serializer): case_note_mentions_serializer.save() audit_trail_service.create_system_user_audit( verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, + action_object=case_note_serializer.instance, target=ecju_query_serializer.instance.case, - payload={}, + payload={"mention_users": case_note_mentions_serializer.get_user_mention_names()}, ) return { "case_note": case_note_serializer.data, From 24839dd5689f0062d2b9c37036a3a2c7eb9cd48c Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Thu, 8 Feb 2024 17:52:32 +0000 Subject: [PATCH 04/12] Fix 400 error response --- api/cases/views/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 964e4caa94..29e67170f1 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -657,7 +657,7 @@ def put(self, request, pk, ecju_pk): # LITE system is the user that creates the case note. mentions_data = self._create_case_note_mention(request, ecju_query, serializer) if "errors" in mentions_data.keys(): - return JsonResponse(data=mentions_data) + return JsonResponse(data=mentions_data, status=status.HTTP_400_BAD_REQUEST) return JsonResponse( data={"ecju_query": serializer.data, **mentions_data}, From 11ec0ade429e385597e0427094af984e66801fee Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Fri, 9 Feb 2024 12:21:04 +0000 Subject: [PATCH 05/12] Rewrite to create mention using case model --- api/cases/models.py | 20 +++++++++- api/cases/tests/test_case_ecju_queries.py | 26 ++++++++++++- api/cases/views/views.py | 47 ++++------------------- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/api/cases/models.py b/api/cases/models.py index d54dc7b9c2..5598a5c247 100644 --- a/api/cases/models.py +++ b/api/cases/models.py @@ -14,7 +14,6 @@ from queryable_properties.managers import QueryablePropertiesManager from queryable_properties.properties import queryable_property - from api.audit_trail.enums import AuditType from api.cases.enums import ( AdviceType, @@ -53,6 +52,7 @@ UserOrganisationRelationship, ExporterNotification, ) +from api.users.enums import SystemUser from lite_content.lite_api import strings denial_reasons_logger = logging.getLogger(settings.DENIAL_REASONS_DELETION_LOGGER) @@ -313,6 +313,24 @@ def set_sub_status(self, sub_status_id): payload={"sub_status": self.sub_status.name, "status": CaseStatusEnum.get_text(self.status.status)}, ) + def create_system_mention(self, case_note_text, mention_user): + """ + Create a LITE system mention e.g. exporter responded to an ECJU query + """ + from api.audit_trail import service as audit_trail_service + + case_note = CaseNote(text=case_note_text, case=self, user=BaseUser.objects.get(id=SystemUser.id)) + case_note.save() + case_note_mentions = CaseNoteMentions(user=mention_user, case_note=case_note) + case_note_mentions.save() + audit_payload = { + "mention_users": [f"{mention_user.full_name} ({mention_user.team.name})"], + "additional_text": case_note_text, + } + audit_trail_service.create_system_user_audit( + verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, action_object=case_note, target=self, payload=audit_payload + ) + class CaseQueue(TimestampableModel): case = models.ForeignKey(Case, related_name="casequeues", on_delete=models.DO_NOTHING) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index bbe3b89abf..e6a3012634 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -23,6 +23,7 @@ from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status from test_helpers.clients import DataTestClient from api.users.tests.factories import ExporterUserFactory +from api.cases.models import CaseNoteMentions faker = Faker() @@ -679,6 +680,8 @@ def test_exporter_cannot_delete_documents_of_closed_query(self): @parameterized.expand(["this is some response text", ""]) def test_exporter_responding_to_query_creates_case_note_mention_for_caseworker(self, response_text): case = self.create_standard_application_case(self.organisation) + + # caseworker raises a query url = reverse("cases:case_ecju_queries", kwargs={"pk": case.id}) question_text = "this is the question text" data = {"question": question_text, "query_type": ECJUQueryType.ECJU} @@ -687,14 +690,35 @@ def test_exporter_responding_to_query_creates_case_note_mention_for_caseworker(s response_data = response.json() ecju_query = EcjuQuery.objects.get(case=case) + self.assertFalse(ecju_query.is_query_closed) self.assertEqual(status.HTTP_201_CREATED, response.status_code) self.assertEqual(response_data["ecju_query_id"], str(ecju_query.id)) self.assertEqual(question_text, ecju_query.question) self.assertIsNone(ecju_query.response) + # exporter responds to the query url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) data = {"response": response_text} + response = self.client.put(url, data, **self.exporter_headers) + ecju_query = EcjuQuery.objects.get(case=case) self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertEqual(list(response.json().keys()), ["ecju_query", "case_note", "case_note_mentions"]) + self.assertTrue(ecju_query.is_query_closed) + + # check case note mention is created + case_note_mentions = CaseNoteMentions.objects.first() + case_note = case_note_mentions.case_note + audit_object = Audit.objects.first() + + expected_gov_user = ecju_query.raised_by_user + expected_exporter_user = ecju_query.responded_by_user + expected_mention_users_text = f"{expected_gov_user.full_name} ({expected_gov_user.team.name})" + expected_case_note_text = f"{expected_exporter_user.get_full_name()} has responded to a query." + expected_audit_payload = ( + {"mention_users": [expected_mention_users_text], "additional_text": expected_case_note_text}, + ) + + self.assertEqual(case_note_mentions.user, expected_gov_user) + self.assertEqual(case_note.text, expected_case_note_text) + self.assertEqual(audit_object.payload, expected_audit_payload) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 29e67170f1..c7b44d4407 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -629,6 +629,8 @@ def put(self, request, pk, ecju_pk): data={"errors": "Enter a reason why you are closing the query"}, status=status.HTTP_400_BAD_REQUEST ) + exporter_user_full_name = getattr(get_user_by_pk(request.user.pk), "full_name", "Exporter user") + data = {"responded_by_user": str(request.user.pk)} if request.data.get("response"): @@ -653,14 +655,13 @@ def put(self, request, pk, ecju_pk): payload={"ecju_response": data.get("response")}, ) - # Create case note mention notification for case worker. - # LITE system is the user that creates the case note. - mentions_data = self._create_case_note_mention(request, ecju_query, serializer) - if "errors" in mentions_data.keys(): - return JsonResponse(data=mentions_data, status=status.HTTP_400_BAD_REQUEST) + ecju_query.case.create_system_mention( + case_note_text=f"{exporter_user_full_name} has responded to a query.", + mention_user=ecju_query.raised_by_user, + ) return JsonResponse( - data={"ecju_query": serializer.data, **mentions_data}, + data={"ecju_query": serializer.data}, status=status.HTTP_201_CREATED, ) else: @@ -668,40 +669,6 @@ def put(self, request, pk, ecju_pk): return JsonResponse(data={"errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) - def _create_case_note_mention(self, request, ecju_query, ecju_query_serializer): - exporter_user_full_name = getattr(get_user_by_pk(request.user.pk), "full_name", "Exporter user") - case_note_data = { - "text": f"{exporter_user_full_name} has responded to a query.", - "case": ecju_query_serializer.instance.case.id, - "user": SystemUser.id, - } - case_note_serializer = CaseNoteSerializer(data=case_note_data) - if case_note_serializer.is_valid(): - case_note_serializer.save() - case_note_mentions_data = [ - {"user": ecju_query.raised_by_user.pk, "case_note": case_note_serializer.instance.id} - ] - case_note_mentions_serializer = CaseNoteMentionsSerializer( - data=case_note_mentions_data, - many=True, - ) - if case_note_mentions_serializer.is_valid(): - case_note_mentions_serializer.save() - audit_trail_service.create_system_user_audit( - verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, - action_object=case_note_serializer.instance, - target=ecju_query_serializer.instance.case, - payload={"mention_users": case_note_mentions_serializer.get_user_mention_names()}, - ) - return { - "case_note": case_note_serializer.data, - "case_note_mentions": case_note_mentions_serializer.data, - } - else: - return {"errors": case_note_mentions_serializer.errors} - else: - return {"errors": case_note_serializer.errors} - class EcjuQueryAddDocument(APIView): authentication_classes = (ExporterAuthentication,) From 448abe45e31e8195e5f1f894e13a3b94da3850d1 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Fri, 9 Feb 2024 13:54:55 +0000 Subject: [PATCH 06/12] Remove unused imports --- api/cases/views/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index c7b44d4407..1bf64cabe6 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -74,8 +74,6 @@ EcjuQueryUserResponseSerializer, EcjuQueryDocumentCreateSerializer, EcjuQueryDocumentViewSerializer, - CaseNoteSerializer, - CaseNoteMentionsSerializer, ) from api.cases.service import get_destinations from api.compliance.helpers import generate_compliance_site_case @@ -103,7 +101,6 @@ from api.users.libraries.get_user import get_user_by_pk from lite_content.lite_api import strings from lite_content.lite_api.strings import Documents, Cases -from api.users.enums import SystemUser class CaseDetail(APIView): From c63bd504b6e32518ab897f10ec608e22cdc9045f Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Fri, 9 Feb 2024 14:11:07 +0000 Subject: [PATCH 07/12] Fix test --- api/cases/tests/test_case_ecju_queries.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index e6a3012634..10e400b361 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -715,9 +715,10 @@ def test_exporter_responding_to_query_creates_case_note_mention_for_caseworker(s expected_exporter_user = ecju_query.responded_by_user expected_mention_users_text = f"{expected_gov_user.full_name} ({expected_gov_user.team.name})" expected_case_note_text = f"{expected_exporter_user.get_full_name()} has responded to a query." - expected_audit_payload = ( - {"mention_users": [expected_mention_users_text], "additional_text": expected_case_note_text}, - ) + expected_audit_payload = { + "mention_users": [expected_mention_users_text], + "additional_text": expected_case_note_text, + } self.assertEqual(case_note_mentions.user, expected_gov_user) self.assertEqual(case_note.text, expected_case_note_text) From a93a97c29744545af9619403f7d5e1b6d6ea0ae4 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Mon, 12 Feb 2024 11:33:16 +0000 Subject: [PATCH 08/12] Refactor and move function to helpers --- api/cases/helpers.py | 25 ++++++++++++++++++++++++- api/cases/models.py | 19 ------------------- api/cases/views/views.py | 4 +++- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/api/cases/helpers.py b/api/cases/helpers.py index 3bfeb801d5..f82b39e03e 100644 --- a/api/cases/helpers.py +++ b/api/cases/helpers.py @@ -1,9 +1,11 @@ from datetime import timedelta +from api.audit_trail.enums import AuditType from api.common.dates import is_bank_holiday, is_weekend from api.cases.enums import CaseTypeReferenceEnum from api.staticdata.statuses.enums import CaseStatusEnum -from api.users.models import GovUser, GovNotification +from api.users.models import BaseUser, GovUser, GovNotification +from api.users.enums import SystemUser def get_assigned_to_user_case_ids(user: GovUser, queue_id=None): @@ -81,3 +83,24 @@ def can_set_status(case, status): def working_days_in_range(start_date, end_date): dates_in_range = [start_date + timedelta(n) for n in range((end_date - start_date).days)] return len([date for date in dates_in_range if (not is_bank_holiday(date) and not is_weekend(date))]) + + +def create_system_mention(case, case_note_text, mention_user): + """ + Create a LITE system mention e.g. exporter responded to an ECJU query + """ + # to avoid circular import ImportError these must be imported here + from api.cases.models import CaseNote, CaseNoteMentions + from api.audit_trail import service as audit_trail_service + + case_note = CaseNote(text=case_note_text, case=case, user=BaseUser.objects.get(id=SystemUser.id)) + case_note.save() + case_note_mentions = CaseNoteMentions(user=mention_user, case_note=case_note) + case_note_mentions.save() + audit_payload = { + "mention_users": [f"{mention_user.full_name} ({mention_user.team.name})"], + "additional_text": case_note_text, + } + audit_trail_service.create_system_user_audit( + verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, action_object=case_note, target=case, payload=audit_payload + ) diff --git a/api/cases/models.py b/api/cases/models.py index 5598a5c247..529651a457 100644 --- a/api/cases/models.py +++ b/api/cases/models.py @@ -52,7 +52,6 @@ UserOrganisationRelationship, ExporterNotification, ) -from api.users.enums import SystemUser from lite_content.lite_api import strings denial_reasons_logger = logging.getLogger(settings.DENIAL_REASONS_DELETION_LOGGER) @@ -313,24 +312,6 @@ def set_sub_status(self, sub_status_id): payload={"sub_status": self.sub_status.name, "status": CaseStatusEnum.get_text(self.status.status)}, ) - def create_system_mention(self, case_note_text, mention_user): - """ - Create a LITE system mention e.g. exporter responded to an ECJU query - """ - from api.audit_trail import service as audit_trail_service - - case_note = CaseNote(text=case_note_text, case=self, user=BaseUser.objects.get(id=SystemUser.id)) - case_note.save() - case_note_mentions = CaseNoteMentions(user=mention_user, case_note=case_note) - case_note_mentions.save() - audit_payload = { - "mention_users": [f"{mention_user.full_name} ({mention_user.team.name})"], - "additional_text": case_note_text, - } - audit_trail_service.create_system_user_audit( - verb=AuditType.CREATED_CASE_NOTE_WITH_MENTIONS, action_object=case_note, target=self, payload=audit_payload - ) - class CaseQueue(TimestampableModel): case = models.ForeignKey(Case, related_name="casequeues", on_delete=models.DO_NOTHING) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 1bf64cabe6..afcdd626df 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -27,6 +27,7 @@ ) from api.cases.generated_documents.models import GeneratedCaseDocument from api.cases.generated_documents.serializers import AdviceDocumentGovSerializer +from api.cases.helpers import create_system_mention from api.cases.libraries.advice import group_advice from api.cases.libraries.finalise import get_required_decision_document_types from api.cases.libraries.get_case import get_case, get_case_document @@ -652,7 +653,8 @@ def put(self, request, pk, ecju_pk): payload={"ecju_response": data.get("response")}, ) - ecju_query.case.create_system_mention( + create_system_mention( + case=ecju_query.case, case_note_text=f"{exporter_user_full_name} has responded to a query.", mention_user=ecju_query.raised_by_user, ) From 093b06d9fe54dbc95971cbf92fb8923aa4e51812 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Mon, 12 Feb 2024 14:33:24 +0000 Subject: [PATCH 09/12] Rewrite to raise error if exporter user not found --- api/cases/views/views.py | 69 ++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index afcdd626df..81301ab6be 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -11,7 +11,7 @@ from rest_framework.views import APIView from api.applications.models import GoodOnApplication -from api.users.models import BaseNotification +from api.users.models import BaseNotification, ExporterUser from api.applications.serializers.advice import ( CountersignAdviceSerializer, CountryWithFlagsSerializer, @@ -606,11 +606,6 @@ def get(self, request, pk, ecju_pk): return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_200_OK) def put(self, request, pk, ecju_pk): - """ - If not validate only Will update the ecju query instance, with a response, and return the data details. - If validate only, this will return if the data is acceptable or not. - """ - ecju_query = get_ecju_query(ecju_pk) if ecju_query.response: return JsonResponse( @@ -618,53 +613,51 @@ def put(self, request, pk, ecju_pk): status=status.HTTP_400_BAD_REQUEST, ) - is_govuser = hasattr(request.user, "govuser") + is_govuser_request = hasattr(request.user, "govuser") is_blank_response = not bool(request.data.get("response")) - # response is required only when a govuser closes a query - if is_govuser and is_blank_response: + if is_govuser_request and is_blank_response: return JsonResponse( data={"errors": "Enter a reason why you are closing the query"}, status=status.HTTP_400_BAD_REQUEST ) - exporter_user_full_name = getattr(get_user_by_pk(request.user.pk), "full_name", "Exporter user") - data = {"responded_by_user": str(request.user.pk)} - if request.data.get("response"): data.update({"response": request.data["response"]}) serializer = EcjuQueryUserResponseSerializer(instance=ecju_query, data=data, partial=True) - if serializer.is_valid(): - if "validate_only" not in request.data or not request.data["validate_only"]: - serializer.save() - # Delete any notifications against this query - ecju_query_type = ContentType.objects.get_for_model(EcjuQuery) - BaseNotification.objects.filter(object_id=ecju_pk, content_type=ecju_query_type).delete() + serializer.save() - # If the user is a Govuser query is manually being closed by a caseworker - query_verb = AuditType.ECJU_QUERY_MANUALLY_CLOSED if is_govuser else AuditType.ECJU_QUERY_RESPONSE - audit_trail_service.create( - actor=request.user, - verb=query_verb, - action_object=serializer.instance, - target=serializer.instance.case, - payload={"ecju_response": data.get("response")}, - ) + # Delete any notifications against this query + ecju_query_type = ContentType.objects.get_for_model(EcjuQuery) + BaseNotification.objects.filter(object_id=ecju_pk, content_type=ecju_query_type).delete() - create_system_mention( - case=ecju_query.case, - case_note_text=f"{exporter_user_full_name} has responded to a query.", - mention_user=ecju_query.raised_by_user, - ) + # If the user is a govuser then query is manually being closed by a case worker + query_verb = AuditType.ECJU_QUERY_MANUALLY_CLOSED if is_govuser_request else AuditType.ECJU_QUERY_RESPONSE + audit_trail_service.create( + actor=request.user, + verb=query_verb, + action_object=serializer.instance, + target=serializer.instance.case, + payload={"ecju_response": data.get("response")}, + ) - return JsonResponse( - data={"ecju_query": serializer.data}, - status=status.HTTP_201_CREATED, - ) - else: - return JsonResponse(data={}, status=status.HTTP_200_OK) + # if an exporter responds to a query, create a mention notification + # for the case worker that lets them know the query has been responded to + if not is_govuser_request: + try: + exporter_user_full_name = ExporterUser.objects.get(baseuser_ptr_id=request.user.pk).full_name + create_system_mention( + case=ecju_query.case, + case_note_text=f"{exporter_user_full_name} has responded to a query.", + mention_user=ecju_query.raised_by_user, + ) + except ExporterUser.DoesNotExist: + raise NotFoundError({"user": f"ExporterUser not found for pk: {request.user.pk}"}) + + # TODO change this to 200 + return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_201_CREATED) return JsonResponse(data={"errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) From 700a08c1b14f68f6882663df1b8db1f21887abf0 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Mon, 12 Feb 2024 14:38:04 +0000 Subject: [PATCH 10/12] Change HTTP_201_CREATED to HTTP_200_OK --- api/cases/tests/test_case_ecju_queries.py | 18 +++++++++--------- api/cases/views/views.py | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index 10e400b361..b4f4926577 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -466,7 +466,7 @@ def _test_exporter_responds_to_query(self, add_documents, query_type): query_response_url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) data = {"response": "Attached the requested documents"} response = self.client.put(query_response_url, data, **self.exporter_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) @@ -499,7 +499,7 @@ def test_caseworker_manually_closes_query(self): self.assertEqual(1, BaseNotification.objects.filter(object_id=ecju_query.id).count()) response = self.client.put(query_response_url, data, **self.gov_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) @@ -521,7 +521,7 @@ def test_close_query_has_optional_response_exporter(self): data = {"response": ""} response = self.client.put(query_response_url, data, **self.exporter_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response_ecju_query = response.json()["ecju_query"] self.assertIsNone(response_ecju_query["response"]) @@ -551,7 +551,7 @@ def test_caseworker_manually_closes_query_exporter_responds_raises_error(self): data = {"response": "exporter provided details"} response = self.client.put(query_response_url, data, **self.gov_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) @@ -568,7 +568,7 @@ def test_caseworker_manually_closes_query_already_closed_raises_error(self): data = {"response": "exporter provided details"} response = self.client.put(query_response_url, data, **self.exporter_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) @@ -595,7 +595,7 @@ def test_exporter_cannot_respond_to_same_ecju_query_twice(self): url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) data = {"response": "Additional details included"} response = self.client.put(url, data, **self.exporter_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) @@ -631,7 +631,7 @@ def test_exporter_cannot_add_documents_to_closed_query(self): query_response_url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) data = {"response": "Attached the requested documents"} response = self.client.put(query_response_url, data, **self.exporter_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) self.assertEqual(len(response["documents"]), 1) @@ -660,7 +660,7 @@ def test_exporter_cannot_delete_documents_of_closed_query(self): url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) data = {"response": "Additional details included"} response = self.client.put(url, data, **self.exporter_headers) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) response = response.json()["ecju_query"] self.assertEqual(response["response"], data["response"]) self.assertEqual(len(response["documents"]), 1) @@ -703,7 +703,7 @@ def test_exporter_responding_to_query_creates_case_note_mention_for_caseworker(s response = self.client.put(url, data, **self.exporter_headers) ecju_query = EcjuQuery.objects.get(case=case) - self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertTrue(ecju_query.is_query_closed) # check case note mention is created diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 81301ab6be..95f1e09592 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -656,8 +656,7 @@ def put(self, request, pk, ecju_pk): except ExporterUser.DoesNotExist: raise NotFoundError({"user": f"ExporterUser not found for pk: {request.user.pk}"}) - # TODO change this to 200 - return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_201_CREATED) + return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_200_OK) return JsonResponse(data={"errors": serializer.errors}, status=status.HTTP_400_BAD_REQUEST) From a6a44a228ce9df878531bb99a762ce0cb933a564 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Mon, 12 Feb 2024 14:43:00 +0000 Subject: [PATCH 11/12] Add docstring --- api/cases/views/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 95f1e09592..65355a0bc0 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -606,6 +606,9 @@ def get(self, request, pk, ecju_pk): return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_200_OK) def put(self, request, pk, ecju_pk): + """ + Update an ECJU query to be closed + """ ecju_query = get_ecju_query(ecju_pk) if ecju_query.response: return JsonResponse( From 24393c2af19ab5873e9ece1b0f91e09bfc4ca274 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Tue, 13 Feb 2024 09:26:39 +0000 Subject: [PATCH 12/12] Remove try-except block --- api/cases/tests/test_case_ecju_queries.py | 1 + api/cases/views/views.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index b4f4926577..fb236eb65c 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -15,6 +15,7 @@ from api.audit_trail.serializers import AuditSerializer from api.cases.enums import ECJUQueryType from api.cases.models import EcjuQuery +from api.core.exceptions import NotFoundError from api.compliance.tests.factories import ComplianceSiteCaseFactory from api.licences.enums import LicenceStatus from api.licences.tests.factories import StandardLicenceFactory diff --git a/api/cases/views/views.py b/api/cases/views/views.py index 65355a0bc0..b00412c126 100644 --- a/api/cases/views/views.py +++ b/api/cases/views/views.py @@ -646,18 +646,15 @@ def put(self, request, pk, ecju_pk): payload={"ecju_response": data.get("response")}, ) - # if an exporter responds to a query, create a mention notification + # If an exporter responds to a query, create a mention notification # for the case worker that lets them know the query has been responded to if not is_govuser_request: - try: - exporter_user_full_name = ExporterUser.objects.get(baseuser_ptr_id=request.user.pk).full_name - create_system_mention( - case=ecju_query.case, - case_note_text=f"{exporter_user_full_name} has responded to a query.", - mention_user=ecju_query.raised_by_user, - ) - except ExporterUser.DoesNotExist: - raise NotFoundError({"user": f"ExporterUser not found for pk: {request.user.pk}"}) + exporter_user_full_name = ExporterUser.objects.get(baseuser_ptr_id=request.user.pk).full_name + create_system_mention( + case=ecju_query.case, + case_note_text=f"{exporter_user_full_name} has responded to a query.", + mention_user=ecju_query.raised_by_user, + ) return JsonResponse(data={"ecju_query": serializer.data}, status=status.HTTP_200_OK)