diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index f355bb24be..6776c139e5 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -1,58 +1,57 @@ # Python imports import json -# Django imports -from django.db.models import ( - Func, - F, - Q, - Exists, - OuterRef, - Count, - Prefetch, - Case, - When, - Value, - CharField, - Subquery, - IntegerField, -) -from django.utils import timezone from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import ArrayField -from django.db.models import UUIDField + +# Django imports +from django.db.models import ( + Case, + CharField, + Count, + Exists, + F, + Func, + OuterRef, + Prefetch, + Q, + UUIDField, + Value, + When, +) from django.db.models.functions import Coalesce +from django.utils import timezone +from rest_framework import status # Third party imports from rest_framework.response import Response -from rest_framework import status -# Module imports -from .. import BaseViewSet, BaseAPIView, WebhookMixin -from plane.app.serializers import ( - CycleSerializer, - CycleFavoriteSerializer, - CycleWriteSerializer, - CycleUserPropertiesSerializer, - ActiveCycleSerializer, -) from plane.app.permissions import ( WorkspaceUserPermission, ProjectEntityPermission, ProjectLitePermission, ) -from plane.db.models import ( - User, - Cycle, - CycleIssue, - Issue, - CycleFavorite, - Label, - CycleUserProperties, +from plane.app.serializers import ( + CycleFavoriteSerializer, + CycleSerializer, + CycleUserPropertiesSerializer, + CycleWriteSerializer, ) from plane.bgtasks.issue_activites_task import issue_activity +from plane.db.models import ( + Cycle, + CycleFavorite, + CycleIssue, + CycleUserProperties, + Issue, + Label, + User, +) from plane.utils.analytics_plot import burndown_plot +# Module imports +from .. import BaseAPIView, BaseViewSet, WebhookMixin + class CycleViewSet(WebhookMixin, BaseViewSet): serializer_class = CycleSerializer @@ -75,60 +74,6 @@ class CycleViewSet(WebhookMixin, BaseViewSet): project_id=self.kwargs.get("project_id"), workspace__slug=self.kwargs.get("slug"), ) - cancelled_issues = ( - Issue.issue_objects.filter( - state__group="cancelled", - issue_cycle__cycle_id=OuterRef("pk"), - ) - .values("issue_cycle__cycle_id") - .annotate(cnt=Count("pk")) - .values("cnt") - ) - completed_issues = ( - Issue.issue_objects.filter( - state__group="completed", - issue_cycle__cycle_id=OuterRef("pk"), - ) - .values("issue_cycle__cycle_id") - .annotate(cnt=Count("pk")) - .values("cnt") - ) - started_issues = ( - Issue.issue_objects.filter( - state__group="started", - issue_cycle__cycle_id=OuterRef("pk"), - ) - .values("issue_cycle__cycle_id") - .annotate(cnt=Count("pk")) - .values("cnt") - ) - unstarted_issues = ( - Issue.issue_objects.filter( - state__group="unstarted", - issue_cycle__cycle_id=OuterRef("pk"), - ) - .values("issue_cycle__cycle_id") - .annotate(cnt=Count("pk")) - .values("cnt") - ) - backlog_issues = ( - Issue.issue_objects.filter( - state__group="backlog", - issue_cycle__cycle_id=OuterRef("pk"), - ) - .values("issue_cycle__cycle_id") - .annotate(cnt=Count("pk")) - .values("cnt") - ) - total_issues = ( - Issue.issue_objects.filter( - issue_cycle__cycle_id=OuterRef("pk"), - ) - .values("issue_cycle__cycle_id") - .annotate(cnt=Count("pk")) - .values("cnt") - ) - return self.filter_queryset( super() .get_queryset() @@ -158,39 +103,62 @@ class CycleViewSet(WebhookMixin, BaseViewSet): ) .annotate(is_favorite=Exists(favorite_subquery)) .annotate( - completed_issues=Coalesce( - Subquery(completed_issues[:1]), - Value(0, output_field=IntegerField()), + total_issues=Count( + "issue_cycle", + filter=Q( + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), ) ) .annotate( - cancelled_issues=Coalesce( - Subquery(cancelled_issues[:1]), - Value(0, output_field=IntegerField()), + completed_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="completed", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), ) ) .annotate( - started_issues=Coalesce( - Subquery(started_issues[:1]), - Value(0, output_field=IntegerField()), + cancelled_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="cancelled", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), ) ) .annotate( - unstarted_issues=Coalesce( - Subquery(unstarted_issues[:1]), - Value(0, output_field=IntegerField()), + started_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="started", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), ) ) .annotate( - backlog_issues=Coalesce( - Subquery(backlog_issues[:1]), - Value(0, output_field=IntegerField()), + unstarted_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="unstarted", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), ) ) .annotate( - total_issues=Coalesce( - Subquery(total_issues[:1]), - Value(0, output_field=IntegerField()), + backlog_issues=Count( + "issue_cycle__issue__state__group", + filter=Q( + issue_cycle__issue__state__group="backlog", + issue_cycle__issue__archived_at__isnull=True, + issue_cycle__issue__is_draft=False, + ), ) ) .annotate( @@ -219,9 +187,6 @@ class CycleViewSet(WebhookMixin, BaseViewSet): distinct=True, filter=~Q( issue_cycle__issue__assignees__id__isnull=True - ) - & Q( - issue_cycle__issue__assignees__member_project__is_active=True ), ), Value([], output_field=ArrayField(UUIDField())), @@ -386,8 +351,8 @@ class CycleViewSet(WebhookMixin, BaseViewSet): "external_id", "progress_snapshot", # meta fields - "total_issues", "is_favorite", + "total_issues", "cancelled_issues", "completed_issues", "started_issues", diff --git a/web/components/command-palette/command-palette.tsx b/web/components/command-palette/command-palette.tsx index 8d162f6ab9..490d392e2c 100644 --- a/web/components/command-palette/command-palette.tsx +++ b/web/components/command-palette/command-palette.tsx @@ -113,8 +113,6 @@ export const CommandPalette: FC = observer(() => { const canPerformWorkspaceCreateActions = useCallback( (showToast: boolean = true) => { const isAllowed = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; - console.log("currentWorkspaceRole", currentWorkspaceRole); - console.log("isAllowed", isAllowed); if (!isAllowed && showToast) setToast({ type: TOAST_TYPE.ERROR, diff --git a/web/components/cycles/modal.tsx b/web/components/cycles/modal.tsx index 008cdcbdeb..6ed53eb326 100644 --- a/web/components/cycles/modal.tsx +++ b/web/components/cycles/modal.tsx @@ -56,7 +56,7 @@ export const CycleCreateUpdateModal: React.FC = (props) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", - message: err.detail ?? "Error in creating cycle. Please try again.", + message: err?.detail ?? "Error in creating cycle. Please try again.", }); captureCycleEvent({ eventName: CYCLE_CREATED, @@ -90,7 +90,7 @@ export const CycleCreateUpdateModal: React.FC = (props) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", - message: err.detail ?? "Error in updating cycle. Please try again.", + message: err?.detail ?? "Error in updating cycle. Please try again.", }); }); }; diff --git a/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx b/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx index 9e54d2cf76..d5b263776b 100644 --- a/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx +++ b/web/components/issues/issue-layouts/calendar/base-calendar-root.tsx @@ -73,7 +73,7 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => { setToast({ title: "Error", type: TOAST_TYPE.ERROR, - message: err.detail ?? "Failed to perform this action", + message: err?.detail ?? "Failed to perform this action", }); }); } diff --git a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx index 21e47c1cd6..4104308531 100644 --- a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -143,7 +143,7 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas setToast({ title: "Error", type: TOAST_TYPE.ERROR, - message: err.detail ?? "Failed to perform this action", + message: err?.detail ?? "Failed to perform this action", }); }); } diff --git a/web/components/modules/modal.tsx b/web/components/modules/modal.tsx index 7242073b1c..89ae7f3212 100644 --- a/web/components/modules/modal.tsx +++ b/web/components/modules/modal.tsx @@ -68,7 +68,7 @@ export const CreateUpdateModuleModal: React.FC = observer((props) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", - message: err.detail ?? "Module could not be created. Please try again.", + message: err?.detail ?? "Module could not be created. Please try again.", }); captureModuleEvent({ eventName: MODULE_CREATED, @@ -99,7 +99,7 @@ export const CreateUpdateModuleModal: React.FC = observer((props) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", - message: err.detail ?? "Module could not be updated. Please try again.", + message: err?.detail ?? "Module could not be updated. Please try again.", }); captureModuleEvent({ eventName: MODULE_UPDATED, diff --git a/web/components/project/create-project-form.tsx b/web/components/project/create-project-form.tsx index a9d76210d0..14d723e6d5 100644 --- a/web/components/project/create-project-form.tsx +++ b/web/components/project/create-project-form.tsx @@ -209,8 +209,10 @@ export const CreateProjectForm: FC = observer((props) => { [val.type]: logoValue, }); }} - defaultIconColor={value.in_use === "icon" ? value.icon?.color : undefined} - defaultOpen={value.in_use === "emoji" ? EmojiIconPickerTypes.EMOJI : EmojiIconPickerTypes.ICON} + defaultIconColor={value.in_use && value.in_use === "icon" ? value.icon?.color : undefined} + defaultOpen={ + value.in_use && value.in_use === "emoji" ? EmojiIconPickerTypes.EMOJI : EmojiIconPickerTypes.ICON + } /> )} /> diff --git a/web/components/project/form.tsx b/web/components/project/form.tsx index aaaaf03b3b..3e770a6185 100644 --- a/web/components/project/form.tsx +++ b/web/components/project/form.tsx @@ -166,8 +166,10 @@ export const ProjectDetailsForm: FC = (props) => { [val.type]: logoValue, }); }} - defaultIconColor={value.in_use === "icon" ? value.icon?.color : undefined} - defaultOpen={value.in_use === "emoji" ? EmojiIconPickerTypes.EMOJI : EmojiIconPickerTypes.ICON} + defaultIconColor={value?.in_use && value.in_use === "icon" ? value?.icon?.color : undefined} + defaultOpen={ + value.in_use && value.in_use === "emoji" ? EmojiIconPickerTypes.EMOJI : EmojiIconPickerTypes.ICON + } disabled={!isAdmin} /> )} diff --git a/web/components/project/project-logo.tsx b/web/components/project/project-logo.tsx index c06dc3061b..fc90fdba3d 100644 --- a/web/components/project/project-logo.tsx +++ b/web/components/project/project-logo.tsx @@ -11,7 +11,7 @@ type Props = { export const ProjectLogo: React.FC = (props) => { const { className, logo } = props; - if (logo && logo.in_use === "icon" && logo.icon) + if (logo?.in_use === "icon" && logo?.icon) return ( = (props) => { ); - if (logo && logo.in_use === "emoji" && logo.emoji) + if (logo?.in_use === "emoji" && logo?.emoji) return ( {logo.emoji.value?.split("-").map((emoji) => String.fromCodePoint(parseInt(emoji, 10)))} ); - return ; + return <>; }; diff --git a/web/components/views/modal.tsx b/web/components/views/modal.tsx index c7b9c9a31c..6e30a76ec1 100644 --- a/web/components/views/modal.tsx +++ b/web/components/views/modal.tsx @@ -54,7 +54,7 @@ export const CreateUpdateProjectViewModal: FC = observer((props) => { setToast({ type: TOAST_TYPE.ERROR, title: "Error!", - message: err.detail ?? "Something went wrong. Please try again.", + message: err?.detail ?? "Something went wrong. Please try again.", }) ); };