dev: separate order by of issue queryset to separate utilty function

This commit is contained in:
pablohashescobar 2024-02-26 12:44:53 +05:30
parent b4fb9f1aa2
commit 0076b09131
5 changed files with 125 additions and 503 deletions

View File

@ -2,6 +2,7 @@
import json import json
import random import random
from itertools import chain from itertools import chain
from collections import defaultdict
# Django imports # Django imports
from django.utils import timezone from django.utils import timezone
@ -11,12 +12,7 @@ from django.db.models import (
Func, Func,
F, F,
Q, Q,
Case,
Value,
CharField,
When,
Exists, Exists,
Max,
) )
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
@ -77,7 +73,7 @@ from plane.db.models import (
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.grouper import group_results from plane.utils.grouper import group_results
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
from collections import defaultdict from plane.utils.order_queryset import order_issue_queryset
class IssueListEndpoint(BaseAPIView): class IssueListEndpoint(BaseAPIView):
@ -95,7 +91,9 @@ class IssueListEndpoint(BaseAPIView):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
issue_ids = [issue_id for issue_id in issue_ids.split(",") if issue_id != ""] issue_ids = [
issue_id for issue_id in issue_ids.split(",") if issue_id != ""
]
queryset = ( queryset = (
Issue.issue_objects.filter( Issue.issue_objects.filter(
@ -157,83 +155,21 @@ class IssueListEndpoint(BaseAPIView):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
# Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
order_by_param = request.GET.get("order_by", "-created_at") order_by_param = request.GET.get("order_by", "-created_at")
issue_queryset = queryset.filter(**filters) issue_queryset = queryset.filter(**filters)
# Priority Ordering # Issue queryset
if order_by_param == "priority" or order_by_param == "-priority": issue_queryset = order_issue_queryset(
priority_order = ( issue_queryset=issue_queryset,
priority_order order_by_param=order_by_param,
if order_by_param == "priority" )
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
if self.fields or self.expand: if self.fields or self.expand:
issues = IssueSerializer( issues = IssueSerializer(
queryset, many=True, fields=self.fields, expand=self.expand issue_queryset,
many=True,
fields=self.fields,
expand=self.expand,
).data ).data
else: else:
issues = issue_queryset.values( issues = issue_queryset.values(
@ -356,77 +292,14 @@ class IssueViewSet(WebhookMixin, BaseViewSet):
issue_queryset = self.get_queryset().filter(**filters) issue_queryset = self.get_queryset().filter(**filters)
# Custom ordering for priority and state # Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
# Priority Ordering # Issue queryset
if order_by_param == "priority" or order_by_param == "-priority": issue_queryset = order_issue_queryset(
priority_order = ( issue_queryset=issue_queryset,
priority_order order_by_param=order_by_param,
if order_by_param == "priority" )
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering if self.fields or self.expand:
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
# Only use serializer when expand or fields else return by values
if self.expand or self.fields:
issues = IssueSerializer( issues = IssueSerializer(
issue_queryset, issue_queryset,
many=True, many=True,
@ -630,15 +503,6 @@ class UserWorkSpaceIssues(BaseAPIView):
def get(self, request, slug): def get(self, request, slug):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
# Custom ordering for priority and state # Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
order_by_param = request.GET.get("order_by", "-created_at") order_by_param = request.GET.get("order_by", "-created_at")
issue_queryset = ( issue_queryset = (
@ -682,65 +546,9 @@ class UserWorkSpaceIssues(BaseAPIView):
.filter(**filters) .filter(**filters)
).distinct() ).distinct()
# Priority Ordering issue_queryset = order_issue_queryset(
if order_by_param == "priority" or order_by_param == "-priority": issue_queryset=issue_queryset, order_by_param=order_by_param
priority_order = ( )
priority_order
if order_by_param == "priority"
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
issues = IssueLiteSerializer(issue_queryset, many=True).data issues = IssueLiteSerializer(issue_queryset, many=True).data
@ -1475,79 +1283,13 @@ class IssueArchiveViewSet(BaseViewSet):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
show_sub_issues = request.GET.get("show_sub_issues", "true") show_sub_issues = request.GET.get("show_sub_issues", "true")
# Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
order_by_param = request.GET.get("order_by", "-created_at") order_by_param = request.GET.get("order_by", "-created_at")
issue_queryset = self.get_queryset().filter(**filters) issue_queryset = self.get_queryset().filter(**filters)
# Priority Ordering issue_queryset = order_issue_queryset(
if order_by_param == "priority" or order_by_param == "-priority": issue_queryset=issue_queryset, order_by_param=order_by_param
priority_order = ( )
priority_order
if order_by_param == "priority"
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
issue_queryset = ( issue_queryset = (
issue_queryset issue_queryset
@ -2152,80 +1894,12 @@ class IssueDraftViewSet(BaseViewSet):
if field if field
] ]
# Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
order_by_param = request.GET.get("order_by", "-created_at") order_by_param = request.GET.get("order_by", "-created_at")
issue_queryset = self.get_queryset().filter(**filters) issue_queryset = self.get_queryset().filter(**filters)
issue_queryset = order_issue_queryset(
# Priority Ordering issue_queryset=issue_queryset, order_by_param=order_by_param
if order_by_param == "priority" or order_by_param == "-priority": )
priority_order = (
priority_order
if order_by_param == "priority"
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
# Only use serializer when expand else return by values # Only use serializer when expand else return by values
if self.expand or self.fields: if self.expand or self.fields:
issues = IssueSerializer( issues = IssueSerializer(

View File

@ -45,6 +45,7 @@ from plane.db.models import (
IssueAttachment, IssueAttachment,
) )
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
from plane.utils.order_queryset import order_issue_queryset
class GlobalViewViewSet(BaseViewSet): class GlobalViewViewSet(BaseViewSet):
@ -142,22 +143,6 @@ class GlobalViewIssuesViewSet(BaseViewSet):
@method_decorator(gzip_page) @method_decorator(gzip_page)
def list(self, request, slug): def list(self, request, slug):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
fields = [
field
for field in request.GET.get("fields", "").split(",")
if field
]
# Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
order_by_param = request.GET.get("order_by", "-created_at") order_by_param = request.GET.get("order_by", "-created_at")
issue_queryset = ( issue_queryset = (
@ -166,66 +151,9 @@ class GlobalViewIssuesViewSet(BaseViewSet):
.filter(project__project_projectmember__member=self.request.user) .filter(project__project_projectmember__member=self.request.user)
.annotate(cycle_id=F("issue_cycle__cycle_id")) .annotate(cycle_id=F("issue_cycle__cycle_id"))
) )
issue_queryset = order_issue_queryset(
# Priority Ordering issue_queryset=issue_queryset, order_by_param=order_by_param
if order_by_param == "priority" or order_by_param == "-priority": )
priority_order = (
priority_order
if order_by_param == "priority"
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
if self.fields: if self.fields:
issues = IssueSerializer( issues = IssueSerializer(

View File

@ -74,7 +74,6 @@ from plane.db.models import (
Label, Label,
WorkspaceMember, WorkspaceMember,
CycleIssue, CycleIssue,
IssueReaction,
WorkspaceUserProperties, WorkspaceUserProperties,
Estimate, Estimate,
EstimatePoint, EstimatePoint,
@ -88,7 +87,6 @@ from plane.app.permissions import (
WorkspaceEntityPermission, WorkspaceEntityPermission,
WorkspaceViewerPermission, WorkspaceViewerPermission,
WorkspaceUserPermission, WorkspaceUserPermission,
ProjectLitePermission,
) )
from plane.bgtasks.workspace_invitation_task import workspace_invitation from plane.bgtasks.workspace_invitation_task import workspace_invitation
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
@ -1337,16 +1335,6 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
] ]
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
# Custom ordering for priority and state
priority_order = ["urgent", "high", "medium", "low", "none"]
state_order = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
order_by_param = request.GET.get("order_by", "-created_at") order_by_param = request.GET.get("order_by", "-created_at")
issue_queryset = ( issue_queryset = (
Issue.issue_objects.filter( Issue.issue_objects.filter(
@ -1411,65 +1399,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
.order_by("created_at") .order_by("created_at")
).distinct() ).distinct()
# Priority Ordering issue_queryset = order_by_param(
if order_by_param == "priority" or order_by_param == "-priority": issue_queryset=issue_queryset, order_by_param=order_by_param
priority_order = ( )
priority_order
if order_by_param == "priority"
else priority_order[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
state_order
if order_by_param in ["state__name", "state__group"]
else state_order[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
issues = IssueSerializer( issues = IssueSerializer(
issue_queryset, many=True, fields=fields if fields else None issue_queryset, many=True, fields=fields if fields else None

View File

@ -0,0 +1,80 @@
from django.db.models import (
Case,
Value,
CharField,
When,
Max,
)
# Custom ordering for priority and state
PRIORITY_ORDER = ["urgent", "high", "medium", "low", "none"]
STATE_ORDER = [
"backlog",
"unstarted",
"started",
"completed",
"cancelled",
]
def order_issue_queryset(issue_queryset, order_by_param="created_at"):
# Priority Ordering
if order_by_param == "priority" or order_by_param == "-priority":
priority_order = (
PRIORITY_ORDER
if order_by_param == "priority"
else PRIORITY_ORDER[::-1]
)
issue_queryset = issue_queryset.annotate(
priority_order=Case(
*[
When(priority=p, then=Value(i))
for i, p in enumerate(priority_order)
],
output_field=CharField(),
)
).order_by("priority_order")
# State Ordering
elif order_by_param in [
"state__name",
"state__group",
"-state__name",
"-state__group",
]:
state_order = (
STATE_ORDER
if order_by_param in ["state__name", "state__group"]
else STATE_ORDER[::-1]
)
issue_queryset = issue_queryset.annotate(
state_order=Case(
*[
When(state__group=state_group, then=Value(i))
for i, state_group in enumerate(state_order)
],
default=Value(len(state_order)),
output_field=CharField(),
)
).order_by("state_order")
# assignee and label ordering
elif order_by_param in [
"labels__name",
"-labels__name",
"assignees__first_name",
"-assignees__first_name",
]:
issue_queryset = issue_queryset.annotate(
max_values=Max(
order_by_param[1::]
if order_by_param.startswith("-")
else order_by_param
)
).order_by(
"-max_values"
if order_by_param.startswith("-")
else "max_values"
)
else:
issue_queryset = issue_queryset.order_by(order_by_param)
return issue_queryset

View File

@ -1,5 +1,5 @@
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError, ValidationError
from collections.abc import Sequence from collections.abc import Sequence
import math import math
@ -159,6 +159,13 @@ class BasePaginator:
) )
return per_page return per_page
def get_layout(self, request):
layout = request.GET.get("layout", "list")
if layout not in ["list", "kanban", "spreadsheet", "calendar", "gantt"]:
raise ValidationError(detail="Invalid layout given")
return layout
def paginate( def paginate(
self, self,
@ -175,6 +182,7 @@ class BasePaginator:
): ):
"""Paginate the request""" """Paginate the request"""
per_page = self.get_per_page(request, default_per_page, max_per_page) per_page = self.get_per_page(request, default_per_page, max_per_page)
layout = self.get_layout(request=request)
# Convert the cursor value to integer and float from string # Convert the cursor value to integer and float from string
input_cursor = None input_cursor = None