diff --git a/api/tests/v1/get/test_api_get.py b/api/tests/v1/get/test_api_get.py index 38af864b..3c464542 100644 --- a/api/tests/v1/get/test_api_get.py +++ b/api/tests/v1/get/test_api_get.py @@ -36,7 +36,12 @@ class APIV1GetTestCase(BaseAPIV1TestCase): """ response = self._run_get_request(url) content = json.loads(response.content) - geojson = content[str(obj.id)] + self.assertIn("rpp", content) + self.assertIn("p", content) + self.assertIn("next", content) + self.assertIn("results", content) + paginated_content = content["results"] + geojson = paginated_content[str(obj.id)] self.assertEqual(response.status_code, 200, msg=response.content) return geojson diff --git a/api/utils/serializer/serializer.py b/api/utils/serializer/serializer.py index 443e10c5..8d618e44 100644 --- a/api/utils/serializer/serializer.py +++ b/api/utils/serializer/serializer.py @@ -10,6 +10,7 @@ from abc import abstractmethod from django.contrib.gis import geos from django.contrib.gis.geos import GEOSGeometry +from django.core.paginator import Paginator from konova.utils.message_templates import DATA_UNSHARED @@ -19,6 +20,10 @@ class AbstractModelAPISerializer: lookup = None properties_data = None + rpp = None + page_number = None + paginator = None + class Meta: abstract = True @@ -80,9 +85,12 @@ class AbstractModelAPISerializer: Returns: serialized_data (dict) """ - entries = self.model.objects.filter(**self.lookup) + entries = self.model.objects.filter(**self.lookup).order_by("id") + self.paginator = Paginator(entries, self.rpp) + requested_entries = self.paginator.page(self.page_number) + serialized_data = {} - for entry in entries: + for entry in requested_entries.object_list: serialized_data[str(entry.id)] = self._model_to_geo_json(entry) return serialized_data diff --git a/api/views/v1/views.py b/api/views/v1/views.py index 7789680f..8da5d49e 100644 --- a/api/views/v1/views.py +++ b/api/views/v1/views.py @@ -21,7 +21,6 @@ class AbstractAPIViewV1(AbstractAPIView): """ Holds general serialization functions for API v1 """ - serializer = None def __init__(self, *args, **kwargs): self.lookup = { @@ -45,11 +44,17 @@ class AbstractAPIViewV1(AbstractAPIView): response (JsonResponse) """ try: + self.rpp = int(request.GET.get("rpp", self.rpp)) + self.page_number = int(request.GET.get("p", self.page_number)) + + self.serializer.rpp = self.rpp + self.serializer.page_number = self.page_number + self.serializer.prepare_lookup(id, self.user) data = self.serializer.fetch_and_serialize() except Exception as e: - return self.return_error_response(e, 500) - return JsonResponse(data) + return self._return_error_response(e, 500) + return self._return_response(request, data) def post(self, request: HttpRequest): """ Handles the POST request @@ -67,7 +72,7 @@ class AbstractAPIViewV1(AbstractAPIView): body = json.loads(body) created_id = self.serializer.create_model_from_json(body, self.user) except Exception as e: - return self.return_error_response(e, 500) + return self._return_error_response(e, 500) return JsonResponse({"id": created_id}) def put(self, request: HttpRequest, id=None): @@ -87,7 +92,7 @@ class AbstractAPIViewV1(AbstractAPIView): body = json.loads(body) updated_id = self.serializer.update_model_from_json(id, body, self.user) except Exception as e: - return self.return_error_response(e, 500) + return self._return_error_response(e, 500) return JsonResponse({"id": updated_id}) def delete(self, request: HttpRequest, id=None): @@ -104,7 +109,7 @@ class AbstractAPIViewV1(AbstractAPIView): try: success = self.serializer.delete_entry(id, self.user) except Exception as e: - return self.return_error_response(e, 500) + return self._return_error_response(e, 500) return JsonResponse( { "success": success, diff --git a/api/views/views.py b/api/views/views.py index fb4f6df1..35c54fee 100644 --- a/api/views/views.py +++ b/api/views/views.py @@ -31,10 +31,22 @@ class AbstractAPIView(View): """ user = None + serializer = None + rpp = 5 # Results per page default + page_number = 1 # Page number default class Meta: abstract = True + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.response_body_base = { + "rpp": None, + "p": None, + "next": None, + "results": None + } + @csrf_exempt def dispatch(self, request, *args, **kwargs): try: @@ -42,13 +54,14 @@ class AbstractAPIView(View): ksp_token = request.headers.get(KSP_TOKEN_HEADER_IDENTIFIER, None) ksp_user = request.headers.get(KSP_USER_HEADER_IDENTIFIER, None) self.user = APIUserToken.get_user_from_token(ksp_token, ksp_user) + request.user = self.user if not self.user.is_default_user(): raise PermissionError("Default permissions required") except PermissionError as e: - return self.return_error_response(e, 403) + return self._return_error_response(e, 403) return super().dispatch(request, *args, **kwargs) - def return_error_response(self, error, status_code=500): + def _return_error_response(self, error, status_code=500): """ Returns an error as JsonReponse Args: @@ -68,6 +81,31 @@ class AbstractAPIView(View): status=status_code ) + def _return_response(self, request: HttpRequest, data): + """ Returns all important data into a response object + + Args: + request (HttpRequest): The incoming request + data (dict): The serialized data + + Returns: + response (JsonResponse): The response to be returned + """ + response = self.response_body_base + next_page = self.page_number + 1 + next_page = next_page if next_page in self.serializer.paginator.page_range else None + if next_page is not None: + next_url = request.build_absolute_uri( + request.path + f"?rpp={self.rpp}&p={next_page}" + ) + else: + next_url = None + response["rpp"] = self.rpp + response["p"] = self.page_number + response["next"] = next_url + response["results"] = data + return JsonResponse(response) + class InterventionCheckAPIView(AbstractAPIView): @@ -82,14 +120,14 @@ class InterventionCheckAPIView(AbstractAPIView): response (JsonResponse) """ if not self.user.is_zb_user(): - return self.return_error_response("Permission not granted", 403) + return self._return_error_response("Permission not granted", 403) try: obj = Intervention.objects.get( id=id, users__in=[self.user] ) except Exception as e: - return self.return_error_response(e) + return self._return_error_response(e) all_valid, check_details = self.run_quality_checks(obj) @@ -161,7 +199,7 @@ class AbstractModelShareAPIView(AbstractAPIView): try: users = self._get_shared_users_of_object(id) except Exception as e: - return self.return_error_response(e) + return self._return_error_response(e) data = { "users": [ @@ -185,7 +223,7 @@ class AbstractModelShareAPIView(AbstractAPIView): try: success = self._process_put_body(request.body, id) except Exception as e: - return self.return_error_response(e) + return self._return_error_response(e) data = { "success": success, }