diff --git a/apiserver/.env.example b/apiserver/.env.example index 733e448d60b..33ef5c4cdf6 100644 --- a/apiserver/.env.example +++ b/apiserver/.env.example @@ -57,5 +57,6 @@ ADMIN_BASE_URL= SPACE_BASE_URL= APP_BASE_URL= + # Hard delete files after days -HARD_DELETE_AFTER_DAYS=60 +HARD_DELETE_AFTER_DAYS=60 \ No newline at end of file diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index c4a131fd746..90515733954 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -212,7 +212,7 @@ def update(self, instance, validated_data): updated_by_id = instance.updated_by_id if assignees is not None: - IssueAssignee.objects.filter(issue=instance).delete(soft=False) + IssueAssignee.objects.filter(issue=instance).delete() IssueAssignee.objects.bulk_create( [ IssueAssignee( @@ -229,7 +229,7 @@ def update(self, instance, validated_data): ) if labels is not None: - IssueLabel.objects.filter(issue=instance).delete(soft=False) + IssueLabel.objects.filter(issue=instance).delete() IssueLabel.objects.bulk_create( [ IssueLabel( diff --git a/apiserver/plane/api/views/cycle.py b/apiserver/plane/api/views/cycle.py index 882692dac04..12e4a1b3c3c 100644 --- a/apiserver/plane/api/views/cycle.py +++ b/apiserver/plane/api/views/cycle.py @@ -404,7 +404,7 @@ def delete(self, request, slug, project_id, pk): epoch=int(timezone.now().timestamp()), ) # Delete the cycle - cycle.delete(soft=False) + cycle.delete() # Delete the user favorite cycle UserFavorite.objects.filter( entity_type="cycle", diff --git a/apiserver/plane/api/views/inbox.py b/apiserver/plane/api/views/inbox.py index f7e18dd76ff..381e12aab07 100644 --- a/apiserver/plane/api/views/inbox.py +++ b/apiserver/plane/api/views/inbox.py @@ -227,7 +227,10 @@ def patch(self, request, slug, project_id, issue_id): ArrayAgg( "labels__id", distinct=True, - filter=~Q(labels__id__isnull=True), + filter=Q( + ~Q(labels__id__isnull=True) + & Q(label_issue__deleted_at__isnull=True), + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -235,7 +238,11 @@ def patch(self, request, slug, project_id, issue_id): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 27c7042b49b..2590532abf1 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -205,7 +205,7 @@ def get(self, request, slug, project_id, pk=None): .annotate( cycle_id=Case( When( - Q(issue_cycle__cycle__deleted_at__isnull=True), + Q(issue_cycle__deleted_at__isnull=True), then=F("issue_cycle__cycle_id"), ), default=None, diff --git a/apiserver/plane/app/serializers/draft.py b/apiserver/plane/app/serializers/draft.py index fca695c9872..e07e416a75d 100644 --- a/apiserver/plane/app/serializers/draft.py +++ b/apiserver/plane/app/serializers/draft.py @@ -169,9 +169,7 @@ def update(self, instance, validated_data): updated_by_id = instance.updated_by_id if assignees is not None: - DraftIssueAssignee.objects.filter(draft_issue=instance).delete( - soft=False - ) + DraftIssueAssignee.objects.filter(draft_issue=instance).delete() DraftIssueAssignee.objects.bulk_create( [ DraftIssueAssignee( @@ -188,9 +186,7 @@ def update(self, instance, validated_data): ) if labels is not None: - DraftIssueLabel.objects.filter(draft_issue=instance).delete( - soft=False - ) + DraftIssueLabel.objects.filter(draft_issue=instance).delete() DraftIssueLabel.objects.bulk_create( [ DraftIssueLabel( @@ -208,7 +204,7 @@ def update(self, instance, validated_data): if cycle_id != "not_provided": DraftIssueCycle.objects.filter(draft_issue=instance).delete() - if cycle_id is not None: + if cycle_id: DraftIssueCycle.objects.create( cycle_id=cycle_id, draft_issue=instance, diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index 22d9dc483df..2323c248a86 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -201,7 +201,7 @@ def update(self, instance, validated_data): updated_by_id = instance.updated_by_id if assignees is not None: - IssueAssignee.objects.filter(issue=instance).delete(soft=False) + IssueAssignee.objects.filter(issue=instance).delete() IssueAssignee.objects.bulk_create( [ IssueAssignee( @@ -218,7 +218,7 @@ def update(self, instance, validated_data): ) if labels is not None: - IssueLabel.objects.filter(issue=instance).delete(soft=False) + IssueLabel.objects.filter(issue=instance).delete() IssueLabel.objects.bulk_create( [ IssueLabel( diff --git a/apiserver/plane/app/views/analytic/base.py b/apiserver/plane/app/views/analytic/base.py index ddbde02692e..47b4c1c7007 100644 --- a/apiserver/plane/app/views/analytic/base.py +++ b/apiserver/plane/app/views/analytic/base.py @@ -110,7 +110,10 @@ def get(self, request, slug): if x_axis in ["labels__id"] or segment in ["labels__id"]: label_details = ( Issue.objects.filter( - workspace__slug=slug, **filters, labels__id__isnull=False + workspace__slug=slug, + **filters, + labels__id__isnull=False + & Q(label_issue__deleted_at__isnull=True), ) .distinct("labels__id") .order_by("labels__id") diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index e021c4ba1ab..a6199476604 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -144,6 +144,11 @@ def get_queryset(self): distinct=True, filter=~Q( issue_cycle__issue__assignees__id__isnull=True + ) + & ( + Q( + issue_cycle__issue__issue_assignee__deleted_at__isnull=True + ) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -491,7 +496,7 @@ def destroy(self, request, slug, project_id, pk): origin=request.META.get("HTTP_ORIGIN"), ) # TODO: Soft delete the cycle break the onetoone relationship with cycle issue - cycle.delete(soft=False) + cycle.delete() # Delete the user favorite cycle UserFavorite.objects.filter( diff --git a/apiserver/plane/app/views/cycle/issue.py b/apiserver/plane/app/views/cycle/issue.py index a9a33059900..c744897a163 100644 --- a/apiserver/plane/app/views/cycle/issue.py +++ b/apiserver/plane/app/views/cycle/issue.py @@ -105,7 +105,7 @@ def list(self, request, slug, project_id, cycle_id): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, diff --git a/apiserver/plane/app/views/dashboard/base.py b/apiserver/plane/app/views/dashboard/base.py index 1cb446abb12..3bfa0fe56e8 100644 --- a/apiserver/plane/app/views/dashboard/base.py +++ b/apiserver/plane/app/views/dashboard/base.py @@ -218,9 +218,9 @@ def dashboard_assigned_issues(self, request, slug): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -229,8 +229,11 @@ def dashboard_assigned_issues(self, request, slug): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -238,9 +241,11 @@ def dashboard_assigned_issues(self, request, slug): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -387,9 +392,9 @@ def dashboard_created_issues(self, request, slug): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -398,8 +403,11 @@ def dashboard_created_issues(self, request, slug): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -407,9 +415,11 @@ def dashboard_created_issues(self, request, slug): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/app/views/inbox/base.py b/apiserver/plane/app/views/inbox/base.py index dcae298d662..62da2b25152 100644 --- a/apiserver/plane/app/views/inbox/base.py +++ b/apiserver/plane/app/views/inbox/base.py @@ -115,7 +115,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -149,9 +149,9 @@ def get_queryset(self): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -160,8 +160,11 @@ def get_queryset(self): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -169,9 +172,11 @@ def get_queryset(self): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -198,8 +203,8 @@ def list(self, request, slug, project_id): ArrayAgg( "issue__labels__id", distinct=True, - filter=~Q(issue__labels__id__isnull=True) - & Q(issue__labels__deleted_at__isnull=True), + filter=Q(~Q(issue__labels__id__isnull=True) + & Q(issue__label_issue__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ) @@ -311,9 +316,11 @@ def create(self, request, slug, project_id): ArrayAgg( "issue__labels__id", distinct=True, - filter=( + filter=Q( ~Q(issue__labels__id__isnull=True) - & Q(issue__labels__deleted_at__isnull=True) + & Q( + issue__label_issue__deleted_at__isnull=True + ) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -323,7 +330,9 @@ def create(self, request, slug, project_id): "issue__assignees__id", distinct=True, filter=~Q(issue__assignees__id__isnull=True) - & Q(issue__assignees__member_project__is_active=True), + & Q( + issue__assignees__member_project__is_active=True + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -376,7 +385,8 @@ def partial_update(self, request, slug, project_id, pk): ArrayAgg( "labels__id", distinct=True, - filter=~Q(labels__id__isnull=True), + filter=Q(~Q(labels__id__isnull=True) + & Q(label_issue__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -384,7 +394,8 @@ def partial_update(self, request, slug, project_id, pk): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True), + filter=Q(~Q(assignees__id__isnull=True) + & Q(issue_assignee__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -507,7 +518,10 @@ def partial_update(self, request, slug, project_id, pk): ArrayAgg( "issue__labels__id", distinct=True, - filter=~Q(issue__labels__id__isnull=True), + filter=Q(~Q(issue__labels__id__isnull=True) + & Q( + issue__label_issue__deleted_at__isnull=True + )), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -515,7 +529,8 @@ def partial_update(self, request, slug, project_id, pk): ArrayAgg( "issue__assignees__id", distinct=True, - filter=~Q(issue__assignees__id__isnull=True), + filter=Q(~Q(issue__assignees__id__isnull=True) + & Q(issue__issue_assignee__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -560,7 +575,8 @@ def retrieve(self, request, slug, project_id, pk): ArrayAgg( "issue__labels__id", distinct=True, - filter=~Q(issue__labels__id__isnull=True), + filter=Q(~Q(issue__labels__id__isnull=True) + & Q(issue__label_issue__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -568,7 +584,8 @@ def retrieve(self, request, slug, project_id, pk): ArrayAgg( "issue__assignees__id", distinct=True, - filter=~Q(issue__assignees__id__isnull=True), + filter=Q(~Q(issue__assignees__id__isnull=True) + & Q(issue__issue_assignee__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/app/views/issue/archive.py b/apiserver/plane/app/views/issue/archive.py index 283f1ad99a8..46815296dbf 100644 --- a/apiserver/plane/app/views/issue/archive.py +++ b/apiserver/plane/app/views/issue/archive.py @@ -67,7 +67,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index 0488a5471d8..7bc3dd10e14 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -88,7 +88,7 @@ def get(self, request, slug, project_id): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -220,7 +220,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -489,9 +489,9 @@ def retrieve(self, request, slug, project_id, pk=None): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -500,8 +500,11 @@ def retrieve(self, request, slug, project_id, pk=None): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -509,9 +512,11 @@ def retrieve(self, request, slug, project_id, pk=None): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -590,9 +595,9 @@ def partial_update(self, request, slug, project_id, pk=None): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -601,8 +606,11 @@ def partial_update(self, request, slug, project_id, pk=None): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -610,9 +618,11 @@ def partial_update(self, request, slug, project_id, pk=None): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -778,7 +788,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -890,9 +900,9 @@ def list(self, request, slug, project_id): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -901,8 +911,11 @@ def list(self, request, slug, project_id): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -910,9 +923,11 @@ def list(self, request, slug, project_id): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/app/views/issue/relation.py b/apiserver/plane/app/views/issue/relation.py index 83385d83d72..20243a11ce2 100644 --- a/apiserver/plane/app/views/issue/relation.py +++ b/apiserver/plane/app/views/issue/relation.py @@ -96,7 +96,7 @@ def list(self, request, slug, project_id, issue_id): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -130,9 +130,9 @@ def list(self, request, slug, project_id, issue_id): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & (Q(label_issue__deleted_at__isnull=True)) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -141,8 +141,9 @@ def list(self, request, slug, project_id, issue_id): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q(~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -289,7 +290,7 @@ def remove_relation(self, request, slug, project_id, issue_id): IssueRelationSerializer(issue_relation).data, cls=DjangoJSONEncoder, ) - issue_relation.delete(soft=False) + issue_relation.delete() issue_activity.delay( type="issue_relation.activity.deleted", requested_data=json.dumps(request.data, cls=DjangoJSONEncoder), diff --git a/apiserver/plane/app/views/issue/sub_issue.py b/apiserver/plane/app/views/issue/sub_issue.py index 700d6db5b39..02f3b6147b4 100644 --- a/apiserver/plane/app/views/issue/sub_issue.py +++ b/apiserver/plane/app/views/issue/sub_issue.py @@ -53,7 +53,7 @@ def get(self, request, slug, project_id, issue_id): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -87,9 +87,9 @@ def get(self, request, slug, project_id, issue_id): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -98,8 +98,11 @@ def get(self, request, slug, project_id, issue_id): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -107,9 +110,11 @@ def get(self, request, slug, project_id, issue_id): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/app/views/module/issue.py b/apiserver/plane/app/views/module/issue.py index f58e477567a..ae59efbca43 100644 --- a/apiserver/plane/app/views/module/issue.py +++ b/apiserver/plane/app/views/module/issue.py @@ -70,7 +70,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 59cfcecd57a..a14e761a552 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -210,7 +210,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -244,9 +244,9 @@ def get_queryset(self): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -255,8 +255,11 @@ def get_queryset(self): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q( + ~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -264,9 +267,11 @@ def get_queryset(self): ArrayAgg( "issue_module__module_id", distinct=True, - filter=~Q(issue_module__module_id__isnull=True) - & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + filter=Q( + ~Q(issue_module__module_id__isnull=True) + & Q(issue_module__module__archived_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) + ), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -288,7 +293,7 @@ def list(self, request, slug): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -552,7 +557,9 @@ def partial_update(self, request, slug, project_id, pk): serializer.errors, status=status.HTTP_400_BAD_REQUEST ) - @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=IssueView) + @allow_permission( + allowed_roles=[ROLE.ADMIN], creator=True, model=IssueView + ) def destroy(self, request, slug, project_id, pk): project_view = IssueView.objects.get( pk=pk, diff --git a/apiserver/plane/app/views/workspace/draft.py b/apiserver/plane/app/views/workspace/draft.py index ae7db2a4012..265ef111ce2 100644 --- a/apiserver/plane/app/views/workspace/draft.py +++ b/apiserver/plane/app/views/workspace/draft.py @@ -60,7 +60,7 @@ def get_queryset(self): .annotate( cycle_id=Case( When( - draft_issue_cycle__cycle__deleted_at__isnull=True, + draft_issue_cycle__deleted_at__isnull=True, then=F("draft_issue_cycle__cycle_id"), ), default=None, @@ -71,9 +71,9 @@ def get_queryset(self): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & (Q(draft_label_issue__deleted_at__isnull=True)) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -82,8 +82,9 @@ def get_queryset(self): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q(~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(draft_issue_assignee__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -91,13 +92,11 @@ def get_queryset(self): ArrayAgg( "draft_issue_module__module_id", distinct=True, - filter=~Q(draft_issue_module__module_id__isnull=True) + filter=Q(~Q(draft_issue_module__module_id__isnull=True) & Q( draft_issue_module__module__archived_at__isnull=True ) - & Q( - draft_issue_module__module__deleted_at__isnull=True - ), + & Q(draft_issue_module__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/app/views/workspace/user.py b/apiserver/plane/app/views/workspace/user.py index fae917af295..a69f18e19f0 100644 --- a/apiserver/plane/app/views/workspace/user.py +++ b/apiserver/plane/app/views/workspace/user.py @@ -123,7 +123,7 @@ def get(self, request, slug, user_id): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, diff --git a/apiserver/plane/bgtasks/analytic_plot_export.py b/apiserver/plane/bgtasks/analytic_plot_export.py index 7d78b89d06b..a4341841e1c 100644 --- a/apiserver/plane/bgtasks/analytic_plot_export.py +++ b/apiserver/plane/bgtasks/analytic_plot_export.py @@ -130,7 +130,9 @@ def get_label_details(slug, filters): """Fetch label details if required""" return ( Issue.objects.filter( - workspace__slug=slug, **filters, labels__id__isnull=False + workspace__slug=slug, + **filters, + labels__id__isnull=False & Q(label_issue__deleted_at__isnull=True), ) .distinct("labels__id") .order_by("labels__id") diff --git a/apiserver/plane/celery.py b/apiserver/plane/celery.py index 865ef95fae5..c58844de33a 100644 --- a/apiserver/plane/celery.py +++ b/apiserver/plane/celery.py @@ -32,14 +32,14 @@ "task": "plane.bgtasks.email_notification_task.stack_email_notification", "schedule": crontab(minute="*/5"), }, - "check-every-day-to-delete-api-logs": { - "task": "plane.bgtasks.api_logs_task.delete_api_logs", - "schedule": crontab(hour=0, minute=0), - }, "check-every-day-to-delete-hard-delete": { "task": "plane.bgtasks.deletion_task.hard_delete", "schedule": crontab(hour=0, minute=0), }, + "check-every-day-to-delete-api-logs": { + "task": "plane.bgtasks.api_logs_task.delete_api_logs", + "schedule": crontab(hour=0, minute=0), + }, "run-every-6-hours-for-instance-trace": { "task": "plane.license.bgtasks.tracer.instance_traces", "schedule": crontab(hour="*/6"), diff --git a/apiserver/plane/db/migrations/0082_alter_issue_managers_alter_cycleissue_issue_and_more.py b/apiserver/plane/db/migrations/0082_alter_issue_managers_alter_cycleissue_issue_and_more.py new file mode 100644 index 00000000000..9d0279eb45c --- /dev/null +++ b/apiserver/plane/db/migrations/0082_alter_issue_managers_alter_cycleissue_issue_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 4.2.15 on 2024-10-22 08:00 + +from django.db import migrations, models +import django.db.models.deletion +import django.db.models.manager + + +class Migration(migrations.Migration): + + dependencies = [ + ("db", "0081_remove_globalview_created_by_and_more"), + ] + + operations = [ + migrations.AlterModelManagers( + name="issue", + managers=[ + ("issue_objects", django.db.models.manager.Manager()), + ], + ), + migrations.AlterField( + model_name="cycleissue", + name="issue", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="issue_cycle", + to="db.issue", + ), + ), + migrations.AlterField( + model_name="draftissuecycle", + name="draft_issue", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="draft_issue_cycle", + to="db.draftissue", + ), + ), + migrations.AlterUniqueTogether( + name="cycleissue", + unique_together={("issue", "cycle", "deleted_at")}, + ), + migrations.AlterUniqueTogether( + name="draftissuecycle", + unique_together={("draft_issue", "cycle", "deleted_at")}, + ), + migrations.AddConstraint( + model_name="cycleissue", + constraint=models.UniqueConstraint( + condition=models.Q(("deleted_at__isnull", True)), + fields=("cycle", "issue"), + name="cycle_issue_when_deleted_at_null", + ), + ), + migrations.AddConstraint( + model_name="draftissuecycle", + constraint=models.UniqueConstraint( + condition=models.Q(("deleted_at__isnull", True)), + fields=("draft_issue", "cycle"), + name="draft_issue_cycle_when_deleted_at_null", + ), + ), + ] diff --git a/apiserver/plane/db/models/cycle.py b/apiserver/plane/db/models/cycle.py index f9f9eece966..c3dccb0c8b4 100644 --- a/apiserver/plane/db/models/cycle.py +++ b/apiserver/plane/db/models/cycle.py @@ -110,7 +110,7 @@ class CycleIssue(ProjectBaseModel): Cycle Issues """ - issue = models.OneToOneField( + issue = models.ForeignKey( "db.Issue", on_delete=models.CASCADE, related_name="issue_cycle" ) cycle = models.ForeignKey( @@ -118,6 +118,14 @@ class CycleIssue(ProjectBaseModel): ) class Meta: + unique_together = ["issue", "cycle", "deleted_at"] + constraints = [ + models.UniqueConstraint( + fields=["cycle", "issue"], + condition=models.Q(deleted_at__isnull=True), + name="cycle_issue_when_deleted_at_null", + ) + ] verbose_name = "Cycle Issue" verbose_name_plural = "Cycle Issues" db_table = "cycle_issues" diff --git a/apiserver/plane/db/models/draft.py b/apiserver/plane/db/models/draft.py index 671b89ff1fa..e80ccdaafb2 100644 --- a/apiserver/plane/db/models/draft.py +++ b/apiserver/plane/db/models/draft.py @@ -234,7 +234,7 @@ class DraftIssueCycle(WorkspaceBaseModel): Draft Issue Cycles """ - draft_issue = models.OneToOneField( + draft_issue = models.ForeignKey( "db.DraftIssue", on_delete=models.CASCADE, related_name="draft_issue_cycle", @@ -244,6 +244,14 @@ class DraftIssueCycle(WorkspaceBaseModel): ) class Meta: + unique_together = ["draft_issue", "cycle", "deleted_at"] + constraints = [ + models.UniqueConstraint( + fields=["draft_issue", "cycle"], + condition=models.Q(deleted_at__isnull=True), + name="draft_issue_cycle_when_deleted_at_null", + ) + ] verbose_name = "Draft Issue Cycle" verbose_name_plural = "Draft Issue Cycles" db_table = "draft_issue_cycles" diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 7ff9af46e9b..4bc529f55f8 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -91,6 +91,7 @@ def get_queryset(self): | models.Q(issue_inbox__status=2) | models.Q(issue_inbox__isnull=True) ) + .filter(deleted_at__isnull=True) .filter(state__is_triage=False) .exclude(archived_at__isnull=False) .exclude(project__archived_at__isnull=False) diff --git a/apiserver/plane/space/serializer/issue.py b/apiserver/plane/space/serializer/issue.py index cf628e85035..d7447681f60 100644 --- a/apiserver/plane/space/serializer/issue.py +++ b/apiserver/plane/space/serializer/issue.py @@ -421,7 +421,7 @@ def update(self, instance, validated_data): updated_by_id = instance.updated_by_id if assignees is not None: - IssueAssignee.objects.filter(issue=instance).delete(soft=False) + IssueAssignee.objects.filter(issue=instance).delete() IssueAssignee.objects.bulk_create( [ IssueAssignee( @@ -438,7 +438,7 @@ def update(self, instance, validated_data): ) if labels is not None: - IssueLabel.objects.filter(issue=instance).delete(soft=False) + IssueLabel.objects.filter(issue=instance).delete() IssueLabel.objects.bulk_create( [ IssueLabel( diff --git a/apiserver/plane/space/utils/grouper.py b/apiserver/plane/space/utils/grouper.py index a1eb16b9c82..68035979081 100644 --- a/apiserver/plane/space/utils/grouper.py +++ b/apiserver/plane/space/utils/grouper.py @@ -35,8 +35,16 @@ def issue_queryset_grouper(queryset, group_by, sub_group_by): } annotations_map = { - "assignee_ids": ("assignees__id", ~Q(assignees__id__isnull=True)), - "label_ids": ("labels__id", ~Q(labels__id__isnull=True)), + "assignee_ids": ( + "assignees__id", + ~Q(assignees__id__isnull=True) + & Q(issue_assignee__deleted_at__isnull=True), + ), + "label_ids": ( + "labels__id", + ~Q(labels__id__isnull=True) + & Q(label_issue__deleted_at__isnull=True), + ), "module_ids": ( "issue_module__module_id", ~Q(issue_module__module_id__isnull=True), diff --git a/apiserver/plane/space/views/issue.py b/apiserver/plane/space/views/issue.py index bd9d499d60b..606664b01e1 100644 --- a/apiserver/plane/space/views/issue.py +++ b/apiserver/plane/space/views/issue.py @@ -109,7 +109,7 @@ def get(self, request, anchor): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -706,7 +706,7 @@ def get(self, request, anchor, issue_id): .annotate( cycle_id=Case( When( - issue_cycle__cycle__deleted_at__isnull=True, + issue_cycle__deleted_at__isnull=True, then=F("issue_cycle__cycle_id"), ), default=None, @@ -717,9 +717,9 @@ def get(self, request, anchor, issue_id): ArrayAgg( "labels__id", distinct=True, - filter=( + filter=Q( ~Q(labels__id__isnull=True) - & Q(labels__deleted_at__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), ), Value([], output_field=ArrayField(UUIDField())), @@ -728,8 +728,9 @@ def get(self, request, anchor, issue_id): ArrayAgg( "assignees__id", distinct=True, - filter=~Q(assignees__id__isnull=True) - & Q(assignees__member_project__is_active=True), + filter=Q(~Q(assignees__id__isnull=True) + & Q(assignees__member_project__is_active=True) + & Q(issue_assignee__deleted_at__isnull=True)), ), Value([], output_field=ArrayField(UUIDField())), ), @@ -739,7 +740,7 @@ def get(self, request, anchor, issue_id): distinct=True, filter=~Q(issue_module__module_id__isnull=True) & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True), + & Q(issue_module__deleted_at__isnull=True), ), Value([], output_field=ArrayField(UUIDField())), ), diff --git a/apiserver/plane/utils/grouper.py b/apiserver/plane/utils/grouper.py index fef47e0b085..38ac74a1647 100644 --- a/apiserver/plane/utils/grouper.py +++ b/apiserver/plane/utils/grouper.py @@ -25,17 +25,22 @@ def issue_queryset_grouper(queryset, group_by, sub_group_by): } annotations_map = { - "assignee_ids": ("assignees__id", ~Q(assignees__id__isnull=True)), + "assignee_ids": ( + "assignees__id", + ~Q(assignees__id__isnull=True) + & Q(issue_assignee__deleted_at__isnull=True), + ), "label_ids": ( "labels__id", - ~Q(labels__id__isnull=True) & (Q(labels__deleted_at__isnull=True)), + ~Q(labels__id__isnull=True) + & Q(label_issue__deleted_at__isnull=True), ), "module_ids": ( "issue_module__module_id", ( ~Q(issue_module__module_id__isnull=True) & Q(issue_module__module__archived_at__isnull=True) - & Q(issue_module__module__deleted_at__isnull=True) + & Q(issue_module__deleted_at__isnull=True) ), ), } diff --git a/apiserver/plane/utils/issue_filters.py b/apiserver/plane/utils/issue_filters.py index 713276d0cdb..b82ba1e8c75 100644 --- a/apiserver/plane/utils/issue_filters.py +++ b/apiserver/plane/utils/issue_filters.py @@ -48,14 +48,22 @@ def string_date_filter( if term == "weeks": if subsequent == "after": if offset == "fromnow": - issue_filter[f"{date_filter}__gte"] = now + timedelta(weeks=duration) + issue_filter[f"{date_filter}__gte"] = now + timedelta( + weeks=duration + ) else: - issue_filter[f"{date_filter}__gte"] = now - timedelta(weeks=duration) + issue_filter[f"{date_filter}__gte"] = now - timedelta( + weeks=duration + ) else: if offset == "fromnow": - issue_filter[f"{date_filter}__lte"] = now + timedelta(weeks=duration) + issue_filter[f"{date_filter}__lte"] = now + timedelta( + weeks=duration + ) else: - issue_filter[f"{date_filter}__lte"] = now - timedelta(weeks=duration) + issue_filter[f"{date_filter}__lte"] = now - timedelta( + weeks=duration + ) def date_filter(issue_filter, date_term, queries): @@ -120,7 +128,9 @@ def filter_state_group(params, issue_filter, method, prefix=""): and len(params.get("state_group")) and params.get("state_group") != "null" ): - issue_filter[f"{prefix}state__group__in"] = params.get("state_group") + issue_filter[f"{prefix}state__group__in"] = params.get( + "state_group" + ) return issue_filter @@ -242,8 +252,8 @@ def filter_mentions(params, issue_filter, method, prefix=""): and len(params.get("mentions")) and params.get("mentions") != "null" ): - issue_filter[f"{prefix}issue_mention__mention__id__in"] = params.get( - "mentions" + issue_filter[f"{prefix}issue_mention__mention__id__in"] = ( + params.get("mentions") ) return issue_filter @@ -411,7 +421,10 @@ def filter_cycle(params, issue_filter, method, prefix=""): and len(params.get("cycle")) and params.get("cycle") != "null" ): - issue_filter[f"{prefix}issue_cycle__cycle_id__in"] = params.get("cycle") + issue_filter[f"{prefix}issue_cycle__cycle_id__in"] = params.get( + "cycle" + ) + issue_filter[f"{prefix}issue_cycle__deleted_at__isnull"] = True return issue_filter @@ -434,6 +447,7 @@ def filter_module(params, issue_filter, method, prefix=""): issue_filter[f"{prefix}issue_module__module_id__in"] = params.get( "module" ) + issue_filter[f"{prefix}issue_module__deleted_at__isnull"] = True return issue_filter