0
0
mirror of https://github.com/makeplane/plane synced 2024-06-14 14:31:34 +00:00

chore: remove unwanted fields from cycles api

This commit is contained in:
pablohashescobar 2024-02-15 13:01:50 +05:30
parent d901277a20
commit 326d59d769
3 changed files with 157 additions and 137 deletions
apiserver/plane/app
serializers
views

View File

@ -3,10 +3,7 @@ from rest_framework import serializers
# Module imports # Module imports
from .base import BaseSerializer from .base import BaseSerializer
from .user import UserLiteSerializer
from .issue import IssueStateSerializer from .issue import IssueStateSerializer
from .workspace import WorkspaceLiteSerializer
from .project import ProjectLiteSerializer
from plane.db.models import ( from plane.db.models import (
Cycle, Cycle,
CycleIssue, CycleIssue,
@ -14,7 +11,6 @@ from plane.db.models import (
CycleUserProperties, CycleUserProperties,
) )
class CycleWriteSerializer(BaseSerializer): class CycleWriteSerializer(BaseSerializer):
def validate(self, data): def validate(self, data):
if ( if (
@ -33,62 +29,79 @@ class CycleWriteSerializer(BaseSerializer):
class CycleSerializer(BaseSerializer): class CycleSerializer(BaseSerializer):
# workspace and project ids
workspace_id = serializers.PrimaryKeyRelatedField(read_only=True)
project_id = serializers.PrimaryKeyRelatedField(read_only=True)
owned_by_id = serializers.PrimaryKeyRelatedField(read_only=True)
# favorite
is_favorite = serializers.BooleanField(read_only=True) is_favorite = serializers.BooleanField(read_only=True)
total_issues = serializers.IntegerField(read_only=True) total_issues = serializers.IntegerField(read_only=True)
# state group wise distribution
cancelled_issues = serializers.IntegerField(read_only=True) cancelled_issues = serializers.IntegerField(read_only=True)
completed_issues = serializers.IntegerField(read_only=True) completed_issues = serializers.IntegerField(read_only=True)
started_issues = serializers.IntegerField(read_only=True) started_issues = serializers.IntegerField(read_only=True)
unstarted_issues = serializers.IntegerField(read_only=True) unstarted_issues = serializers.IntegerField(read_only=True)
backlog_issues = serializers.IntegerField(read_only=True) backlog_issues = serializers.IntegerField(read_only=True)
assignees = serializers.SerializerMethodField(read_only=True) # estimates
total_estimates = serializers.IntegerField(read_only=True) total_estimates = serializers.IntegerField(read_only=True)
completed_estimates = serializers.IntegerField(read_only=True) completed_estimates = serializers.IntegerField(read_only=True)
started_estimates = serializers.IntegerField(read_only=True) started_estimates = serializers.IntegerField(read_only=True)
workspace_detail = WorkspaceLiteSerializer( # method fields
read_only=True, source="workspace" assignees = serializers.SerializerMethodField(read_only=True)
)
project_detail = ProjectLiteSerializer(read_only=True, source="project") # active | draft | upcoming | completed
status = serializers.CharField(read_only=True) status = serializers.CharField(read_only=True)
def validate(self, data):
if (
data.get("start_date", None) is not None
and data.get("end_date", None) is not None
and data.get("start_date", None) > data.get("end_date", None)
):
raise serializers.ValidationError(
"Start date cannot exceed end date"
)
return data
def get_assignees(self, obj): def get_assignees(self, obj):
# Get all the members
members = [ members = [
{ {
"avatar": assignee.avatar,
"display_name": assignee.display_name,
"id": assignee.id, "id": assignee.id,
"display_name": assignee.display_name,
"avatar": assignee.avatar,
} }
for issue_cycle in obj.issue_cycle.prefetch_related( for issue_cycle in obj.issue_cycle.prefetch_related(
"issue__assignees" "issue__assignees"
).all() ).all()
for assignee in issue_cycle.issue.assignees.all() for assignee in issue_cycle.issue.assignees.all()
] ]
# Use a set comprehension to return only the unique objects
unique_objects = {frozenset(item.items()) for item in members}
# Convert the set back to a list of dictionaries # Convert the set back to a list of dictionaries
unique_list = [dict(item) for item in unique_objects] unique_list = [dict(item) for item in {frozenset(item.items()) for item in members}]
return unique_list return unique_list
class Meta: class Meta:
model = Cycle model = Cycle
fields = "__all__" fields = [
read_only_fields = [ # necessary fields
"workspace", "id",
"project", "workspace_id",
"owned_by", "project_id",
# model fields
"description",
"start_date",
"end_date",
"owned_by_id",
"view_props",
"sort_order",
"external_source",
"external_id",
"progress_snapshot",
# meta fields
"is_favorite",
"total_issues",
"cancelled_issues",
"completed_issues",
"started_issues",
"unstarted_issues",
"backlog_issues",
"total_estimates",
"completed_estimates",
"started_estimates",
"assignees",
"status",
] ]
read_only_fields = fields
class CycleIssueSerializer(BaseSerializer): class CycleIssueSerializer(BaseSerializer):

View File

@ -577,9 +577,6 @@ class IssueSerializer(DynamicBaseSerializer):
attachment_count = serializers.IntegerField(read_only=True) attachment_count = serializers.IntegerField(read_only=True)
link_count = serializers.IntegerField(read_only=True) link_count = serializers.IntegerField(read_only=True)
# is_subscribed
is_subscribed = serializers.BooleanField(read_only=True)
class Meta: class Meta:
model = Issue model = Issue
fields = [ fields = [
@ -606,7 +603,6 @@ class IssueSerializer(DynamicBaseSerializer):
"updated_by", "updated_by",
"attachment_count", "attachment_count",
"link_count", "link_count",
"is_subscribed",
"is_draft", "is_draft",
"archived_at", "archived_at",
] ]
@ -614,7 +610,7 @@ class IssueSerializer(DynamicBaseSerializer):
def get_module_ids(self, obj): def get_module_ids(self, obj):
# Access the prefetched modules and extract module IDs # Access the prefetched modules and extract module IDs
return [module for module in obj.issue_module.values_list("module_id", flat=True)] return obj.issue_module.values_list("module_id", flat=True)
class IssueDetailSerializer(IssueSerializer): class IssueDetailSerializer(IssueSerializer):

View File

@ -33,7 +33,6 @@ from plane.app.serializers import (
CycleIssueSerializer, CycleIssueSerializer,
CycleFavoriteSerializer, CycleFavoriteSerializer,
IssueSerializer, IssueSerializer,
IssueStateSerializer,
CycleWriteSerializer, CycleWriteSerializer,
CycleUserPropertiesSerializer, CycleUserPropertiesSerializer,
) )
@ -51,7 +50,6 @@ from plane.db.models import (
IssueAttachment, IssueAttachment,
Label, Label,
CycleUserProperties, CycleUserProperties,
IssueSubscriber,
) )
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
@ -73,7 +71,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
) )
def get_queryset(self): def get_queryset(self):
subquery = CycleFavorite.objects.filter( favorite_subquery = CycleFavorite.objects.filter(
user=self.request.user, user=self.request.user,
cycle_id=OuterRef("pk"), cycle_id=OuterRef("pk"),
project_id=self.kwargs.get("project_id"), project_id=self.kwargs.get("project_id"),
@ -85,10 +83,24 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
.filter(workspace__slug=self.kwargs.get("slug")) .filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id")) .filter(project_id=self.kwargs.get("project_id"))
.filter(project__project_projectmember__member=self.request.user) .filter(project__project_projectmember__member=self.request.user)
.select_related("project") .select_related("project", "workspace", "owned_by")
.select_related("workspace") .prefetch_related(
.select_related("owned_by") Prefetch(
.annotate(is_favorite=Exists(subquery)) "issue_cycle__issue__assignees",
queryset=User.objects.only(
"avatar", "first_name", "id"
).distinct(),
)
)
.prefetch_related(
Prefetch(
"issue_cycle__issue__labels",
queryset=Label.objects.only(
"name", "color", "id"
).distinct(),
)
)
.annotate(is_favorite=Exists(favorite_subquery))
.annotate( .annotate(
total_issues=Count( total_issues=Count(
"issue_cycle", "issue_cycle",
@ -190,22 +202,6 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
output_field=CharField(), output_field=CharField(),
) )
) )
.prefetch_related(
Prefetch(
"issue_cycle__issue__assignees",
queryset=User.objects.only(
"avatar", "first_name", "id"
).distinct(),
)
)
.prefetch_related(
Prefetch(
"issue_cycle__issue__labels",
queryset=Label.objects.only(
"name", "color", "id"
).distinct(),
)
)
.order_by("-is_favorite", "name") .order_by("-is_favorite", "name")
.distinct() .distinct()
) )
@ -213,12 +209,8 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
queryset = self.get_queryset() queryset = self.get_queryset()
cycle_view = request.GET.get("cycle_view", "all") cycle_view = request.GET.get("cycle_view", "all")
fields = [
field
for field in request.GET.get("fields", "").split(",")
if field
]
# Update the order by
queryset = queryset.order_by("-is_favorite", "-created_at") queryset = queryset.order_by("-is_favorite", "-created_at")
# Current Cycle # Current Cycle
@ -230,7 +222,7 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
data = CycleSerializer(queryset, many=True).data data = CycleSerializer(queryset, many=True).data
if len(data): if data:
assignee_distribution = ( assignee_distribution = (
Issue.objects.filter( Issue.objects.filter(
issue_cycle__cycle_id=data[0]["id"], issue_cycle__cycle_id=data[0]["id"],
@ -315,13 +307,13 @@ class CycleViewSet(WebhookMixin, BaseViewSet):
} }
if data[0]["start_date"] and data[0]["end_date"]: if data[0]["start_date"] and data[0]["end_date"]:
data[0]["distribution"][ data[0]["distribution"]["completion_chart"] = (
"completion_chart" burndown_plot(
] = burndown_plot( queryset=queryset.first(),
queryset=queryset.first(), slug=slug,
slug=slug, project_id=project_id,
project_id=project_id, cycle_id=data[0]["id"],
cycle_id=data[0]["id"], )
) )
return Response(data, status=status.HTTP_200_OK) return Response(data, status=status.HTTP_200_OK)
@ -591,20 +583,13 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
issues = ( issues = (
Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id) Issue.issue_objects.filter(issue_cycle__cycle_id=cycle_id)
.annotate(
sub_issues_count=Issue.issue_objects.filter(
parent=OuterRef("id")
)
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.filter(project_id=project_id) .filter(project_id=project_id)
.filter(workspace__slug=slug) .filter(workspace__slug=slug)
.select_related("workspace", "project", "state", "parent") .select_related("workspace", "project", "state", "parent")
.prefetch_related("assignees", "labels", "issue_module__module") .prefetch_related("assignees", "labels", "issue_module__module", "issue_cycle__cycle")
.order_by(order_by) .order_by(order_by)
.filter(**filters) .filter(**filters)
.annotate(module_ids=F("issue_module__module_id"))
.annotate(cycle_id=F("issue_cycle__cycle_id")) .annotate(cycle_id=F("issue_cycle__cycle_id"))
.annotate( .annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id")) link_count=IssueLink.objects.filter(issue=OuterRef("id"))
@ -621,11 +606,12 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
.values("count") .values("count")
) )
.annotate( .annotate(
is_subscribed=Exists( sub_issues_count=Issue.issue_objects.filter(
IssueSubscriber.objects.filter( parent=OuterRef("id")
subscriber=self.request.user, issue_id=OuterRef("id")
)
) )
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
) )
) )
serializer = IssueSerializer( serializer = IssueSerializer(
@ -636,7 +622,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
def create(self, request, slug, project_id, cycle_id): def create(self, request, slug, project_id, cycle_id):
issues = request.data.get("issues", []) issues = request.data.get("issues", [])
if not len(issues): if not issues:
return Response( return Response(
{"error": "Issues are required"}, {"error": "Issues are required"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
@ -659,51 +645,47 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
# Get all CycleIssues already created # Get all CycleIssues already created
cycle_issues = list(CycleIssue.objects.filter(issue_id__in=issues)) cycle_issues = list(CycleIssue.objects.filter(issue_id__in=issues))
update_cycle_issue_activity = [] existing_issues = [
record_to_create = [] str(cycle_issue.issue_id) for cycle_issue in cycle_issues
records_to_update = [] ]
new_issues = list(set(issues) - set(existing_issues))
for issue in issues: # New issues to create
cycle_issue = [ created_records = CycleIssue.objects.bulk_create(
cycle_issue [
for cycle_issue in cycle_issues CycleIssue(
if str(cycle_issue.issue_id) in issues project_id=project_id,
] workspace_id=cycle.workspace_id,
# Update only when cycle changes created_by_id=request.user.id,
if len(cycle_issue): updated_by_id=request.user.id,
if cycle_issue[0].cycle_id != cycle_id: cycle_id=cycle_id,
update_cycle_issue_activity.append( issue_id=issue,
{
"old_cycle_id": str(cycle_issue[0].cycle_id),
"new_cycle_id": str(cycle_id),
"issue_id": str(cycle_issue[0].issue_id),
}
)
cycle_issue[0].cycle_id = cycle_id
records_to_update.append(cycle_issue[0])
else:
record_to_create.append(
CycleIssue(
project_id=project_id,
workspace=cycle.workspace,
created_by=request.user,
updated_by=request.user,
cycle=cycle,
issue_id=issue,
)
) )
for issue in new_issues
CycleIssue.objects.bulk_create( ],
record_to_create,
batch_size=10,
ignore_conflicts=True,
)
CycleIssue.objects.bulk_update(
records_to_update,
["cycle"],
batch_size=10, batch_size=10,
) )
# Updated Issues
updated_records = []
update_cycle_issue_activity = []
# Iterate over each cycle_issue in cycle_issues
for cycle_issue in [
ci for ci in cycle_issues if str(ci.cycle_id) != str(cycle_id)
]:
# Update the cycle_issue's cycle_id
cycle_issue.cycle_id = cycle_id
# Add the modified cycle_issue to the records_to_update list
updated_records.append(cycle_issue)
# Record the update activity
update_cycle_issue_activity.append(
{
"old_cycle_id": str(cycle_issue.cycle_id),
"new_cycle_id": str(cycle_id),
"issue_id": str(cycle_issue.issue_id),
}
)
# Capture Issue Activity # Capture Issue Activity
issue_activity.delay( issue_activity.delay(
type="cycle.activity.created", type="cycle.activity.created",
@ -715,7 +697,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
{ {
"updated_cycle_issues": update_cycle_issue_activity, "updated_cycle_issues": update_cycle_issue_activity,
"created_cycle_issues": serializers.serialize( "created_cycle_issues": serializers.serialize(
"json", record_to_create "json", created_records
), ),
} }
), ),
@ -723,14 +705,39 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
notification=True, notification=True,
origin=request.META.get("HTTP_ORIGIN"), origin=request.META.get("HTTP_ORIGIN"),
) )
# Return all Cycle Issues # Get all the issues for cycle
issues = self.get_queryset().values_list("issue_id", flat=True) issue_queryset = (
Issue.issue_objects.filter(pk__in=self.get_queryset().values_list("issue_id", flat=True))
.filter(workspace__slug=self.kwargs.get("slug"))
.select_related("workspace", "project", "state", "parent")
.prefetch_related("assignees", "labels", "issue_module__module")
.annotate(cycle_id=F("issue_cycle__cycle_id"))
.annotate(
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.annotate(
attachment_count=IssueAttachment.objects.filter(
issue=OuterRef("id")
)
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
.annotate(
sub_issues_count=Issue.issue_objects.filter(
parent=OuterRef("id")
)
.order_by()
.annotate(count=Func(F("id"), function="Count"))
.values("count")
)
)
return Response( return Response(
IssueSerializer( IssueSerializer(issue_queryset, many=True).data,
Issue.objects.filter(pk__in=issues), many=True
).data,
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
@ -1023,7 +1030,9 @@ class TransferCycleIssueEndpoint(BaseAPIView):
assignee_distribution_data = [ assignee_distribution_data = [
{ {
"display_name": item["display_name"], "display_name": item["display_name"],
"assignee_id": str(item["assignee_id"]) if item["assignee_id"] else None, "assignee_id": (
str(item["assignee_id"]) if item["assignee_id"] else None
),
"avatar": item["avatar"], "avatar": item["avatar"],
"total_issues": item["total_issues"], "total_issues": item["total_issues"],
"completed_issues": item["completed_issues"], "completed_issues": item["completed_issues"],
@ -1036,7 +1045,9 @@ class TransferCycleIssueEndpoint(BaseAPIView):
{ {
"label_name": item["label_name"], "label_name": item["label_name"],
"color": item["color"], "color": item["color"],
"label_id": str(item["label_id"]) if item["label_id"] else None, "label_id": (
str(item["label_id"]) if item["label_id"] else None
),
"total_issues": item["total_issues"], "total_issues": item["total_issues"],
"completed_issues": item["completed_issues"], "completed_issues": item["completed_issues"],
"pending_issues": item["pending_issues"], "pending_issues": item["pending_issues"],
@ -1058,7 +1069,7 @@ class TransferCycleIssueEndpoint(BaseAPIView):
"total_estimates": old_cycle.first().total_estimates, "total_estimates": old_cycle.first().total_estimates,
"completed_estimates": old_cycle.first().completed_estimates, "completed_estimates": old_cycle.first().completed_estimates,
"started_estimates": old_cycle.first().started_estimates, "started_estimates": old_cycle.first().started_estimates,
"distribution":{ "distribution": {
"labels": label_distribution_data, "labels": label_distribution_data,
"assignees": assignee_distribution_data, "assignees": assignee_distribution_data,
"completion_chart": completion_chart, "completion_chart": completion_chart,