From 699b1dba877e937a42cf70378fcb94159ac3c8b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:42:17 +0000 Subject: [PATCH 1/5] Bump pyjwt from 2.10.0 to 2.10.1 Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.10.0 to 2.10.1. - [Release notes](https://github.com/jpadilla/pyjwt/releases) - [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst) - [Commits](https://github.com/jpadilla/pyjwt/compare/2.10.0...2.10.1) --- updated-dependencies: - dependency-name: pyjwt dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 61dbfc301..170d9df8f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1466,11 +1466,12 @@ }, "pyjwt": { "hashes": [ - "sha256:543b77207db656de204372350926bed5a86201c4cbff159f623f79c7bb487a15", - "sha256:7628a7eb7938959ac1b26e819a1df0fd3259505627b575e4bad6d08f76db695c" + "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", + "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb" ], + "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==2.10.0" + "version": "==2.10.1" }, "pypdf2": { "hashes": [ From deb6b79f20ae809fe8d3adb6f37b77875fed0079 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 20:48:23 +0000 Subject: [PATCH 2/5] Bump jinja2 from 3.1.4 to 3.1.5 Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.4 to 3.1.5. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.4...3.1.5) --- updated-dependencies: - dependency-name: jinja2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 61dbfc301..514691ed5 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -2543,11 +2543,12 @@ }, "jinja2": { "hashes": [ - "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", - "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d" + "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", + "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb" ], + "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==3.1.4" + "version": "==3.1.5" }, "jmespath": { "hashes": [ From 883bee7ffa4f7c8177e3ffb2c24539a675150810 Mon Sep 17 00:00:00 2001 From: Brendan Smith Date: Tue, 7 Jan 2025 17:16:35 +0000 Subject: [PATCH 3/5] Ensure that case flags are fully present on all cases returned by case search endpoint --- api/cases/managers.py | 1 + api/cases/serializers.py | 3 +++ api/cases/views/search/service.py | 21 +-------------------- api/cases/views/search/views.py | 1 - 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/api/cases/managers.py b/api/cases/managers.py index e777fdfc5..fb8761789 100644 --- a/api/cases/managers.py +++ b/api/cases/managers.py @@ -323,6 +323,7 @@ def search( # noqa "queues", "queues__team", "baseapplication__licences", + "flags", Prefetch( "baseapplication__parties", to_attr="end_user_parties", diff --git a/api/cases/serializers.py b/api/cases/serializers.py index 2262a2a72..3146bc874 100644 --- a/api/cases/serializers.py +++ b/api/cases/serializers.py @@ -33,6 +33,8 @@ from api.compliance.serializers.ComplianceVisitCaseSerializers import ComplianceVisitSerializer from api.core.serializers import KeyValueChoiceField, PrimaryKeyRelatedSerializerField from api.documents.libraries.process_document import process_document +from api.flags.serializers import CaseListFlagSerializer +from api.flags.models import Flag from api.gov_users.serializers import GovUserSimpleSerializer from api.organisations.models import Organisation from api.organisations.serializers import TinyOrganisationViewSerializer @@ -122,6 +124,7 @@ class CaseListSerializer(serializers.Serializer): intended_end_use = serializers.SerializerMethodField() end_users = serializers.SerializerMethodField() sub_status = CaseSubStatusSerializer() + flags = PrimaryKeyRelatedSerializerField(many=True, queryset=Flag.objects.all(), serializer=CaseListFlagSerializer) def __init__(self, *args, **kwargs): self.team = kwargs.pop("team", None) diff --git a/api/cases/views/search/service.py b/api/cases/views/search/service.py index aa85f8c99..5f718fcbb 100644 --- a/api/cases/views/search/service.py +++ b/api/cases/views/search/service.py @@ -16,6 +16,7 @@ from api.applications.serializers.advice import AdviceSearchViewSerializer from api.cases.models import Case, EcjuQuery, Advice from api.common.dates import working_days_in_range, number_of_days_since +from api.flags.models import Flag from api.flags.serializers import CaseListFlagSerializer from api.organisations.models import Organisation from api.staticdata.statuses.enums import CaseStatusEnum @@ -50,27 +51,7 @@ def get_advice_types_list(): return AdviceType.to_representation() -def populate_other_flags(cases: List[Dict]): - from api.flags.models import Flag - - case_ids = [case["id"] for case in cases] - - union_flags = set( - [ - *Flag.objects.filter(cases__id__in=case_ids).annotate(case_id=F("cases__id")), - *Flag.objects.filter(organisations__cases__id__in=case_ids).annotate(case_id=F("organisations__cases__id")), - ] - ) - - for case in cases: - case_id = str(case["id"]) - flags = [flag for flag in union_flags if str(flag.case_id) == case_id] - case["flags"] = CaseListFlagSerializer(flags, many=True).data - - def populate_goods_flags(cases: List[Dict]): - from api.flags.models import Flag - case_ids = [case["id"] for case in cases] qs1 = Flag.objects.filter(goods__goods_on_application__application_id__in=case_ids).annotate( case_id=F("goods__goods_on_application__application_id"), diff --git a/api/cases/views/search/views.py b/api/cases/views/search/views.py index c57c984d0..4c838ec84 100644 --- a/api/cases/views/search/views.py +++ b/api/cases/views/search/views.py @@ -59,7 +59,6 @@ def get(self, request, *args, **kwargs): # Populate certain fields outside of the serializer for performance improvements service.populate_goods_flags(cases) service.populate_destinations_flags(cases) - service.populate_other_flags(cases) service.populate_organisation(cases) service.populate_is_recently_updated(cases) service.populate_activity_updates(case_map) From e4dddb9e2d7f7f5eb1ba0d833212482b14c463e4 Mon Sep 17 00:00:00 2001 From: Brendan Smith Date: Wed, 8 Jan 2025 10:59:46 +0000 Subject: [PATCH 4/5] Relocate case search tests next to file in question --- api/cases/views/search/tests/__init__.py | 0 .../views/search => views/search/tests}/test_service.py | 0 .../search/tests/test_views.py} | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 api/cases/views/search/tests/__init__.py rename api/cases/{tests/views/search => views/search/tests}/test_service.py (100%) rename api/cases/{tests/test_case_search.py => views/search/tests/test_views.py} (99%) diff --git a/api/cases/views/search/tests/__init__.py b/api/cases/views/search/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/api/cases/tests/views/search/test_service.py b/api/cases/views/search/tests/test_service.py similarity index 100% rename from api/cases/tests/views/search/test_service.py rename to api/cases/views/search/tests/test_service.py diff --git a/api/cases/tests/test_case_search.py b/api/cases/views/search/tests/test_views.py similarity index 99% rename from api/cases/tests/test_case_search.py rename to api/cases/views/search/tests/test_views.py index bf4bc5fbc..172b0d37b 100644 --- a/api/cases/tests/test_case_search.py +++ b/api/cases/views/search/tests/test_views.py @@ -910,7 +910,7 @@ def setUp(self): super().setUp() self.other_team = self.create_team("other team") - self.other_team_gov_user = self.create_gov_user("new_user@digital.trade.gov.uk", self.other_team) + self.other_team_gov_user = self.create_gov_user("new_user@digital.trade.gov.uk", self.other_team) # /PS-IGNORE self.queue = self.create_queue("my new queue", self.team) self.case = self.create_standard_application_case(self.organisation) self.case.queues.set([self.queue]) @@ -1011,7 +1011,7 @@ def test_api_success(self): self._create_data() self.advice = FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) self.gov_user2 = self.create_gov_user( - team=self.create_team(name="other_team"), email="new_user2@digital.trade.gov.uk" + team=self.create_team(name="other_team"), email="new_user2@digital.trade.gov.uk" # /PS-IGNORE ) self.group_advice = FinalAdviceFactory( user=self.gov_user2, case=self.case, type=AdviceType.REFUSE, good=self.good @@ -1148,7 +1148,7 @@ def test_api_success(self): ], ) - # Reflect rest framework's way of rendering datetime objects... https://github.com/encode/django-rest-framework/blob/c9e7b68a4c1db1ac60e962053380acda549609f3/rest_framework/utils/encoders.py#L29 + # Reflect rest framework's way of rendering datetime objects... https://github.com/encode/django-rest-framework/blob/c9e7b68a4c1db1ac60e962053380acda549609f3/rest_framework/utils/encoders.py#L29 /PS-IGNORE expected_submitted_at = self.case.submitted_at.isoformat() if expected_submitted_at.endswith("+00:00"): expected_submitted_at = expected_submitted_at[:-6] + "Z" From bc7e1597c567770db05f03973d69a8849939e3f8 Mon Sep 17 00:00:00 2001 From: Brendan Smith Date: Wed, 8 Jan 2025 12:01:09 +0000 Subject: [PATCH 5/5] Add test case to prove case search flags are correct across multiple cases --- api/cases/views/search/tests/test_views.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/api/cases/views/search/tests/test_views.py b/api/cases/views/search/tests/test_views.py index 172b0d37b..8eda5cd6a 100644 --- a/api/cases/views/search/tests/test_views.py +++ b/api/cases/views/search/tests/test_views.py @@ -1154,6 +1154,27 @@ def test_api_success(self): expected_submitted_at = expected_submitted_at[:-6] + "Z" self.assertEqual(case_api_result["submitted_at"], expected_submitted_at) + def test_api_multiple_cases_flags_correct(self): + # Create two cases.. + self._create_data() + self._create_data() + # Add a case flag to each case.. + flag_alias = "REFER_TO_FCDO_MEUC_CONCERNS" + flag = Flag.objects.get(alias=flag_alias) + all_cases = Case.objects.all() + for case in all_cases: + case.flags.add(flag) + + # Perform the search + response = self.client.get(self.url, **self.gov_headers) + response_data = response.json()["results"] + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response_data["cases"]), 2) + for search_result in response_data["cases"]: + self.assertEqual(len(search_result["flags"]), 1) + self.assertEqual(search_result["flags"][0]["alias"], flag_alias) + def test_api_no_advice(self): self._create_data() response = self.client.get(self.url, **self.gov_headers)