From 2fad4e85b6323dea0d47dcd57c5d5ae20f16d4e3 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 22 Feb 2024 15:59:16 +0530 Subject: [PATCH] fix: active cycles be fixes --- apiserver/plane/app/serializers/__init__.py | 6 +- apiserver/plane/app/serializers/cycle.py | 50 ++++++++++++++++ apiserver/plane/app/views/cycle.py | 63 +++++++++------------ packages/types/src/active-cycle.d.ts | 5 ++ packages/types/src/index.d.ts | 3 + 5 files changed, 89 insertions(+), 38 deletions(-) create mode 100644 packages/types/src/active-cycle.d.ts diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py index d8d69f26c4..dd23073239 100644 --- a/apiserver/plane/app/serializers/__init__.py +++ b/apiserver/plane/app/serializers/__init__.py @@ -42,6 +42,7 @@ from .view import ( ) from .cycle import ( CycleSerializer, + ActiveCycleSerializer, CycleIssueSerializer, CycleFavoriteSerializer, CycleWriteSerializer, @@ -118,7 +119,10 @@ from .inbox import ( from .analytic import AnalyticViewSerializer -from .notification import NotificationSerializer, UserNotificationPreferenceSerializer +from .notification import ( + NotificationSerializer, + UserNotificationPreferenceSerializer, +) from .exporter import ExporterHistorySerializer diff --git a/apiserver/plane/app/serializers/cycle.py b/apiserver/plane/app/serializers/cycle.py index a273b349c3..80c1213973 100644 --- a/apiserver/plane/app/serializers/cycle.py +++ b/apiserver/plane/app/serializers/cycle.py @@ -3,6 +3,7 @@ from rest_framework import serializers # Module imports from .base import BaseSerializer +from .project import ProjectLiteSerializer from .issue import IssueStateSerializer from plane.db.models import ( Cycle, @@ -11,6 +12,7 @@ from plane.db.models import ( CycleUserProperties, ) + class CycleWriteSerializer(BaseSerializer): def validate(self, data): if ( @@ -47,6 +49,53 @@ class CycleSerializer(BaseSerializer): # active | draft | upcoming | completed status = serializers.CharField(read_only=True) + class Meta: + model = Cycle + fields = [ + # necessary fields + "id", + "workspace_id", + "project_id", + # model fields + "name", + "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", + "status", + ] + read_only_fields = fields + + +class ActiveCycleSerializer(BaseSerializer): + # favorite + is_favorite = serializers.BooleanField(read_only=True) + total_issues = serializers.IntegerField(read_only=True) + # state group wise distribution + cancelled_issues = serializers.IntegerField(read_only=True) + completed_issues = serializers.IntegerField(read_only=True) + started_issues = serializers.IntegerField(read_only=True) + unstarted_issues = serializers.IntegerField(read_only=True) + backlog_issues = serializers.IntegerField(read_only=True) + + # active | draft | upcoming | completed + status = serializers.CharField(read_only=True) + + # project details + project_detail = ProjectLiteSerializer(read_only=True, source="project") class Meta: model = Cycle @@ -75,6 +124,7 @@ class CycleSerializer(BaseSerializer): "unstarted_issues", "backlog_issues", "status", + "project_detail", ] read_only_fields = fields diff --git a/apiserver/plane/app/views/cycle.py b/apiserver/plane/app/views/cycle.py index 92c228bce6..24471c46ae 100644 --- a/apiserver/plane/app/views/cycle.py +++ b/apiserver/plane/app/views/cycle.py @@ -38,6 +38,7 @@ from plane.app.serializers import ( IssueSerializer, CycleWriteSerializer, CycleUserPropertiesSerializer, + ActiveCycleSerializer, ) from plane.app.permissions import ( WorkspaceUserPermission, @@ -425,9 +426,8 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) def partial_update(self, request, slug, project_id, pk): - queryset = ( - self.get_queryset() - .filter(workspace__slug=slug, project_id=project_id, pk=pk) + queryset = self.get_queryset().filter( + workspace__slug=slug, project_id=project_id, pk=pk ) cycle = queryset.first() request_data = request.data @@ -878,7 +878,9 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet): ) # Update the cycle issues - CycleIssue.objects.bulk_update(updated_records, ["cycle_id"], batch_size=100) + CycleIssue.objects.bulk_update( + updated_records, ["cycle_id"], batch_size=100 + ) # Capture Issue Activity issue_activity.delay( type="cycle.activity.created", @@ -1306,16 +1308,14 @@ class ActiveCycleEndpoint(BaseAPIView): "completion_chart": {}, } if cycle["start_date"] and cycle["end_date"]: - cycle["distribution"][ - "completion_chart" - ] = burndown_plot( + cycle["distribution"]["completion_chart"] = burndown_plot( queryset=active_cycles.get(pk=cycle["id"]), slug=self.kwargs.get("slug"), - project_id=cycle["project"], + project_id=cycle["project_id"], cycle_id=cycle["id"], ) return results - + def get(self, request, slug): subquery = CycleFavorite.objects.filter( user=self.request.user, @@ -1393,27 +1393,6 @@ class ActiveCycleEndpoint(BaseAPIView): ), ) ) - .annotate(total_estimates=Sum("issue_cycle__issue__estimate_point")) - .annotate( - completed_estimates=Sum( - "issue_cycle__issue__estimate_point", - filter=Q( - issue_cycle__issue__state__group="completed", - issue_cycle__issue__archived_at__isnull=True, - issue_cycle__issue__is_draft=False, - ), - ) - ) - .annotate( - started_estimates=Sum( - "issue_cycle__issue__estimate_point", - filter=Q( - issue_cycle__issue__state__group="started", - issue_cycle__issue__archived_at__isnull=True, - issue_cycle__issue__is_draft=False, - ), - ) - ) .annotate( status=Case( When( @@ -1421,7 +1400,9 @@ class ActiveCycleEndpoint(BaseAPIView): & Q(end_date__gte=timezone.now()), then=Value("CURRENT"), ), - When(start_date__gt=timezone.now(), then=Value("UPCOMING")), + When( + start_date__gt=timezone.now(), then=Value("UPCOMING") + ), When(end_date__lt=timezone.now(), then=Value("COMPLETED")), When( Q(start_date__isnull=True) & Q(end_date__isnull=True), @@ -1434,22 +1415,30 @@ class ActiveCycleEndpoint(BaseAPIView): .prefetch_related( Prefetch( "issue_cycle__issue__assignees", - queryset=User.objects.only("avatar", "first_name", "id").distinct(), + queryset=User.objects.only( + "avatar", "first_name", "id" + ).distinct(), ) ) .prefetch_related( Prefetch( "issue_cycle__issue__labels", - queryset=Label.objects.only("name", "color", "id").distinct(), + queryset=Label.objects.only( + "name", "color", "id" + ).distinct(), ) ) .order_by("-created_at") ) - + return self.paginate( request=request, queryset=active_cycles, - on_results=lambda active_cycles: CycleSerializer(active_cycles, many=True).data, - controller=lambda results: self.get_results_controller(results, active_cycles), - default_per_page=int(request.GET.get("per_page", 3)) + on_results=lambda active_cycles: ActiveCycleSerializer( + active_cycles, many=True + ).data, + controller=lambda results: self.get_results_controller( + results, active_cycles + ), + default_per_page=int(request.GET.get("per_page", 3)), ) diff --git a/packages/types/src/active-cycle.d.ts b/packages/types/src/active-cycle.d.ts new file mode 100644 index 0000000000..75ed52f7fb --- /dev/null +++ b/packages/types/src/active-cycle.d.ts @@ -0,0 +1,5 @@ +import type { IProjectLite, ICycle } from "@plane/types"; + +export interface IActiveCycle extends ICycle { + project_detail: IProjectLite; +} diff --git a/packages/types/src/index.d.ts b/packages/types/src/index.d.ts index 6e8ded9429..5dfc481001 100644 --- a/packages/types/src/index.d.ts +++ b/packages/types/src/index.d.ts @@ -32,6 +32,9 @@ export * from "./api_token"; export * from "./instance"; export * from "./app"; +// enterprise +export * from "./active-cycle"; + export type NestedKeyOf = { [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object ? ObjectType[Key] extends { pop: any; push: any }