From 2efa21e8f4f195950ce46a73093ff20e04d1a39a Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Mon, 26 Feb 2024 14:26:37 +0530 Subject: [PATCH] dev: pagination for spreadhseet and gantt --- apiserver/plane/app/views/cycle.py | 27 +++++-- apiserver/plane/app/views/issue.py | 111 ++++++++++++++++++---------- apiserver/plane/app/views/module.py | 24 ++++-- apiserver/plane/app/views/view.py | 24 ++++-- apiserver/plane/utils/paginator.py | 31 ++++---- 5 files changed, 142 insertions(+), 75 deletions(-) diff --git a/apiserver/plane/app/views/cycle.py b/apiserver/plane/app/views/cycle.py index 866396655..35b1fc927 100644 --- a/apiserver/plane/app/views/cycle.py +++ b/apiserver/plane/app/views/cycle.py @@ -708,7 +708,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): ] order_by = request.GET.get("order_by", "created_at") filters = issue_filters(request.query_params, "GET") - queryset = ( + issue_queryset = ( Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id) .filter(project_id=project_id) .filter(workspace__slug=slug) @@ -773,12 +773,12 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): ) .order_by(order_by) ) - if self.fields: - issues = IssueSerializer( - queryset, many=True, fields=fields if fields else None - ).data - else: - issues = queryset.values( + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -805,7 +805,18 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) + def create(self, request, slug, project_id, cycle_id): issues = request.data.get("issues", []) diff --git a/apiserver/plane/app/views/issue.py b/apiserver/plane/app/views/issue.py index daa093c8c..7076840af 100644 --- a/apiserver/plane/app/views/issue.py +++ b/apiserver/plane/app/views/issue.py @@ -164,15 +164,12 @@ class IssueListEndpoint(BaseAPIView): order_by_param=order_by_param, ) - if self.fields or self.expand: - issues = IssueSerializer( - issue_queryset, - many=True, - fields=self.fields, - expand=self.expand, - ).data - else: - issues = issue_queryset.values( + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -199,7 +196,17 @@ class IssueListEndpoint(BaseAPIView): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) class IssueViewSet(WebhookMixin, BaseViewSet): @@ -299,15 +306,12 @@ class IssueViewSet(WebhookMixin, BaseViewSet): order_by_param=order_by_param, ) - if self.fields or self.expand: - issues = IssueSerializer( - issue_queryset, - many=True, - fields=self.fields, - expand=self.expand, - ).data - else: - issues = issue_queryset.values( + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -334,7 +338,18 @@ class IssueViewSet(WebhookMixin, BaseViewSet): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) + def create(self, request, slug, project_id): project = Project.objects.get(pk=project_id) @@ -1296,14 +1311,13 @@ class IssueArchiveViewSet(BaseViewSet): if show_sub_issues == "true" else issue_queryset.filter(parent__isnull=True) ) - if self.expand or self.fields: - issues = IssueSerializer( - issue_queryset, - many=True, - fields=self.fields, - ).data - else: - issues = issue_queryset.values( + + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -1330,7 +1344,18 @@ class IssueArchiveViewSet(BaseViewSet): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) + def retrieve(self, request, slug, project_id, pk=None): issue = ( @@ -1900,16 +1925,12 @@ class IssueDraftViewSet(BaseViewSet): issue_queryset = order_issue_queryset( issue_queryset=issue_queryset, order_by_param=order_by_param ) - # Only use serializer when expand else return by values - if self.expand or self.fields: - issues = IssueSerializer( - issue_queryset, - many=True, - fields=self.fields, - expand=self.expand, - ).data - else: - issues = issue_queryset.values( + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -1936,7 +1957,17 @@ class IssueDraftViewSet(BaseViewSet): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) def create(self, request, slug, project_id): project = Project.objects.get(pk=project_id) diff --git a/apiserver/plane/app/views/module.py b/apiserver/plane/app/views/module.py index 5ac244dda..1d4b21cae 100644 --- a/apiserver/plane/app/views/module.py +++ b/apiserver/plane/app/views/module.py @@ -510,12 +510,12 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): ] filters = issue_filters(request.query_params, "GET") issue_queryset = self.get_queryset().filter(**filters) - if self.fields or self.expand: - issues = IssueSerializer( - issue_queryset, many=True, fields=fields if fields else None - ).data - else: - issues = issue_queryset.values( + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -542,7 +542,17 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) # create multiple issues inside a module def create_module_issues(self, request, slug, project_id, module_id): diff --git a/apiserver/plane/app/views/view.py b/apiserver/plane/app/views/view.py index 91829cf3b..6c3d6476f 100644 --- a/apiserver/plane/app/views/view.py +++ b/apiserver/plane/app/views/view.py @@ -155,12 +155,12 @@ class GlobalViewIssuesViewSet(BaseViewSet): issue_queryset=issue_queryset, order_by_param=order_by_param ) - if self.fields: - issues = IssueSerializer( - issue_queryset, many=True, fields=self.fields - ).data - else: - issues = issue_queryset.values( + def on_results(issues): + if self.expand or self.fields: + return IssueSerializer( + issues, many=True, expand=self.expand, fields=self.fields + ).data + return issues.values( "id", "name", "state_id", @@ -187,7 +187,17 @@ class GlobalViewIssuesViewSet(BaseViewSet): "is_draft", "archived_at", ) - return Response(issues, status=status.HTTP_200_OK) + + if request.GET.get("layout", "spreadsheet") in [ + "layout", + "spreadsheet", + ]: + return self.paginate( + request=request, + queryset=issue_queryset, + on_results=on_results + ) + return on_results(issues=issue_queryset) class IssueViewViewSet(BaseViewSet): diff --git a/apiserver/plane/utils/paginator.py b/apiserver/plane/utils/paginator.py index cfeb49363..e84f98a78 100644 --- a/apiserver/plane/utils/paginator.py +++ b/apiserver/plane/utils/paginator.py @@ -116,14 +116,15 @@ class OffsetPaginator: if offset < 0: raise BadPaginationError("Pagination offset cannot be negative") - results = list(queryset[offset:stop]) + results = queryset[offset:stop] + if cursor.value != limit: results = results[-(limit + 1) :] - next_cursor = Cursor(limit, page + 1, False, len(results) > limit) + next_cursor = Cursor(limit, page + 1, False, results.count() > limit) prev_cursor = Cursor(limit, page - 1, True, page > 0) - results = list(results[:limit]) + results = results[:limit] if self.on_results: results = self.on_results(results) @@ -159,14 +160,19 @@ class BasePaginator: ) return per_page - + def get_layout(self, request): layout = request.GET.get("layout", "list") - if layout not in ["list", "kanban", "spreadsheet", "calendar", "gantt"]: + if layout not in [ + "list", + "kanban", + "spreadsheet", + "calendar", + "gantt", + ]: raise ValidationError(detail="Invalid layout given") return layout - def paginate( self, request, @@ -186,13 +192,12 @@ class BasePaginator: # Convert the cursor value to integer and float from string input_cursor = None - if request.GET.get(self.cursor_name): - try: - input_cursor = cursor_cls.from_string( - request.GET.get(self.cursor_name) - ) - except ValueError: - raise ParseError(detail="Invalid cursor parameter.") + try: + input_cursor = cursor_cls.from_string( + request.GET.get(self.cursor_name, f"{per_page}:0:0"), + ) + except ValueError: + raise ParseError(detail="Invalid cursor parameter.") if not paginator: paginator = paginator_cls(**paginator_kwargs)