From 1547a50ed07074392eaf73838b00540ada3ee8e3 Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Wed, 16 Oct 2024 17:08:03 +0200 Subject: [PATCH 1/6] [#243] initial commit --- .../api/tests/test_partijen.py | 51 +++++++++++++++++++ src/openklant/components/utils/expansion.py | 5 ++ 2 files changed, 56 insertions(+) diff --git a/src/openklant/components/klantinteracties/api/tests/test_partijen.py b/src/openklant/components/klantinteracties/api/tests/test_partijen.py index 171b43f2..8661474d 100644 --- a/src/openklant/components/klantinteracties/api/tests/test_partijen.py +++ b/src/openklant/components/klantinteracties/api/tests/test_partijen.py @@ -5,6 +5,9 @@ from rest_framework import status from vng_api_common.tests import reverse +from openklant.components.klantinteracties.models.constants import SoortPartij +from openklant.components.klantinteracties.api.serializers.digitaal_adres import DigitaalAdresSerializer +from openklant.components.klantinteracties.models.partijen import Partij from openklant.components.klantinteracties.models.tests.factories.digitaal_adres import ( DigitaalAdresFactory, ) @@ -1797,6 +1800,54 @@ def test_destroy_partij(self): data = response.json() self.assertEqual(data["count"], 0) + def test_digitale_adressen_inclusion_param(self): + persoon = PersoonFactory(partij__soort_partij=SoortPartij.persoon) + + persoon_with_adressen = PersoonFactory( + partij__soort_partij=SoortPartij.persoon + ) + digitaal_adres = DigitaalAdresFactory( + partij=persoon_with_adressen.partij + ) + + def _get_detail_url(partij: Partij) -> str: + return reverse( + "klantinteracties:partij-detail", + kwargs={"uuid": str(partij.uuid)} + ) + + # request the partij *without* any digitale adressen + response = self.client.get( + _get_detail_url(persoon.partij), + data=dict(expand="digitaleAdressen") + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response_data: dict = response.json() + + from pprint import pprint + pprint(response_data) + + self.assertEqual(response_data["_expand"], {"digitaleAdressen": []}) + + # request the partij *with* digitale adressen + response = self.client.get( + _get_detail_url(persoon_with_adressen.partij), + data=dict(expand="digitaleAdressen") + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response_data: dict = response.json() + + serializer = DigitaalAdresSerializer(instance=digitaal_adres) + + self.assertEqual( + response_data["_expand"], + {"digitaleAdressen": [serializer.validated_data]} + ) + class PartijIdentificatorTests(APITestCase): def test_list_partij_indetificator(self): diff --git a/src/openklant/components/utils/expansion.py b/src/openklant/components/utils/expansion.py index 88c099ec..6dcdb023 100644 --- a/src/openklant/components/utils/expansion.py +++ b/src/openklant/components/utils/expansion.py @@ -111,6 +111,7 @@ def display_tree(self) -> dict: for node in root_nodes: result[node.id] = node.display_children() + return result @@ -230,6 +231,8 @@ def _field_inclusions( if inclusion_serializer is None: return + # from here, these fields are for inclusion serializer fields + if isinstance(inclusion_serializer, str): inclusion_serializer = import_string(inclusion_serializer) @@ -250,6 +253,8 @@ def _field_inclusions( inclusion_serializers, ): yield entry + else: + return [], inclusion_serializer, instance, new_path, many def _some_related_field_inclusions( self, From ec471469abe6bc24d60058d8f6ac0c0f4eccf7e5 Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Tue, 19 Nov 2024 16:25:09 +0100 Subject: [PATCH 2/6] [#243] show empty expand results --- .../api/tests/test_partijen.py | 14 ++++---- src/openklant/components/utils/expansion.py | 35 +++++++++++++------ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/openklant/components/klantinteracties/api/tests/test_partijen.py b/src/openklant/components/klantinteracties/api/tests/test_partijen.py index 8661474d..a4ff9fdc 100644 --- a/src/openklant/components/klantinteracties/api/tests/test_partijen.py +++ b/src/openklant/components/klantinteracties/api/tests/test_partijen.py @@ -1826,9 +1826,6 @@ def _get_detail_url(partij: Partij) -> str: response_data: dict = response.json() - from pprint import pprint - pprint(response_data) - self.assertEqual(response_data["_expand"], {"digitaleAdressen": []}) # request the partij *with* digitale adressen @@ -1841,11 +1838,16 @@ def _get_detail_url(partij: Partij) -> str: response_data: dict = response.json() - serializer = DigitaalAdresSerializer(instance=digitaal_adres) + received_adressen = response_data["_expand"]["digitaleAdressen"] + expected_url = reverse( + "klantinteracties:digitaaladres-detail", + kwargs=dict(uuid=digitaal_adres.uuid) + ) + self.assertEqual(len(received_adressen), 1) self.assertEqual( - response_data["_expand"], - {"digitaleAdressen": [serializer.validated_data]} + received_adressen[0]["url"], + f"http://testserver{expected_url}" ) diff --git a/src/openklant/components/utils/expansion.py b/src/openklant/components/utils/expansion.py index 6dcdb023..1b5fefc2 100644 --- a/src/openklant/components/utils/expansion.py +++ b/src/openklant/components/utils/expansion.py @@ -1,7 +1,8 @@ # SPDX-License-Identifier: EUPL-1.2 # Copyright (C) 2023 Dimpact import logging -from typing import Dict, Iterator, List, Optional, Tuple, Type, Union +from typing import Dict, Generator, Iterator, List, Optional, Tuple, Type, Union +from uuid import uuid4 from django.db import models from django.utils.module_loading import import_string @@ -65,9 +66,14 @@ def display_children(self) -> dict: """ results = {} for child in self._children: - child_result = child.display() + # TODO: rewrite display function? + child_result = child.display() if child.value else None + if child.many: - results.setdefault(child.label, []).append(child_result) + child_results = results.setdefault(child.label, []) + + if child_result: + child_results.append(child_result) else: results[child.label] = child_result return results @@ -169,9 +175,19 @@ def inclusions_dict(self, serializer: Serializer) -> dict: entries = self._inclusions((), serializer, serializer.instance) for obj, inclusion_serializer, parent, path, many in entries: - data = inclusion_serializer(instance=obj, context=serializer.context).data + if not obj: + data = None + id = str(uuid4()) + else: + serializer = inclusion_serializer( + instance=obj, context=serializer.context + ) + + data: dict | list = serializer.data + id = data["url"] + tree.add_node( - id=data["url"], + id=id, value=data, label=path[-1], many=many, @@ -205,11 +221,11 @@ def _field_inclusions( self, path: Tuple[str, ...], field: Field, - instance: models.Model, + instance: Optional[models.Model], name: str, inclusion_serializers: Dict[str, Union[str, Type[Serializer]]], ) -> Iterator[ - Tuple[models.Model, Type[Serializer], models.Model, Tuple[str, ...], bool] + Tuple[Optional[models.Model], Type[Serializer], models.Model, Tuple[str, ...], bool] ]: """ change return of this generator from (obj, serializer_class) to @@ -231,8 +247,6 @@ def _field_inclusions( if inclusion_serializer is None: return - # from here, these fields are for inclusion serializer fields - if isinstance(inclusion_serializer, str): inclusion_serializer = import_string(inclusion_serializer) @@ -254,7 +268,8 @@ def _field_inclusions( ): yield entry else: - return [], inclusion_serializer, instance, new_path, many + if new_path in self.allowed_paths: + yield None, inclusion_serializer, instance, new_path, many def _some_related_field_inclusions( self, From 6137dd5353690c129e4ef025b627f854b8b0f81b Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Wed, 20 Nov 2024 11:04:01 +0100 Subject: [PATCH 3/6] [#243] fix nested expand values --- .../api/tests/test_partijen.py | 22 ++++++------------ src/openklant/components/utils/expansion.py | 23 ++++++++++++------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/openklant/components/klantinteracties/api/tests/test_partijen.py b/src/openklant/components/klantinteracties/api/tests/test_partijen.py index a4ff9fdc..9b1af25e 100644 --- a/src/openklant/components/klantinteracties/api/tests/test_partijen.py +++ b/src/openklant/components/klantinteracties/api/tests/test_partijen.py @@ -6,7 +6,6 @@ from vng_api_common.tests import reverse from openklant.components.klantinteracties.models.constants import SoortPartij -from openklant.components.klantinteracties.api.serializers.digitaal_adres import DigitaalAdresSerializer from openklant.components.klantinteracties.models.partijen import Partij from openklant.components.klantinteracties.models.tests.factories.digitaal_adres import ( DigitaalAdresFactory, @@ -1803,23 +1802,17 @@ def test_destroy_partij(self): def test_digitale_adressen_inclusion_param(self): persoon = PersoonFactory(partij__soort_partij=SoortPartij.persoon) - persoon_with_adressen = PersoonFactory( - partij__soort_partij=SoortPartij.persoon - ) - digitaal_adres = DigitaalAdresFactory( - partij=persoon_with_adressen.partij - ) + persoon_with_adressen = PersoonFactory(partij__soort_partij=SoortPartij.persoon) + digitaal_adres = DigitaalAdresFactory(partij=persoon_with_adressen.partij) def _get_detail_url(partij: Partij) -> str: return reverse( - "klantinteracties:partij-detail", - kwargs={"uuid": str(partij.uuid)} + "klantinteracties:partij-detail", kwargs={"uuid": str(partij.uuid)} ) # request the partij *without* any digitale adressen response = self.client.get( - _get_detail_url(persoon.partij), - data=dict(expand="digitaleAdressen") + _get_detail_url(persoon.partij), data=dict(expand="digitaleAdressen") ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1831,7 +1824,7 @@ def _get_detail_url(partij: Partij) -> str: # request the partij *with* digitale adressen response = self.client.get( _get_detail_url(persoon_with_adressen.partij), - data=dict(expand="digitaleAdressen") + data=dict(expand="digitaleAdressen"), ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1841,13 +1834,12 @@ def _get_detail_url(partij: Partij) -> str: received_adressen = response_data["_expand"]["digitaleAdressen"] expected_url = reverse( "klantinteracties:digitaaladres-detail", - kwargs=dict(uuid=digitaal_adres.uuid) + kwargs=dict(uuid=digitaal_adres.uuid), ) self.assertEqual(len(received_adressen), 1) self.assertEqual( - received_adressen[0]["url"], - f"http://testserver{expected_url}" + received_adressen[0]["url"], f"http://testserver{expected_url}" ) diff --git a/src/openklant/components/utils/expansion.py b/src/openklant/components/utils/expansion.py index 1b5fefc2..85f458a0 100644 --- a/src/openklant/components/utils/expansion.py +++ b/src/openklant/components/utils/expansion.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: EUPL-1.2 # Copyright (C) 2023 Dimpact import logging -from typing import Dict, Generator, Iterator, List, Optional, Tuple, Type, Union +from typing import Dict, Iterator, List, Optional, Tuple, Type, Union from uuid import uuid4 from django.db import models @@ -66,20 +66,19 @@ def display_children(self) -> dict: """ results = {} for child in self._children: - # TODO: rewrite display function? - child_result = child.display() if child.value else None + child_result = child.display() if child.many: child_results = results.setdefault(child.label, []) - if child_result: + if child_result is not None: child_results.append(child_result) else: results[child.label] = child_result return results - def display(self) -> dict: - data = self.value.copy() + def display(self) -> Optional[dict]: + data = self.value.copy() if self.value is not None else None if self._children: data[EXPAND_KEY] = self.display_children() return data @@ -225,7 +224,13 @@ def _field_inclusions( name: str, inclusion_serializers: Dict[str, Union[str, Type[Serializer]]], ) -> Iterator[ - Tuple[Optional[models.Model], Type[Serializer], models.Model, Tuple[str, ...], bool] + Tuple[ + Optional[models.Model], + Type[Serializer], + models.Model, + Tuple[str, ...], + bool, + ] ]: """ change return of this generator from (obj, serializer_class) to @@ -254,6 +259,8 @@ def _field_inclusions( True if hasattr(field, "child_relation") else getattr(field, "many", False) ) + obj: Optional[models.Model] = None + for obj in self._some_related_field_inclusions( new_path, field, instance, inclusion_serializer ): @@ -269,7 +276,7 @@ def _field_inclusions( yield entry else: if new_path in self.allowed_paths: - yield None, inclusion_serializer, instance, new_path, many + yield obj, inclusion_serializer, instance, new_path, many def _some_related_field_inclusions( self, From f9a954d16eee25f26e6e46a162687ed8b29e8c6a Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Wed, 20 Nov 2024 14:45:07 +0100 Subject: [PATCH 4/6] [#243] set tree node id to `None` whenever applicable --- src/openklant/components/utils/expansion.py | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/openklant/components/utils/expansion.py b/src/openklant/components/utils/expansion.py index 85f458a0..5f66ec02 100644 --- a/src/openklant/components/utils/expansion.py +++ b/src/openklant/components/utils/expansion.py @@ -75,6 +75,7 @@ def display_children(self) -> dict: child_results.append(child_result) else: results[child.label] = child_result + return results def display(self) -> Optional[dict]: @@ -174,24 +175,23 @@ def inclusions_dict(self, serializer: Serializer) -> dict: entries = self._inclusions((), serializer, serializer.instance) for obj, inclusion_serializer, parent, path, many in entries: - if not obj: - data = None - id = str(uuid4()) - else: + tree_kwargs = dict( + id=None, + value=None, + label=path[-1], + many=many, + parent_id=parent.get_absolute_api_url(request=request), + ) + + if obj: serializer = inclusion_serializer( instance=obj, context=serializer.context ) - data: dict | list = serializer.data - id = data["url"] - tree.add_node( - id=id, - value=data, - label=path[-1], - many=many, - parent_id=parent.get_absolute_api_url(request=request), - ) + tree_kwargs.update(dict(value=data, id=data["url"])) + + tree.add_node(**tree_kwargs) result = tree.display_tree() From 9a2e0e6cdba5f5d44ae9dbb224b41177c2385a88 Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Wed, 20 Nov 2024 15:14:13 +0100 Subject: [PATCH 5/6] [#243] remove nested if statements --- src/openklant/components/utils/expansion.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/openklant/components/utils/expansion.py b/src/openklant/components/utils/expansion.py index 5f66ec02..69570a2f 100644 --- a/src/openklant/components/utils/expansion.py +++ b/src/openklant/components/utils/expansion.py @@ -65,16 +65,21 @@ def display_children(self) -> dict: return dict where children are grouped by their label """ results = {} - for child in self._children: - child_result = child.display() - if child.many: - child_results = results.setdefault(child.label, []) + for child in self._children: + child_result: Optional[dict] = child.display() - if child_result is not None: - child_results.append(child_result) - else: + if not child.many: results[child.label] = child_result + continue + + child_results: list = results.setdefault(child.label, []) + + if child_result is None: + continue + + if child_result is not None: + child_results.append(child_result) return results From a5880dc2d72240893f656191958e9514ab61b054 Mon Sep 17 00:00:00 2001 From: Sonny Bakker Date: Wed, 20 Nov 2024 15:15:53 +0100 Subject: [PATCH 6/6] [#243] fix flake8 errors --- src/openklant/components/utils/expansion.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/openklant/components/utils/expansion.py b/src/openklant/components/utils/expansion.py index 69570a2f..853297b5 100644 --- a/src/openklant/components/utils/expansion.py +++ b/src/openklant/components/utils/expansion.py @@ -2,7 +2,6 @@ # Copyright (C) 2023 Dimpact import logging from typing import Dict, Iterator, List, Optional, Tuple, Type, Union -from uuid import uuid4 from django.db import models from django.utils.module_loading import import_string