Skip to content

Commit 492eb16

Browse files
authored
Merge pull request #132 from maykinmedia/feature/127-selectielijstklasse-endpoint
[#127] Selectielijstklasse choices endpoint
2 parents c3824e1 + 424dff7 commit 492eb16

File tree

9 files changed

+261
-10
lines changed

9 files changed

+261
-10
lines changed

Diff for: backend/src/openarchiefbeheer/api/urls.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
DestructionListReviewViewSet,
1515
DestructionListViewSet,
1616
)
17-
from openarchiefbeheer.zaken.api.views import CacheZakenView, ZaaktypenChoicesView
17+
from openarchiefbeheer.zaken.api.views import (
18+
CacheZakenView,
19+
SelectielijstklasseChoicesView,
20+
ZaaktypenChoicesView,
21+
)
1822
from openarchiefbeheer.zaken.api.viewsets import ZakenViewSet
1923

2024
app_name = "api"
@@ -81,6 +85,11 @@
8185
ZaaktypenChoicesView.as_view(),
8286
name="retrieve-zaaktypen-choices",
8387
),
88+
path(
89+
"_selectielijstklasse-choices",
90+
SelectielijstklasseChoicesView.as_view(),
91+
name="retrieve-selectielijstklasse-choices",
92+
),
8493
path("", include(router.urls)),
8594
]
8695
),

Diff for: backend/src/openarchiefbeheer/destruction/migrations/0007_destructionlistreview_destructionlistitemreview.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class Migration(migrations.Migration):
8484
"feedback",
8585
models.TextField(
8686
blank=False,
87-
help_text="If 'other' was selected in the field 'requested change', feedback about what should be done with the case can be provided here.",
87+
help_text="What needs to be changed about the case.",
8888
max_length=2000,
8989
verbose_name="feedback",
9090
),

Diff for: backend/src/openarchiefbeheer/destruction/models.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,7 @@ class DestructionListItemReview(models.Model):
289289
_("feedback"),
290290
max_length=2000,
291291
blank=False,
292-
help_text=_(
293-
"If 'other' was selected in the field 'requested change', "
294-
"feedback about what should be done with the case can be provided here."
295-
),
292+
help_text=_("What needs to be changed about the case."),
296293
)
297294

298295
class Meta:

Diff for: backend/src/openarchiefbeheer/zaken/api/serializers.py

+19
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,22 @@ class ZaaktypeChoiceSerializer(serializers.Serializer):
6666
"the zaaktype will no longer be valid (if present)."
6767
)
6868
)
69+
70+
71+
class SelectielijstklasseChoicesSerializer(serializers.Serializer):
72+
label = serializers.CharField(
73+
help_text=_(
74+
"The description field of the resultaat from the selectielijst API."
75+
)
76+
)
77+
value = serializers.CharField(
78+
help_text=_("The URL field of the resultaat from the selectielijst API.")
79+
)
80+
81+
82+
class SelectielijstklasseChoicesQueryParamSerializer(serializers.Serializer):
83+
zaak = serializers.URLField(
84+
help_text=_(
85+
"The URL of the zaak for which the selectielijstklasse choices are needed."
86+
)
87+
)

Diff for: backend/src/openarchiefbeheer/zaken/api/views.py

+39-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@
1010

1111
from openarchiefbeheer.destruction.api.permissions import CanStartDestructionPermission
1212

13+
from ..models import Zaak
1314
from ..tasks import retrieve_and_cache_zaken_from_openzaak
14-
from ..utils import retrieve_zaaktypen_choices
15-
from .serializers import ZaaktypeChoiceSerializer
15+
from ..utils import retrieve_selectielijstklasse_choices, retrieve_zaaktypen_choices
16+
from .serializers import (
17+
SelectielijstklasseChoicesQueryParamSerializer,
18+
SelectielijstklasseChoicesSerializer,
19+
ZaaktypeChoiceSerializer,
20+
)
1621

1722

1823
class CacheZakenView(APIView):
@@ -49,3 +54,35 @@ def get(self, request, *args, **kwargs):
4954
serializer = ZaaktypeChoiceSerializer(data=zaaktypen_choices, many=True)
5055
serializer.is_valid(raise_exception=True)
5156
return Response(serializer.data, status=status.HTTP_200_OK)
57+
58+
59+
class SelectielijstklasseChoicesView(APIView):
60+
permission_classes = [IsAuthenticated & CanStartDestructionPermission]
61+
62+
@extend_schema(
63+
summary=_("Retrieve selectielijstklasse choices"),
64+
description=_(
65+
"This takes the 'selectielijstprocestype' from the 'zaaktype', "
66+
"then retrieves all the 'resultaten' possible for this 'procestype' from the selectielijst API."
67+
),
68+
tags=["private"],
69+
responses={
70+
200: SelectielijstklasseChoicesSerializer,
71+
},
72+
parameters=[SelectielijstklasseChoicesQueryParamSerializer],
73+
)
74+
def get(self, request, *args, **kwargs):
75+
seralizer = SelectielijstklasseChoicesQueryParamSerializer(
76+
data=request.query_params
77+
)
78+
seralizer.is_valid(raise_exception=True)
79+
80+
zaak = Zaak.objects.get(url=seralizer.validated_data["zaak"])
81+
82+
processtype = zaak._expand["zaaktype"].get("selectielijst_procestype")
83+
84+
if not processtype:
85+
return Response(data=[])
86+
87+
choices = retrieve_selectielijstklasse_choices(processtype["url"])
88+
return Response(data=choices)

Diff for: backend/src/openarchiefbeheer/zaken/tests/factories.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ class Params:
2626
_expand={
2727
"zaaktype": {
2828
"url": "http://catalogue-api.nl/zaaktypen/111-111-111",
29-
"selectielijst_procestype": {"nummer": 1},
29+
"selectielijst_procestype": {
30+
"nummer": 1,
31+
"url": "https://selectielijst.nl/api/v1/procestypen/7ff2b005-4d84-47fe-983a-732bfa958ff5",
32+
},
3033
"omschrijving": "Aangifte behandelen",
3134
},
3235
"resultaat": {

Diff for: backend/src/openarchiefbeheer/zaken/tests/test_views.py

+142
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.core.cache import cache
44
from django.utils.translation import gettext_lazy as _
55

6+
from furl import furl
67
from requests_mock import Mocker
78
from rest_framework import status
89
from rest_framework.reverse import reverse
@@ -13,6 +14,7 @@
1314
from openarchiefbeheer.accounts.tests.factories import UserFactory
1415

1516
from ..tasks import retrieve_and_cache_zaken_from_openzaak
17+
from .factories import ZaakFactory
1618

1719

1820
class ZakenViewsTestCase(APITestCase):
@@ -158,3 +160,143 @@ def test_response_cached(self, m):
158160

159161
history = m.request_history
160162
self.assertEqual(len(history), 1)
163+
164+
165+
class SelectielijstklasseChoicesViewTests(APITestCase):
166+
def setUp(self):
167+
super().setUp()
168+
169+
self.addCleanup(cache.clear)
170+
171+
def test_not_authenticated(self):
172+
endpoint = reverse("api:retrieve-selectielijstklasse-choices")
173+
174+
response = self.client.get(endpoint)
175+
176+
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
177+
178+
def test_authenticated_without_permission(self):
179+
user = UserFactory.create(role__can_start_destruction=False)
180+
181+
self.client.force_authenticate(user=user)
182+
endpoint = reverse("api:retrieve-selectielijstklasse-choices")
183+
184+
response = self.client.get(endpoint)
185+
186+
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
187+
188+
@Mocker()
189+
def test_retrieve_choices(self, m):
190+
ServiceFactory.create(
191+
api_type=APITypes.orc,
192+
api_root="http://selectielijst.nl/api/v1",
193+
)
194+
user = UserFactory.create(role__can_start_destruction=True)
195+
zaak = ZaakFactory.create(with_expand=True)
196+
process_type_url = zaak._expand["zaaktype"]["selectielijst_procestype"]["url"]
197+
198+
m.get(
199+
f"http://selectielijst.nl/api/v1/resultaten?procesType={process_type_url}",
200+
json={
201+
"count": 3,
202+
"results": [
203+
{
204+
"url": "http://selectielijst.nl/api/v1/resultaten/2e86a8ca-0269-446c-8da2-6f4d08be422d",
205+
"nummer": 1,
206+
"volledigNummer": "11.1",
207+
"naam": "Verleend",
208+
"waardering": "vernietigen",
209+
"bewaartermijn": "P1Y",
210+
},
211+
{
212+
"url": "http://selectielijst.nl/api/v1/resultaten/5038528b-0eb7-4502-a415-a3093987d69b",
213+
"nummer": 1,
214+
"naam": "Verleend",
215+
"waardering": "vernietigen",
216+
"bewaartermijn": "P2Y",
217+
},
218+
{
219+
"url": "http://selectielijst.nl/api/v1/resultaten/5d102cc6-4a74-4262-a14a-538bbfe3f2da",
220+
"nummer": 2,
221+
"volledigNummer": "11.1.2",
222+
"naam": "Verleend",
223+
"waardering": "vernietigen",
224+
},
225+
],
226+
},
227+
)
228+
229+
self.client.force_authenticate(user=user)
230+
endpoint = furl(reverse("api:retrieve-selectielijstklasse-choices"))
231+
endpoint.args["zaak"] = zaak.url
232+
233+
response = self.client.get(endpoint.url)
234+
235+
self.assertEqual(response.status_code, status.HTTP_200_OK)
236+
self.assertEqual(
237+
response.json(),
238+
[
239+
{
240+
"value": "http://selectielijst.nl/api/v1/resultaten/2e86a8ca-0269-446c-8da2-6f4d08be422d",
241+
"label": "11.1 - Verleend - vernietigen - P1Y",
242+
},
243+
{
244+
"value": "http://selectielijst.nl/api/v1/resultaten/5038528b-0eb7-4502-a415-a3093987d69b",
245+
"label": "1 - Verleend - vernietigen - P2Y",
246+
},
247+
{
248+
"value": "http://selectielijst.nl/api/v1/resultaten/5d102cc6-4a74-4262-a14a-538bbfe3f2da",
249+
"label": "11.1.2 - Verleend - vernietigen",
250+
},
251+
],
252+
)
253+
254+
@Mocker()
255+
def test_response_cached(self, m):
256+
ServiceFactory.create(
257+
api_type=APITypes.orc,
258+
api_root="http://selectielijst.nl/api/v1",
259+
)
260+
user = UserFactory.create(role__can_start_destruction=True)
261+
zaak = ZaakFactory.create(with_expand=True)
262+
process_type_url = zaak._expand["zaaktype"]["selectielijst_procestype"]["url"]
263+
264+
m.get(
265+
f"http://selectielijst.nl/api/v1/resultaten?procesType={process_type_url}",
266+
json={
267+
"count": 3,
268+
"results": [
269+
{
270+
"url": "http://selectielijst.nl/api/v1/resultaten/2e86a8ca-0269-446c-8da2-6f4d08be422d",
271+
"nummer": 1,
272+
"volledigNummer": "11.1",
273+
"naam": "Verleend",
274+
"waardering": "vernietigen",
275+
"bewaartermijn": "P1Y",
276+
},
277+
{
278+
"url": "http://selectielijst.nl/api/v1/resultaten/5038528b-0eb7-4502-a415-a3093987d69b",
279+
"nummer": 1,
280+
"naam": "Verleend",
281+
"waardering": "vernietigen",
282+
"bewaartermijn": "P2Y",
283+
},
284+
{
285+
"url": "http://selectielijst.nl/api/v1/resultaten/5d102cc6-4a74-4262-a14a-538bbfe3f2da",
286+
"nummer": 2,
287+
"volledigNummer": "11.1.2",
288+
"naam": "Verleend",
289+
"waardering": "vernietigen",
290+
},
291+
],
292+
},
293+
)
294+
295+
self.client.force_authenticate(user=user)
296+
endpoint = furl(reverse("api:retrieve-selectielijstklasse-choices"))
297+
endpoint.args["zaak"] = zaak.url
298+
299+
self.client.get(endpoint.url)
300+
self.client.get(endpoint.url)
301+
302+
self.assertEqual(len(m.request_history), 1)

Diff for: backend/src/openarchiefbeheer/zaken/types.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from dataclasses import dataclass
2+
from typing import Optional
3+
4+
5+
@dataclass
6+
class DropDownChoice:
7+
label: str
8+
value: str
9+
extra_data: Optional[int] = None

Diff for: backend/src/openarchiefbeheer/zaken/utils.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
from ape_pie import APIClient
77
from djangorestframework_camel_case.parser import CamelCaseJSONParser
88
from djangorestframework_camel_case.util import underscoreize
9+
from zgw_consumers.api_models.selectielijst import Resultaat
910
from zgw_consumers.client import build_client
1011
from zgw_consumers.concurrent import parallel
1112
from zgw_consumers.constants import APITypes
1213
from zgw_consumers.models import Service
1314
from zgw_consumers.utils import PaginatedResponseData
1415

16+
from .types import DropDownChoice
17+
1518

1619
def pagination_helper(
1720
client: APIClient, paginated_response: PaginatedResponseData, **kwargs
@@ -75,7 +78,7 @@ def get_zaaktype_extra_info(zaaktype: dict) -> str:
7578
return zaaktype["identificatie"]
7679

7780

78-
def retrieve_zaaktypen_choices() -> list[dict]:
81+
def retrieve_zaaktypen_choices() -> list[DropDownChoice]:
7982
ztc_service = Service.objects.filter(api_type=APITypes.ztc).first()
8083
if not ztc_service:
8184
return []
@@ -101,3 +104,35 @@ def retrieve_zaaktypen_choices() -> list[dict]:
101104
]
102105

103106
return zaaktypen
107+
108+
109+
def format_selectielijstklasse_choice(resultaat: Resultaat) -> DropDownChoice:
110+
description = f"{resultaat.get('volledig_nummer', resultaat["nummer"])} - {resultaat['naam']} - {resultaat['waardering']}"
111+
if resultaat.get("bewaartermijn"):
112+
description = description + f" - {resultaat['bewaartermijn']}"
113+
114+
return {
115+
"label": description,
116+
"value": resultaat["url"],
117+
}
118+
119+
120+
@lru_cache
121+
def retrieve_selectielijstklasse_choices(process_type_url: str) -> list:
122+
selectielijst_service = Service.objects.filter(api_type=APITypes.orc).first()
123+
if not selectielijst_service:
124+
return []
125+
126+
client = build_client(selectielijst_service)
127+
with client:
128+
response = client.get("resultaten", params={"procesType": process_type_url})
129+
response.raise_for_status()
130+
data_iterator = pagination_helper(client, response.json())
131+
132+
results = []
133+
for page in data_iterator:
134+
results += [
135+
format_selectielijstklasse_choice(result) for result in page["results"]
136+
]
137+
138+
return results

0 commit comments

Comments
 (0)