Skip to content

Commit 573e0c5

Browse files
authored
Merge pull request #268 from maykinmedia/feature/262-paginate-destruction-list-item
[#262] Paginate destruction list item and order on processing status
2 parents c24d3b8 + b7555b7 commit 573e0c5

21 files changed

+247
-69
lines changed

backend/src/openarchiefbeheer/destruction/api/filtersets.py

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from django.db.models import QuerySet
1+
from django.db.models import Case, QuerySet, Value, When
2+
from django.utils.translation import gettext_lazy as _
23

34
from django_filters import FilterSet, NumberFilter, OrderingFilter, UUIDFilter
45

6+
from ..constants import InternalStatus
57
from ..models import (
68
DestructionList,
79
DestructionListItem,
@@ -12,9 +14,36 @@
1214

1315

1416
class DestructionListItemFilterset(FilterSet):
17+
destruction_list = UUIDFilter(
18+
field_name="destruction_list",
19+
method="filter_in_destruction_list",
20+
help_text=_(
21+
"Retrieve the items that are in a destruction list and "
22+
"order them based on processing status."
23+
),
24+
)
25+
1526
class Meta:
1627
model = DestructionListItem
17-
fields = ("destruction_list",)
28+
fields = ("destruction_list", "status", "processing_status")
29+
30+
def filter_in_destruction_list(
31+
self, queryset: QuerySet[DestructionListItem], name: str, value: str
32+
) -> QuerySet[DestructionListItem]:
33+
return (
34+
queryset.filter(destruction_list__uuid=value)
35+
.annotate(
36+
processing_status_index=Case(
37+
When(processing_status=InternalStatus.failed, then=Value(1)),
38+
When(processing_status=InternalStatus.processing, then=Value(2)),
39+
When(processing_status=InternalStatus.queued, then=Value(3)),
40+
When(processing_status=InternalStatus.new, then=Value(4)),
41+
When(processing_status=InternalStatus.succeeded, then=Value(5)),
42+
default=Value(1),
43+
),
44+
)
45+
.order_by("processing_status_index")
46+
)
1847

1948

2049
class DestructionListFilterset(FilterSet):

backend/src/openarchiefbeheer/destruction/api/serializers.py

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ class Meta:
111111
"status",
112112
"extra_zaak_data",
113113
"zaak_data",
114+
"processing_status",
114115
)
115116

116117
def validate(self, attrs: dict) -> dict:

backend/src/openarchiefbeheer/destruction/api/viewsets.py

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from rest_framework.response import Response
1212

1313
from openarchiefbeheer.logging import logevent
14+
from openarchiefbeheer.utils.paginators import PageNumberPagination
1415

1516
from ..constants import InternalStatus, ListRole
1617
from ..models import (
@@ -299,6 +300,7 @@ class DestructionListItemsViewSet(
299300
queryset = DestructionListItem.objects.all()
300301
filter_backends = (DjangoFilterBackend,)
301302
filterset_class = DestructionListItemFilterset
303+
pagination_class = PageNumberPagination
302304

303305

304306
@extend_schema_view(

backend/src/openarchiefbeheer/destruction/tests/test_endpoints.py

+38-3
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ def test_retrieve_destruction_list_items(self):
871871

872872
self.assertEqual(response.status_code, status.HTTP_200_OK)
873873

874-
data = sorted(response.json(), key=lambda item: item["zaak"])
874+
data = sorted(response.json()["results"], key=lambda item: item["zaak"])
875875

876876
self.assertEqual(
877877
data[0]["zaakData"]["omschrijving"],
@@ -910,7 +910,7 @@ def test_filter_items_on_destruction_list(self):
910910

911911
self.client.force_authenticate(user=record_manager)
912912
endpoint = furl(reverse("api:destruction-list-items-list"))
913-
endpoint.args["destruction_list"] = destruction_list.pk
913+
endpoint.args["destruction_list"] = str(destruction_list.uuid)
914914

915915
response = self.client.get(
916916
endpoint.url,
@@ -920,7 +920,42 @@ def test_filter_items_on_destruction_list(self):
920920

921921
data = response.json()
922922

923-
self.assertEqual(len(data), 2)
923+
self.assertEqual(data["count"], 2)
924+
925+
def test_order_on_processing_status(self):
926+
record_manager = UserFactory.create(username="record_manager")
927+
destruction_list = DestructionListFactory.create()
928+
item1 = DestructionListItemFactory.create(
929+
status=ListItemStatus.suggested,
930+
destruction_list=destruction_list,
931+
processing_status=InternalStatus.succeeded,
932+
)
933+
item2 = DestructionListItemFactory.create(
934+
status=ListItemStatus.suggested,
935+
destruction_list=destruction_list,
936+
processing_status=InternalStatus.processing,
937+
)
938+
item3 = DestructionListItemFactory.create(
939+
status=ListItemStatus.suggested,
940+
destruction_list=destruction_list,
941+
processing_status=InternalStatus.failed,
942+
)
943+
944+
self.client.force_authenticate(user=record_manager)
945+
endpoint = furl(reverse("api:destruction-list-items-list"))
946+
endpoint.args["destruction_list"] = str(destruction_list.uuid)
947+
948+
response = self.client.get(
949+
endpoint.url,
950+
)
951+
952+
self.assertEqual(response.status_code, status.HTTP_200_OK)
953+
954+
data = response.json()
955+
956+
self.assertEqual(data["results"][0]["pk"], item3.pk)
957+
self.assertEqual(data["results"][1]["pk"], item2.pk)
958+
self.assertEqual(data["results"][2]["pk"], item1.pk)
924959

925960

926961
class DestructionListReviewViewSetTest(APITestCase):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from rest_framework.pagination import PageNumberPagination as _PageNumberPagination
2+
3+
4+
class PageNumberPagination(_PageNumberPagination):
5+
page_size_query_param = "page_size"
6+
page_size = 100

backend/src/openarchiefbeheer/zaken/api/viewsets.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,19 @@
44
from drf_spectacular.utils import extend_schema, extend_schema_view
55
from rest_framework import mixins, viewsets
66
from rest_framework.filters import OrderingFilter
7-
from rest_framework.pagination import PageNumberPagination as _PageNumberPagination
87
from rest_framework.permissions import IsAuthenticated
98

109
from openarchiefbeheer.destruction.api.permissions import (
1110
CanReviewPermission,
1211
CanStartDestructionPermission,
1312
)
13+
from openarchiefbeheer.utils.paginators import PageNumberPagination
1414

1515
from ..models import Zaak
1616
from .filtersets import ZaakFilter
1717
from .serializers import ZaakSerializer
1818

1919

20-
class PageNumberPagination(_PageNumberPagination):
21-
page_size_query_param = "page_size"
22-
page_size = 100
23-
24-
2520
@extend_schema_view(
2621
list=extend_schema(
2722
summary=_("List zaken"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
DestructionListItem,
3+
PaginatedDestructionListItems,
4+
} from "../lib/api/destructionListsItem";
5+
import { createArrayFactory, createObjectFactory } from "./factory";
6+
import { zaakFactory } from "./zaak";
7+
8+
export const FIXTURE_DESTRUCTION_LIST_ITEM: DestructionListItem = {
9+
pk: 1,
10+
zaak: zaakFactory().url,
11+
status: "suggested",
12+
extraZaakData: null,
13+
zaakData: zaakFactory(),
14+
processingStatus: "new",
15+
};
16+
17+
export const destructionListItemFactory = createObjectFactory(
18+
FIXTURE_DESTRUCTION_LIST_ITEM,
19+
);
20+
export const destructionListItemsFactory = createArrayFactory([
21+
FIXTURE_DESTRUCTION_LIST_ITEM,
22+
]);
23+
24+
const FIXTURE_PAGINATED_DESTRUCTION_LIST_ITEMS = {
25+
count: 10,
26+
next: null,
27+
previous: null,
28+
results: destructionListItemsFactory(),
29+
};
30+
31+
export const paginatedDestructionListItemsFactory =
32+
createObjectFactory<PaginatedDestructionListItems>(
33+
FIXTURE_PAGINATED_DESTRUCTION_LIST_ITEMS,
34+
);

frontend/src/lib/api/destructionLists.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { isPrimitive } from "@maykin-ui/admin-ui";
22

33
import { Zaak } from "../../types";
44
import { User } from "./auth";
5+
import { ProcessingStatus } from "./processingStatus";
56
import { request } from "./request";
67

78
export type DestructionList = {
@@ -13,7 +14,7 @@ export type DestructionList = {
1314
created: string;
1415
name: string;
1516
status: DestructionListStatus;
16-
processingStatus: DestructionListProcessingStatus;
17+
processingStatus: ProcessingStatus;
1718
statusChanged: string | null;
1819
uuid: string;
1920
};
@@ -35,13 +36,6 @@ export const DESTRUCTION_LIST_STATUSES = [
3536
// Inferring the type of the array, so that we don't have to repeat the same.
3637
export type DestructionListStatus = (typeof DESTRUCTION_LIST_STATUSES)[number];
3738

38-
export type DestructionListProcessingStatus =
39-
| "new"
40-
| "queued"
41-
| "processing"
42-
| "failed"
43-
| "succeeded";
44-
4539
export type DestructionListUpdateData = {
4640
assignees?: DestructionListAssigneeUpdate[];
4741
items?: DestructionListItemUpdate[];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Zaak } from "../../types";
2+
import { PaginatedResults } from "./paginatedResults";
3+
import { ProcessingStatus } from "./processingStatus";
4+
import { request } from "./request";
5+
6+
export type DestructionListItem = {
7+
pk: number;
8+
zaak: Zaak["url"];
9+
status?: DestructionListItemStatus;
10+
extraZaakData?: Record<string, unknown> | null;
11+
zaakData: Zaak | null;
12+
processingStatus?: ProcessingStatus;
13+
};
14+
15+
export type DestructionListItemStatus = "removed" | "suggested";
16+
17+
export type PaginatedDestructionListItems =
18+
PaginatedResults<DestructionListItem>;
19+
20+
/**
21+
* List destruction lists.
22+
*/
23+
export async function listDestructionListItems(
24+
destructionListUuid: string,
25+
params?:
26+
| URLSearchParams
27+
| {
28+
page?: number;
29+
page_size?: number;
30+
processing_status?: ProcessingStatus;
31+
status: DestructionListItemStatus; // TODO ?
32+
},
33+
) {
34+
const response = await request("GET", "/destruction-list-items/", {
35+
destruction_list: destructionListUuid,
36+
status: "suggested",
37+
...params,
38+
} as typeof params & { destruction_list: string });
39+
const promise: Promise<PaginatedDestructionListItems> = response.json();
40+
return promise;
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type PaginatedResults<T> = {
2+
count: number;
3+
next: string | null;
4+
previous: string | null;
5+
results: T[];
6+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type ProcessingStatus =
2+
| "new"
3+
| "queued"
4+
| "processing"
5+
| "failed"
6+
| "succeeded";

frontend/src/lib/api/reviewResponse.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { DestructionList } from "./destructionLists";
21
import { request } from "./request";
32
import { Review, ReviewItem } from "./review";
43

frontend/src/lib/api/zaken.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { Zaak } from "../../types";
2+
import { PaginatedResults } from "./paginatedResults";
23
import { request } from "./request";
34

4-
export type PaginatedZaken = {
5-
count: number;
6-
next: string | null;
7-
previous: string | null;
8-
results: Zaak[];
9-
};
5+
export type PaginatedZaken = PaginatedResults<Zaak>;
106

117
/**
128
* Retrieve zaken using the configured ZRC service. For information over the query parameters accepted and the schema of

frontend/src/pages/constants.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { BadgeProps, Outline } from "@maykin-ui/admin-ui";
22

33
import {
44
DESTRUCTION_LIST_STATUSES,
5-
DestructionListProcessingStatus,
65
DestructionListStatus,
76
} from "../lib/api/destructionLists";
7+
import { ProcessingStatus } from "../lib/api/processingStatus";
88
import { Review } from "../lib/api/review";
99

1010
export const REVIEW_DECISION_MAPPING: Record<Review["decision"], string> = {
@@ -45,7 +45,7 @@ export const STATUS_LEVEL_MAPPING: {
4545
};
4646

4747
export const PROCESSING_STATUS_MAPPING: {
48-
[key in DestructionListProcessingStatus]: string;
48+
[key in ProcessingStatus]: string;
4949
} = {
5050
new: "new",
5151
queued: "queued",
@@ -55,7 +55,7 @@ export const PROCESSING_STATUS_MAPPING: {
5555
};
5656

5757
export const PROCESSING_STATUS_ICON_MAPPING: {
58-
[key in DestructionListProcessingStatus]: React.ReactNode;
58+
[key in ProcessingStatus]: React.ReactNode;
5959
} = {
6060
new: <Outline.PlusCircleIcon />,
6161
queued: <Outline.CircleStackIcon />,
@@ -65,7 +65,7 @@ export const PROCESSING_STATUS_ICON_MAPPING: {
6565
};
6666

6767
export const PROCESSING_STATUS_LEVEL_MAPPING: {
68-
[key in DestructionListProcessingStatus]: BadgeProps["level"];
68+
[key in ProcessingStatus]: BadgeProps["level"];
6969
} = {
7070
new: "info",
7171
queued: "info",

0 commit comments

Comments
 (0)