Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion assets
Submodule assets updated 1 files
+7 −0 openapi-schema.yaml
41 changes: 37 additions & 4 deletions dref/test_dref3_filters.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime, timedelta

from django.contrib.auth import get_user_model
from django.utils import timezone
from rest_framework import status

from api.models import Country, Region, RegionName
Expand Down Expand Up @@ -145,14 +146,46 @@ def test_pagination(self):
self.authenticate(self.superuser)
full = self.client.get(self.url)
self.assertEqual(full.status_code, status.HTTP_200_OK)
total = len(full.json())
all_rows = full.json()
total_codes = {row["appeal_id"] for row in all_rows}
page1 = self.client.get(self.url, {"limit": 1, "offset": 0})
self.assertEqual(page1.status_code, status.HTTP_200_OK)
assert len(page1.json()) == 1
if total > 1:
page1_rows = page1.json()
assert len({row["appeal_id"] for row in page1_rows}) == 1
if len(total_codes) > 1:
page2 = self.client.get(self.url, {"limit": 1, "offset": 1})
self.assertEqual(page2.status_code, status.HTTP_200_OK)
assert len(page2.json()) == 1
page2_rows = page2.json()
assert len({row["appeal_id"] for row in page2_rows}) == 1
assert {row["appeal_id"] for row in page2_rows} != {row["appeal_id"] for row in page1_rows}

def test_pagination_order_by_created_at(self):
self.authenticate(self.superuser)
now = timezone.now()
self.dref_a.created_at = now - timedelta(days=2)
self.dref_a.save(update_fields=["created_at"])
self.dref_b.created_at = now - timedelta(days=1)
self.dref_b.save(update_fields=["created_at"])

page1 = self.client.get(self.url, {"limit": 1, "offset": 0, "order_by": "created_at"})
self.assertEqual(page1.status_code, status.HTTP_200_OK)
page1_codes = {row["appeal_id"] for row in page1.json()}
assert page1_codes == {"APPEAL_A"}

page2 = self.client.get(self.url, {"limit": 1, "offset": 1, "order_by": "created_at"})
self.assertEqual(page2.status_code, status.HTTP_200_OK)
page2_codes = {row["appeal_id"] for row in page2.json()}
assert page2_codes == {"APPEAL_B"}

page1_desc = self.client.get(self.url, {"limit": 1, "offset": 0, "order_by": "-created_at"})
self.assertEqual(page1_desc.status_code, status.HTTP_200_OK)
page1_desc_codes = {row["appeal_id"] for row in page1_desc.json()}
assert page1_desc_codes == {"APPEAL_B"}

page2_desc = self.client.get(self.url, {"limit": 1, "offset": 1, "order_by": "-created_at"})
self.assertEqual(page2_desc.status_code, status.HTTP_200_OK)
page2_desc_codes = {row["appeal_id"] for row in page2_desc.json()}
assert page2_desc_codes == {"APPEAL_A"}

def test_export_csv(self):
self.authenticate(self.superuser)
Expand Down
68 changes: 52 additions & 16 deletions dref/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from django.http import HttpResponse
from django.templatetags.static import static
from django.utils.translation import gettext
from drf_spectacular.utils import extend_schema
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework import (
mixins,
permissions,
Expand Down Expand Up @@ -464,6 +464,52 @@ def _status_to_int(self, raw):
name_map = {s.name.lower(): s.value for s in Dref.Status}
return label_map.get(str(raw).lower()) or name_map.get(str(raw).lower())

def _order_codes(self, codes, request):
order_by = request.query_params.get("order_by")
if order_by not in ("created_at", "-created_at"):
return sorted(codes)

created_map = {
row["appeal_code"]: row["first_created_at"]
for row in Dref.objects.filter(appeal_code__in=codes)
.values("appeal_code")
.annotate(first_created_at=models.Min("created_at"))
}

present = [code for code in codes if created_map.get(code) is not None]
missing = sorted([code for code in codes if created_map.get(code) is None])
present_sorted = sorted(present, key=lambda code: (created_map.get(code), code), reverse=order_by == "-created_at")
return present_sorted + missing

def _paginate_codes(self, codes, request):
try:
limit = int(request.query_params.get("limit")) if request.query_params.get("limit") else None
except ValueError:
limit = None
try:
offset = int(request.query_params.get("offset")) if request.query_params.get("offset") else 0
except ValueError:
offset = 0

if not offset and limit is None:
return codes

end = offset + limit if limit is not None else None
return codes[offset:end]

@extend_schema(
parameters=[
OpenApiParameter(
name="order_by",
description=(
"Ordering for paged appeal codes. Use 'created_at' or '-created_at' to sort by the first "
"DREF application created_at per appeal_code; any other value defaults to appeal_code ordering."
),
required=False,
type=str,
)
]
)
def list(self, request):
# === First approach – would be nice to work like this, but recent definitons are more complex than that:
# # Aggregate all appeal-codes from the three models
Expand Down Expand Up @@ -564,7 +610,7 @@ def list(self, request):
combined = set()
for s in codes_sets:
combined.update([c for c in s if c])
codes = sorted(combined)
codes = list(combined)

# Additional date range filters (applied to root Dref only where fields exist)
date_range_fields = [
Expand Down Expand Up @@ -592,6 +638,9 @@ def list(self, request):
if excluded_codes:
codes = [c for c in codes if c and c.upper() not in excluded_codes]

codes = self._order_codes(codes, request)
codes = self._paginate_codes(codes, request)

data = []
old_kwargs = getattr(self, "kwargs", {}).copy()
self.kwargs = {self.lookup_field: codes}
Expand Down Expand Up @@ -630,20 +679,7 @@ def list(self, request):
if id_param:
if wanted_ids := {i.strip() for i in str(id_param).split(",")}:
data = [row for row in data if row.get("id") in wanted_ids]
# pagination
try:
limit = int(request.query_params.get("limit")) if request.query_params.get("limit") else None
except ValueError:
limit = None
try:
offset = int(request.query_params.get("offset")) if request.query_params.get("offset") else 0
except ValueError:
offset = 0
if offset or limit is not None:
end = offset + limit if limit is not None else None
data_paginated = data[offset:end]
else:
data_paginated = data
data_paginated = data

export_param = request.query_params.get("export")
if export_param and export_param.lower() == "csv":
Expand Down
Loading