diff --git a/apiserver/back_migration.py b/apiserver/back_migration.py index 328b9db2b..69eb2f1df 100644 --- a/apiserver/back_migration.py +++ b/apiserver/back_migration.py @@ -26,9 +26,7 @@ def update_description(): updated_issues.append(issue) Issue.objects.bulk_update( - updated_issues, - ["description_html", "description_stripped"], - batch_size=100, + updated_issues, ["description_html", "description_stripped"], batch_size=100 ) print("Success") except Exception as e: @@ -42,9 +40,7 @@ def update_comments(): updated_issue_comments = [] for issue_comment in issue_comments: - issue_comment.comment_html = ( - f"

{issue_comment.comment_stripped}

" - ) + issue_comment.comment_html = f"

{issue_comment.comment_stripped}

" updated_issue_comments.append(issue_comment) IssueComment.objects.bulk_update( @@ -103,9 +99,7 @@ def updated_issue_sort_order(): issue.sort_order = issue.sequence_id * random.randint(100, 500) updated_issues.append(issue) - Issue.objects.bulk_update( - updated_issues, ["sort_order"], batch_size=100 - ) + Issue.objects.bulk_update(updated_issues, ["sort_order"], batch_size=100) print("Success") except Exception as e: print(e) @@ -143,9 +137,7 @@ def update_project_cover_images(): project.cover_image = project_cover_images[random.randint(0, 19)] updated_projects.append(project) - Project.objects.bulk_update( - updated_projects, ["cover_image"], batch_size=100 - ) + Project.objects.bulk_update(updated_projects, ["cover_image"], batch_size=100) print("Success") except Exception as e: print(e) @@ -194,9 +186,7 @@ def update_label_color(): def create_slack_integration(): try: - _ = Integration.objects.create( - provider="slack", network=2, title="Slack" - ) + _ = Integration.objects.create(provider="slack", network=2, title="Slack") print("Success") except Exception as e: print(e) @@ -222,16 +212,12 @@ def update_integration_verified(): def update_start_date(): try: - issues = Issue.objects.filter( - state__group__in=["started", "completed"] - ) + issues = Issue.objects.filter(state__group__in=["started", "completed"]) updated_issues = [] for issue in issues: issue.start_date = issue.created_at.date() updated_issues.append(issue) - Issue.objects.bulk_update( - updated_issues, ["start_date"], batch_size=500 - ) + Issue.objects.bulk_update(updated_issues, ["start_date"], batch_size=500) print("Success") except Exception as e: print(e) diff --git a/apiserver/manage.py b/apiserver/manage.py index 744086783..972869462 100644 --- a/apiserver/manage.py +++ b/apiserver/manage.py @@ -3,9 +3,7 @@ import os import sys if __name__ == "__main__": - os.environ.setdefault( - "DJANGO_SETTINGS_MODULE", "plane.settings.production" - ) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "plane.settings.production") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/apiserver/plane/api/middleware/api_authentication.py b/apiserver/plane/api/middleware/api_authentication.py index 893df7f84..ddabb4132 100644 --- a/apiserver/plane/api/middleware/api_authentication.py +++ b/apiserver/plane/api/middleware/api_authentication.py @@ -25,10 +25,7 @@ class APIKeyAuthentication(authentication.BaseAuthentication): def validate_api_token(self, token): try: api_token = APIToken.objects.get( - Q( - Q(expired_at__gt=timezone.now()) - | Q(expired_at__isnull=True) - ), + Q(Q(expired_at__gt=timezone.now()) | Q(expired_at__isnull=True)), token=token, is_active=True, ) diff --git a/apiserver/plane/api/rate_limit.py b/apiserver/plane/api/rate_limit.py index 65cfd6b0c..a70ae4186 100644 --- a/apiserver/plane/api/rate_limit.py +++ b/apiserver/plane/api/rate_limit.py @@ -80,4 +80,4 @@ class ServiceTokenRateThrottle(SimpleRateThrottle): request.META["X-RateLimit-Remaining"] = max(0, available) request.META["X-RateLimit-Reset"] = reset_time - return allowed \ No newline at end of file + return allowed diff --git a/apiserver/plane/api/serializers/__init__.py b/apiserver/plane/api/serializers/__init__.py index 1f17e053e..b7780a90c 100644 --- a/apiserver/plane/api/serializers/__init__.py +++ b/apiserver/plane/api/serializers/__init__.py @@ -13,9 +13,5 @@ from .issue import ( ) from .state import StateLiteSerializer, StateSerializer from .cycle import CycleSerializer, CycleIssueSerializer, CycleLiteSerializer -from .module import ( - ModuleSerializer, - ModuleIssueSerializer, - ModuleLiteSerializer, -) +from .module import ModuleSerializer, ModuleIssueSerializer, ModuleLiteSerializer from .intake import IntakeIssueSerializer diff --git a/apiserver/plane/api/serializers/base.py b/apiserver/plane/api/serializers/base.py index 5653ba318..d51434554 100644 --- a/apiserver/plane/api/serializers/base.py +++ b/apiserver/plane/api/serializers/base.py @@ -102,8 +102,6 @@ class BaseSerializer(serializers.ModelSerializer): response[expand] = exp_serializer.data else: # You might need to handle this case differently - response[expand] = getattr( - instance, f"{expand}_id", None - ) + response[expand] = getattr(instance, f"{expand}_id", None) return response diff --git a/apiserver/plane/api/serializers/cycle.py b/apiserver/plane/api/serializers/cycle.py index 90e3e1b42..f4f06c324 100644 --- a/apiserver/plane/api/serializers/cycle.py +++ b/apiserver/plane/api/serializers/cycle.py @@ -23,9 +23,7 @@ class CycleSerializer(BaseSerializer): 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" - ) + raise serializers.ValidationError("Start date cannot exceed end date") return data class Meta: @@ -50,11 +48,7 @@ class CycleIssueSerializer(BaseSerializer): class Meta: model = CycleIssue fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "cycle", - ] + read_only_fields = ["workspace", "project", "cycle"] class CycleLiteSerializer(BaseSerializer): diff --git a/apiserver/plane/api/serializers/intake.py b/apiserver/plane/api/serializers/intake.py index 0b207c704..69c85ed61 100644 --- a/apiserver/plane/api/serializers/intake.py +++ b/apiserver/plane/api/serializers/intake.py @@ -6,7 +6,6 @@ from rest_framework import serializers class IntakeIssueSerializer(BaseSerializer): - issue_detail = IssueExpandSerializer(read_only=True, source="issue") inbox = serializers.UUIDField(source="intake.id", read_only=True) diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 905157339..72918b268 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -49,25 +49,13 @@ class IssueSerializer(BaseSerializer): required=False, ) type_id = serializers.PrimaryKeyRelatedField( - source="type", - queryset=IssueType.objects.all(), - required=False, - allow_null=True, + source="type", queryset=IssueType.objects.all(), required=False, allow_null=True ) class Meta: model = Issue - read_only_fields = [ - "id", - "workspace", - "project", - "updated_by", - "updated_at", - ] - exclude = [ - "description", - "description_stripped", - ] + read_only_fields = ["id", "workspace", "project", "updated_by", "updated_at"] + exclude = ["description", "description_stripped"] def validate(self, data): if ( @@ -75,9 +63,7 @@ class IssueSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError( - "Start date cannot exceed target date" - ) + raise serializers.ValidationError("Start date cannot exceed target date") try: if data.get("description_html", None) is not None: @@ -99,16 +85,14 @@ class IssueSerializer(BaseSerializer): # Validate labels are from project if data.get("labels", []): data["labels"] = Label.objects.filter( - project_id=self.context.get("project_id"), - id__in=data["labels"], + project_id=self.context.get("project_id"), id__in=data["labels"] ).values_list("id", flat=True) # Check state is from the project only else raise validation error if ( data.get("state") and not State.objects.filter( - project_id=self.context.get("project_id"), - pk=data.get("state").id, + project_id=self.context.get("project_id"), pk=data.get("state").id ).exists() ): raise serializers.ValidationError( @@ -119,8 +103,7 @@ class IssueSerializer(BaseSerializer): if ( data.get("parent") and not Issue.objects.filter( - workspace_id=self.context.get("workspace_id"), - pk=data.get("parent").id, + workspace_id=self.context.get("workspace_id"), pk=data.get("parent").id ).exists() ): raise serializers.ValidationError( @@ -147,9 +130,7 @@ class IssueSerializer(BaseSerializer): issue_type = issue_type issue = Issue.objects.create( - **validated_data, - project_id=project_id, - type=issue_type, + **validated_data, project_id=project_id, type=issue_type ) # Issue Audit Users @@ -264,13 +245,9 @@ class IssueSerializer(BaseSerializer): ] if "labels" in self.fields: if "labels" in self.expand: - data["labels"] = LabelSerializer( - instance.labels.all(), many=True - ).data + data["labels"] = LabelSerializer(instance.labels.all(), many=True).data else: - data["labels"] = [ - str(label.id) for label in instance.labels.all() - ] + data["labels"] = [str(label.id) for label in instance.labels.all()] return data @@ -278,11 +255,7 @@ class IssueSerializer(BaseSerializer): class IssueLiteSerializer(BaseSerializer): class Meta: model = Issue - fields = [ - "id", - "sequence_id", - "project_id", - ] + fields = ["id", "sequence_id", "project_id"] read_only_fields = fields @@ -334,8 +307,7 @@ class IssueLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if IssueLink.objects.filter( - url=validated_data.get("url"), - issue_id=validated_data.get("issue_id"), + url=validated_data.get("url"), issue_id=validated_data.get("issue_id") ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -345,8 +317,7 @@ class IssueLinkSerializer(BaseSerializer): def update(self, instance, validated_data): if ( IssueLink.objects.filter( - url=validated_data.get("url"), - issue_id=instance.issue_id, + url=validated_data.get("url"), issue_id=instance.issue_id ) .exclude(pk=instance.id) .exists() @@ -387,10 +358,7 @@ class IssueCommentSerializer(BaseSerializer): "created_at", "updated_at", ] - exclude = [ - "comment_stripped", - "comment_json", - ] + exclude = ["comment_stripped", "comment_json"] def validate(self, data): try: @@ -407,38 +375,27 @@ class IssueCommentSerializer(BaseSerializer): class IssueActivitySerializer(BaseSerializer): class Meta: model = IssueActivity - exclude = [ - "created_by", - "updated_by", - ] + exclude = ["created_by", "updated_by"] class CycleIssueSerializer(BaseSerializer): cycle = CycleSerializer(read_only=True) class Meta: - fields = [ - "cycle", - ] + fields = ["cycle"] class ModuleIssueSerializer(BaseSerializer): module = ModuleSerializer(read_only=True) class Meta: - fields = [ - "module", - ] + fields = ["module"] class LabelLiteSerializer(BaseSerializer): class Meta: model = Label - fields = [ - "id", - "name", - "color", - ] + fields = ["id", "name", "color"] class IssueExpandSerializer(BaseSerializer): diff --git a/apiserver/plane/api/serializers/module.py b/apiserver/plane/api/serializers/module.py index c1a0c577d..ace4e15c8 100644 --- a/apiserver/plane/api/serializers/module.py +++ b/apiserver/plane/api/serializers/module.py @@ -53,14 +53,11 @@ class ModuleSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError( - "Start date cannot exceed target date" - ) + raise serializers.ValidationError("Start date cannot exceed target date") if data.get("members", []): data["members"] = ProjectMember.objects.filter( - project_id=self.context.get("project_id"), - member_id__in=data["members"], + project_id=self.context.get("project_id"), member_id__in=data["members"] ).values_list("member_id", flat=True) return data @@ -74,9 +71,7 @@ class ModuleSerializer(BaseSerializer): module_name = validated_data.get("name") if module_name: # Lookup for the module name in the module table for that project - if Module.objects.filter( - name=module_name, project_id=project_id - ).exists(): + if Module.objects.filter(name=module_name, project_id=project_id).exists(): raise serializers.ValidationError( {"error": "Module with this name already exists"} ) @@ -107,9 +102,7 @@ class ModuleSerializer(BaseSerializer): if module_name: # Lookup for the module name in the module table for that project if ( - Module.objects.filter( - name=module_name, project=instance.project - ) + Module.objects.filter(name=module_name, project=instance.project) .exclude(id=instance.id) .exists() ): @@ -172,8 +165,7 @@ class ModuleLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if ModuleLink.objects.filter( - url=validated_data.get("url"), - module_id=validated_data.get("module_id"), + url=validated_data.get("url"), module_id=validated_data.get("module_id") ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} diff --git a/apiserver/plane/api/serializers/project.py b/apiserver/plane/api/serializers/project.py index d9c3e2c2a..744084ab1 100644 --- a/apiserver/plane/api/serializers/project.py +++ b/apiserver/plane/api/serializers/project.py @@ -2,11 +2,7 @@ from rest_framework import serializers # Module imports -from plane.db.models import ( - Project, - ProjectIdentifier, - WorkspaceMember, -) +from plane.db.models import Project, ProjectIdentifier, WorkspaceMember from .base import BaseSerializer @@ -67,16 +63,12 @@ class ProjectSerializer(BaseSerializer): def create(self, validated_data): identifier = validated_data.get("identifier", "").strip().upper() if identifier == "": - raise serializers.ValidationError( - detail="Project Identifier is required" - ) + raise serializers.ValidationError(detail="Project Identifier is required") if ProjectIdentifier.objects.filter( name=identifier, workspace_id=self.context["workspace_id"] ).exists(): - raise serializers.ValidationError( - detail="Project Identifier is taken" - ) + raise serializers.ValidationError(detail="Project Identifier is taken") project = Project.objects.create( **validated_data, workspace_id=self.context["workspace_id"] diff --git a/apiserver/plane/api/serializers/state.py b/apiserver/plane/api/serializers/state.py index f4ffcba85..85b4c41ed 100644 --- a/apiserver/plane/api/serializers/state.py +++ b/apiserver/plane/api/serializers/state.py @@ -7,9 +7,9 @@ class StateSerializer(BaseSerializer): def validate(self, data): # If the default is being provided then make all other states default False if data.get("default", False): - State.objects.filter( - project_id=self.context.get("project_id") - ).update(default=False) + State.objects.filter(project_id=self.context.get("project_id")).update( + default=False + ) return data class Meta: @@ -30,10 +30,5 @@ class StateSerializer(BaseSerializer): class StateLiteSerializer(BaseSerializer): class Meta: model = State - fields = [ - "id", - "name", - "color", - "group", - ] + fields = ["id", "name", "color", "group"] read_only_fields = fields diff --git a/apiserver/plane/api/serializers/workspace.py b/apiserver/plane/api/serializers/workspace.py index a47de3d31..84453b8e0 100644 --- a/apiserver/plane/api/serializers/workspace.py +++ b/apiserver/plane/api/serializers/workspace.py @@ -8,9 +8,5 @@ class WorkspaceLiteSerializer(BaseSerializer): class Meta: model = Workspace - fields = [ - "name", - "slug", - "id", - ] + fields = ["name", "slug", "id"] read_only_fields = fields diff --git a/apiserver/plane/api/urls/member.py b/apiserver/plane/api/urls/member.py index 5fe1785a7..1ec9cddb3 100644 --- a/apiserver/plane/api/urls/member.py +++ b/apiserver/plane/api/urls/member.py @@ -1,13 +1,11 @@ from django.urls import path -from plane.api.views import ( - ProjectMemberAPIEndpoint, -) +from plane.api.views import ProjectMemberAPIEndpoint urlpatterns = [ path( "workspaces//projects//members/", ProjectMemberAPIEndpoint.as_view(), name="users", - ), + ) ] diff --git a/apiserver/plane/api/urls/project.py b/apiserver/plane/api/urls/project.py index 5efb85bb0..d35c2cdd5 100644 --- a/apiserver/plane/api/urls/project.py +++ b/apiserver/plane/api/urls/project.py @@ -1,15 +1,10 @@ from django.urls import path -from plane.api.views import ( - ProjectAPIEndpoint, - ProjectArchiveUnarchiveAPIEndpoint, -) +from plane.api.views import ProjectAPIEndpoint, ProjectArchiveUnarchiveAPIEndpoint urlpatterns = [ path( - "workspaces//projects/", - ProjectAPIEndpoint.as_view(), - name="project", + "workspaces//projects/", ProjectAPIEndpoint.as_view(), name="project" ), path( "workspaces//projects//", diff --git a/apiserver/plane/api/views/base.py b/apiserver/plane/api/views/base.py index a3241eaf3..c79c2f853 100644 --- a/apiserver/plane/api/views/base.py +++ b/apiserver/plane/api/views/base.py @@ -37,13 +37,9 @@ class TimezoneMixin: class BaseAPIView(TimezoneMixin, APIView, BasePaginator): - authentication_classes = [ - APIKeyAuthentication, - ] + authentication_classes = [APIKeyAuthentication] - permission_classes = [ - IsAuthenticated, - ] + permission_classes = [IsAuthenticated] def filter_queryset(self, queryset): for backend in list(self.filter_backends): @@ -56,8 +52,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): if api_key: service_token = APIToken.objects.filter( - token=api_key, - is_service=True, + token=api_key, is_service=True ).first() if service_token: @@ -123,9 +118,7 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): def finalize_response(self, request, response, *args, **kwargs): # Call super to get the default response - response = super().finalize_response( - request, response, *args, **kwargs - ) + response = super().finalize_response(request, response, *args, **kwargs) # Add custom headers if they exist in the request META ratelimit_remaining = request.META.get("X-RateLimit-Remaining") @@ -154,17 +147,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): @property def fields(self): fields = [ - field - for field in self.request.GET.get("fields", "").split(",") - if field + field for field in self.request.GET.get("fields", "").split(",") if field ] return fields if fields else None @property def expand(self): expand = [ - expand - for expand in self.request.GET.get("expand", "").split(",") - if expand + expand for expand in self.request.GET.get("expand", "").split(",") if expand ] - return expand if expand else None \ No newline at end of file + return expand if expand else None diff --git a/apiserver/plane/api/views/cycle.py b/apiserver/plane/api/views/cycle.py index a4a61a928..3665e3b0f 100644 --- a/apiserver/plane/api/views/cycle.py +++ b/apiserver/plane/api/views/cycle.py @@ -25,10 +25,7 @@ from rest_framework import status from rest_framework.response import Response # Module imports -from plane.api.serializers import ( - CycleIssueSerializer, - CycleSerializer, -) +from plane.api.serializers import CycleIssueSerializer, CycleSerializer from plane.app.permissions import ProjectEntityPermission from plane.bgtasks.issue_activities_task import issue_activity from plane.db.models import ( @@ -57,9 +54,7 @@ class CycleAPIEndpoint(BaseAPIView): serializer_class = CycleSerializer model = Cycle webhook_event = "cycle" - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): return ( @@ -143,26 +138,18 @@ class CycleAPIEndpoint(BaseAPIView): def get(self, request, slug, project_id, pk=None): if pk: - queryset = ( - self.get_queryset().filter(archived_at__isnull=True).get(pk=pk) - ) + queryset = self.get_queryset().filter(archived_at__isnull=True).get(pk=pk) data = CycleSerializer( - queryset, - fields=self.fields, - expand=self.expand, + queryset, fields=self.fields, expand=self.expand ).data - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) queryset = self.get_queryset().filter(archived_at__isnull=True) cycle_view = request.GET.get("cycle_view", "all") # Current Cycle if cycle_view == "current": queryset = queryset.filter( - start_date__lte=timezone.now(), - end_date__gte=timezone.now(), + start_date__lte=timezone.now(), end_date__gte=timezone.now() ) data = CycleSerializer( queryset, many=True, fields=self.fields, expand=self.expand @@ -176,10 +163,7 @@ class CycleAPIEndpoint(BaseAPIView): request=request, queryset=(queryset), on_results=lambda cycles: CycleSerializer( - cycles, - many=True, - fields=self.fields, - expand=self.expand, + cycles, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -190,53 +174,38 @@ class CycleAPIEndpoint(BaseAPIView): request=request, queryset=(queryset), on_results=lambda cycles: CycleSerializer( - cycles, - many=True, - fields=self.fields, - expand=self.expand, + cycles, many=True, fields=self.fields, expand=self.expand ).data, ) # Draft Cycles if cycle_view == "draft": - queryset = queryset.filter( - end_date=None, - start_date=None, - ) + queryset = queryset.filter(end_date=None, start_date=None) return self.paginate( request=request, queryset=(queryset), on_results=lambda cycles: CycleSerializer( - cycles, - many=True, - fields=self.fields, - expand=self.expand, + cycles, many=True, fields=self.fields, expand=self.expand ).data, ) # Incomplete Cycles if cycle_view == "incomplete": queryset = queryset.filter( - Q(end_date__gte=timezone.now()) | Q(end_date__isnull=True), + Q(end_date__gte=timezone.now()) | Q(end_date__isnull=True) ) return self.paginate( request=request, queryset=(queryset), on_results=lambda cycles: CycleSerializer( - cycles, - many=True, - fields=self.fields, - expand=self.expand, + cycles, many=True, fields=self.fields, expand=self.expand ).data, ) return self.paginate( request=request, queryset=(queryset), on_results=lambda cycles: CycleSerializer( - cycles, - many=True, - fields=self.fields, - expand=self.expand, + cycles, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -273,10 +242,7 @@ class CycleAPIEndpoint(BaseAPIView): }, status=status.HTTP_409_CONFLICT, ) - serializer.save( - project_id=project_id, - owned_by=request.user, - ) + serializer.save(project_id=project_id, owned_by=request.user) # Send the model activity model_activity.delay( model_name="cycle", @@ -287,12 +253,8 @@ class CycleAPIEndpoint(BaseAPIView): slug=slug, origin=request.META.get("HTTP_ORIGIN"), ) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: return Response( { @@ -302,9 +264,7 @@ class CycleAPIEndpoint(BaseAPIView): ) def patch(self, request, slug, project_id, pk): - cycle = Cycle.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) current_instance = json.dumps( CycleSerializer(cycle).data, cls=DjangoJSONEncoder @@ -322,9 +282,7 @@ class CycleAPIEndpoint(BaseAPIView): if "sort_order" in request_data: # Can only change sort order request_data = { - "sort_order": request_data.get( - "sort_order", cycle.sort_order - ) + "sort_order": request_data.get("sort_order", cycle.sort_order) } else: return Response( @@ -371,9 +329,7 @@ class CycleAPIEndpoint(BaseAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, slug, project_id, pk): - cycle = Cycle.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) if cycle.owned_by_id != request.user.id and ( not ProjectMember.objects.filter( workspace__slug=slug, @@ -389,9 +345,9 @@ class CycleAPIEndpoint(BaseAPIView): ) cycle_issues = list( - CycleIssue.objects.filter( - cycle_id=self.kwargs.get("pk") - ).values_list("issue", flat=True) + CycleIssue.objects.filter(cycle_id=self.kwargs.get("pk")).values_list( + "issue", flat=True + ) ) issue_activity.delay( @@ -413,17 +369,13 @@ class CycleAPIEndpoint(BaseAPIView): cycle.delete() # Delete the user favorite cycle UserFavorite.objects.filter( - entity_type="cycle", - entity_identifier=pk, - project_id=project_id, + entity_type="cycle", entity_identifier=pk, project_id=project_id ).delete() return Response(status=status.HTTP_204_NO_CONTENT) class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): return ( @@ -502,9 +454,7 @@ class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView): ), ) ) - .annotate( - total_estimates=Sum("issue_cycle__issue__estimate_point") - ) + .annotate(total_estimates=Sum("issue_cycle__issue__estimate_point")) .annotate( completed_estimates=Sum( "issue_cycle__issue__estimate_point", @@ -536,10 +486,7 @@ class CycleArchiveUnarchiveAPIEndpoint(BaseAPIView): request=request, queryset=(self.get_queryset()), on_results=lambda cycles: CycleSerializer( - cycles, - many=True, - fields=self.fields, - expand=self.expand, + cycles, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -582,16 +529,12 @@ class CycleIssueAPIEndpoint(BaseAPIView): model = CycleIssue webhook_event = "cycle_issue" bulk = True - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): return ( CycleIssue.objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("issue_id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue_id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -630,13 +573,10 @@ class CycleIssueAPIEndpoint(BaseAPIView): order_by = request.GET.get("order_by", "created_at") issues = ( Issue.issue_objects.filter( - issue_cycle__cycle_id=cycle_id, - issue_cycle__deleted_at__isnull=True, + issue_cycle__cycle_id=cycle_id, issue_cycle__deleted_at__isnull=True ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -672,10 +612,7 @@ class CycleIssueAPIEndpoint(BaseAPIView): request=request, queryset=(issues), on_results=lambda issues: CycleSerializer( - issues, - many=True, - fields=self.fields, - expand=self.expand, + issues, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -684,8 +621,7 @@ class CycleIssueAPIEndpoint(BaseAPIView): if not issues: return Response( - {"error": "Issues are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST ) cycle = Cycle.objects.get( @@ -694,9 +630,7 @@ class CycleIssueAPIEndpoint(BaseAPIView): # Get all CycleIssues already created cycle_issues = list( - CycleIssue.objects.filter( - ~Q(cycle_id=cycle_id), issue_id__in=issues - ) + CycleIssue.objects.filter(~Q(cycle_id=cycle_id), issue_id__in=issues) ) existing_issues = [ @@ -741,9 +675,7 @@ class CycleIssueAPIEndpoint(BaseAPIView): ) # 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( @@ -802,9 +734,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): """ - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def post(self, request, slug, project_id, cycle_id): new_cycle_id = request.data.get("new_cycle_id", False) @@ -930,9 +860,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): ) .values("display_name", "assignee_id", "avatar", "avatar_url") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -961,9 +889,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): { "display_name": item["display_name"], "assignee_id": ( - str(item["assignee_id"]) - if item["assignee_id"] - else None + str(item["assignee_id"]) if item["assignee_id"] else None ), "avatar": item.get("avatar", None), "avatar_url": item.get("avatar_url", None), @@ -986,9 +912,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): .annotate(label_id=F("labels__id")) .values("label_name", "color", "label_id") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -1025,9 +949,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): { "label_name": item["label_name"], "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_estimates": item["total_estimates"], "completed_estimates": item["completed_estimates"], "pending_estimates": item["pending_estimates"], @@ -1059,8 +981,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): ), # If `avatar_asset` is None, fall back to using `avatar` field directly When( - assignees__avatar_asset__isnull=True, - then="assignees__avatar", + assignees__avatar_asset__isnull=True, then="assignees__avatar" ), default=Value(None), output_field=models.CharField(), @@ -1069,12 +990,8 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): .values("display_name", "assignee_id", "avatar_url") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -1128,12 +1045,8 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -1163,9 +1076,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): { "label_name": item["label_name"], "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"], "completed_issues": item["completed_issues"], "pending_issues": item["pending_issues"], @@ -1210,10 +1121,7 @@ class TransferCycleIssueAPIEndpoint(BaseAPIView): } current_cycle.save(update_fields=["progress_snapshot"]) - if ( - new_cycle.end_date is not None - and new_cycle.end_date < timezone.now() - ): + if new_cycle.end_date is not None and new_cycle.end_date < timezone.now(): return Response( { "error": "The cycle where the issues are transferred is already completed" diff --git a/apiserver/plane/api/views/intake.py b/apiserver/plane/api/views/intake.py index 95354f235..c2d0733ba 100644 --- a/apiserver/plane/api/views/intake.py +++ b/apiserver/plane/api/views/intake.py @@ -17,14 +17,7 @@ from rest_framework.response import Response from plane.api.serializers import IntakeIssueSerializer, IssueSerializer from plane.app.permissions import ProjectLitePermission from plane.bgtasks.issue_activities_task import issue_activity -from plane.db.models import ( - Intake, - IntakeIssue, - Issue, - Project, - ProjectMember, - State, -) +from plane.db.models import Intake, IntakeIssue, Issue, Project, ProjectMember, State from .base import BaseAPIView @@ -36,16 +29,12 @@ class IntakeIssueAPIEndpoint(BaseAPIView): """ - permission_classes = [ - ProjectLitePermission, - ] + permission_classes = [ProjectLitePermission] serializer_class = IntakeIssueSerializer model = IntakeIssue - filterset_fields = [ - "status", - ] + filterset_fields = ["status"] def get_queryset(self): intake = Intake.objects.filter( @@ -54,8 +43,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): ).first() project = Project.objects.get( - workspace__slug=self.kwargs.get("slug"), - pk=self.kwargs.get("project_id"), + workspace__slug=self.kwargs.get("slug"), pk=self.kwargs.get("project_id") ) if intake is None and not project.intake_view: @@ -63,8 +51,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): return ( IntakeIssue.objects.filter( - Q(snoozed_till__gte=timezone.now()) - | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), workspace__slug=self.kwargs.get("slug"), project_id=self.kwargs.get("project_id"), intake_id=intake.id, @@ -77,41 +64,29 @@ class IntakeIssueAPIEndpoint(BaseAPIView): if issue_id: intake_issue_queryset = self.get_queryset().get(issue_id=issue_id) intake_issue_data = IntakeIssueSerializer( - intake_issue_queryset, - fields=self.fields, - expand=self.expand, + intake_issue_queryset, fields=self.fields, expand=self.expand ).data - return Response( - intake_issue_data, - status=status.HTTP_200_OK, - ) + return Response(intake_issue_data, status=status.HTTP_200_OK) issue_queryset = self.get_queryset() return self.paginate( request=request, queryset=(issue_queryset), on_results=lambda intake_issues: IntakeIssueSerializer( - intake_issues, - many=True, - fields=self.fields, - expand=self.expand, + intake_issues, many=True, fields=self.fields, expand=self.expand ).data, ) def post(self, request, slug, project_id): if not request.data.get("issue", {}).get("name", False): return Response( - {"error": "Name is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST ) intake = Intake.objects.filter( workspace__slug=slug, project_id=project_id ).first() - project = Project.objects.get( - workspace__slug=slug, - pk=project_id, - ) + project = Project.objects.get(workspace__slug=slug, pk=project_id) # Intake view if intake is None and not project.intake_view: @@ -131,8 +106,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): "none", ]: return Response( - {"error": "Invalid priority"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Invalid priority"}, status=status.HTTP_400_BAD_REQUEST ) # Create or get state @@ -184,10 +158,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): workspace__slug=slug, project_id=project_id ).first() - project = Project.objects.get( - workspace__slug=slug, - pk=project_id, - ) + project = Project.objects.get(workspace__slug=slug, pk=project_id) # Intake view if intake is None and not project.intake_view: @@ -234,7 +205,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -251,11 +222,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): ), Value([], output_field=ArrayField(UUIDField())), ), - ).get( - pk=issue_id, - workspace__slug=slug, - project_id=project_id, - ) + ).get(pk=issue_id, workspace__slug=slug, project_id=project_id) # Only allow guests to edit name and description if project_member.role <= 5: issue_data = { @@ -263,14 +230,10 @@ class IntakeIssueAPIEndpoint(BaseAPIView): "description_html": issue_data.get( "description_html", issue.description_html ), - "description": issue_data.get( - "description", issue.description - ), + "description": issue_data.get("description", issue.description), } - issue_serializer = IssueSerializer( - issue, data=issue_data, partial=True - ) + issue_serializer = IssueSerializer(issue, data=issue_data, partial=True) if issue_serializer.is_valid(): current_instance = issue @@ -310,14 +273,10 @@ class IntakeIssueAPIEndpoint(BaseAPIView): # Update the issue state if the issue is rejected or marked as duplicate if serializer.data["status"] in [-1, 2]: issue = Issue.objects.get( - pk=issue_id, - workspace__slug=slug, - project_id=project_id, + pk=issue_id, workspace__slug=slug, project_id=project_id ) state = State.objects.filter( - group="cancelled", - workspace__slug=slug, - project_id=project_id, + group="cancelled", workspace__slug=slug, project_id=project_id ).first() if state is not None: issue.state = state @@ -326,18 +285,14 @@ class IntakeIssueAPIEndpoint(BaseAPIView): # Update the issue state if it is accepted if serializer.data["status"] in [1]: issue = Issue.objects.get( - pk=issue_id, - workspace__slug=slug, - project_id=project_id, + pk=issue_id, workspace__slug=slug, project_id=project_id ) # Update the issue state only if it is in triage state if issue.state.is_triage: # Move to default state state = State.objects.filter( - workspace__slug=slug, - project_id=project_id, - default=True, + workspace__slug=slug, project_id=project_id, default=True ).first() if state is not None: issue.state = state @@ -346,9 +301,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): # create a activity for status change issue_activity.delay( type="intake.activity.created", - requested_data=json.dumps( - request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(request.data, cls=DjangoJSONEncoder), actor_id=str(request.user.id), issue_id=str(issue_id), project_id=str(project_id), @@ -360,13 +313,10 @@ class IntakeIssueAPIEndpoint(BaseAPIView): ) return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: return Response( - IntakeIssueSerializer(intake_issue).data, - status=status.HTTP_200_OK, + IntakeIssueSerializer(intake_issue).data, status=status.HTTP_200_OK ) def delete(self, request, slug, project_id, issue_id): @@ -374,10 +324,7 @@ class IntakeIssueAPIEndpoint(BaseAPIView): workspace__slug=slug, project_id=project_id ).first() - project = Project.objects.get( - workspace__slug=slug, - pk=project_id, - ) + project = Project.objects.get(workspace__slug=slug, pk=project_id) # Intake view if intake is None and not project.intake_view: diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index 26945a766..df7b9aec2 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -73,9 +73,7 @@ class WorkspaceIssueAPIEndpoint(BaseAPIView): def get_queryset(self): return ( Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -91,14 +89,10 @@ class WorkspaceIssueAPIEndpoint(BaseAPIView): .order_by(self.kwargs.get("order_by", "-created_at")) ).distinct() - def get( - self, request, slug, project__identifier=None, issue__identifier=None - ): + def get(self, request, slug, project__identifier=None, issue__identifier=None): if issue__identifier and project__identifier: issue = Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -108,11 +102,7 @@ class WorkspaceIssueAPIEndpoint(BaseAPIView): sequence_id=issue__identifier, ) return Response( - IssueSerializer( - issue, - fields=self.fields, - expand=self.expand, - ).data, + IssueSerializer(issue, fields=self.fields, expand=self.expand).data, status=status.HTTP_200_OK, ) @@ -126,17 +116,13 @@ class IssueAPIEndpoint(BaseAPIView): model = Issue webhook_event = "issue" - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] serializer_class = IssueSerializer def get_queryset(self): return ( Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -164,41 +150,25 @@ class IssueAPIEndpoint(BaseAPIView): project_id=project_id, ) return Response( - IssueSerializer( - issue, - fields=self.fields, - expand=self.expand, - ).data, + IssueSerializer(issue, fields=self.fields, expand=self.expand).data, status=status.HTTP_200_OK, ) if pk: issue = Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") ).get(workspace__slug=slug, project_id=project_id, pk=pk) return Response( - IssueSerializer( - issue, - fields=self.fields, - expand=self.expand, - ).data, + IssueSerializer(issue, fields=self.fields, expand=self.expand).data, status=status.HTTP_200_OK, ) # Custom ordering for priority and state priority_order = ["urgent", "high", "medium", "low", "none"] - state_order = [ - "backlog", - "unstarted", - "started", - "completed", - "cancelled", - ] + state_order = ["backlog", "unstarted", "started", "completed", "cancelled"] order_by_param = request.GET.get("order_by", "-created_at") @@ -231,9 +201,7 @@ class IssueAPIEndpoint(BaseAPIView): # 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] + priority_order if order_by_param == "priority" else priority_order[::-1] ) issue_queryset = issue_queryset.annotate( priority_order=Case( @@ -281,9 +249,7 @@ class IssueAPIEndpoint(BaseAPIView): else order_by_param ) ).order_by( - "-max_values" - if order_by_param.startswith("-") - else "max_values" + "-max_values" if order_by_param.startswith("-") else "max_values" ) else: issue_queryset = issue_queryset.order_by(order_by_param) @@ -292,10 +258,7 @@ class IssueAPIEndpoint(BaseAPIView): request=request, queryset=(issue_queryset), on_results=lambda issues: IssueSerializer( - issues, - many=True, - fields=self.fields, - expand=self.expand, + issues, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -339,22 +302,16 @@ class IssueAPIEndpoint(BaseAPIView): serializer.save() # Refetch the issue issue = Issue.objects.filter( - workspace__slug=slug, - project_id=project_id, - pk=serializer.data["id"], + workspace__slug=slug, project_id=project_id, pk=serializer.data["id"] ).first() issue.created_at = request.data.get("created_at", timezone.now()) - issue.created_by_id = request.data.get( - "created_by", request.user.id - ) + issue.created_by_id = request.data.get("created_by", request.user.id) issue.save(update_fields=["created_at", "created_by"]) # Track the issue issue_activity.delay( type="issue.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(request.user.id), issue_id=str(serializer.data.get("id", None)), project_id=str(project_id), @@ -391,9 +348,7 @@ class IssueAPIEndpoint(BaseAPIView): # Get the requested data, encode it as django object and pass it # to serializer to validation - requested_data = json.dumps( - self.request.data, cls=DjangoJSONEncoder - ) + requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) serializer = IssueSerializer( issue, data=request.data, @@ -451,9 +406,7 @@ class IssueAPIEndpoint(BaseAPIView): # If any of the created_at or created_by is present, update # the issue with the provided data, else return with the # default states given. - issue.created_at = request.data.get( - "created_at", timezone.now() - ) + issue.created_at = request.data.get("created_at", timezone.now()) issue.created_by_id = request.data.get( "created_by", request.user.id ) @@ -470,12 +423,8 @@ class IssueAPIEndpoint(BaseAPIView): current_instance=None, epoch=int(timezone.now().timestamp()), ) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: return Response( {"error": "external_id and external_source are required"}, @@ -483,9 +432,7 @@ class IssueAPIEndpoint(BaseAPIView): ) def patch(self, request, slug, project_id, pk=None): - issue = Issue.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) project = Project.objects.get(pk=project_id) current_instance = json.dumps( IssueSerializer(issue).data, cls=DjangoJSONEncoder @@ -494,10 +441,7 @@ class IssueAPIEndpoint(BaseAPIView): serializer = IssueSerializer( issue, data=request.data, - context={ - "project_id": project_id, - "workspace_id": project.workspace_id, - }, + context={"project_id": project_id, "workspace_id": project.workspace_id}, partial=True, ) if serializer.is_valid(): @@ -535,9 +479,7 @@ class IssueAPIEndpoint(BaseAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, slug, project_id, pk=None): - issue = Issue.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) if issue.created_by_id != request.user.id and ( not ProjectMember.objects.filter( workspace__slug=slug, @@ -576,9 +518,7 @@ class LabelAPIEndpoint(BaseAPIView): serializer_class = LabelSerializer model = Label - permission_classes = [ - ProjectMemberPermission, - ] + permission_classes = [ProjectMemberPermission] def get_queryset(self): return ( @@ -625,12 +565,8 @@ class LabelAPIEndpoint(BaseAPIView): ) serializer.save(project_id=project_id) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError: label = Label.objects.filter( workspace__slug=slug, @@ -651,18 +587,11 @@ class LabelAPIEndpoint(BaseAPIView): request=request, queryset=(self.get_queryset()), on_results=lambda labels: LabelSerializer( - labels, - many=True, - fields=self.fields, - expand=self.expand, + labels, many=True, fields=self.fields, expand=self.expand ).data, ) label = self.get_queryset().get(pk=pk) - serializer = LabelSerializer( - label, - fields=self.fields, - expand=self.expand, - ) + serializer = LabelSerializer(label, fields=self.fields, expand=self.expand) return Response(serializer.data, status=status.HTTP_200_OK) def patch(self, request, slug, project_id, pk=None): @@ -705,9 +634,7 @@ class IssueLinkAPIEndpoint(BaseAPIView): """ - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] model = IssueLink serializer_class = IssueLinkSerializer @@ -730,46 +657,32 @@ class IssueLinkAPIEndpoint(BaseAPIView): if pk is None: issue_links = self.get_queryset() serializer = IssueLinkSerializer( - issue_links, - fields=self.fields, - expand=self.expand, + issue_links, fields=self.fields, expand=self.expand ) return self.paginate( request=request, queryset=(self.get_queryset()), on_results=lambda issue_links: IssueLinkSerializer( - issue_links, - many=True, - fields=self.fields, - expand=self.expand, + issue_links, many=True, fields=self.fields, expand=self.expand ).data, ) issue_link = self.get_queryset().get(pk=pk) serializer = IssueLinkSerializer( - issue_link, - fields=self.fields, - expand=self.expand, + issue_link, fields=self.fields, expand=self.expand ) return Response(serializer.data, status=status.HTTP_200_OK) def post(self, request, slug, project_id, issue_id): serializer = IssueLinkSerializer(data=request.data) if serializer.is_valid(): - serializer.save( - project_id=project_id, - issue_id=issue_id, - ) + serializer.save(project_id=project_id, issue_id=issue_id) link = IssueLink.objects.get(pk=serializer.data["id"]) - link.created_by_id = request.data.get( - "created_by", request.user.id - ) + link.created_by_id = request.data.get("created_by", request.user.id) link.save(update_fields=["created_by"]) issue_activity.delay( type="link.activity.created", - requested_data=json.dumps( - serializer.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), actor_id=str(link.created_by_id), @@ -781,19 +694,13 @@ class IssueLinkAPIEndpoint(BaseAPIView): def patch(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) requested_data = json.dumps(request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( - IssueLinkSerializer(issue_link).data, - cls=DjangoJSONEncoder, - ) - serializer = IssueLinkSerializer( - issue_link, data=request.data, partial=True + IssueLinkSerializer(issue_link).data, cls=DjangoJSONEncoder ) + serializer = IssueLinkSerializer(issue_link, data=request.data, partial=True) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -810,14 +717,10 @@ class IssueLinkAPIEndpoint(BaseAPIView): def delete(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) current_instance = json.dumps( - IssueLinkSerializer(issue_link).data, - cls=DjangoJSONEncoder, + IssueLinkSerializer(issue_link).data, cls=DjangoJSONEncoder ) issue_activity.delay( type="link.activity.deleted", @@ -842,15 +745,11 @@ class IssueCommentAPIEndpoint(BaseAPIView): serializer_class = IssueCommentSerializer model = IssueComment webhook_event = "issue_comment" - permission_classes = [ - ProjectLitePermission, - ] + permission_classes = [ProjectLitePermission] def get_queryset(self): return ( - IssueComment.objects.filter( - workspace__slug=self.kwargs.get("slug") - ) + IssueComment.objects.filter(workspace__slug=self.kwargs.get("slug")) .filter(project_id=self.kwargs.get("project_id")) .filter(issue_id=self.kwargs.get("issue_id")) .filter( @@ -877,19 +776,14 @@ class IssueCommentAPIEndpoint(BaseAPIView): if pk: issue_comment = self.get_queryset().get(pk=pk) serializer = IssueCommentSerializer( - issue_comment, - fields=self.fields, - expand=self.expand, + issue_comment, fields=self.fields, expand=self.expand ) return Response(serializer.data, status=status.HTTP_200_OK) return self.paginate( request=request, queryset=(self.get_queryset()), on_results=lambda issue_comment: IssueCommentSerializer( - issue_comment, - many=True, - fields=self.fields, - expand=self.expand, + issue_comment, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -922,17 +816,11 @@ class IssueCommentAPIEndpoint(BaseAPIView): serializer = IssueCommentSerializer(data=request.data) if serializer.is_valid(): serializer.save( - project_id=project_id, - issue_id=issue_id, - actor=request.user, - ) - issue_comment = IssueComment.objects.get( - pk=serializer.data.get("id") + project_id=project_id, issue_id=issue_id, actor=request.user ) + issue_comment = IssueComment.objects.get(pk=serializer.data.get("id")) # Update the created_at and the created_by and save the comment - issue_comment.created_at = request.data.get( - "created_at", timezone.now() - ) + issue_comment.created_at = request.data.get("created_at", timezone.now()) issue_comment.created_by_id = request.data.get( "created_by", request.user.id ) @@ -940,9 +828,7 @@ class IssueCommentAPIEndpoint(BaseAPIView): issue_activity.delay( type="comment.activity.created", - requested_data=json.dumps( - serializer.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), actor_id=str(issue_comment.created_by_id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -954,24 +840,17 @@ class IssueCommentAPIEndpoint(BaseAPIView): def patch(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( - IssueCommentSerializer(issue_comment).data, - cls=DjangoJSONEncoder, + IssueCommentSerializer(issue_comment).data, cls=DjangoJSONEncoder ) # Validation check if the issue already exists if ( request.data.get("external_id") - and ( - issue_comment.external_id - != str(request.data.get("external_id")) - ) + and (issue_comment.external_id != str(request.data.get("external_id"))) and IssueComment.objects.filter( project_id=project_id, workspace__slug=slug, @@ -1008,14 +887,10 @@ class IssueCommentAPIEndpoint(BaseAPIView): def delete(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) current_instance = json.dumps( - IssueCommentSerializer(issue_comment).data, - cls=DjangoJSONEncoder, + IssueCommentSerializer(issue_comment).data, cls=DjangoJSONEncoder ) issue_comment.delete() issue_activity.delay( @@ -1031,9 +906,7 @@ class IssueCommentAPIEndpoint(BaseAPIView): class IssueActivityAPIEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get(self, request, slug, project_id, issue_id, pk=None): issue_activities = ( @@ -1058,19 +931,14 @@ class IssueActivityAPIEndpoint(BaseAPIView): request=request, queryset=(issue_activities), on_results=lambda issue_activity: IssueActivitySerializer( - issue_activity, - many=True, - fields=self.fields, - expand=self.expand, + issue_activity, many=True, fields=self.fields, expand=self.expand ).data, ) class IssueAttachmentEndpoint(BaseAPIView): serializer_class = IssueAttachmentSerializer - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] model = FileAsset parser_classes = (MultiPartParser, FormParser) @@ -1109,10 +977,7 @@ class IssueAttachmentEndpoint(BaseAPIView): actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(self.kwargs.get("project_id", None)), - current_instance=json.dumps( - serializer.data, - cls=DjangoJSONEncoder, - ), + current_instance=json.dumps(serializer.data, cls=DjangoJSONEncoder), epoch=int(timezone.now().timestamp()), notification=True, origin=request.META.get("HTTP_ORIGIN"), diff --git a/apiserver/plane/api/views/member.py b/apiserver/plane/api/views/member.py index d6e5fed0c..954ee030b 100644 --- a/apiserver/plane/api/views/member.py +++ b/apiserver/plane/api/views/member.py @@ -13,24 +13,14 @@ from rest_framework import status # Module imports from .base import BaseAPIView from plane.api.serializers import UserLiteSerializer -from plane.db.models import ( - User, - Workspace, - Project, - WorkspaceMember, - ProjectMember, -) +from plane.db.models import User, Workspace, Project, WorkspaceMember, ProjectMember -from plane.app.permissions import ( - ProjectMemberPermission, -) +from plane.app.permissions import ProjectMemberPermission # API endpoint to get and insert users inside the workspace class ProjectMemberAPIEndpoint(BaseAPIView): - permission_classes = [ - ProjectMemberPermission, - ] + permission_classes = [ProjectMemberPermission] # Get all the users that are present inside the workspace def get(self, request, slug, project_id): @@ -48,10 +38,7 @@ class ProjectMemberAPIEndpoint(BaseAPIView): # Get all the users that are present inside the workspace users = UserLiteSerializer( - User.objects.filter( - id__in=project_members, - ), - many=True, + User.objects.filter(id__in=project_members), many=True ).data return Response(users, status=status.HTTP_200_OK) @@ -78,8 +65,7 @@ class ProjectMemberAPIEndpoint(BaseAPIView): validate_email(email) except ValidationError: return Response( - {"error": "Invalid email provided"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Invalid email provided"}, status=status.HTTP_400_BAD_REQUEST ) workspace = Workspace.objects.filter(slug=slug).first() @@ -108,9 +94,7 @@ class ProjectMemberAPIEndpoint(BaseAPIView): ).first() if project_member: return Response( - { - "error": "User is already part of the workspace and project" - }, + {"error": "User is already part of the workspace and project"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -131,18 +115,14 @@ class ProjectMemberAPIEndpoint(BaseAPIView): # Create a workspace member for the user if not already a member if not workspace_member: workspace_member = WorkspaceMember.objects.create( - workspace=workspace, - member=user, - role=request.data.get("role", 5), + workspace=workspace, member=user, role=request.data.get("role", 5) ) workspace_member.save() # Create a project member for the user if not already a member if not project_member: project_member = ProjectMember.objects.create( - project=project, - member=user, - role=request.data.get("role", 5), + project=project, member=user, role=request.data.get("role", 5) ) project_member.save() @@ -150,4 +130,3 @@ class ProjectMemberAPIEndpoint(BaseAPIView): user_data = UserLiteSerializer(user).data return Response(user_data, status=status.HTTP_201_CREATED) - diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index c8a8c1716..9e4f4143c 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -43,9 +43,7 @@ class ModuleAPIEndpoint(BaseAPIView): """ model = Module - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] serializer_class = ModuleSerializer webhook_event = "module" @@ -60,9 +58,7 @@ class ModuleAPIEndpoint(BaseAPIView): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related( - "module", "created_by" - ), + queryset=ModuleLink.objects.select_related("module", "created_by"), ) ) .annotate( @@ -74,7 +70,7 @@ class ModuleAPIEndpoint(BaseAPIView): issue_module__deleted_at__isnull=True, ), distinct=True, - ), + ) ) .annotate( completed_issues=Count( @@ -143,10 +139,7 @@ class ModuleAPIEndpoint(BaseAPIView): project = Project.objects.get(pk=project_id, workspace__slug=slug) serializer = ModuleSerializer( data=request.data, - context={ - "project_id": project_id, - "workspace_id": project.workspace_id, - }, + context={"project_id": project_id, "workspace_id": project.workspace_id}, ) if serializer.is_valid(): if ( @@ -189,9 +182,7 @@ class ModuleAPIEndpoint(BaseAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def patch(self, request, slug, project_id, pk): - module = Module.objects.get( - pk=pk, project_id=project_id, workspace__slug=slug - ) + module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug) current_instance = json.dumps( ModuleSerializer(module).data, cls=DjangoJSONEncoder @@ -203,10 +194,7 @@ class ModuleAPIEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) serializer = ModuleSerializer( - module, - data=request.data, - context={"project_id": project_id}, - partial=True, + module, data=request.data, context={"project_id": project_id}, partial=True ) if serializer.is_valid(): if ( @@ -246,33 +234,21 @@ class ModuleAPIEndpoint(BaseAPIView): def get(self, request, slug, project_id, pk=None): if pk: - queryset = ( - self.get_queryset().filter(archived_at__isnull=True).get(pk=pk) - ) + queryset = self.get_queryset().filter(archived_at__isnull=True).get(pk=pk) data = ModuleSerializer( - queryset, - fields=self.fields, - expand=self.expand, + queryset, fields=self.fields, expand=self.expand ).data - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) return self.paginate( request=request, queryset=(self.get_queryset().filter(archived_at__isnull=True)), on_results=lambda modules: ModuleSerializer( - modules, - many=True, - fields=self.fields, - expand=self.expand, + modules, many=True, fields=self.fields, expand=self.expand ).data, ) def delete(self, request, slug, project_id, pk): - module = Module.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + module = Module.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) if module.created_by_id != request.user.id and ( not ProjectMember.objects.filter( workspace__slug=slug, @@ -288,9 +264,7 @@ class ModuleAPIEndpoint(BaseAPIView): ) module_issues = list( - ModuleIssue.objects.filter(module_id=pk).values_list( - "issue", flat=True - ) + ModuleIssue.objects.filter(module_id=pk).values_list("issue", flat=True) ) issue_activity.delay( type="module.activity.deleted", @@ -304,24 +278,15 @@ class ModuleAPIEndpoint(BaseAPIView): actor_id=str(request.user.id), issue_id=None, project_id=str(project_id), - current_instance=json.dumps( - { - "module_name": str(module.name), - } - ), + current_instance=json.dumps({"module_name": str(module.name)}), epoch=int(timezone.now().timestamp()), ) module.delete() # Delete the module issues - ModuleIssue.objects.filter( - module=pk, - project_id=project_id, - ).delete() + ModuleIssue.objects.filter(module=pk, project_id=project_id).delete() # Delete the user favorite module UserFavorite.objects.filter( - entity_type="module", - entity_identifier=pk, - project_id=project_id, + entity_type="module", entity_identifier=pk, project_id=project_id ).delete() return Response(status=status.HTTP_204_NO_CONTENT) @@ -338,16 +303,12 @@ class ModuleIssueAPIEndpoint(BaseAPIView): webhook_event = "module_issue" bulk = True - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): return ( ModuleIssue.objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("issue") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -374,13 +335,10 @@ class ModuleIssueAPIEndpoint(BaseAPIView): order_by = request.GET.get("order_by", "created_at") issues = ( Issue.issue_objects.filter( - issue_module__module_id=module_id, - issue_module__deleted_at__isnull=True, + issue_module__module_id=module_id, issue_module__deleted_at__isnull=True ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -415,10 +373,7 @@ class ModuleIssueAPIEndpoint(BaseAPIView): request=request, queryset=(issues), on_results=lambda issues: IssueSerializer( - issues, - many=True, - fields=self.fields, - expand=self.expand, + issues, many=True, fields=self.fields, expand=self.expand ).data, ) @@ -426,8 +381,7 @@ class ModuleIssueAPIEndpoint(BaseAPIView): issues = request.data.get("issues", []) if not len(issues): return Response( - {"error": "Issues are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST ) module = Module.objects.get( workspace__slug=slug, project_id=project_id, pk=module_id @@ -474,16 +428,10 @@ class ModuleIssueAPIEndpoint(BaseAPIView): ) ModuleIssue.objects.bulk_create( - record_to_create, - batch_size=10, - ignore_conflicts=True, + record_to_create, batch_size=10, ignore_conflicts=True ) - ModuleIssue.objects.bulk_update( - records_to_update, - ["module"], - batch_size=10, - ) + ModuleIssue.objects.bulk_update(records_to_update, ["module"], batch_size=10) # Capture Issue Activity issue_activity.delay( @@ -519,10 +467,7 @@ class ModuleIssueAPIEndpoint(BaseAPIView): issue_activity.delay( type="module.activity.deleted", requested_data=json.dumps( - { - "module_id": str(module_id), - "issues": [str(module_issue.issue_id)], - } + {"module_id": str(module_id), "issues": [str(module_issue.issue_id)]} ), actor_id=str(request.user.id), issue_id=str(issue_id), @@ -534,9 +479,7 @@ class ModuleIssueAPIEndpoint(BaseAPIView): class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): return ( @@ -550,9 +493,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related( - "module", "created_by" - ), + queryset=ModuleLink.objects.select_related("module", "created_by"), ) ) .annotate( @@ -564,7 +505,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): issue_module__deleted_at__isnull=True, ), distinct=True, - ), + ) ) .annotate( completed_issues=Count( @@ -634,22 +575,15 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): request=request, queryset=(self.get_queryset()), on_results=lambda modules: ModuleSerializer( - modules, - many=True, - fields=self.fields, - expand=self.expand, + modules, many=True, fields=self.fields, expand=self.expand ).data, ) def post(self, request, slug, project_id, pk): - module = Module.objects.get( - pk=pk, project_id=project_id, workspace__slug=slug - ) + module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug) if module.status not in ["completed", "cancelled"]: return Response( - { - "error": "Only completed or cancelled modules can be archived" - }, + {"error": "Only completed or cancelled modules can be archived"}, status=status.HTTP_400_BAD_REQUEST, ) module.archived_at = timezone.now() @@ -663,9 +597,7 @@ class ModuleArchiveUnarchiveAPIEndpoint(BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) def delete(self, request, slug, project_id, pk): - module = Module.objects.get( - pk=pk, project_id=project_id, workspace__slug=slug - ) + module = Module.objects.get(pk=pk, project_id=project_id, workspace__slug=slug) module.archived_at = None module.save() return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/api/views/project.py b/apiserver/plane/api/views/project.py index 92a924e11..c52caac0f 100644 --- a/apiserver/plane/api/views/project.py +++ b/apiserver/plane/api/views/project.py @@ -39,9 +39,7 @@ class ProjectAPIEndpoint(BaseAPIView): model = Project webhook_event = "project" - permission_classes = [ - ProjectBasePermission, - ] + permission_classes = [ProjectBasePermission] def get_queryset(self): return ( @@ -54,10 +52,7 @@ class ProjectAPIEndpoint(BaseAPIView): | Q(network=2) ) .select_related( - "workspace", - "workspace__owner", - "default_assignee", - "project_lead", + "workspace", "workspace__owner", "default_assignee", "project_lead" ) .annotate( is_member=Exists( @@ -71,9 +66,7 @@ class ProjectAPIEndpoint(BaseAPIView): ) .annotate( total_members=ProjectMember.objects.filter( - project_id=OuterRef("id"), - member__is_bot=False, - is_active=True, + project_id=OuterRef("id"), member__is_bot=False, is_active=True ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -125,8 +118,7 @@ class ProjectAPIEndpoint(BaseAPIView): Prefetch( "project_projectmember", queryset=ProjectMember.objects.filter( - workspace__slug=slug, - is_active=True, + workspace__slug=slug, is_active=True ).select_related("member"), ) ) @@ -136,18 +128,11 @@ class ProjectAPIEndpoint(BaseAPIView): request=request, queryset=(projects), on_results=lambda projects: ProjectSerializer( - projects, - many=True, - fields=self.fields, - expand=self.expand, + projects, many=True, fields=self.fields, expand=self.expand ).data, ) project = self.get_queryset().get(workspace__slug=slug, pk=pk) - serializer = ProjectSerializer( - project, - fields=self.fields, - expand=self.expand, - ) + serializer = ProjectSerializer(project, fields=self.fields, expand=self.expand) return Response(serializer.data, status=status.HTTP_200_OK) def post(self, request, slug): @@ -161,14 +146,11 @@ class ProjectAPIEndpoint(BaseAPIView): # Add the user as Administrator to the project _ = ProjectMember.objects.create( - project_id=serializer.data["id"], - member=request.user, - role=20, + project_id=serializer.data["id"], member=request.user, role=20 ) # Also create the issue property for the user _ = IssueUserProperty.objects.create( - project_id=serializer.data["id"], - user=request.user, + project_id=serializer.data["id"], user=request.user ) if serializer.data["project_lead"] is not None and str( @@ -236,11 +218,7 @@ class ProjectAPIEndpoint(BaseAPIView): ] ) - project = ( - self.get_queryset() - .filter(pk=serializer.data["id"]) - .first() - ) + project = self.get_queryset().filter(pk=serializer.data["id"]).first() # Model activity model_activity.delay( @@ -254,13 +232,8 @@ class ProjectAPIEndpoint(BaseAPIView): ) serializer = ProjectSerializer(project) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError as e: if "already exists" in str(e): return Response( @@ -269,8 +242,7 @@ class ProjectAPIEndpoint(BaseAPIView): ) except Workspace.DoesNotExist: return Response( - {"error": "Workspace does not exist"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Workspace does not exist"}, status=status.HTTP_404_NOT_FOUND ) except ValidationError: return Response( @@ -298,10 +270,7 @@ class ProjectAPIEndpoint(BaseAPIView): serializer = ProjectSerializer( project, - data={ - **request.data, - "intake_view": intake_view, - }, + data={**request.data, "intake_view": intake_view}, context={"workspace_id": workspace.id}, partial=True, ) @@ -310,8 +279,7 @@ class ProjectAPIEndpoint(BaseAPIView): serializer.save() if serializer.data["intake_view"]: intake = Intake.objects.filter( - project=project, - is_default=True, + project=project, is_default=True ).first() if not intake: Intake.objects.create( @@ -330,11 +298,7 @@ class ProjectAPIEndpoint(BaseAPIView): is_triage=True, ) - project = ( - self.get_queryset() - .filter(pk=serializer.data["id"]) - .first() - ) + project = self.get_queryset().filter(pk=serializer.data["id"]).first() model_activity.delay( model_name="project", @@ -348,9 +312,7 @@ class ProjectAPIEndpoint(BaseAPIView): serializer = ProjectSerializer(project) return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError as e: if "already exists" in str(e): return Response( @@ -359,8 +321,7 @@ class ProjectAPIEndpoint(BaseAPIView): ) except (Project.DoesNotExist, Workspace.DoesNotExist): return Response( - {"error": "Project does not exist"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project does not exist"}, status=status.HTTP_404_NOT_FOUND ) except ValidationError: return Response( @@ -372,28 +333,20 @@ class ProjectAPIEndpoint(BaseAPIView): project = Project.objects.get(pk=pk, workspace__slug=slug) # Delete the user favorite cycle UserFavorite.objects.filter( - entity_type="project", - entity_identifier=pk, - project_id=pk, + entity_type="project", entity_identifier=pk, project_id=pk ).delete() project.delete() return Response(status=status.HTTP_204_NO_CONTENT) class ProjectArchiveUnarchiveAPIEndpoint(BaseAPIView): - - permission_classes = [ - ProjectBasePermission, - ] + permission_classes = [ProjectBasePermission] def post(self, request, slug, project_id): project = Project.objects.get(pk=project_id, workspace__slug=slug) project.archived_at = timezone.now() project.save() - UserFavorite.objects.filter( - workspace__slug=slug, - project=project_id, - ).delete() + UserFavorite.objects.filter(workspace__slug=slug, project=project_id).delete() return Response(status=status.HTTP_204_NO_CONTENT) def delete(self, request, slug, project_id): diff --git a/apiserver/plane/api/views/state.py b/apiserver/plane/api/views/state.py index dd239754c..0fbbd222a 100644 --- a/apiserver/plane/api/views/state.py +++ b/apiserver/plane/api/views/state.py @@ -16,9 +16,7 @@ from .base import BaseAPIView class StateAPIEndpoint(BaseAPIView): serializer_class = StateSerializer model = State - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): return ( @@ -67,9 +65,7 @@ class StateAPIEndpoint(BaseAPIView): serializer.save(project_id=project_id) return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError: state = State.objects.filter( workspace__slug=slug, @@ -96,19 +92,13 @@ class StateAPIEndpoint(BaseAPIView): request=request, queryset=(self.get_queryset()), on_results=lambda states: StateSerializer( - states, - many=True, - fields=self.fields, - expand=self.expand, + states, many=True, fields=self.fields, expand=self.expand ).data, ) def delete(self, request, slug, project_id, state_id): state = State.objects.get( - is_triage=False, - pk=state_id, - project_id=project_id, - workspace__slug=slug, + is_triage=False, pk=state_id, project_id=project_id, workspace__slug=slug ) if state.default: @@ -122,9 +112,7 @@ class StateAPIEndpoint(BaseAPIView): if issue_exist: return Response( - { - "error": "The state is not empty, only empty states can be deleted" - }, + {"error": "The state is not empty, only empty states can be deleted"}, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/middleware/api_authentication.py b/apiserver/plane/app/middleware/api_authentication.py index 893df7f84..ddabb4132 100644 --- a/apiserver/plane/app/middleware/api_authentication.py +++ b/apiserver/plane/app/middleware/api_authentication.py @@ -25,10 +25,7 @@ class APIKeyAuthentication(authentication.BaseAuthentication): def validate_api_token(self, token): try: api_token = APIToken.objects.get( - Q( - Q(expired_at__gt=timezone.now()) - | Q(expired_at__isnull=True) - ), + Q(Q(expired_at__gt=timezone.now()) | Q(expired_at__isnull=True)), token=token, is_active=True, ) diff --git a/apiserver/plane/app/permissions/__init__.py b/apiserver/plane/app/permissions/__init__.py index e45388144..b7a095e74 100644 --- a/apiserver/plane/app/permissions/__init__.py +++ b/apiserver/plane/app/permissions/__init__.py @@ -12,4 +12,4 @@ from .project import ( ProjectMemberPermission, ProjectLitePermission, ) -from .base import allow_permission, ROLE \ No newline at end of file +from .base import allow_permission, ROLE diff --git a/apiserver/plane/app/permissions/base.py b/apiserver/plane/app/permissions/base.py index 06faeceb6..7ba12a2e2 100644 --- a/apiserver/plane/app/permissions/base.py +++ b/apiserver/plane/app/permissions/base.py @@ -5,6 +5,7 @@ from rest_framework import status from enum import Enum + class ROLE(Enum): ADMIN = 20 MEMBER = 15 @@ -15,7 +16,6 @@ def allow_permission(allowed_roles, level="PROJECT", creator=False, model=None): def decorator(view_func): @wraps(view_func) def _wrapped_view(instance, request, *args, **kwargs): - # Check for creator if required if creator and model: obj = model.objects.filter( @@ -26,8 +26,7 @@ def allow_permission(allowed_roles, level="PROJECT", creator=False, model=None): # Convert allowed_roles to their values if they are enum members allowed_role_values = [ - role.value if isinstance(role, ROLE) else role - for role in allowed_roles + role.value if isinstance(role, ROLE) else role for role in allowed_roles ] # Check role permissions diff --git a/apiserver/plane/app/permissions/project.py b/apiserver/plane/app/permissions/project.py index 11eab008b..470960fcc 100644 --- a/apiserver/plane/app/permissions/project.py +++ b/apiserver/plane/app/permissions/project.py @@ -18,9 +18,7 @@ class ProjectBasePermission(BasePermission): ## Safe Methods -> Handle the filtering logic in queryset if request.method in SAFE_METHODS: return WorkspaceMember.objects.filter( - workspace__slug=view.workspace_slug, - member=request.user, - is_active=True, + workspace__slug=view.workspace_slug, member=request.user, is_active=True ).exists() ## Only workspace owners or admins can create the projects @@ -50,9 +48,7 @@ class ProjectMemberPermission(BasePermission): ## Safe Methods -> Handle the filtering logic in queryset if request.method in SAFE_METHODS: return ProjectMember.objects.filter( - workspace__slug=view.workspace_slug, - member=request.user, - is_active=True, + workspace__slug=view.workspace_slug, member=request.user, is_active=True ).exists() ## Only workspace owners or admins can create the projects if request.method == "POST": diff --git a/apiserver/plane/app/permissions/workspace.py b/apiserver/plane/app/permissions/workspace.py index c2dcc9f95..8dc791c0c 100644 --- a/apiserver/plane/app/permissions/workspace.py +++ b/apiserver/plane/app/permissions/workspace.py @@ -50,9 +50,7 @@ class WorkspaceOwnerPermission(BasePermission): return False return WorkspaceMember.objects.filter( - workspace__slug=view.workspace_slug, - member=request.user, - role=Admin, + workspace__slug=view.workspace_slug, member=request.user, role=Admin ).exists() @@ -77,9 +75,7 @@ class WorkspaceEntityPermission(BasePermission): ## Safe Methods -> Handle the filtering logic in queryset if request.method in SAFE_METHODS: return WorkspaceMember.objects.filter( - workspace__slug=view.workspace_slug, - member=request.user, - is_active=True, + workspace__slug=view.workspace_slug, member=request.user, is_active=True ).exists() return WorkspaceMember.objects.filter( @@ -96,9 +92,7 @@ class WorkspaceViewerPermission(BasePermission): return False return WorkspaceMember.objects.filter( - member=request.user, - workspace__slug=view.workspace_slug, - is_active=True, + member=request.user, workspace__slug=view.workspace_slug, is_active=True ).exists() @@ -108,7 +102,5 @@ class WorkspaceUserPermission(BasePermission): return False return WorkspaceMember.objects.filter( - member=request.user, - workspace__slug=view.workspace_slug, - is_active=True, + member=request.user, workspace__slug=view.workspace_slug, is_active=True ).exists() diff --git a/apiserver/plane/app/serializers/__init__.py b/apiserver/plane/app/serializers/__init__.py index 2e2c91baa..2e7022688 100644 --- a/apiserver/plane/app/serializers/__init__.py +++ b/apiserver/plane/app/serializers/__init__.py @@ -36,9 +36,7 @@ from .project import ( ProjectMemberRoleSerializer, ) from .state import StateSerializer, StateLiteSerializer -from .view import ( - IssueViewSerializer, -) +from .view import IssueViewSerializer from .cycle import ( CycleSerializer, CycleIssueSerializer, @@ -112,10 +110,7 @@ from .intake 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/analytic.py b/apiserver/plane/app/serializers/analytic.py index 9f3ee6d0a..13b24d14d 100644 --- a/apiserver/plane/app/serializers/analytic.py +++ b/apiserver/plane/app/serializers/analytic.py @@ -7,10 +7,7 @@ class AnalyticViewSerializer(BaseSerializer): class Meta: model = AnalyticView fields = "__all__" - read_only_fields = [ - "workspace", - "query", - ] + read_only_fields = ["workspace", "query"] def create(self, validated_data): query_params = validated_data.get("query_dict", {}) diff --git a/apiserver/plane/app/serializers/asset.py b/apiserver/plane/app/serializers/asset.py index 136e2264b..560cd3538 100644 --- a/apiserver/plane/app/serializers/asset.py +++ b/apiserver/plane/app/serializers/asset.py @@ -6,9 +6,4 @@ class FileAssetSerializer(BaseSerializer): class Meta: model = FileAsset fields = "__all__" - read_only_fields = [ - "created_by", - "updated_by", - "created_at", - "updated_at", - ] + read_only_fields = ["created_by", "updated_by", "created_at", "updated_at"] diff --git a/apiserver/plane/app/serializers/base.py b/apiserver/plane/app/serializers/base.py index ed3201043..715ad6eae 100644 --- a/apiserver/plane/app/serializers/base.py +++ b/apiserver/plane/app/serializers/base.py @@ -178,15 +178,10 @@ class DynamicBaseSerializer(BaseSerializer): response[expand] = exp_serializer.data else: # You might need to handle this case differently - response[expand] = getattr( - instance, f"{expand}_id", None - ) + response[expand] = getattr(instance, f"{expand}_id", None) # Check if issue_attachments is in fields or expand - if ( - "issue_attachments" in self.fields - or "issue_attachments" in self.expand - ): + if "issue_attachments" in self.fields or "issue_attachments" in self.expand: # Import the model here to avoid circular imports from plane.db.models import FileAsset @@ -199,11 +194,9 @@ class DynamicBaseSerializer(BaseSerializer): entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT, ) # Serialize issue_attachments and add them to the response - response["issue_attachments"] = ( - IssueAttachmentLiteSerializer( - issue_attachments, many=True - ).data - ) + response["issue_attachments"] = IssueAttachmentLiteSerializer( + issue_attachments, many=True + ).data else: response["issue_attachments"] = [] diff --git a/apiserver/plane/app/serializers/cycle.py b/apiserver/plane/app/serializers/cycle.py index 97fd47960..bf08de4fe 100644 --- a/apiserver/plane/app/serializers/cycle.py +++ b/apiserver/plane/app/serializers/cycle.py @@ -4,11 +4,7 @@ from rest_framework import serializers # Module imports from .base import BaseSerializer from .issue import IssueStateSerializer -from plane.db.models import ( - Cycle, - CycleIssue, - CycleUserProperties, -) +from plane.db.models import Cycle, CycleIssue, CycleUserProperties class CycleWriteSerializer(BaseSerializer): @@ -18,20 +14,13 @@ class CycleWriteSerializer(BaseSerializer): 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" - ) + raise serializers.ValidationError("Start date cannot exceed end date") return data class Meta: model = Cycle fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "owned_by", - "archived_at", - ] + read_only_fields = ["workspace", "project", "owned_by", "archived_at"] class CycleSerializer(BaseSerializer): @@ -87,18 +76,11 @@ class CycleIssueSerializer(BaseSerializer): class Meta: model = CycleIssue fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "cycle", - ] + read_only_fields = ["workspace", "project", "cycle"] + class CycleUserPropertiesSerializer(BaseSerializer): class Meta: model = CycleUserProperties fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "cycle" "user", - ] + read_only_fields = ["workspace", "project", "cycle" "user"] diff --git a/apiserver/plane/app/serializers/draft.py b/apiserver/plane/app/serializers/draft.py index e07e416a7..f30835263 100644 --- a/apiserver/plane/app/serializers/draft.py +++ b/apiserver/plane/app/serializers/draft.py @@ -22,16 +22,10 @@ from plane.db.models import ( class DraftIssueCreateSerializer(BaseSerializer): # ids state_id = serializers.PrimaryKeyRelatedField( - source="state", - queryset=State.objects.all(), - required=False, - allow_null=True, + source="state", queryset=State.objects.all(), required=False, allow_null=True ) parent_id = serializers.PrimaryKeyRelatedField( - source="parent", - queryset=Issue.objects.all(), - required=False, - allow_null=True, + source="parent", queryset=Issue.objects.all(), required=False, allow_null=True ) label_ids = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=Label.objects.all()), @@ -69,9 +63,7 @@ class DraftIssueCreateSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError( - "Start date cannot exceed target date" - ) + raise serializers.ValidationError("Start date cannot exceed target date") return data def create(self, validated_data): @@ -86,9 +78,7 @@ class DraftIssueCreateSerializer(BaseSerializer): # Create Issue issue = DraftIssue.objects.create( - **validated_data, - workspace_id=workspace_id, - project_id=project_id, + **validated_data, workspace_id=workspace_id, project_id=project_id ) # Issue Audit Users @@ -239,20 +229,11 @@ class DraftIssueCreateSerializer(BaseSerializer): class DraftIssueSerializer(BaseSerializer): # ids cycle_id = serializers.PrimaryKeyRelatedField(read_only=True) - module_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + module_ids = serializers.ListField(child=serializers.UUIDField(), required=False) # Many to many - label_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) - assignee_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + label_ids = serializers.ListField(child=serializers.UUIDField(), required=False) + assignee_ids = serializers.ListField(child=serializers.UUIDField(), required=False) class Meta: model = DraftIssue @@ -286,7 +267,5 @@ class DraftIssueDetailSerializer(DraftIssueSerializer): description_html = serializers.CharField() class Meta(DraftIssueSerializer.Meta): - fields = DraftIssueSerializer.Meta.fields + [ - "description_html", - ] + fields = DraftIssueSerializer.Meta.fields + ["description_html"] read_only_fields = fields diff --git a/apiserver/plane/app/serializers/estimate.py b/apiserver/plane/app/serializers/estimate.py index 8cb083ca5..b2d65ef8c 100644 --- a/apiserver/plane/app/serializers/estimate.py +++ b/apiserver/plane/app/serializers/estimate.py @@ -7,14 +7,10 @@ from rest_framework import serializers class EstimateSerializer(BaseSerializer): - class Meta: model = Estimate fields = "__all__" - read_only_fields = [ - "workspace", - "project", - ] + read_only_fields = ["workspace", "project"] class EstimatePointSerializer(BaseSerializer): @@ -23,19 +19,13 @@ class EstimatePointSerializer(BaseSerializer): raise serializers.ValidationError("Estimate points are required") value = data.get("value") if value and len(value) > 20: - raise serializers.ValidationError( - "Value can't be more than 20 characters" - ) + raise serializers.ValidationError("Value can't be more than 20 characters") return data class Meta: model = EstimatePoint fields = "__all__" - read_only_fields = [ - "estimate", - "workspace", - "project", - ] + read_only_fields = ["estimate", "workspace", "project"] class EstimateReadSerializer(BaseSerializer): @@ -44,11 +34,7 @@ class EstimateReadSerializer(BaseSerializer): class Meta: model = Estimate fields = "__all__" - read_only_fields = [ - "points", - "name", - "description", - ] + read_only_fields = ["points", "name", "description"] class WorkspaceEstimateSerializer(BaseSerializer): @@ -57,8 +43,4 @@ class WorkspaceEstimateSerializer(BaseSerializer): class Meta: model = Estimate fields = "__all__" - read_only_fields = [ - "points", - "name", - "description", - ] + read_only_fields = ["points", "name", "description"] diff --git a/apiserver/plane/app/serializers/exporter.py b/apiserver/plane/app/serializers/exporter.py index 2dd850fd3..5c78cfa69 100644 --- a/apiserver/plane/app/serializers/exporter.py +++ b/apiserver/plane/app/serializers/exporter.py @@ -5,9 +5,7 @@ from .user import UserLiteSerializer class ExporterHistorySerializer(BaseSerializer): - initiated_by_detail = UserLiteSerializer( - source="initiated_by", read_only=True - ) + initiated_by_detail = UserLiteSerializer(source="initiated_by", read_only=True) class Meta: model = ExporterHistory diff --git a/apiserver/plane/app/serializers/favorite.py b/apiserver/plane/app/serializers/favorite.py index 8e0beda10..940b8ee82 100644 --- a/apiserver/plane/app/serializers/favorite.py +++ b/apiserver/plane/app/serializers/favorite.py @@ -1,18 +1,9 @@ from rest_framework import serializers -from plane.db.models import ( - UserFavorite, - Cycle, - Module, - Issue, - IssueView, - Page, - Project, -) +from plane.db.models import UserFavorite, Cycle, Module, Issue, IssueView, Page, Project class ProjectFavoriteLiteSerializer(serializers.ModelSerializer): - class Meta: model = Project fields = ["id", "name", "logo_props"] @@ -33,21 +24,18 @@ class PageFavoriteLiteSerializer(serializers.ModelSerializer): class CycleFavoriteLiteSerializer(serializers.ModelSerializer): - class Meta: model = Cycle fields = ["id", "name", "logo_props", "project_id"] class ModuleFavoriteLiteSerializer(serializers.ModelSerializer): - class Meta: model = Module fields = ["id", "name", "logo_props", "project_id"] class ViewFavoriteSerializer(serializers.ModelSerializer): - class Meta: model = IssueView fields = ["id", "name", "logo_props", "project_id"] @@ -89,9 +77,7 @@ class UserFavoriteSerializer(serializers.ModelSerializer): entity_type = obj.entity_type entity_identifier = obj.entity_identifier - entity_model, entity_serializer = get_entity_model_and_serializer( - entity_type - ) + entity_model, entity_serializer = get_entity_model_and_serializer(entity_type) if entity_model and entity_serializer: try: entity = entity_model.objects.get(pk=entity_identifier) diff --git a/apiserver/plane/app/serializers/importer.py b/apiserver/plane/app/serializers/importer.py index c058994d6..8997f6392 100644 --- a/apiserver/plane/app/serializers/importer.py +++ b/apiserver/plane/app/serializers/importer.py @@ -7,13 +7,9 @@ from plane.db.models import Importer class ImporterSerializer(BaseSerializer): - initiated_by_detail = UserLiteSerializer( - source="initiated_by", read_only=True - ) + initiated_by_detail = UserLiteSerializer(source="initiated_by", read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True) - workspace_detail = WorkspaceLiteSerializer( - source="workspace", read_only=True - ) + workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) class Meta: model = Importer diff --git a/apiserver/plane/app/serializers/intake.py b/apiserver/plane/app/serializers/intake.py index 3800b13ab..8b8bbacf7 100644 --- a/apiserver/plane/app/serializers/intake.py +++ b/apiserver/plane/app/serializers/intake.py @@ -3,11 +3,7 @@ from rest_framework import serializers # Module imports from .base import BaseSerializer -from .issue import ( - IssueIntakeSerializer, - LabelLiteSerializer, - IssueDetailSerializer, -) +from .issue import IssueIntakeSerializer, LabelLiteSerializer, IssueDetailSerializer from .project import ProjectLiteSerializer from .state import StateLiteSerializer from .user import UserLiteSerializer @@ -21,10 +17,7 @@ class IntakeSerializer(BaseSerializer): class Meta: model = Intake fields = "__all__" - read_only_fields = [ - "project", - "workspace", - ] + read_only_fields = ["project", "workspace"] class IntakeIssueSerializer(BaseSerializer): @@ -41,10 +34,7 @@ class IntakeIssueSerializer(BaseSerializer): "issue", "created_by", ] - read_only_fields = [ - "project", - "workspace", - ] + read_only_fields = ["project", "workspace"] def to_representation(self, instance): # Pass the annotated fields to the Issue instance if they exist @@ -70,10 +60,7 @@ class IntakeIssueDetailSerializer(BaseSerializer): "source", "issue", ] - read_only_fields = [ - "project", - "workspace", - ] + read_only_fields = ["project", "workspace"] def to_representation(self, instance): # Pass the annotated fields to the Issue instance if they exist @@ -95,12 +82,8 @@ class IntakeIssueLiteSerializer(BaseSerializer): class IssueStateIntakeSerializer(BaseSerializer): state_detail = StateLiteSerializer(read_only=True, source="state") project_detail = ProjectLiteSerializer(read_only=True, source="project") - label_details = LabelLiteSerializer( - read_only=True, source="labels", many=True - ) - assignee_details = UserLiteSerializer( - read_only=True, source="assignees", many=True - ) + label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) + assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) sub_issues_count = serializers.IntegerField(read_only=True) issue_intake = IntakeIssueLiteSerializer(read_only=True, many=True) diff --git a/apiserver/plane/app/serializers/issue.py b/apiserver/plane/app/serializers/issue.py index b0904834f..007f34849 100644 --- a/apiserver/plane/app/serializers/issue.py +++ b/apiserver/plane/app/serializers/issue.py @@ -60,12 +60,7 @@ class IssueProjectLiteSerializer(BaseSerializer): class Meta: model = Issue - fields = [ - "id", - "project_detail", - "name", - "sequence_id", - ] + fields = ["id", "project_detail", "name", "sequence_id"] read_only_fields = fields @@ -74,16 +69,10 @@ class IssueProjectLiteSerializer(BaseSerializer): class IssueCreateSerializer(BaseSerializer): # ids state_id = serializers.PrimaryKeyRelatedField( - source="state", - queryset=State.objects.all(), - required=False, - allow_null=True, + source="state", queryset=State.objects.all(), required=False, allow_null=True ) parent_id = serializers.PrimaryKeyRelatedField( - source="parent", - queryset=Issue.objects.all(), - required=False, - allow_null=True, + source="parent", queryset=Issue.objects.all(), required=False, allow_null=True ) label_ids = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=Label.objects.all()), @@ -124,9 +113,7 @@ class IssueCreateSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError( - "Start date cannot exceed target date" - ) + raise serializers.ValidationError("Start date cannot exceed target date") return data def create(self, validated_data): @@ -138,10 +125,7 @@ class IssueCreateSerializer(BaseSerializer): default_assignee_id = self.context["default_assignee_id"] # Create Issue - issue = Issue.objects.create( - **validated_data, - project_id=project_id, - ) + issue = Issue.objects.create(**validated_data, project_id=project_id) # Issue Audit Users created_by_id = issue.created_by_id @@ -245,9 +229,7 @@ class IssueActivitySerializer(BaseSerializer): actor_detail = UserLiteSerializer(read_only=True, source="actor") issue_detail = IssueFlatSerializer(read_only=True, source="issue") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer( - read_only=True, source="workspace" - ) + workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") class Meta: model = IssueActivity @@ -258,11 +240,7 @@ class IssueUserPropertySerializer(BaseSerializer): class Meta: model = IssueUserProperty fields = "__all__" - read_only_fields = [ - "user", - "workspace", - "project", - ] + read_only_fields = ["user", "workspace", "project"] class LabelSerializer(BaseSerializer): @@ -277,30 +255,20 @@ class LabelSerializer(BaseSerializer): "workspace_id", "sort_order", ] - read_only_fields = [ - "workspace", - "project", - ] + read_only_fields = ["workspace", "project"] class LabelLiteSerializer(BaseSerializer): class Meta: model = Label - fields = [ - "id", - "name", - "color", - ] + fields = ["id", "name", "color"] class IssueLabelSerializer(BaseSerializer): class Meta: model = IssueLabel fields = "__all__" - read_only_fields = [ - "workspace", - "project", - ] + read_only_fields = ["workspace", "project"] class IssueRelationSerializer(BaseSerializer): @@ -316,17 +284,8 @@ class IssueRelationSerializer(BaseSerializer): class Meta: model = IssueRelation - fields = [ - "id", - "project_id", - "sequence_id", - "relation_type", - "name", - ] - read_only_fields = [ - "workspace", - "project", - ] + fields = ["id", "project_id", "sequence_id", "relation_type", "name"] + read_only_fields = ["workspace", "project"] class RelatedIssueSerializer(BaseSerializer): @@ -334,25 +293,14 @@ class RelatedIssueSerializer(BaseSerializer): project_id = serializers.PrimaryKeyRelatedField( source="issue.project_id", read_only=True ) - sequence_id = serializers.IntegerField( - source="issue.sequence_id", read_only=True - ) + sequence_id = serializers.IntegerField(source="issue.sequence_id", read_only=True) name = serializers.CharField(source="issue.name", read_only=True) relation_type = serializers.CharField(read_only=True) class Meta: model = IssueRelation - fields = [ - "id", - "project_id", - "sequence_id", - "relation_type", - "name", - ] - read_only_fields = [ - "workspace", - "project", - ] + fields = ["id", "project_id", "sequence_id", "relation_type", "name"] + read_only_fields = ["workspace", "project"] class IssueAssigneeSerializer(BaseSerializer): @@ -460,8 +408,7 @@ class IssueLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if IssueLink.objects.filter( - url=validated_data.get("url"), - issue_id=validated_data.get("issue_id"), + url=validated_data.get("url"), issue_id=validated_data.get("issue_id") ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -471,8 +418,7 @@ class IssueLinkSerializer(BaseSerializer): def update(self, instance, validated_data): if ( IssueLink.objects.filter( - url=validated_data.get("url"), - issue_id=instance.issue_id, + url=validated_data.get("url"), issue_id=instance.issue_id ) .exclude(pk=instance.id) .exists() @@ -500,7 +446,6 @@ class IssueLinkLiteSerializer(BaseSerializer): class IssueAttachmentSerializer(BaseSerializer): - asset_url = serializers.CharField(read_only=True) class Meta: @@ -538,37 +483,20 @@ class IssueReactionSerializer(BaseSerializer): class Meta: model = IssueReaction fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "issue", - "actor", - "deleted_at", - ] + read_only_fields = ["workspace", "project", "issue", "actor", "deleted_at"] class IssueReactionLiteSerializer(DynamicBaseSerializer): class Meta: model = IssueReaction - fields = [ - "id", - "actor", - "issue", - "reaction", - ] + fields = ["id", "actor", "issue", "reaction"] class CommentReactionSerializer(BaseSerializer): class Meta: model = CommentReaction fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "comment", - "actor", - "deleted_at", - ] + read_only_fields = ["workspace", "project", "comment", "actor", "deleted_at"] class IssueVoteSerializer(BaseSerializer): @@ -576,14 +504,7 @@ class IssueVoteSerializer(BaseSerializer): class Meta: model = IssueVote - fields = [ - "issue", - "vote", - "workspace", - "project", - "actor", - "actor_detail", - ] + fields = ["issue", "vote", "workspace", "project", "actor", "actor_detail"] read_only_fields = fields @@ -591,9 +512,7 @@ class IssueCommentSerializer(BaseSerializer): actor_detail = UserLiteSerializer(read_only=True, source="actor") issue_detail = IssueFlatSerializer(read_only=True, source="issue") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer( - read_only=True, source="workspace" - ) + workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") comment_reactions = CommentReactionSerializer(read_only=True, many=True) is_member = serializers.BooleanField(read_only=True) @@ -617,25 +536,15 @@ class IssueStateFlatSerializer(BaseSerializer): class Meta: model = Issue - fields = [ - "id", - "sequence_id", - "name", - "state_detail", - "project_detail", - ] + fields = ["id", "sequence_id", "name", "state_detail", "project_detail"] # Issue Serializer with state details class IssueStateSerializer(DynamicBaseSerializer): - label_details = LabelLiteSerializer( - read_only=True, source="labels", many=True - ) + label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) state_detail = StateLiteSerializer(read_only=True, source="state") project_detail = ProjectLiteSerializer(read_only=True, source="project") - assignee_details = UserLiteSerializer( - read_only=True, source="assignees", many=True - ) + assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) sub_issues_count = serializers.IntegerField(read_only=True) attachment_count = serializers.IntegerField(read_only=True) link_count = serializers.IntegerField(read_only=True) @@ -646,10 +555,7 @@ class IssueStateSerializer(DynamicBaseSerializer): class IssueIntakeSerializer(DynamicBaseSerializer): - label_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + label_ids = serializers.ListField(child=serializers.UUIDField(), required=False) class Meta: model = Issue @@ -669,20 +575,11 @@ class IssueIntakeSerializer(DynamicBaseSerializer): class IssueSerializer(DynamicBaseSerializer): # ids cycle_id = serializers.PrimaryKeyRelatedField(read_only=True) - module_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + module_ids = serializers.ListField(child=serializers.UUIDField(), required=False) # Many to many - label_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) - assignee_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + label_ids = serializers.ListField(child=serializers.UUIDField(), required=False) + assignee_ids = serializers.ListField(child=serializers.UUIDField(), required=False) # Count items sub_issues_count = serializers.IntegerField(read_only=True) @@ -724,11 +621,7 @@ class IssueSerializer(DynamicBaseSerializer): class IssueLiteSerializer(DynamicBaseSerializer): class Meta: model = Issue - fields = [ - "id", - "sequence_id", - "project_id", - ] + fields = ["id", "sequence_id", "project_id"] read_only_fields = fields @@ -737,10 +630,7 @@ class IssueDetailSerializer(IssueSerializer): is_subscribed = serializers.BooleanField(read_only=True) class Meta(IssueSerializer.Meta): - fields = IssueSerializer.Meta.fields + [ - "description_html", - "is_subscribed", - ] + fields = IssueSerializer.Meta.fields + ["description_html", "is_subscribed"] read_only_fields = fields @@ -776,8 +666,4 @@ class IssueSubscriberSerializer(BaseSerializer): class Meta: model = IssueSubscriber fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "issue", - ] + read_only_fields = ["workspace", "project", "issue"] diff --git a/apiserver/plane/app/serializers/module.py b/apiserver/plane/app/serializers/module.py index 48ace6022..22ff44279 100644 --- a/apiserver/plane/app/serializers/module.py +++ b/apiserver/plane/app/serializers/module.py @@ -21,10 +21,7 @@ from plane.db.models import ( class ModuleWriteSerializer(BaseSerializer): lead_id = serializers.PrimaryKeyRelatedField( - source="lead", - queryset=User.objects.all(), - required=False, - allow_null=True, + source="lead", queryset=User.objects.all(), required=False, allow_null=True ) member_ids = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), @@ -48,9 +45,7 @@ class ModuleWriteSerializer(BaseSerializer): def to_representation(self, instance): data = super().to_representation(instance) - data["member_ids"] = [ - str(member.id) for member in instance.members.all() - ] + data["member_ids"] = [str(member.id) for member in instance.members.all()] return data def validate(self, data): @@ -59,9 +54,7 @@ class ModuleWriteSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError( - "Start date cannot exceed target date" - ) + raise serializers.ValidationError("Start date cannot exceed target date") return data def create(self, validated_data): @@ -71,9 +64,7 @@ class ModuleWriteSerializer(BaseSerializer): module_name = validated_data.get("name") if module_name: # Lookup for the module name in the module table for that project - if Module.objects.filter( - name=module_name, project=project - ).exists(): + if Module.objects.filter(name=module_name, project=project).exists(): raise serializers.ValidationError( {"error": "Module with this name already exists"} ) @@ -104,9 +95,7 @@ class ModuleWriteSerializer(BaseSerializer): if module_name: # Lookup for the module name in the module table for that project if ( - Module.objects.filter( - name=module_name, project=instance.project - ) + Module.objects.filter(name=module_name, project=instance.project) .exclude(id=instance.id) .exists() ): @@ -203,8 +192,7 @@ class ModuleLinkSerializer(BaseSerializer): def create(self, validated_data): validated_data["url"] = self.validate_url(validated_data.get("url")) if ModuleLink.objects.filter( - url=validated_data.get("url"), - module_id=validated_data.get("module_id"), + url=validated_data.get("url"), module_id=validated_data.get("module_id") ).exists(): raise serializers.ValidationError({"error": "URL already exists."}) return super().create(validated_data) @@ -213,8 +201,7 @@ class ModuleLinkSerializer(BaseSerializer): validated_data["url"] = self.validate_url(validated_data.get("url")) if ( ModuleLink.objects.filter( - url=validated_data.get("url"), - module_id=instance.module_id, + url=validated_data.get("url"), module_id=instance.module_id ) .exclude(pk=instance.id) .exists() diff --git a/apiserver/plane/app/serializers/notification.py b/apiserver/plane/app/serializers/notification.py index 2f79992f2..58007ec26 100644 --- a/apiserver/plane/app/serializers/notification.py +++ b/apiserver/plane/app/serializers/notification.py @@ -8,9 +8,7 @@ from rest_framework import serializers class NotificationSerializer(BaseSerializer): - triggered_by_details = UserLiteSerializer( - read_only=True, source="triggered_by" - ) + triggered_by_details = UserLiteSerializer(read_only=True, source="triggered_by") is_inbox_issue = serializers.BooleanField(read_only=True) is_intake_issue = serializers.BooleanField(read_only=True) is_mentioned_notification = serializers.BooleanField(read_only=True) diff --git a/apiserver/plane/app/serializers/page.py b/apiserver/plane/app/serializers/page.py index e7f273d40..b69221081 100644 --- a/apiserver/plane/app/serializers/page.py +++ b/apiserver/plane/app/serializers/page.py @@ -22,14 +22,8 @@ class PageSerializer(BaseSerializer): required=False, ) # Many to many - label_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) - project_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + label_ids = serializers.ListField(child=serializers.UUIDField(), required=False) + project_ids = serializers.ListField(child=serializers.UUIDField(), required=False) class Meta: model = Page @@ -54,10 +48,7 @@ class PageSerializer(BaseSerializer): "label_ids", "project_ids", ] - read_only_fields = [ - "workspace", - "owned_by", - ] + read_only_fields = ["workspace", "owned_by"] def create(self, validated_data): labels = validated_data.pop("labels", None) @@ -127,9 +118,7 @@ class PageDetailSerializer(PageSerializer): description_html = serializers.CharField() class Meta(PageSerializer.Meta): - fields = PageSerializer.Meta.fields + [ - "description_html", - ] + fields = PageSerializer.Meta.fields + ["description_html"] class SubPageSerializer(BaseSerializer): @@ -138,10 +127,7 @@ class SubPageSerializer(BaseSerializer): class Meta: model = PageLog fields = "__all__" - read_only_fields = [ - "workspace", - "page", - ] + read_only_fields = ["workspace", "page"] def get_entity_details(self, obj): entity_name = obj.entity_name @@ -158,10 +144,7 @@ class PageLogSerializer(BaseSerializer): class Meta: model = PageLog fields = "__all__" - read_only_fields = [ - "workspace", - "page", - ] + read_only_fields = ["workspace", "page"] class PageVersionSerializer(BaseSerializer): @@ -178,10 +161,7 @@ class PageVersionSerializer(BaseSerializer): "created_by", "updated_by", ] - read_only_fields = [ - "workspace", - "page", - ] + read_only_fields = ["workspace", "page"] class PageVersionDetailSerializer(BaseSerializer): @@ -201,7 +181,4 @@ class PageVersionDetailSerializer(BaseSerializer): "created_by", "updated_by", ] - read_only_fields = [ - "workspace", - "page", - ] + read_only_fields = ["workspace", "page"] diff --git a/apiserver/plane/app/serializers/project.py b/apiserver/plane/app/serializers/project.py index ccf9251bc..cc7b7fbcb 100644 --- a/apiserver/plane/app/serializers/project.py +++ b/apiserver/plane/app/serializers/project.py @@ -4,10 +4,7 @@ from rest_framework import serializers # Module imports from .base import BaseSerializer, DynamicBaseSerializer from plane.app.serializers.workspace import WorkspaceLiteSerializer -from plane.app.serializers.user import ( - UserLiteSerializer, - UserAdminLiteSerializer, -) +from plane.app.serializers.user import UserLiteSerializer, UserAdminLiteSerializer from plane.db.models import ( Project, ProjectMember, @@ -19,32 +16,23 @@ from plane.db.models import ( class ProjectSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer( - source="workspace", read_only=True - ) + workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) inbox_view = serializers.BooleanField(read_only=True, source="intake_view") class Meta: model = Project fields = "__all__" - read_only_fields = [ - "workspace", - "deleted_at", - ] + read_only_fields = ["workspace", "deleted_at"] def create(self, validated_data): identifier = validated_data.get("identifier", "").strip().upper() if identifier == "": - raise serializers.ValidationError( - detail="Project Identifier is required" - ) + raise serializers.ValidationError(detail="Project Identifier is required") if ProjectIdentifier.objects.filter( name=identifier, workspace_id=self.context["workspace_id"] ).exists(): - raise serializers.ValidationError( - detail="Project Identifier is taken" - ) + raise serializers.ValidationError(detail="Project Identifier is taken") project = Project.objects.create( **validated_data, workspace_id=self.context["workspace_id"] ) @@ -83,9 +71,7 @@ class ProjectSerializer(BaseSerializer): return project # If not same fail update - raise serializers.ValidationError( - detail="Project Identifier is already taken" - ) + raise serializers.ValidationError(detail="Project Identifier is already taken") class ProjectLiteSerializer(BaseSerializer): @@ -214,26 +200,16 @@ class ProjectMemberLiteSerializer(BaseSerializer): class DeployBoardSerializer(BaseSerializer): project_details = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer( - read_only=True, source="workspace" - ) + workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") class Meta: model = DeployBoard fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "anchor", - ] + read_only_fields = ["workspace", "project", "anchor"] class ProjectPublicMemberSerializer(BaseSerializer): class Meta: model = ProjectPublicMember fields = "__all__" - read_only_fields = [ - "workspace", - "project", - "member", - ] + read_only_fields = ["workspace", "project", "member"] diff --git a/apiserver/plane/app/serializers/state.py b/apiserver/plane/app/serializers/state.py index 773d8e461..61af5cab7 100644 --- a/apiserver/plane/app/serializers/state.py +++ b/apiserver/plane/app/serializers/state.py @@ -19,19 +19,11 @@ class StateSerializer(BaseSerializer): "description", "sequence", ] - read_only_fields = [ - "workspace", - "project", - ] + read_only_fields = ["workspace", "project"] class StateLiteSerializer(BaseSerializer): class Meta: model = State - fields = [ - "id", - "name", - "color", - "group", - ] + fields = ["id", "name", "color", "group"] read_only_fields = fields diff --git a/apiserver/plane/app/serializers/user.py b/apiserver/plane/app/serializers/user.py index 993f74c82..ebc002c9c 100644 --- a/apiserver/plane/app/serializers/user.py +++ b/apiserver/plane/app/serializers/user.py @@ -2,13 +2,7 @@ from rest_framework import serializers # Module import -from plane.db.models import ( - Account, - Profile, - User, - Workspace, - WorkspaceMemberInvite, -) +from plane.db.models import Account, Profile, User, Workspace, WorkspaceMemberInvite from .base import BaseSerializer @@ -17,11 +11,7 @@ class UserSerializer(BaseSerializer): class Meta: model = User # Exclude password field from the serializer - fields = [ - field.name - for field in User._meta.fields - if field.name != "password" - ] + fields = [field.name for field in User._meta.fields if field.name != "password"] # Make all system fields and email read only read_only_fields = [ "id", @@ -56,7 +46,6 @@ class UserSerializer(BaseSerializer): class UserMeSerializer(BaseSerializer): - class Meta: model = User fields = [ @@ -87,11 +76,7 @@ class UserMeSettingsSerializer(BaseSerializer): class Meta: model = User - fields = [ - "id", - "email", - "workspace", - ] + fields = ["id", "email", "workspace"] read_only_fields = fields def get_workspace(self, obj): @@ -128,8 +113,7 @@ class UserMeSettingsSerializer(BaseSerializer): else: fallback_workspace = ( Workspace.objects.filter( - workspace_member__member_id=obj.id, - workspace_member__is_active=True, + workspace_member__member_id=obj.id, workspace_member__is_active=True ) .order_by("created_at") .first() @@ -138,14 +122,10 @@ class UserMeSettingsSerializer(BaseSerializer): "last_workspace_id": None, "last_workspace_slug": None, "fallback_workspace_id": ( - fallback_workspace.id - if fallback_workspace is not None - else None + fallback_workspace.id if fallback_workspace is not None else None ), "fallback_workspace_slug": ( - fallback_workspace.slug - if fallback_workspace is not None - else None + fallback_workspace.slug if fallback_workspace is not None else None ), "invites": workspace_invites, } @@ -163,10 +143,7 @@ class UserLiteSerializer(BaseSerializer): "is_bot", "display_name", ] - read_only_fields = [ - "id", - "is_bot", - ] + read_only_fields = ["id", "is_bot"] class UserAdminLiteSerializer(BaseSerializer): @@ -183,10 +160,7 @@ class UserAdminLiteSerializer(BaseSerializer): "email", "last_login_medium", ] - read_only_fields = [ - "id", - "is_bot", - ] + read_only_fields = ["id", "is_bot"] class ChangePasswordSerializer(serializers.Serializer): @@ -207,9 +181,7 @@ class ChangePasswordSerializer(serializers.Serializer): if data.get("new_password") != data.get("confirm_password"): raise serializers.ValidationError( - { - "error": "Confirm password should be same as the new password." - } + {"error": "Confirm password should be same as the new password."} ) return data @@ -227,15 +199,11 @@ class ProfileSerializer(BaseSerializer): class Meta: model = Profile fields = "__all__" - read_only_fields = [ - "user", - ] + read_only_fields = ["user"] class AccountSerializer(BaseSerializer): class Meta: model = Account fields = "__all__" - read_only_fields = [ - "user", - ] + read_only_fields = ["user"] diff --git a/apiserver/plane/app/serializers/webhook.py b/apiserver/plane/app/serializers/webhook.py index 918d1705c..fa4019f7a 100644 --- a/apiserver/plane/app/serializers/webhook.py +++ b/apiserver/plane/app/serializers/webhook.py @@ -47,13 +47,9 @@ class WebhookSerializer(DynamicBaseSerializer): # Additional validation for multiple request domains and their subdomains request = self.context.get("request") - disallowed_domains = [ - "plane.so", - ] # Add your disallowed domains here + disallowed_domains = ["plane.so"] # Add your disallowed domains here if request: - request_host = request.get_host().split(":")[ - 0 - ] # Remove port if present + request_host = request.get_host().split(":")[0] # Remove port if present disallowed_domains.append(request_host) # Check if hostname is a subdomain or exact match of any disallowed domain @@ -99,9 +95,7 @@ class WebhookSerializer(DynamicBaseSerializer): # Additional validation for multiple request domains and their subdomains request = self.context.get("request") - disallowed_domains = [ - "plane.so", - ] # Add your disallowed domains here + disallowed_domains = ["plane.so"] # Add your disallowed domains here if request: request_host = request.get_host().split(":")[ 0 @@ -122,10 +116,7 @@ class WebhookSerializer(DynamicBaseSerializer): class Meta: model = Webhook fields = "__all__" - read_only_fields = [ - "workspace", - "secret_key", - ] + read_only_fields = ["workspace", "secret_key"] class WebhookLogSerializer(DynamicBaseSerializer): diff --git a/apiserver/plane/app/serializers/workspace.py b/apiserver/plane/app/serializers/workspace.py index 1a2b89bba..03e64e32a 100644 --- a/apiserver/plane/app/serializers/workspace.py +++ b/apiserver/plane/app/serializers/workspace.py @@ -47,11 +47,7 @@ class WorkSpaceSerializer(DynamicBaseSerializer): class WorkspaceLiteSerializer(BaseSerializer): class Meta: model = Workspace - fields = [ - "name", - "slug", - "id", - ] + fields = ["name", "slug", "id"] read_only_fields = fields @@ -66,6 +62,7 @@ class WorkSpaceMemberSerializer(DynamicBaseSerializer): class WorkspaceMemberMeSerializer(BaseSerializer): draft_issue_count = serializers.IntegerField(read_only=True) + class Meta: model = WorkspaceMember fields = "__all__" @@ -101,9 +98,7 @@ class WorkSpaceMemberInviteSerializer(BaseSerializer): class TeamSerializer(BaseSerializer): - members_detail = UserLiteSerializer( - read_only=True, source="members", many=True - ) + members_detail = UserLiteSerializer(read_only=True, source="members", many=True) members = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), write_only=True, @@ -140,9 +135,7 @@ class TeamSerializer(BaseSerializer): members = validated_data.pop("members") TeamMember.objects.filter(team=instance).delete() team_members = [ - TeamMember( - member=member, team=instance, workspace=instance.workspace - ) + TeamMember(member=member, team=instance, workspace=instance.workspace) for member in members ] TeamMember.objects.bulk_create(team_members, batch_size=10) @@ -154,17 +147,11 @@ class WorkspaceThemeSerializer(BaseSerializer): class Meta: model = WorkspaceTheme fields = "__all__" - read_only_fields = [ - "workspace", - "actor", - ] + read_only_fields = ["workspace", "actor"] class WorkspaceUserPropertiesSerializer(BaseSerializer): class Meta: model = WorkspaceUserProperties fields = "__all__" - read_only_fields = [ - "workspace", - "user", - ] + read_only_fields = ["workspace", "user"] diff --git a/apiserver/plane/app/urls/asset.py b/apiserver/plane/app/urls/asset.py index eed379fd7..ec0f41b62 100644 --- a/apiserver/plane/app/urls/asset.py +++ b/apiserver/plane/app/urls/asset.py @@ -26,11 +26,7 @@ urlpatterns = [ FileAssetEndpoint.as_view(), name="file-assets", ), - path( - "users/file-assets/", - UserAssetsEndpoint.as_view(), - name="user-file-assets", - ), + path("users/file-assets/", UserAssetsEndpoint.as_view(), name="user-file-assets"), path( "users/file-assets//", UserAssetsEndpoint.as_view(), @@ -38,11 +34,7 @@ urlpatterns = [ ), path( "workspaces/file-assets///restore/", - FileAssetViewSet.as_view( - { - "post": "restore", - } - ), + FileAssetViewSet.as_view({"post": "restore"}), name="file-assets-restore", ), # V2 Endpoints diff --git a/apiserver/plane/app/urls/cycle.py b/apiserver/plane/app/urls/cycle.py index 0d62d0271..f188d0872 100644 --- a/apiserver/plane/app/urls/cycle.py +++ b/apiserver/plane/app/urls/cycle.py @@ -17,12 +17,7 @@ from plane.app.views import ( urlpatterns = [ path( "workspaces//projects//cycles/", - CycleViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + CycleViewSet.as_view({"get": "list", "post": "create"}), name="project-cycle", ), path( @@ -39,12 +34,7 @@ urlpatterns = [ ), path( "workspaces//projects//cycles//cycle-issues/", - CycleIssueViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + CycleIssueViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-cycle", ), path( @@ -66,21 +56,12 @@ urlpatterns = [ ), path( "workspaces//projects//user-favorite-cycles/", - CycleFavoriteViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + CycleFavoriteViewSet.as_view({"get": "list", "post": "create"}), name="user-favorite-cycle", ), path( "workspaces//projects//user-favorite-cycles//", - CycleFavoriteViewSet.as_view( - { - "delete": "destroy", - } - ), + CycleFavoriteViewSet.as_view({"delete": "destroy"}), name="user-favorite-cycle", ), path( diff --git a/apiserver/plane/app/urls/estimate.py b/apiserver/plane/app/urls/estimate.py index 7db94aa46..8e5af2a85 100644 --- a/apiserver/plane/app/urls/estimate.py +++ b/apiserver/plane/app/urls/estimate.py @@ -16,42 +16,24 @@ urlpatterns = [ ), path( "workspaces//projects//estimates/", - BulkEstimatePointEndpoint.as_view( - { - "get": "list", - "post": "create", - } - ), + BulkEstimatePointEndpoint.as_view({"get": "list", "post": "create"}), name="bulk-create-estimate-points", ), path( "workspaces//projects//estimates//", BulkEstimatePointEndpoint.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="bulk-create-estimate-points", ), path( "workspaces//projects//estimates//estimate-points/", - EstimatePointEndpoint.as_view( - { - "post": "create", - } - ), + EstimatePointEndpoint.as_view({"post": "create"}), name="estimate-points", ), path( "workspaces//projects//estimates//estimate-points//", - EstimatePointEndpoint.as_view( - { - "patch": "partial_update", - "delete": "destroy", - } - ), + EstimatePointEndpoint.as_view({"patch": "partial_update", "delete": "destroy"}), name="estimate-points", ), ] diff --git a/apiserver/plane/app/urls/external.py b/apiserver/plane/app/urls/external.py index 744c646ca..4972962d8 100644 --- a/apiserver/plane/app/urls/external.py +++ b/apiserver/plane/app/urls/external.py @@ -6,17 +6,13 @@ from plane.app.views import GPTIntegrationEndpoint, WorkspaceGPTIntegrationEndpo urlpatterns = [ - path( - "unsplash/", - UnsplashEndpoint.as_view(), - name="unsplash", - ), + path("unsplash/", UnsplashEndpoint.as_view(), name="unsplash"), path( "workspaces//projects//ai-assistant/", GPTIntegrationEndpoint.as_view(), name="importer", ), - path( + path( "workspaces//ai-assistant/", WorkspaceGPTIntegrationEndpoint.as_view(), name="importer", diff --git a/apiserver/plane/app/urls/intake.py b/apiserver/plane/app/urls/intake.py index be2f3a053..397579262 100644 --- a/apiserver/plane/app/urls/intake.py +++ b/apiserver/plane/app/urls/intake.py @@ -1,94 +1,55 @@ from django.urls import path -from plane.app.views import ( - IntakeViewSet, - IntakeIssueViewSet, -) +from plane.app.views import IntakeViewSet, IntakeIssueViewSet urlpatterns = [ path( "workspaces//projects//intakes/", - IntakeViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IntakeViewSet.as_view({"get": "list", "post": "create"}), name="intake", ), path( "workspaces//projects//intakes//", IntakeViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="intake", ), path( "workspaces//projects//intake-issues/", - IntakeIssueViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IntakeIssueViewSet.as_view({"get": "list", "post": "create"}), name="intake-issue", ), path( "workspaces//projects//intake-issues//", IntakeIssueViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="intake-issue", ), path( "workspaces//projects//inboxes/", - IntakeViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IntakeViewSet.as_view({"get": "list", "post": "create"}), name="inbox", ), path( "workspaces//projects//inboxes//", IntakeViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="inbox", ), path( "workspaces//projects//inbox-issues/", - IntakeIssueViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IntakeIssueViewSet.as_view({"get": "list", "post": "create"}), name="inbox-issue", ), path( "workspaces//projects//inbox-issues//", IntakeIssueViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="inbox-issue", ), diff --git a/apiserver/plane/app/urls/issue.py b/apiserver/plane/app/urls/issue.py index e8ad4408d..07dfa5112 100644 --- a/apiserver/plane/app/urls/issue.py +++ b/apiserver/plane/app/urls/issue.py @@ -34,12 +34,7 @@ urlpatterns = [ ), path( "workspaces//projects//issues/", - IssueViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueViewSet.as_view({"get": "list", "post": "create"}), name="project-issue", ), path( @@ -68,12 +63,7 @@ urlpatterns = [ ), path( "workspaces//projects//issue-labels/", - LabelViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + LabelViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-labels", ), path( @@ -111,12 +101,7 @@ urlpatterns = [ ), path( "workspaces//projects//issues//issue-links/", - IssueLinkViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueLinkViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-links", ), path( @@ -169,12 +154,7 @@ urlpatterns = [ ## IssueComments path( "workspaces//projects//issues//comments/", - IssueCommentViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueCommentViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-comment", ), path( @@ -193,12 +173,7 @@ urlpatterns = [ # Issue Subscribers path( "workspaces//projects//issues//issue-subscribers/", - IssueSubscriberViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueSubscriberViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-subscribers", ), path( @@ -209,11 +184,7 @@ urlpatterns = [ path( "workspaces//projects//issues//subscribe/", IssueSubscriberViewSet.as_view( - { - "get": "subscription_status", - "post": "subscribe", - "delete": "unsubscribe", - } + {"get": "subscription_status", "post": "subscribe", "delete": "unsubscribe"} ), name="project-issue-subscribers", ), @@ -221,42 +192,24 @@ urlpatterns = [ # Issue Reactions path( "workspaces//projects//issues//reactions/", - IssueReactionViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueReactionViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-reactions", ), path( "workspaces//projects//issues//reactions//", - IssueReactionViewSet.as_view( - { - "delete": "destroy", - } - ), + IssueReactionViewSet.as_view({"delete": "destroy"}), name="project-issue-reactions", ), ## End Issue Reactions # Comment Reactions path( "workspaces//projects//comments//reactions/", - CommentReactionViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + CommentReactionViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-comment-reactions", ), path( "workspaces//projects//comments//reactions//", - CommentReactionViewSet.as_view( - { - "delete": "destroy", - } - ), + CommentReactionViewSet.as_view({"delete": "destroy"}), name="project-issue-comment-reactions", ), ## End Comment Reactions @@ -270,21 +223,13 @@ urlpatterns = [ ## Issue Archives path( "workspaces//projects//archived-issues/", - IssueArchiveViewSet.as_view( - { - "get": "list", - } - ), + IssueArchiveViewSet.as_view({"get": "list"}), name="project-issue-archive", ), path( "workspaces//projects//issues//archive/", IssueArchiveViewSet.as_view( - { - "get": "retrieve", - "post": "archive", - "delete": "unarchive", - } + {"get": "retrieve", "post": "archive", "delete": "unarchive"} ), name="project-issue-archive-unarchive", ), @@ -292,21 +237,12 @@ urlpatterns = [ ## Issue Relation path( "workspaces//projects//issues//issue-relation/", - IssueRelationViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueRelationViewSet.as_view({"get": "list", "post": "create"}), name="issue-relation", ), path( "workspaces//projects//issues//remove-relation/", - IssueRelationViewSet.as_view( - { - "post": "remove_relation", - } - ), + IssueRelationViewSet.as_view({"post": "remove_relation"}), name="issue-relation", ), ## End Issue Relation diff --git a/apiserver/plane/app/urls/module.py b/apiserver/plane/app/urls/module.py index bf6c84b2f..75cbb14d6 100644 --- a/apiserver/plane/app/urls/module.py +++ b/apiserver/plane/app/urls/module.py @@ -14,12 +14,7 @@ from plane.app.views import ( urlpatterns = [ path( "workspaces//projects//modules/", - ModuleViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + ModuleViewSet.as_view({"get": "list", "post": "create"}), name="project-modules", ), path( @@ -36,21 +31,12 @@ urlpatterns = [ ), path( "workspaces//projects//issues//modules/", - ModuleIssueViewSet.as_view( - { - "post": "create_issue_modules", - } - ), + ModuleIssueViewSet.as_view({"post": "create_issue_modules"}), name="issue-module", ), path( "workspaces//projects//modules//issues/", - ModuleIssueViewSet.as_view( - { - "post": "create_module_issues", - "get": "list", - } - ), + ModuleIssueViewSet.as_view({"post": "create_module_issues", "get": "list"}), name="project-module-issues", ), path( @@ -67,12 +53,7 @@ urlpatterns = [ ), path( "workspaces//projects//modules//module-links/", - ModuleLinkViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + ModuleLinkViewSet.as_view({"get": "list", "post": "create"}), name="project-issue-module-links", ), path( @@ -89,21 +70,12 @@ urlpatterns = [ ), path( "workspaces//projects//user-favorite-modules/", - ModuleFavoriteViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + ModuleFavoriteViewSet.as_view({"get": "list", "post": "create"}), name="user-favorite-module", ), path( "workspaces//projects//user-favorite-modules//", - ModuleFavoriteViewSet.as_view( - { - "delete": "destroy", - } - ), + ModuleFavoriteViewSet.as_view({"delete": "destroy"}), name="user-favorite-module", ), path( diff --git a/apiserver/plane/app/urls/notification.py b/apiserver/plane/app/urls/notification.py index 0bbf4f3c7..cd5647ea4 100644 --- a/apiserver/plane/app/urls/notification.py +++ b/apiserver/plane/app/urls/notification.py @@ -12,42 +12,24 @@ from plane.app.views import ( urlpatterns = [ path( "workspaces//users/notifications/", - NotificationViewSet.as_view( - { - "get": "list", - } - ), + NotificationViewSet.as_view({"get": "list"}), name="notifications", ), path( "workspaces//users/notifications//", NotificationViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="notifications", ), path( "workspaces//users/notifications//read/", - NotificationViewSet.as_view( - { - "post": "mark_read", - "delete": "mark_unread", - } - ), + NotificationViewSet.as_view({"post": "mark_read", "delete": "mark_unread"}), name="notifications", ), path( "workspaces//users/notifications//archive/", - NotificationViewSet.as_view( - { - "post": "archive", - "delete": "unarchive", - } - ), + NotificationViewSet.as_view({"post": "archive", "delete": "unarchive"}), name="notifications", ), path( @@ -57,11 +39,7 @@ urlpatterns = [ ), path( "workspaces//users/notifications/mark-all-read/", - MarkAllReadNotificationViewSet.as_view( - { - "post": "create", - } - ), + MarkAllReadNotificationViewSet.as_view({"post": "create"}), name="mark-all-read-notifications", ), path( diff --git a/apiserver/plane/app/urls/page.py b/apiserver/plane/app/urls/page.py index 7c1ac5dfe..b49f1d4a2 100644 --- a/apiserver/plane/app/urls/page.py +++ b/apiserver/plane/app/urls/page.py @@ -14,66 +14,38 @@ from plane.app.views import ( urlpatterns = [ path( "workspaces//projects//pages/", - PageViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + PageViewSet.as_view({"get": "list", "post": "create"}), name="project-pages", ), path( "workspaces//projects//pages//", PageViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="project-pages", ), # favorite pages path( "workspaces//projects//favorite-pages//", - PageFavoriteViewSet.as_view( - { - "post": "create", - "delete": "destroy", - } - ), + PageFavoriteViewSet.as_view({"post": "create", "delete": "destroy"}), name="user-favorite-pages", ), # archived pages path( "workspaces//projects//pages//archive/", - PageViewSet.as_view( - { - "post": "archive", - "delete": "unarchive", - } - ), + PageViewSet.as_view({"post": "archive", "delete": "unarchive"}), name="project-page-archive-unarchive", ), # lock and unlock path( "workspaces//projects//pages//lock/", - PageViewSet.as_view( - { - "post": "lock", - "delete": "unlock", - } - ), + PageViewSet.as_view({"post": "lock", "delete": "unlock"}), name="project-pages-lock-unlock", ), # private and public page path( "workspaces//projects//pages//access/", - PageViewSet.as_view( - { - "post": "access", - } - ), + PageViewSet.as_view({"post": "access"}), name="project-pages-access", ), path( @@ -93,12 +65,7 @@ urlpatterns = [ ), path( "workspaces//projects//pages//description/", - PagesDescriptionViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - } - ), + PagesDescriptionViewSet.as_view({"get": "retrieve", "patch": "partial_update"}), name="page-description", ), path( diff --git a/apiserver/plane/app/urls/project.py b/apiserver/plane/app/urls/project.py index 0807c7616..3f41caee8 100644 --- a/apiserver/plane/app/urls/project.py +++ b/apiserver/plane/app/urls/project.py @@ -21,12 +21,7 @@ from plane.app.views import ( urlpatterns = [ path( "workspaces//projects/", - ProjectViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + ProjectViewSet.as_view({"get": "list", "post": "create"}), name="project", ), path( @@ -48,32 +43,17 @@ urlpatterns = [ ), path( "workspaces//projects//invitations/", - ProjectInvitationsViewset.as_view( - { - "get": "list", - "post": "create", - }, - ), + ProjectInvitationsViewset.as_view({"get": "list", "post": "create"}), name="project-member-invite", ), path( "workspaces//projects//invitations//", - ProjectInvitationsViewset.as_view( - { - "get": "retrieve", - "delete": "destroy", - } - ), + ProjectInvitationsViewset.as_view({"get": "retrieve", "delete": "destroy"}), name="project-member-invite", ), path( "users/me/workspaces//projects/invitations/", - UserProjectInvitationsViewset.as_view( - { - "get": "list", - "post": "create", - }, - ), + UserProjectInvitationsViewset.as_view({"get": "list", "post": "create"}), name="user-project-invitations", ), path( @@ -88,32 +68,19 @@ urlpatterns = [ ), path( "workspaces//projects//members/", - ProjectMemberViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + ProjectMemberViewSet.as_view({"get": "list", "post": "create"}), name="project-member", ), path( "workspaces//projects//members//", ProjectMemberViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="project-member", ), path( "workspaces//projects//members/leave/", - ProjectMemberViewSet.as_view( - { - "post": "leave", - } - ), + ProjectMemberViewSet.as_view({"post": "leave"}), name="project-member", ), path( @@ -133,21 +100,12 @@ urlpatterns = [ ), path( "workspaces//user-favorite-projects/", - ProjectFavoritesViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + ProjectFavoritesViewSet.as_view({"get": "list", "post": "create"}), name="project-favorite", ), path( "workspaces//user-favorite-projects//", - ProjectFavoritesViewSet.as_view( - { - "delete": "destroy", - } - ), + ProjectFavoritesViewSet.as_view({"delete": "destroy"}), name="project-favorite", ), path( @@ -157,22 +115,13 @@ urlpatterns = [ ), path( "workspaces//projects//project-deploy-boards/", - DeployBoardViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + DeployBoardViewSet.as_view({"get": "list", "post": "create"}), name="project-deploy-board", ), path( "workspaces//projects//project-deploy-boards//", DeployBoardViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="project-deploy-board", ), diff --git a/apiserver/plane/app/urls/search.py b/apiserver/plane/app/urls/search.py index 05a79994e..bbea8093d 100644 --- a/apiserver/plane/app/urls/search.py +++ b/apiserver/plane/app/urls/search.py @@ -1,10 +1,7 @@ from django.urls import path -from plane.app.views import ( - GlobalSearchEndpoint, - IssueSearchEndpoint, -) +from plane.app.views import GlobalSearchEndpoint, IssueSearchEndpoint urlpatterns = [ diff --git a/apiserver/plane/app/urls/state.py b/apiserver/plane/app/urls/state.py index 9fec70ea1..b9ffd0341 100644 --- a/apiserver/plane/app/urls/state.py +++ b/apiserver/plane/app/urls/state.py @@ -7,32 +7,19 @@ from plane.app.views import StateViewSet urlpatterns = [ path( "workspaces//projects//states/", - StateViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + StateViewSet.as_view({"get": "list", "post": "create"}), name="project-states", ), path( "workspaces//projects//states//", StateViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="project-state", ), path( "workspaces//projects//states//mark-default/", - StateViewSet.as_view( - { - "post": "mark_as_default", - } - ), + StateViewSet.as_view({"post": "mark_as_default"}), name="project-state", ), ] diff --git a/apiserver/plane/app/urls/user.py b/apiserver/plane/app/urls/user.py index fd18ea87b..443961d0e 100644 --- a/apiserver/plane/app/urls/user.py +++ b/apiserver/plane/app/urls/user.py @@ -22,60 +22,30 @@ urlpatterns = [ path( "users/me/", UserEndpoint.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "deactivate", - } + {"get": "retrieve", "patch": "partial_update", "delete": "deactivate"} ), name="users", ), - path( - "users/session/", - UserSessionEndpoint.as_view(), - name="user-session", - ), + path("users/session/", UserSessionEndpoint.as_view(), name="user-session"), path( "users/me/settings/", - UserEndpoint.as_view( - { - "get": "retrieve_user_settings", - } - ), + UserEndpoint.as_view({"get": "retrieve_user_settings"}), name="users", ), # Profile - path( - "users/me/profile/", - ProfileEndpoint.as_view(), - name="accounts", - ), + path("users/me/profile/", ProfileEndpoint.as_view(), name="accounts"), # End profile # Accounts - path( - "users/me/accounts/", - AccountEndpoint.as_view(), - name="accounts", - ), - path( - "users/me/accounts//", - AccountEndpoint.as_view(), - name="accounts", - ), + path("users/me/accounts/", AccountEndpoint.as_view(), name="accounts"), + path("users/me/accounts//", AccountEndpoint.as_view(), name="accounts"), ## End Accounts path( "users/me/instance-admin/", - UserEndpoint.as_view( - { - "get": "retrieve_instance_admin", - } - ), + UserEndpoint.as_view({"get": "retrieve_instance_admin"}), name="users", ), path( - "users/me/onboard/", - UpdateUserOnBoardedEndpoint.as_view(), - name="user-onboard", + "users/me/onboard/", UpdateUserOnBoardedEndpoint.as_view(), name="user-onboard" ), path( "users/me/tour-completed/", @@ -83,15 +53,11 @@ urlpatterns = [ name="user-tour", ), path( - "users/me/activities/", - UserActivityEndpoint.as_view(), - name="user-activities", + "users/me/activities/", UserActivityEndpoint.as_view(), name="user-activities" ), # user workspaces path( - "users/me/workspaces/", - UserWorkSpacesEndpoint.as_view(), - name="user-workspace", + "users/me/workspaces/", UserWorkSpacesEndpoint.as_view(), name="user-workspace" ), # User Graphs path( diff --git a/apiserver/plane/app/urls/views.py b/apiserver/plane/app/urls/views.py index a2f8e2ac8..063e39c3d 100644 --- a/apiserver/plane/app/urls/views.py +++ b/apiserver/plane/app/urls/views.py @@ -12,12 +12,7 @@ from plane.app.views import ( urlpatterns = [ path( "workspaces//projects//views/", - IssueViewViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueViewViewSet.as_view({"get": "list", "post": "create"}), name="project-view", ), path( @@ -34,12 +29,7 @@ urlpatterns = [ ), path( "workspaces//views/", - WorkspaceViewViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + WorkspaceViewViewSet.as_view({"get": "list", "post": "create"}), name="global-view", ), path( @@ -56,30 +46,17 @@ urlpatterns = [ ), path( "workspaces//issues/", - WorkspaceViewIssuesViewSet.as_view( - { - "get": "list", - } - ), + WorkspaceViewIssuesViewSet.as_view({"get": "list"}), name="global-view-issues", ), path( "workspaces//projects//user-favorite-views/", - IssueViewFavoriteViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueViewFavoriteViewSet.as_view({"get": "list", "post": "create"}), name="user-favorite-view", ), path( "workspaces//projects//user-favorite-views//", - IssueViewFavoriteViewSet.as_view( - { - "delete": "destroy", - } - ), + IssueViewFavoriteViewSet.as_view({"delete": "destroy"}), name="user-favorite-view", ), ] diff --git a/apiserver/plane/app/urls/webhook.py b/apiserver/plane/app/urls/webhook.py index 16cc48be8..e21ec7261 100644 --- a/apiserver/plane/app/urls/webhook.py +++ b/apiserver/plane/app/urls/webhook.py @@ -8,11 +8,7 @@ from plane.app.views import ( urlpatterns = [ - path( - "workspaces//webhooks/", - WebhookEndpoint.as_view(), - name="webhooks", - ), + path("workspaces//webhooks/", WebhookEndpoint.as_view(), name="webhooks"), path( "workspaces//webhooks//", WebhookEndpoint.as_view(), diff --git a/apiserver/plane/app/urls/workspace.py b/apiserver/plane/app/urls/workspace.py index fb6f4c13a..00e996995 100644 --- a/apiserver/plane/app/urls/workspace.py +++ b/apiserver/plane/app/urls/workspace.py @@ -39,12 +39,7 @@ urlpatterns = [ ), path( "workspaces/", - WorkSpaceViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + WorkSpaceViewSet.as_view({"get": "list", "post": "create"}), name="workspace", ), path( @@ -61,34 +56,20 @@ urlpatterns = [ ), path( "workspaces//invitations/", - WorkspaceInvitationsViewset.as_view( - { - "get": "list", - "post": "create", - }, - ), + WorkspaceInvitationsViewset.as_view({"get": "list", "post": "create"}), name="workspace-invitations", ), path( "workspaces//invitations//", WorkspaceInvitationsViewset.as_view( - { - "delete": "destroy", - "get": "retrieve", - "patch": "partial_update", - } + {"delete": "destroy", "get": "retrieve", "patch": "partial_update"} ), name="workspace-invitations", ), # user workspace invitations path( "users/me/workspaces/invitations/", - UserWorkspaceInvitationsViewSet.as_view( - { - "get": "list", - "post": "create", - }, - ), + UserWorkspaceInvitationsViewSet.as_view({"get": "list", "post": "create"}), name="user-workspace-invitations", ), path( @@ -110,31 +91,18 @@ urlpatterns = [ path( "workspaces//members//", WorkSpaceMemberViewSet.as_view( - { - "patch": "partial_update", - "delete": "destroy", - "get": "retrieve", - } + {"patch": "partial_update", "delete": "destroy", "get": "retrieve"} ), name="workspace-member", ), path( "workspaces//members/leave/", - WorkSpaceMemberViewSet.as_view( - { - "post": "leave", - }, - ), + WorkSpaceMemberViewSet.as_view({"post": "leave"}), name="leave-workspace-members", ), path( "workspaces//teams/", - TeamMemberViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + TeamMemberViewSet.as_view({"get": "list", "post": "create"}), name="workspace-team-members", ), path( @@ -166,22 +134,13 @@ urlpatterns = [ ), path( "workspaces//workspace-themes/", - WorkspaceThemeViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + WorkspaceThemeViewSet.as_view({"get": "list", "post": "create"}), name="workspace-themes", ), path( "workspaces//workspace-themes//", WorkspaceThemeViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="workspace-themes", ), @@ -257,22 +216,13 @@ urlpatterns = [ ), path( "workspaces//draft-issues/", - WorkspaceDraftIssueViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + WorkspaceDraftIssueViewSet.as_view({"get": "list", "post": "create"}), name="workspace-draft-issues", ), path( "workspaces//draft-issues//", WorkspaceDraftIssueViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="workspace-drafts-issues", ), diff --git a/apiserver/plane/app/views/__init__.py b/apiserver/plane/app/views/__init__.py index 159686fa3..c21000a4e 100644 --- a/apiserver/plane/app/views/__init__.py +++ b/apiserver/plane/app/views/__init__.py @@ -59,12 +59,8 @@ from .workspace.invite import ( WorkspaceJoinEndpoint, UserWorkspaceInvitationsViewSet, ) -from .workspace.label import ( - WorkspaceLabelsEndpoint, -) -from .workspace.state import ( - WorkspaceStatesEndpoint, -) +from .workspace.label import WorkspaceLabelsEndpoint +from .workspace.state import WorkspaceStatesEndpoint from .workspace.user import ( UserLastProjectWithWorkspaceEndpoint, WorkspaceUserProfileIssuesEndpoint, @@ -75,15 +71,9 @@ from .workspace.user import ( UserActivityGraphEndpoint, UserIssueCompletedGraphEndpoint, ) -from .workspace.estimate import ( - WorkspaceEstimatesEndpoint, -) -from .workspace.module import ( - WorkspaceModulesEndpoint, -) -from .workspace.cycle import ( - WorkspaceCyclesEndpoint, -) +from .workspace.estimate import WorkspaceEstimatesEndpoint +from .workspace.module import WorkspaceModulesEndpoint +from .workspace.cycle import WorkspaceCyclesEndpoint from .state.base import StateViewSet from .view.base import ( @@ -103,18 +93,10 @@ from .cycle.base import ( CycleAnalyticsEndpoint, CycleProgressEndpoint, ) -from .cycle.issue import ( - CycleIssueViewSet, -) -from .cycle.archive import ( - CycleArchiveUnarchiveEndpoint, -) +from .cycle.issue import CycleIssueViewSet +from .cycle.archive import CycleArchiveUnarchiveEndpoint -from .asset.base import ( - FileAssetEndpoint, - UserAssetsEndpoint, - FileAssetViewSet, -) +from .asset.base import FileAssetEndpoint, UserAssetsEndpoint, FileAssetViewSet from .asset.v2 import ( WorkspaceFileAssetEndpoint, UserAssetsV2Endpoint, @@ -134,9 +116,7 @@ from .issue.base import ( IssueBulkUpdateDateEndpoint, ) -from .issue.activity import ( - IssueActivityEndpoint, -) +from .issue.activity import IssueActivityEndpoint from .issue.archive import IssueArchiveViewSet, BulkArchiveIssuesEndpoint @@ -146,35 +126,19 @@ from .issue.attachment import ( IssueAttachmentV2Endpoint, ) -from .issue.comment import ( - IssueCommentViewSet, - CommentReactionViewSet, -) +from .issue.comment import IssueCommentViewSet, CommentReactionViewSet -from .issue.label import ( - LabelViewSet, - BulkCreateIssueLabelsEndpoint, -) +from .issue.label import LabelViewSet, BulkCreateIssueLabelsEndpoint -from .issue.link import ( - IssueLinkViewSet, -) +from .issue.link import IssueLinkViewSet -from .issue.relation import ( - IssueRelationViewSet, -) +from .issue.relation import IssueRelationViewSet -from .issue.reaction import ( - IssueReactionViewSet, -) +from .issue.reaction import IssueReactionViewSet -from .issue.sub_issue import ( - SubIssuesEndpoint, -) +from .issue.sub_issue import SubIssuesEndpoint -from .issue.subscriber import ( - IssueSubscriberViewSet, -) +from .issue.subscriber import IssueSubscriberViewSet from .module.base import ( ModuleViewSet, @@ -183,18 +147,11 @@ from .module.base import ( ModuleUserPropertiesEndpoint, ) -from .module.issue import ( - ModuleIssueViewSet, -) +from .module.issue import ModuleIssueViewSet -from .module.archive import ( - ModuleArchiveUnarchiveEndpoint, -) +from .module.archive import ModuleArchiveUnarchiveEndpoint -from .api import ( - ApiTokenEndpoint, - ServiceApiTokenEndpoint, -) +from .api import ApiTokenEndpoint, ServiceApiTokenEndpoint from .page.base import ( PageViewSet, diff --git a/apiserver/plane/app/views/analytic/base.py b/apiserver/plane/app/views/analytic/base.py index 49728cde8..a36458406 100644 --- a/apiserver/plane/app/views/analytic/base.py +++ b/apiserver/plane/app/views/analytic/base.py @@ -22,14 +22,7 @@ from plane.app.permissions import allow_permission, ROLE class AnalyticsEndpoint(BaseAPIView): - - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ], - level="WORKSPACE", - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def get(self, request, slug): x_axis = request.GET.get("x_axis", False) y_axis = request.GET.get("y_axis", False) @@ -50,10 +43,7 @@ class AnalyticsEndpoint(BaseAPIView): "completed_at", ] - valid_yaxis = [ - "issue_count", - "estimate", - ] + valid_yaxis = ["issue_count", "estimate"] # Check for x-axis and y-axis as thery are required parameters if ( @@ -70,9 +60,7 @@ class AnalyticsEndpoint(BaseAPIView): ) # If segment is present it cannot be same as x-axis - if segment and ( - segment not in valid_xaxis_segment or x_axis == segment - ): + if segment and (segment not in valid_xaxis_segment or x_axis == segment): return Response( { "error": "Both segment and x axis cannot be same and segment should be valid" @@ -97,10 +85,7 @@ class AnalyticsEndpoint(BaseAPIView): state_details = {} if x_axis in ["state_id"] or segment in ["state_id"]: state_details = ( - Issue.issue_objects.filter( - workspace__slug=slug, - **filters, - ) + Issue.issue_objects.filter(workspace__slug=slug, **filters) .distinct("state_id") .order_by("state_id") .values("state_id", "state__name", "state__color") @@ -163,9 +148,7 @@ class AnalyticsEndpoint(BaseAPIView): ) cycle_details = {} - if x_axis in ["issue_cycle__cycle_id"] or segment in [ - "issue_cycle__cycle_id" - ]: + if x_axis in ["issue_cycle__cycle_id"] or segment in ["issue_cycle__cycle_id"]: cycle_details = ( Issue.issue_objects.filter( workspace__slug=slug, @@ -175,10 +158,7 @@ class AnalyticsEndpoint(BaseAPIView): ) .distinct("issue_cycle__cycle_id") .order_by("issue_cycle__cycle_id") - .values( - "issue_cycle__cycle_id", - "issue_cycle__cycle__name", - ) + .values("issue_cycle__cycle_id", "issue_cycle__cycle__name") ) module_details = {} @@ -194,10 +174,7 @@ class AnalyticsEndpoint(BaseAPIView): ) .distinct("issue_module__module_id") .order_by("issue_module__module_id") - .values( - "issue_module__module_id", - "issue_module__module__name", - ) + .values("issue_module__module_id", "issue_module__module__name") ) return Response( @@ -217,9 +194,7 @@ class AnalyticsEndpoint(BaseAPIView): class AnalyticViewViewset(BaseViewSet): - permission_classes = [ - WorkSpaceAdminPermission, - ] + permission_classes = [WorkSpaceAdminPermission] model = AnalyticView serializer_class = AnalyticViewSerializer @@ -229,25 +204,14 @@ class AnalyticViewViewset(BaseViewSet): def get_queryset(self): return self.filter_queryset( - super() - .get_queryset() - .filter(workspace__slug=self.kwargs.get("slug")) + super().get_queryset().filter(workspace__slug=self.kwargs.get("slug")) ) class SavedAnalyticEndpoint(BaseAPIView): - - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ], - level="WORKSPACE", - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def get(self, request, slug, analytic_id): - analytic_view = AnalyticView.objects.get( - pk=analytic_id, workspace__slug=slug - ) + analytic_view = AnalyticView.objects.get(pk=analytic_id, workspace__slug=slug) filter = analytic_view.query queryset = Issue.issue_objects.filter(**filter) @@ -273,14 +237,7 @@ class SavedAnalyticEndpoint(BaseAPIView): class ExportAnalyticsEndpoint(BaseAPIView): - - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ], - level="WORKSPACE", - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def post(self, request, slug): x_axis = request.data.get("x_axis", False) y_axis = request.data.get("y_axis", False) @@ -301,10 +258,7 @@ class ExportAnalyticsEndpoint(BaseAPIView): "completed_at", ] - valid_yaxis = [ - "issue_count", - "estimate", - ] + valid_yaxis = ["issue_count", "estimate"] # Check for x-axis and y-axis as thery are required parameters if ( @@ -321,9 +275,7 @@ class ExportAnalyticsEndpoint(BaseAPIView): ) # If segment is present it cannot be same as x-axis - if segment and ( - segment not in valid_xaxis_segment or x_axis == segment - ): + if segment and (segment not in valid_xaxis_segment or x_axis == segment): return Response( { "error": "Both segment and x axis cannot be same and segment should be valid" @@ -344,13 +296,10 @@ class ExportAnalyticsEndpoint(BaseAPIView): class DefaultAnalyticsEndpoint(BaseAPIView): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE") def get(self, request, slug): filters = issue_filters(request.GET, "GET") - base_issues = Issue.issue_objects.filter( - workspace__slug=slug, **filters - ) + base_issues = Issue.issue_objects.filter(workspace__slug=slug, **filters) total_issues = base_issues.count() @@ -363,9 +312,7 @@ class DefaultAnalyticsEndpoint(BaseAPIView): ) open_issues_groups = ["backlog", "unstarted", "started"] - open_issues_queryset = state_groups.filter( - state__group__in=open_issues_groups - ) + open_issues_queryset = state_groups.filter(state__group__in=open_issues_groups) open_issues = open_issues_queryset.count() open_issues_classified = ( @@ -407,8 +354,7 @@ class DefaultAnalyticsEndpoint(BaseAPIView): ), # If `avatar_asset` is None, fall back to using `avatar` field directly When( - created_by__avatar_asset__isnull=True, - then="created_by__avatar", + created_by__avatar_asset__isnull=True, then="created_by__avatar" ), default=Value(None), output_field=models.CharField(), @@ -441,8 +387,7 @@ class DefaultAnalyticsEndpoint(BaseAPIView): ), # If `avatar_asset` is None, fall back to using `avatar` field directly When( - assignees__avatar_asset__isnull=True, - then="assignees__avatar", + assignees__avatar_asset__isnull=True, then="assignees__avatar" ), default=Value(None), output_field=models.CharField(), @@ -469,8 +414,7 @@ class DefaultAnalyticsEndpoint(BaseAPIView): ), # If `avatar_asset` is None, fall back to using `avatar` field directly When( - assignees__avatar_asset__isnull=True, - then="assignees__avatar", + assignees__avatar_asset__isnull=True, then="assignees__avatar" ), default=Value(None), output_field=models.CharField(), @@ -479,9 +423,7 @@ class DefaultAnalyticsEndpoint(BaseAPIView): .order_by("-count") ) - open_estimate_sum = open_issues_queryset.aggregate(sum=Sum("point"))[ - "sum" - ] + open_estimate_sum = open_issues_queryset.aggregate(sum=Sum("point"))["sum"] total_estimate_sum = base_issues.aggregate(sum=Sum("point"))["sum"] return Response( diff --git a/apiserver/plane/app/views/api.py b/apiserver/plane/app/views/api.py index fe7259fbb..732d96832 100644 --- a/apiserver/plane/app/views/api.py +++ b/apiserver/plane/app/views/api.py @@ -13,9 +13,7 @@ from plane.app.permissions import WorkspaceOwnerPermission class ApiTokenEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceOwnerPermission, - ] + permission_classes = [WorkspaceOwnerPermission] def post(self, request, slug): label = request.data.get("label", str(uuid4().hex)) @@ -37,10 +35,7 @@ class ApiTokenEndpoint(BaseAPIView): serializer = APITokenSerializer(api_token) # Token will be only visible while creating - return Response( - serializer.data, - status=status.HTTP_201_CREATED, - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) def get(self, request, slug, pk=None): if pk is None: @@ -58,23 +53,14 @@ class ApiTokenEndpoint(BaseAPIView): def delete(self, request, slug, pk): api_token = APIToken.objects.get( - workspace__slug=slug, - user=request.user, - pk=pk, - is_service=False, + workspace__slug=slug, user=request.user, pk=pk, is_service=False ) api_token.delete() return Response(status=status.HTTP_204_NO_CONTENT) def patch(self, request, slug, pk): - api_token = APIToken.objects.get( - workspace__slug=slug, - user=request.user, - pk=pk, - ) - serializer = APITokenSerializer( - api_token, data=request.data, partial=True - ) + api_token = APIToken.objects.get(workspace__slug=slug, user=request.user, pk=pk) + serializer = APITokenSerializer(api_token, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) @@ -82,25 +68,17 @@ class ApiTokenEndpoint(BaseAPIView): class ServiceApiTokenEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceOwnerPermission, - ] + permission_classes = [WorkspaceOwnerPermission] def post(self, request, slug): workspace = Workspace.objects.get(slug=slug) api_token = APIToken.objects.filter( - workspace=workspace, - is_service=True, + workspace=workspace, is_service=True ).first() if api_token: - return Response( - { - "token": str(api_token.token), - }, - status=status.HTTP_200_OK, - ) + return Response({"token": str(api_token.token)}, status=status.HTTP_200_OK) else: # Check the user type user_type = 1 if request.user.is_bot else 0 @@ -114,9 +92,5 @@ class ServiceApiTokenEndpoint(BaseAPIView): is_service=True, ) return Response( - { - "token": str(api_token.token), - }, - status=status.HTTP_201_CREATED, + {"token": str(api_token.token)}, status=status.HTTP_201_CREATED ) - diff --git a/apiserver/plane/app/views/asset/base.py b/apiserver/plane/app/views/asset/base.py index ebf6bc164..d30f0bb26 100644 --- a/apiserver/plane/app/views/asset/base.py +++ b/apiserver/plane/app/views/asset/base.py @@ -10,11 +10,7 @@ from plane.app.serializers import FileAssetSerializer class FileAssetEndpoint(BaseAPIView): - parser_classes = ( - MultiPartParser, - FormParser, - JSONParser, - ) + parser_classes = (MultiPartParser, FormParser, JSONParser) """ A viewset for viewing and editing task instances. @@ -28,8 +24,7 @@ class FileAssetEndpoint(BaseAPIView): files, context={"request": request}, many=True ) return Response( - {"data": serializer.data, "status": True}, - status=status.HTTP_200_OK, + {"data": serializer.data, "status": True}, status=status.HTTP_200_OK ) else: return Response( @@ -67,16 +62,11 @@ class UserAssetsEndpoint(BaseAPIView): parser_classes = (MultiPartParser, FormParser) def get(self, request, asset_key): - files = FileAsset.objects.filter( - asset=asset_key, created_by=request.user - ) + files = FileAsset.objects.filter(asset=asset_key, created_by=request.user) if files.exists(): - serializer = FileAssetSerializer( - files, context={"request": request} - ) + serializer = FileAssetSerializer(files, context={"request": request}) return Response( - {"data": serializer.data, "status": True}, - status=status.HTTP_200_OK, + {"data": serializer.data, "status": True}, status=status.HTTP_200_OK ) else: return Response( @@ -92,9 +82,7 @@ class UserAssetsEndpoint(BaseAPIView): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, asset_key): - file_asset = FileAsset.objects.get( - asset=asset_key, created_by=request.user - ) + file_asset = FileAsset.objects.get(asset=asset_key, created_by=request.user) file_asset.is_deleted = True file_asset.save(update_fields=["is_deleted"]) return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/app/views/asset/v2.py b/apiserver/plane/app/views/asset/v2.py index 16e82de59..827c95908 100644 --- a/apiserver/plane/app/views/asset/v2.py +++ b/apiserver/plane/app/views/asset/v2.py @@ -13,12 +13,7 @@ from rest_framework.permissions import AllowAny # Module imports from ..base import BaseAPIView -from plane.db.models import ( - FileAsset, - Workspace, - Project, - User, -) +from plane.db.models import FileAsset, Workspace, Project, User from plane.settings.storage import S3Storage from plane.app.permissions import allow_permission, ROLE from plane.utils.cache import invalidate_cache_directly @@ -49,10 +44,7 @@ class UserAssetsV2Endpoint(BaseAPIView): user.avatar_asset_id = asset_id user.save() invalidate_cache_directly( - path="/api/users/me/", - url_params=False, - user=True, - request=request, + path="/api/users/me/", url_params=False, user=True, request=request ) invalidate_cache_directly( path="/api/users/me/settings/", @@ -72,10 +64,7 @@ class UserAssetsV2Endpoint(BaseAPIView): user.cover_image_asset_id = asset_id user.save() invalidate_cache_directly( - path="/api/users/me/", - url_params=False, - user=True, - request=request, + path="/api/users/me/", url_params=False, user=True, request=request ) invalidate_cache_directly( path="/api/users/me/settings/", @@ -93,10 +82,7 @@ class UserAssetsV2Endpoint(BaseAPIView): user.avatar_asset_id = None user.save() invalidate_cache_directly( - path="/api/users/me/", - url_params=False, - user=True, - request=request, + path="/api/users/me/", url_params=False, user=True, request=request ) invalidate_cache_directly( path="/api/users/me/settings/", @@ -111,10 +97,7 @@ class UserAssetsV2Endpoint(BaseAPIView): user.cover_image_asset_id = None user.save() invalidate_cache_directly( - path="/api/users/me/", - url_params=False, - user=True, - request=request, + path="/api/users/me/", url_params=False, user=True, request=request ) invalidate_cache_directly( path="/api/users/me/settings/", @@ -138,20 +121,12 @@ class UserAssetsV2Endpoint(BaseAPIView): # Check if the entity type is allowed if not entity_type or entity_type not in ["USER_AVATAR", "USER_COVER"]: return Response( - { - "error": "Invalid entity type.", - "status": False, - }, + {"error": "Invalid entity type.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) # Check if the file type is allowed - allowed_types = [ - "image/jpeg", - "image/png", - "image/webp", - "image/jpg", - ] + allowed_types = ["image/jpeg", "image/png", "image/webp", "image/jpg"] if type not in allowed_types: return Response( { @@ -166,11 +141,7 @@ class UserAssetsV2Endpoint(BaseAPIView): # Create a File Asset asset = FileAsset.objects.create( - attributes={ - "name": name, - "type": type, - "size": size_limit, - }, + attributes={"name": name, "type": type, "size": size_limit}, asset=asset_key, size=size_limit, user=request.user, @@ -182,9 +153,7 @@ class UserAssetsV2Endpoint(BaseAPIView): storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object presigned_url = storage.generate_presigned_post( - object_name=asset_key, - file_type=type, - file_size=size_limit, + object_name=asset_key, file_type=type, file_size=size_limit ) # Return the presigned URL return Response( @@ -235,45 +204,33 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): def get_entity_id_field(self, entity_type, entity_id): # Workspace Logo if entity_type == FileAsset.EntityTypeContext.WORKSPACE_LOGO: - return { - "workspace_id": entity_id, - } + return {"workspace_id": entity_id} # Project Cover if entity_type == FileAsset.EntityTypeContext.PROJECT_COVER: - return { - "project_id": entity_id, - } + return {"project_id": entity_id} # User Avatar and Cover if entity_type in [ FileAsset.EntityTypeContext.USER_AVATAR, FileAsset.EntityTypeContext.USER_COVER, ]: - return { - "user_id": entity_id, - } + return {"user_id": entity_id} # Issue Attachment and Description if entity_type in [ FileAsset.EntityTypeContext.ISSUE_ATTACHMENT, FileAsset.EntityTypeContext.ISSUE_DESCRIPTION, ]: - return { - "issue_id": entity_id, - } + return {"issue_id": entity_id} # Page Description if entity_type == FileAsset.EntityTypeContext.PAGE_DESCRIPTION: - return { - "page_id": entity_id, - } + return {"page_id": entity_id} # Comment Description if entity_type == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION: - return { - "comment_id": entity_id, - } + return {"comment_id": entity_id} return {} def asset_delete(self, asset_id): @@ -301,10 +258,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): workspace.logo_asset_id = asset_id workspace.save() invalidate_cache_directly( - path="/api/workspaces/", - url_params=False, - user=False, - request=request, + path="/api/workspaces/", url_params=False, user=False, request=request ) invalidate_cache_directly( path="/api/users/me/workspaces/", @@ -313,10 +267,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): request=request, ) invalidate_cache_directly( - path="/api/instances/", - url_params=False, - user=False, - request=request, + path="/api/instances/", url_params=False, user=False, request=request ) return @@ -345,10 +296,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): workspace.logo_asset_id = None workspace.save() invalidate_cache_directly( - path="/api/workspaces/", - url_params=False, - user=False, - request=request, + path="/api/workspaces/", url_params=False, user=False, request=request ) invalidate_cache_directly( path="/api/users/me/workspaces/", @@ -357,10 +305,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): request=request, ) invalidate_cache_directly( - path="/api/instances/", - url_params=False, - user=False, - request=request, + path="/api/instances/", url_params=False, user=False, request=request ) return # Project Cover @@ -384,10 +329,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): # Check if the entity type is allowed if entity_type not in FileAsset.EntityTypeContext.values: return Response( - { - "error": "Invalid entity type.", - "status": False, - }, + {"error": "Invalid entity type.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) @@ -419,11 +361,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): # Create a File Asset asset = FileAsset.objects.create( - attributes={ - "name": name, - "type": type, - "size": size_limit, - }, + attributes={"name": name, "type": type, "size": size_limit}, asset=asset_key, size=size_limit, workspace=workspace, @@ -438,9 +376,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object presigned_url = storage.generate_presigned_post( - object_name=asset_key, - file_type=type, - file_size=size_limit, + object_name=asset_key, file_type=type, file_size=size_limit ) # Return the presigned URL return Response( @@ -491,18 +427,14 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): # Check if the asset is uploaded if not asset.is_uploaded: return Response( - { - "error": "The requested asset could not be found.", - }, + {"error": "The requested asset could not be found."}, status=status.HTTP_404_NOT_FOUND, ) # Get the presigned URL storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object - signed_url = storage.generate_presigned_url( - object_name=asset.asset.name, - ) + signed_url = storage.generate_presigned_url(object_name=asset.asset.name) # Redirect to the signed URL return HttpResponseRedirect(signed_url) @@ -510,9 +442,7 @@ class WorkspaceFileAssetEndpoint(BaseAPIView): class StaticFileAssetEndpoint(BaseAPIView): """This endpoint is used to get the signed URL for a static asset.""" - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, asset_id): # get the asset id @@ -521,9 +451,7 @@ class StaticFileAssetEndpoint(BaseAPIView): # Check if the asset is uploaded if not asset.is_uploaded: return Response( - { - "error": "The requested asset could not be found.", - }, + {"error": "The requested asset could not be found."}, status=status.HTTP_404_NOT_FOUND, ) @@ -535,19 +463,14 @@ class StaticFileAssetEndpoint(BaseAPIView): FileAsset.EntityTypeContext.PROJECT_COVER, ]: return Response( - { - "error": "Invalid entity type.", - "status": False, - }, + {"error": "Invalid entity type.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) # Get the presigned URL storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object - signed_url = storage.generate_presigned_url( - object_name=asset.asset.name, - ) + signed_url = storage.generate_presigned_url(object_name=asset.asset.name) # Redirect to the signed URL return HttpResponseRedirect(signed_url) @@ -569,50 +492,34 @@ class ProjectAssetEndpoint(BaseAPIView): def get_entity_id_field(self, entity_type, entity_id): if entity_type == FileAsset.EntityTypeContext.WORKSPACE_LOGO: - return { - "workspace_id": entity_id, - } + return {"workspace_id": entity_id} if entity_type == FileAsset.EntityTypeContext.PROJECT_COVER: - return { - "project_id": entity_id, - } + return {"project_id": entity_id} if entity_type in [ FileAsset.EntityTypeContext.USER_AVATAR, FileAsset.EntityTypeContext.USER_COVER, ]: - return { - "user_id": entity_id, - } + return {"user_id": entity_id} if entity_type in [ FileAsset.EntityTypeContext.ISSUE_ATTACHMENT, FileAsset.EntityTypeContext.ISSUE_DESCRIPTION, ]: - return { - "issue_id": entity_id, - } + return {"issue_id": entity_id} if entity_type == FileAsset.EntityTypeContext.PAGE_DESCRIPTION: - return { - "page_id": entity_id, - } + return {"page_id": entity_id} if entity_type == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION: - return { - "comment_id": entity_id, - } + return {"comment_id": entity_id} if entity_type == FileAsset.EntityTypeContext.DRAFT_ISSUE_DESCRIPTION: - return { - "draft_issue_id": entity_id, - } + return {"draft_issue_id": entity_id} return {} - @allow_permission( - [ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def post(self, request, slug, project_id): name = request.data.get("name") type = request.data.get("type", "image/jpeg") @@ -623,10 +530,7 @@ class ProjectAssetEndpoint(BaseAPIView): # Check if the entity type is allowed if entity_type not in FileAsset.EntityTypeContext.values: return Response( - { - "error": "Invalid entity type.", - "status": False, - }, + {"error": "Invalid entity type.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) @@ -658,11 +562,7 @@ class ProjectAssetEndpoint(BaseAPIView): # Create a File Asset asset = FileAsset.objects.create( - attributes={ - "name": name, - "type": type, - "size": size_limit, - }, + attributes={"name": name, "type": type, "size": size_limit}, asset=asset_key, size=size_limit, workspace=workspace, @@ -676,9 +576,7 @@ class ProjectAssetEndpoint(BaseAPIView): storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object presigned_url = storage.generate_presigned_post( - object_name=asset_key, - file_type=type, - file_size=size_limit, + object_name=asset_key, file_type=type, file_size=size_limit ) # Return the presigned URL return Response( @@ -690,14 +588,10 @@ class ProjectAssetEndpoint(BaseAPIView): status=status.HTTP_200_OK, ) - @allow_permission( - [ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def patch(self, request, slug, project_id, pk): # get the asset id - asset = FileAsset.objects.get( - id=pk, - ) + asset = FileAsset.objects.get(id=pk) # get the storage metadata asset.is_uploaded = True # get the storage metadata @@ -714,9 +608,7 @@ class ProjectAssetEndpoint(BaseAPIView): def delete(self, request, slug, project_id, pk): # Get the asset asset = FileAsset.objects.get( - id=pk, - workspace__slug=slug, - project_id=project_id, + id=pk, workspace__slug=slug, project_id=project_id ) # Check deleted assets asset.is_deleted = True @@ -729,32 +621,25 @@ class ProjectAssetEndpoint(BaseAPIView): def get(self, request, slug, project_id, pk): # get the asset id asset = FileAsset.objects.get( - workspace__slug=slug, - project_id=project_id, - pk=pk, + workspace__slug=slug, project_id=project_id, pk=pk ) # Check if the asset is uploaded if not asset.is_uploaded: return Response( - { - "error": "The requested asset could not be found.", - }, + {"error": "The requested asset could not be found."}, status=status.HTTP_404_NOT_FOUND, ) # Get the presigned URL storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object - signed_url = storage.generate_presigned_url( - object_name=asset.asset.name, - ) + signed_url = storage.generate_presigned_url(object_name=asset.asset.name) # Redirect to the signed URL return HttpResponseRedirect(signed_url) class ProjectBulkAssetEndpoint(BaseAPIView): - def save_project_cover(self, asset, project_id): project = Project.objects.get(id=project_id) project.cover_image_asset_id = asset.id @@ -767,60 +652,36 @@ class ProjectBulkAssetEndpoint(BaseAPIView): # Check if the asset ids are provided if not asset_ids: return Response( - { - "error": "No asset ids provided.", - }, - status=status.HTTP_400_BAD_REQUEST, + {"error": "No asset ids provided."}, status=status.HTTP_400_BAD_REQUEST ) # get the asset id - assets = FileAsset.objects.filter( - id__in=asset_ids, - workspace__slug=slug, - ) + assets = FileAsset.objects.filter(id__in=asset_ids, workspace__slug=slug) # Get the first asset asset = assets.first() if not asset: return Response( - { - "error": "The requested asset could not be found.", - }, + {"error": "The requested asset could not be found."}, status=status.HTTP_404_NOT_FOUND, ) # Check if the asset is uploaded if asset.entity_type == FileAsset.EntityTypeContext.PROJECT_COVER: - assets.update( - project_id=project_id, - ) + assets.update(project_id=project_id) [self.save_project_cover(asset, project_id) for asset in assets] if asset.entity_type == FileAsset.EntityTypeContext.ISSUE_DESCRIPTION: - assets.update( - issue_id=entity_id, - ) + assets.update(issue_id=entity_id) - if ( - asset.entity_type - == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION - ): - assets.update( - comment_id=entity_id, - ) + if asset.entity_type == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION: + assets.update(comment_id=entity_id) if asset.entity_type == FileAsset.EntityTypeContext.PAGE_DESCRIPTION: - assets.update( - page_id=entity_id, - ) + assets.update(page_id=entity_id) - if ( - asset.entity_type - == FileAsset.EntityTypeContext.DRAFT_ISSUE_DESCRIPTION - ): - assets.update( - draft_issue_id=entity_id, - ) + if asset.entity_type == FileAsset.EntityTypeContext.DRAFT_ISSUE_DESCRIPTION: + assets.update(draft_issue_id=entity_id) return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/app/views/base.py b/apiserver/plane/app/views/base.py index 45488b64e..92c374966 100644 --- a/apiserver/plane/app/views/base.py +++ b/apiserver/plane/app/views/base.py @@ -43,18 +43,11 @@ class TimezoneMixin: class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): model = None - permission_classes = [ - IsAuthenticated, - ] + permission_classes = [IsAuthenticated] - filter_backends = ( - DjangoFilterBackend, - SearchFilter, - ) + filter_backends = (DjangoFilterBackend, SearchFilter) - authentication_classes = [ - BaseSessionAuthentication, - ] + authentication_classes = [BaseSessionAuthentication] filterset_fields = [] @@ -65,9 +58,7 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): return self.model.objects.all() except Exception as e: log_exception(e) - raise APIException( - "Please check the view", status.HTTP_400_BAD_REQUEST - ) + raise APIException("Please check the view", status.HTTP_400_BAD_REQUEST) def handle_exception(self, exc): """ @@ -146,35 +137,24 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): @property def fields(self): fields = [ - field - for field in self.request.GET.get("fields", "").split(",") - if field + field for field in self.request.GET.get("fields", "").split(",") if field ] return fields if fields else None @property def expand(self): expand = [ - expand - for expand in self.request.GET.get("expand", "").split(",") - if expand + expand for expand in self.request.GET.get("expand", "").split(",") if expand ] return expand if expand else None class BaseAPIView(TimezoneMixin, APIView, BasePaginator): - permission_classes = [ - IsAuthenticated, - ] + permission_classes = [IsAuthenticated] - filter_backends = ( - DjangoFilterBackend, - SearchFilter, - ) + filter_backends = (DjangoFilterBackend, SearchFilter) - authentication_classes = [ - BaseSessionAuthentication, - ] + authentication_classes = [BaseSessionAuthentication] filterset_fields = [] @@ -251,17 +231,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): @property def fields(self): fields = [ - field - for field in self.request.GET.get("fields", "").split(",") - if field + field for field in self.request.GET.get("fields", "").split(",") if field ] return fields if fields else None @property def expand(self): expand = [ - expand - for expand in self.request.GET.get("expand", "").split(",") - if expand + expand for expand in self.request.GET.get("expand", "").split(",") if expand ] return expand if expand else None diff --git a/apiserver/plane/app/views/cycle/archive.py b/apiserver/plane/app/views/cycle/archive.py index d75e60e91..f58ad9aea 100644 --- a/apiserver/plane/app/views/cycle/archive.py +++ b/apiserver/plane/app/views/cycle/archive.py @@ -34,7 +34,6 @@ from .. import BaseAPIView class CycleArchiveUnarchiveEndpoint(BaseAPIView): - def get_queryset(self): favorite_subquery = UserFavorite.objects.filter( user=self.request.user, @@ -52,9 +51,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("issue_cycle__cycle_id") .annotate( - backlog_estimate_point=Sum( - Cast("estimate_point__value", FloatField()) - ) + backlog_estimate_point=Sum(Cast("estimate_point__value", FloatField())) ) .values("backlog_estimate_point")[:1] ) @@ -82,9 +79,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("issue_cycle__cycle_id") .annotate( - started_estimate_point=Sum( - Cast("estimate_point__value", FloatField()) - ) + started_estimate_point=Sum(Cast("estimate_point__value", FloatField())) ) .values("started_estimate_point")[:1] ) @@ -126,9 +121,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("issue_cycle__cycle_id") .annotate( - total_estimate_points=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimate_points=Sum(Cast("estimate_point__value", FloatField())) ) .values("total_estimate_points")[:1] ) @@ -153,9 +146,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): .prefetch_related( Prefetch( "issue_cycle__issue__labels", - queryset=Label.objects.only( - "name", "color", "id" - ).distinct(), + queryset=Label.objects.only("name", "color", "id").distinct(), ) ) .annotate(is_favorite=Exists(favorite_subquery)) @@ -237,9 +228,7 @@ class CycleArchiveUnarchiveEndpoint(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), @@ -254,9 +243,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ArrayAgg( "issue_cycle__issue__assignees__id", distinct=True, - filter=~Q( - issue_cycle__issue__assignees__id__isnull=True - ), + filter=~Q(issue_cycle__issue__assignees__id__isnull=True), ), Value([], output_field=ArrayField(UUIDField())), ) @@ -265,48 +252,42 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): backlog_estimate_points=Coalesce( Subquery(backlog_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( unstarted_estimate_points=Coalesce( Subquery(unstarted_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( started_estimate_points=Coalesce( Subquery(started_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( cancelled_estimate_points=Coalesce( Subquery(cancelled_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( completed_estimate_points=Coalesce( Subquery(completed_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( total_estimate_points=Coalesce( - Subquery(total_estimate_point), - Value(0, output_field=FloatField()), - ), + Subquery(total_estimate_point), Value(0, output_field=FloatField()) + ) ) .order_by("-is_favorite", "name") .distinct() ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def get(self, request, slug, project_id, pk=None): if pk is None: queryset = ( @@ -342,9 +323,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): return Response(queryset, status=status.HTTP_200_OK) else: queryset = ( - self.get_queryset() - .filter(archived_at__isnull=False) - .filter(pk=pk) + self.get_queryset().filter(archived_at__isnull=False).filter(pk=pk) ) data = ( self.get_queryset() @@ -437,9 +416,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("display_name", "assignee_id", "avatar_url") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -476,9 +453,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): .annotate(label_id=F("labels__id")) .values("label_name", "color", "label_id") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -509,14 +484,12 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): } if data["start_date"] and data["end_date"]: - data["estimate_distribution"]["completion_chart"] = ( - burndown_plot( - queryset=queryset, - slug=slug, - project_id=project_id, - plot_type="points", - cycle_id=pk, - ) + data["estimate_distribution"]["completion_chart"] = burndown_plot( + queryset=queryset, + slug=slug, + project_id=project_id, + plot_type="points", + cycle_id=pk, ) # Assignee Distribution @@ -560,12 +533,8 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -604,12 +573,8 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -649,10 +614,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): cycle_id=pk, ) - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def post(self, request, slug, project_id, cycle_id): @@ -675,8 +637,7 @@ class CycleArchiveUnarchiveEndpoint(BaseAPIView): workspace__slug=slug, ).delete() return Response( - {"archived_at": str(cycle.archived_at)}, - status=status.HTTP_200_OK, + {"archived_at": str(cycle.archived_at)}, status=status.HTTP_200_OK ) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 006440010..61ea9eed4 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -89,9 +89,7 @@ class CycleViewSet(BaseViewSet): .prefetch_related( Prefetch( "issue_cycle__issue__labels", - queryset=Label.objects.only( - "name", "color", "id" - ).distinct(), + queryset=Label.objects.only("name", "color", "id").distinct(), ) ) .annotate(is_favorite=Exists(favorite_subquery)) @@ -125,9 +123,7 @@ class CycleViewSet(BaseViewSet): & 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), @@ -142,9 +138,7 @@ class CycleViewSet(BaseViewSet): ArrayAgg( "issue_cycle__issue__assignees__id", distinct=True, - filter=~Q( - issue_cycle__issue__assignees__id__isnull=True - ) + filter=~Q(issue_cycle__issue__assignees__id__isnull=True) & ( Q( issue_cycle__issue__issue_assignee__deleted_at__isnull=True @@ -169,8 +163,7 @@ class CycleViewSet(BaseViewSet): # Current Cycle if cycle_view == "current": queryset = queryset.filter( - start_date__lte=timezone.now(), - end_date__gte=timezone.now(), + start_date__lte=timezone.now(), end_date__gte=timezone.now() ) data = queryset.values( @@ -241,10 +234,7 @@ class CycleViewSet(BaseViewSet): ): serializer = CycleWriteSerializer(data=request.data) if serializer.is_valid(): - serializer.save( - project_id=project_id, - owned_by=request.user, - ) + serializer.save(project_id=project_id, owned_by=request.user) cycle = ( self.get_queryset() .filter(pk=serializer.data["id"]) @@ -288,9 +278,7 @@ class CycleViewSet(BaseViewSet): origin=request.META.get("HTTP_ORIGIN"), ) return Response(cycle, status=status.HTTP_201_CREATED) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: return Response( { @@ -321,9 +309,7 @@ class CycleViewSet(BaseViewSet): if "sort_order" in request_data: # Can only change sort order for a completed cycle`` request_data = { - "sort_order": request_data.get( - "sort_order", cycle.sort_order - ) + "sort_order": request_data.get("sort_order", cycle.sort_order) } else: return Response( @@ -333,9 +319,7 @@ class CycleViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - serializer = CycleWriteSerializer( - cycle, data=request.data, partial=True - ) + serializer = CycleWriteSerializer(cycle, data=request.data, partial=True) if serializer.is_valid(): serializer.save() cycle = queryset.values( @@ -379,16 +363,9 @@ class CycleViewSet(BaseViewSet): return Response(cycle, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def retrieve(self, request, slug, project_id, pk): - queryset = ( - self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk) - ) + queryset = self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk) data = ( self.get_queryset() .filter(pk=pk) @@ -436,8 +413,7 @@ class CycleViewSet(BaseViewSet): if data is None: return Response( - {"error": "Cycle not found"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Cycle not found"}, status=status.HTTP_404_NOT_FOUND ) queryset = queryset.first() @@ -449,16 +425,11 @@ class CycleViewSet(BaseViewSet): user_id=request.user.id, project_id=project_id, ) - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN], creator=True, model=Cycle) def destroy(self, request, slug, project_id, pk): - cycle = Cycle.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + cycle = Cycle.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) if cycle.owned_by_id != request.user.id and not ( ProjectMember.objects.filter( workspace__slug=slug, @@ -474,9 +445,9 @@ class CycleViewSet(BaseViewSet): ) cycle_issues = list( - CycleIssue.objects.filter( - cycle_id=self.kwargs.get("pk") - ).values_list("issue", flat=True) + CycleIssue.objects.filter(cycle_id=self.kwargs.get("pk")).values_list( + "issue", flat=True + ) ) issue_activity.delay( @@ -707,9 +678,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): ) .values("display_name", "assignee_id", "avatar_url") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -738,9 +707,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): { "display_name": item["display_name"], "assignee_id": ( - str(item["assignee_id"]) - if item["assignee_id"] - else None + str(item["assignee_id"]) if item["assignee_id"] else None ), "avatar": item.get("avatar"), "avatar_url": item.get("avatar_url"), @@ -763,9 +730,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): .annotate(label_id=F("labels__id")) .values("label_name", "color", "label_id") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -802,9 +767,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): { "label_name": item["label_name"], "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_estimates": item["total_estimates"], "completed_estimates": item["completed_estimates"], "pending_estimates": item["pending_estimates"], @@ -835,8 +798,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): ), # If `avatar_asset` is None, fall back to using `avatar` field directly When( - assignees__avatar_asset__isnull=True, - then="assignees__avatar", + assignees__avatar_asset__isnull=True, then="assignees__avatar" ), default=Value(None), output_field=models.CharField(), @@ -845,12 +807,8 @@ class TransferCycleIssueEndpoint(BaseAPIView): .values("display_name", "assignee_id", "avatar_url") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -904,12 +862,8 @@ class TransferCycleIssueEndpoint(BaseAPIView): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -939,9 +893,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): { "label_name": item["label_name"], "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"], "completed_issues": item["completed_issues"], "pending_issues": item["pending_issues"], @@ -986,10 +938,7 @@ class TransferCycleIssueEndpoint(BaseAPIView): } current_cycle.save(update_fields=["progress_snapshot"]) - if ( - new_cycle.end_date is not None - and new_cycle.end_date < timezone.now() - ): + if new_cycle.end_date is not None and new_cycle.end_date < timezone.now(): return Response( { "error": "The cycle where the issues are transferred is already completed" @@ -1052,9 +1001,7 @@ class CycleUserPropertiesEndpoint(BaseAPIView): workspace__slug=slug, ) - cycle_properties.filters = request.data.get( - "filters", cycle_properties.filters - ) + cycle_properties.filters = request.data.get("filters", cycle_properties.filters) cycle_properties.display_filters = request.data.get( "display_filters", cycle_properties.display_filters ) @@ -1089,9 +1036,7 @@ class CycleProgressEndpoint(BaseAPIView): workspace__slug=slug, project_id=project_id, ) - .annotate( - value_as_float=Cast("estimate_point__value", FloatField()) - ) + .annotate(value_as_float=Cast("estimate_point__value", FloatField())) .aggregate( backlog_estimate_point=Sum( Case( @@ -1129,9 +1074,7 @@ class CycleProgressEndpoint(BaseAPIView): ) ), total_estimate_points=Sum( - "value_as_float", - default=Value(0), - output_field=FloatField(), + "value_as_float", default=Value(0), output_field=FloatField() ), ) ) @@ -1185,17 +1128,13 @@ class CycleProgressEndpoint(BaseAPIView): return Response( { - "backlog_estimate_points": aggregate_estimates[ - "backlog_estimate_point" - ] + "backlog_estimate_points": aggregate_estimates["backlog_estimate_point"] or 0, "unstarted_estimate_points": aggregate_estimates[ "unstarted_estimate_point" ] or 0, - "started_estimate_points": aggregate_estimates[ - "started_estimate_point" - ] + "started_estimate_points": aggregate_estimates["started_estimate_point"] or 0, "cancelled_estimate_points": aggregate_estimates[ "cancelled_estimate_point" @@ -1205,9 +1144,7 @@ class CycleProgressEndpoint(BaseAPIView): "completed_estimate_points" ] or 0, - "total_estimate_points": aggregate_estimates[ - "total_estimate_points" - ], + "total_estimate_points": aggregate_estimates["total_estimate_points"], "backlog_issues": backlog_issues, "total_issues": total_issues, "completed_issues": completed_issues, @@ -1220,15 +1157,12 @@ class CycleProgressEndpoint(BaseAPIView): class CycleAnalyticsEndpoint(BaseAPIView): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def get(self, request, slug, project_id, cycle_id): analytic_type = request.GET.get("type", "issues") cycle = ( Cycle.objects.filter( - workspace__slug=slug, - project_id=project_id, - id=cycle_id, + workspace__slug=slug, project_id=project_id, id=cycle_id ) .annotate( total_issues=Count( @@ -1294,9 +1228,7 @@ class CycleAnalyticsEndpoint(BaseAPIView): ) .values("display_name", "assignee_id", "avatar_url") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -1333,9 +1265,7 @@ class CycleAnalyticsEndpoint(BaseAPIView): .annotate(label_id=F("labels__id")) .values("label_name", "color", "label_id") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -1402,7 +1332,7 @@ class CycleAnalyticsEndpoint(BaseAPIView): total_issues=Count( "assignee_id", filter=Q(archived_at__isnull=True, is_draft=False), - ), + ) ) .annotate( completed_issues=Count( @@ -1440,8 +1370,7 @@ class CycleAnalyticsEndpoint(BaseAPIView): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "label_id", - filter=Q(archived_at__isnull=True, is_draft=False), + "label_id", filter=Q(archived_at__isnull=True, is_draft=False) ) ) .annotate( diff --git a/apiserver/plane/app/views/cycle/issue.py b/apiserver/plane/app/views/cycle/issue.py index 6066d9045..6e131d428 100644 --- a/apiserver/plane/app/views/cycle/issue.py +++ b/apiserver/plane/app/views/cycle/issue.py @@ -15,17 +15,9 @@ from rest_framework.response import Response # Module imports from .. import BaseViewSet -from plane.app.serializers import ( - CycleIssueSerializer, -) +from plane.app.serializers import CycleIssueSerializer from plane.bgtasks.issue_activities_task import issue_activity -from plane.db.models import ( - Cycle, - CycleIssue, - Issue, - FileAsset, - IssueLink, -) +from plane.db.models import Cycle, CycleIssue, Issue, FileAsset, IssueLink from plane.utils.grouper import ( issue_group_values, issue_on_results, @@ -33,14 +25,10 @@ from plane.utils.grouper import ( ) from plane.utils.issue_filters import issue_filters from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator from plane.app.permissions import allow_permission, ROLE - class CycleIssueViewSet(BaseViewSet): serializer_class = CycleIssueSerializer model = CycleIssue @@ -48,19 +36,14 @@ class CycleIssueViewSet(BaseViewSet): webhook_event = "cycle_issue" bulk = True - filterset_fields = [ - "issue__labels__id", - "issue__assignees__id", - ] + filterset_fields = ["issue__labels__id", "issue__assignees__id"] def get_queryset(self): return self.filter_queryset( super() .get_queryset() .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("issue_id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("issue_id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -82,29 +65,20 @@ class CycleIssueViewSet(BaseViewSet): ) @method_decorator(gzip_page) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def list(self, request, slug, project_id, cycle_id): order_by_param = request.GET.get("order_by", "created_at") filters = issue_filters(request.query_params, "GET") issue_queryset = ( Issue.issue_objects.filter( - issue_cycle__cycle_id=cycle_id, - issue_cycle__deleted_at__isnull=True, + issue_cycle__cycle_id=cycle_id, issue_cycle__deleted_at__isnull=True ) .filter(project_id=project_id) .filter(workspace__slug=slug) .filter(**filters) .select_related("workspace", "project", "state", "parent") .prefetch_related( - "assignees", - "labels", - "issue_module__module", - "issue_cycle__cycle", + "assignees", "labels", "issue_module__module", "issue_cycle__cycle" ) .filter(**filters) .annotate( @@ -130,9 +104,7 @@ class CycleIssueViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -144,8 +116,7 @@ class CycleIssueViewSet(BaseViewSet): issue_queryset = issue_queryset.filter(**filters) # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -154,9 +125,7 @@ class CycleIssueViewSet(BaseViewSet): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) if group_by: @@ -176,9 +145,7 @@ class CycleIssueViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -212,9 +179,7 @@ class CycleIssueViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -250,8 +215,7 @@ class CycleIssueViewSet(BaseViewSet): if not issues: return Response( - {"error": "Issues are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST ) cycle = Cycle.objects.get( @@ -268,13 +232,9 @@ class CycleIssueViewSet(BaseViewSet): # Get all CycleIssues already created cycle_issues = list( - CycleIssue.objects.filter( - ~Q(cycle_id=cycle_id), issue_id__in=issues - ) + CycleIssue.objects.filter(~Q(cycle_id=cycle_id), issue_id__in=issues) ) - existing_issues = [ - str(cycle_issue.issue_id) for cycle_issue in cycle_issues - ] + existing_issues = [str(cycle_issue.issue_id) for cycle_issue in cycle_issues] new_issues = list(set(issues) - set(existing_issues)) # New issues to create @@ -313,9 +273,7 @@ class CycleIssueViewSet(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", diff --git a/apiserver/plane/app/views/dashboard/base.py b/apiserver/plane/app/views/dashboard/base.py index 238cb67a5..8ccce092e 100644 --- a/apiserver/plane/app/views/dashboard/base.py +++ b/apiserver/plane/app/views/dashboard/base.py @@ -227,7 +227,7 @@ def dashboard_assigned_issues(self, request, slug): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -260,10 +260,7 @@ def dashboard_assigned_issues(self, request, slug): ) if WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role=5, - is_active=True, + workspace__slug=slug, member=request.user, role=5, is_active=True ).exists(): assigned_issues = assigned_issues.filter(created_by=request.user) @@ -271,10 +268,7 @@ def dashboard_assigned_issues(self, request, slug): priority_order = ["urgent", "high", "medium", "low", "none"] assigned_issues = assigned_issues.annotate( priority_order=Case( - *[ - When(priority=p, then=Value(i)) - for i, p in enumerate(priority_order) - ], + *[When(priority=p, then=Value(i)) for i, p in enumerate(priority_order)], output_field=CharField(), ) ).order_by("priority_order") @@ -300,9 +294,7 @@ def dashboard_assigned_issues(self, request, slug): completed_issues_count = assigned_issues.filter( state__group__in=["completed"] ).count() - completed_issues = assigned_issues.filter( - state__group__in=["completed"] - )[:5] + completed_issues = assigned_issues.filter(state__group__in=["completed"])[:5] return Response( { "issues": IssueSerializer( @@ -407,7 +399,7 @@ def dashboard_created_issues(self, request, slug): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -444,10 +436,7 @@ def dashboard_created_issues(self, request, slug): priority_order = ["urgent", "high", "medium", "low", "none"] created_issues = created_issues.annotate( priority_order=Case( - *[ - When(priority=p, then=Value(i)) - for i, p in enumerate(priority_order) - ], + *[When(priority=p, then=Value(i)) for i, p in enumerate(priority_order)], output_field=CharField(), ) ).order_by("priority_order") @@ -473,9 +462,7 @@ def dashboard_created_issues(self, request, slug): completed_issues_count = created_issues.filter( state__group__in=["completed"] ).count() - completed_issues = created_issues.filter( - state__group__in=["completed"] - )[:5] + completed_issues = created_issues.filter(state__group__in=["completed"])[:5] return Response( { "issues": IssueSerializer(completed_issues, many=True).data, @@ -530,10 +517,7 @@ def dashboard_issues_by_state_groups(self, request, slug): extra_filters = {} if WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role=5, - is_active=True, + workspace__slug=slug, member=request.user, role=5, is_active=True ).exists(): extra_filters = {"created_by": request.user} @@ -570,10 +554,7 @@ def dashboard_issues_by_priority(self, request, slug): extra_filters = {} if WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role=5, - is_active=True, + workspace__slug=slug, member=request.user, role=5, is_active=True ).exists(): extra_filters = {"created_by": request.user} @@ -598,8 +579,7 @@ def dashboard_issues_by_priority(self, request, slug): # Prepare output including all groups with their counts output_data = [ - {"priority": group, "count": count} - for group, count in all_groups.items() + {"priority": group, "count": count} for group, count in all_groups.items() ] return Response(output_data, status=status.HTTP_200_OK) @@ -616,8 +596,7 @@ def dashboard_recent_activity(self, request, slug): ).select_related("actor", "workspace", "issue", "project")[:8] return Response( - IssueActivitySerializer(queryset, many=True).data, - status=status.HTTP_200_OK, + IssueActivitySerializer(queryset, many=True).data, status=status.HTTP_200_OK ) @@ -647,22 +626,14 @@ def dashboard_recent_projects(self, request, slug): ).exclude(id__in=unique_project_ids) # Append additional project IDs to the existing list - unique_project_ids.update( - additional_projects.values_list("id", flat=True) - ) + unique_project_ids.update(additional_projects.values_list("id", flat=True)) - return Response( - list(unique_project_ids)[:4], - status=status.HTTP_200_OK, - ) + return Response(list(unique_project_ids)[:4], status=status.HTTP_200_OK) def dashboard_recent_collaborators(self, request, slug): project_members_with_activities = ( - WorkspaceMember.objects.filter( - workspace__slug=slug, - is_active=True, - ) + WorkspaceMember.objects.filter(workspace__slug=slug, is_active=True) .annotate( active_issue_count=Count( Case( @@ -687,10 +658,7 @@ def dashboard_recent_collaborators(self, request, slug): .order_by("-active_issue_count") .distinct() ) - return Response( - (project_members_with_activities), - status=status.HTTP_200_OK, - ) + return Response((project_members_with_activities), status=status.HTTP_200_OK) class DashboardEndpoint(BaseAPIView): @@ -739,14 +707,13 @@ class DashboardEndpoint(BaseAPIView): updated_dashboard_widgets = [] for widget_key in widgets_to_fetch: - widget = Widget.objects.filter( - key=widget_key - ).values_list("id", flat=True) + widget = Widget.objects.filter(key=widget_key).values_list( + "id", flat=True + ) if widget: updated_dashboard_widgets.append( DashboardWidget( - widget_id=widget, - dashboard_id=dashboard.id, + widget_id=widget, dashboard_id=dashboard.id ) ) @@ -813,11 +780,7 @@ class DashboardEndpoint(BaseAPIView): func = WIDGETS_MAPPER.get(widget_key) if func is not None: - response = func( - self, - request=request, - slug=slug, - ) + response = func(self, request=request, slug=slug) if isinstance(response, Response): return response @@ -830,8 +793,7 @@ class DashboardEndpoint(BaseAPIView): class WidgetsEndpoint(BaseAPIView): def patch(self, request, dashboard_id, widget_id): dashboard_widget = DashboardWidget.objects.filter( - widget_id=widget_id, - dashboard_id=dashboard_id, + widget_id=widget_id, dashboard_id=dashboard_id ).first() dashboard_widget.is_visible = request.data.get( "is_visible", dashboard_widget.is_visible @@ -839,10 +801,6 @@ class WidgetsEndpoint(BaseAPIView): dashboard_widget.sort_order = request.data.get( "sort_order", dashboard_widget.sort_order ) - dashboard_widget.filters = request.data.get( - "filters", dashboard_widget.filters - ) + dashboard_widget.filters = request.data.get("filters", dashboard_widget.filters) dashboard_widget.save() - return Response( - {"message": "successfully updated"}, status=status.HTTP_200_OK - ) + return Response({"message": "successfully updated"}, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/error_404.py b/apiserver/plane/app/views/error_404.py index 3c31474e0..97c3c59f7 100644 --- a/apiserver/plane/app/views/error_404.py +++ b/apiserver/plane/app/views/error_404.py @@ -1,5 +1,6 @@ # views.py from django.http import JsonResponse + def custom_404_view(request, exception=None): return JsonResponse({"error": "Page not found."}, status=404) diff --git a/apiserver/plane/app/views/estimate/base.py b/apiserver/plane/app/views/estimate/base.py index 80943c05e..c0e931ca6 100644 --- a/apiserver/plane/app/views/estimate/base.py +++ b/apiserver/plane/app/views/estimate/base.py @@ -11,11 +11,7 @@ from rest_framework import status # Module imports from ..base import BaseViewSet, BaseAPIView -from plane.app.permissions import ( - ProjectEntityPermission, - allow_permission, - ROLE, -) +from plane.app.permissions import ProjectEntityPermission, allow_permission, ROLE from plane.db.models import Project, Estimate, EstimatePoint, Issue from plane.app.serializers import ( EstimateSerializer, @@ -32,13 +28,7 @@ def generate_random_name(length=10): class ProjectEstimatePointEndpoint(BaseAPIView): - - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def get(self, request, slug, project_id): project = Project.objects.get(workspace__slug=slug, pk=project_id) if project.estimate_id is not None: @@ -53,17 +43,13 @@ class ProjectEstimatePointEndpoint(BaseAPIView): class BulkEstimatePointEndpoint(BaseViewSet): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] model = Estimate serializer_class = EstimateSerializer def list(self, request, slug, project_id): estimates = ( - Estimate.objects.filter( - workspace__slug=slug, project_id=project_id - ) + Estimate.objects.filter(workspace__slug=slug, project_id=project_id) .prefetch_related("points") .select_related("workspace", "project") ) @@ -91,9 +77,7 @@ class BulkEstimatePointEndpoint(BaseViewSet): data=request.data.get("estimate_points"), many=True ) if not serializer.is_valid(): - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) estimate_points = EstimatePoint.objects.bulk_create( [ @@ -121,16 +105,12 @@ class BulkEstimatePointEndpoint(BaseViewSet): pk=estimate_id, workspace__slug=slug, project_id=project_id ) serializer = EstimateReadSerializer(estimate) - return Response( - serializer.data, - status=status.HTTP_200_OK, - ) + return Response(serializer.data, status=status.HTTP_200_OK) @invalidate_cache( path="/api/workspaces/:slug/estimates/", url_params=True, user=False ) def partial_update(self, request, slug, project_id, estimate_id): - if not len(request.data.get("estimate_points", [])): return Response( {"error": "Estimate points are required"}, @@ -140,20 +120,15 @@ class BulkEstimatePointEndpoint(BaseViewSet): estimate = Estimate.objects.get(pk=estimate_id) if request.data.get("estimate"): - estimate.name = request.data.get("estimate").get( - "name", estimate.name - ) - estimate.type = request.data.get("estimate").get( - "type", estimate.type - ) + estimate.name = request.data.get("estimate").get("name", estimate.name) + estimate.type = request.data.get("estimate").get("type", estimate.type) estimate.save() estimate_points_data = request.data.get("estimate_points", []) estimate_points = EstimatePoint.objects.filter( pk__in=[ - estimate_point.get("id") - for estimate_point in estimate_points_data + estimate_point.get("id") for estimate_point in estimate_points_data ], workspace__slug=slug, project_id=project_id, @@ -178,16 +153,11 @@ class BulkEstimatePointEndpoint(BaseViewSet): updated_estimate_points.append(estimate_point) EstimatePoint.objects.bulk_update( - updated_estimate_points, - ["key", "value"], - batch_size=10, + updated_estimate_points, ["key", "value"], batch_size=10 ) estimate_serializer = EstimateReadSerializer(estimate) - return Response( - estimate_serializer.data, - status=status.HTTP_200_OK, - ) + return Response(estimate_serializer.data, status=status.HTTP_200_OK) @invalidate_cache( path="/api/workspaces/:slug/estimates/", url_params=True, user=False @@ -201,7 +171,6 @@ class BulkEstimatePointEndpoint(BaseViewSet): class EstimatePointEndpoint(BaseViewSet): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def create(self, request, slug, project_id, estimate_id): # TODO: add a key validation if the same key already exists @@ -213,18 +182,13 @@ class EstimatePointEndpoint(BaseViewSet): key = request.data.get("key", 0) value = request.data.get("value", "") estimate_point = EstimatePoint.objects.create( - estimate_id=estimate_id, - project_id=project_id, - key=key, - value=value, + estimate_id=estimate_id, project_id=project_id, key=key, value=value ) serializer = EstimatePointSerializer(estimate_point).data return Response(serializer, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) - def partial_update( - self, request, slug, project_id, estimate_id, estimate_point_id - ): + def partial_update(self, request, slug, project_id, estimate_id, estimate_point_id): # TODO: add a key validation if the same key already exists estimate_point = EstimatePoint.objects.get( pk=estimate_point_id, @@ -236,21 +200,15 @@ class EstimatePointEndpoint(BaseViewSet): estimate_point, data=request.data, partial=True ) if not serializer.is_valid(): - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) - def destroy( - self, request, slug, project_id, estimate_id, estimate_point_id - ): + def destroy(self, request, slug, project_id, estimate_id, estimate_point_id): new_estimate_id = request.data.get("new_estimate_id", None) estimate_points = EstimatePoint.objects.filter( - estimate_id=estimate_id, - project_id=project_id, - workspace__slug=slug, + estimate_id=estimate_id, project_id=project_id, workspace__slug=slug ) # update all the issues with the new estimate if new_estimate_id: @@ -265,10 +223,8 @@ class EstimatePointEndpoint(BaseViewSet): requested_data=json.dumps( { "estimate_point": ( - str(new_estimate_id) - if new_estimate_id - else None - ), + str(new_estimate_id) if new_estimate_id else None + ) } ), actor_id=str(request.user.id), @@ -280,7 +236,7 @@ class EstimatePointEndpoint(BaseViewSet): str(issue.estimate_point_id) if issue.estimate_point_id else None - ), + ) } ), epoch=int(timezone.now().timestamp()), @@ -295,11 +251,7 @@ class EstimatePointEndpoint(BaseViewSet): for issue in issues: issue_activity.delay( type="issue.activity.updated", - requested_data=json.dumps( - { - "estimate_point": None, - } - ), + requested_data=json.dumps({"estimate_point": None}), actor_id=str(request.user.id), issue_id=issue.id, project_id=str(project_id), @@ -309,16 +261,14 @@ class EstimatePointEndpoint(BaseViewSet): str(issue.estimate_point_id) if issue.estimate_point_id else None - ), + ) } ), epoch=int(timezone.now().timestamp()), ) # delete the estimate point - old_estimate_point = EstimatePoint.objects.filter( - pk=estimate_point_id - ).first() + old_estimate_point = EstimatePoint.objects.filter(pk=estimate_point_id).first() # rearrange the estimate points updated_estimate_points = [] @@ -328,9 +278,7 @@ class EstimatePointEndpoint(BaseViewSet): updated_estimate_points.append(estimate_point) EstimatePoint.objects.bulk_update( - updated_estimate_points, - ["key"], - batch_size=10, + updated_estimate_points, ["key"], batch_size=10 ) old_estimate_point.delete() diff --git a/apiserver/plane/app/views/exporter/base.py b/apiserver/plane/app/views/exporter/base.py index 39255bd1e..8e683e56d 100644 --- a/apiserver/plane/app/views/exporter/base.py +++ b/apiserver/plane/app/views/exporter/base.py @@ -51,9 +51,7 @@ class ExportIssuesEndpoint(BaseAPIView): slug=slug, ) return Response( - { - "message": "Once the export is ready you will be able to download it" - }, + {"message": "Once the export is ready you will be able to download it"}, status=status.HTTP_200_OK, ) else: @@ -62,18 +60,13 @@ class ExportIssuesEndpoint(BaseAPIView): status=status.HTTP_400_BAD_REQUEST, ) - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def get(self, request, slug): exporter_history = ExporterHistory.objects.filter( - workspace__slug=slug, - type="issue_exports", + workspace__slug=slug, type="issue_exports" ).select_related("workspace", "initiated_by") - if request.GET.get("per_page", False) and request.GET.get( - "cursor", False - ): + if request.GET.get("per_page", False) and request.GET.get("cursor", False): return self.paginate( order_by=request.GET.get("order_by", "-created_at"), request=request, diff --git a/apiserver/plane/app/views/external/base.py b/apiserver/plane/app/views/external/base.py index 6ae3f37ba..1dfbc421a 100644 --- a/apiserver/plane/app/views/external/base.py +++ b/apiserver/plane/app/views/external/base.py @@ -13,15 +13,11 @@ from rest_framework import status from ..base import BaseAPIView from plane.app.permissions import allow_permission, ROLE from plane.db.models import Workspace, Project -from plane.app.serializers import ( - ProjectLiteSerializer, - WorkspaceLiteSerializer, -) +from plane.app.serializers import ProjectLiteSerializer, WorkspaceLiteSerializer from plane.license.utils.instance_value import get_configuration_value class GPTIntegrationEndpoint(BaseAPIView): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def post(self, request, slug, project_id): OPENAI_API_KEY, GPT_ENGINE = get_configuration_value( @@ -50,19 +46,15 @@ class GPTIntegrationEndpoint(BaseAPIView): if not task: return Response( - {"error": "Task is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Task is required"}, status=status.HTTP_400_BAD_REQUEST ) final_text = task + "\n" + prompt - client = OpenAI( - api_key=OPENAI_API_KEY, - ) + client = OpenAI(api_key=OPENAI_API_KEY) response = client.chat.completions.create( - model=GPT_ENGINE, - messages=[{"role": "user", "content": final_text}], + model=GPT_ENGINE, messages=[{"role": "user", "content": final_text}] ) workspace = Workspace.objects.get(slug=slug) @@ -82,10 +74,7 @@ class GPTIntegrationEndpoint(BaseAPIView): class WorkspaceGPTIntegrationEndpoint(BaseAPIView): - - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def post(self, request, slug): OPENAI_API_KEY, GPT_ENGINE = get_configuration_value( [ @@ -113,29 +102,21 @@ class WorkspaceGPTIntegrationEndpoint(BaseAPIView): if not task: return Response( - {"error": "Task is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Task is required"}, status=status.HTTP_400_BAD_REQUEST ) final_text = task + "\n" + prompt - client = OpenAI( - api_key=OPENAI_API_KEY, - ) + client = OpenAI(api_key=OPENAI_API_KEY) response = client.chat.completions.create( - model=GPT_ENGINE, - messages=[{"role": "user", "content": final_text}], + model=GPT_ENGINE, messages=[{"role": "user", "content": final_text}] ) text = response.choices[0].message.content.strip() text_html = text.replace("\n", "
") return Response( - { - "response": text, - "response_html": text_html, - }, - status=status.HTTP_200_OK, + {"response": text, "response_html": text_html}, status=status.HTTP_200_OK ) @@ -164,9 +145,7 @@ class UnsplashEndpoint(BaseAPIView): else f"https://api.unsplash.com/photos/?client_id={UNSPLASH_ACCESS_KEY}&page={page}&per_page={per_page}" ) - headers = { - "Content-Type": "application/json", - } + headers = {"Content-Type": "application/json"} resp = requests.get(url=url, headers=headers) return Response(resp.json(), status=resp.status_code) diff --git a/apiserver/plane/app/views/intake/base.py b/apiserver/plane/app/views/intake/base.py index 9e420c007..8647117c5 100644 --- a/apiserver/plane/app/views/intake/base.py +++ b/apiserver/plane/app/views/intake/base.py @@ -40,7 +40,6 @@ from plane.bgtasks.issue_activities_task import issue_activity class IntakeViewSet(BaseViewSet): - serializer_class = IntakeSerializer model = Intake @@ -54,8 +53,7 @@ class IntakeViewSet(BaseViewSet): ) .annotate( pending_issue_count=Count( - "issue_intake", - filter=Q(issue_intake__status=-2), + "issue_intake", filter=Q(issue_intake__status=-2) ) ) .select_related("workspace", "project") @@ -64,10 +62,7 @@ class IntakeViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def list(self, request, slug, project_id): intake = self.get_queryset().first() - return Response( - IntakeSerializer(intake).data, - status=status.HTTP_200_OK, - ) + return Response(IntakeSerializer(intake).data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def perform_create(self, serializer): @@ -89,13 +84,10 @@ class IntakeViewSet(BaseViewSet): class IntakeIssueViewSet(BaseViewSet): - serializer_class = IntakeIssueSerializer model = IntakeIssue - filterset_fields = [ - "statulls", - ] + filterset_fields = ["statulls"] def get_queryset(self): return ( @@ -136,9 +128,7 @@ class IntakeIssueViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -150,7 +140,7 @@ class IntakeIssueViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -194,9 +184,7 @@ class IntakeIssueViewSet(BaseViewSet): intake_id=intake_id.id, project_id=project_id, **filters ) .select_related("issue") - .prefetch_related( - "issue__labels", - ) + .prefetch_related("issue__labels") .annotate( label_ids=Coalesce( ArrayAgg( @@ -235,8 +223,7 @@ class IntakeIssueViewSet(BaseViewSet): request=request, queryset=(intake_issue), on_results=lambda intake_issues: IntakeIssueSerializer( - intake_issues, - many=True, + intake_issues, many=True ).data, ) @@ -244,8 +231,7 @@ class IntakeIssueViewSet(BaseViewSet): def create(self, request, slug, project_id): if not request.data.get("issue", {}).get("name", False): return Response( - {"error": "Name is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST ) # Check for valid priority @@ -257,8 +243,7 @@ class IntakeIssueViewSet(BaseViewSet): "none", ]: return Response( - {"error": "Invalid priority"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Invalid priority"}, status=status.HTTP_400_BAD_REQUEST ) # create an issue @@ -298,10 +283,7 @@ class IntakeIssueViewSet(BaseViewSet): ) intake_issue = ( IntakeIssue.objects.select_related("issue") - .prefetch_related( - "issue__labels", - "issue__assignees", - ) + .prefetch_related("issue__labels", "issue__assignees") .annotate( label_ids=Coalesce( ArrayAgg( @@ -309,9 +291,7 @@ class IntakeIssueViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(issue__labels__id__isnull=True) - & Q( - issue__label_issue__deleted_at__isnull=True - ) + & Q(issue__label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -321,9 +301,7 @@ class IntakeIssueViewSet(BaseViewSet): "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())), ), @@ -337,9 +315,7 @@ class IntakeIssueViewSet(BaseViewSet): serializer = IntakeIssueDetailSerializer(intake_issue) return Response(serializer.data, status=status.HTTP_200_OK) else: - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=Issue) def partial_update(self, request, slug, project_id, pk): @@ -394,11 +370,7 @@ class IntakeIssueViewSet(BaseViewSet): ), Value([], output_field=ArrayField(UUIDField())), ), - ).get( - pk=intake_issue.issue_id, - workspace__slug=slug, - project_id=project_id, - ) + ).get(pk=intake_issue.issue_id, workspace__slug=slug, project_id=project_id) # Only allow guests to edit name and description if project_member.role <= 5: issue_data = { @@ -406,9 +378,7 @@ class IntakeIssueViewSet(BaseViewSet): "description_html": issue_data.get( "description_html", issue.description_html ), - "description": issue_data.get( - "description", issue.description - ), + "description": issue_data.get("description", issue.description), } issue_serializer = IssueCreateSerializer( @@ -459,9 +429,7 @@ class IntakeIssueViewSet(BaseViewSet): project_id=project_id, ) state = State.objects.filter( - group="cancelled", - workspace__slug=slug, - project_id=project_id, + group="cancelled", workspace__slug=slug, project_id=project_id ).first() if state is not None: issue.state = state @@ -479,9 +447,7 @@ class IntakeIssueViewSet(BaseViewSet): if issue.state.is_triage: # Move to default state state = State.objects.filter( - workspace__slug=slug, - project_id=project_id, - default=True, + workspace__slug=slug, project_id=project_id, default=True ).first() if state is not None: issue.state = state @@ -489,9 +455,7 @@ class IntakeIssueViewSet(BaseViewSet): # create a activity for status change issue_activity.delay( type="intake.activity.created", - requested_data=json.dumps( - request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(request.data, cls=DjangoJSONEncoder), actor_id=str(request.user.id), issue_id=str(pk), project_id=str(project_id), @@ -504,10 +468,7 @@ class IntakeIssueViewSet(BaseViewSet): intake_issue = ( IntakeIssue.objects.select_related("issue") - .prefetch_related( - "issue__labels", - "issue__assignees", - ) + .prefetch_related("issue__labels", "issue__assignees") .annotate( label_ids=Coalesce( ArrayAgg( @@ -515,9 +476,7 @@ class IntakeIssueViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(issue__labels__id__isnull=True) - & Q( - issue__label_issue__deleted_at__isnull=True - ) + & Q(issue__label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -528,37 +487,23 @@ class IntakeIssueViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(issue__assignees__id__isnull=True) - & Q( - issue__issue_assignee__deleted_at__isnull=True - ) + & Q(issue__issue_assignee__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), ), ) - .get( - intake_id=intake_id.id, - issue_id=pk, - project_id=project_id, - ) + .get(intake_id=intake_id.id, issue_id=pk, project_id=project_id) ) serializer = IntakeIssueDetailSerializer(intake_issue).data return Response(serializer, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: serializer = IntakeIssueDetailSerializer(intake_issue).data return Response(serializer, status=status.HTTP_200_OK) @allow_permission( - allowed_roles=[ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ], - creator=True, - model=Issue, + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], creator=True, model=Issue ) def retrieve(self, request, slug, project_id, pk): intake_id = Intake.objects.filter( @@ -567,10 +512,7 @@ class IntakeIssueViewSet(BaseViewSet): project = Project.objects.get(pk=project_id) intake_issue = ( IntakeIssue.objects.select_related("issue") - .prefetch_related( - "issue__labels", - "issue__assignees", - ) + .prefetch_related("issue__labels", "issue__assignees") .annotate( label_ids=Coalesce( ArrayAgg( @@ -613,10 +555,7 @@ class IntakeIssueViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) issue = IntakeIssueDetailSerializer(intake_issue).data - return Response( - issue, - status=status.HTTP_200_OK, - ) + return Response(issue, status=status.HTTP_200_OK) @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=Issue) def destroy(self, request, slug, project_id, pk): diff --git a/apiserver/plane/app/views/issue/activity.py b/apiserver/plane/app/views/issue/activity.py index f46cb87f3..1b6629e47 100644 --- a/apiserver/plane/app/views/issue/activity.py +++ b/apiserver/plane/app/views/issue/activity.py @@ -2,10 +2,7 @@ from itertools import chain # Django imports -from django.db.models import ( - Prefetch, - Q, -) +from django.db.models import Prefetch, Q from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page @@ -15,35 +12,16 @@ from rest_framework import status # Module imports from .. import BaseAPIView -from plane.app.serializers import ( - IssueActivitySerializer, - IssueCommentSerializer, -) -from plane.app.permissions import ( - ProjectEntityPermission, - allow_permission, - ROLE, -) -from plane.db.models import ( - IssueActivity, - IssueComment, - CommentReaction, -) +from plane.app.serializers import IssueActivitySerializer, IssueCommentSerializer +from plane.app.permissions import ProjectEntityPermission, allow_permission, ROLE +from plane.db.models import IssueActivity, IssueComment, CommentReaction class IssueActivityEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] @method_decorator(gzip_page) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def get(self, request, slug, project_id, issue_id): filters = {} if request.GET.get("created_at__gt", None) is not None: @@ -79,9 +57,7 @@ class IssueActivityEndpoint(BaseAPIView): ) ) ) - issue_activities = IssueActivitySerializer( - issue_activities, many=True - ).data + issue_activities = IssueActivitySerializer(issue_activities, many=True).data issue_comments = IssueCommentSerializer(issue_comments, many=True).data if request.GET.get("activity_type", None) == "issue-property": diff --git a/apiserver/plane/app/views/issue/archive.py b/apiserver/plane/app/views/issue/archive.py index 292d9d617..4f1e357da 100644 --- a/apiserver/plane/app/views/issue/archive.py +++ b/apiserver/plane/app/views/issue/archive.py @@ -12,9 +12,7 @@ from django.views.decorators.gzip import gzip_page from rest_framework import status from rest_framework.response import Response -from plane.app.permissions import ( - ProjectEntityPermission, -) +from plane.app.permissions import ProjectEntityPermission from plane.app.serializers import ( IssueFlatSerializer, IssueSerializer, @@ -27,7 +25,7 @@ from plane.db.models import ( IssueLink, IssueSubscriber, IssueReaction, - CycleIssue + CycleIssue, ) from plane.utils.grouper import ( issue_group_values, @@ -36,10 +34,7 @@ from plane.utils.grouper import ( ) from plane.utils.issue_filters import issue_filters from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator from plane.app.permissions import allow_permission, ROLE from plane.utils.error_codes import ERROR_CODES @@ -88,9 +83,7 @@ class IssueArchiveViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -98,12 +91,7 @@ class IssueArchiveViewSet(BaseViewSet): ) @method_decorator(gzip_page) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def list(self, request, slug, project_id): filters = issue_filters(request.query_params, "GET") show_sub_issues = request.GET.get("show_sub_issues", "true") @@ -119,8 +107,7 @@ class IssueArchiveViewSet(BaseViewSet): ) # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -129,9 +116,7 @@ class IssueArchiveViewSet(BaseViewSet): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) if group_by: @@ -151,9 +136,7 @@ class IssueArchiveViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -187,9 +170,7 @@ class IssueArchiveViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -219,12 +200,7 @@ class IssueArchiveViewSet(BaseViewSet): ), ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def retrieve(self, request, slug, project_id, pk=None): issue = ( self.get_queryset() @@ -232,9 +208,7 @@ class IssueArchiveViewSet(BaseViewSet): .prefetch_related( Prefetch( "issue_reactions", - queryset=IssueReaction.objects.select_related( - "issue", "actor" - ), + queryset=IssueReaction.objects.select_related("issue", "actor"), ) ) .prefetch_related( @@ -265,24 +239,17 @@ class IssueArchiveViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def archive(self, request, slug, project_id, pk=None): issue = Issue.issue_objects.get( - workspace__slug=slug, - project_id=project_id, - pk=pk, + workspace__slug=slug, project_id=project_id, pk=pk ) if issue.state.group not in ["completed", "cancelled"]: return Response( - { - "error": "Can only archive completed or cancelled state group issue" - }, + {"error": "Can only archive completed or cancelled state group issue"}, status=status.HTTP_400_BAD_REQUEST, ) issue_activity.delay( type="issue.activity.updated", requested_data=json.dumps( - { - "archived_at": str(timezone.now().date()), - "automation": False, - } + {"archived_at": str(timezone.now().date()), "automation": False} ), actor_id=str(request.user.id), issue_id=str(issue.id), @@ -329,9 +296,7 @@ class IssueArchiveViewSet(BaseViewSet): class BulkArchiveIssuesEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def post(self, request, slug, project_id): @@ -339,8 +304,7 @@ class BulkArchiveIssuesEndpoint(BaseAPIView): if not len(issue_ids): return Response( - {"error": "Issue IDs are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issue IDs are required"}, status=status.HTTP_400_BAD_REQUEST ) issues = Issue.objects.filter( @@ -351,9 +315,7 @@ class BulkArchiveIssuesEndpoint(BaseAPIView): if issue.state.group not in ["completed", "cancelled"]: return Response( { - "error_code": ERROR_CODES[ - "INVALID_ARCHIVE_STATE_GROUP" - ], + "error_code": ERROR_CODES["INVALID_ARCHIVE_STATE_GROUP"], "error_message": "INVALID_ARCHIVE_STATE_GROUP", }, status=status.HTTP_400_BAD_REQUEST, @@ -361,10 +323,7 @@ class BulkArchiveIssuesEndpoint(BaseAPIView): issue_activity.delay( type="issue.activity.updated", requested_data=json.dumps( - { - "archived_at": str(timezone.now().date()), - "automation": False, - } + {"archived_at": str(timezone.now().date()), "automation": False} ), actor_id=str(request.user.id), issue_id=str(issue.id), @@ -381,6 +340,5 @@ class BulkArchiveIssuesEndpoint(BaseAPIView): Issue.objects.bulk_update(bulk_archive_issues, ["archived_at"]) return Response( - {"archived_at": str(timezone.now().date())}, - status=status.HTTP_200_OK, + {"archived_at": str(timezone.now().date())}, status=status.HTTP_200_OK ) diff --git a/apiserver/plane/app/views/issue/attachment.py b/apiserver/plane/app/views/issue/attachment.py index 48b80c6cb..4427227f1 100644 --- a/apiserver/plane/app/views/issue/attachment.py +++ b/apiserver/plane/app/views/issue/attachment.py @@ -45,10 +45,7 @@ class IssueAttachmentEndpoint(BaseAPIView): actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(self.kwargs.get("project_id", None)), - current_instance=json.dumps( - serializer.data, - cls=DjangoJSONEncoder, - ), + current_instance=json.dumps(serializer.data, cls=DjangoJSONEncoder), epoch=int(timezone.now().timestamp()), notification=True, origin=request.META.get("HTTP_ORIGIN"), @@ -75,13 +72,7 @@ class IssueAttachmentEndpoint(BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def get(self, request, slug, project_id, issue_id): issue_attachments = FileAsset.objects.filter( issue_id=issue_id, workspace__slug=slug, project_id=project_id @@ -91,7 +82,6 @@ class IssueAttachmentEndpoint(BaseAPIView): class IssueAttachmentV2Endpoint(BaseAPIView): - serializer_class = IssueAttachmentSerializer model = FileAsset @@ -103,10 +93,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): if not type or type not in settings.ATTACHMENT_MIME_TYPES: return Response( - { - "error": "Invalid file type.", - "status": False, - }, + {"error": "Invalid file type.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) @@ -121,11 +108,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): # Create a File Asset asset = FileAsset.objects.create( - attributes={ - "name": name, - "type": type, - "size": size_limit, - }, + attributes={"name": name, "type": type, "size": size_limit}, asset=asset_key, size=size_limit, workspace_id=workspace.id, @@ -139,9 +122,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object presigned_url = storage.generate_presigned_post( - object_name=asset_key, - file_type=type, - file_size=size_limit, + object_name=asset_key, file_type=type, file_size=size_limit ) # Return the presigned URL return Response( @@ -177,13 +158,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): return Response(status=status.HTTP_204_NO_CONTENT) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def get(self, request, slug, project_id, issue_id, pk=None): if pk: # Get the asset @@ -194,10 +169,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): # Check if the asset is uploaded if not asset.is_uploaded: return Response( - { - "error": "The asset is not uploaded.", - "status": False, - }, + {"error": "The asset is not uploaded.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) @@ -221,13 +193,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): serializer = IssueAttachmentSerializer(issue_attachments, many=True) return Response(serializer.data, status=status.HTTP_200_OK) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def patch(self, request, slug, project_id, issue_id, pk): issue_attachment = FileAsset.objects.get( pk=pk, workspace__slug=slug, project_id=project_id @@ -242,10 +208,7 @@ class IssueAttachmentV2Endpoint(BaseAPIView): actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(self.kwargs.get("project_id", None)), - current_instance=json.dumps( - serializer.data, - cls=DjangoJSONEncoder, - ), + current_instance=json.dumps(serializer.data, cls=DjangoJSONEncoder), epoch=int(timezone.now().timestamp()), notification=True, origin=request.META.get("HTTP_ORIGIN"), diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index c30f889ba..3d548aeac 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -54,10 +54,7 @@ from plane.utils.grouper import ( ) from plane.utils.issue_filters import issue_filters from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator from .. import BaseAPIView, BaseViewSet from plane.utils.user_timezone_converter import user_timezone_converter from plane.bgtasks.recent_visited_task import recent_visited_task @@ -72,13 +69,10 @@ class IssueListEndpoint(BaseAPIView): if not issue_ids: return Response( - {"error": "Issues are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issues are required"}, 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 = ( Issue.issue_objects.filter( @@ -110,9 +104,7 @@ class IssueListEndpoint(BaseAPIView): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -125,8 +117,7 @@ class IssueListEndpoint(BaseAPIView): issue_queryset = queryset.filter(**filters) # Issue queryset issue_queryset, _ = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -135,9 +126,7 @@ class IssueListEndpoint(BaseAPIView): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) recent_visited_task.delay( @@ -199,21 +188,13 @@ class IssueViewSet(BaseViewSet): model = Issue webhook_event = "issue" - search_fields = [ - "name", - ] + search_fields = ["name"] - filterset_fields = [ - "state__name", - "assignees__id", - "workspace__id", - ] + filterset_fields = ["state__name", "assignees__id", "workspace__id"] def get_queryset(self): return ( - Issue.issue_objects.filter( - project_id=self.kwargs.get("project_id") - ) + Issue.issue_objects.filter(project_id=self.kwargs.get("project_id")) .filter(workspace__slug=self.kwargs.get("slug")) .select_related("workspace", "project", "state", "parent") .prefetch_related("assignees", "labels", "issue_module__module") @@ -240,9 +221,7 @@ class IssueViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -254,9 +233,7 @@ class IssueViewSet(BaseViewSet): def list(self, request, slug, project_id): extra_filters = {} if request.GET.get("updated_at__gt", None) is not None: - extra_filters = { - "updated_at__gt": request.GET.get("updated_at__gt") - } + extra_filters = {"updated_at__gt": request.GET.get("updated_at__gt")} project = Project.objects.get(pk=project_id, workspace__slug=slug) filters = issue_filters(request.query_params, "GET") @@ -267,8 +244,7 @@ class IssueViewSet(BaseViewSet): # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -277,9 +253,7 @@ class IssueViewSet(BaseViewSet): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) recent_visited_task.delay( @@ -316,9 +290,7 @@ class IssueViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -351,9 +323,7 @@ class IssueViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -401,9 +371,7 @@ class IssueViewSet(BaseViewSet): # Track the issue issue_activity.delay( type="issue.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(request.user.id), issue_id=str(serializer.data.get("id", None)), project_id=str(project_id), @@ -414,9 +382,7 @@ class IssueViewSet(BaseViewSet): ) issue = ( issue_queryset_grouper( - queryset=self.get_queryset().filter( - pk=serializer.data["id"] - ), + queryset=self.get_queryset().filter(pk=serializer.data["id"]), group_by=None, sub_group_by=None, ) @@ -468,21 +434,13 @@ class IssueViewSet(BaseViewSet): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @allow_permission( - allowed_roles=[ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ], - creator=True, - model=Issue, + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], creator=True, model=Issue ) def retrieve(self, request, slug, project_id, pk=None): project = Project.objects.get(pk=project_id, workspace__slug=slug) issue = ( - Issue.objects.filter( - project_id=self.kwargs.get("project_id") - ) + Issue.objects.filter(project_id=self.kwargs.get("project_id")) .filter(workspace__slug=self.kwargs.get("slug")) .select_related("workspace", "project", "state", "parent") .prefetch_related("assignees", "labels", "issue_module__module") @@ -511,13 +469,11 @@ class IssueViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") - ) + ) .filter(pk=pk) .annotate( label_ids=Coalesce( @@ -526,7 +482,7 @@ class IssueViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -559,9 +515,7 @@ class IssueViewSet(BaseViewSet): .prefetch_related( Prefetch( "issue_reactions", - queryset=IssueReaction.objects.select_related( - "issue", "actor" - ), + queryset=IssueReaction.objects.select_related("issue", "actor"), ) ) .prefetch_related( @@ -632,7 +586,7 @@ class IssueViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -668,8 +622,7 @@ class IssueViewSet(BaseViewSet): if not issue: return Response( - {"error": "Issue not found"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Issue not found"}, status=status.HTTP_404_NOT_FOUND ) current_instance = json.dumps( @@ -677,9 +630,7 @@ class IssueViewSet(BaseViewSet): ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) - serializer = IssueCreateSerializer( - issue, data=request.data, partial=True - ) + serializer = IssueCreateSerializer(issue, data=request.data, partial=True) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -707,9 +658,7 @@ class IssueViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN], creator=True, model=Issue) def destroy(self, request, slug, project_id, pk=None): - issue = Issue.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + issue = Issue.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) issue.delete() issue_activity.delay( @@ -730,13 +679,10 @@ class IssueUserDisplayPropertyEndpoint(BaseAPIView): @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def patch(self, request, slug, project_id): issue_property = IssueUserProperty.objects.get( - user=request.user, - project_id=project_id, + user=request.user, project_id=project_id ) - issue_property.filters = request.data.get( - "filters", issue_property.filters - ) + issue_property.filters = request.data.get("filters", issue_property.filters) issue_property.display_filters = request.data.get( "display_filters", issue_property.display_filters ) @@ -747,13 +693,7 @@ class IssueUserDisplayPropertyEndpoint(BaseAPIView): serializer = IssueUserPropertySerializer(issue_property) return Response(serializer.data, status=status.HTTP_201_CREATED) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def get(self, request, slug, project_id): issue_property, _ = IssueUserProperty.objects.get_or_create( user=request.user, project_id=project_id @@ -769,8 +709,7 @@ class BulkDeleteIssuesEndpoint(BaseAPIView): if not len(issue_ids): return Response( - {"error": "Issue IDs are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issue IDs are required"}, status=status.HTTP_400_BAD_REQUEST ) issues = Issue.issue_objects.filter( @@ -794,10 +733,7 @@ class DeletedIssuesListViewSet(BaseAPIView): if request.GET.get("updated_at__gt", None) is not None: filters = {"updated_at__gt": request.GET.get("updated_at__gt")} deleted_issues = ( - Issue.all_objects.filter( - workspace__slug=slug, - project_id=project_id, - ) + Issue.all_objects.filter(workspace__slug=slug, project_id=project_id) .filter(Q(archived_at__isnull=False) | Q(deleted_at__isnull=False)) .filter(**filters) .values_list("id", flat=True) @@ -816,9 +752,7 @@ class IssuePaginatedViewSet(BaseViewSet): ) return ( - issue_queryset.select_related( - "workspace", "project", "state", "parent" - ) + issue_queryset.select_related("workspace", "project", "state", "parent") .prefetch_related("assignees", "labels", "issue_module__module") .annotate( cycle_id=Subquery( @@ -843,9 +777,7 @@ class IssuePaginatedViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -935,7 +867,7 @@ class IssuePaginatedViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -983,9 +915,7 @@ class IssueDetailEndpoint(BaseAPIView): def get(self, request, slug, project_id): filters = issue_filters(request.query_params, "GET") issue = ( - Issue.issue_objects.filter( - workspace__slug=slug, project_id=project_id - ) + Issue.issue_objects.filter(workspace__slug=slug, project_id=project_id) .select_related("workspace", "project", "state", "parent") .prefetch_related("assignees", "labels", "issue_module__module") .annotate( @@ -1002,7 +932,7 @@ class IssueDetailEndpoint(BaseAPIView): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -1048,9 +978,7 @@ class IssueDetailEndpoint(BaseAPIView): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -1060,27 +988,20 @@ class IssueDetailEndpoint(BaseAPIView): order_by_param = request.GET.get("order_by", "-created_at") # Issue queryset issue, order_by_param = order_issue_queryset( - issue_queryset=issue, - order_by_param=order_by_param, + issue_queryset=issue, order_by_param=order_by_param ) return self.paginate( request=request, order_by=order_by_param, queryset=(issue), on_results=lambda issue: IssueSerializer( - issue, - many=True, - fields=self.fields, - expand=self.expand, + issue, many=True, fields=self.fields, expand=self.expand ).data, ) class IssueBulkUpdateDateEndpoint(BaseAPIView): - - def validate_dates( - self, current_start, current_target, new_start, new_target - ): + def validate_dates(self, current_start, current_target, new_start, new_target): """ Validate that start date is before target date. """ @@ -1093,7 +1014,6 @@ class IssueBulkUpdateDateEndpoint(BaseAPIView): @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def post(self, request, slug, project_id): - updates = request.data.get("updates", []) issue_ids = [update["id"] for update in updates] @@ -1118,21 +1038,15 @@ class IssueBulkUpdateDateEndpoint(BaseAPIView): ) if not validate_dates: return Response( - { - "message": "Start date cannot exceed target date", - }, + {"message": "Start date cannot exceed target date"}, status=status.HTTP_400_BAD_REQUEST, ) if start_date: issue_activity.delay( type="issue.activity.updated", - requested_data=json.dumps( - {"start_date": update.get("start_date")} - ), - current_instance=json.dumps( - {"start_date": str(issue.start_date)} - ), + requested_data=json.dumps({"start_date": update.get("start_date")}), + current_instance=json.dumps({"start_date": str(issue.start_date)}), issue_id=str(issue_id), actor_id=str(request.user.id), project_id=str(project_id), @@ -1159,11 +1073,8 @@ class IssueBulkUpdateDateEndpoint(BaseAPIView): issues_to_update.append(issue) # Bulk update issues - Issue.objects.bulk_update( - issues_to_update, ["start_date", "target_date"] - ) + Issue.objects.bulk_update(issues_to_update, ["start_date", "target_date"]) return Response( - {"message": "Issues updated successfully"}, - status=status.HTTP_200_OK, + {"message": "Issues updated successfully"}, status=status.HTTP_200_OK ) diff --git a/apiserver/plane/app/views/issue/comment.py b/apiserver/plane/app/views/issue/comment.py index 12964dc3a..d072bb881 100644 --- a/apiserver/plane/app/views/issue/comment.py +++ b/apiserver/plane/app/views/issue/comment.py @@ -12,18 +12,9 @@ from rest_framework import status # Module imports from .. import BaseViewSet -from plane.app.serializers import ( - IssueCommentSerializer, - CommentReactionSerializer, -) +from plane.app.serializers import IssueCommentSerializer, CommentReactionSerializer from plane.app.permissions import allow_permission, ROLE -from plane.db.models import ( - IssueComment, - ProjectMember, - CommentReaction, - Project, - Issue, -) +from plane.db.models import IssueComment, ProjectMember, CommentReaction, Project, Issue from plane.bgtasks.issue_activities_task import issue_activity @@ -32,10 +23,7 @@ class IssueCommentViewSet(BaseViewSet): model = IssueComment webhook_event = "issue_comment" - filterset_fields = [ - "issue__id", - "workspace__id", - ] + filterset_fields = ["issue__id", "workspace__id"] def get_queryset(self): return self.filter_queryset( @@ -65,13 +53,7 @@ class IssueCommentViewSet(BaseViewSet): .distinct() ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def create(self, request, slug, project_id, issue_id): project = Project.objects.get(pk=project_id) issue = Issue.objects.get(pk=issue_id) @@ -93,15 +75,11 @@ class IssueCommentViewSet(BaseViewSet): serializer = IssueCommentSerializer(data=request.data) if serializer.is_valid(): serializer.save( - project_id=project_id, - issue_id=issue_id, - actor=request.user, + project_id=project_id, issue_id=issue_id, actor=request.user ) issue_activity.delay( type="comment.activity.created", - requested_data=json.dumps( - serializer.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -113,22 +91,14 @@ class IssueCommentViewSet(BaseViewSet): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @allow_permission( - allowed_roles=[ROLE.ADMIN], - creator=True, - model=IssueComment, - ) + @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=IssueComment) def partial_update(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) requested_data = json.dumps(self.request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( - IssueCommentSerializer(issue_comment).data, - cls=DjangoJSONEncoder, + IssueCommentSerializer(issue_comment).data, cls=DjangoJSONEncoder ) serializer = IssueCommentSerializer( issue_comment, data=request.data, partial=True @@ -149,19 +119,13 @@ class IssueCommentViewSet(BaseViewSet): return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @allow_permission( - allowed_roles=[ROLE.ADMIN], creator=True, model=IssueComment - ) + @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=IssueComment) def destroy(self, request, slug, project_id, issue_id, pk): issue_comment = IssueComment.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) current_instance = json.dumps( - IssueCommentSerializer(issue_comment).data, - cls=DjangoJSONEncoder, + IssueCommentSerializer(issue_comment).data, cls=DjangoJSONEncoder ) issue_comment.delete() issue_activity.delay( @@ -198,20 +162,12 @@ class CommentReactionViewSet(BaseViewSet): .distinct() ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def create(self, request, slug, project_id, comment_id): serializer = CommentReactionSerializer(data=request.data) if serializer.is_valid(): serializer.save( - project_id=project_id, - actor_id=request.user.id, - comment_id=comment_id, + project_id=project_id, actor_id=request.user.id, comment_id=comment_id ) issue_activity.delay( type="comment_reaction.activity.created", @@ -227,13 +183,7 @@ class CommentReactionViewSet(BaseViewSet): return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def destroy(self, request, slug, project_id, comment_id, reaction_code): comment_reaction = CommentReaction.objects.get( workspace__slug=slug, diff --git a/apiserver/plane/app/views/issue/label.py b/apiserver/plane/app/views/issue/label.py index 7a72932ff..b8a960161 100644 --- a/apiserver/plane/app/views/issue/label.py +++ b/apiserver/plane/app/views/issue/label.py @@ -12,19 +12,14 @@ from rest_framework import status from .. import BaseViewSet, BaseAPIView from plane.app.serializers import LabelSerializer from plane.app.permissions import allow_permission, ProjectBasePermission, ROLE -from plane.db.models import ( - Project, - Label, -) +from plane.db.models import Project, Label from plane.utils.cache import invalidate_cache class LabelViewSet(BaseViewSet): serializer_class = LabelSerializer model = Label - permission_classes = [ - ProjectBasePermission, - ] + permission_classes = [ProjectBasePermission] def get_queryset(self): return self.filter_queryset( @@ -40,39 +35,27 @@ class LabelViewSet(BaseViewSet): .order_by("sort_order") ) - @invalidate_cache( - path="/api/workspaces/:slug/labels/", url_params=True, user=False - ) + @invalidate_cache(path="/api/workspaces/:slug/labels/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) def create(self, request, slug, project_id): try: serializer = LabelSerializer(data=request.data) if serializer.is_valid(): serializer.save(project_id=project_id) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError: return Response( - { - "error": "Label with the same name already exists in the project" - }, + {"error": "Label with the same name already exists in the project"}, status=status.HTTP_400_BAD_REQUEST, ) - @invalidate_cache( - path="/api/workspaces/:slug/labels/", url_params=True, user=False - ) + @invalidate_cache(path="/api/workspaces/:slug/labels/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) def partial_update(self, request, *args, **kwargs): return super().partial_update(request, *args, **kwargs) - @invalidate_cache( - path="/api/workspaces/:slug/labels/", url_params=True, user=False - ) + @invalidate_cache(path="/api/workspaces/:slug/labels/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) def destroy(self, request, *args, **kwargs): return super().destroy(request, *args, **kwargs) diff --git a/apiserver/plane/app/views/issue/link.py b/apiserver/plane/app/views/issue/link.py index 1a4110a2f..85faa8368 100644 --- a/apiserver/plane/app/views/issue/link.py +++ b/apiserver/plane/app/views/issue/link.py @@ -18,9 +18,7 @@ from plane.bgtasks.issue_activities_task import issue_activity class IssueLinkViewSet(BaseViewSet): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] model = IssueLink serializer_class = IssueLinkSerializer @@ -44,15 +42,10 @@ class IssueLinkViewSet(BaseViewSet): def create(self, request, slug, project_id, issue_id): serializer = IssueLinkSerializer(data=request.data) if serializer.is_valid(): - serializer.save( - project_id=project_id, - issue_id=issue_id, - ) + serializer.save(project_id=project_id, issue_id=issue_id) issue_activity.delay( type="link.activity.created", - requested_data=json.dumps( - serializer.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id")), project_id=str(self.kwargs.get("project_id")), @@ -66,19 +59,13 @@ class IssueLinkViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) requested_data = json.dumps(request.data, cls=DjangoJSONEncoder) current_instance = json.dumps( - IssueLinkSerializer(issue_link).data, - cls=DjangoJSONEncoder, - ) - serializer = IssueLinkSerializer( - issue_link, data=request.data, partial=True + IssueLinkSerializer(issue_link).data, cls=DjangoJSONEncoder ) + serializer = IssueLinkSerializer(issue_link, data=request.data, partial=True) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -97,14 +84,10 @@ class IssueLinkViewSet(BaseViewSet): def destroy(self, request, slug, project_id, issue_id, pk): issue_link = IssueLink.objects.get( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - pk=pk, + workspace__slug=slug, project_id=project_id, issue_id=issue_id, pk=pk ) current_instance = json.dumps( - IssueLinkSerializer(issue_link).data, - cls=DjangoJSONEncoder, + IssueLinkSerializer(issue_link).data, cls=DjangoJSONEncoder ) issue_activity.delay( type="link.activity.deleted", diff --git a/apiserver/plane/app/views/issue/reaction.py b/apiserver/plane/app/views/issue/reaction.py index a43f7bda6..7fe53b456 100644 --- a/apiserver/plane/app/views/issue/reaction.py +++ b/apiserver/plane/app/views/issue/reaction.py @@ -42,9 +42,7 @@ class IssueReactionViewSet(BaseViewSet): serializer = IssueReactionSerializer(data=request.data) if serializer.is_valid(): serializer.save( - issue_id=issue_id, - project_id=project_id, - actor=request.user, + issue_id=issue_id, project_id=project_id, actor=request.user ) issue_activity.delay( type="issue_reaction.activity.created", @@ -76,10 +74,7 @@ class IssueReactionViewSet(BaseViewSet): issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(self.kwargs.get("project_id", None)), current_instance=json.dumps( - { - "reaction": str(reaction_code), - "identifier": str(issue_reaction.id), - } + {"reaction": str(reaction_code), "identifier": str(issue_reaction.id)} ), epoch=int(timezone.now().timestamp()), notification=True, diff --git a/apiserver/plane/app/views/issue/relation.py b/apiserver/plane/app/views/issue/relation.py index 67bfeff1f..35d88a54b 100644 --- a/apiserver/plane/app/views/issue/relation.py +++ b/apiserver/plane/app/views/issue/relation.py @@ -3,16 +3,7 @@ import json # Django imports from django.utils import timezone -from django.db.models import ( - Q, - OuterRef, - F, - Func, - UUIDField, - Value, - CharField, - Subquery, -) +from django.db.models import Q, OuterRef, F, Func, UUIDField, Value, CharField, Subquery from django.core.serializers.json import DjangoJSONEncoder from django.db.models.functions import Coalesce from django.contrib.postgres.aggregates import ArrayAgg @@ -24,10 +15,7 @@ from rest_framework import status # Module imports from .. import BaseViewSet -from plane.app.serializers import ( - IssueRelationSerializer, - RelatedIssueSerializer, -) +from plane.app.serializers import IssueRelationSerializer, RelatedIssueSerializer from plane.app.permissions import ProjectEntityPermission from plane.db.models import ( Project, @@ -44,9 +32,7 @@ from plane.utils.issue_relation_mapper import get_actual_relation class IssueRelationViewSet(BaseViewSet): serializer_class = IssueRelationSerializer model = IssueRelation - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def list(self, request, slug, project_id, issue_id): issue_relations = ( @@ -137,9 +123,7 @@ class IssueRelationViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -191,78 +175,34 @@ class IssueRelationViewSet(BaseViewSet): response_data = { "blocking": queryset.filter(pk__in=blocking_issues) - .annotate( - relation_type=Value("blocking", output_field=CharField()) - ) + .annotate(relation_type=Value("blocking", output_field=CharField())) .values(*fields), "blocked_by": queryset.filter(pk__in=blocked_by_issues) - .annotate( - relation_type=Value("blocked_by", output_field=CharField()) - ) + .annotate(relation_type=Value("blocked_by", output_field=CharField())) .values(*fields), "duplicate": queryset.filter(pk__in=duplicate_issues) - .annotate( - relation_type=Value( - "duplicate", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("duplicate", output_field=CharField())) .values(*fields) | queryset.filter(pk__in=duplicate_issues_related) - .annotate( - relation_type=Value( - "duplicate", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("duplicate", output_field=CharField())) .values(*fields), "relates_to": queryset.filter(pk__in=relates_to_issues) - .annotate( - relation_type=Value( - "relates_to", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("relates_to", output_field=CharField())) .values(*fields) | queryset.filter(pk__in=relates_to_issues_related) - .annotate( - relation_type=Value( - "relates_to", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("relates_to", output_field=CharField())) .values(*fields), "start_after": queryset.filter(pk__in=start_after_issues) - .annotate( - relation_type=Value( - "start_after", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("start_after", output_field=CharField())) .values(*fields), "start_before": queryset.filter(pk__in=start_before_issues) - .annotate( - relation_type=Value( - "start_before", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("start_before", output_field=CharField())) .values(*fields), "finish_after": queryset.filter(pk__in=finish_after_issues) - .annotate( - relation_type=Value( - "finish_after", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("finish_after", output_field=CharField())) .values(*fields), "finish_before": queryset.filter(pk__in=finish_before_issues) - .annotate( - relation_type=Value( - "finish_before", - output_field=CharField(), - ) - ) + .annotate(relation_type=Value("finish_before", output_field=CharField())) .values(*fields), } @@ -284,14 +224,12 @@ class IssueRelationViewSet(BaseViewSet): IssueRelation( issue_id=( issue - if relation_type - in ["blocking", "start_after", "finish_after"] + if relation_type in ["blocking", "start_after", "finish_after"] else issue_id ), related_issue_id=( issue_id - if relation_type - in ["blocking", "start_after", "finish_after"] + if relation_type in ["blocking", "start_after", "finish_after"] else issue ), relation_type=(get_actual_relation(relation_type)), @@ -348,8 +286,7 @@ class IssueRelationViewSet(BaseViewSet): related_issue_id=related_issue, ) current_instance = json.dumps( - IssueRelationSerializer(issue_relation).data, - cls=DjangoJSONEncoder, + IssueRelationSerializer(issue_relation).data, cls=DjangoJSONEncoder ) issue_relation.delete() issue_activity.delay( diff --git a/apiserver/plane/app/views/issue/sub_issue.py b/apiserver/plane/app/views/issue/sub_issue.py index cf3e108de..e461917fb 100644 --- a/apiserver/plane/app/views/issue/sub_issue.py +++ b/apiserver/plane/app/views/issue/sub_issue.py @@ -18,28 +18,19 @@ from rest_framework import status from .. import BaseAPIView from plane.app.serializers import IssueSerializer from plane.app.permissions import ProjectEntityPermission -from plane.db.models import ( - Issue, - IssueLink, - FileAsset, - CycleIssue, -) +from plane.db.models import Issue, IssueLink, FileAsset, CycleIssue from plane.bgtasks.issue_activities_task import issue_activity from plane.utils.user_timezone_converter import user_timezone_converter from collections import defaultdict class SubIssuesEndpoint(BaseAPIView): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] @method_decorator(gzip_page) def get(self, request, slug, project_id, issue_id): sub_issues = ( - Issue.issue_objects.filter( - parent_id=issue_id, workspace__slug=slug - ) + Issue.issue_objects.filter(parent_id=issue_id, workspace__slug=slug) .select_related("workspace", "project", "state", "parent") .prefetch_related("assignees", "labels", "issue_module__module") .annotate( @@ -65,9 +56,7 @@ class SubIssuesEndpoint(BaseAPIView): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -79,7 +68,7 @@ class SubIssuesEndpoint(BaseAPIView): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -150,10 +139,7 @@ class SubIssuesEndpoint(BaseAPIView): sub_issues, datetime_fields, request.user.user_timezone ) return Response( - { - "sub_issues": sub_issues, - "state_distribution": result, - }, + {"sub_issues": sub_issues, "state_distribution": result}, status=status.HTTP_200_OK, ) @@ -175,9 +161,9 @@ class SubIssuesEndpoint(BaseAPIView): _ = Issue.objects.bulk_update(sub_issues, ["parent"], batch_size=10) - updated_sub_issues = Issue.issue_objects.filter( - id__in=sub_issue_ids - ).annotate(state_group=F("state__group")) + updated_sub_issues = Issue.issue_objects.filter(id__in=sub_issue_ids).annotate( + state_group=F("state__group") + ) # Track the issue _ = [ @@ -200,14 +186,8 @@ class SubIssuesEndpoint(BaseAPIView): for sub_issue in updated_sub_issues: result[sub_issue.state_group].append(str(sub_issue.id)) - serializer = IssueSerializer( - updated_sub_issues, - many=True, - ) + serializer = IssueSerializer(updated_sub_issues, many=True) return Response( - { - "sub_issues": serializer.data, - "state_distribution": result, - }, + {"sub_issues": serializer.data, "state_distribution": result}, status=status.HTTP_200_OK, ) diff --git a/apiserver/plane/app/views/issue/subscriber.py b/apiserver/plane/app/views/issue/subscriber.py index dc727de28..58f3ba4c7 100644 --- a/apiserver/plane/app/views/issue/subscriber.py +++ b/apiserver/plane/app/views/issue/subscriber.py @@ -4,37 +4,22 @@ from rest_framework import status # Module imports from .. import BaseViewSet -from plane.app.serializers import ( - IssueSubscriberSerializer, - ProjectMemberLiteSerializer, -) -from plane.app.permissions import ( - ProjectEntityPermission, - ProjectLitePermission, -) -from plane.db.models import ( - IssueSubscriber, - ProjectMember, -) +from plane.app.serializers import IssueSubscriberSerializer, ProjectMemberLiteSerializer +from plane.app.permissions import ProjectEntityPermission, ProjectLitePermission +from plane.db.models import IssueSubscriber, ProjectMember class IssueSubscriberViewSet(BaseViewSet): serializer_class = IssueSubscriberSerializer model = IssueSubscriber - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_permissions(self): if self.action in ["subscribe", "unsubscribe", "subscription_status"]: - self.permission_classes = [ - ProjectLitePermission, - ] + self.permission_classes = [ProjectLitePermission] else: - self.permission_classes = [ - ProjectEntityPermission, - ] + self.permission_classes = [ProjectEntityPermission] return super(IssueSubscriberViewSet, self).get_permissions() @@ -62,9 +47,7 @@ class IssueSubscriberViewSet(BaseViewSet): def list(self, request, slug, project_id, issue_id): members = ProjectMember.objects.filter( - workspace__slug=slug, - project_id=project_id, - is_active=True, + workspace__slug=slug, project_id=project_id, is_active=True ).select_related("member") serializer = ProjectMemberLiteSerializer(members, many=True) return Response(serializer.data, status=status.HTTP_200_OK) @@ -77,9 +60,7 @@ class IssueSubscriberViewSet(BaseViewSet): issue=issue_id, ) issue_subscriber.delete() - return Response( - status=status.HTTP_204_NO_CONTENT, - ) + return Response(status=status.HTTP_204_NO_CONTENT) def subscribe(self, request, slug, project_id, issue_id): if IssueSubscriber.objects.filter( @@ -94,9 +75,7 @@ class IssueSubscriberViewSet(BaseViewSet): ) subscriber = IssueSubscriber.objects.create( - issue_id=issue_id, - subscriber_id=request.user.id, - project_id=project_id, + issue_id=issue_id, subscriber_id=request.user.id, project_id=project_id ) serializer = IssueSubscriberSerializer(subscriber) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -109,9 +88,7 @@ class IssueSubscriberViewSet(BaseViewSet): issue=issue_id, ) issue_subscriber.delete() - return Response( - status=status.HTTP_204_NO_CONTENT, - ) + return Response(status=status.HTTP_204_NO_CONTENT) def subscription_status(self, request, slug, project_id, issue_id): issue_subscriber = IssueSubscriber.objects.filter( @@ -120,6 +97,4 @@ class IssueSubscriberViewSet(BaseViewSet): workspace__slug=slug, project=project_id, ).exists() - return Response( - {"subscribed": issue_subscriber}, status=status.HTTP_200_OK - ) + return Response({"subscribed": issue_subscriber}, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/module/archive.py b/apiserver/plane/app/views/module/archive.py index 997cac4cd..82c1d47eb 100644 --- a/apiserver/plane/app/views/module/archive.py +++ b/apiserver/plane/app/views/module/archive.py @@ -24,12 +24,8 @@ from django.db import models # Third party imports from rest_framework import status from rest_framework.response import Response -from plane.app.permissions import ( - ProjectEntityPermission, -) -from plane.app.serializers import ( - ModuleDetailSerializer, -) +from plane.app.permissions import ProjectEntityPermission +from plane.app.serializers import ModuleDetailSerializer from plane.db.models import Issue, Module, ModuleLink, UserFavorite, Project from plane.utils.analytics_plot import burndown_plot from plane.utils.user_timezone_converter import user_timezone_converter @@ -40,10 +36,7 @@ from .. import BaseAPIView class ModuleArchiveUnarchiveEndpoint(BaseAPIView): - - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] def get_queryset(self): favorite_subquery = UserFavorite.objects.filter( @@ -136,9 +129,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("issue_module__module_id") .annotate( - total_estimate_points=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimate_points=Sum(Cast("estimate_point__value", FloatField())) ) .values("total_estimate_points")[:1] ) @@ -151,9 +142,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("issue_module__module_id") .annotate( - backlog_estimate_point=Sum( - Cast("estimate_point__value", FloatField()) - ) + backlog_estimate_point=Sum(Cast("estimate_point__value", FloatField())) ) .values("backlog_estimate_point")[:1] ) @@ -181,9 +170,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .values("issue_module__module_id") .annotate( - started_estimate_point=Sum( - Cast("estimate_point__value", FloatField()) - ) + started_estimate_point=Sum(Cast("estimate_point__value", FloatField())) ) .values("started_estimate_point")[:1] ) @@ -212,9 +199,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related( - "module", "created_by" - ), + queryset=ModuleLink.objects.select_related("module", "created_by"), ) ) .annotate( @@ -231,8 +216,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( started_issues=Coalesce( - Subquery(started_issues[:1]), - Value(0, output_field=IntegerField()), + Subquery(started_issues[:1]), Value(0, output_field=IntegerField()) ) ) .annotate( @@ -243,51 +227,48 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( backlog_issues=Coalesce( - Subquery(backlog_issues[:1]), - Value(0, output_field=IntegerField()), + Subquery(backlog_issues[:1]), Value(0, output_field=IntegerField()) ) ) .annotate( total_issues=Coalesce( - Subquery(total_issues[:1]), - Value(0, output_field=IntegerField()), + Subquery(total_issues[:1]), Value(0, output_field=IntegerField()) ) ) .annotate( backlog_estimate_points=Coalesce( Subquery(backlog_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( unstarted_estimate_points=Coalesce( Subquery(unstarted_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( started_estimate_points=Coalesce( Subquery(started_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( cancelled_estimate_points=Coalesce( Subquery(cancelled_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( completed_estimate_points=Coalesce( Subquery(completed_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( total_estimate_points=Coalesce( - Subquery(total_estimate_point), - Value(0, output_field=FloatField()), - ), + Subquery(total_estimate_point), Value(0, output_field=FloatField()) + ) ) .annotate( member_ids=Coalesce( @@ -409,9 +390,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): "display_name", ) .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ), + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -447,9 +426,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): .annotate(label_id=F("labels__id")) .values("label_name", "color", "label_id") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ), + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -473,20 +450,16 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .order_by("label_name") ) - data["estimate_distribution"][ - "assignees" - ] = assignee_distribution + data["estimate_distribution"]["assignees"] = assignee_distribution data["estimate_distribution"]["labels"] = label_distribution if modules and modules.start_date and modules.target_date: - data["estimate_distribution"]["completion_chart"] = ( - burndown_plot( - queryset=modules, - slug=slug, - project_id=project_id, - plot_type="points", - module_id=pk, - ) + data["estimate_distribution"]["completion_chart"] = burndown_plot( + queryset=modules, + slug=slug, + project_id=project_id, + plot_type="points", + module_id=pk, ) assignee_distribution = ( @@ -529,12 +502,8 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -572,12 +541,8 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -616,10 +581,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): module_id=pk, ) - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) def post(self, request, slug, project_id, module_id): module = Module.objects.get( @@ -627,9 +589,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): ) if module.status not in ["completed", "cancelled"]: return Response( - { - "error": "Only completed or cancelled modules can be archived" - }, + {"error": "Only completed or cancelled modules can be archived"}, status=status.HTTP_400_BAD_REQUEST, ) module.archived_at = timezone.now() @@ -641,8 +601,7 @@ class ModuleArchiveUnarchiveEndpoint(BaseAPIView): workspace__slug=slug, ).delete() return Response( - {"archived_at": str(module.archived_at)}, - status=status.HTTP_200_OK, + {"archived_at": str(module.archived_at)}, status=status.HTTP_200_OK ) def delete(self, request, slug, project_id, module_id): diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index d53f13190..8f9839b71 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -164,9 +164,7 @@ class ModuleViewSet(BaseViewSet): ) .values("issue_module__module_id") .annotate( - total_estimate_points=Sum( - Cast("estimate_point__value", FloatField()) - ) + total_estimate_points=Sum(Cast("estimate_point__value", FloatField())) ) .values("total_estimate_points")[:1] ) @@ -179,9 +177,7 @@ class ModuleViewSet(BaseViewSet): ) .values("issue_module__module_id") .annotate( - backlog_estimate_point=Sum( - Cast("estimate_point__value", FloatField()) - ) + backlog_estimate_point=Sum(Cast("estimate_point__value", FloatField())) ) .values("backlog_estimate_point")[:1] ) @@ -209,9 +205,7 @@ class ModuleViewSet(BaseViewSet): ) .values("issue_module__module_id") .annotate( - started_estimate_point=Sum( - Cast("estimate_point__value", FloatField()) - ) + started_estimate_point=Sum(Cast("estimate_point__value", FloatField())) ) .values("started_estimate_point")[:1] ) @@ -243,9 +237,7 @@ class ModuleViewSet(BaseViewSet): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related( - "module", "created_by" - ), + queryset=ModuleLink.objects.select_related("module", "created_by"), ) ) .annotate( @@ -262,8 +254,7 @@ class ModuleViewSet(BaseViewSet): ) .annotate( started_issues=Coalesce( - Subquery(started_issues[:1]), - Value(0, output_field=IntegerField()), + Subquery(started_issues[:1]), Value(0, output_field=IntegerField()) ) ) .annotate( @@ -274,51 +265,48 @@ class ModuleViewSet(BaseViewSet): ) .annotate( backlog_issues=Coalesce( - Subquery(backlog_issues[:1]), - Value(0, output_field=IntegerField()), + Subquery(backlog_issues[:1]), Value(0, output_field=IntegerField()) ) ) .annotate( total_issues=Coalesce( - Subquery(total_issues[:1]), - Value(0, output_field=IntegerField()), + Subquery(total_issues[:1]), Value(0, output_field=IntegerField()) ) ) .annotate( backlog_estimate_points=Coalesce( Subquery(backlog_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( unstarted_estimate_points=Coalesce( Subquery(unstarted_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( started_estimate_points=Coalesce( Subquery(started_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( cancelled_estimate_points=Coalesce( Subquery(cancelled_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( completed_estimate_points=Coalesce( Subquery(completed_estimate_point), Value(0, output_field=FloatField()), - ), + ) ) .annotate( total_estimate_points=Coalesce( - Subquery(total_estimate_point), - Value(0, output_field=FloatField()), - ), + Subquery(total_estimate_point), Value(0, output_field=FloatField()) + ) ) .annotate( member_ids=Coalesce( @@ -333,12 +321,7 @@ class ModuleViewSet(BaseViewSet): .order_by("-is_favorite", "-created_at") ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def create(self, request, slug, project_id): project = Project.objects.get(workspace__slug=slug, pk=project_id) serializer = ModuleWriteSerializer( @@ -405,11 +388,7 @@ class ModuleViewSet(BaseViewSet): def list(self, request, slug, project_id): queryset = self.get_queryset().filter(archived_at__isnull=True) if self.fields: - modules = ModuleSerializer( - queryset, - many=True, - fields=self.fields, - ).data + modules = ModuleSerializer(queryset, many=True, fields=self.fields).data else: modules = queryset.values( # Required fields "id", @@ -470,8 +449,7 @@ class ModuleViewSet(BaseViewSet): if not queryset.exists(): return Response( - {"error": "Module not found"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Module not found"}, status=status.HTTP_404_NOT_FOUND ) estimate_type = Project.objects.filter( @@ -526,9 +504,7 @@ class ModuleViewSet(BaseViewSet): "display_name", ) .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ), + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -565,9 +541,7 @@ class ModuleViewSet(BaseViewSet): .annotate(label_id=F("labels__id")) .values("label_name", "color", "label_id") .annotate( - total_estimates=Sum( - Cast("estimate_point__value", FloatField()) - ), + total_estimates=Sum(Cast("estimate_point__value", FloatField())) ) .annotate( completed_estimates=Sum( @@ -595,14 +569,12 @@ class ModuleViewSet(BaseViewSet): data["estimate_distribution"]["labels"] = label_distribution if modules and modules.start_date and modules.target_date: - data["estimate_distribution"]["completion_chart"] = ( - burndown_plot( - queryset=modules, - slug=slug, - project_id=project_id, - plot_type="points", - module_id=pk, - ) + data["estimate_distribution"]["completion_chart"] = burndown_plot( + queryset=modules, + slug=slug, + project_id=project_id, + plot_type="points", + module_id=pk, ) assignee_distribution = ( @@ -629,28 +601,19 @@ class ModuleViewSet(BaseViewSet): ), # If `avatar_asset` is None, fall back to using `avatar` field directly When( - assignees__avatar_asset__isnull=True, - then="assignees__avatar", + assignees__avatar_asset__isnull=True, then="assignees__avatar" ), default=Value(None), output_field=models.CharField(), ) ) .values( - "first_name", - "last_name", - "assignee_id", - "avatar_url", - "display_name", + "first_name", "last_name", "assignee_id", "avatar_url", "display_name" ) .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -688,12 +651,8 @@ class ModuleViewSet(BaseViewSet): .values("label_name", "color", "label_id") .annotate( total_issues=Count( - "id", - filter=Q( - archived_at__isnull=True, - is_draft=False, - ), - ), + "id", filter=Q(archived_at__isnull=True, is_draft=False) + ) ) .annotate( completed_issues=Count( @@ -746,10 +705,7 @@ class ModuleViewSet(BaseViewSet): project_id=project_id, ) - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def partial_update(self, request, slug, project_id, pk): @@ -823,14 +779,10 @@ class ModuleViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN], creator=True, model=Module) def destroy(self, request, slug, project_id, pk): - module = Module.objects.get( - workspace__slug=slug, project_id=project_id, pk=pk - ) + module = Module.objects.get(workspace__slug=slug, project_id=project_id, pk=pk) module_issues = list( - ModuleIssue.objects.filter(module_id=pk).values_list( - "issue", flat=True - ) + ModuleIssue.objects.filter(module_id=pk).values_list("issue", flat=True) ) _ = [ issue_activity.delay( @@ -848,10 +800,7 @@ class ModuleViewSet(BaseViewSet): ] module.delete() # Delete the module issues - ModuleIssue.objects.filter( - module=pk, - project_id=project_id, - ).delete() + ModuleIssue.objects.filter(module=pk, project_id=project_id).delete() # Delete the user favorite module UserFavorite.objects.filter( user=request.user, @@ -863,9 +812,7 @@ class ModuleViewSet(BaseViewSet): class ModuleLinkViewSet(BaseViewSet): - permission_classes = [ - ProjectEntityPermission, - ] + permission_classes = [ProjectEntityPermission] model = ModuleLink serializer_class = ModuleLinkSerializer @@ -895,9 +842,7 @@ class ModuleLinkViewSet(BaseViewSet): class ModuleFavoriteViewSet(BaseViewSet): model = UserFavorite - permission_classes = [ - ProjectLitePermission, - ] + permission_classes = [ProjectLitePermission] def get_queryset(self): return self.filter_queryset( @@ -930,7 +875,6 @@ class ModuleFavoriteViewSet(BaseViewSet): class ModuleUserPropertiesEndpoint(BaseAPIView): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def patch(self, request, slug, project_id, module_id): module_properties = ModuleUserProperties.objects.get( diff --git a/apiserver/plane/app/views/module/issue.py b/apiserver/plane/app/views/module/issue.py index dd829753c..06b0a2fb1 100644 --- a/apiserver/plane/app/views/module/issue.py +++ b/apiserver/plane/app/views/module/issue.py @@ -13,9 +13,7 @@ from rest_framework import status from rest_framework.response import Response from plane.app.permissions import allow_permission, ROLE -from plane.app.serializers import ( - ModuleIssueSerializer, -) +from plane.app.serializers import ModuleIssueSerializer from plane.bgtasks.issue_activities_task import issue_activity from plane.db.models import ( Issue, @@ -32,10 +30,7 @@ from plane.utils.grouper import ( ) from plane.utils.issue_filters import issue_filters from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator # Module imports from .. import BaseViewSet @@ -47,10 +42,7 @@ class ModuleIssueViewSet(BaseViewSet): webhook_event = "module_issue" bulk = True - filterset_fields = [ - "issue__labels__id", - "issue__assignees__id", - ] + filterset_fields = ["issue__labels__id", "issue__assignees__id"] def get_queryset(self): return ( @@ -85,9 +77,7 @@ class ModuleIssueViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -95,12 +85,7 @@ class ModuleIssueViewSet(BaseViewSet): ).distinct() @method_decorator(gzip_page) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def list(self, request, slug, project_id, module_id): filters = issue_filters(request.query_params, "GET") issue_queryset = self.get_queryset().filter(**filters) @@ -108,8 +93,7 @@ class ModuleIssueViewSet(BaseViewSet): # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -118,9 +102,7 @@ class ModuleIssueViewSet(BaseViewSet): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) if group_by: @@ -140,9 +122,7 @@ class ModuleIssueViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -176,9 +156,7 @@ class ModuleIssueViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -214,8 +192,7 @@ class ModuleIssueViewSet(BaseViewSet): issues = request.data.get("issues", []) if not issues: return Response( - {"error": "Issues are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Issues are required"}, status=status.HTTP_400_BAD_REQUEST ) project = Project.objects.get(pk=project_id) _ = ModuleIssue.objects.bulk_create( diff --git a/apiserver/plane/app/views/notification/base.py b/apiserver/plane/app/views/notification/base.py index c4aa14969..d2aa1a02d 100644 --- a/apiserver/plane/app/views/notification/base.py +++ b/apiserver/plane/app/views/notification/base.py @@ -41,8 +41,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator): ) @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def list(self, request, slug): # Get query parameters @@ -79,10 +78,8 @@ class NotificationViewSet(BaseViewSet, BasePaginator): # Filters based on query parameters snoozed_filters = { - "true": Q(snoozed_till__lt=timezone.now()) - | Q(snoozed_till__isnull=False), - "false": Q(snoozed_till__gte=timezone.now()) - | Q(snoozed_till__isnull=True), + "true": Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False), + "false": Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), } notifications = notifications.filter(snoozed_filters[snoozed]) @@ -103,9 +100,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator): if mentioned: notifications = notifications.filter(sender__icontains="mentioned") else: - notifications = notifications.exclude( - sender__icontains="mentioned" - ) + notifications = notifications.exclude(sender__icontains="mentioned") type = type.split(",") # Subscribed issues @@ -143,10 +138,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator): # Created issues if "created" in type: if WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role__lt=15, - is_active=True, + workspace__slug=slug, member=request.user, role__lt=15, is_active=True ).exists(): notifications = notifications.none() else: @@ -159,9 +151,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator): notifications = notifications.filter(q_filters) # Pagination - if request.GET.get("per_page", False) and request.GET.get( - "cursor", False - ): + if request.GET.get("per_page", False) and request.GET.get("cursor", False): return self.paginate( order_by=request.GET.get("order_by", "-created_at"), request=request, @@ -175,17 +165,14 @@ class NotificationViewSet(BaseViewSet, BasePaginator): return Response(serializer.data, status=status.HTTP_200_OK) @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def partial_update(self, request, slug, pk): notification = Notification.objects.get( workspace__slug=slug, pk=pk, receiver=request.user ) # Only read_at and snoozed_till can be updated - notification_data = { - "snoozed_till": request.data.get("snoozed_till", None), - } + notification_data = {"snoozed_till": request.data.get("snoozed_till", None)} serializer = NotificationSerializer( notification, data=notification_data, partial=True ) @@ -246,8 +233,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator): class UnreadNotificationEndpoint(BaseAPIView): @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def get(self, request, slug): # Watching Issues Count @@ -274,12 +260,8 @@ class UnreadNotificationEndpoint(BaseAPIView): return Response( { - "total_unread_notifications_count": int( - unread_notifications_count - ), - "mention_unread_notifications_count": int( - mention_notifications_count - ), + "total_unread_notifications_count": int(unread_notifications_count), + "mention_unread_notifications_count": int(mention_notifications_count), }, status=status.HTTP_200_OK, ) @@ -296,9 +278,7 @@ class MarkAllReadNotificationViewSet(BaseViewSet): notifications = ( Notification.objects.filter( - workspace__slug=slug, - receiver_id=request.user.id, - read_at__isnull=True, + workspace__slug=slug, receiver_id=request.user.id, read_at__isnull=True ) .select_related("workspace", "project", "triggered_by", "receiver") .order_by("snoozed_till", "-created_at") @@ -307,13 +287,11 @@ class MarkAllReadNotificationViewSet(BaseViewSet): # Filter for snoozed notifications if snoozed: notifications = notifications.filter( - Q(snoozed_till__lt=timezone.now()) - | Q(snoozed_till__isnull=False) + Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False) ) else: notifications = notifications.filter( - Q(snoozed_till__gte=timezone.now()) - | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True) ) # Filter for archived or unarchive @@ -327,35 +305,26 @@ class MarkAllReadNotificationViewSet(BaseViewSet): issue_ids = IssueSubscriber.objects.filter( workspace__slug=slug, subscriber_id=request.user.id ).values_list("issue_id", flat=True) - notifications = notifications.filter( - entity_identifier__in=issue_ids - ) + notifications = notifications.filter(entity_identifier__in=issue_ids) # Assigned Issues if type == "assigned": issue_ids = IssueAssignee.objects.filter( workspace__slug=slug, assignee_id=request.user.id ).values_list("issue_id", flat=True) - notifications = notifications.filter( - entity_identifier__in=issue_ids - ) + notifications = notifications.filter(entity_identifier__in=issue_ids) # Created issues if type == "created": if WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role__lt=15, - is_active=True, + workspace__slug=slug, member=request.user, role__lt=15, is_active=True ).exists(): notifications = Notification.objects.none() else: issue_ids = Issue.objects.filter( workspace__slug=slug, created_by=request.user ).values_list("pk", flat=True) - notifications = notifications.filter( - entity_identifier__in=issue_ids - ) + notifications = notifications.filter(entity_identifier__in=issue_ids) updated_notifications = [] for notification in notifications: @@ -376,9 +345,7 @@ class UserNotificationPreferenceEndpoint(BaseAPIView): user_notification_preference = UserNotificationPreference.objects.get( user=request.user ) - serializer = UserNotificationPreferenceSerializer( - user_notification_preference - ) + serializer = UserNotificationPreferenceSerializer(user_notification_preference) return Response(serializer.data, status=status.HTTP_200_OK) # update the object diff --git a/apiserver/plane/app/views/page/base.py b/apiserver/plane/app/views/page/base.py index 5e56cc703..24ceb2d3f 100644 --- a/apiserver/plane/app/views/page/base.py +++ b/apiserver/plane/app/views/page/base.py @@ -60,9 +60,7 @@ def unarchive_archive_page_and_descendants(page_id, archived_at): class PageViewSet(BaseViewSet): serializer_class = PageSerializer model = Page - search_fields = [ - "name", - ] + search_fields = ["name"] def get_queryset(self): subquery = UserFavorite.objects.filter( @@ -92,8 +90,7 @@ class PageViewSet(BaseViewSet): .annotate( project=Exists( ProjectPage.objects.filter( - page_id=OuterRef("id"), - project_id=self.kwargs.get("project_id"), + page_id=OuterRef("id"), project_id=self.kwargs.get("project_id") ) ) ) @@ -108,9 +105,7 @@ class PageViewSet(BaseViewSet): ), project_ids=Coalesce( ArrayAgg( - "projects__id", - distinct=True, - filter=~Q(projects__id=True), + "projects__id", distinct=True, filter=~Q(projects__id=True) ), Value([], output_field=ArrayField(UUIDField())), ), @@ -126,9 +121,7 @@ class PageViewSet(BaseViewSet): context={ "project_id": project_id, "owned_by_id": request.user.id, - "description_html": request.data.get( - "description_html", "

" - ), + "description_html": request.data.get("description_html", "

"), }, ) @@ -145,23 +138,18 @@ class PageViewSet(BaseViewSet): def partial_update(self, request, slug, project_id, pk): try: page = Page.objects.get( - pk=pk, - workspace__slug=slug, - projects__id=project_id, + pk=pk, workspace__slug=slug, projects__id=project_id ) if page.is_locked: return Response( - {"error": "Page is locked"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Page is locked"}, status=status.HTTP_400_BAD_REQUEST ) parent = request.data.get("parent", None) if parent: _ = Page.objects.get( - pk=parent, - workspace__slug=slug, - projects__id=project_id, + pk=parent, workspace__slug=slug, projects__id=project_id ) # Only update access if the page owner is the requesting user @@ -176,9 +164,7 @@ class PageViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - serializer = PageDetailSerializer( - page, data=request.data, partial=True - ) + serializer = PageDetailSerializer(page, data=request.data, partial=True) page_description = page.description_html if serializer.is_valid(): serializer.save() @@ -187,18 +173,14 @@ class PageViewSet(BaseViewSet): page_transaction.delay( new_value=request.data, old_value=json.dumps( - { - "description_html": page_description, - }, + {"description_html": page_description}, cls=DjangoJSONEncoder, ), page_id=pk, ) return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except Page.DoesNotExist: return Response( { @@ -207,13 +189,7 @@ class PageViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def retrieve(self, request, slug, project_id, pk=None): page = self.get_queryset().filter(pk=pk).first() project = Project.objects.get(pk=project_id) @@ -241,8 +217,7 @@ class PageViewSet(BaseViewSet): if page is None: return Response( - {"error": "Page not found"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Page not found"}, status=status.HTTP_404_NOT_FOUND ) else: issue_ids = PageLog.objects.filter( @@ -257,10 +232,7 @@ class PageViewSet(BaseViewSet): user_id=request.user.id, project_id=project_id, ) - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def lock(self, request, slug, project_id, pk): @@ -306,13 +278,7 @@ class PageViewSet(BaseViewSet): page.save() return Response(status=status.HTTP_204_NO_CONTENT) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def list(self, request, slug, project_id): queryset = self.get_queryset() project = Project.objects.get(pk=project_id) @@ -332,17 +298,12 @@ class PageViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def archive(self, request, slug, project_id, pk): - page = Page.objects.get( - pk=pk, workspace__slug=slug, projects__id=project_id - ) + page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id) # only the owner or admin can archive the page if ( ProjectMember.objects.filter( - project_id=project_id, - member=request.user, - is_active=True, - role__lte=15, + project_id=project_id, member=request.user, is_active=True, role__lte=15 ).exists() and request.user.id != page.owned_by_id ): @@ -360,24 +321,16 @@ class PageViewSet(BaseViewSet): unarchive_archive_page_and_descendants(pk, datetime.now()) - return Response( - {"archived_at": str(datetime.now())}, - status=status.HTTP_200_OK, - ) + return Response({"archived_at": str(datetime.now())}, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def unarchive(self, request, slug, project_id, pk): - page = Page.objects.get( - pk=pk, workspace__slug=slug, projects__id=project_id - ) + page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id) # only the owner or admin can un archive the page if ( ProjectMember.objects.filter( - project_id=project_id, - member=request.user, - is_active=True, - role__lte=15, + project_id=project_id, member=request.user, is_active=True, role__lte=15 ).exists() and request.user.id != page.owned_by_id ): @@ -397,9 +350,7 @@ class PageViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN], creator=True, model=Page) def destroy(self, request, slug, project_id, pk): - page = Page.objects.get( - pk=pk, workspace__slug=slug, projects__id=project_id - ) + page = Page.objects.get(pk=pk, workspace__slug=slug, projects__id=project_id) if page.archived_at is None: return Response( @@ -438,7 +389,6 @@ class PageViewSet(BaseViewSet): class PageFavoriteViewSet(BaseViewSet): - model = UserFavorite @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) @@ -465,7 +415,6 @@ class PageFavoriteViewSet(BaseViewSet): class PageLogEndpoint(BaseAPIView): - serializer_class = PageLogSerializer model = PageLog @@ -504,7 +453,6 @@ class PageLogEndpoint(BaseAPIView): class SubPagesEndpoint(BaseAPIView): - @method_decorator(gzip_page) def get(self, request, slug, project_id, page_id): pages = ( @@ -522,27 +470,15 @@ class SubPagesEndpoint(BaseAPIView): class PagesDescriptionViewSet(BaseViewSet): - - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ] - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def retrieve(self, request, slug, project_id, pk): page = ( - Page.objects.filter( - pk=pk, workspace__slug=slug, projects__id=project_id - ) + Page.objects.filter(pk=pk, workspace__slug=slug, projects__id=project_id) .filter(Q(owned_by=self.request.user) | Q(access=0)) .first() ) if page is None: - return Response( - {"error": "Page not found"}, - status=404, - ) + return Response({"error": "Page not found"}, status=404) binary_data = page.description_binary def stream_data(): @@ -554,26 +490,19 @@ class PagesDescriptionViewSet(BaseViewSet): response = StreamingHttpResponse( stream_data(), content_type="application/octet-stream" ) - response["Content-Disposition"] = ( - 'attachment; filename="page_description.bin"' - ) + response["Content-Disposition"] = 'attachment; filename="page_description.bin"' return response @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def partial_update(self, request, slug, project_id, pk): page = ( - Page.objects.filter( - pk=pk, workspace__slug=slug, projects__id=project_id - ) + Page.objects.filter(pk=pk, workspace__slug=slug, projects__id=project_id) .filter(Q(owned_by=self.request.user) | Q(access=0)) .first() ) if page is None: - return Response( - {"error": "Page not found"}, - status=404, - ) + return Response({"error": "Page not found"}, status=404) if page.is_locked: return Response( @@ -595,10 +524,7 @@ class PagesDescriptionViewSet(BaseViewSet): # Serialize the existing instance existing_instance = json.dumps( - { - "description_html": page.description_html, - }, - cls=DjangoJSONEncoder, + {"description_html": page.description_html}, cls=DjangoJSONEncoder ) # Get the base64 data from the request @@ -611,9 +537,7 @@ class PagesDescriptionViewSet(BaseViewSet): # capture the page transaction if request.data.get("description_html"): page_transaction.delay( - new_value=request.data, - old_value=existing_instance, - page_id=pk, + new_value=request.data, old_value=existing_instance, page_id=pk ) # Store the updated binary data page.description_binary = new_binary_data diff --git a/apiserver/plane/app/views/page/version.py b/apiserver/plane/app/views/page/version.py index 1152be4f7..bcf2f4f5b 100644 --- a/apiserver/plane/app/views/page/version.py +++ b/apiserver/plane/app/views/page/version.py @@ -5,34 +5,25 @@ from rest_framework.response import Response # Module imports from plane.db.models import PageVersion from ..base import BaseAPIView -from plane.app.serializers import ( - PageVersionSerializer, - PageVersionDetailSerializer, -) +from plane.app.serializers import PageVersionSerializer, PageVersionDetailSerializer from plane.app.permissions import allow_permission, ROLE class PageVersionEndpoint(BaseAPIView): - - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST] - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def get(self, request, slug, project_id, page_id, pk=None): # Check if pk is provided if pk: # Return a single page version page_version = PageVersion.objects.get( - workspace__slug=slug, - page_id=page_id, - pk=pk, + workspace__slug=slug, page_id=page_id, pk=pk ) # Serialize the page version serializer = PageVersionDetailSerializer(page_version) return Response(serializer.data, status=status.HTTP_200_OK) # Return all page versions page_versions = PageVersion.objects.filter( - workspace__slug=slug, - page_id=page_id, + workspace__slug=slug, page_id=page_id ) # Serialize the page versions serializer = PageVersionSerializer(page_versions, many=True) diff --git a/apiserver/plane/app/views/project/base.py b/apiserver/plane/app/views/project/base.py index d53b3fac7..7b027df98 100644 --- a/apiserver/plane/app/views/project/base.py +++ b/apiserver/plane/app/views/project/base.py @@ -6,15 +6,7 @@ import json # Django imports from django.db import IntegrityError -from django.db.models import ( - Exists, - F, - Func, - OuterRef, - Prefetch, - Q, - Subquery, -) +from django.db.models import Exists, F, Func, OuterRef, Prefetch, Q, Subquery from django.core.serializers.json import DjangoJSONEncoder # Third Party imports @@ -30,11 +22,7 @@ from plane.app.serializers import ( DeployBoardSerializer, ) -from plane.app.permissions import ( - ProjectMemberPermission, - allow_permission, - ROLE, -) +from plane.app.permissions import ProjectMemberPermission, allow_permission, ROLE from plane.db.models import ( UserFavorite, Cycle, @@ -73,10 +61,7 @@ class ProjectViewSet(BaseViewSet): .get_queryset() .filter(workspace__slug=self.kwargs.get("slug")) .select_related( - "workspace", - "workspace__owner", - "default_assignee", - "project_lead", + "workspace", "workspace__owner", "default_assignee", "project_lead" ) .annotate( is_favorite=Exists( @@ -100,9 +85,7 @@ class ProjectViewSet(BaseViewSet): ) .annotate( total_members=ProjectMember.objects.filter( - project_id=OuterRef("id"), - member__is_bot=False, - is_active=True, + project_id=OuterRef("id"), member__is_bot=False, is_active=True ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -139,8 +122,7 @@ class ProjectViewSet(BaseViewSet): Prefetch( "project_projectmember", queryset=ProjectMember.objects.filter( - workspace__slug=self.kwargs.get("slug"), - is_active=True, + workspace__slug=self.kwargs.get("slug"), is_active=True ).select_related("member"), to_attr="members_list", ) @@ -149,21 +131,13 @@ class ProjectViewSet(BaseViewSet): ) @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def list(self, request, slug): - fields = [ - field - for field in request.GET.get("fields", "").split(",") - if field - ] + fields = [field for field in request.GET.get("fields", "").split(",") if field] projects = self.get_queryset().order_by("sort_order", "name") if WorkspaceMember.objects.filter( - member=request.user, - workspace__slug=slug, - is_active=True, - role=5, + member=request.user, workspace__slug=slug, is_active=True, role=5 ).exists(): projects = projects.filter( project_projectmember__member=self.request.user, @@ -171,10 +145,7 @@ class ProjectViewSet(BaseViewSet): ) if WorkspaceMember.objects.filter( - member=request.user, - workspace__slug=slug, - is_active=True, - role=15, + member=request.user, workspace__slug=slug, is_active=True, role=15 ).exists(): projects = projects.filter( Q( @@ -184,9 +155,7 @@ class ProjectViewSet(BaseViewSet): | Q(network=2) ) - if request.GET.get("per_page", False) and request.GET.get( - "cursor", False - ): + if request.GET.get("per_page", False) and request.GET.get("cursor", False): return self.paginate( order_by=request.GET.get("order_by", "-created_at"), request=request, @@ -202,8 +171,7 @@ class ProjectViewSet(BaseViewSet): return Response(projects, status=status.HTTP_200_OK) @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def retrieve(self, request, slug, pk): project = ( @@ -212,7 +180,7 @@ class ProjectViewSet(BaseViewSet): .filter(pk=pk) .annotate( total_issues=Issue.issue_objects.filter( - project_id=self.kwargs.get("pk"), + project_id=self.kwargs.get("pk") ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -220,8 +188,7 @@ class ProjectViewSet(BaseViewSet): ) .annotate( sub_issues=Issue.issue_objects.filter( - project_id=self.kwargs.get("pk"), - parent__isnull=False, + project_id=self.kwargs.get("pk"), parent__isnull=False ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -229,8 +196,7 @@ class ProjectViewSet(BaseViewSet): ) .annotate( archived_issues=Issue.objects.filter( - project_id=self.kwargs.get("pk"), - archived_at__isnull=False, + project_id=self.kwargs.get("pk"), archived_at__isnull=False ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -248,8 +214,7 @@ class ProjectViewSet(BaseViewSet): ) .annotate( draft_issues=Issue.objects.filter( - project_id=self.kwargs.get("pk"), - is_draft=True, + project_id=self.kwargs.get("pk"), is_draft=True ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -269,8 +234,7 @@ class ProjectViewSet(BaseViewSet): if project is None: return Response( - {"error": "Project does not exist"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project does not exist"}, status=status.HTTP_404_NOT_FOUND ) recent_visited_task.delay( @@ -297,14 +261,11 @@ class ProjectViewSet(BaseViewSet): # Add the user as Administrator to the project _ = ProjectMember.objects.create( - project_id=serializer.data["id"], - member=request.user, - role=20, + project_id=serializer.data["id"], member=request.user, role=20 ) # Also create the issue property for the user _ = IssueUserProperty.objects.create( - project_id=serializer.data["id"], - user=request.user, + project_id=serializer.data["id"], user=request.user ) if serializer.data["project_lead"] is not None and str( @@ -372,11 +333,7 @@ class ProjectViewSet(BaseViewSet): ] ) - project = ( - self.get_queryset() - .filter(pk=serializer.data["id"]) - .first() - ) + project = self.get_queryset().filter(pk=serializer.data["id"]).first() # Create the model activity model_activity.delay( @@ -390,13 +347,8 @@ class ProjectViewSet(BaseViewSet): ) serializer = ProjectListSerializer(project) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError as e: if "already exists" in str(e): return Response( @@ -405,8 +357,7 @@ class ProjectViewSet(BaseViewSet): ) except Workspace.DoesNotExist: return Response( - {"error": "Workspace does not exist"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Workspace does not exist"}, status=status.HTTP_404_NOT_FOUND ) except serializers.ValidationError: return Response( @@ -445,10 +396,7 @@ class ProjectViewSet(BaseViewSet): serializer = ProjectSerializer( project, - data={ - **request.data, - "intake_view": intake_view, - }, + data={**request.data, "intake_view": intake_view}, context={"workspace_id": workspace.id}, partial=True, ) @@ -457,8 +405,7 @@ class ProjectViewSet(BaseViewSet): serializer.save() if intake_view: intake = Intake.objects.filter( - project=project, - is_default=True, + project=project, is_default=True ).first() if not intake: Intake.objects.create( @@ -477,11 +424,7 @@ class ProjectViewSet(BaseViewSet): is_triage=True, ) - project = ( - self.get_queryset() - .filter(pk=serializer.data["id"]) - .first() - ) + project = self.get_queryset().filter(pk=serializer.data["id"]).first() model_activity.delay( model_name="project", @@ -494,9 +437,7 @@ class ProjectViewSet(BaseViewSet): ) serializer = ProjectListSerializer(project) return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError as e: if "already exists" in str(e): @@ -506,8 +447,7 @@ class ProjectViewSet(BaseViewSet): ) except (Project.DoesNotExist, Workspace.DoesNotExist): return Response( - {"error": "Project does not exist"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project does not exist"}, status=status.HTTP_404_NOT_FOUND ) except serializers.ValidationError: return Response( @@ -518,10 +458,7 @@ class ProjectViewSet(BaseViewSet): def destroy(self, request, slug, pk): if ( WorkspaceMember.objects.filter( - member=request.user, - workspace__slug=slug, - is_active=True, - role=20, + member=request.user, workspace__slug=slug, is_active=True, role=20 ).exists() or ProjectMember.objects.filter( member=request.user, @@ -535,16 +472,10 @@ class ProjectViewSet(BaseViewSet): project.delete() # Delete the project members - DeployBoard.objects.filter( - project_id=pk, - workspace__slug=slug, - ).delete() + DeployBoard.objects.filter(project_id=pk, workspace__slug=slug).delete() # Delete the user favorite - UserFavorite.objects.filter( - project_id=pk, - workspace__slug=slug, - ).delete() + UserFavorite.objects.filter(project_id=pk, workspace__slug=slug).delete() return Response(status=status.HTTP_204_NO_CONTENT) else: @@ -555,19 +486,14 @@ class ProjectViewSet(BaseViewSet): class ProjectArchiveUnarchiveEndpoint(BaseAPIView): - @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def post(self, request, slug, project_id): project = Project.objects.get(pk=project_id, workspace__slug=slug) project.archived_at = timezone.now() project.save() - UserFavorite.objects.filter( - workspace__slug=slug, - project=project_id, - ).delete() + UserFavorite.objects.filter(workspace__slug=slug, project=project_id).delete() return Response( - {"archived_at": str(project.archived_at)}, - status=status.HTTP_200_OK, + {"archived_at": str(project.archived_at)}, status=status.HTTP_200_OK ) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) @@ -585,8 +511,7 @@ class ProjectIdentifierEndpoint(BaseAPIView): if name == "": return Response( - {"error": "Name is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST ) exists = ProjectIdentifier.objects.filter( @@ -594,8 +519,7 @@ class ProjectIdentifierEndpoint(BaseAPIView): ).values("id", "name", "project") return Response( - {"exists": len(exists), "identifiers": exists}, - status=status.HTTP_200_OK, + {"exists": len(exists), "identifiers": exists}, status=status.HTTP_200_OK ) @allow_permission([ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") @@ -604,27 +528,18 @@ class ProjectIdentifierEndpoint(BaseAPIView): if name == "": return Response( - {"error": "Name is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST ) - if Project.objects.filter( - identifier=name, workspace__slug=slug - ).exists(): + if Project.objects.filter(identifier=name, workspace__slug=slug).exists(): return Response( - { - "error": "Cannot delete an identifier of an existing project" - }, + {"error": "Cannot delete an identifier of an existing project"}, status=status.HTTP_400_BAD_REQUEST, ) - ProjectIdentifier.objects.filter( - name=name, workspace__slug=slug - ).delete() + ProjectIdentifier.objects.filter(name=name, workspace__slug=slug).delete() - return Response( - status=status.HTTP_204_NO_CONTENT, - ) + return Response(status=status.HTTP_204_NO_CONTENT) class ProjectUserViewsEndpoint(BaseAPIView): @@ -632,15 +547,11 @@ class ProjectUserViewsEndpoint(BaseAPIView): project = Project.objects.get(pk=project_id, workspace__slug=slug) project_member = ProjectMember.objects.filter( - member=request.user, - project=project, - is_active=True, + member=request.user, project=project, is_active=True ).first() if project_member is None: - return Response( - {"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN - ) + return Response({"error": "Forbidden"}, status=status.HTTP_403_FORBIDDEN) view_props = project_member.view_props default_props = project_member.default_props @@ -648,12 +559,8 @@ class ProjectUserViewsEndpoint(BaseAPIView): sort_order = project_member.sort_order project_member.view_props = request.data.get("view_props", view_props) - project_member.default_props = request.data.get( - "default_props", default_props - ) - project_member.preferences = request.data.get( - "preferences", preferences - ) + project_member.default_props = request.data.get("default_props", default_props) + project_member.preferences = request.data.get("preferences", preferences) project_member.sort_order = request.data.get("sort_order", sort_order) project_member.save() @@ -701,9 +608,7 @@ class ProjectFavoritesViewSet(BaseViewSet): class ProjectPublicCoverImagesEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] # Cache the below api for 24 hours @cache_response(60 * 60 * 24, user=False) @@ -746,17 +651,13 @@ class ProjectPublicCoverImagesEndpoint(BaseAPIView): class DeployBoardViewSet(BaseViewSet): - permission_classes = [ - ProjectMemberPermission, - ] + permission_classes = [ProjectMemberPermission] serializer_class = DeployBoardSerializer model = DeployBoard def list(self, request, slug, project_id): project_deploy_board = DeployBoard.objects.filter( - entity_name="project", - entity_identifier=project_id, - workspace__slug=slug, + entity_name="project", entity_identifier=project_id, workspace__slug=slug ).first() serializer = DeployBoardSerializer(project_deploy_board) @@ -779,9 +680,7 @@ class DeployBoardViewSet(BaseViewSet): ) project_deploy_board, _ = DeployBoard.objects.get_or_create( - entity_name="project", - entity_identifier=project_id, - project_id=project_id, + entity_name="project", entity_identifier=project_id, project_id=project_id ) project_deploy_board.intake = intake project_deploy_board.view_props = views diff --git a/apiserver/plane/app/views/project/invite.py b/apiserver/plane/app/views/project/invite.py index b48602cec..af8c6084b 100644 --- a/apiserver/plane/app/views/project/invite.py +++ b/apiserver/plane/app/views/project/invite.py @@ -52,24 +52,19 @@ class ProjectInvitationsViewset(BaseViewSet): # Check if email is provided if not emails: return Response( - {"error": "Emails are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST ) for email in emails: workspace_role = WorkspaceMember.objects.filter( - workspace__slug=slug, - member__email=email.get("email"), - is_active=True, + workspace__slug=slug, member__email=email.get("email"), is_active=True ).role - if workspace_role in [5, 20] and workspace_role != email.get( - "role", 5 - ): + if workspace_role in [5, 20] and workspace_role != email.get("role", 5): return Response( { "error": "You cannot invite a user with different role than workspace role" - }, + } ) workspace = Workspace.objects.get(slug=slug) @@ -84,10 +79,7 @@ class ProjectInvitationsViewset(BaseViewSet): project_id=project_id, workspace_id=workspace.id, token=jwt.encode( - { - "email": email, - "timestamp": datetime.now().timestamp(), - }, + {"email": email, "timestamp": datetime.now().timestamp()}, settings.SECRET_KEY, algorithm="HS256", ), @@ -120,10 +112,7 @@ class ProjectInvitationsViewset(BaseViewSet): ) return Response( - { - "message": "Email sent successfully", - }, - status=status.HTTP_200_OK, + {"message": "Email sent successfully"}, status=status.HTTP_200_OK ) @@ -144,9 +133,7 @@ class UserProjectInvitationsViewset(BaseViewSet): # Get the workspace user role workspace_member = WorkspaceMember.objects.get( - member=request.user, - workspace__slug=slug, - is_active=True, + member=request.user, workspace__slug=slug, is_active=True ) workspace_role = workspace_member.role @@ -154,9 +141,7 @@ class UserProjectInvitationsViewset(BaseViewSet): # If the user was already part of workspace _ = ProjectMember.objects.filter( - workspace__slug=slug, - project_id__in=project_ids, - member=request.user, + workspace__slug=slug, project_id__in=project_ids, member=request.user ).update(is_active=True) ProjectMember.objects.bulk_create( @@ -187,21 +172,16 @@ class UserProjectInvitationsViewset(BaseViewSet): ) return Response( - {"message": "Projects joined successfully"}, - status=status.HTTP_201_CREATED, + {"message": "Projects joined successfully"}, status=status.HTTP_201_CREATED ) class ProjectJoinEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def post(self, request, slug, project_id, pk): project_invite = ProjectMemberInvite.objects.get( - pk=pk, - project_id=project_id, - workspace__slug=slug, + pk=pk, project_id=project_id, workspace__slug=slug ) email = request.data.get("email", "") @@ -230,11 +210,7 @@ class ProjectJoinEndpoint(BaseAPIView): _ = WorkspaceMember.objects.create( workspace_id=project_invite.workspace_id, member=user, - role=( - 15 - if project_invite.role >= 15 - else project_invite.role - ), + role=(15 if project_invite.role >= 15 else project_invite.role), ) else: # Else make him active diff --git a/apiserver/plane/app/views/project/member.py b/apiserver/plane/app/views/project/member.py index ccb5e7521..f46bf6c50 100644 --- a/apiserver/plane/app/views/project/member.py +++ b/apiserver/plane/app/views/project/member.py @@ -36,20 +36,13 @@ class ProjectMemberViewSet(BaseViewSet): def get_permissions(self): if self.action == "leave": - self.permission_classes = [ - ProjectLitePermission, - ] + self.permission_classes = [ProjectLitePermission] else: - self.permission_classes = [ - ProjectMemberPermission, - ] + self.permission_classes = [ProjectMemberPermission] return super(ProjectMemberViewSet, self).get_permissions() - search_fields = [ - "member__display_name", - "member__first_name", - ] + search_fields = ["member__display_name", "member__first_name"] def get_queryset(self): return self.filter_queryset( @@ -91,14 +84,9 @@ class ProjectMemberViewSet(BaseViewSet): # check the workspace role of the new user for member in member_roles: workspace_member_role = WorkspaceMember.objects.get( - workspace__slug=slug, - member=member, - is_active=True, + workspace__slug=slug, member=member, is_active=True ).role - if workspace_member_role in [20] and member_roles.get(member) in [ - 5, - 15, - ]: + if workspace_member_role in [20] and member_roles.get(member) in [5, 15]: return Response( { "error": "You cannot add a user with role lower than the workspace role" @@ -106,10 +94,7 @@ class ProjectMemberViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - if workspace_member_role in [5] and member_roles.get(member) in [ - 15, - 20, - ]: + if workspace_member_role in [5] and member_roles.get(member) in [15, 20]: return Response( { "error": "You cannot add a user with role higher than the workspace role" @@ -143,13 +128,11 @@ class ProjectMemberViewSet(BaseViewSet): # Loop through requested members for member in members: - # Get the sort orders of the member sort_order = [ project_member.get("sort_order") for project_member in project_members - if str(project_member.get("member_id")) - == str(member.get("member_id")) + if str(project_member.get("member_id")) == str(member.get("member_id")) ] # Create a new project member bulk_project_members.append( @@ -158,9 +141,7 @@ class ProjectMemberViewSet(BaseViewSet): role=member.get("role", 5), project_id=project_id, workspace_id=project.workspace_id, - sort_order=( - sort_order[0] - 10000 if len(sort_order) else 65535 - ), + sort_order=(sort_order[0] - 10000 if len(sort_order) else 65535), ) ) # Create a new issue property @@ -174,9 +155,7 @@ class ProjectMemberViewSet(BaseViewSet): # Bulk create the project members and issue properties project_members = ProjectMember.objects.bulk_create( - bulk_project_members, - batch_size=10, - ignore_conflicts=True, + bulk_project_members, batch_size=10, ignore_conflicts=True ) _ = IssueUserProperty.objects.bulk_create( @@ -219,10 +198,7 @@ class ProjectMemberViewSet(BaseViewSet): @allow_permission([ROLE.ADMIN]) def partial_update(self, request, slug, project_id, pk): project_member = ProjectMember.objects.get( - pk=pk, - workspace__slug=slug, - project_id=project_id, - is_active=True, + pk=pk, workspace__slug=slug, project_id=project_id, is_active=True ) if request.user.id == project_member.member_id: return Response( @@ -238,9 +214,7 @@ class ProjectMemberViewSet(BaseViewSet): ) workspace_role = WorkspaceMember.objects.get( - workspace__slug=slug, - member=project_member.member, - is_active=True, + workspace__slug=slug, member=project_member.member, is_active=True ).role if workspace_role in [5] and int( request.data.get("role", project_member.role) @@ -258,9 +232,7 @@ class ProjectMemberViewSet(BaseViewSet): > requested_project_member.role ): return Response( - { - "error": "You cannot update a role that is higher than your own role" - }, + {"error": "You cannot update a role that is higher than your own role"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -300,9 +272,7 @@ class ProjectMemberViewSet(BaseViewSet): # User cannot deactivate higher role if requesting_project_member.role < project_member.role: return Response( - { - "error": "You cannot remove a user having role higher than you" - }, + {"error": "You cannot remove a user having role higher than you"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -323,16 +293,13 @@ class ProjectMemberViewSet(BaseViewSet): if ( project_member.role == 20 and not ProjectMember.objects.filter( - workspace__slug=slug, - project_id=project_id, - role=20, - is_active=True, + workspace__slug=slug, project_id=project_id, role=20, is_active=True ).count() > 1 ): return Response( { - "error": "You cannot leave the project as your the only admin of the project you will have to either delete the project or create an another admin", + "error": "You cannot leave the project as your the only admin of the project you will have to either delete the project or create an another admin" }, status=status.HTTP_400_BAD_REQUEST, ) @@ -343,9 +310,7 @@ class ProjectMemberViewSet(BaseViewSet): class AddTeamToProjectEndpoint(BaseAPIView): - permission_classes = [ - ProjectBasePermission, - ] + permission_classes = [ProjectBasePermission] def post(self, request, slug, project_id): team_members = TeamMember.objects.filter( @@ -354,8 +319,7 @@ class AddTeamToProjectEndpoint(BaseAPIView): if len(team_members) == 0: return Response( - {"error": "No such team exists"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "No such team exists"}, status=status.HTTP_400_BAD_REQUEST ) workspace = Workspace.objects.get(slug=slug) @@ -406,19 +370,14 @@ class ProjectMemberUserEndpoint(BaseAPIView): class UserProjectRolesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceUserPermission, - ] + permission_classes = [WorkspaceUserPermission] def get(self, request, slug): project_members = ProjectMember.objects.filter( - workspace__slug=slug, - member_id=request.user.id, - is_active=True, + workspace__slug=slug, member_id=request.user.id, is_active=True ).values("project_id", "role") project_members = { - str(member["project_id"]): member["role"] - for member in project_members + str(member["project_id"]): member["role"] for member in project_members } return Response(project_members, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/search/base.py b/apiserver/plane/app/views/search/base.py index 714ccacc6..5161103f5 100644 --- a/apiserver/plane/app/views/search/base.py +++ b/apiserver/plane/app/views/search/base.py @@ -36,9 +36,7 @@ class GlobalSearchEndpoint(BaseAPIView): for field in fields: q |= Q(**{f"{field}__icontains": query}) return ( - Workspace.objects.filter( - q, workspace_member__member=self.request.user - ) + Workspace.objects.filter(q, workspace_member__member=self.request.user) .distinct() .values("name", "id", "slug") ) @@ -110,11 +108,7 @@ class GlobalSearchEndpoint(BaseAPIView): cycles = cycles.filter(project_id=project_id) return cycles.distinct().values( - "name", - "id", - "project_id", - "project__identifier", - "workspace__slug", + "name", "id", "project_id", "project__identifier", "workspace__slug" ) def filter_modules(self, query, slug, project_id, workspace_search): @@ -135,11 +129,7 @@ class GlobalSearchEndpoint(BaseAPIView): modules = modules.filter(project_id=project_id) return modules.distinct().values( - "name", - "id", - "project_id", - "project__identifier", - "workspace__slug", + "name", "id", "project_id", "project__identifier", "workspace__slug" ) def filter_pages(self, query, slug, project_id, workspace_search): @@ -159,12 +149,10 @@ class GlobalSearchEndpoint(BaseAPIView): .annotate( project_ids=Coalesce( ArrayAgg( - "projects__id", - distinct=True, - filter=~Q(projects__id=True), + "projects__id", distinct=True, filter=~Q(projects__id=True) ), Value([], output_field=ArrayField(UUIDField())), - ), + ) ) .annotate( project_identifiers=Coalesce( @@ -174,26 +162,21 @@ class GlobalSearchEndpoint(BaseAPIView): filter=~Q(projects__id=True), ), Value([], output_field=ArrayField(CharField())), - ), + ) ) ) if workspace_search == "false" and project_id: project_subquery = ProjectPage.objects.filter( - page_id=OuterRef("id"), - project_id=project_id, + page_id=OuterRef("id"), project_id=project_id ).values_list("project_id", flat=True)[:1] - pages = pages.annotate( - project_id=Subquery(project_subquery) - ).filter(project_id=project_id) + pages = pages.annotate(project_id=Subquery(project_subquery)).filter( + project_id=project_id + ) return pages.distinct().values( - "name", - "id", - "project_ids", - "project_identifiers", - "workspace__slug", + "name", "id", "project_ids", "project_identifiers", "workspace__slug" ) def filter_views(self, query, slug, project_id, workspace_search): @@ -214,18 +197,12 @@ class GlobalSearchEndpoint(BaseAPIView): issue_views = issue_views.filter(project_id=project_id) return issue_views.distinct().values( - "name", - "id", - "project_id", - "project__identifier", - "workspace__slug", + "name", "id", "project_id", "project__identifier", "workspace__slug" ) def get(self, request, slug): query = request.query_params.get("search", False) - workspace_search = request.query_params.get( - "workspace_search", "false" - ) + workspace_search = request.query_params.get("workspace_search", "false") project_id = request.query_params.get("project_id", False) if not query: diff --git a/apiserver/plane/app/views/search/issue.py b/apiserver/plane/app/views/search/issue.py index 4ae5b5ea1..13fdc4eff 100644 --- a/apiserver/plane/app/views/search/issue.py +++ b/apiserver/plane/app/views/search/issue.py @@ -16,9 +16,7 @@ from plane.utils.issue_search import search_issues class IssueSearchEndpoint(BaseAPIView): def get(self, request, slug, project_id): query = request.query_params.get("search", False) - workspace_search = request.query_params.get( - "workspace_search", "false" - ) + workspace_search = request.query_params.get("workspace_search", "false") parent = request.query_params.get("parent", "false") issue_relation = request.query_params.get("issue_relation", "false") cycle = request.query_params.get("cycle", "false") @@ -45,9 +43,7 @@ class IssueSearchEndpoint(BaseAPIView): issue = Issue.issue_objects.filter(pk=issue_id).first() if issue: issues = issues.filter( - ~Q(pk=issue_id), - ~Q(pk=issue.parent_id), - ~Q(parent_id=issue_id), + ~Q(pk=issue_id), ~Q(pk=issue.parent_id), ~Q(parent_id=issue_id) ) if issue_relation == "true" and issue_id: issue = Issue.issue_objects.filter(pk=issue_id).first() @@ -72,8 +68,7 @@ class IssueSearchEndpoint(BaseAPIView): if cycle == "true": issues = issues.exclude( - Q(issue_cycle__isnull=False) - & Q(issue_cycle__deleted_at__isnull=True) + Q(issue_cycle__isnull=False) & Q(issue_cycle__deleted_at__isnull=True) ) if module: @@ -86,10 +81,7 @@ class IssueSearchEndpoint(BaseAPIView): issues = issues.filter(target_date__isnull=True) if ProjectMember.objects.filter( - project_id=project_id, - member=self.request.user, - is_active=True, - role=5, + project_id=project_id, member=self.request.user, is_active=True, role=5 ).exists(): issues = issues.filter(created_by=self.request.user) diff --git a/apiserver/plane/app/views/state/base.py b/apiserver/plane/app/views/state/base.py index fd94a8dae..00f4813e6 100644 --- a/apiserver/plane/app/views/state/base.py +++ b/apiserver/plane/app/views/state/base.py @@ -8,10 +8,7 @@ from rest_framework import status # Module imports from .. import BaseViewSet from plane.app.serializers import StateSerializer -from plane.app.permissions import ( - ROLE, - allow_permission -) +from plane.app.permissions import ROLE, allow_permission from plane.db.models import State, Issue from plane.utils.cache import invalidate_cache @@ -37,9 +34,7 @@ class StateViewSet(BaseViewSet): .distinct() ) - @invalidate_cache( - path="workspaces/:slug/states/", url_params=True, user=False - ) + @invalidate_cache(path="workspaces/:slug/states/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) def create(self, request, slug, project_id): serializer = StateSerializer(data=request.data) @@ -62,9 +57,7 @@ class StateViewSet(BaseViewSet): return Response(state_dict, status=status.HTTP_200_OK) return Response(states, status=status.HTTP_200_OK) - @invalidate_cache( - path="workspaces/:slug/states/", url_params=True, user=False - ) + @invalidate_cache(path="workspaces/:slug/states/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) def mark_as_default(self, request, slug, project_id, pk): # Select all the states which are marked as default @@ -76,16 +69,11 @@ class StateViewSet(BaseViewSet): ).update(default=True) return Response(status=status.HTTP_204_NO_CONTENT) - @invalidate_cache( - path="workspaces/:slug/states/", url_params=True, user=False - ) + @invalidate_cache(path="workspaces/:slug/states/", url_params=True, user=False) @allow_permission([ROLE.ADMIN]) def destroy(self, request, slug, project_id, pk): state = State.objects.get( - is_triage=False, - pk=pk, - project_id=project_id, - workspace__slug=slug, + is_triage=False, pk=pk, project_id=project_id, workspace__slug=slug ) if state.default: @@ -99,9 +87,7 @@ class StateViewSet(BaseViewSet): if issue_exist: return Response( - { - "error": "The state is not empty, only empty states can be deleted" - }, + {"error": "The state is not empty, only empty states can be deleted"}, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/app/views/user/base.py b/apiserver/plane/app/views/user/base.py index 5c036929c..4eca872f3 100644 --- a/apiserver/plane/app/views/user/base.py +++ b/apiserver/plane/app/views/user/base.py @@ -52,10 +52,7 @@ class UserEndpoint(BaseViewSet): @method_decorator(vary_on_cookie) def retrieve(self, request): serialized_data = UserMeSerializer(request.user).data - return Response( - serialized_data, - status=status.HTTP_200_OK, - ) + return Response(serialized_data, status=status.HTTP_200_OK) @method_decorator(cache_control(private=True, max_age=12)) @method_decorator(vary_on_cookie) @@ -68,9 +65,7 @@ class UserEndpoint(BaseViewSet): is_admin = InstanceAdmin.objects.filter( instance=instance, user=request.user ).exists() - return Response( - {"is_instance_admin": is_admin}, status=status.HTTP_200_OK - ) + return Response({"is_instance_admin": is_admin}, status=status.HTTP_200_OK) def partial_update(self, request, *args, **kwargs): return super().partial_update(request, *args, **kwargs) @@ -96,10 +91,7 @@ class UserEndpoint(BaseViewSet): ).annotate( other_admin_exists=Count( Case( - When( - Q(role=20, is_active=True) & ~Q(member=request.user), - then=1, - ), + When(Q(role=20, is_active=True) & ~Q(member=request.user), then=1), default=0, output_field=IntegerField(), ) @@ -124,10 +116,7 @@ class UserEndpoint(BaseViewSet): ).annotate( other_admin_exists=Count( Case( - When( - Q(role=20, is_active=True) & ~Q(member=request.user), - then=1, - ), + When(Q(role=20, is_active=True) & ~Q(member=request.user), then=1), default=0, output_field=IntegerField(), ) @@ -136,9 +125,7 @@ class UserEndpoint(BaseViewSet): ) for workspace in workspaces: - if workspace.other_admin_exists > 0 or ( - workspace.total_members == 1 - ): + if workspace.other_admin_exists > 0 or (workspace.total_members == 1): workspace.is_active = False workspaces_to_deactivate.append(workspace) else: @@ -158,9 +145,7 @@ class UserEndpoint(BaseViewSet): ) # Delete all workspace invites - WorkspaceMemberInvite.objects.filter( - email=user.email, - ).delete() + WorkspaceMemberInvite.objects.filter(email=user.email).delete() # Delete all sessions Session.objects.filter(user_id=request.user.id).delete() @@ -191,9 +176,7 @@ class UserEndpoint(BaseViewSet): user.save() # Send an email to the user - user_deactivation_email.delay( - base_host(request=request, is_app=True), user.id - ) + user_deactivation_email.delay(base_host(request=request, is_app=True), user.id) # Logout the user logout(request) @@ -201,10 +184,7 @@ class UserEndpoint(BaseViewSet): class UserSessionEndpoint(BaseAPIView): - - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request): if request.user.is_authenticated: @@ -214,41 +194,30 @@ class UserSessionEndpoint(BaseAPIView): data["user"] = serializer.data return Response(data, status=status.HTTP_200_OK) else: - return Response( - {"is_authenticated": False}, status=status.HTTP_200_OK - ) + return Response({"is_authenticated": False}, status=status.HTTP_200_OK) class UpdateUserOnBoardedEndpoint(BaseAPIView): - def patch(self, request): profile = Profile.objects.get(user_id=request.user.id) profile.is_onboarded = request.data.get("is_onboarded", False) profile.save() - return Response( - {"message": "Updated successfully"}, status=status.HTTP_200_OK - ) + return Response({"message": "Updated successfully"}, status=status.HTTP_200_OK) class UpdateUserTourCompletedEndpoint(BaseAPIView): - def patch(self, request): profile = Profile.objects.get(user_id=request.user.id) - profile.is_tour_completed = request.data.get( - "is_tour_completed", False - ) + profile.is_tour_completed = request.data.get("is_tour_completed", False) profile.save() - return Response( - {"message": "Updated successfully"}, status=status.HTTP_200_OK - ) + return Response({"message": "Updated successfully"}, status=status.HTTP_200_OK) class UserActivityEndpoint(BaseAPIView, BasePaginator): - def get(self, request): - queryset = IssueActivity.objects.filter( - actor=request.user - ).select_related("actor", "workspace", "issue", "project") + queryset = IssueActivity.objects.filter(actor=request.user).select_related( + "actor", "workspace", "issue", "project" + ) return self.paginate( order_by=request.GET.get("order_by", "-created_at"), @@ -261,7 +230,6 @@ class UserActivityEndpoint(BaseAPIView, BasePaginator): class AccountEndpoint(BaseAPIView): - def get(self, request, pk=None): if pk: account = Account.objects.get(pk=pk, user=request.user) @@ -270,10 +238,7 @@ class AccountEndpoint(BaseAPIView): account = Account.objects.filter(user=request.user) serializer = AccountSerializer(account, many=True) - return Response( - serializer.data, - status=status.HTTP_200_OK, - ) + return Response(serializer.data, status=status.HTTP_200_OK) def delete(self, request, pk): account = Account.objects.get(pk=pk, user=request.user) @@ -291,9 +256,7 @@ class ProfileEndpoint(BaseAPIView): def patch(self, request): profile = Profile.objects.get(user=request.user) - serializer = ProfileSerializer( - profile, data=request.data, partial=True - ) + serializer = ProfileSerializer(profile, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 53c962e70..ca4048b70 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -1,16 +1,7 @@ # Django imports from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import ArrayField -from django.db.models import ( - Exists, - F, - Func, - OuterRef, - Q, - UUIDField, - Value, - Subquery, -) +from django.db.models import Exists, F, Func, OuterRef, Q, UUIDField, Value, Subquery from django.db.models.functions import Coalesce from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page @@ -21,13 +12,8 @@ from rest_framework import status from rest_framework.response import Response # Module imports -from plane.app.permissions import ( - allow_permission, - ROLE, -) -from plane.app.serializers import ( - IssueViewSerializer, -) +from plane.app.permissions import allow_permission, ROLE +from plane.app.serializers import IssueViewSerializer from plane.db.models import ( Issue, FileAsset, @@ -46,15 +32,10 @@ from plane.utils.grouper import ( ) from plane.utils.issue_filters import issue_filters from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator from plane.bgtasks.recent_visited_task import recent_visited_task from .. import BaseViewSet -from plane.db.models import ( - UserFavorite, -) +from plane.db.models import UserFavorite class WorkspaceViewViewSet(BaseViewSet): @@ -78,21 +59,13 @@ class WorkspaceViewViewSet(BaseViewSet): ) @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def list(self, request, slug): queryset = self.get_queryset() - fields = [ - field - for field in request.GET.get("fields", "").split(",") - if field - ] + fields = [field for field in request.GET.get("fields", "").split(",") if field] if WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role=5, - is_active=True, + workspace__slug=slug, member=request.user, role=5, is_active=True ).exists(): queryset = queryset.filter(owned_by=request.user) views = IssueViewSerializer( @@ -106,22 +79,18 @@ class WorkspaceViewViewSet(BaseViewSet): def partial_update(self, request, slug, pk): with transaction.atomic(): workspace_view = IssueView.objects.select_for_update().get( - pk=pk, - workspace__slug=slug, + pk=pk, workspace__slug=slug ) if workspace_view.is_locked: return Response( - {"error": "view is locked"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "view is locked"}, status=status.HTTP_400_BAD_REQUEST ) # Only update the view if owner is updating if workspace_view.owned_by_id != request.user.id: return Response( - { - "error": "Only the owner of the view can update the view" - }, + {"error": "Only the owner of the view can update the view"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -132,9 +101,7 @@ class WorkspaceViewViewSet(BaseViewSet): if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, slug, pk): issue_view = self.get_queryset().filter(pk=pk).first() @@ -146,33 +113,18 @@ class WorkspaceViewViewSet(BaseViewSet): entity_identifier=pk, user_id=request.user.id, ) - return Response( - serializer.data, - status=status.HTTP_200_OK, - ) + return Response(serializer.data, status=status.HTTP_200_OK) @allow_permission( - allowed_roles=[], - level="WORKSPACE", - creator=True, - model=IssueView, + allowed_roles=[], level="WORKSPACE", creator=True, model=IssueView ) def destroy(self, request, slug, pk): - workspace_view = IssueView.objects.get( - pk=pk, - workspace__slug=slug, - ) + workspace_view = IssueView.objects.get(pk=pk, workspace__slug=slug) workspace_member = WorkspaceMember.objects.filter( - workspace__slug=slug, - member=request.user, - role=20, - is_active=True, + workspace__slug=slug, member=request.user, role=20, is_active=True ) - if ( - workspace_member.exists() - or workspace_view.owned_by == request.user - ): + if workspace_member.exists() or workspace_view.owned_by == request.user: workspace_view.delete() # Delete the user favorite view UserFavorite.objects.filter( @@ -193,9 +145,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): def get_queryset(self): return ( Issue.issue_objects.annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -230,9 +180,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -244,7 +192,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -278,8 +226,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): @method_decorator(gzip_page) @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def list(self, request, slug): filters = issue_filters(request.query_params, "GET") @@ -318,8 +265,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -328,9 +274,7 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) if group_by: @@ -350,16 +294,11 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( - field=group_by, - slug=slug, - project_id=None, - filters=filters, + field=group_by, slug=slug, project_id=None, filters=filters ), sub_group_by_fields=issue_group_values( field=sub_group_by, @@ -386,16 +325,11 @@ class WorkspaceViewIssuesViewSet(BaseViewSet): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( - field=group_by, - slug=slug, - project_id=None, - filters=filters, + field=group_by, slug=slug, project_id=None, filters=filters ), group_by_field_name=group_by, count_filter=Q( @@ -425,8 +359,7 @@ class IssueViewViewSet(BaseViewSet): def perform_create(self, serializer): serializer.save( - project_id=self.kwargs.get("project_id"), - owned_by=self.request.user, + project_id=self.kwargs.get("project_id"), owned_by=self.request.user ) def get_queryset(self): @@ -470,11 +403,7 @@ class IssueViewViewSet(BaseViewSet): and not project.guest_view_all_features ): queryset = queryset.filter(owned_by=request.user) - fields = [ - field - for field in request.GET.get("fields", "").split(",") - if field - ] + fields = [field for field in request.GET.get("fields", "").split(",") if field] views = IssueViewSerializer( queryset, many=True, fields=fields if fields else None ).data @@ -482,9 +411,7 @@ class IssueViewViewSet(BaseViewSet): @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) def retrieve(self, request, slug, project_id, pk): - issue_view = ( - self.get_queryset().filter(pk=pk, project_id=project_id).first() - ) + issue_view = self.get_queryset().filter(pk=pk, project_id=project_id).first() project = Project.objects.get(id=project_id) """ if the role is guest and guest_view_all_features is false and owned by is not @@ -515,10 +442,7 @@ class IssueViewViewSet(BaseViewSet): entity_identifier=pk, user_id=request.user.id, ) - return Response( - serializer.data, - status=status.HTTP_200_OK, - ) + return Response(serializer.data, status=status.HTTP_200_OK) @allow_permission(allowed_roles=[], creator=True, model=IssueView) def partial_update(self, request, slug, project_id, pk): @@ -529,16 +453,13 @@ class IssueViewViewSet(BaseViewSet): if issue_view.is_locked: return Response( - {"error": "view is locked"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "view is locked"}, status=status.HTTP_400_BAD_REQUEST ) # Only update the view if owner is updating if issue_view.owned_by_id != request.user.id: return Response( - { - "error": "Only the owner of the view can update the view" - }, + {"error": "Only the owner of the view can update the view"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -549,18 +470,12 @@ class IssueViewViewSet(BaseViewSet): if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(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, - project_id=project_id, - workspace__slug=slug, + pk=pk, project_id=project_id, workspace__slug=slug ) if ( ProjectMember.objects.filter( diff --git a/apiserver/plane/app/views/webhook/base.py b/apiserver/plane/app/views/webhook/base.py index 5581b6aa3..d62838fab 100644 --- a/apiserver/plane/app/views/webhook/base.py +++ b/apiserver/plane/app/views/webhook/base.py @@ -14,7 +14,6 @@ from plane.app.serializers import WebhookSerializer, WebhookLogSerializer class WebhookEndpoint(BaseAPIView): - @allow_permission(allowed_roles=[ROLE.ADMIN], level="WORKSPACE") def post(self, request, slug): workspace = Workspace.objects.get(slug=slug) @@ -24,12 +23,8 @@ class WebhookEndpoint(BaseAPIView): ) if serializer.is_valid(): serializer.save(workspace_id=workspace.id) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) except IntegrityError as e: if "already exists" in str(e): return Response( @@ -112,7 +107,6 @@ class WebhookEndpoint(BaseAPIView): class WebhookSecretRegenerateEndpoint(BaseAPIView): - @allow_permission(allowed_roles=[ROLE.ADMIN], level="WORKSPACE") def post(self, request, slug, pk): webhook = Webhook.objects.get(workspace__slug=slug, pk=pk) @@ -123,7 +117,6 @@ class WebhookSecretRegenerateEndpoint(BaseAPIView): class WebhookLogsEndpoint(BaseAPIView): - @allow_permission(allowed_roles=[ROLE.ADMIN], level="WORKSPACE") def get(self, request, slug, webhook_id): webhook_logs = WebhookLog.objects.filter( diff --git a/apiserver/plane/app/views/workspace/base.py b/apiserver/plane/app/views/workspace/base.py index bd552368a..6ffb643a9 100644 --- a/apiserver/plane/app/views/workspace/base.py +++ b/apiserver/plane/app/views/workspace/base.py @@ -5,14 +5,7 @@ from datetime import date from dateutil.relativedelta import relativedelta from django.db import IntegrityError -from django.db.models import ( - Count, - F, - Func, - OuterRef, - Prefetch, - Q, -) +from django.db.models import Count, F, Func, OuterRef, Prefetch, Q from django.db.models.fields import DateField from django.db.models.functions import Cast, ExtractDay, ExtractWeek @@ -31,10 +24,7 @@ from plane.app.permissions import ( ) # Module imports -from plane.app.serializers import ( - WorkSpaceSerializer, - WorkspaceThemeSerializer, -) +from plane.app.serializers import WorkSpaceSerializer, WorkspaceThemeSerializer from plane.app.views.base import BaseAPIView, BaseViewSet from plane.db.models import ( Issue, @@ -53,25 +43,17 @@ from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS class WorkSpaceViewSet(BaseViewSet): model = Workspace serializer_class = WorkSpaceSerializer - permission_classes = [ - WorkSpaceBasePermission, - ] + permission_classes = [WorkSpaceBasePermission] - search_fields = [ - "name", - ] - filterset_fields = [ - "owner", - ] + search_fields = ["name"] + filterset_fields = ["owner"] lookup_field = "slug" def get_queryset(self): member_count = ( WorkspaceMember.objects.filter( - workspace=OuterRef("id"), - member__is_bot=False, - is_active=True, + workspace=OuterRef("id"), member__is_bot=False, is_active=True ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -85,9 +67,7 @@ class WorkSpaceViewSet(BaseViewSet): .values("count") ) return ( - self.filter_queryset( - super().get_queryset().select_related("owner") - ) + self.filter_queryset(super().get_queryset().select_related("owner")) .order_by("name") .filter( workspace_member__member=self.request.user, @@ -113,9 +93,7 @@ class WorkSpaceViewSet(BaseViewSet): if len(name) > 80 or len(slug) > 48: return Response( - { - "error": "The maximum length for name is 80 and for slug is 48" - }, + {"error": "The maximum length for name is 80 and for slug is 48"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -128,9 +106,7 @@ class WorkSpaceViewSet(BaseViewSet): role=20, company_role=request.data.get("company_role", ""), ) - return Response( - serializer.data, status=status.HTTP_201_CREATED - ) + return Response(serializer.data, status=status.HTTP_201_CREATED) return Response( [serializer.errors[error][0] for error in serializer.errors], status=status.HTTP_400_BAD_REQUEST, @@ -143,23 +119,11 @@ class WorkSpaceViewSet(BaseViewSet): status=status.HTTP_410_GONE, ) - @allow_permission( - [ - ROLE.ADMIN, - ROLE.MEMBER, - ROLE.GUEST, - ], - level="WORKSPACE", - ) + @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE") def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs) - @allow_permission( - [ - ROLE.ADMIN, - ], - level="WORKSPACE", - ) + @allow_permission([ROLE.ADMIN], level="WORKSPACE") def partial_update(self, request, *args, **kwargs): return super().partial_update(request, *args, **kwargs) @@ -169,26 +133,16 @@ class WorkSpaceViewSet(BaseViewSet): class UserWorkSpacesEndpoint(BaseAPIView): - search_fields = [ - "name", - ] - filterset_fields = [ - "owner", - ] + search_fields = ["name"] + filterset_fields = ["owner"] @method_decorator(cache_control(private=True, max_age=12)) @method_decorator(vary_on_cookie) def get(self, request): - fields = [ - field - for field in request.GET.get("fields", "").split(",") - if field - ] + fields = [field for field in request.GET.get("fields", "").split(",") if field] member_count = ( WorkspaceMember.objects.filter( - workspace=OuterRef("id"), - member__is_bot=False, - is_active=True, + workspace=OuterRef("id"), member__is_bot=False, is_active=True ) .order_by() .annotate(count=Func(F("id"), function="Count")) @@ -215,8 +169,7 @@ class UserWorkSpacesEndpoint(BaseAPIView): .annotate(total_members=member_count) .annotate(total_issues=issue_count) .filter( - workspace_member__member=request.user, - workspace_member__is_active=True, + workspace_member__member=request.user, workspace_member__is_active=True ) .distinct() ) @@ -291,15 +244,12 @@ class UserWorkspaceDashboardEndpoint(BaseAPIView): ).count() completed_issues_count = Issue.issue_objects.filter( - workspace__slug=slug, - assignees__in=[request.user], - state__group="completed", + workspace__slug=slug, assignees__in=[request.user], state__group="completed" ).count() issues_due_week = ( Issue.issue_objects.filter( - workspace__slug=slug, - assignees__in=[request.user], + workspace__slug=slug, assignees__in=[request.user] ) .annotate(target_week=ExtractWeek("target_date")) .filter(target_week=timezone.now().date().isocalendar()[1]) @@ -349,18 +299,12 @@ class UserWorkspaceDashboardEndpoint(BaseAPIView): class WorkspaceThemeViewSet(BaseViewSet): - permission_classes = [ - WorkSpaceAdminPermission, - ] + permission_classes = [WorkSpaceAdminPermission] model = WorkspaceTheme serializer_class = WorkspaceThemeSerializer def get_queryset(self): - return ( - super() - .get_queryset() - .filter(workspace__slug=self.kwargs.get("slug")) - ) + return super().get_queryset().filter(workspace__slug=self.kwargs.get("slug")) def create(self, request, slug): workspace = Workspace.objects.get(slug=slug) @@ -372,9 +316,7 @@ class WorkspaceThemeViewSet(BaseViewSet): class ExportWorkspaceUserActivityEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceEntityPermission, - ] + permission_classes = [WorkspaceEntityPermission] def generate_csv_from_rows(self, rows): """Generate CSV buffer from rows.""" @@ -385,11 +327,9 @@ class ExportWorkspaceUserActivityEndpoint(BaseAPIView): return csv_buffer def post(self, request, slug, user_id): - if not request.data.get("date"): return Response( - {"error": "Date is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Date is required"}, status=status.HTTP_400_BAD_REQUEST ) user_activities = IssueActivity.objects.filter( diff --git a/apiserver/plane/app/views/workspace/cycle.py b/apiserver/plane/app/views/workspace/cycle.py index 6040827ad..ec08f47c9 100644 --- a/apiserver/plane/app/views/workspace/cycle.py +++ b/apiserver/plane/app/views/workspace/cycle.py @@ -1,8 +1,5 @@ # Django imports -from django.db.models import ( - Q, - Count, -) +from django.db.models import Q, Count # Third party modules from rest_framework import status @@ -16,9 +13,7 @@ from plane.app.serializers.cycle import CycleSerializer class WorkspaceCyclesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceViewerPermission, - ] + permission_classes = [WorkspaceViewerPermission] def get(self, request, slug): cycles = ( diff --git a/apiserver/plane/app/views/workspace/draft.py b/apiserver/plane/app/views/workspace/draft.py index b2cb529fc..fa161cbab 100644 --- a/apiserver/plane/app/views/workspace/draft.py +++ b/apiserver/plane/app/views/workspace/draft.py @@ -7,13 +7,7 @@ from django.core import serializers from django.core.serializers.json import DjangoJSONEncoder from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import ArrayField -from django.db.models import ( - Q, - UUIDField, - Value, - Subquery, - OuterRef, -) +from django.db.models import Q, UUIDField, Value, Subquery, OuterRef from django.db.models.functions import Coalesce from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page @@ -51,9 +45,7 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): return ( DraftIssue.objects.filter(workspace__slug=self.kwargs.get("slug")) .select_related("workspace", "project", "state", "parent") - .prefetch_related( - "assignees", "labels", "draft_issue_module__module" - ) + .prefetch_related("assignees", "labels", "draft_issue_module__module") .annotate( cycle_id=Subquery( DraftIssueCycle.objects.filter( @@ -91,9 +83,7 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): distinct=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__archived_at__isnull=True) & Q(draft_issue_module__deleted_at__isnull=True) ), ), @@ -109,9 +99,7 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): def list(self, request, slug): filters = issue_filters(request.query_params, "GET") issues = ( - self.get_queryset() - .filter(created_by=request.user) - .order_by("-created_at") + self.get_queryset().filter(created_by=request.user).order_by("-created_at") ) issues = issues.filter(**filters) @@ -119,10 +107,7 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): return self.paginate( request=request, queryset=(issues), - on_results=lambda issues: DraftIssueSerializer( - issues, - many=True, - ).data, + on_results=lambda issues: DraftIssueSerializer(issues, many=True).data, ) @allow_permission( @@ -179,14 +164,11 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): level="WORKSPACE", ) def partial_update(self, request, slug, pk): - issue = ( - self.get_queryset().filter(pk=pk, created_by=request.user).first() - ) + issue = self.get_queryset().filter(pk=pk, created_by=request.user).first() if not issue: return Response( - {"error": "Issue not found"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Issue not found"}, status=status.HTTP_404_NOT_FOUND ) serializer = DraftIssueCreateSerializer( @@ -206,15 +188,10 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @allow_permission( - allowed_roles=[ROLE.ADMIN], - creator=True, - model=Issue, - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN], creator=True, model=Issue, level="WORKSPACE" ) def retrieve(self, request, slug, pk=None): - issue = ( - self.get_queryset().filter(pk=pk, created_by=request.user).first() - ) + issue = self.get_queryset().filter(pk=pk, created_by=request.user).first() if not issue: return Response( @@ -226,20 +203,14 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): return Response(serializer.data, status=status.HTTP_200_OK) @allow_permission( - allowed_roles=[ROLE.ADMIN], - creator=True, - model=DraftIssue, - level="WORKSPACE", + allowed_roles=[ROLE.ADMIN], creator=True, model=DraftIssue, level="WORKSPACE" ) def destroy(self, request, slug, pk=None): draft_issue = DraftIssue.objects.get(workspace__slug=slug, pk=pk) draft_issue.delete() return Response(status=status.HTTP_204_NO_CONTENT) - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], - level="WORKSPACE", - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def create_draft_to_issue(self, request, slug, draft_id): draft_issue = self.get_queryset().filter(pk=draft_id).first() @@ -263,9 +234,7 @@ class WorkspaceDraftIssueViewSet(BaseViewSet): issue_activity.delay( type="issue.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(request.user.id), issue_id=str(serializer.data.get("id", None)), project_id=str(draft_issue.project_id), diff --git a/apiserver/plane/app/views/workspace/estimate.py b/apiserver/plane/app/views/workspace/estimate.py index 59a23d867..beef2a8ec 100644 --- a/apiserver/plane/app/views/workspace/estimate.py +++ b/apiserver/plane/app/views/workspace/estimate.py @@ -11,9 +11,7 @@ from plane.utils.cache import cache_response class WorkspaceEstimatesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceEntityPermission, - ] + permission_classes = [WorkspaceEntityPermission] @cache_response(60 * 60 * 2) def get(self, request, slug): diff --git a/apiserver/plane/app/views/workspace/favorite.py b/apiserver/plane/app/views/workspace/favorite.py index 25c76779c..38fa1bdef 100644 --- a/apiserver/plane/app/views/workspace/favorite.py +++ b/apiserver/plane/app/views/workspace/favorite.py @@ -13,16 +13,11 @@ from plane.app.permissions import allow_permission, ROLE class WorkspaceFavoriteEndpoint(BaseAPIView): - - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def get(self, request, slug): # the second filter is to check if the user is a member of the project favorites = UserFavorite.objects.filter( - user=request.user, - workspace__slug=slug, - parent__isnull=True, + user=request.user, workspace__slug=slug, parent__isnull=True ).filter( Q(project__isnull=True) & ~Q(entity_type="page") | ( @@ -34,9 +29,7 @@ class WorkspaceFavoriteEndpoint(BaseAPIView): serializer = UserFavoriteSerializer(favorites, many=True) return Response(serializer.data, status=status.HTTP_200_OK) - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def post(self, request, slug): workspace = Workspace.objects.get(slug=slug) serializer = UserFavoriteSerializer(data=request.data) @@ -49,24 +42,18 @@ class WorkspaceFavoriteEndpoint(BaseAPIView): return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def patch(self, request, slug, favorite_id): favorite = UserFavorite.objects.get( user=request.user, workspace__slug=slug, pk=favorite_id ) - serializer = UserFavoriteSerializer( - favorite, data=request.data, partial=True - ) + serializer = UserFavoriteSerializer(favorite, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def delete(self, request, slug, favorite_id): favorite = UserFavorite.objects.get( user=request.user, workspace__slug=slug, pk=favorite_id @@ -76,15 +63,10 @@ class WorkspaceFavoriteEndpoint(BaseAPIView): class WorkspaceFavoriteGroupEndpoint(BaseAPIView): - - @allow_permission( - allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE" - ) + @allow_permission(allowed_roles=[ROLE.ADMIN, ROLE.MEMBER], level="WORKSPACE") def get(self, request, slug, favorite_id): favorites = UserFavorite.objects.filter( - user=request.user, - workspace__slug=slug, - parent_id=favorite_id, + user=request.user, workspace__slug=slug, parent_id=favorite_id ).filter( Q(project__isnull=True) | ( diff --git a/apiserver/plane/app/views/workspace/invite.py b/apiserver/plane/app/views/workspace/invite.py index 79b03d8a0..486a3c93b 100644 --- a/apiserver/plane/app/views/workspace/invite.py +++ b/apiserver/plane/app/views/workspace/invite.py @@ -24,12 +24,7 @@ from plane.app.serializers import ( from plane.app.views.base import BaseAPIView from plane.bgtasks.event_tracking_task import workspace_invite_event from plane.bgtasks.workspace_invitation_task import workspace_invitation -from plane.db.models import ( - User, - Workspace, - WorkspaceMember, - WorkspaceMemberInvite, -) +from plane.db.models import User, Workspace, WorkspaceMember, WorkspaceMemberInvite from plane.utils.cache import invalidate_cache, invalidate_cache_directly from .. import BaseViewSet @@ -41,9 +36,7 @@ class WorkspaceInvitationsViewset(BaseViewSet): serializer_class = WorkSpaceMemberInviteSerializer model = WorkspaceMemberInvite - permission_classes = [ - WorkSpaceAdminPermission, - ] + permission_classes = [WorkSpaceAdminPermission] def get_queryset(self): return self.filter_queryset( @@ -58,15 +51,12 @@ class WorkspaceInvitationsViewset(BaseViewSet): # Check if email is provided if not emails: return Response( - {"error": "Emails are required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST ) # check for role level of the requesting user requesting_user = WorkspaceMember.objects.get( - workspace__slug=slug, - member=request.user, - is_active=True, + workspace__slug=slug, member=request.user, is_active=True ) # Check if any invited user has an higher role @@ -112,10 +102,7 @@ class WorkspaceInvitationsViewset(BaseViewSet): email=email.get("email").strip().lower(), workspace_id=workspace.id, token=jwt.encode( - { - "email": email, - "timestamp": datetime.now().timestamp(), - }, + {"email": email, "timestamp": datetime.now().timestamp()}, settings.SECRET_KEY, algorithm="HS256", ), @@ -148,10 +135,7 @@ class WorkspaceInvitationsViewset(BaseViewSet): ) return Response( - { - "message": "Emails sent successfully", - }, - status=status.HTTP_200_OK, + {"message": "Emails sent successfully"}, status=status.HTTP_200_OK ) def destroy(self, request, slug, pk): @@ -163,9 +147,7 @@ class WorkspaceInvitationsViewset(BaseViewSet): class WorkspaceJoinEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] """Invitation response endpoint the user can respond to the invitation""" @invalidate_cache(path="/api/workspaces/", user=False) diff --git a/apiserver/plane/app/views/workspace/label.py b/apiserver/plane/app/views/workspace/label.py index 0ea9ea24a..c93cd44c8 100644 --- a/apiserver/plane/app/views/workspace/label.py +++ b/apiserver/plane/app/views/workspace/label.py @@ -9,11 +9,10 @@ from plane.db.models import Label from plane.app.permissions import WorkspaceViewerPermission from plane.utils.cache import cache_response + class WorkspaceLabelsEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceViewerPermission, - ] - + permission_classes = [WorkspaceViewerPermission] + @cache_response(60 * 60 * 2) def get(self, request, slug): labels = Label.objects.filter( diff --git a/apiserver/plane/app/views/workspace/member.py b/apiserver/plane/app/views/workspace/member.py index e64c4e5ed..3f5a4bd2b 100644 --- a/apiserver/plane/app/views/workspace/member.py +++ b/apiserver/plane/app/views/workspace/member.py @@ -1,12 +1,5 @@ # Django imports -from django.db.models import ( - CharField, - Count, - Q, - OuterRef, - Subquery, - IntegerField, -) +from django.db.models import CharField, Count, Q, OuterRef, Subquery, IntegerField from django.db.models.functions import Coalesce from django.db.models.functions import Cast @@ -49,19 +42,13 @@ class WorkSpaceMemberViewSet(BaseViewSet): serializer_class = WorkspaceMemberAdminSerializer model = WorkspaceMember - search_fields = [ - "member__display_name", - "member__first_name", - ] + search_fields = ["member__display_name", "member__first_name"] def get_queryset(self): return self.filter_queryset( super() .get_queryset() - .filter( - workspace__slug=self.kwargs.get("slug"), - is_active=True, - ) + .filter(workspace__slug=self.kwargs.get("slug"), is_active=True) .select_related("workspace", "workspace__owner") .select_related("member") ) @@ -71,34 +58,25 @@ class WorkSpaceMemberViewSet(BaseViewSet): ) def list(self, request, slug): workspace_member = WorkspaceMember.objects.get( - member=request.user, - workspace__slug=slug, - is_active=True, + member=request.user, workspace__slug=slug, is_active=True ) # Get all active workspace members workspace_members = self.get_queryset() if workspace_member.role > 5: serializer = WorkspaceMemberAdminSerializer( - workspace_members, - fields=("id", "member", "role"), - many=True, + workspace_members, fields=("id", "member", "role"), many=True ) else: serializer = WorkSpaceMemberSerializer( - workspace_members, - fields=("id", "member", "role"), - many=True, + workspace_members, fields=("id", "member", "role"), many=True ) return Response(serializer.data, status=status.HTTP_200_OK) @allow_permission(allowed_roles=[ROLE.ADMIN], level="WORKSPACE") def partial_update(self, request, slug, pk): workspace_member = WorkspaceMember.objects.get( - pk=pk, - workspace__slug=slug, - member__is_bot=False, - is_active=True, + pk=pk, workspace__slug=slug, member__is_bot=False, is_active=True ) if request.user.id == workspace_member.member_id: return Response( @@ -124,17 +102,12 @@ class WorkSpaceMemberViewSet(BaseViewSet): def destroy(self, request, slug, pk): # Check the user role who is deleting the user workspace_member = WorkspaceMember.objects.get( - workspace__slug=slug, - pk=pk, - member__is_bot=False, - is_active=True, + workspace__slug=slug, pk=pk, member__is_bot=False, is_active=True ) # check requesting user role requesting_workspace_member = WorkspaceMember.objects.get( - workspace__slug=slug, - member=request.user, - is_active=True, + workspace__slug=slug, member=request.user, is_active=True ) if str(workspace_member.id) == str(requesting_workspace_member.id): @@ -147,9 +120,7 @@ class WorkSpaceMemberViewSet(BaseViewSet): if requesting_workspace_member.role < workspace_member.role: return Response( - { - "error": "You cannot remove a user having role higher than you" - }, + {"error": "You cannot remove a user having role higher than you"}, status=status.HTTP_400_BAD_REQUEST, ) @@ -176,9 +147,7 @@ class WorkSpaceMemberViewSet(BaseViewSet): # Deactivate the users from the projects where the user is part of _ = ProjectMember.objects.filter( - workspace__slug=slug, - member_id=workspace_member.member_id, - is_active=True, + workspace__slug=slug, member_id=workspace_member.member_id, is_active=True ).update(is_active=False) workspace_member.is_active = False @@ -192,26 +161,20 @@ class WorkSpaceMemberViewSet(BaseViewSet): multiple=True, ) @invalidate_cache(path="/api/users/me/settings/") - @invalidate_cache( - path="api/users/me/workspaces/", user=False, multiple=True - ) + @invalidate_cache(path="api/users/me/workspaces/", user=False, multiple=True) @allow_permission( allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST], level="WORKSPACE" ) def leave(self, request, slug): workspace_member = WorkspaceMember.objects.get( - workspace__slug=slug, - member=request.user, - is_active=True, + workspace__slug=slug, member=request.user, is_active=True ) # Check if the leaving user is the only admin of the workspace if ( workspace_member.role == 20 and not WorkspaceMember.objects.filter( - workspace__slug=slug, - role=20, - is_active=True, + workspace__slug=slug, role=20, is_active=True ).count() > 1 ): @@ -245,9 +208,7 @@ class WorkSpaceMemberViewSet(BaseViewSet): # # Deactivate the users from the projects where the user is part of _ = ProjectMember.objects.filter( - workspace__slug=slug, - member_id=workspace_member.member_id, - is_active=True, + workspace__slug=slug, member_id=workspace_member.member_id, is_active=True ).update(is_active=False) # # Deactivate the user @@ -259,9 +220,7 @@ class WorkSpaceMemberViewSet(BaseViewSet): class WorkspaceMemberUserViewsEndpoint(BaseAPIView): def post(self, request, slug): workspace_member = WorkspaceMember.objects.get( - workspace__slug=slug, - member=request.user, - is_active=True, + workspace__slug=slug, member=request.user, is_active=True ) workspace_member.view_props = request.data.get("view_props", {}) workspace_member.save() @@ -273,8 +232,7 @@ class WorkspaceMemberUserEndpoint(BaseAPIView): def get(self, request, slug): draft_issue_count = ( DraftIssue.objects.filter( - created_by=request.user, - workspace_id=OuterRef("workspace_id"), + created_by=request.user, workspace_id=OuterRef("workspace_id") ) .values("workspace_id") .annotate(count=Count("id")) @@ -300,30 +258,21 @@ class WorkspaceProjectMemberEndpoint(BaseAPIView): serializer_class = ProjectMemberRoleSerializer model = ProjectMember - permission_classes = [ - WorkspaceEntityPermission, - ] + permission_classes = [WorkspaceEntityPermission] def get(self, request, slug): # Fetch all project IDs where the user is involved project_ids = ( - ProjectMember.objects.filter( - member=request.user, - is_active=True, - ) + ProjectMember.objects.filter(member=request.user, is_active=True) .values_list("project_id", flat=True) .distinct() ) # Get all the project members in which the user is involved project_members = ProjectMember.objects.filter( - workspace__slug=slug, - project_id__in=project_ids, - is_active=True, + workspace__slug=slug, project_id__in=project_ids, is_active=True ).select_related("project", "member", "workspace") - project_members = ProjectMemberRoleSerializer( - project_members, many=True - ).data + project_members = ProjectMemberRoleSerializer(project_members, many=True).data project_members_dict = dict() @@ -340,14 +289,9 @@ class WorkspaceProjectMemberEndpoint(BaseAPIView): class TeamMemberViewSet(BaseViewSet): serializer_class = TeamSerializer model = Team - permission_classes = [ - WorkSpaceAdminPermission, - ] + permission_classes = [WorkSpaceAdminPermission] - search_fields = [ - "member__display_name", - "member__first_name", - ] + search_fields = ["member__display_name", "member__first_name"] def get_queryset(self): return self.filter_queryset( @@ -371,9 +315,7 @@ class TeamMemberViewSet(BaseViewSet): ) if len(members) != len(request.data.get("members", [])): - users = list( - set(request.data.get("members", [])).difference(members) - ) + users = list(set(request.data.get("members", [])).difference(members)) users = User.objects.filter(pk__in=users) serializer = UserLiteSerializer(users, many=True) @@ -387,9 +329,7 @@ class TeamMemberViewSet(BaseViewSet): workspace = Workspace.objects.get(slug=slug) - serializer = TeamSerializer( - data=request.data, context={"workspace": workspace} - ) + serializer = TeamSerializer(data=request.data, context={"workspace": workspace}) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) diff --git a/apiserver/plane/app/views/workspace/module.py b/apiserver/plane/app/views/workspace/module.py index 4b1e0904a..e61fc70e7 100644 --- a/apiserver/plane/app/views/workspace/module.py +++ b/apiserver/plane/app/views/workspace/module.py @@ -1,9 +1,5 @@ # Django imports -from django.db.models import ( - Prefetch, - Q, - Count, -) +from django.db.models import Prefetch, Q, Count # Third party modules from rest_framework import status @@ -11,17 +7,13 @@ from rest_framework.response import Response # Module imports from plane.app.views.base import BaseAPIView -from plane.db.models import ( - Module, - ModuleLink, -) +from plane.db.models import Module, ModuleLink from plane.app.permissions import WorkspaceViewerPermission from plane.app.serializers.module import ModuleSerializer + class WorkspaceModulesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceViewerPermission, - ] + permission_classes = [WorkspaceViewerPermission] def get(self, request, slug): modules = ( @@ -34,9 +26,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): .prefetch_related( Prefetch( "link_module", - queryset=ModuleLink.objects.select_related( - "module", "created_by" - ), + queryset=ModuleLink.objects.select_related("module", "created_by"), ) ) .annotate( @@ -48,7 +38,7 @@ class WorkspaceModulesEndpoint(BaseAPIView): issue_module__deleted_at__isnull=True, ), distinct=True, - ), + ) ) .annotate( completed_issues=Count( diff --git a/apiserver/plane/app/views/workspace/state.py b/apiserver/plane/app/views/workspace/state.py index c69b56d4f..c00044cff 100644 --- a/apiserver/plane/app/views/workspace/state.py +++ b/apiserver/plane/app/views/workspace/state.py @@ -9,10 +9,9 @@ from plane.db.models import State from plane.app.permissions import WorkspaceEntityPermission from plane.utils.cache import cache_response + class WorkspaceStatesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceEntityPermission, - ] + permission_classes = [WorkspaceEntityPermission] @cache_response(60 * 60 * 2) def get(self, request, slug): diff --git a/apiserver/plane/app/views/workspace/user.py b/apiserver/plane/app/views/workspace/user.py index fe7b72cd1..0010b4ae7 100644 --- a/apiserver/plane/app/views/workspace/user.py +++ b/apiserver/plane/app/views/workspace/user.py @@ -24,10 +24,7 @@ from django.utils import timezone from rest_framework import status from rest_framework.response import Response -from plane.app.permissions import ( - WorkspaceEntityPermission, - WorkspaceViewerPermission, -) +from plane.app.permissions import WorkspaceEntityPermission, WorkspaceViewerPermission # Module imports from plane.app.serializers import ( @@ -58,10 +55,7 @@ from plane.utils.grouper import ( ) from plane.utils.issue_filters import issue_filters from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator class UserLastProjectWithWorkspaceEndpoint(BaseAPIView): @@ -72,10 +66,7 @@ class UserLastProjectWithWorkspaceEndpoint(BaseAPIView): if last_workspace_id is None: return Response( - { - "project_details": [], - "workspace_details": {}, - }, + {"project_details": [], "workspace_details": {}}, status=status.HTTP_200_OK, ) @@ -86,9 +77,7 @@ class UserLastProjectWithWorkspaceEndpoint(BaseAPIView): workspace_id=last_workspace_id, member=request.user ).select_related("workspace", "project", "member", "workspace__owner") - project_member_serializer = ProjectMemberSerializer( - project_member, many=True - ) + project_member_serializer = ProjectMemberSerializer(project_member, many=True) return Response( { @@ -100,12 +89,9 @@ class UserLastProjectWithWorkspaceEndpoint(BaseAPIView): class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceViewerPermission, - ] + permission_classes = [WorkspaceViewerPermission] def get(self, request, slug, user_id): - filters = issue_filters(request.query_params, "GET") order_by_param = request.GET.get("order_by", "-created_at") @@ -144,9 +130,7 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -156,8 +140,7 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -166,9 +149,7 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) if group_by: @@ -186,20 +167,14 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( - field=group_by, - slug=slug, - filters=filters, + field=group_by, slug=slug, filters=filters ), sub_group_by_fields=issue_group_values( - field=sub_group_by, - slug=slug, - filters=filters, + field=sub_group_by, slug=slug, filters=filters ), group_by_field_name=group_by, sub_group_by_field_name=sub_group_by, @@ -219,15 +194,11 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( - field=group_by, - slug=slug, - filters=filters, + field=group_by, slug=slug, filters=filters ), group_by_field_name=group_by, count_filter=Q( @@ -251,14 +222,11 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): class WorkspaceUserPropertiesEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceViewerPermission, - ] + permission_classes = [WorkspaceViewerPermission] def patch(self, request, slug): workspace_properties = WorkspaceUserProperties.objects.get( - user=request.user, - workspace__slug=slug, + user=request.user, workspace__slug=slug ) workspace_properties.filters = request.data.get( @@ -276,10 +244,7 @@ class WorkspaceUserPropertiesEndpoint(BaseAPIView): return Response(serializer.data, status=status.HTTP_201_CREATED) def get(self, request, slug): - ( - workspace_properties, - _, - ) = WorkspaceUserProperties.objects.get_or_create( + (workspace_properties, _) = WorkspaceUserProperties.objects.get_or_create( user=request.user, workspace__slug=slug ) serializer = WorkspaceUserPropertiesSerializer(workspace_properties) @@ -291,9 +256,7 @@ class WorkspaceUserProfileEndpoint(BaseAPIView): user_data = User.objects.get(pk=user_id) requesting_workspace_member = WorkspaceMember.objects.get( - workspace__slug=slug, - member=request.user, - is_active=True, + workspace__slug=slug, member=request.user, is_active=True ) projects = [] if requesting_workspace_member.role >= 15: @@ -379,9 +342,7 @@ class WorkspaceUserProfileEndpoint(BaseAPIView): class WorkspaceUserActivityEndpoint(BaseAPIView): - permission_classes = [ - WorkspaceEntityPermission, - ] + permission_classes = [WorkspaceEntityPermission] def get(self, request, slug, user_id): projects = request.query_params.getlist("project", []) @@ -513,18 +474,14 @@ class WorkspaceUserProfileStatsEndpoint(BaseAPIView): upcoming_cycles = CycleIssue.objects.filter( workspace__slug=slug, cycle__start_date__gt=timezone.now(), - issue__assignees__in=[ - user_id, - ], + issue__assignees__in=[user_id], ).values("cycle__name", "cycle__id", "cycle__project_id") present_cycle = CycleIssue.objects.filter( workspace__slug=slug, cycle__start_date__lt=timezone.now(), cycle__end_date__gt=timezone.now(), - issue__assignees__in=[ - user_id, - ], + issue__assignees__in=[user_id], ).values("cycle__name", "cycle__id", "cycle__project_id") return Response( diff --git a/apiserver/plane/asgi.py b/apiserver/plane/asgi.py index 7333baae3..2dd703ffe 100644 --- a/apiserver/plane/asgi.py +++ b/apiserver/plane/asgi.py @@ -11,8 +11,4 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "plane.settings.production") # is populated before importing code that may import ORM models. -application = ProtocolTypeRouter( - { - "http": get_asgi_application(), - } -) +application = ProtocolTypeRouter({"http": get_asgi_application()}) diff --git a/apiserver/plane/authentication/adapter/base.py b/apiserver/plane/authentication/adapter/base.py index 906d55700..c7a8c43d3 100644 --- a/apiserver/plane/authentication/adapter/base.py +++ b/apiserver/plane/authentication/adapter/base.py @@ -11,11 +11,7 @@ from django.core.exceptions import ValidationError from zxcvbn import zxcvbn # Module imports -from plane.db.models import ( - Profile, - User, - WorkspaceMemberInvite, -) +from plane.db.models import Profile, User, WorkspaceMemberInvite from plane.license.utils.instance_value import get_configuration_value from .error import AuthenticationException, AUTHENTICATION_ERROR_CODES from plane.bgtasks.user_activation_email_task import user_activation_email @@ -90,20 +86,13 @@ class Adapter: # Get configuration value (ENABLE_SIGNUP,) = get_configuration_value( - [ - { - "key": "ENABLE_SIGNUP", - "default": os.environ.get("ENABLE_SIGNUP", "1"), - }, - ] + [{"key": "ENABLE_SIGNUP", "default": os.environ.get("ENABLE_SIGNUP", "1")}] ) # Check if sign up is disabled and invite is present or not if ( ENABLE_SIGNUP == "0" - and not WorkspaceMemberInvite.objects.filter( - email=email, - ).exists() + and not WorkspaceMemberInvite.objects.filter(email=email).exists() ): # Raise exception raise AuthenticationException( @@ -124,9 +113,7 @@ class Adapter: user.token_updated_at = timezone.now() # If user is not active, send the activation email and set the user as active if not user.is_active: - user_activation_email.delay( - base_host(request=self.request), user.id - ) + user_activation_email.delay(base_host(request=self.request), user.id) # Set user as active user.is_active = True user.save() @@ -182,11 +169,7 @@ class Adapter: # Call callback if present if self.callback: - self.callback( - user, - is_signup, - self.request, - ) + self.callback(user, is_signup, self.request) # Create or update account if token data is present if self.token_data: diff --git a/apiserver/plane/authentication/adapter/error.py b/apiserver/plane/authentication/adapter/error.py index 90e236a80..63fafffbe 100644 --- a/apiserver/plane/authentication/adapter/error.py +++ b/apiserver/plane/authentication/adapter/error.py @@ -67,7 +67,6 @@ AUTHENTICATION_ERROR_CODES = { class AuthenticationException(Exception): - error_code = None error_message = None payload = {} @@ -78,10 +77,7 @@ class AuthenticationException(Exception): self.payload = payload def get_error_dict(self): - error = { - "error_code": self.error_code, - "error_message": self.error_message, - } + error = {"error_code": self.error_code, "error_message": self.error_message} for key in self.payload: error[key] = self.payload[key] diff --git a/apiserver/plane/authentication/adapter/exception.py b/apiserver/plane/authentication/adapter/exception.py index a6f7637a9..e906c5a50 100644 --- a/apiserver/plane/authentication/adapter/exception.py +++ b/apiserver/plane/authentication/adapter/exception.py @@ -4,7 +4,10 @@ from rest_framework.exceptions import NotAuthenticated from rest_framework.exceptions import Throttled # Module imports -from plane.authentication.adapter.error import AuthenticationException, AUTHENTICATION_ERROR_CODES +from plane.authentication.adapter.error import ( + AuthenticationException, + AUTHENTICATION_ERROR_CODES, +) def auth_exception_handler(exc, context): @@ -22,6 +25,6 @@ def auth_exception_handler(exc, context): ) response.data = exc.get_error_dict() response.status_code = 429 - + # Return the response that is generated by the default exception handler. return response diff --git a/apiserver/plane/authentication/adapter/oauth.py b/apiserver/plane/authentication/adapter/oauth.py index c4e78573c..e89383837 100644 --- a/apiserver/plane/authentication/adapter/oauth.py +++ b/apiserver/plane/authentication/adapter/oauth.py @@ -68,31 +68,25 @@ class OauthAdapter(Adapter): def get_user_token(self, data, headers=None): try: headers = headers or {} - response = requests.post( - self.get_token_url(), data=data, headers=headers - ) + response = requests.post(self.get_token_url(), data=data, headers=headers) response.raise_for_status() return response.json() except requests.RequestException: code = self.authentication_error_code() raise AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[code], - error_message=str(code), + error_code=AUTHENTICATION_ERROR_CODES[code], error_message=str(code) ) def get_user_response(self): try: - headers = { - "Authorization": f"Bearer {self.token_data.get('access_token')}" - } + headers = {"Authorization": f"Bearer {self.token_data.get('access_token')}"} response = requests.get(self.get_user_info_url(), headers=headers) response.raise_for_status() return response.json() except requests.RequestException: code = self.authentication_error_code() raise AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[code], - error_message=str(code), + error_code=AUTHENTICATION_ERROR_CODES[code], error_message=str(code) ) def set_user_data(self, data): @@ -104,16 +98,12 @@ class OauthAdapter(Adapter): account = Account.objects.filter( user=user, provider=self.provider, - provider_account_id=self.user_data.get("user").get( - "provider_id" - ), + provider_account_id=self.user_data.get("user").get("provider_id"), ).first() # Update the account if it exists if account: account.access_token = self.token_data.get("access_token") - account.refresh_token = self.token_data.get( - "refresh_token", None - ) + account.refresh_token = self.token_data.get("refresh_token", None) account.access_token_expired_at = self.token_data.get( "access_token_expired_at" ) diff --git a/apiserver/plane/authentication/middleware/session.py b/apiserver/plane/authentication/middleware/session.py index 2bb62b881..822c88316 100644 --- a/apiserver/plane/authentication/middleware/session.py +++ b/apiserver/plane/authentication/middleware/session.py @@ -17,9 +17,7 @@ class SessionMiddleware(MiddlewareMixin): def process_request(self, request): if "instances" in request.path: - session_key = request.COOKIES.get( - settings.ADMIN_SESSION_COOKIE_NAME - ) + session_key = request.COOKIES.get(settings.ADMIN_SESSION_COOKIE_NAME) else: session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) request.session = self.SessionStore(session_key) diff --git a/apiserver/plane/authentication/provider/credentials/email.py b/apiserver/plane/authentication/provider/credentials/email.py index 4c7764128..4b8ae0595 100644 --- a/apiserver/plane/authentication/provider/credentials/email.py +++ b/apiserver/plane/authentication/provider/credentials/email.py @@ -12,20 +12,10 @@ from plane.license.utils.instance_value import get_configuration_value class EmailProvider(CredentialAdapter): - provider = "email" - def __init__( - self, - request, - key=None, - code=None, - is_signup=False, - callback=None, - ): - super().__init__( - request=request, provider=self.provider, callback=callback - ) + def __init__(self, request, key=None, code=None, is_signup=False, callback=None): + super().__init__(request=request, provider=self.provider, callback=callback) self.key = key self.code = code self.is_signup = is_signup @@ -35,7 +25,7 @@ class EmailProvider(CredentialAdapter): { "key": "ENABLE_EMAIL_PASSWORD", "default": os.environ.get("ENABLE_EMAIL_PASSWORD"), - }, + } ] ) @@ -53,9 +43,7 @@ class EmailProvider(CredentialAdapter): if User.objects.filter(email=self.key).exists(): raise AuthenticationException( error_message="USER_ALREADY_EXIST", - error_code=AUTHENTICATION_ERROR_CODES[ - "USER_ALREADY_EXIST" - ], + error_code=AUTHENTICATION_ERROR_CODES["USER_ALREADY_EXIST"], ) super().set_user_data( @@ -72,20 +60,14 @@ class EmailProvider(CredentialAdapter): ) return else: - user = User.objects.filter( - email=self.key, - ).first() + user = User.objects.filter(email=self.key).first() # User does not exists if not user: raise AuthenticationException( error_message="USER_DOES_NOT_EXIST", - error_code=AUTHENTICATION_ERROR_CODES[ - "USER_DOES_NOT_EXIST" - ], - payload={ - "email": self.key, - }, + error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], + payload={"email": self.key}, ) # Check user password diff --git a/apiserver/plane/authentication/provider/credentials/magic_code.py b/apiserver/plane/authentication/provider/credentials/magic_code.py index 418dd2a06..4fe8924f3 100644 --- a/apiserver/plane/authentication/provider/credentials/magic_code.py +++ b/apiserver/plane/authentication/provider/credentials/magic_code.py @@ -17,26 +17,12 @@ from plane.db.models import User class MagicCodeProvider(CredentialAdapter): - provider = "magic-code" - def __init__( - self, - request, - key, - code=None, - callback=None, - ): - - ( - EMAIL_HOST, - ENABLE_MAGIC_LINK_LOGIN, - ) = get_configuration_value( + def __init__(self, request, key, code=None, callback=None): + (EMAIL_HOST, ENABLE_MAGIC_LINK_LOGIN) = get_configuration_value( [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST"), - }, + {"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST")}, { "key": "ENABLE_MAGIC_LINK_LOGIN", "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), @@ -53,16 +39,12 @@ class MagicCodeProvider(CredentialAdapter): if ENABLE_MAGIC_LINK_LOGIN == "0": raise AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "MAGIC_LINK_LOGIN_DISABLED" - ], + error_code=AUTHENTICATION_ERROR_CODES["MAGIC_LINK_LOGIN_DISABLED"], error_message="MAGIC_LINK_LOGIN_DISABLED", payload={"email": str(key)}, ) - super().__init__( - request=request, provider=self.provider, callback=callback - ) + super().__init__(request=request, provider=self.provider, callback=callback) self.key = key self.code = code @@ -164,17 +146,13 @@ class MagicCodeProvider(CredentialAdapter): email = str(self.key).replace("magic_", "", 1) if User.objects.filter(email=email).exists(): raise AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "EXPIRED_MAGIC_CODE_SIGN_IN" - ], + error_code=AUTHENTICATION_ERROR_CODES["EXPIRED_MAGIC_CODE_SIGN_IN"], error_message="EXPIRED_MAGIC_CODE_SIGN_IN", payload={"email": str(email)}, ) else: raise AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "EXPIRED_MAGIC_CODE_SIGN_UP" - ], + error_code=AUTHENTICATION_ERROR_CODES["EXPIRED_MAGIC_CODE_SIGN_UP"], error_message="EXPIRED_MAGIC_CODE_SIGN_UP", payload={"email": str(email)}, ) diff --git a/apiserver/plane/authentication/provider/oauth/github.py b/apiserver/plane/authentication/provider/oauth/github.py index edd99b1ba..1808aa515 100644 --- a/apiserver/plane/authentication/provider/oauth/github.py +++ b/apiserver/plane/authentication/provider/oauth/github.py @@ -16,14 +16,12 @@ from plane.authentication.adapter.error import ( class GitHubOAuthProvider(OauthAdapter): - token_url = "https://github.com/login/oauth/access_token" userinfo_url = "https://api.github.com/user" provider = "github" scope = "read:user user:email" def __init__(self, request, code=None, state=None, callback=None): - GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET = get_configuration_value( [ { @@ -53,9 +51,7 @@ class GitHubOAuthProvider(OauthAdapter): "scope": self.scope, "state": state, } - auth_url = ( - f"https://github.com/login/oauth/authorize?{urlencode(url_params)}" - ) + auth_url = f"https://github.com/login/oauth/authorize?{urlencode(url_params)}" super().__init__( request, self.provider, @@ -86,16 +82,14 @@ class GitHubOAuthProvider(OauthAdapter): "refresh_token": token_response.get("refresh_token", None), "access_token_expired_at": ( datetime.fromtimestamp( - token_response.get("expires_in"), - tz=pytz.utc, + token_response.get("expires_in"), tz=pytz.utc ) if token_response.get("expires_in") else None ), "refresh_token_expired_at": ( datetime.fromtimestamp( - token_response.get("refresh_token_expired_at"), - tz=pytz.utc, + token_response.get("refresh_token_expired_at"), tz=pytz.utc ) if token_response.get("refresh_token_expired_at") else None @@ -110,19 +104,12 @@ class GitHubOAuthProvider(OauthAdapter): emails_url = "https://api.github.com/user/emails" emails_response = requests.get(emails_url, headers=headers).json() email = next( - ( - email["email"] - for email in emails_response - if email["primary"] - ), - None, + (email["email"] for email in emails_response if email["primary"]), None ) return email except requests.RequestException: raise AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITHUB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITHUB_OAUTH_PROVIDER_ERROR"], error_message="GITHUB_OAUTH_PROVIDER_ERROR", ) diff --git a/apiserver/plane/authentication/provider/oauth/gitlab.py b/apiserver/plane/authentication/provider/oauth/gitlab.py index 3795cc37f..df6fb7c44 100644 --- a/apiserver/plane/authentication/provider/oauth/gitlab.py +++ b/apiserver/plane/authentication/provider/oauth/gitlab.py @@ -15,31 +15,25 @@ from plane.authentication.adapter.error import ( class GitLabOAuthProvider(OauthAdapter): - provider = "gitlab" scope = "read_user" def __init__(self, request, code=None, state=None, callback=None): - - GITLAB_CLIENT_ID, GITLAB_CLIENT_SECRET, GITLAB_HOST = ( - get_configuration_value( - [ - { - "key": "GITLAB_CLIENT_ID", - "default": os.environ.get("GITLAB_CLIENT_ID"), - }, - { - "key": "GITLAB_CLIENT_SECRET", - "default": os.environ.get("GITLAB_CLIENT_SECRET"), - }, - { - "key": "GITLAB_HOST", - "default": os.environ.get( - "GITLAB_HOST", "https://gitlab.com" - ), - }, - ] - ) + GITLAB_CLIENT_ID, GITLAB_CLIENT_SECRET, GITLAB_HOST = get_configuration_value( + [ + { + "key": "GITLAB_CLIENT_ID", + "default": os.environ.get("GITLAB_CLIENT_ID"), + }, + { + "key": "GITLAB_CLIENT_SECRET", + "default": os.environ.get("GITLAB_CLIENT_SECRET"), + }, + { + "key": "GITLAB_HOST", + "default": os.environ.get("GITLAB_HOST", "https://gitlab.com"), + }, + ] ) self.host = GITLAB_HOST @@ -104,8 +98,7 @@ class GitLabOAuthProvider(OauthAdapter): ), "refresh_token_expired_at": ( datetime.fromtimestamp( - token_response.get("refresh_token_expired_at"), - tz=pytz.utc, + token_response.get("refresh_token_expired_at"), tz=pytz.utc ) if token_response.get("refresh_token_expired_at") else None diff --git a/apiserver/plane/authentication/provider/oauth/google.py b/apiserver/plane/authentication/provider/oauth/google.py index 9c17a75af..d3f683619 100644 --- a/apiserver/plane/authentication/provider/oauth/google.py +++ b/apiserver/plane/authentication/provider/oauth/google.py @@ -53,7 +53,9 @@ class GoogleOAuthProvider(OauthAdapter): "prompt": "consent", "state": state, } - auth_url = f"https://accounts.google.com/o/oauth2/v2/auth?{urlencode(url_params)}" + auth_url = ( + f"https://accounts.google.com/o/oauth2/v2/auth?{urlencode(url_params)}" + ) super().__init__( request, @@ -84,16 +86,14 @@ class GoogleOAuthProvider(OauthAdapter): "refresh_token": token_response.get("refresh_token", None), "access_token_expired_at": ( datetime.fromtimestamp( - token_response.get("expires_in"), - tz=pytz.utc, + token_response.get("expires_in"), tz=pytz.utc ) if token_response.get("expires_in") else None ), "refresh_token_expired_at": ( datetime.fromtimestamp( - token_response.get("refresh_token_expired_at"), - tz=pytz.utc, + token_response.get("refresh_token_expired_at"), tz=pytz.utc ) if token_response.get("refresh_token_expired_at") else None diff --git a/apiserver/plane/authentication/session.py b/apiserver/plane/authentication/session.py index 7bb0b4a00..862a63c13 100644 --- a/apiserver/plane/authentication/session.py +++ b/apiserver/plane/authentication/session.py @@ -2,7 +2,6 @@ from rest_framework.authentication import SessionAuthentication class BaseSessionAuthentication(SessionAuthentication): - # Disable csrf for the rest apis def enforce_csrf(self, request): return diff --git a/apiserver/plane/authentication/urls.py b/apiserver/plane/authentication/urls.py index a375d94cb..b52cf2a21 100644 --- a/apiserver/plane/authentication/urls.py +++ b/apiserver/plane/authentication/urls.py @@ -40,64 +40,20 @@ from .views import ( urlpatterns = [ # credentials - path( - "sign-in/", - SignInAuthEndpoint.as_view(), - name="sign-in", - ), - path( - "sign-up/", - SignUpAuthEndpoint.as_view(), - name="sign-up", - ), - path( - "spaces/sign-in/", - SignInAuthSpaceEndpoint.as_view(), - name="sign-in", - ), - path( - "spaces/sign-up/", - SignUpAuthSpaceEndpoint.as_view(), - name="sign-in", - ), + path("sign-in/", SignInAuthEndpoint.as_view(), name="sign-in"), + path("sign-up/", SignUpAuthEndpoint.as_view(), name="sign-up"), + path("spaces/sign-in/", SignInAuthSpaceEndpoint.as_view(), name="sign-in"), + path("spaces/sign-up/", SignUpAuthSpaceEndpoint.as_view(), name="sign-in"), # signout - path( - "sign-out/", - SignOutAuthEndpoint.as_view(), - name="sign-out", - ), - path( - "spaces/sign-out/", - SignOutAuthSpaceEndpoint.as_view(), - name="sign-out", - ), + path("sign-out/", SignOutAuthEndpoint.as_view(), name="sign-out"), + path("spaces/sign-out/", SignOutAuthSpaceEndpoint.as_view(), name="sign-out"), # csrf token - path( - "get-csrf-token/", - CSRFTokenEndpoint.as_view(), - name="get_csrf_token", - ), + path("get-csrf-token/", CSRFTokenEndpoint.as_view(), name="get_csrf_token"), # Magic sign in - path( - "magic-generate/", - MagicGenerateEndpoint.as_view(), - name="magic-generate", - ), - path( - "magic-sign-in/", - MagicSignInEndpoint.as_view(), - name="magic-sign-in", - ), - path( - "magic-sign-up/", - MagicSignUpEndpoint.as_view(), - name="magic-sign-up", - ), - path( - "get-csrf-token/", - CSRFTokenEndpoint.as_view(), - name="get_csrf_token", - ), + path("magic-generate/", MagicGenerateEndpoint.as_view(), name="magic-generate"), + path("magic-sign-in/", MagicSignInEndpoint.as_view(), name="magic-sign-in"), + path("magic-sign-up/", MagicSignUpEndpoint.as_view(), name="magic-sign-up"), + path("get-csrf-token/", CSRFTokenEndpoint.as_view(), name="get_csrf_token"), path( "spaces/magic-generate/", MagicGenerateSpaceEndpoint.as_view(), @@ -114,16 +70,8 @@ urlpatterns = [ name="magic-sign-up", ), ## Google Oauth - path( - "google/", - GoogleOauthInitiateEndpoint.as_view(), - name="google-initiate", - ), - path( - "google/callback/", - GoogleCallbackEndpoint.as_view(), - name="google-callback", - ), + path("google/", GoogleOauthInitiateEndpoint.as_view(), name="google-initiate"), + path("google/callback/", GoogleCallbackEndpoint.as_view(), name="google-callback"), path( "spaces/google/", GoogleOauthInitiateSpaceEndpoint.as_view(), @@ -135,16 +83,8 @@ urlpatterns = [ name="google-callback", ), ## Github Oauth - path( - "github/", - GitHubOauthInitiateEndpoint.as_view(), - name="github-initiate", - ), - path( - "github/callback/", - GitHubCallbackEndpoint.as_view(), - name="github-callback", - ), + path("github/", GitHubOauthInitiateEndpoint.as_view(), name="github-initiate"), + path("github/callback/", GitHubCallbackEndpoint.as_view(), name="github-callback"), path( "spaces/github/", GitHubOauthInitiateSpaceEndpoint.as_view(), @@ -156,16 +96,8 @@ urlpatterns = [ name="github-callback", ), ## Gitlab Oauth - path( - "gitlab/", - GitLabOauthInitiateEndpoint.as_view(), - name="gitlab-initiate", - ), - path( - "gitlab/callback/", - GitLabCallbackEndpoint.as_view(), - name="gitlab-callback", - ), + path("gitlab/", GitLabOauthInitiateEndpoint.as_view(), name="gitlab-initiate"), + path("gitlab/callback/", GitLabCallbackEndpoint.as_view(), name="gitlab-callback"), path( "spaces/gitlab/", GitLabOauthInitiateSpaceEndpoint.as_view(), @@ -177,22 +109,10 @@ urlpatterns = [ name="gitlab-callback", ), # Email Check - path( - "email-check/", - EmailCheckEndpoint.as_view(), - name="email-check", - ), - path( - "spaces/email-check/", - EmailCheckSpaceEndpoint.as_view(), - name="email-check", - ), + path("email-check/", EmailCheckEndpoint.as_view(), name="email-check"), + path("spaces/email-check/", EmailCheckSpaceEndpoint.as_view(), name="email-check"), # Password - path( - "forgot-password/", - ForgotPasswordEndpoint.as_view(), - name="forgot-password", - ), + path("forgot-password/", ForgotPasswordEndpoint.as_view(), name="forgot-password"), path( "reset-password///", ResetPasswordEndpoint.as_view(), @@ -208,14 +128,6 @@ urlpatterns = [ ResetPasswordSpaceEndpoint.as_view(), name="forgot-password", ), - path( - "change-password/", - ChangePasswordEndpoint.as_view(), - name="forgot-password", - ), - path( - "set-password/", - SetUserPasswordEndpoint.as_view(), - name="set-password", - ), + path("change-password/", ChangePasswordEndpoint.as_view(), name="forgot-password"), + path("set-password/", SetUserPasswordEndpoint.as_view(), name="set-password"), ] diff --git a/apiserver/plane/authentication/utils/login.py b/apiserver/plane/authentication/utils/login.py index f5d453d02..ba7f9d1e1 100644 --- a/apiserver/plane/authentication/utils/login.py +++ b/apiserver/plane/authentication/utils/login.py @@ -17,10 +17,7 @@ def user_login(request, user, is_app=False, is_admin=False, is_space=False): "user_agent": request.META.get("HTTP_USER_AGENT", ""), "ip_address": request.META.get("REMOTE_ADDR", ""), "domain": base_host( - request=request, - is_app=is_app, - is_admin=is_admin, - is_space=is_space, + request=request, is_app=is_app, is_admin=is_admin, is_space=is_space ), } request.session["device_info"] = device_info diff --git a/apiserver/plane/authentication/utils/redirection_path.py b/apiserver/plane/authentication/utils/redirection_path.py index 62cc1f019..459ad7434 100644 --- a/apiserver/plane/authentication/utils/redirection_path.py +++ b/apiserver/plane/authentication/utils/redirection_path.py @@ -27,8 +27,7 @@ def get_redirection_path(user): fallback_workspace = ( Workspace.objects.filter( - workspace_member__member_id=user.id, - workspace_member__is_active=True, + workspace_member__member_id=user.id, workspace_member__is_active=True ) .order_by("created_at") .first() diff --git a/apiserver/plane/authentication/utils/user_auth_workflow.py b/apiserver/plane/authentication/utils/user_auth_workflow.py index e7cb4942e..13de4c287 100644 --- a/apiserver/plane/authentication/utils/user_auth_workflow.py +++ b/apiserver/plane/authentication/utils/user_auth_workflow.py @@ -1,9 +1,5 @@ from .workspace_project_join import process_workspace_project_invitations -def post_user_auth_workflow( - user, - is_signup, - request, -): +def post_user_auth_workflow(user, is_signup, request): process_workspace_project_invitations(user=user) diff --git a/apiserver/plane/authentication/views/__init__.py b/apiserver/plane/authentication/views/__init__.py index af58a9cbd..24ae1f673 100644 --- a/apiserver/plane/authentication/views/__init__.py +++ b/apiserver/plane/authentication/views/__init__.py @@ -1,52 +1,23 @@ -from .common import ( - ChangePasswordEndpoint, - CSRFTokenEndpoint, - SetUserPasswordEndpoint, -) +from .common import ChangePasswordEndpoint, CSRFTokenEndpoint, SetUserPasswordEndpoint from .app.check import EmailCheckEndpoint -from .app.email import ( - SignInAuthEndpoint, - SignUpAuthEndpoint, -) -from .app.github import ( - GitHubCallbackEndpoint, - GitHubOauthInitiateEndpoint, -) -from .app.gitlab import ( - GitLabCallbackEndpoint, - GitLabOauthInitiateEndpoint, -) -from .app.google import ( - GoogleCallbackEndpoint, - GoogleOauthInitiateEndpoint, -) -from .app.magic import ( - MagicGenerateEndpoint, - MagicSignInEndpoint, - MagicSignUpEndpoint, -) +from .app.email import SignInAuthEndpoint, SignUpAuthEndpoint +from .app.github import GitHubCallbackEndpoint, GitHubOauthInitiateEndpoint +from .app.gitlab import GitLabCallbackEndpoint, GitLabOauthInitiateEndpoint +from .app.google import GoogleCallbackEndpoint, GoogleOauthInitiateEndpoint +from .app.magic import MagicGenerateEndpoint, MagicSignInEndpoint, MagicSignUpEndpoint from .app.signout import SignOutAuthEndpoint from .space.email import SignInAuthSpaceEndpoint, SignUpAuthSpaceEndpoint -from .space.github import ( - GitHubCallbackSpaceEndpoint, - GitHubOauthInitiateSpaceEndpoint, -) +from .space.github import GitHubCallbackSpaceEndpoint, GitHubOauthInitiateSpaceEndpoint -from .space.gitlab import ( - GitLabCallbackSpaceEndpoint, - GitLabOauthInitiateSpaceEndpoint, -) +from .space.gitlab import GitLabCallbackSpaceEndpoint, GitLabOauthInitiateSpaceEndpoint -from .space.google import ( - GoogleCallbackSpaceEndpoint, - GoogleOauthInitiateSpaceEndpoint, -) +from .space.google import GoogleCallbackSpaceEndpoint, GoogleOauthInitiateSpaceEndpoint from .space.magic import ( MagicGenerateSpaceEndpoint, @@ -62,7 +33,4 @@ from .space.password_management import ( ForgotPasswordSpaceEndpoint, ResetPasswordSpaceEndpoint, ) -from .app.password_management import ( - ForgotPasswordEndpoint, - ResetPasswordEndpoint, -) +from .app.password_management import ForgotPasswordEndpoint, ResetPasswordEndpoint diff --git a/apiserver/plane/authentication/views/app/check.py b/apiserver/plane/authentication/views/app/check.py index 6af8859fc..c7e4b8a5e 100644 --- a/apiserver/plane/authentication/views/app/check.py +++ b/apiserver/plane/authentication/views/app/check.py @@ -19,42 +19,27 @@ from plane.authentication.adapter.error import ( AUTHENTICATION_ERROR_CODES, ) from plane.authentication.rate_limit import AuthenticationThrottle -from plane.license.utils.instance_value import ( - get_configuration_value, -) +from plane.license.utils.instance_value import get_configuration_value class EmailCheckEndpoint(APIView): + permission_classes = [AllowAny] - permission_classes = [ - AllowAny, - ] - - throttle_classes = [ - AuthenticationThrottle, - ] + throttle_classes = [AuthenticationThrottle] def post(self, request): # Check instance configuration instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) (EMAIL_HOST, ENABLE_MAGIC_LINK_LOGIN) = get_configuration_value( [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST", ""), - }, + {"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST", "")}, { "key": "ENABLE_MAGIC_LINK_LOGIN", "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), @@ -73,10 +58,7 @@ class EmailCheckEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["EMAIL_REQUIRED"], error_message="EMAIL_REQUIRED", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Validate email try: @@ -86,10 +68,7 @@ class EmailCheckEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"], error_message="INVALID_EMAIL", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Check if a user already exists with the given email existing_user = User.objects.filter(email=email).first() diff --git a/apiserver/plane/authentication/views/app/email.py b/apiserver/plane/authentication/views/app/email.py index 08a3e8b01..805b273a1 100644 --- a/apiserver/plane/authentication/views/app/email.py +++ b/apiserver/plane/authentication/views/app/email.py @@ -13,9 +13,7 @@ from plane.authentication.utils.login import user_login from plane.license.models import Instance from plane.authentication.utils.host import base_host from plane.authentication.utils.redirection_path import get_redirection_path -from plane.authentication.utils.user_auth_workflow import ( - post_user_auth_workflow, -) +from plane.authentication.utils.user_auth_workflow import post_user_auth_workflow from plane.db.models import User from plane.authentication.adapter.error import ( AuthenticationException, @@ -24,7 +22,6 @@ from plane.authentication.adapter.error import ( class SignInAuthEndpoint(View): - def post(self, request): next_path = request.POST.get("next_path") # Check instance configuration @@ -32,9 +29,7 @@ class SignInAuthEndpoint(View): if instance is None or not instance.is_setup_done: # Redirection params exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() @@ -42,8 +37,7 @@ class SignInAuthEndpoint(View): params["next_path"] = str(next_path) # Base URL join url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -66,8 +60,7 @@ class SignInAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -85,8 +78,7 @@ class SignInAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -102,8 +94,7 @@ class SignInAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -132,14 +123,12 @@ class SignInAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) class SignUpAuthEndpoint(View): - def post(self, request): next_path = request.POST.get("next_path") # Check instance configuration @@ -147,17 +136,14 @@ class SignUpAuthEndpoint(View): if instance is None or not instance.is_setup_done: # Redirection params exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -177,8 +163,7 @@ class SignUpAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) # Validate the email @@ -196,8 +181,7 @@ class SignUpAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -215,8 +199,7 @@ class SignUpAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -244,7 +227,6 @@ class SignUpAuthEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/github.py b/apiserver/plane/authentication/views/app/github.py index f93beefa3..f1a15474c 100644 --- a/apiserver/plane/authentication/views/app/github.py +++ b/apiserver/plane/authentication/views/app/github.py @@ -9,9 +9,7 @@ from django.views import View from plane.authentication.provider.oauth.github import GitHubOAuthProvider from plane.authentication.utils.login import user_login from plane.authentication.utils.redirection_path import get_redirection_path -from plane.authentication.utils.user_auth_workflow import ( - post_user_auth_workflow, -) +from plane.authentication.utils.user_auth_workflow import post_user_auth_workflow from plane.license.models import Instance from plane.authentication.utils.host import base_host from plane.authentication.adapter.error import ( @@ -21,7 +19,6 @@ from plane.authentication.adapter.error import ( class GitHubOauthInitiateEndpoint(View): - def get(self, request): # Get host and next path request.session["host"] = base_host(request=request, is_app=True) @@ -33,17 +30,14 @@ class GitHubOauthInitiateEndpoint(View): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) try: @@ -57,14 +51,12 @@ class GitHubOauthInitiateEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) class GitHubCallbackEndpoint(View): - def get(self, request): code = request.GET.get("code") state = request.GET.get("state") @@ -73,41 +65,29 @@ class GitHubCallbackEndpoint(View): if state != request.session.get("state", ""): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITHUB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITHUB_OAUTH_PROVIDER_ERROR"], error_message="GITHUB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) if not code: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITHUB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITHUB_OAUTH_PROVIDER_ERROR"], error_message="GITHUB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) try: provider = GitHubOAuthProvider( - request=request, - code=code, - callback=post_user_auth_workflow, + request=request, code=code, callback=post_user_auth_workflow ) user = provider.authenticate() # Login the user and record his device info @@ -124,8 +104,5 @@ class GitHubCallbackEndpoint(View): params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/gitlab.py b/apiserver/plane/authentication/views/app/gitlab.py index 02a44aeb4..bc0c9c8d7 100644 --- a/apiserver/plane/authentication/views/app/gitlab.py +++ b/apiserver/plane/authentication/views/app/gitlab.py @@ -9,9 +9,7 @@ from django.views import View from plane.authentication.provider.oauth.gitlab import GitLabOAuthProvider from plane.authentication.utils.login import user_login from plane.authentication.utils.redirection_path import get_redirection_path -from plane.authentication.utils.user_auth_workflow import ( - post_user_auth_workflow, -) +from plane.authentication.utils.user_auth_workflow import post_user_auth_workflow from plane.license.models import Instance from plane.authentication.utils.host import base_host from plane.authentication.adapter.error import ( @@ -21,7 +19,6 @@ from plane.authentication.adapter.error import ( class GitLabOauthInitiateEndpoint(View): - def get(self, request): # Get host and next path request.session["host"] = base_host(request=request, is_app=True) @@ -33,17 +30,14 @@ class GitLabOauthInitiateEndpoint(View): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) try: @@ -57,14 +51,12 @@ class GitLabOauthInitiateEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) class GitLabCallbackEndpoint(View): - def get(self, request): code = request.GET.get("code") state = request.GET.get("state") @@ -73,41 +65,29 @@ class GitLabCallbackEndpoint(View): if state != request.session.get("state", ""): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITLAB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITLAB_OAUTH_PROVIDER_ERROR"], error_message="GITLAB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) if not code: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITLAB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITLAB_OAUTH_PROVIDER_ERROR"], error_message="GITLAB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) try: provider = GitLabOAuthProvider( - request=request, - code=code, - callback=post_user_auth_workflow, + request=request, code=code, callback=post_user_auth_workflow ) user = provider.authenticate() # Login the user and record his device info @@ -124,8 +104,5 @@ class GitLabCallbackEndpoint(View): params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/google.py b/apiserver/plane/authentication/views/app/google.py index 05f4511e2..46c0d1980 100644 --- a/apiserver/plane/authentication/views/app/google.py +++ b/apiserver/plane/authentication/views/app/google.py @@ -11,9 +11,7 @@ from django.views import View from plane.authentication.provider.oauth.google import GoogleOAuthProvider from plane.authentication.utils.login import user_login from plane.authentication.utils.redirection_path import get_redirection_path -from plane.authentication.utils.user_auth_workflow import ( - post_user_auth_workflow, -) +from plane.authentication.utils.user_auth_workflow import post_user_auth_workflow from plane.license.models import Instance from plane.authentication.utils.host import base_host from plane.authentication.adapter.error import ( @@ -33,17 +31,14 @@ class GoogleOauthInitiateEndpoint(View): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -58,8 +53,7 @@ class GoogleOauthInitiateEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -73,39 +67,27 @@ class GoogleCallbackEndpoint(View): if state != request.session.get("state", ""): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GOOGLE_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GOOGLE_OAUTH_PROVIDER_ERROR"], error_message="GOOGLE_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) if not code: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GOOGLE_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GOOGLE_OAUTH_PROVIDER_ERROR"], error_message="GOOGLE_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() if next_path: params["next_path"] = next_path - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) try: provider = GoogleOAuthProvider( - request=request, - code=code, - callback=post_user_auth_workflow, + request=request, code=code, callback=post_user_auth_workflow ) user = provider.authenticate() # Login the user and record his device info @@ -119,8 +101,5 @@ class GoogleCallbackEndpoint(View): params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) - url = urljoin( - base_host, - "?" + urlencode(params), - ) + url = urljoin(base_host, "?" + urlencode(params)) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/magic.py b/apiserver/plane/authentication/views/app/magic.py index 980eb4e7c..e02784333 100644 --- a/apiserver/plane/authentication/views/app/magic.py +++ b/apiserver/plane/authentication/views/app/magic.py @@ -13,14 +13,10 @@ from rest_framework.response import Response from rest_framework.views import APIView # Module imports -from plane.authentication.provider.credentials.magic_code import ( - MagicCodeProvider, -) +from plane.authentication.provider.credentials.magic_code import MagicCodeProvider from plane.authentication.utils.login import user_login from plane.authentication.utils.redirection_path import get_redirection_path -from plane.authentication.utils.user_auth_workflow import ( - post_user_auth_workflow, -) +from plane.authentication.utils.user_auth_workflow import post_user_auth_workflow from plane.bgtasks.magic_link_code_task import magic_link from plane.license.models import Instance from plane.authentication.utils.host import base_host @@ -33,28 +29,19 @@ from plane.authentication.rate_limit import AuthenticationThrottle class MagicGenerateEndpoint(APIView): + permission_classes = [AllowAny] - permission_classes = [ - AllowAny, - ] - - throttle_classes = [ - AuthenticationThrottle, - ] + throttle_classes = [AuthenticationThrottle] def post(self, request): # Check if instance is configured instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) - return Response( - exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) origin = request.META.get("HTTP_ORIGIN", "/") email = request.data.get("email", False) @@ -69,16 +56,11 @@ class MagicGenerateEndpoint(APIView): return Response({"key": str(key)}, status=status.HTTP_200_OK) except AuthenticationException as e: params = e.get_error_dict() - return Response( - params, - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(params, status=status.HTTP_400_BAD_REQUEST) class MagicSignInEndpoint(View): - def post(self, request): - # set the referer as session to redirect after login code = request.POST.get("code", "").strip() email = request.POST.get("email", "").strip().lower() @@ -95,8 +77,7 @@ class MagicSignInEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -112,8 +93,7 @@ class MagicSignInEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -146,16 +126,13 @@ class MagicSignInEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "sign-in?" + urlencode(params), + base_host(request=request, is_app=True), "sign-in?" + urlencode(params) ) return HttpResponseRedirect(url) class MagicSignUpEndpoint(View): - def post(self, request): - # set the referer as session to redirect after login code = request.POST.get("code", "").strip() email = request.POST.get("email", "").strip().lower() @@ -172,8 +149,7 @@ class MagicSignUpEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) # Existing user @@ -187,8 +163,7 @@ class MagicSignUpEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) @@ -216,7 +191,6 @@ class MagicSignUpEndpoint(View): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request, is_app=True), - "?" + urlencode(params), + base_host(request=request, is_app=True), "?" + urlencode(params) ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/password_management.py b/apiserver/plane/authentication/views/app/password_management.py index 43054867e..cad498e63 100644 --- a/apiserver/plane/authentication/views/app/password_management.py +++ b/apiserver/plane/authentication/views/app/password_management.py @@ -14,11 +14,7 @@ from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.http import HttpResponseRedirect -from django.utils.encoding import ( - DjangoUnicodeDecodeError, - smart_bytes, - smart_str, -) +from django.utils.encoding import DjangoUnicodeDecodeError, smart_bytes, smart_str from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.views import View @@ -34,6 +30,7 @@ from plane.authentication.adapter.error import ( ) from plane.authentication.rate_limit import AuthenticationThrottle + def generate_password_token(user): uidb64 = urlsafe_base64_encode(smart_bytes(user.id)) token = PasswordResetTokenGenerator().make_token(user) @@ -42,13 +39,9 @@ def generate_password_token(user): class ForgotPasswordEndpoint(APIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] - throttle_classes = [ - AuthenticationThrottle, - ] + throttle_classes = [AuthenticationThrottle] def post(self, request): email = request.data.get("email") @@ -57,23 +50,13 @@ class ForgotPasswordEndpoint(APIView): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) (EMAIL_HOST,) = get_configuration_value( - [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST"), - }, - ] + [{"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST")}] ) if not (EMAIL_HOST): @@ -81,10 +64,7 @@ class ForgotPasswordEndpoint(APIView): error_message="SMTP_NOT_CONFIGURED", error_code=AUTHENTICATION_ERROR_CODES["SMTP_NOT_CONFIGURED"], ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) try: validate_email(email) @@ -93,10 +73,7 @@ class ForgotPasswordEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"], error_message="INVALID_EMAIL", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Get the user user = User.objects.filter(email=email).first() @@ -116,14 +93,10 @@ class ForgotPasswordEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], error_message="USER_DOES_NOT_EXIST", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) class ResetPasswordEndpoint(View): - def post(self, request, uidb64, token): try: # Decode the id from the uidb64 @@ -133,9 +106,7 @@ class ResetPasswordEndpoint(View): # check if the token is valid for the user if not PasswordResetTokenGenerator().check_token(user, token): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INVALID_PASSWORD_TOKEN" - ], + error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD_TOKEN"], error_message="INVALID_PASSWORD_TOKEN", ) params = exc.get_error_dict() @@ -154,8 +125,7 @@ class ResetPasswordEndpoint(View): ) url = urljoin( base_host(request=request, is_app=True), - "accounts/reset-password?" - + urlencode(exc.get_error_dict()), + "accounts/reset-password?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -168,8 +138,7 @@ class ResetPasswordEndpoint(View): ) url = urljoin( base_host(request=request, is_app=True), - "accounts/reset-password?" - + urlencode(exc.get_error_dict()), + "accounts/reset-password?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -185,9 +154,7 @@ class ResetPasswordEndpoint(View): return HttpResponseRedirect(url) except DjangoUnicodeDecodeError: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "EXPIRED_PASSWORD_TOKEN" - ], + error_code=AUTHENTICATION_ERROR_CODES["EXPIRED_PASSWORD_TOKEN"], error_message="EXPIRED_PASSWORD_TOKEN", ) url = urljoin( diff --git a/apiserver/plane/authentication/views/app/signout.py b/apiserver/plane/authentication/views/app/signout.py index 260a89a8d..b8019dac1 100644 --- a/apiserver/plane/authentication/views/app/signout.py +++ b/apiserver/plane/authentication/views/app/signout.py @@ -10,7 +10,6 @@ from plane.db.models import User class SignOutAuthEndpoint(View): - def post(self, request): # Get user try: @@ -20,10 +19,6 @@ class SignOutAuthEndpoint(View): user.save() # Log the user out logout(request) - return HttpResponseRedirect( - base_host(request=request, is_app=True) - ) + return HttpResponseRedirect(base_host(request=request, is_app=True)) except Exception: - return HttpResponseRedirect( - base_host(request=request, is_app=True) - ) + return HttpResponseRedirect(base_host(request=request, is_app=True)) diff --git a/apiserver/plane/authentication/views/common.py b/apiserver/plane/authentication/views/common.py index 3e95d6ed8..cdcf6bc96 100644 --- a/apiserver/plane/authentication/views/common.py +++ b/apiserver/plane/authentication/views/common.py @@ -9,9 +9,7 @@ from rest_framework.views import APIView from zxcvbn import zxcvbn ## Module imports -from plane.app.serializers import ( - UserSerializer, -) +from plane.app.serializers import UserSerializer from plane.authentication.utils.login import user_login from plane.db.models import User from plane.authentication.adapter.error import ( @@ -22,24 +20,24 @@ from django.middleware.csrf import get_token from plane.utils.cache import invalidate_cache from plane.authentication.utils.host import base_host -class CSRFTokenEndpoint(APIView): - permission_classes = [ - AllowAny, - ] +class CSRFTokenEndpoint(APIView): + permission_classes = [AllowAny] def get(self, request): # Generate a CSRF token csrf_token = get_token(request) # Return the CSRF token in a JSON response - return Response( - {"csrf_token": str(csrf_token)}, status=status.HTTP_200_OK - ) + return Response({"csrf_token": str(csrf_token)}, status=status.HTTP_200_OK) def csrf_failure(request, reason=""): """Custom CSRF failure view""" - return render(request, "csrf_failure.html", {"reason": reason, "root_url": base_host(request=request)}) + return render( + request, + "csrf_failure.html", + {"reason": reason, "root_url": base_host(request=request)}, + ) class ChangePasswordEndpoint(APIView): @@ -55,23 +53,15 @@ class ChangePasswordEndpoint(APIView): error_message="MISSING_PASSWORD", payload={"error": "Old or new password is missing"}, ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) if not user.check_password(old_password): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INCORRECT_OLD_PASSWORD" - ], + error_code=AUTHENTICATION_ERROR_CODES["INCORRECT_OLD_PASSWORD"], error_message="INCORRECT_OLD_PASSWORD", payload={"error": "Old password is not correct"}, ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # check the password score results = zxcvbn(new_password) @@ -80,10 +70,7 @@ class ChangePasswordEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_NEW_PASSWORD"], error_message="INVALID_NEW_PASSWORD", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # set_password also hashes the password that the user will get user.set_password(new_password) @@ -91,13 +78,11 @@ class ChangePasswordEndpoint(APIView): user.save() user_login(user=user, request=request, is_app=True) return Response( - {"message": "Password updated successfully"}, - status=status.HTTP_200_OK, + {"message": "Password updated successfully"}, status=status.HTTP_200_OK ) class SetUserPasswordEndpoint(APIView): - @invalidate_cache("/api/users/me/") def post(self, request): user = User.objects.get(pk=request.user.id) @@ -112,10 +97,7 @@ class SetUserPasswordEndpoint(APIView): "error": "Your password is already set please change your password from profile" }, ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Check password validation if not password: @@ -123,10 +105,7 @@ class SetUserPasswordEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"], error_message="INVALID_PASSWORD", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) results = zxcvbn(password) if results["score"] < 3: @@ -134,10 +113,7 @@ class SetUserPasswordEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD"], error_message="INVALID_PASSWORD", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Set the user password user.set_password(password) diff --git a/apiserver/plane/authentication/views/space/check.py b/apiserver/plane/authentication/views/space/check.py index 560ae0e31..9b4d8aa56 100644 --- a/apiserver/plane/authentication/views/space/check.py +++ b/apiserver/plane/authentication/views/space/check.py @@ -23,36 +23,23 @@ from plane.license.utils.instance_value import get_configuration_value class EmailCheckSpaceEndpoint(APIView): + permission_classes = [AllowAny] - permission_classes = [ - AllowAny, - ] - - throttle_classes = [ - AuthenticationThrottle, - ] + throttle_classes = [AuthenticationThrottle] def post(self, request): # Check instance configuration instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) (EMAIL_HOST, ENABLE_MAGIC_LINK_LOGIN) = get_configuration_value( [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST", ""), - }, + {"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST", "")}, { "key": "ENABLE_MAGIC_LINK_LOGIN", "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), @@ -71,10 +58,7 @@ class EmailCheckSpaceEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["EMAIL_REQUIRED"], error_message="EMAIL_REQUIRED", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Validate email try: @@ -84,10 +68,7 @@ class EmailCheckSpaceEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"], error_message="INVALID_EMAIL", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Check if a user already exists with the given email existing_user = User.objects.filter(email=email).first() diff --git a/apiserver/plane/authentication/views/space/email.py b/apiserver/plane/authentication/views/space/email.py index 4329ed26d..278cdf80b 100644 --- a/apiserver/plane/authentication/views/space/email.py +++ b/apiserver/plane/authentication/views/space/email.py @@ -20,7 +20,6 @@ from plane.authentication.adapter.error import ( class SignInAuthSpaceEndpoint(View): - def post(self, request): next_path = request.POST.get("next_path") # Check instance configuration @@ -28,9 +27,7 @@ class SignInAuthSpaceEndpoint(View): if instance is None or not instance.is_setup_done: # Redirection params exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() @@ -108,7 +105,6 @@ class SignInAuthSpaceEndpoint(View): class SignUpAuthSpaceEndpoint(View): - def post(self, request): next_path = request.POST.get("next_path") # Check instance configuration @@ -116,9 +112,7 @@ class SignUpAuthSpaceEndpoint(View): if instance is None or not instance.is_setup_done: # Redirection params exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() diff --git a/apiserver/plane/authentication/views/space/github.py b/apiserver/plane/authentication/views/space/github.py index 711f7eaa7..1d9d1d4ee 100644 --- a/apiserver/plane/authentication/views/space/github.py +++ b/apiserver/plane/authentication/views/space/github.py @@ -18,7 +18,6 @@ from plane.authentication.adapter.error import ( class GitHubOauthInitiateSpaceEndpoint(View): - def get(self, request): # Get host and next path request.session["host"] = base_host(request=request, is_space=True) @@ -30,9 +29,7 @@ class GitHubOauthInitiateSpaceEndpoint(View): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() @@ -56,7 +53,6 @@ class GitHubOauthInitiateSpaceEndpoint(View): class GitHubCallbackSpaceEndpoint(View): - def get(self, request): code = request.GET.get("code") state = request.GET.get("state") @@ -65,9 +61,7 @@ class GitHubCallbackSpaceEndpoint(View): if state != request.session.get("state", ""): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITHUB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITHUB_OAUTH_PROVIDER_ERROR"], error_message="GITHUB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() @@ -78,9 +72,7 @@ class GitHubCallbackSpaceEndpoint(View): if not code: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITHUB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITHUB_OAUTH_PROVIDER_ERROR"], error_message="GITHUB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() @@ -90,10 +82,7 @@ class GitHubCallbackSpaceEndpoint(View): return HttpResponseRedirect(url) try: - provider = GitHubOAuthProvider( - request=request, - code=code, - ) + provider = GitHubOAuthProvider(request=request, code=code) user = provider.authenticate() # Login the user and record his device info user_login(request=request, user=user, is_space=True) diff --git a/apiserver/plane/authentication/views/space/gitlab.py b/apiserver/plane/authentication/views/space/gitlab.py index 7ebd9d187..9fb314442 100644 --- a/apiserver/plane/authentication/views/space/gitlab.py +++ b/apiserver/plane/authentication/views/space/gitlab.py @@ -18,7 +18,6 @@ from plane.authentication.adapter.error import ( class GitLabOauthInitiateSpaceEndpoint(View): - def get(self, request): # Get host and next path request.session["host"] = base_host(request=request, is_space=True) @@ -30,9 +29,7 @@ class GitLabOauthInitiateSpaceEndpoint(View): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() @@ -56,7 +53,6 @@ class GitLabOauthInitiateSpaceEndpoint(View): class GitLabCallbackSpaceEndpoint(View): - def get(self, request): code = request.GET.get("code") state = request.GET.get("state") @@ -65,9 +61,7 @@ class GitLabCallbackSpaceEndpoint(View): if state != request.session.get("state", ""): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITLAB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITLAB_OAUTH_PROVIDER_ERROR"], error_message="GITLAB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() @@ -78,9 +72,7 @@ class GitLabCallbackSpaceEndpoint(View): if not code: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GITLAB_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GITLAB_OAUTH_PROVIDER_ERROR"], error_message="GITLAB_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() @@ -90,10 +82,7 @@ class GitLabCallbackSpaceEndpoint(View): return HttpResponseRedirect(url) try: - provider = GitLabOAuthProvider( - request=request, - code=code, - ) + provider = GitLabOAuthProvider(request=request, code=code) user = provider.authenticate() # Login the user and record his device info user_login(request=request, user=user, is_space=True) diff --git a/apiserver/plane/authentication/views/space/google.py b/apiserver/plane/authentication/views/space/google.py index 38a2b910a..479a18883 100644 --- a/apiserver/plane/authentication/views/space/google.py +++ b/apiserver/plane/authentication/views/space/google.py @@ -28,9 +28,7 @@ class GoogleOauthInitiateSpaceEndpoint(View): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) params = exc.get_error_dict() @@ -62,9 +60,7 @@ class GoogleCallbackSpaceEndpoint(View): if state != request.session.get("state", ""): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GOOGLE_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GOOGLE_OAUTH_PROVIDER_ERROR"], error_message="GOOGLE_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() @@ -74,9 +70,7 @@ class GoogleCallbackSpaceEndpoint(View): return HttpResponseRedirect(url) if not code: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "GOOGLE_OAUTH_PROVIDER_ERROR" - ], + error_code=AUTHENTICATION_ERROR_CODES["GOOGLE_OAUTH_PROVIDER_ERROR"], error_message="GOOGLE_OAUTH_PROVIDER_ERROR", ) params = exc.get_error_dict() @@ -85,10 +79,7 @@ class GoogleCallbackSpaceEndpoint(View): url = f"{base_host(request=request, is_space=True)}?{urlencode(params)}" return HttpResponseRedirect(url) try: - provider = GoogleOAuthProvider( - request=request, - code=code, - ) + provider = GoogleOAuthProvider(request=request, code=code) user = provider.authenticate() # Login the user and record his device info user_login(request=request, user=user, is_space=True) diff --git a/apiserver/plane/authentication/views/space/magic.py b/apiserver/plane/authentication/views/space/magic.py index 838039f96..e763aaa07 100644 --- a/apiserver/plane/authentication/views/space/magic.py +++ b/apiserver/plane/authentication/views/space/magic.py @@ -13,9 +13,7 @@ from rest_framework.response import Response from rest_framework.views import APIView # Module imports -from plane.authentication.provider.credentials.magic_code import ( - MagicCodeProvider, -) +from plane.authentication.provider.credentials.magic_code import MagicCodeProvider from plane.authentication.utils.login import user_login from plane.bgtasks.magic_link_code_task import magic_link from plane.license.models import Instance @@ -28,24 +26,17 @@ from plane.authentication.adapter.error import ( class MagicGenerateSpaceEndpoint(APIView): - - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def post(self, request): # Check if instance is configured instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) - return Response( - exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) origin = base_host(request=request, is_space=True) email = request.data.get("email", False) @@ -59,16 +50,11 @@ class MagicGenerateSpaceEndpoint(APIView): magic_link.delay(email, key, token, origin) return Response({"key": str(key)}, status=status.HTTP_200_OK) except AuthenticationException as e: - return Response( - e.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(e.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) class MagicSignInSpaceEndpoint(View): - def post(self, request): - # set the referer as session to redirect after login code = request.POST.get("code", "").strip() email = request.POST.get("email", "").strip().lower() @@ -122,9 +108,7 @@ class MagicSignInSpaceEndpoint(View): class MagicSignUpSpaceEndpoint(View): - def post(self, request): - # set the referer as session to redirect after login code = request.POST.get("code", "").strip() email = request.POST.get("email", "").strip().lower() diff --git a/apiserver/plane/authentication/views/space/password_management.py b/apiserver/plane/authentication/views/space/password_management.py index 3e0379b96..710d0db2f 100644 --- a/apiserver/plane/authentication/views/space/password_management.py +++ b/apiserver/plane/authentication/views/space/password_management.py @@ -14,11 +14,7 @@ from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.http import HttpResponseRedirect -from django.utils.encoding import ( - DjangoUnicodeDecodeError, - smart_bytes, - smart_str, -) +from django.utils.encoding import DjangoUnicodeDecodeError, smart_bytes, smart_str from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode from django.views import View @@ -43,13 +39,9 @@ def generate_password_token(user): class ForgotPasswordSpaceEndpoint(APIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] - throttle_classes = [ - AuthenticationThrottle, - ] + throttle_classes = [AuthenticationThrottle] def post(self, request): email = request.data.get("email") @@ -58,33 +50,23 @@ class ForgotPasswordSpaceEndpoint(APIView): instance = Instance.objects.first() if instance is None or not instance.is_setup_done: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) - (EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD) = ( - get_configuration_value( - [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST"), - }, - { - "key": "EMAIL_HOST_USER", - "default": os.environ.get("EMAIL_HOST_USER"), - }, - { - "key": "EMAIL_HOST_PASSWORD", - "default": os.environ.get("EMAIL_HOST_PASSWORD"), - }, - ] - ) + (EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD) = get_configuration_value( + [ + {"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST")}, + { + "key": "EMAIL_HOST_USER", + "default": os.environ.get("EMAIL_HOST_USER"), + }, + { + "key": "EMAIL_HOST_PASSWORD", + "default": os.environ.get("EMAIL_HOST_PASSWORD"), + }, + ] ) if not (EMAIL_HOST): @@ -92,10 +74,7 @@ class ForgotPasswordSpaceEndpoint(APIView): error_message="SMTP_NOT_CONFIGURED", error_code=AUTHENTICATION_ERROR_CODES["SMTP_NOT_CONFIGURED"], ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) try: validate_email(email) @@ -104,10 +83,7 @@ class ForgotPasswordSpaceEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["INVALID_EMAIL"], error_message="INVALID_EMAIL", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) # Get the user user = User.objects.filter(email=email).first() @@ -127,14 +103,10 @@ class ForgotPasswordSpaceEndpoint(APIView): error_code=AUTHENTICATION_ERROR_CODES["USER_DOES_NOT_EXIST"], error_message="USER_DOES_NOT_EXIST", ) - return Response( - exc.get_error_dict(), - status=status.HTTP_400_BAD_REQUEST, - ) + return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) class ResetPasswordSpaceEndpoint(View): - def post(self, request, uidb64, token): try: # Decode the id from the uidb64 @@ -144,9 +116,7 @@ class ResetPasswordSpaceEndpoint(View): # check if the token is valid for the user if not PasswordResetTokenGenerator().check_token(user, token): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INVALID_PASSWORD_TOKEN" - ], + error_code=AUTHENTICATION_ERROR_CODES["INVALID_PASSWORD_TOKEN"], error_message="INVALID_PASSWORD_TOKEN", ) params = exc.get_error_dict() @@ -178,14 +148,10 @@ class ResetPasswordSpaceEndpoint(View): user.is_password_autoset = False user.save() - return HttpResponseRedirect( - base_host(request=request, is_space=True) - ) + return HttpResponseRedirect(base_host(request=request, is_space=True)) except DjangoUnicodeDecodeError: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "EXPIRED_PASSWORD_TOKEN" - ], + error_code=AUTHENTICATION_ERROR_CODES["EXPIRED_PASSWORD_TOKEN"], error_message="EXPIRED_PASSWORD_TOKEN", ) url = f"{base_host(request=request, is_space=True)}/accounts/reset-password/?{urlencode(exc.get_error_dict())}" diff --git a/apiserver/plane/authentication/views/space/signout.py b/apiserver/plane/authentication/views/space/signout.py index d3f29bd8d..babd18ee9 100644 --- a/apiserver/plane/authentication/views/space/signout.py +++ b/apiserver/plane/authentication/views/space/signout.py @@ -10,7 +10,6 @@ from plane.db.models import User class SignOutAuthSpaceEndpoint(View): - def post(self, request): next_path = request.POST.get("next_path") diff --git a/apiserver/plane/bgtasks/analytic_plot_export.py b/apiserver/plane/bgtasks/analytic_plot_export.py index 7e8630e65..0a335e5b5 100644 --- a/apiserver/plane/bgtasks/analytic_plot_export.py +++ b/apiserver/plane/bgtasks/analytic_plot_export.py @@ -106,10 +106,7 @@ def get_assignee_details(slug, filters): ), ), # If `avatar_asset` is None, fall back to using `avatar` field directly - When( - assignees__avatar_asset__isnull=True, - then="assignees__avatar", - ), + When(assignees__avatar_asset__isnull=True, then="assignees__avatar"), default=Value(None), output_field=models.CharField(), ) @@ -143,10 +140,7 @@ def get_label_details(slug, filters): def get_state_details(slug, filters): return ( - Issue.issue_objects.filter( - workspace__slug=slug, - **filters, - ) + Issue.issue_objects.filter(workspace__slug=slug, **filters) .distinct("state_id") .order_by("state_id") .values("state_id", "state__name", "state__color") @@ -163,10 +157,7 @@ def get_module_details(slug, filters): ) .distinct("issue_module__module_id") .order_by("issue_module__module_id") - .values( - "issue_module__module_id", - "issue_module__module__name", - ) + .values("issue_module__module_id", "issue_module__module__name") ) @@ -180,10 +171,7 @@ def get_cycle_details(slug, filters): ) .distinct("issue_cycle__cycle_id") .order_by("issue_cycle__cycle_id") - .values( - "issue_cycle__cycle_id", - "issue_cycle__cycle__name", - ) + .values("issue_cycle__cycle_id", "issue_cycle__cycle__name") ) @@ -209,9 +197,7 @@ def generate_segmented_rows( ): segment_zero = list( set( - item.get("segment") - for sublist in distribution.values() - for item in sublist + item.get("segment") for sublist in distribution.values() for item in sublist ) ) @@ -230,9 +216,7 @@ def generate_segmented_rows( ] for segment in segment_zero: - value = next( - (x.get(key) for x in data if x.get("segment") == segment), "0" - ) + value = next((x.get(key) for x in data if x.get("segment") == segment), "0") generated_row.append(value) if x_axis == ASSIGNEE_ID: @@ -251,12 +235,7 @@ def generate_segmented_rows( if x_axis == LABEL_ID: label = next( - ( - lab - for lab in label_details - if str(lab[LABEL_ID]) == str(item) - ), - None, + (lab for lab in label_details if str(lab[LABEL_ID]) == str(item)), None ) if label: @@ -264,12 +243,7 @@ def generate_segmented_rows( if x_axis == STATE_ID: state = next( - ( - sta - for sta in state_details - if str(sta[STATE_ID]) == str(item) - ), - None, + (sta for sta in state_details if str(sta[STATE_ID]) == str(item)), None ) if state: @@ -277,12 +251,7 @@ def generate_segmented_rows( if x_axis == CYCLE_ID: cycle = next( - ( - cyc - for cyc in cycle_details - if str(cyc[CYCLE_ID]) == str(item) - ), - None, + (cyc for cyc in cycle_details if str(cyc[CYCLE_ID]) == str(item)), None ) if cycle: @@ -290,11 +259,7 @@ def generate_segmented_rows( if x_axis == MODULE_ID: module = next( - ( - mod - for mod in module_details - if str(mod[MODULE_ID]) == str(item) - ), + (mod for mod in module_details if str(mod[MODULE_ID]) == str(item)), None, ) @@ -321,12 +286,7 @@ def generate_segmented_rows( if segmented == LABEL_ID: for index, segm in enumerate(row_zero[2:]): label = next( - ( - lab - for lab in label_details - if str(lab[LABEL_ID]) == str(segm) - ), - None, + (lab for lab in label_details if str(lab[LABEL_ID]) == str(segm)), None ) if label: row_zero[index + 2] = label["labels__name"] @@ -334,12 +294,7 @@ def generate_segmented_rows( if segmented == STATE_ID: for index, segm in enumerate(row_zero[2:]): state = next( - ( - sta - for sta in state_details - if str(sta[STATE_ID]) == str(segm) - ), - None, + (sta for sta in state_details if str(sta[STATE_ID]) == str(segm)), None ) if state: row_zero[index + 2] = state["state__name"] @@ -347,12 +302,7 @@ def generate_segmented_rows( if segmented == MODULE_ID: for index, segm in enumerate(row_zero[2:]): module = next( - ( - mod - for mod in label_details - if str(mod[MODULE_ID]) == str(segm) - ), - None, + (mod for mod in label_details if str(mod[MODULE_ID]) == str(segm)), None ) if module: row_zero[index + 2] = module["issue_module__module__name"] @@ -360,12 +310,7 @@ def generate_segmented_rows( if segmented == CYCLE_ID: for index, segm in enumerate(row_zero[2:]): cycle = next( - ( - cyc - for cyc in cycle_details - if str(cyc[CYCLE_ID]) == str(segm) - ), - None, + (cyc for cyc in cycle_details if str(cyc[CYCLE_ID]) == str(segm)), None ) if cycle: row_zero[index + 2] = cycle["issue_cycle__cycle__name"] @@ -386,10 +331,7 @@ def generate_non_segmented_rows( ): rows = [] for item, data in distribution.items(): - row = [ - item, - data[0].get("count" if y_axis == "issue_count" else "estimate"), - ] + row = [item, data[0].get("count" if y_axis == "issue_count" else "estimate")] if x_axis == ASSIGNEE_ID: assignee = next( @@ -407,12 +349,7 @@ def generate_non_segmented_rows( if x_axis == LABEL_ID: label = next( - ( - lab - for lab in label_details - if str(lab[LABEL_ID]) == str(item) - ), - None, + (lab for lab in label_details if str(lab[LABEL_ID]) == str(item)), None ) if label: @@ -420,12 +357,7 @@ def generate_non_segmented_rows( if x_axis == STATE_ID: state = next( - ( - sta - for sta in state_details - if str(sta[STATE_ID]) == str(item) - ), - None, + (sta for sta in state_details if str(sta[STATE_ID]) == str(item)), None ) if state: @@ -433,12 +365,7 @@ def generate_non_segmented_rows( if x_axis == CYCLE_ID: cycle = next( - ( - cyc - for cyc in cycle_details - if str(cyc[CYCLE_ID]) == str(item) - ), - None, + (cyc for cyc in cycle_details if str(cyc[CYCLE_ID]) == str(item)), None ) if cycle: @@ -446,11 +373,7 @@ def generate_non_segmented_rows( if x_axis == MODULE_ID: module = next( - ( - mod - for mod in module_details - if str(mod[MODULE_ID]) == str(item) - ), + (mod for mod in module_details if str(mod[MODULE_ID]) == str(item)), None, ) @@ -459,10 +382,7 @@ def generate_non_segmented_rows( rows.append(tuple(row)) - row_zero = [ - row_mapping.get(x_axis, "X-Axis"), - row_mapping.get(y_axis, "Y-Axis"), - ] + row_zero = [row_mapping.get(x_axis, "X-Axis"), row_mapping.get(y_axis, "Y-Axis")] return [tuple(row_zero)] + rows diff --git a/apiserver/plane/bgtasks/deletion_task.py b/apiserver/plane/bgtasks/deletion_task.py index b0b2a6eb2..0752272e3 100644 --- a/apiserver/plane/bgtasks/deletion_task.py +++ b/apiserver/plane/bgtasks/deletion_task.py @@ -10,9 +10,7 @@ from celery import shared_task @shared_task -def soft_delete_related_objects( - app_label, model_name, instance_pk, using=None -): +def soft_delete_related_objects(app_label, model_name, instance_pk, using=None): model_class = apps.get_model(app_label, model_name) instance = model_class.all_objects.get(pk=instance_pk) related_fields = instance._meta.get_fields() @@ -29,9 +27,7 @@ def soft_delete_related_objects( elif field.one_to_one: related_object = getattr(instance, field.name) related_objects = ( - [related_object] - if related_object is not None - else [] + [related_object] if related_object is not None else [] ) for obj in related_objects: @@ -49,7 +45,6 @@ def restore_related_objects(app_label, model_name, instance_pk, using=None): @shared_task def hard_delete(): - from plane.db.models import ( Workspace, Project, diff --git a/apiserver/plane/bgtasks/dummy_data_task.py b/apiserver/plane/bgtasks/dummy_data_task.py index 889286219..a3f95d0bc 100644 --- a/apiserver/plane/bgtasks/dummy_data_task.py +++ b/apiserver/plane/bgtasks/dummy_data_task.py @@ -51,11 +51,7 @@ def create_project(workspace, user_id): ) # Add current member as project member - _ = ProjectMember.objects.create( - project=project, - member_id=user_id, - role=20, - ) + _ = ProjectMember.objects.create(project=project, member_id=user_id, role=20) return project @@ -88,24 +84,14 @@ def create_states(workspace, project, user_id): "group": "backlog", "default": True, }, - { - "name": "Todo", - "color": "#3A3A3A", - "sequence": 25000, - "group": "unstarted", - }, + {"name": "Todo", "color": "#3A3A3A", "sequence": 25000, "group": "unstarted"}, { "name": "In Progress", "color": "#F59E0B", "sequence": 35000, "group": "started", }, - { - "name": "Done", - "color": "#16A34A", - "sequence": 45000, - "group": "completed", - }, + {"name": "Done", "color": "#16A34A", "sequence": 45000, "group": "completed"}, { "name": "Cancelled", "color": "#EF4444", @@ -177,8 +163,7 @@ def create_cycles(workspace, project, user_id, cycle_count): # Ensure end_date is strictly after start_date if start_date is not None while start_date is not None and ( - end_date <= start_date - or (start_date, end_date) in used_date_ranges + end_date <= start_date or (start_date, end_date) in used_date_ranges ): end_date = fake.date_this_year() @@ -259,11 +244,7 @@ def create_pages(workspace, project, user_id, pages_count): # Add Page to project ProjectPage.objects.bulk_create( [ - ProjectPage( - page=page, - project=project, - workspace=workspace, - ) + ProjectPage(page=page, project=project, workspace=workspace) for page in pages ], batch_size=1000, @@ -274,26 +255,16 @@ def create_page_labels(workspace, project, user_id, pages_count): # labels labels = Label.objects.filter(project=project).values_list("id", flat=True) pages = random.sample( - list( - Page.objects.filter(projects__id=project.id).values_list( - "id", flat=True - ) - ), + list(Page.objects.filter(projects__id=project.id).values_list("id", flat=True)), int(pages_count / 2), ) # Bulk page labels bulk_page_labels = [] for page in pages: - for label in random.sample( - list(labels), random.randint(0, len(labels) - 1) - ): + for label in random.sample(list(labels), random.randint(0, len(labels) - 1)): bulk_page_labels.append( - PageLabel( - page_id=page, - label_id=label, - workspace=workspace, - ) + PageLabel(page_id=page, label_id=label, workspace=workspace) ) # Page labels @@ -318,9 +289,7 @@ def create_issues(workspace, project, user_id, issue_count): issues = [] # Get the maximum sequence_id - last_id = IssueSequence.objects.filter( - project=project, - ).aggregate( + last_id = IssueSequence.objects.filter(project=project).aggregate( largest=Max("sequence") )["largest"] @@ -328,8 +297,7 @@ def create_issues(workspace, project, user_id, issue_count): # Get the maximum sort order largest_sort_order = Issue.objects.filter( - project=project, - state_id=states[random.randint(0, len(states) - 1)], + project=project, state_id=states[random.randint(0, len(states) - 1)] ).aggregate(largest=Max("sort_order"))["largest"] largest_sort_order = ( @@ -370,9 +338,7 @@ def create_issues(workspace, project, user_id, issue_count): largest_sort_order = largest_sort_order + random.randint(0, 1000) last_id = last_id + 1 - issues = Issue.objects.bulk_create( - issues, ignore_conflicts=True, batch_size=1000 - ) + issues = Issue.objects.bulk_create(issues, ignore_conflicts=True, batch_size=1000) # Sequences _ = IssueSequence.objects.bulk_create( [ @@ -409,9 +375,7 @@ def create_issues(workspace, project, user_id, issue_count): def create_intake_issues(workspace, project, user_id, intake_issue_count): issues = create_issues(workspace, project, user_id, intake_issue_count) intake, create = Intake.objects.get_or_create( - name="Intake", - project=project, - is_default=True, + name="Intake", project=project, is_default=True ) IntakeIssue.objects.bulk_create( [ @@ -435,21 +399,18 @@ def create_intake_issues(workspace, project, user_id, intake_issue_count): def create_issue_parent(workspace, project, user_id, issue_count): - parent_count = issue_count / 4 - parent_issues = Issue.objects.filter(project=project).values_list( - "id", flat=True - )[: int(parent_count)] - sub_issues = Issue.objects.filter(project=project).exclude( - pk__in=parent_issues - )[: int(issue_count / 2)] + parent_issues = Issue.objects.filter(project=project).values_list("id", flat=True)[ + : int(parent_count) + ] + sub_issues = Issue.objects.filter(project=project).exclude(pk__in=parent_issues)[ + : int(issue_count / 2) + ] bulk_sub_issues = [] for sub_issue in sub_issues: - sub_issue.parent_id = parent_issues[ - random.randint(0, int(parent_count - 1)) - ] + sub_issue.parent_id = parent_issues[random.randint(0, int(parent_count - 1))] Issue.objects.bulk_update(bulk_sub_issues, ["parent"], batch_size=1000) @@ -460,9 +421,7 @@ def create_issue_assignees(workspace, project, user_id, issue_count): "member_id", flat=True ) issues = random.sample( - list( - Issue.objects.filter(project=project).values_list("id", flat=True) - ), + list(Issue.objects.filter(project=project).values_list("id", flat=True)), int(issue_count / 2), ) @@ -496,24 +455,17 @@ def create_issue_labels(workspace, project, user_id, issue_count): # ), # int(issue_count / 2), # ) - issues = list( - Issue.objects.filter(project=project).values_list("id", flat=True) - ) + issues = list(Issue.objects.filter(project=project).values_list("id", flat=True)) shuffled_labels = list(labels) # Bulk issue bulk_issue_labels = [] for issue in issues: random.shuffle(shuffled_labels) - for label in random.sample( - shuffled_labels, random.randint(0, 5) - ): + for label in random.sample(shuffled_labels, random.randint(0, 5)): bulk_issue_labels.append( IssueLabel( - issue_id=issue, - label_id=label, - project=project, - workspace=workspace, + issue_id=issue, label_id=label, project=project, workspace=workspace ) ) @@ -527,9 +479,7 @@ def create_cycle_issues(workspace, project, user_id, issue_count): # assignees cycles = Cycle.objects.filter(project=project).values_list("id", flat=True) issues = random.sample( - list( - Issue.objects.filter(project=project).values_list("id", flat=True) - ), + list(Issue.objects.filter(project=project).values_list("id", flat=True)), int(issue_count / 2), ) @@ -539,10 +489,7 @@ def create_cycle_issues(workspace, project, user_id, issue_count): cycle = cycles[random.randint(0, len(cycles) - 1)] bulk_cycle_issues.append( CycleIssue( - cycle_id=cycle, - issue_id=issue, - project=project, - workspace=workspace, + cycle_id=cycle, issue_id=issue, project=project, workspace=workspace ) ) @@ -554,18 +501,14 @@ def create_cycle_issues(workspace, project, user_id, issue_count): def create_module_issues(workspace, project, user_id, issue_count): # assignees - modules = Module.objects.filter(project=project).values_list( - "id", flat=True - ) + modules = Module.objects.filter(project=project).values_list("id", flat=True) # issues = random.sample( # list( # Issue.objects.filter(project=project).values_list("id", flat=True) # ), # int(issue_count / 2), # ) - issues = list( - Issue.objects.filter(project=project).values_list("id", flat=True) - ) + issues = list(Issue.objects.filter(project=project).values_list("id", flat=True)) shuffled_modules = list(modules) @@ -573,9 +516,7 @@ def create_module_issues(workspace, project, user_id, issue_count): bulk_module_issues = [] for issue in issues: random.shuffle(shuffled_modules) - for module in random.sample( - shuffled_modules, random.randint(0, 5) - ): + for module in random.sample(shuffled_modules, random.randint(0, 5)): bulk_module_issues.append( ModuleIssue( module_id=module, @@ -610,9 +551,7 @@ def create_dummy_data( project = create_project(workspace=workspace, user_id=user_id) # create project members - create_project_members( - workspace=workspace, project=project, members=members - ) + create_project_members(workspace=workspace, project=project, members=members) # Create states create_states(workspace=workspace, project=project, user_id=user_id) @@ -622,42 +561,27 @@ def create_dummy_data( # create cycles create_cycles( - workspace=workspace, - project=project, - user_id=user_id, - cycle_count=cycle_count, + workspace=workspace, project=project, user_id=user_id, cycle_count=cycle_count ) # create modules create_modules( - workspace=workspace, - project=project, - user_id=user_id, - module_count=module_count, + workspace=workspace, project=project, user_id=user_id, module_count=module_count ) # create pages create_pages( - workspace=workspace, - project=project, - user_id=user_id, - pages_count=pages_count, + workspace=workspace, project=project, user_id=user_id, pages_count=pages_count ) # create page labels create_page_labels( - workspace=workspace, - project=project, - user_id=user_id, - pages_count=pages_count, + workspace=workspace, project=project, user_id=user_id, pages_count=pages_count ) # create issues create_issues( - workspace=workspace, - project=project, - user_id=user_id, - issue_count=issue_count, + workspace=workspace, project=project, user_id=user_id, issue_count=issue_count ) # create intake issues @@ -670,42 +594,27 @@ def create_dummy_data( # create issue parent create_issue_parent( - workspace=workspace, - project=project, - user_id=user_id, - issue_count=issue_count, + workspace=workspace, project=project, user_id=user_id, issue_count=issue_count ) # create issue assignees create_issue_assignees( - workspace=workspace, - project=project, - user_id=user_id, - issue_count=issue_count, + workspace=workspace, project=project, user_id=user_id, issue_count=issue_count ) # create issue labels create_issue_labels( - workspace=workspace, - project=project, - user_id=user_id, - issue_count=issue_count, + workspace=workspace, project=project, user_id=user_id, issue_count=issue_count ) # create cycle issues create_cycle_issues( - workspace=workspace, - project=project, - user_id=user_id, - issue_count=issue_count, + workspace=workspace, project=project, user_id=user_id, issue_count=issue_count ) # create module issues create_module_issues( - workspace=workspace, - project=project, - user_id=user_id, - issue_count=issue_count, + workspace=workspace, project=project, user_id=user_id, issue_count=issue_count ) return diff --git a/apiserver/plane/bgtasks/email_notification_task.py b/apiserver/plane/bgtasks/email_notification_task.py index f2db0de59..b0f75b5dc 100644 --- a/apiserver/plane/bgtasks/email_notification_task.py +++ b/apiserver/plane/bgtasks/email_notification_task.py @@ -75,9 +75,7 @@ def stack_email_notification(): for receiver_notification in receiver_notifications: payload.setdefault( receiver_notification.get("entity_identifier"), {} - ).setdefault( - str(receiver_notification.get("triggered_by_id")), [] - ).append( + ).setdefault(str(receiver_notification.get("triggered_by_id")), []).append( receiver_notification.get("data") ) # append processed notifications @@ -184,11 +182,7 @@ def send_email_notification( if acquire_lock(lock_id=lock_id): # get the redis instance ri = redis_instance() - base_api = ( - ri.get(str(issue_id)).decode() - if ri.get(str(issue_id)) - else None - ) + base_api = ri.get(str(issue_id)).decode() if ri.get(str(issue_id)) else None # Skip if base api is not present if not base_api: @@ -283,9 +277,7 @@ def send_email_notification( "name": issue.name, "issue_url": f"{base_api}/{str(issue.project.workspace.slug)}/projects/{str(issue.project.id)}/issues/{str(issue.id)}", }, - "receiver": { - "email": receiver.email, - }, + "receiver": {"email": receiver.email}, "issue_url": f"{base_api}/{str(issue.project.workspace.slug)}/projects/{str(issue.project.id)}/issues/{str(issue.id)}", "project_url": f"{base_api}/{str(issue.project.workspace.slug)}/projects/{str(issue.project.id)}/issues/", "workspace": str(issue.project.workspace.slug), @@ -333,9 +325,7 @@ def send_email_notification( release_lock(lock_id=lock_id) return else: - logging.getLogger("plane").info( - "Duplicate email received skipping" - ) + logging.getLogger("plane").info("Duplicate email received skipping") return except (Issue.DoesNotExist, User.DoesNotExist): release_lock(lock_id=lock_id) diff --git a/apiserver/plane/bgtasks/event_tracking_task.py b/apiserver/plane/bgtasks/event_tracking_task.py index 135ae1dd1..0629db93a 100644 --- a/apiserver/plane/bgtasks/event_tracking_task.py +++ b/apiserver/plane/bgtasks/event_tracking_task.py @@ -17,10 +17,7 @@ def posthogConfiguration(): "key": "POSTHOG_API_KEY", "default": os.environ.get("POSTHOG_API_KEY", None), }, - { - "key": "POSTHOG_HOST", - "default": os.environ.get("POSTHOG_HOST", None), - }, + {"key": "POSTHOG_HOST", "default": os.environ.get("POSTHOG_HOST", None)}, ] ) if POSTHOG_API_KEY and POSTHOG_HOST: @@ -42,10 +39,7 @@ def auth_events(user, email, user_agent, ip, event_name, medium, first_time): properties={ "event_id": uuid.uuid4().hex, "user": {"email": email, "id": str(user)}, - "device_ctx": { - "ip": ip, - "user_agent": user_agent, - }, + "device_ctx": {"ip": ip, "user_agent": user_agent}, "medium": medium, "first_time": first_time, }, @@ -56,9 +50,7 @@ def auth_events(user, email, user_agent, ip, event_name, medium, first_time): @shared_task -def workspace_invite_event( - user, email, user_agent, ip, event_name, accepted_from -): +def workspace_invite_event(user, email, user_agent, ip, event_name, accepted_from): try: POSTHOG_API_KEY, POSTHOG_HOST = posthogConfiguration() @@ -70,10 +62,7 @@ def workspace_invite_event( properties={ "event_id": uuid.uuid4().hex, "user": {"email": email, "id": str(user)}, - "device_ctx": { - "ip": ip, - "user_agent": user_agent, - }, + "device_ctx": {"ip": ip, "user_agent": user_agent}, "accepted_from": accepted_from, }, ) diff --git a/apiserver/plane/bgtasks/export_task.py b/apiserver/plane/bgtasks/export_task.py index b88aad327..2b21d83d9 100644 --- a/apiserver/plane/bgtasks/export_task.py +++ b/apiserver/plane/bgtasks/export_task.py @@ -69,7 +69,9 @@ def create_zip_file(files): def upload_to_s3(zip_file, workspace_id, token_id, slug): - file_name = f"{workspace_id}/export-{slug}-{token_id[:6]}-{str(timezone.now().date())}.zip" + file_name = ( + f"{workspace_id}/export-{slug}-{token_id[:6]}-{str(timezone.now().date())}.zip" + ) expires_in = 7 * 24 * 60 * 60 if settings.USE_MINIO: @@ -98,10 +100,7 @@ def upload_to_s3(zip_file, workspace_id, token_id, slug): presigned_url = presign_s3.generate_presigned_url( "get_object", - Params={ - "Bucket": settings.AWS_STORAGE_BUCKET_NAME, - "Key": file_name, - }, + Params={"Bucket": settings.AWS_STORAGE_BUCKET_NAME, "Key": file_name}, ExpiresIn=expires_in, ) else: @@ -134,10 +133,7 @@ def upload_to_s3(zip_file, workspace_id, token_id, slug): # Generate presigned url for the uploaded file presigned_url = s3.generate_presigned_url( "get_object", - Params={ - "Bucket": settings.AWS_STORAGE_BUCKET_NAME, - "Key": file_name, - }, + Params={"Bucket": settings.AWS_STORAGE_BUCKET_NAME, "Key": file_name}, ExpiresIn=expires_in, ) @@ -164,8 +160,7 @@ def generate_table_row(issue): issue["priority"], ( f"{issue['created_by__first_name']} {issue['created_by__last_name']}" - if issue["created_by__first_name"] - and issue["created_by__last_name"] + if issue["created_by__first_name"] and issue["created_by__last_name"] else "" ), ( @@ -197,8 +192,7 @@ def generate_json_row(issue): "Priority": issue["priority"], "Created By": ( f"{issue['created_by__first_name']} {issue['created_by__last_name']}" - if issue["created_by__first_name"] - and issue["created_by__last_name"] + if issue["created_by__first_name"] and issue["created_by__last_name"] else "" ), "Assignee": ( @@ -208,17 +202,11 @@ def generate_json_row(issue): ), "Labels": issue["labels__name"] if issue["labels__name"] else "", "Cycle Name": issue["issue_cycle__cycle__name"], - "Cycle Start Date": dateConverter( - issue["issue_cycle__cycle__start_date"] - ), + "Cycle Start Date": dateConverter(issue["issue_cycle__cycle__start_date"]), "Cycle End Date": dateConverter(issue["issue_cycle__cycle__end_date"]), "Module Name": issue["issue_module__module__name"], - "Module Start Date": dateConverter( - issue["issue_module__module__start_date"] - ), - "Module Target Date": dateConverter( - issue["issue_module__module__target_date"] - ), + "Module Start Date": dateConverter(issue["issue_module__module__start_date"]), + "Module Target Date": dateConverter(issue["issue_module__module__target_date"]), "Created At": dateTimeConverter(issue["created_at"]), "Updated At": dateTimeConverter(issue["updated_at"]), "Completed At": dateTimeConverter(issue["completed_at"]), @@ -257,11 +245,7 @@ def update_json_row(rows, row): def update_table_row(rows, row): matched_index = next( - ( - index - for index, existing_row in enumerate(rows) - if existing_row[0] == row[0] - ), + (index for index, existing_row in enumerate(rows) if existing_row[0] == row[0]), None, ) @@ -285,9 +269,7 @@ def generate_csv(header, project_id, issues, files): """ Generate CSV export for all the passed issues. """ - rows = [ - header, - ] + rows = [header] for issue in issues: row = generate_table_row(issue) update_table_row(rows, row) @@ -314,9 +296,7 @@ def generate_xlsx(header, project_id, issues, files): @shared_task -def issue_export_task( - provider, workspace_id, project_ids, token_id, multiple, slug -): +def issue_export_task(provider, workspace_id, project_ids, token_id, multiple, slug): try: exporter_instance = ExporterHistory.objects.get(token=token_id) exporter_instance.status = "processing" @@ -331,14 +311,9 @@ def issue_export_task( project__project_projectmember__is_active=True, project__archived_at__isnull=True, ) - .select_related( - "project", "workspace", "state", "parent", "created_by" - ) + .select_related("project", "workspace", "state", "parent", "created_by") .prefetch_related( - "assignees", - "labels", - "issue_cycle__cycle", - "issue_module__module", + "assignees", "labels", "issue_cycle__cycle", "issue_module__module" ) .values( "id", @@ -405,22 +380,12 @@ def issue_export_task( issues = workspace_issues.filter(project__id=project_id) exporter = EXPORTER_MAPPER.get(provider) if exporter is not None: - exporter( - header, - project_id, - issues, - files, - ) + exporter(header, project_id, issues, files) else: exporter = EXPORTER_MAPPER.get(provider) if exporter is not None: - exporter( - header, - workspace_id, - workspace_issues, - files, - ) + exporter(header, workspace_id, workspace_issues, files) zip_buffer = create_zip_file(files) upload_to_s3(zip_buffer, workspace_id, token_id, slug) diff --git a/apiserver/plane/bgtasks/exporter_expired_task.py b/apiserver/plane/bgtasks/exporter_expired_task.py index d408c6476..30b638c84 100644 --- a/apiserver/plane/bgtasks/exporter_expired_task.py +++ b/apiserver/plane/bgtasks/exporter_expired_task.py @@ -19,8 +19,7 @@ from plane.db.models import ExporterHistory def delete_old_s3_link(): # Get a list of keys and IDs to process expired_exporter_history = ExporterHistory.objects.filter( - Q(url__isnull=False) - & Q(created_at__lte=timezone.now() - timedelta(days=8)) + Q(url__isnull=False) & Q(created_at__lte=timezone.now() - timedelta(days=8)) ).values_list("key", "id") if settings.USE_MINIO: s3 = boto3.client( @@ -43,12 +42,8 @@ def delete_old_s3_link(): # Delete object from S3 if file_name: if settings.USE_MINIO: - s3.delete_object( - Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name - ) + s3.delete_object(Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name) else: - s3.delete_object( - Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name - ) + s3.delete_object(Bucket=settings.AWS_STORAGE_BUCKET_NAME, Key=file_name) ExporterHistory.objects.filter(id=exporter_id).update(url=None) diff --git a/apiserver/plane/bgtasks/file_asset_task.py b/apiserver/plane/bgtasks/file_asset_task.py index e05ed6d37..b7b05df3b 100644 --- a/apiserver/plane/bgtasks/file_asset_task.py +++ b/apiserver/plane/bgtasks/file_asset_task.py @@ -19,9 +19,7 @@ def delete_unuploaded_file_asset(): FileAsset.objects.filter( Q( created_at__lt=timezone.now() - - timedelta( - days=int(os.environ.get("UNUPLOADED_ASSET_DELETE_DAYS", "7")) - ) + - timedelta(days=int(os.environ.get("UNUPLOADED_ASSET_DELETE_DAYS", "7"))) ) & Q(is_uploaded=False) ).delete() diff --git a/apiserver/plane/bgtasks/forgot_password_task.py b/apiserver/plane/bgtasks/forgot_password_task.py index f830eb1e2..6e8990ad1 100644 --- a/apiserver/plane/bgtasks/forgot_password_task.py +++ b/apiserver/plane/bgtasks/forgot_password_task.py @@ -18,7 +18,9 @@ from plane.utils.exception_logger import log_exception @shared_task def forgot_password(first_name, email, uidb64, token, current_site): try: - relative_link = f"/accounts/reset-password/?uidb64={uidb64}&token={token}&email={email}" + relative_link = ( + f"/accounts/reset-password/?uidb64={uidb64}&token={token}&email={email}" + ) abs_url = str(current_site) + relative_link ( @@ -39,9 +41,7 @@ def forgot_password(first_name, email, uidb64, token, current_site): "email": email, } - html_content = render_to_string( - "emails/auth/forgot_password.html", context - ) + html_content = render_to_string("emails/auth/forgot_password.html", context) text_content = strip_tags(html_content) diff --git a/apiserver/plane/bgtasks/issue_activities_task.py b/apiserver/plane/bgtasks/issue_activities_task.py index 23239dddb..e0e2ea84e 100644 --- a/apiserver/plane/bgtasks/issue_activities_task.py +++ b/apiserver/plane/bgtasks/issue_activities_task.py @@ -147,12 +147,8 @@ def track_parent( project_id=project_id, workspace_id=workspace_id, comment="updated the parent issue to", - old_identifier=( - old_parent.id if old_parent is not None else None - ), - new_identifier=( - new_parent.id if new_parent is not None else None - ), + old_identifier=(old_parent.id if old_parent is not None else None), + new_identifier=(new_parent.id if new_parent is not None else None), epoch=epoch, ) ) @@ -199,9 +195,7 @@ def track_state( ): if current_instance.get("state_id") != requested_data.get("state_id"): new_state = State.objects.get(pk=requested_data.get("state_id", None)) - old_state = State.objects.get( - pk=current_instance.get("state_id", None) - ) + old_state = State.objects.get(pk=current_instance.get("state_id", None)) issue_activities.append( IssueActivity( @@ -232,9 +226,7 @@ def track_target_date( issue_activities, epoch, ): - if current_instance.get("target_date") != requested_data.get( - "target_date" - ): + if current_instance.get("target_date") != requested_data.get("target_date"): issue_activities.append( IssueActivity( issue_id=issue_id, @@ -306,12 +298,8 @@ def track_labels( issue_activities, epoch, ): - requested_labels = set( - [str(lab) for lab in requested_data.get("label_ids", [])] - ) - current_labels = set( - [str(lab) for lab in current_instance.get("label_ids", [])] - ) + requested_labels = set([str(lab) for lab in requested_data.get("label_ids", [])]) + current_labels = set([str(lab) for lab in current_instance.get("label_ids", [])]) added_labels = requested_labels - current_labels dropped_labels = current_labels - requested_labels @@ -445,9 +433,7 @@ def track_estimate_points( issue_activities, epoch, ): - if current_instance.get("estimate_point") != requested_data.get( - "estimate_point" - ): + if current_instance.get("estimate_point") != requested_data.get("estimate_point"): old_estimate = ( EstimatePoint.objects.filter( pk=current_instance.get("estimate_point") @@ -498,9 +484,7 @@ def track_archive_at( issue_activities, epoch, ): - if current_instance.get("archived_at") != requested_data.get( - "archived_at" - ): + if current_instance.get("archived_at") != requested_data.get("archived_at"): if requested_data.get("archived_at") is None: issue_activities.append( IssueActivity( @@ -594,9 +578,7 @@ def create_issue_activity( issue_activity.created_at = issue.created_at issue_activity.actor_id = issue.created_by_id issue_activity.save(update_fields=["created_at", "actor_id"]) - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None if requested_data.get("assignee_ids") is not None: track_assignees( requested_data, @@ -635,9 +617,7 @@ def update_issue_activity( "closed_to": track_closed_to, } - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -691,9 +671,7 @@ def create_comment_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -725,16 +703,12 @@ def update_comment_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) - if current_instance.get("comment_html") != requested_data.get( - "comment_html" - ): + if current_instance.get("comment_html") != requested_data.get("comment_html"): issue_activities.append( IssueActivity( issue_id=issue_id, @@ -788,18 +762,14 @@ def create_cycle_issue_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) # Updated Records: updated_records = current_instance.get("updated_cycle_issues", []) - created_records = json.loads( - current_instance.get("created_cycle_issues", []) - ) + created_records = json.loads(current_instance.get("created_cycle_issues", [])) for updated_record in updated_records: old_cycle = Cycle.objects.filter( @@ -868,9 +838,7 @@ def delete_cycle_issue_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -911,9 +879,7 @@ def create_module_issue_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None module = Module.objects.filter(pk=requested_data.get("module_id")).first() issue = Issue.objects.filter(pk=issue_id).first() if issue: @@ -946,9 +912,7 @@ def delete_module_issue_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -988,9 +952,7 @@ def create_link_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1021,9 +983,7 @@ def update_link_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1087,9 +1047,7 @@ def create_attachment_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1144,9 +1102,7 @@ def create_issue_reaction_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None if requested_data and requested_data.get("reaction") is not None: issue_reaction = ( IssueReaction.objects.filter( @@ -1218,9 +1174,7 @@ def create_comment_reaction_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None if requested_data and requested_data.get("reaction") is not None: comment_reaction_id, comment_id = ( CommentReaction.objects.filter( @@ -1231,9 +1185,7 @@ def create_comment_reaction_activity( .values_list("id", "comment__id") .first() ) - comment = IssueComment.objects.get( - pk=comment_id, project_id=project_id - ) + comment = IssueComment.objects.get(pk=comment_id, project_id=project_id) if ( comment is not None and comment_reaction_id is not None @@ -1307,9 +1259,7 @@ def create_issue_vote_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None if requested_data and requested_data.get("vote") is not None: issue_activities.append( IssueActivity( @@ -1371,9 +1321,7 @@ def create_issue_relation_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1395,9 +1343,7 @@ def create_issue_relation_activity( epoch=epoch, ) ) - inverse_relation = get_inverse_relation( - requested_data.get("relation_type") - ) + inverse_relation = get_inverse_relation(requested_data.get("relation_type")) issue = Issue.objects.get(pk=issue_id) issue_activities.append( IssueActivity( @@ -1426,9 +1372,7 @@ def delete_issue_relation_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1508,9 +1452,7 @@ def update_draft_issue_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1577,9 +1519,7 @@ def create_intake_activity( issue_activities, epoch, ): - requested_data = ( - json.loads(requested_data) if requested_data is not None else None - ) + requested_data = json.loads(requested_data) if requested_data is not None else None current_instance = ( json.loads(current_instance) if current_instance is not None else None ) @@ -1685,9 +1625,7 @@ def issue_activity( ) # Save all the values to database - issue_activities_created = IssueActivity.objects.bulk_create( - issue_activities - ) + issue_activities_created = IssueActivity.objects.bulk_create(issue_activities) # Post the updates to segway for integrations and webhooks if len(issue_activities_created): for activity in issue_activities_created: @@ -1695,28 +1633,26 @@ def issue_activity( event=( "issue_comment" if activity.field == "comment" - else "intake_issue" if intake else "issue" + else "intake_issue" + if intake + else "issue" ), event_id=( activity.issue_comment_id if activity.field == "comment" - else intake if intake else activity.issue_id + else intake + if intake + else activity.issue_id ), verb=activity.verb, field=( - "description" - if activity.field == "comment" - else activity.field + "description" if activity.field == "comment" else activity.field ), old_value=( - activity.old_value - if activity.old_value != "" - else None + activity.old_value if activity.old_value != "" else None ), new_value=( - activity.new_value - if activity.new_value != "" - else None + activity.new_value if activity.new_value != "" else None ), actor_id=activity.actor_id, current_site=origin, @@ -1733,9 +1669,7 @@ def issue_activity( project_id=project_id, subscriber=subscriber, issue_activities_created=json.dumps( - IssueActivitySerializer( - issue_activities_created, many=True - ).data, + IssueActivitySerializer(issue_activities_created, many=True).data, cls=DjangoJSONEncoder, ), requested_data=requested_data, diff --git a/apiserver/plane/bgtasks/issue_automation_task.py b/apiserver/plane/bgtasks/issue_automation_task.py index 7e50106a0..68f3d32da 100644 --- a/apiserver/plane/bgtasks/issue_automation_task.py +++ b/apiserver/plane/bgtasks/issue_automation_task.py @@ -35,9 +35,7 @@ def archive_old_issues(): Q( project=project_id, archived_at__isnull=True, - updated_at__lte=( - timezone.now() - timedelta(days=archive_in * 30) - ), + updated_at__lte=(timezone.now() - timedelta(days=archive_in * 30)), state__group__in=["completed", "cancelled"], ), Q(issue_cycle__isnull=True) @@ -76,10 +74,7 @@ def archive_old_issues(): issue_activity.delay( type="issue.activity.updated", requested_data=json.dumps( - { - "archived_at": str(archive_at), - "automation": True, - } + {"archived_at": str(archive_at), "automation": True} ), actor_id=str(project.created_by_id), issue_id=issue.id, @@ -113,9 +108,7 @@ def close_old_issues(): Q( project=project_id, archived_at__isnull=True, - updated_at__lte=( - timezone.now() - timedelta(days=close_in * 30) - ), + updated_at__lte=(timezone.now() - timedelta(days=close_in * 30)), state__group__in=["backlog", "unstarted", "started"], ), Q(issue_cycle__isnull=True) @@ -138,9 +131,7 @@ def close_old_issues(): # Check if Issues if issues: if project.default_state is None: - close_state = State.objects.filter( - group="cancelled" - ).first() + close_state = State.objects.filter(group="cancelled").first() else: close_state = project.default_state diff --git a/apiserver/plane/bgtasks/magic_link_code_task.py b/apiserver/plane/bgtasks/magic_link_code_task.py index 7be0ae9f8..848ea623f 100644 --- a/apiserver/plane/bgtasks/magic_link_code_task.py +++ b/apiserver/plane/bgtasks/magic_link_code_task.py @@ -32,9 +32,7 @@ def magic_link(email, key, token, current_site): subject = f"Your unique Plane login code is {token}" context = {"code": token, "email": email} - html_content = render_to_string( - "emails/auth/magic_signin.html", context - ) + html_content = render_to_string("emails/auth/magic_signin.html", context) text_content = strip_tags(html_content) connection = get_connection( diff --git a/apiserver/plane/bgtasks/notification_task.py b/apiserver/plane/bgtasks/notification_task.py index 44f325015..49dc46cb6 100644 --- a/apiserver/plane/bgtasks/notification_task.py +++ b/apiserver/plane/bgtasks/notification_task.py @@ -40,9 +40,7 @@ def update_mentions_for_issue(issue, project, new_mentions, removed_mention): ) IssueMention.objects.bulk_create(aggregated_issue_mentions, batch_size=100) - IssueMention.objects.filter( - issue=issue, mention__in=removed_mention - ).delete() + IssueMention.objects.filter(issue=issue, mention__in=removed_mention).delete() def get_new_mentions(requested_instance, current_instance): @@ -89,14 +87,10 @@ def extract_mentions_as_subscribers(project_id, issue_id, mentions): # If the particular mention has not already been subscribed to the issue, he must be sent the mentioned notification if ( not IssueSubscriber.objects.filter( - issue_id=issue_id, - subscriber_id=mention_id, - project_id=project_id, + issue_id=issue_id, subscriber_id=mention_id, project_id=project_id ).exists() and not IssueAssignee.objects.filter( - project_id=project_id, - issue_id=issue_id, - assignee_id=mention_id, + project_id=project_id, issue_id=issue_id, assignee_id=mention_id ).exists() and not Issue.objects.filter( project_id=project_id, pk=issue_id, created_by_id=mention_id @@ -124,13 +118,9 @@ def extract_mentions(issue_instance): data = json.loads(issue_instance) html = data.get("description_html") soup = BeautifulSoup(html, "html.parser") - mention_tags = soup.find_all( - "mention-component", attrs={"target": "users"} - ) + mention_tags = soup.find_all("mention-component", attrs={"target": "users"}) - mentions = [ - mention_tag["entity_identifier"] for mention_tag in mention_tags - ] + mentions = [mention_tag["entity_identifier"] for mention_tag in mention_tags] return list(set(mentions)) except Exception: @@ -142,9 +132,7 @@ def extract_comment_mentions(comment_value): try: mentions = [] soup = BeautifulSoup(comment_value, "html.parser") - mentions_tags = soup.find_all( - "mention-component", attrs={"target": "users"} - ) + mentions_tags = soup.find_all("mention-component", attrs={"target": "users"}) for mention_tag in mentions_tags: mentions.append(mention_tag["entity_identifier"]) return list(set(mentions)) @@ -167,13 +155,7 @@ def get_new_comment_mentions(new_value, old_value): def create_mention_notification( - project, - notification_comment, - issue, - actor_id, - mention_id, - issue_id, - activity, + project, notification_comment, issue, actor_id, mention_id, issue_id, activity ): return Notification( workspace=project.workspace, @@ -259,25 +241,19 @@ def notifications( # Get new mentions from the newer instance new_mentions = get_new_mentions( - requested_instance=requested_data, - current_instance=current_instance, + requested_instance=requested_data, current_instance=current_instance ) removed_mention = get_removed_mentions( - requested_instance=requested_data, - current_instance=current_instance, + requested_instance=requested_data, current_instance=current_instance ) comment_mentions = [] all_comment_mentions = [] # Get New Subscribers from the mentions of the newer instance - requested_mentions = extract_mentions( - issue_instance=requested_data - ) + requested_mentions = extract_mentions(issue_instance=requested_data) mention_subscribers = extract_mentions_as_subscribers( - project_id=project_id, - issue_id=issue_id, - mentions=requested_mentions, + project_id=project_id, issue_id=issue_id, mentions=requested_mentions ) for issue_activity in issue_activities_created: @@ -299,9 +275,7 @@ def notifications( comment_mentions = comment_mentions + new_comment_mentions comment_mention_subscribers = extract_mentions_as_subscribers( - project_id=project_id, - issue_id=issue_id, - mentions=all_comment_mentions, + project_id=project_id, issue_id=issue_id, mentions=all_comment_mentions ) """ We will not send subscription activity notification to the below mentioned user sets @@ -312,13 +286,9 @@ def notifications( # ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- # issue_subscribers = list( - IssueSubscriber.objects.filter( - project_id=project_id, issue_id=issue_id - ) + IssueSubscriber.objects.filter(project_id=project_id, issue_id=issue_id) .exclude( - subscriber_id__in=list( - new_mentions + comment_mentions + [actor_id] - ) + subscriber_id__in=list(new_mentions + comment_mentions + [actor_id]) ) .values_list("subscriber", flat=True) ) @@ -329,9 +299,7 @@ def notifications( # add the user to issue subscriber try: _ = IssueSubscriber.objects.get_or_create( - project_id=project_id, - issue_id=issue_id, - subscriber_id=actor_id, + project_id=project_id, issue_id=issue_id, subscriber_id=actor_id ) except Exception: pass @@ -342,9 +310,7 @@ def notifications( issue_id=issue_id, project_id=project_id ).values_list("assignee", flat=True) - issue_subscribers = list( - set(issue_subscribers) - {uuid.UUID(actor_id)} - ) + issue_subscribers = list(set(issue_subscribers) - {uuid.UUID(actor_id)}) for subscriber in issue_subscribers: if issue.created_by_id and issue.created_by_id == subscriber: @@ -357,16 +323,11 @@ def notifications( else: sender = "in_app:issue_activities:subscribed" - preference = UserNotificationPreference.objects.get( - user_id=subscriber - ) + preference = UserNotificationPreference.objects.get(user_id=subscriber) for issue_activity in issue_activities_created: # If activity done in blocking then blocked by email should not go - if ( - issue_activity.get("issue_detail").get("id") - != issue_id - ): + if issue_activity.get("issue_detail").get("id") != issue_id: continue # Do not send notification for description update @@ -391,8 +352,7 @@ def notifications( ): send_email = True elif ( - issue_activity.get("field") == "comment" - and preference.comment + issue_activity.get("field") == "comment" and preference.comment ): send_email = True elif preference.property_change: @@ -427,9 +387,7 @@ def notifications( "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str( - issue.project.identifier - ), + "identifier": str(issue.project.identifier), "sequence_id": issue.sequence_id, "state_name": issue.state.name, "state_group": issue.state.group, @@ -438,35 +396,21 @@ def notifications( "id": str(issue_activity.get("id")), "verb": str(issue_activity.get("verb")), "field": str(issue_activity.get("field")), - "actor": str( - issue_activity.get("actor_id") - ), - "new_value": str( - issue_activity.get("new_value") - ), - "old_value": str( - issue_activity.get("old_value") - ), + "actor": str(issue_activity.get("actor_id")), + "new_value": str(issue_activity.get("new_value")), + "old_value": str(issue_activity.get("old_value")), "issue_comment": str( issue_comment.comment_stripped if issue_comment is not None else "" ), "old_identifier": ( - str( - issue_activity.get( - "old_identifier" - ) - ) + str(issue_activity.get("old_identifier")) if issue_activity.get("old_identifier") else None ), "new_identifier": ( - str( - issue_activity.get( - "new_identifier" - ) - ) + str(issue_activity.get("new_identifier")) if issue_activity.get("new_identifier") else None ), @@ -486,9 +430,7 @@ def notifications( "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str( - issue.project.identifier - ), + "identifier": str(issue.project.identifier), "project_id": str(issue.project.id), "workspace_slug": str( issue.project.workspace.slug @@ -499,15 +441,9 @@ def notifications( }, "issue_activity": { "id": str(issue_activity.get("id")), - "verb": str( - issue_activity.get("verb") - ), - "field": str( - issue_activity.get("field") - ), - "actor": str( - issue_activity.get("actor_id") - ), + "verb": str(issue_activity.get("verb")), + "field": str(issue_activity.get("field")), + "actor": str(issue_activity.get("actor_id")), "new_value": str( issue_activity.get("new_value") ), @@ -520,25 +456,13 @@ def notifications( else "" ), "old_identifier": ( - str( - issue_activity.get( - "old_identifier" - ) - ) - if issue_activity.get( - "old_identifier" - ) + str(issue_activity.get("old_identifier")) + if issue_activity.get("old_identifier") else None ), "new_identifier": ( - str( - issue_activity.get( - "new_identifier" - ) - ) - if issue_activity.get( - "new_identifier" - ) + str(issue_activity.get("new_identifier")) + if issue_activity.get("new_identifier") else None ), "activity_time": issue_activity.get( @@ -594,26 +518,18 @@ def notifications( "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str( - issue.project.identifier - ), + "identifier": str(issue.project.identifier), "sequence_id": issue.sequence_id, "state_name": issue.state.name, "state_group": issue.state.group, - "project_id": str( - issue.project.id - ), + "project_id": str(issue.project.id), "workspace_slug": str( issue.project.workspace.slug ), }, "issue_activity": { - "id": str( - issue_activity.get("id") - ), - "verb": str( - issue_activity.get("verb") - ), + "id": str(issue_activity.get("id")), + "verb": str(issue_activity.get("verb")), "field": str("mention"), "actor": str( issue_activity.get("actor_id") @@ -626,24 +542,16 @@ def notifications( ), "old_identifier": ( str( - issue_activity.get( - "old_identifier" - ) - ) - if issue_activity.get( - "old_identifier" + issue_activity.get("old_identifier") ) + if issue_activity.get("old_identifier") else None ), "new_identifier": ( str( - issue_activity.get( - "new_identifier" - ) - ) - if issue_activity.get( - "new_identifier" + issue_activity.get("new_identifier") ) + if issue_activity.get("new_identifier") else None ), "activity_time": issue_activity.get( @@ -679,9 +587,7 @@ def notifications( "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str( - issue.project.identifier - ), + "identifier": str(issue.project.identifier), "sequence_id": issue.sequence_id, "state_name": issue.state.name, "state_group": issue.state.group, @@ -695,32 +601,16 @@ def notifications( "verb": str(last_activity.verb), "field": str(last_activity.field), "actor": str(last_activity.actor_id), - "new_value": str( - last_activity.new_value - ), - "old_value": str( - last_activity.old_value - ), + "new_value": str(last_activity.new_value), + "old_value": str(last_activity.old_value), "old_identifier": ( - str( - issue_activity.get( - "old_identifier" - ) - ) - if issue_activity.get( - "old_identifier" - ) + str(issue_activity.get("old_identifier")) + if issue_activity.get("old_identifier") else None ), "new_identifier": ( - str( - issue_activity.get( - "new_identifier" - ) - ) - if issue_activity.get( - "new_identifier" - ) + str(issue_activity.get("new_identifier")) + if issue_activity.get("new_identifier") else None ), }, @@ -738,9 +628,7 @@ def notifications( "issue": { "id": str(issue_id), "name": str(issue.name), - "identifier": str( - issue.project.identifier - ), + "identifier": str(issue.project.identifier), "sequence_id": issue.sequence_id, "state_name": issue.state.name, "state_group": issue.state.group, @@ -749,35 +637,21 @@ def notifications( "id": str(last_activity.id), "verb": str(last_activity.verb), "field": "mention", - "actor": str( - last_activity.actor_id - ), - "new_value": str( - last_activity.new_value - ), - "old_value": str( - last_activity.old_value - ), + "actor": str(last_activity.actor_id), + "new_value": str(last_activity.new_value), + "old_value": str(last_activity.old_value), "old_identifier": ( str( - issue_activity.get( - "old_identifier" - ) - ) - if issue_activity.get( - "old_identifier" + issue_activity.get("old_identifier") ) + if issue_activity.get("old_identifier") else None ), "new_identifier": ( str( - issue_activity.get( - "new_identifier" - ) - ) - if issue_activity.get( - "new_identifier" + issue_activity.get("new_identifier") ) + if issue_activity.get("new_identifier") else None ), "activity_time": str( @@ -817,27 +691,17 @@ def notifications( "state_group": issue.state.group, }, "issue_activity": { - "id": str( - issue_activity.get("id") - ), - "verb": str( - issue_activity.get("verb") - ), + "id": str(issue_activity.get("id")), + "verb": str(issue_activity.get("verb")), "field": str("mention"), "actor": str( - issue_activity.get( - "actor_id" - ) + issue_activity.get("actor_id") ), "new_value": str( - issue_activity.get( - "new_value" - ) + issue_activity.get("new_value") ), "old_value": str( - issue_activity.get( - "old_value" - ) + issue_activity.get("old_value") ), "old_identifier": ( str( @@ -878,9 +742,7 @@ def notifications( removed_mention=removed_mention, ) # Bulk create notifications - Notification.objects.bulk_create( - bulk_notifications, batch_size=100 - ) + Notification.objects.bulk_create(bulk_notifications, batch_size=100) EmailNotificationLog.objects.bulk_create( bulk_email_logs, batch_size=100, ignore_conflicts=True ) diff --git a/apiserver/plane/bgtasks/page_version_task.py b/apiserver/plane/bgtasks/page_version_task.py index 628fe62b7..7a5f94c9e 100644 --- a/apiserver/plane/bgtasks/page_version_task.py +++ b/apiserver/plane/bgtasks/page_version_task.py @@ -10,20 +10,14 @@ from plane.utils.exception_logger import log_exception @shared_task -def page_version( - page_id, - existing_instance, - user_id, -): +def page_version(page_id, existing_instance, user_id): try: # Get the page page = Page.objects.get(id=page_id) # Get the current instance current_instance = ( - json.loads(existing_instance) - if existing_instance is not None - else {} + json.loads(existing_instance) if existing_instance is not None else {} ) # Create a version if description_html is updated diff --git a/apiserver/plane/bgtasks/recent_visited_task.py b/apiserver/plane/bgtasks/recent_visited_task.py index 9569abf5e..e8e3eb60f 100644 --- a/apiserver/plane/bgtasks/recent_visited_task.py +++ b/apiserver/plane/bgtasks/recent_visited_task.py @@ -10,9 +10,7 @@ from plane.utils.exception_logger import log_exception @shared_task -def recent_visited_task( - entity_name, entity_identifier, user_id, project_id, slug -): +def recent_visited_task(entity_name, entity_identifier, user_id, project_id, slug): try: workspace = Workspace.objects.get(slug=slug) recent_visited = UserRecentVisit.objects.filter( @@ -27,7 +25,6 @@ def recent_visited_task( recent_visited.visited_at = timezone.now() recent_visited.save(update_fields=["visited_at"]) else: - recent_visited_count = UserRecentVisit.objects.filter( user_id=user_id, workspace_id=workspace.id ).count() @@ -51,9 +48,7 @@ def recent_visited_task( ) recent_activity.created_by_id = user_id recent_activity.updated_by_id = user_id - recent_activity.save( - update_fields=["created_by_id", "updated_by_id"] - ) + recent_activity.save(update_fields=["created_by_id", "updated_by_id"]) return except Exception as e: diff --git a/apiserver/plane/bgtasks/user_activation_email_task.py b/apiserver/plane/bgtasks/user_activation_email_task.py index 2fdfc4ddb..23f0e1d01 100644 --- a/apiserver/plane/bgtasks/user_activation_email_task.py +++ b/apiserver/plane/bgtasks/user_activation_email_task.py @@ -22,15 +22,10 @@ def user_activation_email(current_site, user_id): user = User.objects.get(id=user_id) subject = f"{user.first_name or user.display_name or user.email} has been activated on Plane" - context = { - "email": str(user.email), - "profile_url": current_site + "/profile", - } + context = {"email": str(user.email), "profile_url": current_site + "/profile"} # Send email to user - html_content = render_to_string( - "emails/user/user_activation.html", context - ) + html_content = render_to_string("emails/user/user_activation.html", context) text_content = strip_tags(html_content) # Configure email connection from the database diff --git a/apiserver/plane/bgtasks/user_deactivation_email_task.py b/apiserver/plane/bgtasks/user_deactivation_email_task.py index fa8523d50..9425dc324 100644 --- a/apiserver/plane/bgtasks/user_deactivation_email_task.py +++ b/apiserver/plane/bgtasks/user_deactivation_email_task.py @@ -22,15 +22,10 @@ def user_deactivation_email(current_site, user_id): user = User.objects.get(id=user_id) subject = f"{user.first_name or user.display_name or user.email} has been deactivated on Plane" - context = { - "email": str(user.email), - "login_url": current_site + "/login", - } + context = {"email": str(user.email), "login_url": current_site + "/login"} # Send email to user - html_content = render_to_string( - "emails/user/user_deactivation.html", context - ) + html_content = render_to_string("emails/user/user_deactivation.html", context) text_content = strip_tags(html_content) # Configure email connection from the database diff --git a/apiserver/plane/bgtasks/webhook_task.py b/apiserver/plane/bgtasks/webhook_task.py index 4670b084d..d5d8b0043 100644 --- a/apiserver/plane/bgtasks/webhook_task.py +++ b/apiserver/plane/bgtasks/webhook_task.py @@ -131,12 +131,7 @@ def webhook_task(self, webhook, slug, event, event_data, action, current_site): headers["X-Plane-Signature"] = signature # Send the webhook event - response = requests.post( - webhook.url, - headers=headers, - json=payload, - timeout=30, - ) + response = requests.post(webhook.url, headers=headers, json=payload, timeout=30) # Log the webhook request WebhookLog.objects.create( @@ -190,9 +185,7 @@ def webhook_task(self, webhook, slug, event, event_data, action, current_site): @shared_task -def send_webhook_deactivation_email( - webhook_id, receiver_id, current_site, reason -): +def send_webhook_deactivation_email(webhook_id, receiver_id, current_site, reason): # Get email configurations ( EMAIL_HOST, @@ -207,9 +200,7 @@ def send_webhook_deactivation_email( receiver = User.objects.get(pk=receiver_id) webhook = Webhook.objects.get(pk=webhook_id) subject = "Webhook Deactivated" - message = ( - f"Webhook {webhook.url} has been deactivated due to failed requests." - ) + message = f"Webhook {webhook.url} has been deactivated due to failed requests." # Send the mail context = { @@ -256,14 +247,7 @@ def send_webhook_deactivation_email( retry_jitter=True, ) def webhook_send_task( - self, - webhook, - slug, - event, - event_data, - action, - current_site, - activity, + self, webhook, slug, event, event_data, action, current_site, activity ): try: webhook = Webhook.objects.get(id=webhook, workspace__slug=slug) @@ -315,12 +299,7 @@ def webhook_send_task( headers["X-Plane-Signature"] = signature # Send the webhook event - response = requests.post( - webhook.url, - headers=headers, - json=payload, - timeout=30, - ) + response = requests.post(webhook.url, headers=headers, json=payload, timeout=30) # Log the webhook request WebhookLog.objects.create( @@ -408,10 +387,7 @@ def webhook_activity( webhook=webhook.id, slug=slug, event=event, - event_data=get_model_data( - event=event, - event_id=event_id, - ), + event_data=get_model_data(event=event, event_id=event_id), action=verb, current_site=current_site, activity={ @@ -436,13 +412,7 @@ def webhook_activity( @shared_task def model_activity( - model_name, - model_id, - requested_data, - current_instance, - actor_id, - slug, - origin=None, + model_name, model_id, requested_data, current_instance, actor_id, slug, origin=None ): """Function takes in two json and computes differences between keys of both the json""" if current_instance is None: diff --git a/apiserver/plane/db/management/commands/clear_cache.py b/apiserver/plane/db/management/commands/clear_cache.py index c1908eee7..c9189ca32 100644 --- a/apiserver/plane/db/management/commands/clear_cache.py +++ b/apiserver/plane/db/management/commands/clear_cache.py @@ -8,18 +8,14 @@ class Command(BaseCommand): def add_arguments(self, parser): # Positional argument - parser.add_argument( - "--key", type=str, nargs="?", help="Key to clear cache" - ) + parser.add_argument("--key", type=str, nargs="?", help="Key to clear cache") def handle(self, *args, **options): try: if options["key"]: cache.delete(options["key"]) self.stdout.write( - self.style.SUCCESS( - f"Cache Cleared for key: {options['key']}" - ) + self.style.SUCCESS(f"Cache Cleared for key: {options['key']}") ) return diff --git a/apiserver/plane/db/management/commands/create_bucket.py b/apiserver/plane/db/management/commands/create_bucket.py index 9313b6b1c..838edd6c6 100644 --- a/apiserver/plane/db/management/commands/create_bucket.py +++ b/apiserver/plane/db/management/commands/create_bucket.py @@ -15,9 +15,7 @@ class Command(BaseCommand): try: s3_client = boto3.client( "s3", - endpoint_url=os.environ.get( - "AWS_S3_ENDPOINT_URL" - ), # MinIO endpoint + endpoint_url=os.environ.get("AWS_S3_ENDPOINT_URL"), # MinIO endpoint aws_access_key_id=os.environ.get( "AWS_ACCESS_KEY_ID" ), # MinIO access key @@ -33,9 +31,7 @@ class Command(BaseCommand): # Check if the bucket exists s3_client.head_bucket(Bucket=bucket_name) # If the bucket exists, print a success message - self.stdout.write( - self.style.SUCCESS(f"Bucket '{bucket_name}' exists.") - ) + self.stdout.write(self.style.SUCCESS(f"Bucket '{bucket_name}' exists.")) return except ClientError as e: error_code = int(e.response["Error"]["Code"]) @@ -58,9 +54,7 @@ class Command(BaseCommand): # Handle the exception if the bucket creation fails except ClientError as create_error: self.stdout.write( - self.style.ERROR( - f"Failed to create bucket: {create_error}" - ) + self.style.ERROR(f"Failed to create bucket: {create_error}") ) # Handle the exception if access to the bucket is forbidden @@ -73,9 +67,7 @@ class Command(BaseCommand): ) else: # Another ClientError occurred - self.stdout.write( - self.style.ERROR(f"Failed to check bucket: {e}") - ) + self.stdout.write(self.style.ERROR(f"Failed to check bucket: {e}")) except Exception as ex: # Handle any other exception self.stdout.write(self.style.ERROR(f"An error occurred: {ex}")) diff --git a/apiserver/plane/db/management/commands/create_dummy_data.py b/apiserver/plane/db/management/commands/create_dummy_data.py index 64637864d..3eedc390c 100644 --- a/apiserver/plane/db/management/commands/create_dummy_data.py +++ b/apiserver/plane/db/management/commands/create_dummy_data.py @@ -10,7 +10,6 @@ class Command(BaseCommand): help = "Create dump issues, cycles etc. for a project in a given workspace" def handle(self, *args: Any, **options: Any) -> str | None: - try: workspace_name = input("Workspace Name: ") workspace_slug = input("Workspace slug: ") @@ -23,10 +22,7 @@ class Command(BaseCommand): creator = input("Your email: ") - if ( - creator == "" - or not User.objects.filter(email=creator).exists() - ): + if creator == "" or not User.objects.filter(email=creator).exists(): raise CommandError( "User email is required and should have signed in plane" ) @@ -37,23 +33,15 @@ class Command(BaseCommand): members = members.split(",") if members != "" else [] # Create workspace workspace = Workspace.objects.create( - slug=workspace_slug, - name=workspace_name, - owner=user, + slug=workspace_slug, name=workspace_name, owner=user ) # Create workspace member - WorkspaceMember.objects.create( - workspace=workspace, role=20, member=user - ) + WorkspaceMember.objects.create(workspace=workspace, role=20, member=user) user_ids = User.objects.filter(email__in=members) _ = WorkspaceMember.objects.bulk_create( [ - WorkspaceMember( - workspace=workspace, - member=user_id, - role=20, - ) + WorkspaceMember(workspace=workspace, member=user_id, role=20) for user_id in user_ids ], ignore_conflicts=True, @@ -62,9 +50,7 @@ class Command(BaseCommand): project_count = int(input("Number of projects to be created: ")) for i in range(project_count): - print( - f"Please provide the following details for project {i+1}:" - ) + print(f"Please provide the following details for project {i+1}:") issue_count = int(input("Number of issues to be created: ")) cycle_count = int(input("Number of cycles to be created: ")) module_count = int(input("Number of modules to be created: ")) @@ -86,12 +72,8 @@ class Command(BaseCommand): intake_issue_count=intake_issue_count, ) - self.stdout.write( - self.style.SUCCESS("Data is pushed to the queue") - ) + self.stdout.write(self.style.SUCCESS("Data is pushed to the queue")) return except Exception as e: - self.stdout.write( - self.style.ERROR(f"Command errored out {str(e)}") - ) + self.stdout.write(self.style.ERROR(f"Command errored out {str(e)}")) return diff --git a/apiserver/plane/db/management/commands/create_instance_admin.py b/apiserver/plane/db/management/commands/create_instance_admin.py index 21f79c15e..8b957f7fc 100644 --- a/apiserver/plane/db/management/commands/create_instance_admin.py +++ b/apiserver/plane/db/management/commands/create_instance_admin.py @@ -11,12 +11,9 @@ class Command(BaseCommand): def add_arguments(self, parser): # Positional argument - parser.add_argument( - "admin_email", type=str, help="Instance Admin Email" - ) + parser.add_argument("admin_email", type=str, help="Instance Admin Email") def handle(self, *args, **options): - admin_email = options.get("admin_email", False) if not admin_email: @@ -36,13 +33,9 @@ class Command(BaseCommand): ) if not created: - raise CommandError( - "The provided email is already an instance admin." - ) + raise CommandError("The provided email is already an instance admin.") - self.stdout.write( - self.style.SUCCESS("Successfully created the admin") - ) + self.stdout.write(self.style.SUCCESS("Successfully created the admin")) except Exception as e: print(e) raise CommandError("Failed to create the instance admin.") diff --git a/apiserver/plane/db/management/commands/reset_password.py b/apiserver/plane/db/management/commands/reset_password.py index 9c137d320..8ec472bfa 100644 --- a/apiserver/plane/db/management/commands/reset_password.py +++ b/apiserver/plane/db/management/commands/reset_password.py @@ -52,15 +52,11 @@ class Command(BaseCommand): results = zxcvbn(password) if results["score"] < 3: - raise CommandError( - "Password is too common please set a complex password" - ) + raise CommandError("Password is too common please set a complex password") # Set user password user.set_password(password) user.is_password_autoset = False user.save() - self.stdout.write( - self.style.SUCCESS("User password updated succesfully") - ) + self.stdout.write(self.style.SUCCESS("User password updated succesfully")) diff --git a/apiserver/plane/db/management/commands/test_email.py b/apiserver/plane/db/management/commands/test_email.py index facea7e9c..2ed20eeb3 100644 --- a/apiserver/plane/db/management/commands/test_email.py +++ b/apiserver/plane/db/management/commands/test_email.py @@ -53,9 +53,7 @@ class Command(BaseCommand): subject=subject, body=text_content, from_email=EMAIL_FROM, - to=[ - receiver_email, - ], + to=[receiver_email], connection=connection, ) msg.attach_alternative(html_content, "text/html") @@ -63,7 +61,5 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS("Email successfully sent")) except Exception as e: self.stdout.write( - self.style.ERROR( - f"Error: Email could not be delivered due to {e}" - ) + self.style.ERROR(f"Error: Email could not be delivered due to {e}") ) diff --git a/apiserver/plane/db/management/commands/update_bucket.py b/apiserver/plane/db/management/commands/update_bucket.py index 4feddef2e..27eb5c83e 100644 --- a/apiserver/plane/db/management/commands/update_bucket.py +++ b/apiserver/plane/db/management/commands/update_bucket.py @@ -14,12 +14,8 @@ class Command(BaseCommand): def get_s3_client(self): s3_client = boto3.client( "s3", - endpoint_url=os.environ.get( - "AWS_S3_ENDPOINT_URL" - ), # MinIO endpoint - aws_access_key_id=os.environ.get( - "AWS_ACCESS_KEY_ID" - ), # MinIO access key + endpoint_url=os.environ.get("AWS_S3_ENDPOINT_URL"), # MinIO endpoint + aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"), # MinIO access key aws_secret_access_key=os.environ.get( "AWS_SECRET_ACCESS_KEY" ), # MinIO secret key @@ -64,9 +60,7 @@ class Command(BaseCommand): # 3. Test s3:PutObject (attempt to upload an object) try: s3_client.put_object( - Bucket=bucket_name, - Key="test_permission_check.txt", - Body=b"Test", + Bucket=bucket_name, Key="test_permission_check.txt", Body=b"Test" ) permissions["s3:PutObject"] = True # Clean up @@ -78,9 +72,7 @@ class Command(BaseCommand): # Clean up try: - s3_client.delete_object( - Bucket=bucket_name, Key="test_permission_check.txt" - ) + s3_client.delete_object(Bucket=bucket_name, Key="test_permission_check.txt") except ClientError: self.stdout.write("Coudn't delete test object") @@ -97,9 +89,7 @@ class Command(BaseCommand): } ], } - s3_client.put_bucket_policy( - Bucket=bucket_name, Policy=json.dumps(policy) - ) + s3_client.put_bucket_policy(Bucket=bucket_name, Policy=json.dumps(policy)) permissions["s3:PutBucketPolicy"] = True except ClientError as e: if e.response["Error"]["Code"] == "AccessDenied": @@ -142,9 +132,7 @@ class Command(BaseCommand): Bucket=bucket_name, Policy=json.dumps(bucket_policy) ) # Print a success message - self.stdout.write( - "Bucket is private, but existing objects remain public." - ) + self.stdout.write("Bucket is private, but existing objects remain public.") return def handle(self, *args, **options): @@ -177,9 +165,7 @@ class Command(BaseCommand): else: self.stdout.write(f"Error: {e}") # If the bucket exists, print a success message - self.stdout.write( - self.style.SUCCESS(f"Bucket '{bucket_name}' exists.") - ) + self.stdout.write(self.style.SUCCESS(f"Bucket '{bucket_name}' exists.")) try: # Check the permissions of the access key @@ -192,9 +178,7 @@ class Command(BaseCommand): try: if all(permissions.values()): self.stdout.write( - self.style.SUCCESS( - "Access key has the required permissions." - ) + self.style.SUCCESS("Access key has the required permissions.") ) # Making the existing objects public self.make_objects_public(bucket_name) @@ -213,9 +197,7 @@ class Command(BaseCommand): with open("permissions.json", "w") as f: f.write(json.dumps(self.generate_bucket_policy(bucket_name))) self.stdout.write( - self.style.WARNING( - "Permissions have been written to permissions.json." - ) + self.style.WARNING("Permissions have been written to permissions.json.") ) return except IOError as e: diff --git a/apiserver/plane/db/mixins.py b/apiserver/plane/db/mixins.py index 4d4cee978..b198de121 100644 --- a/apiserver/plane/db/mixins.py +++ b/apiserver/plane/db/mixins.py @@ -9,13 +9,8 @@ from plane.bgtasks.deletion_task import soft_delete_related_objects class TimeAuditModel(models.Model): """To path when the record was created and last modified""" - created_at = models.DateTimeField( - auto_now_add=True, - verbose_name="Created At", - ) - updated_at = models.DateTimeField( - auto_now=True, verbose_name="Last Modified At" - ) + created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created At") + updated_at = models.DateTimeField(auto_now=True, verbose_name="Last Modified At") class Meta: abstract = True @@ -61,11 +56,7 @@ class SoftDeletionManager(models.Manager): class SoftDeleteModel(models.Model): """To soft delete records""" - deleted_at = models.DateTimeField( - verbose_name="Deleted At", - null=True, - blank=True, - ) + deleted_at = models.DateTimeField(verbose_name="Deleted At", null=True, blank=True) objects = SoftDeletionManager() all_objects = models.Manager() @@ -80,10 +71,7 @@ class SoftDeleteModel(models.Model): self.save(using=using) soft_delete_related_objects.delay( - self._meta.app_label, - self._meta.model_name, - self.pk, - using=using, + self._meta.app_label, self._meta.model_name, self.pk, using=using ) else: diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index ff930447a..d00a2fa07 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -42,25 +42,9 @@ from .issue import ( IssueSubscriber, IssueVote, ) -from .module import ( - Module, - ModuleIssue, - ModuleLink, - ModuleMember, - ModuleUserProperties, -) -from .notification import ( - EmailNotificationLog, - Notification, - UserNotificationPreference, -) -from .page import ( - Page, - PageLabel, - PageLog, - ProjectPage, - PageVersion, -) +from .module import Module, ModuleIssue, ModuleLink, ModuleMember, ModuleUserProperties +from .notification import EmailNotificationLog, Notification, UserNotificationPreference +from .page import Page, PageLabel, PageLog, ProjectPage, PageVersion from .project import ( Project, ProjectBaseModel, @@ -97,11 +81,7 @@ from .intake import Intake, IntakeIssue from .analytic import AnalyticView -from .notification import ( - Notification, - UserNotificationPreference, - EmailNotificationLog, -) +from .notification import Notification, UserNotificationPreference, EmailNotificationLog from .exporter import ExporterHistory diff --git a/apiserver/plane/db/models/api.py b/apiserver/plane/db/models/api.py index bc24ee8a8..01be8e643 100644 --- a/apiserver/plane/db/models/api.py +++ b/apiserver/plane/db/models/api.py @@ -30,18 +30,13 @@ class APIToken(BaseModel): # User Information user = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="bot_tokens", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="bot_tokens" ) user_type = models.PositiveSmallIntegerField( choices=((0, "Human"), (1, "Bot")), default=0 ) workspace = models.ForeignKey( - "db.Workspace", - related_name="api_tokens", - on_delete=models.CASCADE, - null=True, + "db.Workspace", related_name="api_tokens", on_delete=models.CASCADE, null=True ) expired_at = models.DateTimeField(blank=True, null=True) is_service = models.BooleanField(default=False) diff --git a/apiserver/plane/db/models/asset.py b/apiserver/plane/db/models/asset.py index e230d3aec..d7a380003 100644 --- a/apiserver/plane/db/models/asset.py +++ b/apiserver/plane/db/models/asset.py @@ -11,7 +11,6 @@ from .base import BaseModel def get_upload_path(instance, filename): - if instance.workspace_id is not None: return f"{instance.workspace.id}/{uuid4().hex}-{filename}" return f"user-{uuid4().hex}-{filename}" @@ -40,57 +39,30 @@ class FileAsset(BaseModel): DRAFT_ISSUE_DESCRIPTION = "DRAFT_ISSUE_DESCRIPTION" attributes = models.JSONField(default=dict) - asset = models.FileField( - upload_to=get_upload_path, - max_length=800, - ) + asset = models.FileField(upload_to=get_upload_path, max_length=800) user = models.ForeignKey( - "db.User", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.User", on_delete=models.CASCADE, null=True, related_name="assets" ) workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.Workspace", on_delete=models.CASCADE, null=True, related_name="assets" ) draft_issue = models.ForeignKey( - "db.DraftIssue", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.DraftIssue", on_delete=models.CASCADE, null=True, related_name="assets" ) project = models.ForeignKey( - "db.Project", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.Project", on_delete=models.CASCADE, null=True, related_name="assets" ) issue = models.ForeignKey( - "db.Issue", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.Issue", on_delete=models.CASCADE, null=True, related_name="assets" ) comment = models.ForeignKey( - "db.IssueComment", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.IssueComment", on_delete=models.CASCADE, null=True, related_name="assets" ) page = models.ForeignKey( - "db.Page", - on_delete=models.CASCADE, - null=True, - related_name="assets", + "db.Page", on_delete=models.CASCADE, null=True, related_name="assets" ) entity_type = models.CharField( - max_length=255, - choices=EntityTypeContext.choices, - null=True, - blank=True, + max_length=255, choices=EntityTypeContext.choices, null=True, blank=True ) is_deleted = models.BooleanField(default=False) is_archived = models.BooleanField(default=False) diff --git a/apiserver/plane/db/models/base.py b/apiserver/plane/db/models/base.py index 63c08afa4..d0531e881 100644 --- a/apiserver/plane/db/models/base.py +++ b/apiserver/plane/db/models/base.py @@ -12,11 +12,7 @@ from ..mixins import AuditModel class BaseModel(AuditModel): id = models.UUIDField( - default=uuid.uuid4, - unique=True, - editable=False, - db_index=True, - primary_key=True, + default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) class Meta: diff --git a/apiserver/plane/db/models/cycle.py b/apiserver/plane/db/models/cycle.py index c3dccb0c8..6449fd145 100644 --- a/apiserver/plane/db/models/cycle.py +++ b/apiserver/plane/db/models/cycle.py @@ -55,15 +55,9 @@ def get_default_display_properties(): class Cycle(ProjectBaseModel): name = models.CharField(max_length=255, verbose_name="Cycle Name") - description = models.TextField( - verbose_name="Cycle Description", blank=True - ) - start_date = models.DateTimeField( - verbose_name="Start Date", blank=True, null=True - ) - end_date = models.DateTimeField( - verbose_name="End Date", blank=True, null=True - ) + description = models.TextField(verbose_name="Cycle Description", blank=True) + start_date = models.DateTimeField(verbose_name="Start Date", blank=True, null=True) + end_date = models.DateTimeField(verbose_name="End Date", blank=True, null=True) owned_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, @@ -78,9 +72,7 @@ class Cycle(ProjectBaseModel): logo_props = models.JSONField(default=dict) # timezone TIMEZONE_CHOICES = tuple(zip(pytz.all_timezones, pytz.all_timezones)) - timezone = models.CharField( - max_length=255, default="UTC", choices=TIMEZONE_CHOICES - ) + timezone = models.CharField(max_length=255, default="UTC", choices=TIMEZONE_CHOICES) version = models.IntegerField(default=1) class Meta: @@ -91,9 +83,9 @@ class Cycle(ProjectBaseModel): def save(self, *args, **kwargs): if self._state.adding: - smallest_sort_order = Cycle.objects.filter( - project=self.project - ).aggregate(smallest=models.Min("sort_order"))["smallest"] + smallest_sort_order = Cycle.objects.filter(project=self.project).aggregate( + smallest=models.Min("sort_order") + )["smallest"] if smallest_sort_order is not None: self.sort_order = smallest_sort_order - 10000 @@ -137,9 +129,7 @@ class CycleIssue(ProjectBaseModel): class CycleUserProperties(ProjectBaseModel): cycle = models.ForeignKey( - "db.Cycle", - on_delete=models.CASCADE, - related_name="cycle_user_properties", + "db.Cycle", on_delete=models.CASCADE, related_name="cycle_user_properties" ) user = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -148,9 +138,7 @@ class CycleUserProperties(ProjectBaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField( - default=get_default_display_properties - ) + display_properties = models.JSONField(default=get_default_display_properties) class Meta: unique_together = ["cycle", "user", "deleted_at"] diff --git a/apiserver/plane/db/models/dashboard.py b/apiserver/plane/db/models/dashboard.py index b9c3c45b0..e0f401753 100644 --- a/apiserver/plane/db/models/dashboard.py +++ b/apiserver/plane/db/models/dashboard.py @@ -20,9 +20,7 @@ class Dashboard(BaseModel): description_html = models.TextField(blank=True, default="

") identifier = models.UUIDField(null=True) owned_by = models.ForeignKey( - "db.User", - on_delete=models.CASCADE, - related_name="dashboards", + "db.User", on_delete=models.CASCADE, related_name="dashboards" ) is_default = models.BooleanField(default=False) type_identifier = models.CharField( @@ -46,11 +44,7 @@ class Dashboard(BaseModel): class Widget(TimeAuditModel): id = models.UUIDField( - default=uuid.uuid4, - unique=True, - editable=False, - db_index=True, - primary_key=True, + default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) key = models.CharField(max_length=255) filters = models.JSONField(default=dict) @@ -69,14 +63,10 @@ class Widget(TimeAuditModel): class DashboardWidget(BaseModel): widget = models.ForeignKey( - Widget, - on_delete=models.CASCADE, - related_name="dashboard_widgets", + Widget, on_delete=models.CASCADE, related_name="dashboard_widgets" ) dashboard = models.ForeignKey( - Dashboard, - on_delete=models.CASCADE, - related_name="dashboard_widgets", + Dashboard, on_delete=models.CASCADE, related_name="dashboard_widgets" ) is_visible = models.BooleanField(default=True) sort_order = models.FloatField(default=65535) diff --git a/apiserver/plane/db/models/deploy_board.py b/apiserver/plane/db/models/deploy_board.py index f72d1155b..f053f4a82 100644 --- a/apiserver/plane/db/models/deploy_board.py +++ b/apiserver/plane/db/models/deploy_board.py @@ -24,21 +24,14 @@ class DeployBoard(WorkspaceBaseModel): ) entity_identifier = models.UUIDField(null=True) - entity_name = models.CharField( - max_length=30, - null=True, - blank=True, - ) + entity_name = models.CharField(max_length=30, null=True, blank=True) anchor = models.CharField( max_length=255, default=get_anchor, unique=True, db_index=True ) is_comments_enabled = models.BooleanField(default=False) is_reactions_enabled = models.BooleanField(default=False) intake = models.ForeignKey( - "db.Intake", - related_name="publish_intake", - on_delete=models.SET_NULL, - null=True, + "db.Intake", related_name="publish_intake", on_delete=models.SET_NULL, null=True ) is_votes_enabled = models.BooleanField(default=False) view_props = models.JSONField(default=dict) diff --git a/apiserver/plane/db/models/device.py b/apiserver/plane/db/models/device.py index 862861f90..055d8ccc4 100644 --- a/apiserver/plane/db/models/device.py +++ b/apiserver/plane/db/models/device.py @@ -12,9 +12,7 @@ class Device(BaseModel): DESKTOP = "DESKTOP", "Desktop" user = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="devices", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="devices" ) device_id = models.CharField(max_length=255, blank=True, null=True) device_type = models.CharField(max_length=255, choices=DeviceType.choices) @@ -29,14 +27,10 @@ class Device(BaseModel): class DeviceSession(BaseModel): device = models.ForeignKey( - Device, - on_delete=models.CASCADE, - related_name="sessions", + Device, on_delete=models.CASCADE, related_name="sessions" ) session = models.ForeignKey( - "db.Session", - on_delete=models.CASCADE, - related_name="device_sessions", + "db.Session", on_delete=models.CASCADE, related_name="device_sessions" ) is_active = models.BooleanField(default=True) user_agent = models.CharField(max_length=255, null=True, blank=True) diff --git a/apiserver/plane/db/models/draft.py b/apiserver/plane/db/models/draft.py index e80ccdaaf..42148d5bb 100644 --- a/apiserver/plane/db/models/draft.py +++ b/apiserver/plane/db/models/draft.py @@ -61,10 +61,7 @@ class DraftIssue(WorkspaceBaseModel): through_fields=("draft_issue", "assignee"), ) labels = models.ManyToManyField( - "db.Label", - blank=True, - related_name="draft_labels", - through="DraftIssueLabel", + "db.Label", blank=True, related_name="draft_labels", through="DraftIssueLabel" ) sort_order = models.FloatField(default=65535) completed_at = models.DateTimeField(null=True) @@ -90,9 +87,7 @@ class DraftIssue(WorkspaceBaseModel): from plane.db.models import State default_state = State.objects.filter( - ~models.Q(is_triage=True), - project=self.project, - default=True, + ~models.Q(is_triage=True), project=self.project, default=True ).first() if default_state is None: random_state = State.objects.filter( @@ -118,10 +113,7 @@ class DraftIssue(WorkspaceBaseModel): # Strip the html tags using html parser self.description_stripped = ( None - if ( - self.description_html == "" - or self.description_html is None - ) + if (self.description_html == "" or self.description_html is None) else strip_tags(self.description_html) ) largest_sort_order = DraftIssue.objects.filter( @@ -136,10 +128,7 @@ class DraftIssue(WorkspaceBaseModel): # Strip the html tags using html parser self.description_stripped = ( None - if ( - self.description_html == "" - or self.description_html is None - ) + if (self.description_html == "" or self.description_html is None) else strip_tags(self.description_html) ) super(DraftIssue, self).save(*args, **kwargs) @@ -151,9 +140,7 @@ class DraftIssue(WorkspaceBaseModel): class DraftIssueAssignee(WorkspaceBaseModel): draft_issue = models.ForeignKey( - DraftIssue, - on_delete=models.CASCADE, - related_name="draft_issue_assignee", + DraftIssue, on_delete=models.CASCADE, related_name="draft_issue_assignee" ) assignee = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -181,9 +168,7 @@ class DraftIssueAssignee(WorkspaceBaseModel): class DraftIssueLabel(WorkspaceBaseModel): draft_issue = models.ForeignKey( - "db.DraftIssue", - on_delete=models.CASCADE, - related_name="draft_label_issue", + "db.DraftIssue", on_delete=models.CASCADE, related_name="draft_label_issue" ) label = models.ForeignKey( "db.Label", on_delete=models.CASCADE, related_name="draft_label_issue" @@ -201,14 +186,10 @@ class DraftIssueLabel(WorkspaceBaseModel): class DraftIssueModule(WorkspaceBaseModel): module = models.ForeignKey( - "db.Module", - on_delete=models.CASCADE, - related_name="draft_issue_module", + "db.Module", on_delete=models.CASCADE, related_name="draft_issue_module" ) draft_issue = models.ForeignKey( - "db.DraftIssue", - on_delete=models.CASCADE, - related_name="draft_issue_module", + "db.DraftIssue", on_delete=models.CASCADE, related_name="draft_issue_module" ) class Meta: @@ -235,9 +216,7 @@ class DraftIssueCycle(WorkspaceBaseModel): """ draft_issue = models.ForeignKey( - "db.DraftIssue", - on_delete=models.CASCADE, - related_name="draft_issue_cycle", + "db.DraftIssue", on_delete=models.CASCADE, related_name="draft_issue_cycle" ) cycle = models.ForeignKey( "db.Cycle", on_delete=models.CASCADE, related_name="draft_issue_cycle" diff --git a/apiserver/plane/db/models/estimate.py b/apiserver/plane/db/models/estimate.py index eb2eaf49b..b0097562d 100644 --- a/apiserver/plane/db/models/estimate.py +++ b/apiserver/plane/db/models/estimate.py @@ -9,9 +9,7 @@ from .project import ProjectBaseModel class Estimate(ProjectBaseModel): name = models.CharField(max_length=255) - description = models.TextField( - verbose_name="Estimate Description", blank=True - ) + description = models.TextField(verbose_name="Estimate Description", blank=True) type = models.CharField(max_length=255, default="categories") last_used = models.BooleanField(default=False) @@ -36,9 +34,7 @@ class Estimate(ProjectBaseModel): class EstimatePoint(ProjectBaseModel): estimate = models.ForeignKey( - "db.Estimate", - on_delete=models.CASCADE, - related_name="points", + "db.Estimate", on_delete=models.CASCADE, related_name="points" ) key = models.IntegerField( default=0, validators=[MinValueValidator(0), MaxValueValidator(12)] diff --git a/apiserver/plane/db/models/exporter.py b/apiserver/plane/db/models/exporter.py index d26f03450..48d40a1aa 100644 --- a/apiserver/plane/db/models/exporter.py +++ b/apiserver/plane/db/models/exporter.py @@ -30,20 +30,11 @@ class ExporterHistory(BaseModel): ), ) workspace = models.ForeignKey( - "db.WorkSpace", - on_delete=models.CASCADE, - related_name="workspace_exporters", - ) - project = ArrayField( - models.UUIDField(default=uuid.uuid4), blank=True, null=True + "db.WorkSpace", on_delete=models.CASCADE, related_name="workspace_exporters" ) + project = ArrayField(models.UUIDField(default=uuid.uuid4), blank=True, null=True) provider = models.CharField( - max_length=50, - choices=( - ("json", "json"), - ("csv", "csv"), - ("xlsx", "xlsx"), - ), + max_length=50, choices=(("json", "json"), ("csv", "csv"), ("xlsx", "xlsx")) ) status = models.CharField( max_length=50, @@ -58,9 +49,7 @@ class ExporterHistory(BaseModel): reason = models.TextField(blank=True) key = models.TextField(blank=True) url = models.URLField(max_length=800, blank=True, null=True) - token = models.CharField( - max_length=255, default=generate_token, unique=True - ) + token = models.CharField(max_length=255, default=generate_token, unique=True) initiated_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, diff --git a/apiserver/plane/db/models/favorite.py b/apiserver/plane/db/models/favorite.py index b921a8bd6..680bf7e37 100644 --- a/apiserver/plane/db/models/favorite.py +++ b/apiserver/plane/db/models/favorite.py @@ -13,9 +13,7 @@ class UserFavorite(WorkspaceBaseModel): """ user = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="favorites", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="favorites" ) entity_type = models.CharField(max_length=100) entity_identifier = models.UUIDField(null=True, blank=True) @@ -31,12 +29,7 @@ class UserFavorite(WorkspaceBaseModel): ) class Meta: - unique_together = [ - "entity_type", - "user", - "entity_identifier", - "deleted_at", - ] + unique_together = ["entity_type", "user", "entity_identifier", "deleted_at"] constraints = [ models.UniqueConstraint( fields=["entity_type", "entity_identifier", "user"], @@ -57,7 +50,7 @@ class UserFavorite(WorkspaceBaseModel): ).aggregate(largest=models.Max("sequence"))["largest"] else: largest_sequence = UserFavorite.objects.filter( - workspace=self.workspace, + workspace=self.workspace ).aggregate(largest=models.Max("sequence"))["largest"] if largest_sequence is not None: self.sequence = largest_sequence + 10000 diff --git a/apiserver/plane/db/models/importer.py b/apiserver/plane/db/models/importer.py index ebc7571d5..df93b95d1 100644 --- a/apiserver/plane/db/models/importer.py +++ b/apiserver/plane/db/models/importer.py @@ -8,11 +8,7 @@ from .project import ProjectBaseModel class Importer(ProjectBaseModel): service = models.CharField( - max_length=50, - choices=( - ("github", "GitHub"), - ("jira", "Jira"), - ), + max_length=50, choices=(("github", "GitHub"), ("jira", "Jira")) ) status = models.CharField( max_length=50, @@ -25,9 +21,7 @@ class Importer(ProjectBaseModel): default="queued", ) initiated_by = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="imports", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="imports" ) metadata = models.JSONField(default=dict) config = models.JSONField(default=dict) diff --git a/apiserver/plane/db/models/intake.py b/apiserver/plane/db/models/intake.py index 2a4bf75ca..3860b97f2 100644 --- a/apiserver/plane/db/models/intake.py +++ b/apiserver/plane/db/models/intake.py @@ -7,9 +7,7 @@ from plane.db.models.project import ProjectBaseModel class Intake(ProjectBaseModel): name = models.CharField(max_length=255) - description = models.TextField( - verbose_name="Intake Description", blank=True - ) + description = models.TextField(verbose_name="Intake Description", blank=True) is_default = models.BooleanField(default=False) view_props = models.JSONField(default=dict) logo_props = models.JSONField(default=dict) @@ -57,12 +55,7 @@ class IntakeIssue(ProjectBaseModel): on_delete=models.SET_NULL, null=True, ) - source = models.CharField( - max_length=255, - default="IN_APP", - null=True, - blank=True, - ) + source = models.CharField(max_length=255, default="IN_APP", null=True, blank=True) source_email = models.TextField(blank=True, null=True) external_source = models.CharField(max_length=255, null=True, blank=True) external_id = models.CharField(max_length=255, blank=True, null=True) diff --git a/apiserver/plane/db/models/integration/base.py b/apiserver/plane/db/models/integration/base.py index 3c296895f..61dad67b0 100644 --- a/apiserver/plane/db/models/integration/base.py +++ b/apiserver/plane/db/models/integration/base.py @@ -11,11 +11,7 @@ from plane.db.mixins import AuditModel class Integration(AuditModel): id = models.UUIDField( - default=uuid.uuid4, - unique=True, - editable=False, - db_index=True, - primary_key=True, + default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) title = models.CharField(max_length=400) provider = models.CharField(max_length=400, unique=True) @@ -44,18 +40,14 @@ class Integration(AuditModel): class WorkspaceIntegration(BaseModel): workspace = models.ForeignKey( - "db.Workspace", - related_name="workspace_integrations", - on_delete=models.CASCADE, + "db.Workspace", related_name="workspace_integrations", on_delete=models.CASCADE ) # Bot user actor = models.ForeignKey( "db.User", related_name="integrations", on_delete=models.CASCADE ) integration = models.ForeignKey( - "db.Integration", - related_name="integrated_workspaces", - on_delete=models.CASCADE, + "db.Integration", related_name="integrated_workspaces", on_delete=models.CASCADE ) api_token = models.ForeignKey( "db.APIToken", related_name="integrations", on_delete=models.CASCADE diff --git a/apiserver/plane/db/models/integration/github.py b/apiserver/plane/db/models/integration/github.py index 9e4294175..410972404 100644 --- a/apiserver/plane/db/models/integration/github.py +++ b/apiserver/plane/db/models/integration/github.py @@ -35,15 +35,10 @@ class GithubRepositorySync(ProjectBaseModel): "db.User", related_name="user_syncs", on_delete=models.CASCADE ) workspace_integration = models.ForeignKey( - "db.WorkspaceIntegration", - related_name="github_syncs", - on_delete=models.CASCADE, + "db.WorkspaceIntegration", related_name="github_syncs", on_delete=models.CASCADE ) label = models.ForeignKey( - "db.Label", - on_delete=models.SET_NULL, - null=True, - related_name="repo_syncs", + "db.Label", on_delete=models.SET_NULL, null=True, related_name="repo_syncs" ) def __str__(self): @@ -66,9 +61,7 @@ class GithubIssueSync(ProjectBaseModel): "db.Issue", related_name="github_syncs", on_delete=models.CASCADE ) repository_sync = models.ForeignKey( - "db.GithubRepositorySync", - related_name="issue_syncs", - on_delete=models.CASCADE, + "db.GithubRepositorySync", related_name="issue_syncs", on_delete=models.CASCADE ) def __str__(self): @@ -86,14 +79,10 @@ class GithubIssueSync(ProjectBaseModel): class GithubCommentSync(ProjectBaseModel): repo_comment_id = models.BigIntegerField() comment = models.ForeignKey( - "db.IssueComment", - related_name="comment_syncs", - on_delete=models.CASCADE, + "db.IssueComment", related_name="comment_syncs", on_delete=models.CASCADE ) issue_sync = models.ForeignKey( - "db.GithubIssueSync", - related_name="comment_syncs", - on_delete=models.CASCADE, + "db.GithubIssueSync", related_name="comment_syncs", on_delete=models.CASCADE ) def __str__(self): diff --git a/apiserver/plane/db/models/integration/slack.py b/apiserver/plane/db/models/integration/slack.py index 94d5d7d83..1e8ea469b 100644 --- a/apiserver/plane/db/models/integration/slack.py +++ b/apiserver/plane/db/models/integration/slack.py @@ -16,9 +16,7 @@ class SlackProjectSync(ProjectBaseModel): team_id = models.CharField(max_length=30) team_name = models.CharField(max_length=300) workspace_integration = models.ForeignKey( - "db.WorkspaceIntegration", - related_name="slack_syncs", - on_delete=models.CASCADE, + "db.WorkspaceIntegration", related_name="slack_syncs", on_delete=models.CASCADE ) def __str__(self): diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index adede2982..e37c0e7c1 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -122,9 +122,7 @@ class Issue(ProjectBaseModel): related_name="state_issue", ) point = models.IntegerField( - validators=[MinValueValidator(0), MaxValueValidator(12)], - null=True, - blank=True, + validators=[MinValueValidator(0), MaxValueValidator(12)], null=True, blank=True ) estimate_point = models.ForeignKey( "db.EstimatePoint", @@ -153,9 +151,7 @@ class Issue(ProjectBaseModel): through="IssueAssignee", through_fields=("issue", "assignee"), ) - sequence_id = models.IntegerField( - default=1, verbose_name="Issue Sequence ID" - ) + sequence_id = models.IntegerField(default=1, verbose_name="Issue Sequence ID") labels = models.ManyToManyField( "db.Label", blank=True, related_name="labels", through="IssueLabel" ) @@ -187,9 +183,7 @@ class Issue(ProjectBaseModel): from plane.db.models import State default_state = State.objects.filter( - ~models.Q(is_triage=True), - project=self.project, - default=True, + ~models.Q(is_triage=True), project=self.project, default=True ).first() if default_state is None: random_state = State.objects.filter( @@ -222,10 +216,7 @@ class Issue(ProjectBaseModel): # Strip the html tags using html parser self.description_stripped = ( None - if ( - self.description_html == "" - or self.description_html is None - ) + if (self.description_html == "" or self.description_html is None) else strip_tags(self.description_html) ) largest_sort_order = Issue.objects.filter( @@ -243,10 +234,7 @@ class Issue(ProjectBaseModel): # Strip the html tags using html parser self.description_stripped = ( None - if ( - self.description_html == "" - or self.description_html is None - ) + if (self.description_html == "" or self.description_html is None) else strip_tags(self.description_html) ) super(Issue, self).save(*args, **kwargs) @@ -319,9 +307,7 @@ class IssueMention(ProjectBaseModel): Issue, on_delete=models.CASCADE, related_name="issue_mention" ) mention = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="issue_mention", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="issue_mention" ) class Meta: @@ -400,12 +386,7 @@ def file_size(value): class IssueAttachment(ProjectBaseModel): attributes = models.JSONField(default=dict) - asset = models.FileField( - upload_to=get_upload_path, - validators=[ - file_size, - ], - ) + asset = models.FileField(upload_to=get_upload_path, validators=[file_size]) issue = models.ForeignKey( "db.Issue", on_delete=models.CASCADE, related_name="issue_attachment" ) @@ -424,28 +405,17 @@ class IssueAttachment(ProjectBaseModel): class IssueActivity(ProjectBaseModel): issue = models.ForeignKey( - Issue, - on_delete=models.SET_NULL, - null=True, - related_name="issue_activity", - ) - verb = models.CharField( - max_length=255, verbose_name="Action", default="created" + Issue, on_delete=models.SET_NULL, null=True, related_name="issue_activity" ) + verb = models.CharField(max_length=255, verbose_name="Action", default="created") field = models.CharField( max_length=255, verbose_name="Field Name", blank=True, null=True ) - old_value = models.TextField( - verbose_name="Old Value", blank=True, null=True - ) - new_value = models.TextField( - verbose_name="New Value", blank=True, null=True - ) + old_value = models.TextField(verbose_name="Old Value", blank=True, null=True) + new_value = models.TextField(verbose_name="New Value", blank=True, null=True) comment = models.TextField(verbose_name="Comment", blank=True) - attachments = ArrayField( - models.URLField(), size=10, blank=True, default=list - ) + attachments = ArrayField(models.URLField(), size=10, blank=True, default=list) issue_comment = models.ForeignKey( "db.IssueComment", on_delete=models.SET_NULL, @@ -477,9 +447,7 @@ class IssueComment(ProjectBaseModel): comment_stripped = models.TextField(verbose_name="Comment", blank=True) comment_json = models.JSONField(blank=True, default=dict) comment_html = models.TextField(blank=True, default="

") - attachments = ArrayField( - models.URLField(), size=10, blank=True, default=list - ) + attachments = ArrayField(models.URLField(), size=10, blank=True, default=list) issue = models.ForeignKey( Issue, on_delete=models.CASCADE, related_name="issue_comments" ) @@ -491,10 +459,7 @@ class IssueComment(ProjectBaseModel): null=True, ) access = models.CharField( - choices=( - ("INTERNAL", "INTERNAL"), - ("EXTERNAL", "EXTERNAL"), - ), + choices=(("INTERNAL", "INTERNAL"), ("EXTERNAL", "EXTERNAL")), default="INTERNAL", max_length=100, ) @@ -526,9 +491,7 @@ class IssueUserProperty(ProjectBaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField( - default=get_default_display_properties - ) + display_properties = models.JSONField(default=get_default_display_properties) class Meta: verbose_name = "Issue User Property" @@ -648,9 +611,7 @@ class CommentReaction(ProjectBaseModel): related_name="comment_reactions", ) comment = models.ForeignKey( - IssueComment, - on_delete=models.CASCADE, - related_name="comment_reactions", + IssueComment, on_delete=models.CASCADE, related_name="comment_reactions" ) reaction = models.CharField(max_length=20) @@ -673,28 +634,14 @@ class CommentReaction(ProjectBaseModel): class IssueVote(ProjectBaseModel): - issue = models.ForeignKey( - Issue, on_delete=models.CASCADE, related_name="votes" - ) + issue = models.ForeignKey(Issue, on_delete=models.CASCADE, related_name="votes") actor = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="votes", - ) - vote = models.IntegerField( - choices=( - (-1, "DOWNVOTE"), - (1, "UPVOTE"), - ), - default=1, + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="votes" ) + vote = models.IntegerField(choices=((-1, "DOWNVOTE"), (1, "UPVOTE")), default=1) class Meta: - unique_together = [ - "issue", - "actor", - "deleted_at", - ] + unique_together = ["issue", "actor", "deleted_at"] constraints = [ models.UniqueConstraint( fields=["issue", "actor"], diff --git a/apiserver/plane/db/models/issue_type.py b/apiserver/plane/db/models/issue_type.py index e6e409c84..953afcc8b 100644 --- a/apiserver/plane/db/models/issue_type.py +++ b/apiserver/plane/db/models/issue_type.py @@ -9,9 +9,7 @@ from .base import BaseModel class IssueType(BaseModel): workspace = models.ForeignKey( - "db.Workspace", - related_name="issue_types", - on_delete=models.CASCADE, + "db.Workspace", related_name="issue_types", on_delete=models.CASCADE ) name = models.CharField(max_length=255) description = models.TextField(blank=True) @@ -34,9 +32,7 @@ class IssueType(BaseModel): class ProjectIssueType(ProjectBaseModel): issue_type = models.ForeignKey( - "db.IssueType", - related_name="project_issue_types", - on_delete=models.CASCADE, + "db.IssueType", related_name="project_issue_types", on_delete=models.CASCADE ) level = models.PositiveIntegerField(default=0) is_default = models.BooleanField(default=False) diff --git a/apiserver/plane/db/models/module.py b/apiserver/plane/db/models/module.py index 6238fbd21..6fba4d03c 100644 --- a/apiserver/plane/db/models/module.py +++ b/apiserver/plane/db/models/module.py @@ -53,9 +53,7 @@ def get_default_display_properties(): class Module(ProjectBaseModel): name = models.CharField(max_length=255, verbose_name="Module Name") - description = models.TextField( - verbose_name="Module Description", blank=True - ) + description = models.TextField(verbose_name="Module Description", blank=True) description_text = models.JSONField( verbose_name="Module Description RT", blank=True, null=True ) @@ -77,10 +75,7 @@ class Module(ProjectBaseModel): max_length=20, ) lead = models.ForeignKey( - "db.User", - on_delete=models.SET_NULL, - related_name="module_leads", - null=True, + "db.User", on_delete=models.SET_NULL, related_name="module_leads", null=True ) members = models.ManyToManyField( settings.AUTH_USER_MODEL, @@ -112,9 +107,9 @@ class Module(ProjectBaseModel): def save(self, *args, **kwargs): if self._state.adding: - smallest_sort_order = Module.objects.filter( - project=self.project - ).aggregate(smallest=models.Min("sort_order"))["smallest"] + smallest_sort_order = Module.objects.filter(project=self.project).aggregate( + smallest=models.Min("sort_order") + )["smallest"] if smallest_sort_order is not None: self.sort_order = smallest_sort_order - 10000 @@ -193,9 +188,7 @@ class ModuleLink(ProjectBaseModel): class ModuleUserProperties(ProjectBaseModel): module = models.ForeignKey( - "db.Module", - on_delete=models.CASCADE, - related_name="module_user_properties", + "db.Module", on_delete=models.CASCADE, related_name="module_user_properties" ) user = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -204,9 +197,7 @@ class ModuleUserProperties(ProjectBaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField( - default=get_default_display_properties - ) + display_properties = models.JSONField(default=get_default_display_properties) class Meta: unique_together = ["module", "user", "deleted_at"] diff --git a/apiserver/plane/db/models/notification.py b/apiserver/plane/db/models/notification.py index 33241e05d..2847c07cf 100644 --- a/apiserver/plane/db/models/notification.py +++ b/apiserver/plane/db/models/notification.py @@ -6,16 +6,12 @@ from django.db import models from .base import BaseModel - class Notification(BaseModel): workspace = models.ForeignKey( "db.Workspace", related_name="notifications", on_delete=models.CASCADE ) project = models.ForeignKey( - "db.Project", - related_name="notifications", - on_delete=models.CASCADE, - null=True, + "db.Project", related_name="notifications", on_delete=models.CASCADE, null=True ) data = models.JSONField(null=True) entity_identifier = models.UUIDField(null=True) @@ -32,9 +28,7 @@ class Notification(BaseModel): null=True, ) receiver = models.ForeignKey( - "db.User", - related_name="received_notifications", - on_delete=models.CASCADE, + "db.User", related_name="received_notifications", on_delete=models.CASCADE ) read_at = models.DateTimeField(null=True) snoozed_till = models.DateTimeField(null=True) @@ -53,18 +47,10 @@ class Notification(BaseModel): def get_default_preference(): return { - "property_change": { - "email": True, - }, - "state": { - "email": True, - }, - "comment": { - "email": True, - }, - "mentions": { - "email": True, - }, + "property_change": {"email": True}, + "state": {"email": True}, + "comment": {"email": True}, + "mentions": {"email": True}, } diff --git a/apiserver/plane/db/models/page.py b/apiserver/plane/db/models/page.py index 433e74a12..91fd6ac44 100644 --- a/apiserver/plane/db/models/page.py +++ b/apiserver/plane/db/models/page.py @@ -26,9 +26,7 @@ class Page(BaseModel): description_html = models.TextField(blank=True, default="

") description_stripped = models.TextField(blank=True, null=True) owned_by = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="pages", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="pages" ) access = models.PositiveSmallIntegerField( choices=((0, "Public"), (1, "Private")), default=0 @@ -92,19 +90,13 @@ class PageLog(BaseModel): ("user_mention", "User Mention"), ) transaction = models.UUIDField(default=uuid.uuid4) - page = models.ForeignKey( - Page, related_name="page_log", on_delete=models.CASCADE - ) + page = models.ForeignKey(Page, related_name="page_log", on_delete=models.CASCADE) entity_identifier = models.UUIDField(null=True) entity_name = models.CharField( - max_length=30, - choices=TYPE_CHOICES, - verbose_name="Transaction Type", + max_length=30, choices=TYPE_CHOICES, verbose_name="Transaction Type" ) workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="workspace_page_log", + "db.Workspace", on_delete=models.CASCADE, related_name="workspace_page_log" ) class Meta: @@ -126,9 +118,7 @@ class PageLabel(BaseModel): "db.Page", on_delete=models.CASCADE, related_name="page_labels" ) workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="workspace_page_label", + "db.Workspace", on_delete=models.CASCADE, related_name="workspace_page_label" ) class Meta: @@ -198,20 +188,14 @@ class TeamPage(BaseModel): class PageVersion(BaseModel): workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="page_versions", + "db.Workspace", on_delete=models.CASCADE, related_name="page_versions" ) page = models.ForeignKey( - "db.Page", - on_delete=models.CASCADE, - related_name="page_versions", + "db.Page", on_delete=models.CASCADE, related_name="page_versions" ) last_saved_at = models.DateTimeField(default=timezone.now) owned_by = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="page_versions", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="page_versions" ) description_binary = models.BinaryField(null=True) description_html = models.TextField(blank=True, default="

") diff --git a/apiserver/plane/db/models/project.py b/apiserver/plane/db/models/project.py index c55ffe646..c97c550ee 100644 --- a/apiserver/plane/db/models/project.py +++ b/apiserver/plane/db/models/project.py @@ -14,11 +14,7 @@ from plane.db.mixins import AuditModel # Module imports from .base import BaseModel -ROLE_CHOICES = ( - (20, "Admin"), - (15, "Member"), - (5, "Guest"), -) +ROLE_CHOICES = ((20, "Admin"), (15, "Member"), (5, "Guest")) def get_default_props(): @@ -53,27 +49,19 @@ def get_default_preferences(): class Project(BaseModel): NETWORK_CHOICES = ((0, "Secret"), (2, "Public")) name = models.CharField(max_length=255, verbose_name="Project Name") - description = models.TextField( - verbose_name="Project Description", blank=True - ) + description = models.TextField(verbose_name="Project Description", blank=True) description_text = models.JSONField( verbose_name="Project Description RT", blank=True, null=True ) description_html = models.JSONField( verbose_name="Project Description HTML", blank=True, null=True ) - network = models.PositiveSmallIntegerField( - default=2, choices=NETWORK_CHOICES - ) + network = models.PositiveSmallIntegerField(default=2, choices=NETWORK_CHOICES) workspace = models.ForeignKey( - "db.WorkSpace", - on_delete=models.CASCADE, - related_name="workspace_project", + "db.WorkSpace", on_delete=models.CASCADE, related_name="workspace_project" ) identifier = models.CharField( - max_length=12, - verbose_name="Project Identifier", - db_index=True, + max_length=12, verbose_name="Project Identifier", db_index=True ) default_assignee = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -108,10 +96,7 @@ class Project(BaseModel): related_name="project_cover_image", ) estimate = models.ForeignKey( - "db.Estimate", - on_delete=models.SET_NULL, - related_name="projects", - null=True, + "db.Estimate", on_delete=models.SET_NULL, related_name="projects", null=True ) archive_in = models.IntegerField( default=0, validators=[MinValueValidator(0), MaxValueValidator(12)] @@ -121,17 +106,12 @@ class Project(BaseModel): ) logo_props = models.JSONField(default=dict) default_state = models.ForeignKey( - "db.State", - on_delete=models.SET_NULL, - null=True, - related_name="default_state", + "db.State", on_delete=models.SET_NULL, null=True, related_name="default_state" ) archived_at = models.DateTimeField(null=True) # timezone TIMEZONE_CHOICES = tuple(zip(pytz.all_timezones, pytz.all_timezones)) - timezone = models.CharField( - max_length=255, default="UTC", choices=TIMEZONE_CHOICES - ) + timezone = models.CharField(max_length=255, default="UTC", choices=TIMEZONE_CHOICES) @property def cover_image_url(self): @@ -181,9 +161,7 @@ class ProjectBaseModel(BaseModel): Project, on_delete=models.CASCADE, related_name="project_%(class)s" ) workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="workspace_%(class)s", + "db.Workspace", on_delete=models.CASCADE, related_name="workspace_%(class)s" ) class Meta: @@ -262,10 +240,7 @@ class ProjectMember(ProjectBaseModel): # TODO: Remove workspace relation later class ProjectIdentifier(AuditModel): workspace = models.ForeignKey( - "db.Workspace", - models.CASCADE, - related_name="project_identifiers", - null=True, + "db.Workspace", models.CASCADE, related_name="project_identifiers", null=True ) project = models.OneToOneField( Project, on_delete=models.CASCADE, related_name="project_identifier" @@ -310,10 +285,7 @@ class ProjectDeployBoard(ProjectBaseModel): comments = models.BooleanField(default=False) reactions = models.BooleanField(default=False) intake = models.ForeignKey( - "db.Intake", - related_name="board_intake", - on_delete=models.SET_NULL, - null=True, + "db.Intake", related_name="board_intake", on_delete=models.SET_NULL, null=True ) votes = models.BooleanField(default=False) views = models.JSONField(default=get_default_views) diff --git a/apiserver/plane/db/models/recent_visit.py b/apiserver/plane/db/models/recent_visit.py index 4696ead46..14737eba5 100644 --- a/apiserver/plane/db/models/recent_visit.py +++ b/apiserver/plane/db/models/recent_visit.py @@ -17,10 +17,7 @@ class EntityNameEnum(models.TextChoices): class UserRecentVisit(WorkspaceBaseModel): entity_identifier = models.UUIDField(null=True) - entity_name = models.CharField( - max_length=30, - choices=EntityNameEnum.choices, - ) + entity_name = models.CharField(max_length=30, choices=EntityNameEnum.choices) user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, diff --git a/apiserver/plane/db/models/session.py b/apiserver/plane/db/models/session.py index 95e8e0b7d..3b35ebc70 100644 --- a/apiserver/plane/db/models/session.py +++ b/apiserver/plane/db/models/session.py @@ -11,19 +11,9 @@ VALID_KEY_CHARS = string.ascii_lowercase + string.digits class Session(AbstractBaseSession): - device_info = models.JSONField( - null=True, - blank=True, - default=None, - ) - session_key = models.CharField( - max_length=128, - primary_key=True, - ) - user_id = models.CharField( - null=True, - max_length=50, - ) + device_info = models.JSONField(null=True, blank=True, default=None) + session_key = models.CharField(max_length=128, primary_key=True) + user_id = models.CharField(null=True, max_length=50) @classmethod def get_session_store_class(cls): @@ -34,7 +24,6 @@ class Session(AbstractBaseSession): class SessionStore(DBSessionStore): - @classmethod def get_model_class(cls): return Session @@ -59,7 +48,5 @@ class SessionStore(DBSessionStore): # Save the device info device_info = data.get("device_info") - obj.device_info = ( - device_info if isinstance(device_info, dict) else None - ) + obj.device_info = device_info if isinstance(device_info, dict) else None return obj diff --git a/apiserver/plane/db/models/social_connection.py b/apiserver/plane/db/models/social_connection.py index 2a21c55fd..9a85a320d 100644 --- a/apiserver/plane/db/models/social_connection.py +++ b/apiserver/plane/db/models/social_connection.py @@ -10,7 +10,12 @@ from .base import BaseModel class SocialLoginConnection(BaseModel): medium = models.CharField( max_length=20, - choices=(("Google", "google"), ("Github", "github"), ("GitLab", "gitlab"), ("Jira", "jira")), + choices=( + ("Google", "google"), + ("Github", "github"), + ("GitLab", "gitlab"), + ("Jira", "jira"), + ), default=None, ) last_login_at = models.DateTimeField(default=timezone.now, null=True) diff --git a/apiserver/plane/db/models/state.py b/apiserver/plane/db/models/state.py index c661a4d61..3478d70d2 100644 --- a/apiserver/plane/db/models/state.py +++ b/apiserver/plane/db/models/state.py @@ -9,9 +9,7 @@ from .project import ProjectBaseModel class State(ProjectBaseModel): name = models.CharField(max_length=255, verbose_name="State Name") - description = models.TextField( - verbose_name="State Description", blank=True - ) + description = models.TextField(verbose_name="State Description", blank=True) color = models.CharField(max_length=255, verbose_name="State Color") slug = models.SlugField(max_length=100, blank=True) sequence = models.FloatField(default=65535) @@ -22,7 +20,7 @@ class State(ProjectBaseModel): ("started", "Started"), ("completed", "Completed"), ("cancelled", "Cancelled"), - ("triage", "Triage") + ("triage", "Triage"), ), default="backlog", max_length=20, diff --git a/apiserver/plane/db/models/user.py b/apiserver/plane/db/models/user.py index e97d04e7a..34a86a251 100644 --- a/apiserver/plane/db/models/user.py +++ b/apiserver/plane/db/models/user.py @@ -4,11 +4,7 @@ import string import uuid import pytz -from django.contrib.auth.models import ( - AbstractBaseUser, - PermissionsMixin, - UserManager, -) +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager # Django imports from django.db import models @@ -32,18 +28,12 @@ def get_default_onboarding(): class User(AbstractBaseUser, PermissionsMixin): id = models.UUIDField( - default=uuid.uuid4, - unique=True, - editable=False, - db_index=True, - primary_key=True, + default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) username = models.CharField(max_length=128, unique=True) # user fields mobile_number = models.CharField(max_length=255, blank=True, null=True) - email = models.CharField( - max_length=255, null=True, blank=True, unique=True - ) + email = models.CharField(max_length=255, null=True, blank=True, unique=True) # identity display_name = models.CharField(max_length=255, default="") @@ -69,15 +59,9 @@ class User(AbstractBaseUser, PermissionsMixin): ) # tracking metrics - date_joined = models.DateTimeField( - auto_now_add=True, verbose_name="Created At" - ) - created_at = models.DateTimeField( - auto_now_add=True, verbose_name="Created At" - ) - updated_at = models.DateTimeField( - auto_now=True, verbose_name="Last Modified At" - ) + date_joined = models.DateTimeField(auto_now_add=True, verbose_name="Created At") + created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created At") + updated_at = models.DateTimeField(auto_now=True, verbose_name="Last Modified At") last_location = models.CharField(max_length=255, blank=True) created_location = models.CharField(max_length=255, blank=True) @@ -98,20 +82,14 @@ class User(AbstractBaseUser, PermissionsMixin): last_logout_time = models.DateTimeField(null=True) last_login_ip = models.CharField(max_length=255, blank=True) last_logout_ip = models.CharField(max_length=255, blank=True) - last_login_medium = models.CharField( - max_length=20, - default="email", - ) + last_login_medium = models.CharField(max_length=20, default="email") last_login_uagent = models.TextField(blank=True) token_updated_at = models.DateTimeField(null=True) # my_issues_prop = models.JSONField(null=True) is_bot = models.BooleanField(default=False) bot_type = models.CharField( - max_length=30, - verbose_name="Bot Type", - blank=True, - null=True, + max_length=30, verbose_name="Bot Type", blank=True, null=True ) # timezone @@ -168,9 +146,7 @@ class User(AbstractBaseUser, PermissionsMixin): self.display_name = ( self.email.split("@")[0] if len(self.email.split("@")) - else "".join( - random.choice(string.ascii_letters) for _ in range(6) - ) + else "".join(random.choice(string.ascii_letters) for _ in range(6)) ) if self.is_superuser: @@ -181,11 +157,7 @@ class User(AbstractBaseUser, PermissionsMixin): class Profile(TimeAuditModel): id = models.UUIDField( - default=uuid.uuid4, - unique=True, - editable=False, - db_index=True, - primary_key=True, + default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) # User user = models.OneToOneField( @@ -222,19 +194,13 @@ class Account(TimeAuditModel): ) id = models.UUIDField( - default=uuid.uuid4, - unique=True, - editable=False, - db_index=True, - primary_key=True, + default=uuid.uuid4, unique=True, editable=False, db_index=True, primary_key=True ) user = models.ForeignKey( "db.User", on_delete=models.CASCADE, related_name="accounts" ) provider_account_id = models.CharField(max_length=255) - provider = models.CharField( - choices=PROVIDER_CHOICES, - ) + provider = models.CharField(choices=PROVIDER_CHOICES) access_token = models.TextField() access_token_expired_at = models.DateTimeField(null=True) refresh_token = models.TextField(null=True, blank=True) diff --git a/apiserver/plane/db/models/view.py b/apiserver/plane/db/models/view.py index 586ab257d..c9182acce 100644 --- a/apiserver/plane/db/models/view.py +++ b/apiserver/plane/db/models/view.py @@ -57,18 +57,14 @@ class IssueView(WorkspaceBaseModel): query = models.JSONField(verbose_name="View Query") filters = models.JSONField(default=dict) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField( - default=get_default_display_properties - ) + display_properties = models.JSONField(default=get_default_display_properties) access = models.PositiveSmallIntegerField( default=1, choices=((0, "Private"), (1, "Public")) ) sort_order = models.FloatField(default=65535) logo_props = models.JSONField(default=dict) owned_by = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="views", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="views" ) is_locked = models.BooleanField(default=False) @@ -80,9 +76,7 @@ class IssueView(WorkspaceBaseModel): def save(self, *args, **kwargs): query_params = self.filters - self.query = ( - issue_filters(query_params, "POST") if query_params else {} - ) + self.query = issue_filters(query_params, "POST") if query_params else {} if self._state.adding: if self.project: diff --git a/apiserver/plane/db/models/webhook.py b/apiserver/plane/db/models/webhook.py index fbe74d03a..be2a5e9a3 100644 --- a/apiserver/plane/db/models/webhook.py +++ b/apiserver/plane/db/models/webhook.py @@ -17,9 +17,7 @@ def generate_token(): def validate_schema(value): parsed_url = urlparse(value) if parsed_url.scheme not in ["http", "https"]: - raise ValidationError( - "Invalid schema. Only HTTP and HTTPS are allowed." - ) + raise ValidationError("Invalid schema. Only HTTP and HTTPS are allowed.") def validate_domain(value): @@ -31,16 +29,9 @@ def validate_domain(value): class Webhook(BaseModel): workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="workspace_webhooks", - ) - url = models.URLField( - validators=[ - validate_schema, - validate_domain, - ] + "db.Workspace", on_delete=models.CASCADE, related_name="workspace_webhooks" ) + url = models.URLField(validators=[validate_schema, validate_domain]) is_active = models.BooleanField(default=True) secret_key = models.CharField(max_length=255, default=generate_token) project = models.BooleanField(default=False) @@ -65,9 +56,7 @@ class WebhookLog(BaseModel): "db.Workspace", on_delete=models.CASCADE, related_name="webhook_logs" ) # Associated webhook - webhook = models.ForeignKey( - Webhook, on_delete=models.CASCADE, related_name="logs" - ) + webhook = models.ForeignKey(Webhook, on_delete=models.CASCADE, related_name="logs") # Basic request details event_type = models.CharField(max_length=255, blank=True, null=True) diff --git a/apiserver/plane/db/models/workspace.py b/apiserver/plane/db/models/workspace.py index 8dd4d44f0..93f8a24e0 100644 --- a/apiserver/plane/db/models/workspace.py +++ b/apiserver/plane/db/models/workspace.py @@ -10,11 +10,7 @@ from django.db import models from .base import BaseModel from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS -ROLE_CHOICES = ( - (20, "Admin"), - (15, "Member"), - (5, "Guest"), -) +ROLE_CHOICES = ((20, "Admin"), (15, "Member"), (5, "Guest")) def get_default_props(): @@ -81,7 +77,7 @@ def get_default_display_filters(): "show_empty_groups": True, "layout": "list", "calendar_date_range": "", - }, + } } @@ -101,17 +97,12 @@ def get_default_display_properties(): "state": True, "sub_issue_count": True, "updated_on": True, - }, + } } def get_issue_props(): - return { - "subscribed": True, - "assigned": True, - "created": True, - "all_issues": True, - } + return {"subscribed": True, "assigned": True, "created": True, "all_issues": True} def slug_validator(value): @@ -137,17 +128,10 @@ class Workspace(BaseModel): related_name="owner_workspace", ) slug = models.SlugField( - max_length=48, - db_index=True, - unique=True, - validators=[ - slug_validator, - ], + max_length=48, db_index=True, unique=True, validators=[slug_validator] ) organization_size = models.CharField(max_length=20, blank=True, null=True) - timezone = models.CharField( - max_length=255, default="UTC", choices=TIMEZONE_CHOICES - ) + timezone = models.CharField(max_length=255, default="UTC", choices=TIMEZONE_CHOICES) def __str__(self): """Return name of the Workspace""" @@ -176,10 +160,7 @@ class WorkspaceBaseModel(BaseModel): "db.Workspace", models.CASCADE, related_name="workspace_%(class)s" ) project = models.ForeignKey( - "db.Project", - models.CASCADE, - related_name="project_%(class)s", - null=True, + "db.Project", models.CASCADE, related_name="project_%(class)s", null=True ) class Meta: @@ -193,9 +174,7 @@ class WorkspaceBaseModel(BaseModel): class WorkspaceMember(BaseModel): workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="workspace_member", + "db.Workspace", on_delete=models.CASCADE, related_name="workspace_member" ) member = models.ForeignKey( settings.AUTH_USER_MODEL, @@ -230,9 +209,7 @@ class WorkspaceMember(BaseModel): class WorkspaceMemberInvite(BaseModel): workspace = models.ForeignKey( - "db.Workspace", - on_delete=models.CASCADE, - related_name="workspace_member_invite", + "db.Workspace", on_delete=models.CASCADE, related_name="workspace_member_invite" ) email = models.CharField(max_length=255) accepted = models.BooleanField(default=False) @@ -297,13 +274,9 @@ class TeamMember(BaseModel): workspace = models.ForeignKey( Workspace, on_delete=models.CASCADE, related_name="team_member" ) - team = models.ForeignKey( - Team, on_delete=models.CASCADE, related_name="team_member" - ) + team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="team_member") member = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="team_member", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="team_member" ) def __str__(self): @@ -330,9 +303,7 @@ class WorkspaceTheme(BaseModel): ) name = models.CharField(max_length=300) actor = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="themes", + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="themes" ) colors = models.JSONField(default=dict) @@ -367,9 +338,7 @@ class WorkspaceUserProperties(BaseModel): ) filters = models.JSONField(default=get_default_filters) display_filters = models.JSONField(default=get_default_display_filters) - display_properties = models.JSONField( - default=get_default_display_properties - ) + display_properties = models.JSONField(default=get_default_display_properties) class Meta: unique_together = ["workspace", "user", "deleted_at"] diff --git a/apiserver/plane/license/api/permissions/instance.py b/apiserver/plane/license/api/permissions/instance.py index 9ee85404b..848de4d7b 100644 --- a/apiserver/plane/license/api/permissions/instance.py +++ b/apiserver/plane/license/api/permissions/instance.py @@ -12,7 +12,5 @@ class InstanceAdminPermission(BasePermission): instance = Instance.objects.first() return InstanceAdmin.objects.filter( - role__gte=15, - instance=instance, - user=request.user, + role__gte=15, instance=instance, user=request.user ).exists() diff --git a/apiserver/plane/license/api/serializers/__init__.py b/apiserver/plane/license/api/serializers/__init__.py index 7b9cb676f..681dbeb5c 100644 --- a/apiserver/plane/license/api/serializers/__init__.py +++ b/apiserver/plane/license/api/serializers/__init__.py @@ -1,6 +1,4 @@ -from .instance import ( - InstanceSerializer, -) +from .instance import InstanceSerializer from .configuration import InstanceConfigurationSerializer from .admin import InstanceAdminSerializer, InstanceAdminMeSerializer diff --git a/apiserver/plane/license/api/serializers/admin.py b/apiserver/plane/license/api/serializers/admin.py index 119460b4b..4df6901ca 100644 --- a/apiserver/plane/license/api/serializers/admin.py +++ b/apiserver/plane/license/api/serializers/admin.py @@ -35,8 +35,4 @@ class InstanceAdminSerializer(BaseSerializer): class Meta: model = InstanceAdmin fields = "__all__" - read_only_fields = [ - "id", - "instance", - "user", - ] + read_only_fields = ["id", "instance", "user"] diff --git a/apiserver/plane/license/api/serializers/instance.py b/apiserver/plane/license/api/serializers/instance.py index b5b9c4762..49c5194c8 100644 --- a/apiserver/plane/license/api/serializers/instance.py +++ b/apiserver/plane/license/api/serializers/instance.py @@ -12,9 +12,4 @@ class InstanceSerializer(BaseSerializer): class Meta: model = Instance fields = "__all__" - read_only_fields = [ - "id", - "email", - "last_checked_at", - "is_setup_done", - ] + read_only_fields = ["id", "email", "last_checked_at", "is_setup_done"] diff --git a/apiserver/plane/license/api/views/__init__.py b/apiserver/plane/license/api/views/__init__.py index ad1aaa58d..9427ed15e 100644 --- a/apiserver/plane/license/api/views/__init__.py +++ b/apiserver/plane/license/api/views/__init__.py @@ -1,13 +1,7 @@ -from .instance import ( - InstanceEndpoint, - SignUpScreenVisitedEndpoint, -) +from .instance import InstanceEndpoint, SignUpScreenVisitedEndpoint -from .configuration import ( - EmailCredentialCheckEndpoint, - InstanceConfigurationEndpoint, -) +from .configuration import EmailCredentialCheckEndpoint, InstanceConfigurationEndpoint from .admin import ( diff --git a/apiserver/plane/license/api/views/admin.py b/apiserver/plane/license/api/views/admin.py index 5d93aba49..10c6df5c3 100644 --- a/apiserver/plane/license/api/views/admin.py +++ b/apiserver/plane/license/api/views/admin.py @@ -36,9 +36,7 @@ from plane.authentication.adapter.error import ( class InstanceAdminEndpoint(BaseAPIView): - permission_classes = [ - InstanceAdminPermission, - ] + permission_classes = [InstanceAdminPermission] @invalidate_cache(path="/api/instances/", user=False) # Create an instance admin @@ -48,8 +46,7 @@ class InstanceAdminEndpoint(BaseAPIView): if not email: return Response( - {"error": "Email is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Email is required"}, status=status.HTTP_400_BAD_REQUEST ) instance = Instance.objects.first() @@ -63,9 +60,7 @@ class InstanceAdminEndpoint(BaseAPIView): user = User.objects.get(email=email) instance_admin = InstanceAdmin.objects.create( - instance=instance, - user=user, - role=role, + instance=instance, user=user, role=role ) serializer = InstanceAdminSerializer(instance_admin) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -90,9 +85,7 @@ class InstanceAdminEndpoint(BaseAPIView): class InstanceAdminSignUpEndpoint(View): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] @invalidate_cache(path="/api/instances/", user=False) def post(self, request): @@ -100,9 +93,7 @@ class InstanceAdminSignUpEndpoint(View): instance = Instance.objects.first() if instance is None: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) url = urljoin( @@ -178,9 +169,7 @@ class InstanceAdminSignUpEndpoint(View): # Existing user if User.objects.filter(email=email).exists(): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "ADMIN_USER_ALREADY_EXIST" - ], + error_code=AUTHENTICATION_ERROR_CODES["ADMIN_USER_ALREADY_EXIST"], error_message="ADMIN_USER_ALREADY_EXIST", payload={ "email": email, @@ -196,13 +185,10 @@ class InstanceAdminSignUpEndpoint(View): ) return HttpResponseRedirect(url) else: - results = zxcvbn(password) if results["score"] < 3: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INVALID_ADMIN_PASSWORD" - ], + error_code=AUTHENTICATION_ERROR_CODES["INVALID_ADMIN_PASSWORD"], error_message="INVALID_ADMIN_PASSWORD", payload={ "email": email, @@ -237,10 +223,7 @@ class InstanceAdminSignUpEndpoint(View): user.save() # Register the user as an instance admin - _ = InstanceAdmin.objects.create( - user=user, - instance=instance, - ) + _ = InstanceAdmin.objects.create(user=user, instance=instance) # Make the setup flag True instance.is_setup_done = True instance.instance_name = company_name @@ -254,9 +237,7 @@ class InstanceAdminSignUpEndpoint(View): class InstanceAdminSignInEndpoint(View): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] @invalidate_cache(path="/api/instances/", user=False) def post(self, request): @@ -264,9 +245,7 @@ class InstanceAdminSignInEndpoint(View): instance = Instance.objects.first() if instance is None: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "INSTANCE_NOT_CONFIGURED" - ], + error_code=AUTHENTICATION_ERROR_CODES["INSTANCE_NOT_CONFIGURED"], error_message="INSTANCE_NOT_CONFIGURED", ) url = urljoin( @@ -282,13 +261,9 @@ class InstanceAdminSignInEndpoint(View): # return error if the email and password is not present if not email or not password: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "REQUIRED_ADMIN_EMAIL_PASSWORD" - ], + error_code=AUTHENTICATION_ERROR_CODES["REQUIRED_ADMIN_EMAIL_PASSWORD"], error_message="REQUIRED_ADMIN_EMAIL_PASSWORD", - payload={ - "email": email, - }, + payload={"email": email}, ) url = urljoin( base_host(request=request, is_admin=True), @@ -304,9 +279,7 @@ class InstanceAdminSignInEndpoint(View): exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES["INVALID_ADMIN_EMAIL"], error_message="INVALID_ADMIN_EMAIL", - payload={ - "email": email, - }, + payload={"email": email}, ) url = urljoin( base_host(request=request, is_admin=True), @@ -320,9 +293,7 @@ class InstanceAdminSignInEndpoint(View): # is_active if not user.is_active: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "ADMIN_USER_DEACTIVATED" - ], + error_code=AUTHENTICATION_ERROR_CODES["ADMIN_USER_DEACTIVATED"], error_message="ADMIN_USER_DEACTIVATED", ) url = urljoin( @@ -334,13 +305,9 @@ class InstanceAdminSignInEndpoint(View): # Error out if the user is not present if not user: exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "ADMIN_USER_DOES_NOT_EXIST" - ], + error_code=AUTHENTICATION_ERROR_CODES["ADMIN_USER_DOES_NOT_EXIST"], error_message="ADMIN_USER_DOES_NOT_EXIST", - payload={ - "email": email, - }, + payload={"email": email}, ) url = urljoin( base_host(request=request, is_admin=True), @@ -351,13 +318,9 @@ class InstanceAdminSignInEndpoint(View): # Check password of the user if not user.check_password(password): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "ADMIN_AUTHENTICATION_FAILED" - ], + error_code=AUTHENTICATION_ERROR_CODES["ADMIN_AUTHENTICATION_FAILED"], error_message="ADMIN_AUTHENTICATION_FAILED", - payload={ - "email": email, - }, + payload={"email": email}, ) url = urljoin( base_host(request=request, is_admin=True), @@ -368,13 +331,9 @@ class InstanceAdminSignInEndpoint(View): # Check if the user is an instance admin if not InstanceAdmin.objects.filter(instance=instance, user=user): exc = AuthenticationException( - error_code=AUTHENTICATION_ERROR_CODES[ - "ADMIN_AUTHENTICATION_FAILED" - ], + error_code=AUTHENTICATION_ERROR_CODES["ADMIN_AUTHENTICATION_FAILED"], error_message="ADMIN_AUTHENTICATION_FAILED", - payload={ - "email": email, - }, + payload={"email": email}, ) url = urljoin( base_host(request=request, is_admin=True), @@ -397,24 +356,15 @@ class InstanceAdminSignInEndpoint(View): class InstanceAdminUserMeEndpoint(BaseAPIView): - - permission_classes = [ - InstanceAdminPermission, - ] + permission_classes = [InstanceAdminPermission] def get(self, request): serializer = InstanceAdminMeSerializer(request.user) - return Response( - serializer.data, - status=status.HTTP_200_OK, - ) + return Response(serializer.data, status=status.HTTP_200_OK) class InstanceAdminUserSessionEndpoint(BaseAPIView): - - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request): if ( @@ -424,21 +374,13 @@ class InstanceAdminUserSessionEndpoint(BaseAPIView): serializer = InstanceAdminMeSerializer(request.user) data = {"is_authenticated": True} data["user"] = serializer.data - return Response( - data, - status=status.HTTP_200_OK, - ) + return Response(data, status=status.HTTP_200_OK) else: - return Response( - {"is_authenticated": False}, status=status.HTTP_200_OK - ) + return Response({"is_authenticated": False}, status=status.HTTP_200_OK) class InstanceAdminSignOutEndpoint(View): - - permission_classes = [ - InstanceAdminPermission, - ] + permission_classes = [InstanceAdminPermission] def post(self, request): # Get user @@ -452,6 +394,4 @@ class InstanceAdminSignOutEndpoint(View): url = urljoin(base_host(request=request, is_admin=True)) return HttpResponseRedirect(url) except Exception: - return HttpResponseRedirect( - base_host(request=request, is_admin=True) - ) + return HttpResponseRedirect(base_host(request=request, is_admin=True)) diff --git a/apiserver/plane/license/api/views/base.py b/apiserver/plane/license/api/views/base.py index 7e367f941..05b42b801 100644 --- a/apiserver/plane/license/api/views/base.py +++ b/apiserver/plane/license/api/views/base.py @@ -36,18 +36,11 @@ class TimezoneMixin: class BaseAPIView(TimezoneMixin, APIView, BasePaginator): - permission_classes = [ - InstanceAdminPermission, - ] + permission_classes = [InstanceAdminPermission] - filter_backends = ( - DjangoFilterBackend, - SearchFilter, - ) + filter_backends = (DjangoFilterBackend, SearchFilter) - authentication_classes = [ - BaseSessionAuthentication, - ] + authentication_classes = [BaseSessionAuthentication] filterset_fields = [] @@ -116,17 +109,13 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator): @property def fields(self): fields = [ - field - for field in self.request.GET.get("fields", "").split(",") - if field + field for field in self.request.GET.get("fields", "").split(",") if field ] return fields if fields else None @property def expand(self): expand = [ - expand - for expand in self.request.GET.get("expand", "").split(",") - if expand + expand for expand in self.request.GET.get("expand", "").split(",") if expand ] return expand if expand else None diff --git a/apiserver/plane/license/api/views/changelog.py b/apiserver/plane/license/api/views/changelog.py index a81504e47..52583a35f 100644 --- a/apiserver/plane/license/api/views/changelog.py +++ b/apiserver/plane/license/api/views/changelog.py @@ -14,9 +14,7 @@ from .base import BaseAPIView class ChangeLogEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def fetch_change_logs(self): response = requests.get(settings.INSTANCE_CHANGELOG_URL) diff --git a/apiserver/plane/license/api/views/configuration.py b/apiserver/plane/license/api/views/configuration.py index 06f53b753..594a899eb 100644 --- a/apiserver/plane/license/api/views/configuration.py +++ b/apiserver/plane/license/api/views/configuration.py @@ -8,11 +8,7 @@ from smtplib import ( ) # Django imports -from django.core.mail import ( - BadHeaderError, - EmailMultiAlternatives, - get_connection, -) +from django.core.mail import BadHeaderError, EmailMultiAlternatives, get_connection # Third party imports from rest_framework import status @@ -25,22 +21,16 @@ from plane.license.models import InstanceConfiguration from plane.license.api.serializers import InstanceConfigurationSerializer from plane.license.utils.encryption import encrypt_data from plane.utils.cache import cache_response, invalidate_cache -from plane.license.utils.instance_value import ( - get_email_configuration, -) +from plane.license.utils.instance_value import get_email_configuration class InstanceConfigurationEndpoint(BaseAPIView): - permission_classes = [ - InstanceAdminPermission, - ] + permission_classes = [InstanceAdminPermission] @cache_response(60 * 60 * 2, user=False) def get(self, request): instance_configurations = InstanceConfiguration.objects.all() - serializer = InstanceConfigurationSerializer( - instance_configurations, many=True - ) + serializer = InstanceConfigurationSerializer(instance_configurations, many=True) return Response(serializer.data, status=status.HTTP_200_OK) @invalidate_cache(path="/api/instances/configurations/", user=False) @@ -68,7 +58,6 @@ class InstanceConfigurationEndpoint(BaseAPIView): class EmailCredentialCheckEndpoint(BaseAPIView): - def post(self, request): receiver_email = request.data.get("receiver_email", False) if not receiver_email: @@ -98,9 +87,7 @@ class EmailCredentialCheckEndpoint(BaseAPIView): ) # Prepare email details subject = "Email Notification from Plane" - message = ( - "This is a sample email notification sent from Plane application." - ) + message = "This is a sample email notification sent from Plane application." # Send the email try: msg = EmailMultiAlternatives( @@ -112,13 +99,11 @@ class EmailCredentialCheckEndpoint(BaseAPIView): ) msg.send(fail_silently=False) return Response( - {"message": "Email successfully sent."}, - status=status.HTTP_200_OK, + {"message": "Email successfully sent."}, status=status.HTTP_200_OK ) except BadHeaderError: return Response( - {"error": "Invalid email header."}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Invalid email header."}, status=status.HTTP_400_BAD_REQUEST ) except SMTPAuthenticationError: return Response( @@ -147,9 +132,7 @@ class EmailCredentialCheckEndpoint(BaseAPIView): ) except TimeoutError: return Response( - { - "error": "Timeout error while trying to connect to the SMTP server." - }, + {"error": "Timeout error while trying to connect to the SMTP server."}, status=status.HTTP_400_BAD_REQUEST, ) except ConnectionError: @@ -161,8 +144,6 @@ class EmailCredentialCheckEndpoint(BaseAPIView): ) except Exception: return Response( - { - "error": "Could not send email. Please check your configuration" - }, + {"error": "Could not send email. Please check your configuration"}, status=status.HTTP_400_BAD_REQUEST, ) diff --git a/apiserver/plane/license/api/views/instance.py b/apiserver/plane/license/api/views/instance.py index c61b55bb6..883fb7c97 100644 --- a/apiserver/plane/license/api/views/instance.py +++ b/apiserver/plane/license/api/views/instance.py @@ -12,16 +12,10 @@ from rest_framework.response import Response # Module imports from plane.app.views import BaseAPIView from plane.db.models import Workspace -from plane.license.api.permissions import ( - InstanceAdminPermission, -) -from plane.license.api.serializers import ( - InstanceSerializer, -) +from plane.license.api.permissions import InstanceAdminPermission +from plane.license.api.serializers import InstanceSerializer from plane.license.models import Instance -from plane.license.utils.instance_value import ( - get_configuration_value, -) +from plane.license.utils.instance_value import get_configuration_value from plane.utils.cache import cache_response, invalidate_cache from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_control @@ -30,12 +24,8 @@ from django.views.decorators.cache import cache_control class InstanceEndpoint(BaseAPIView): def get_permissions(self): if self.request.method == "PATCH": - return [ - InstanceAdminPermission(), - ] - return [ - AllowAny(), - ] + return [InstanceAdminPermission()] + return [AllowAny()] @cache_response(60 * 60 * 2, user=False) @method_decorator(cache_control(private=True, max_age=12)) @@ -91,10 +81,7 @@ class InstanceEndpoint(BaseAPIView): "key": "IS_GITLAB_ENABLED", "default": os.environ.get("IS_GITLAB_ENABLED", "0"), }, - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST", ""), - }, + {"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST", "")}, { "key": "ENABLE_MAGIC_LINK_LOGIN", "default": os.environ.get("ENABLE_MAGIC_LINK_LOGIN", "1"), @@ -161,9 +148,7 @@ class InstanceEndpoint(BaseAPIView): data["has_openai_configured"] = bool(OPENAI_API_KEY) # File size settings - data["file_size_limit"] = float( - os.environ.get("FILE_SIZE_LIMIT", 5242880) - ) + data["file_size_limit"] = float(os.environ.get("FILE_SIZE_LIMIT", 5242880)) # is smtp configured data["is_smtp_configured"] = bool(EMAIL_HOST) @@ -189,9 +174,7 @@ class InstanceEndpoint(BaseAPIView): def patch(self, request): # Get the instance instance = Instance.objects.first() - serializer = InstanceSerializer( - instance, data=request.data, partial=True - ) + serializer = InstanceSerializer(instance, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) @@ -199,9 +182,7 @@ class InstanceEndpoint(BaseAPIView): class SignUpScreenVisitedEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] @invalidate_cache(path="/api/instances/", user=False) def post(self, request): diff --git a/apiserver/plane/license/bgtasks/tracer.py b/apiserver/plane/license/bgtasks/tracer.py index 44b2be569..60588f5b7 100644 --- a/apiserver/plane/license/bgtasks/tracer.py +++ b/apiserver/plane/license/bgtasks/tracer.py @@ -49,12 +49,8 @@ def instance_traces(): span.set_attribute("instance_name", instance.instance_name) span.set_attribute("current_version", instance.current_version) span.set_attribute("latest_version", instance.latest_version) - span.set_attribute( - "is_telemetry_enabled", instance.is_telemetry_enabled - ) - span.set_attribute( - "is_support_required", instance.is_support_required - ) + span.set_attribute("is_telemetry_enabled", instance.is_telemetry_enabled) + span.set_attribute("is_support_required", instance.is_support_required) span.set_attribute("is_setup_done", instance.is_setup_done) span.set_attribute( "is_signup_screen_visited", instance.is_signup_screen_visited @@ -80,16 +76,10 @@ def instance_traces(): issue_count = Issue.objects.filter(workspace=workspace).count() module_count = Module.objects.filter(workspace=workspace).count() cycle_count = Cycle.objects.filter(workspace=workspace).count() - cycle_issue_count = CycleIssue.objects.filter( - workspace=workspace - ).count() - module_issue_count = ModuleIssue.objects.filter( - workspace=workspace - ).count() + cycle_issue_count = CycleIssue.objects.filter(workspace=workspace).count() + module_issue_count = ModuleIssue.objects.filter(workspace=workspace).count() page_count = Page.objects.filter(workspace=workspace).count() - member_count = WorkspaceMember.objects.filter( - workspace=workspace - ).count() + member_count = WorkspaceMember.objects.filter(workspace=workspace).count() # Set span attributes with tracer.start_as_current_span("workspace_details") as span: diff --git a/apiserver/plane/license/management/commands/configure_instance.py b/apiserver/plane/license/management/commands/configure_instance.py index ba6a57d4b..a1c27851e 100644 --- a/apiserver/plane/license/management/commands/configure_instance.py +++ b/apiserver/plane/license/management/commands/configure_instance.py @@ -177,32 +177,24 @@ class Command(BaseCommand): ) else: self.stdout.write( - self.style.WARNING( - f"{obj.key} configuration already exists" - ) + self.style.WARNING(f"{obj.key} configuration already exists") ) keys = ["IS_GOOGLE_ENABLED", "IS_GITHUB_ENABLED", "IS_GITLAB_ENABLED"] if not InstanceConfiguration.objects.filter(key__in=keys).exists(): for key in keys: if key == "IS_GOOGLE_ENABLED": - GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET = ( - get_configuration_value( - [ - { - "key": "GOOGLE_CLIENT_ID", - "default": os.environ.get( - "GOOGLE_CLIENT_ID", "" - ), - }, - { - "key": "GOOGLE_CLIENT_SECRET", - "default": os.environ.get( - "GOOGLE_CLIENT_SECRET", "0" - ), - }, - ] - ) + GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET = get_configuration_value( + [ + { + "key": "GOOGLE_CLIENT_ID", + "default": os.environ.get("GOOGLE_CLIENT_ID", ""), + }, + { + "key": "GOOGLE_CLIENT_SECRET", + "default": os.environ.get("GOOGLE_CLIENT_SECRET", "0"), + }, + ] ) if bool(GOOGLE_CLIENT_ID) and bool(GOOGLE_CLIENT_SECRET): value = "1" @@ -220,23 +212,17 @@ class Command(BaseCommand): ) ) if key == "IS_GITHUB_ENABLED": - GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET = ( - get_configuration_value( - [ - { - "key": "GITHUB_CLIENT_ID", - "default": os.environ.get( - "GITHUB_CLIENT_ID", "" - ), - }, - { - "key": "GITHUB_CLIENT_SECRET", - "default": os.environ.get( - "GITHUB_CLIENT_SECRET", "0" - ), - }, - ] - ) + GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET = get_configuration_value( + [ + { + "key": "GITHUB_CLIENT_ID", + "default": os.environ.get("GITHUB_CLIENT_ID", ""), + }, + { + "key": "GITHUB_CLIENT_SECRET", + "default": os.environ.get("GITHUB_CLIENT_SECRET", "0"), + }, + ] ) if bool(GITHUB_CLIENT_ID) and bool(GITHUB_CLIENT_SECRET): value = "1" @@ -265,9 +251,7 @@ class Command(BaseCommand): }, { "key": "GITLAB_CLIENT_ID", - "default": os.environ.get( - "GITLAB_CLIENT_ID", "" - ), + "default": os.environ.get("GITLAB_CLIENT_ID", ""), }, { "key": "GITLAB_CLIENT_SECRET", diff --git a/apiserver/plane/license/management/commands/register_instance.py b/apiserver/plane/license/management/commands/register_instance.py index 2ee4239d1..692ca350f 100644 --- a/apiserver/plane/license/management/commands/register_instance.py +++ b/apiserver/plane/license/management/commands/register_instance.py @@ -18,9 +18,7 @@ class Command(BaseCommand): def add_arguments(self, parser): # Positional argument - parser.add_argument( - "machine_signature", type=str, help="Machine signature" - ) + parser.add_argument("machine_signature", type=str, help="Machine signature") def read_package_json(self): with open("package.json", "r") as file: @@ -39,9 +37,7 @@ class Command(BaseCommand): # If instance is None then register this instance if instance is None: - machine_signature = options.get( - "machine_signature", "machine-signature" - ) + machine_signature = options.get("machine_signature", "machine-signature") if not machine_signature: raise CommandError("Machine signature is required") @@ -60,9 +56,7 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS("Instance registered")) else: - self.stdout.write( - self.style.SUCCESS("Instance already registered") - ) + self.stdout.write(self.style.SUCCESS("Instance already registered")) payload = self.read_package_json() # Update the instance details instance.last_checked_at = timezone.now() diff --git a/apiserver/plane/license/models/__init__.py b/apiserver/plane/license/models/__init__.py index 7580abf89..d49524024 100644 --- a/apiserver/plane/license/models/__init__.py +++ b/apiserver/plane/license/models/__init__.py @@ -1,6 +1 @@ -from .instance import ( - Instance, - InstanceAdmin, - InstanceConfiguration, - InstanceEdition, -) +from .instance import Instance, InstanceAdmin, InstanceConfiguration, InstanceEdition diff --git a/apiserver/plane/license/urls.py b/apiserver/plane/license/urls.py index 444d2a119..50e6d0de8 100644 --- a/apiserver/plane/license/urls.py +++ b/apiserver/plane/license/urls.py @@ -11,30 +11,14 @@ from plane.license.api.views import ( InstanceAdminUserMeEndpoint, InstanceAdminSignOutEndpoint, InstanceAdminUserSessionEndpoint, - ChangeLogEndpoint + ChangeLogEndpoint, ) urlpatterns = [ - path( - "", - InstanceEndpoint.as_view(), - name="instance", - ), - path( - "changelog/", - ChangeLogEndpoint.as_view(), - name="instance-changelog", - ), - path( - "admins/", - InstanceAdminEndpoint.as_view(), - name="instance-admins", - ), - path( - "admins/me/", - InstanceAdminUserMeEndpoint.as_view(), - name="instance-admins", - ), + path("", InstanceEndpoint.as_view(), name="instance"), + path("changelog/", ChangeLogEndpoint.as_view(), name="instance-changelog"), + path("admins/", InstanceAdminEndpoint.as_view(), name="instance-admins"), + path("admins/me/", InstanceAdminUserMeEndpoint.as_view(), name="instance-admins"), path( "admins/session/", InstanceAdminUserSessionEndpoint.as_view(), @@ -45,11 +29,7 @@ urlpatterns = [ InstanceAdminSignOutEndpoint.as_view(), name="instance-admins", ), - path( - "admins//", - InstanceAdminEndpoint.as_view(), - name="instance-admins", - ), + path("admins//", InstanceAdminEndpoint.as_view(), name="instance-admins"), path( "configurations/", InstanceConfigurationEndpoint.as_view(), diff --git a/apiserver/plane/license/utils/instance_value.py b/apiserver/plane/license/utils/instance_value.py index 4c191feda..72241fe54 100644 --- a/apiserver/plane/license/utils/instance_value.py +++ b/apiserver/plane/license/utils/instance_value.py @@ -22,9 +22,7 @@ def get_configuration_value(keys): for item in instance_configuration: if key.get("key") == item.get("key"): if item.get("is_encrypted", False): - environment_list.append( - decrypt_data(item.get("value")) - ) + environment_list.append(decrypt_data(item.get("value"))) else: environment_list.append(item.get("value")) @@ -34,9 +32,7 @@ def get_configuration_value(keys): else: # Get the configuration from os for key in keys: - environment_list.append( - os.environ.get(key.get("key"), key.get("default")) - ) + environment_list.append(os.environ.get(key.get("key"), key.get("default"))) return tuple(environment_list) @@ -44,30 +40,15 @@ def get_configuration_value(keys): def get_email_configuration(): return get_configuration_value( [ - { - "key": "EMAIL_HOST", - "default": os.environ.get("EMAIL_HOST"), - }, - { - "key": "EMAIL_HOST_USER", - "default": os.environ.get("EMAIL_HOST_USER"), - }, + {"key": "EMAIL_HOST", "default": os.environ.get("EMAIL_HOST")}, + {"key": "EMAIL_HOST_USER", "default": os.environ.get("EMAIL_HOST_USER")}, { "key": "EMAIL_HOST_PASSWORD", "default": os.environ.get("EMAIL_HOST_PASSWORD"), }, - { - "key": "EMAIL_PORT", - "default": os.environ.get("EMAIL_PORT", 587), - }, - { - "key": "EMAIL_USE_TLS", - "default": os.environ.get("EMAIL_USE_TLS", "1"), - }, - { - "key": "EMAIL_USE_SSL", - "default": os.environ.get("EMAIL_USE_SSL", "0"), - }, + {"key": "EMAIL_PORT", "default": os.environ.get("EMAIL_PORT", 587)}, + {"key": "EMAIL_USE_TLS", "default": os.environ.get("EMAIL_USE_TLS", "1")}, + {"key": "EMAIL_USE_SSL", "default": os.environ.get("EMAIL_USE_SSL", "0")}, { "key": "EMAIL_FROM", "default": os.environ.get( diff --git a/apiserver/plane/middleware/api_log_middleware.py b/apiserver/plane/middleware/api_log_middleware.py index 96c62c2fd..c7a0841ad 100644 --- a/apiserver/plane/middleware/api_log_middleware.py +++ b/apiserver/plane/middleware/api_log_middleware.py @@ -23,13 +23,9 @@ class APITokenLogMiddleware: method=request.method, query_params=request.META.get("QUERY_STRING", ""), headers=str(request.headers), - body=( - request_body.decode("utf-8") if request_body else None - ), + body=(request_body.decode("utf-8") if request_body else None), response_body=( - response.content.decode("utf-8") - if response.content - else None + response.content.decode("utf-8") if response.content else None ), response_code=response.status_code, ip_address=request.META.get("REMOTE_ADDR", None), diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index a7096ef2a..864010d3c 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -18,9 +18,7 @@ from corsheaders.defaults import default_headers # OpenTelemetry from opentelemetry import trace -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import ( - OTLPSpanExporter, -) +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.sdk.resources import Resource @@ -93,20 +91,14 @@ REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "rest_framework.authentication.SessionAuthentication", ), - "DEFAULT_PERMISSION_CLASSES": ( - "rest_framework.permissions.IsAuthenticated", - ), + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), "DEFAULT_RENDERER_CLASSES": ("rest_framework.renderers.JSONRenderer",), - "DEFAULT_FILTER_BACKENDS": ( - "django_filters.rest_framework.DjangoFilterBackend", - ), + "DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",), "EXCEPTION_HANDLER": "plane.authentication.adapter.exception.auth_exception_handler", } # Django Auth Backend -AUTHENTICATION_BACKENDS = ( - "django.contrib.auth.backends.ModelBackend", -) # default +AUTHENTICATION_BACKENDS = ("django.contrib.auth.backends.ModelBackend",) # default # Root Urls ROOT_URLCONF = "plane.urls" @@ -115,9 +107,7 @@ ROOT_URLCONF = "plane.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [ - "templates", - ], + "DIRS": ["templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -125,9 +115,9 @@ TEMPLATES = [ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", - ], + ] }, - }, + } ] @@ -164,9 +154,7 @@ AUTH_USER_MODEL = "db.User" # Database if bool(os.environ.get("DATABASE_URL")): # Parse database configuration from $DATABASE_URL - DATABASES = { - "default": dj_database_url.config(), - } + DATABASES = {"default": dj_database_url.config()} else: DATABASES = { "default": { @@ -199,26 +187,18 @@ else: "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": REDIS_URL, - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, + "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, } } # Password validations AUTH_PASSWORD_VALIDATORS = [ { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" }, + {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, ] # Password reset time the number of seconds the uniquely generated uid will be valid @@ -254,12 +234,10 @@ USE_MINIO = int(os.environ.get("USE_MINIO", 0)) == 1 STORAGES = { "staticfiles": { - "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", - }, -} -STORAGES["default"] = { - "BACKEND": "plane.settings.storage.S3Storage", + "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage" + } } +STORAGES["default"] = {"BACKEND": "plane.settings.storage.S3Storage"} AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", "access-key") AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", "secret-key") AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_S3_BUCKET_NAME", "uploads") @@ -267,9 +245,9 @@ AWS_REGION = os.environ.get("AWS_REGION", "") AWS_DEFAULT_ACL = "public-read" AWS_QUERYSTRING_AUTH = False AWS_S3_FILE_OVERWRITE = False -AWS_S3_ENDPOINT_URL = os.environ.get( - "AWS_S3_ENDPOINT_URL", None -) or os.environ.get("MINIO_ENDPOINT_URL", None) +AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL", None) or os.environ.get( + "MINIO_ENDPOINT_URL", None +) if AWS_S3_ENDPOINT_URL and USE_MINIO: parsed_url = urlparse(os.environ.get("WEB_URL", "http://localhost")) AWS_S3_CUSTOM_DOMAIN = f"{parsed_url.netloc}/{AWS_STORAGE_BUCKET_NAME}" @@ -322,9 +300,7 @@ if bool(os.environ.get("SENTRY_DSN", False)) and os.environ.get( traces_sample_rate=1, send_default_pii=True, environment=os.environ.get("SENTRY_ENVIRONMENT", "development"), - profiles_sample_rate=float( - os.environ.get("SENTRY_PROFILE_SAMPLE_RATE", 0) - ), + profiles_sample_rate=float(os.environ.get("SENTRY_PROFILE_SAMPLE_RATE", 0)), ) @@ -346,8 +322,7 @@ POSTHOG_HOST = os.environ.get("POSTHOG_HOST", False) # instance key INSTANCE_KEY = os.environ.get( - "INSTANCE_KEY", - "ae6517d563dfc13d8270bd45cf17b08f70b37d989128a9dab46ff687603333c3", + "INSTANCE_KEY", "ae6517d563dfc13d8270bd45cf17b08f70b37d989128a9dab46ff687603333c3" ) # Skip environment variable configuration @@ -362,9 +337,7 @@ SESSION_ENGINE = "plane.db.models.session" SESSION_COOKIE_AGE = os.environ.get("SESSION_COOKIE_AGE", 604800) SESSION_COOKIE_NAME = os.environ.get("SESSION_COOKIE_NAME", "session-id") SESSION_COOKIE_DOMAIN = os.environ.get("COOKIE_DOMAIN", None) -SESSION_SAVE_EVERY_REQUEST = ( - os.environ.get("SESSION_SAVE_EVERY_REQUEST", "0") == "1" -) +SESSION_SAVE_EVERY_REQUEST = os.environ.get("SESSION_SAVE_EVERY_REQUEST", "0") == "1" # Admin Cookie ADMIN_SESSION_COOKIE_NAME = "admin-session-id" diff --git a/apiserver/plane/settings/local.py b/apiserver/plane/settings/local.py index b175e4c83..d33115e2b 100644 --- a/apiserver/plane/settings/local.py +++ b/apiserver/plane/settings/local.py @@ -21,9 +21,7 @@ CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": REDIS_URL, # noqa - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, + "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, } } @@ -44,14 +42,14 @@ LOGGING = { "verbose": { "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}", "style": "{", - }, + } }, "handlers": { "console": { "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "verbose", - }, + } }, "loggers": { "django.request": { @@ -59,10 +57,6 @@ LOGGING = { "level": "DEBUG", "propagate": False, }, - "plane": { - "handlers": ["console"], - "level": "DEBUG", - "propagate": False, - }, + "plane": {"handlers": ["console"], "level": "DEBUG", "propagate": False}, }, } diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index 806f83aca..9390a2847 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -59,11 +59,7 @@ LOGGING = { }, }, "loggers": { - "django": { - "handlers": ["console", "file"], - "level": "INFO", - "propagate": True, - }, + "django": {"handlers": ["console", "file"], "level": "INFO", "propagate": True}, "django.request": { "handlers": ["console", "file"], "level": "INFO", diff --git a/apiserver/plane/settings/storage.py b/apiserver/plane/settings/storage.py index ac99077f3..d82d1c912 100644 --- a/apiserver/plane/settings/storage.py +++ b/apiserver/plane/settings/storage.py @@ -12,7 +12,6 @@ from storages.backends.s3boto3 import S3Boto3Storage class S3Storage(S3Boto3Storage): - def url(self, name, parameters=None, expire=None, http_method=None): return name @@ -61,9 +60,7 @@ class S3Storage(S3Boto3Storage): self, object_name, file_type, file_size, expiration=3600 ): """Generate a presigned URL to upload an S3 object""" - fields = { - "Content-Type": file_type, - } + fields = {"Content-Type": file_type} conditions = [ {"bucket": self.aws_storage_bucket_name}, @@ -113,9 +110,7 @@ class S3Storage(S3Boto3Storage): disposition="inline", filename=None, ): - content_disposition = self._get_content_disposition( - disposition, filename - ) + content_disposition = self._get_content_disposition(disposition, filename) """Generate a presigned URL to share an S3 object""" try: response = self.s3_client.generate_presigned_url( diff --git a/apiserver/plane/settings/test.py b/apiserver/plane/settings/test.py index a86b044a3..6a75f7904 100644 --- a/apiserver/plane/settings/test.py +++ b/apiserver/plane/settings/test.py @@ -8,5 +8,5 @@ DEBUG = True EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" INSTALLED_APPS.append( # noqa - "plane.tests", + "plane.tests" ) diff --git a/apiserver/plane/space/serializer/__init__.py b/apiserver/plane/space/serializer/__init__.py index 63dfe9262..ad4e9897d 100644 --- a/apiserver/plane/space/serializer/__init__.py +++ b/apiserver/plane/space/serializer/__init__.py @@ -1,9 +1,5 @@ from .user import UserLiteSerializer -from .issue import ( - LabelLiteSerializer, - StateLiteSerializer, - IssuePublicSerializer, -) +from .issue import LabelLiteSerializer, StateLiteSerializer, IssuePublicSerializer from .state import StateSerializer, StateLiteSerializer diff --git a/apiserver/plane/space/serializer/cycle.py b/apiserver/plane/space/serializer/cycle.py index d4f5d86e0..afa760a59 100644 --- a/apiserver/plane/space/serializer/cycle.py +++ b/apiserver/plane/space/serializer/cycle.py @@ -1,8 +1,6 @@ # Module imports from .base import BaseSerializer -from plane.db.models import ( - Cycle, -) +from plane.db.models import Cycle class CycleBaseSerializer(BaseSerializer): diff --git a/apiserver/plane/space/serializer/intake.py b/apiserver/plane/space/serializer/intake.py index b1e536b6d..444c20d42 100644 --- a/apiserver/plane/space/serializer/intake.py +++ b/apiserver/plane/space/serializer/intake.py @@ -7,10 +7,7 @@ from .user import UserLiteSerializer from .state import StateLiteSerializer from .project import ProjectLiteSerializer from .issue import IssueFlatSerializer, LabelLiteSerializer -from plane.db.models import ( - Issue, - IntakeIssue, -) +from plane.db.models import Issue, IntakeIssue class IntakeIssueSerializer(BaseSerializer): @@ -20,10 +17,7 @@ class IntakeIssueSerializer(BaseSerializer): class Meta: model = IntakeIssue fields = "__all__" - read_only_fields = [ - "project", - "workspace", - ] + read_only_fields = ["project", "workspace"] class IntakeIssueLiteSerializer(BaseSerializer): @@ -36,12 +30,8 @@ class IntakeIssueLiteSerializer(BaseSerializer): class IssueStateIntakeSerializer(BaseSerializer): state_detail = StateLiteSerializer(read_only=True, source="state") project_detail = ProjectLiteSerializer(read_only=True, source="project") - label_details = LabelLiteSerializer( - read_only=True, source="labels", many=True - ) - assignee_details = UserLiteSerializer( - read_only=True, source="assignees", many=True - ) + label_details = LabelLiteSerializer(read_only=True, source="labels", many=True) + assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) sub_issues_count = serializers.IntegerField(read_only=True) bridge_id = serializers.UUIDField(read_only=True) issue_intake = IntakeIssueLiteSerializer(read_only=True, many=True) diff --git a/apiserver/plane/space/serializer/issue.py b/apiserver/plane/space/serializer/issue.py index d7447681f..e1445b4e6 100644 --- a/apiserver/plane/space/serializer/issue.py +++ b/apiserver/plane/space/serializer/issue.py @@ -36,28 +36,17 @@ class IssueStateFlatSerializer(BaseSerializer): class Meta: model = Issue - fields = [ - "id", - "sequence_id", - "name", - "state_detail", - "project_detail", - ] + fields = ["id", "sequence_id", "name", "state_detail", "project_detail"] class LabelSerializer(BaseSerializer): - workspace_detail = WorkspaceLiteSerializer( - source="workspace", read_only=True - ) + workspace_detail = WorkspaceLiteSerializer(source="workspace", read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True) class Meta: model = Label fields = "__all__" - read_only_fields = [ - "workspace", - "project", - ] + read_only_fields = ["workspace", "project"] class IssueProjectLiteSerializer(BaseSerializer): @@ -65,33 +54,17 @@ class IssueProjectLiteSerializer(BaseSerializer): class Meta: model = Issue - fields = [ - "id", - "project_detail", - "name", - "sequence_id", - ] + fields = ["id", "project_detail", "name", "sequence_id"] read_only_fields = fields class IssueRelationSerializer(BaseSerializer): - issue_detail = IssueProjectLiteSerializer( - read_only=True, source="related_issue" - ) + issue_detail = IssueProjectLiteSerializer(read_only=True, source="related_issue") class Meta: model = IssueRelation - fields = [ - "issue_detail", - "relation_type", - "related_issue", - "issue", - "id", - ] - read_only_fields = [ - "workspace", - "project", - ] + fields = ["issue_detail", "relation_type", "related_issue", "issue", "id"] + read_only_fields = ["workspace", "project"] class RelatedIssueSerializer(BaseSerializer): @@ -99,17 +72,8 @@ class RelatedIssueSerializer(BaseSerializer): class Meta: model = IssueRelation - fields = [ - "issue_detail", - "relation_type", - "related_issue", - "issue", - "id", - ] - read_only_fields = [ - "workspace", - "project", - ] + fields = ["issue_detail", "relation_type", "related_issue", "issue", "id"] + read_only_fields = ["workspace", "project"] class IssueCycleDetailSerializer(BaseSerializer): @@ -163,8 +127,7 @@ class IssueLinkSerializer(BaseSerializer): # Validation if url already exists def create(self, validated_data): if IssueLink.objects.filter( - url=validated_data.get("url"), - issue_id=validated_data.get("issue_id"), + url=validated_data.get("url"), issue_id=validated_data.get("issue_id") ).exists(): raise serializers.ValidationError( {"error": "URL already exists for this Issue"} @@ -188,22 +151,10 @@ class IssueAttachmentSerializer(BaseSerializer): class IssueReactionSerializer(BaseSerializer): - class Meta: model = IssueReaction - fields = [ - "issue", - "reaction", - "workspace", - "project", - "actor", - ] - read_only_fields = [ - "workspace", - "project", - "issue", - "actor", - ] + fields = ["issue", "reaction", "workspace", "project", "actor"] + read_only_fields = ["workspace", "project", "issue", "actor"] class IssueSerializer(BaseSerializer): @@ -211,9 +162,7 @@ class IssueSerializer(BaseSerializer): state_detail = StateSerializer(read_only=True, source="state") parent_detail = IssueStateFlatSerializer(read_only=True, source="parent") label_details = LabelSerializer(read_only=True, source="labels", many=True) - assignee_details = UserLiteSerializer( - read_only=True, source="assignees", many=True - ) + assignee_details = UserLiteSerializer(read_only=True, source="assignees", many=True) related_issues = IssueRelationSerializer( read_only=True, source="issue_relation", many=True ) @@ -264,24 +213,15 @@ class CommentReactionLiteSerializer(BaseSerializer): class Meta: model = CommentReaction - fields = [ - "id", - "reaction", - "comment", - "actor_detail", - ] + fields = ["id", "reaction", "comment", "actor_detail"] class IssueCommentSerializer(BaseSerializer): actor_detail = UserLiteSerializer(read_only=True, source="actor") issue_detail = IssueFlatSerializer(read_only=True, source="issue") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer( - read_only=True, source="workspace" - ) - comment_reactions = CommentReactionLiteSerializer( - read_only=True, many=True - ) + workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") + comment_reactions = CommentReactionLiteSerializer(read_only=True, many=True) is_member = serializers.BooleanField(read_only=True) class Meta: @@ -304,9 +244,7 @@ class IssueCreateSerializer(BaseSerializer): state_detail = StateSerializer(read_only=True, source="state") created_by_detail = UserLiteSerializer(read_only=True, source="created_by") project_detail = ProjectLiteSerializer(read_only=True, source="project") - workspace_detail = WorkspaceLiteSerializer( - read_only=True, source="workspace" - ) + workspace_detail = WorkspaceLiteSerializer(read_only=True, source="workspace") assignees = serializers.ListField( child=serializers.PrimaryKeyRelatedField(queryset=User.objects.all()), @@ -334,9 +272,7 @@ class IssueCreateSerializer(BaseSerializer): def to_representation(self, instance): data = super().to_representation(instance) - data["assignees"] = [ - str(assignee.id) for assignee in instance.assignees.all() - ] + data["assignees"] = [str(assignee.id) for assignee in instance.assignees.all()] data["labels"] = [str(label.id) for label in instance.labels.all()] return data @@ -346,9 +282,7 @@ class IssueCreateSerializer(BaseSerializer): and data.get("target_date", None) is not None and data.get("start_date", None) > data.get("target_date", None) ): - raise serializers.ValidationError( - "Start date cannot exceed target date" - ) + raise serializers.ValidationError("Start date cannot exceed target date") return data def create(self, validated_data): @@ -467,16 +401,9 @@ class CommentReactionSerializer(BaseSerializer): class IssueVoteSerializer(BaseSerializer): - class Meta: model = IssueVote - fields = [ - "issue", - "vote", - "workspace", - "project", - "actor", - ] + fields = ["issue", "vote", "workspace", "project", "actor"] read_only_fields = fields @@ -485,18 +412,9 @@ class IssuePublicSerializer(BaseSerializer): read_only=True, many=True, source="issue_reactions" ) votes = IssueVoteSerializer(read_only=True, many=True) - module_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) - label_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) - assignee_ids = serializers.ListField( - child=serializers.UUIDField(), - required=False, - ) + module_ids = serializers.ListField(child=serializers.UUIDField(), required=False) + label_ids = serializers.ListField(child=serializers.UUIDField(), required=False) + assignee_ids = serializers.ListField(child=serializers.UUIDField(), required=False) class Meta: model = Issue @@ -522,8 +440,4 @@ class IssuePublicSerializer(BaseSerializer): class LabelLiteSerializer(BaseSerializer): class Meta: model = Label - fields = [ - "id", - "name", - "color", - ] + fields = ["id", "name", "color"] diff --git a/apiserver/plane/space/serializer/module.py b/apiserver/plane/space/serializer/module.py index dda1861d1..53840f078 100644 --- a/apiserver/plane/space/serializer/module.py +++ b/apiserver/plane/space/serializer/module.py @@ -1,8 +1,6 @@ # Module imports from .base import BaseSerializer -from plane.db.models import ( - Module, -) +from plane.db.models import Module class ModuleBaseSerializer(BaseSerializer): diff --git a/apiserver/plane/space/serializer/project.py b/apiserver/plane/space/serializer/project.py index be23e0ce2..f79eef686 100644 --- a/apiserver/plane/space/serializer/project.py +++ b/apiserver/plane/space/serializer/project.py @@ -1,8 +1,6 @@ # Module imports from .base import BaseSerializer -from plane.db.models import ( - Project, -) +from plane.db.models import Project class ProjectLiteSerializer(BaseSerializer): diff --git a/apiserver/plane/space/serializer/state.py b/apiserver/plane/space/serializer/state.py index 55064ed0e..184f48b40 100644 --- a/apiserver/plane/space/serializer/state.py +++ b/apiserver/plane/space/serializer/state.py @@ -1,27 +1,17 @@ # Module imports from .base import BaseSerializer -from plane.db.models import ( - State, -) +from plane.db.models import State class StateSerializer(BaseSerializer): class Meta: model = State fields = "__all__" - read_only_fields = [ - "workspace", - "project", - ] + read_only_fields = ["workspace", "project"] class StateLiteSerializer(BaseSerializer): class Meta: model = State - fields = [ - "id", - "name", - "color", - "group", - ] + fields = ["id", "name", "color", "group"] read_only_fields = fields diff --git a/apiserver/plane/space/serializer/user.py b/apiserver/plane/space/serializer/user.py index 13cd2e45e..9b707a343 100644 --- a/apiserver/plane/space/serializer/user.py +++ b/apiserver/plane/space/serializer/user.py @@ -1,8 +1,6 @@ # Module imports from .base import BaseSerializer -from plane.db.models import ( - User, -) +from plane.db.models import User class UserLiteSerializer(BaseSerializer): @@ -17,7 +15,4 @@ class UserLiteSerializer(BaseSerializer): "is_bot", "display_name", ] - read_only_fields = [ - "id", - "is_bot", - ] + read_only_fields = ["id", "is_bot"] diff --git a/apiserver/plane/space/serializer/workspace.py b/apiserver/plane/space/serializer/workspace.py index a31bb3744..4945af96a 100644 --- a/apiserver/plane/space/serializer/workspace.py +++ b/apiserver/plane/space/serializer/workspace.py @@ -1,16 +1,10 @@ # Module imports from .base import BaseSerializer -from plane.db.models import ( - Workspace, -) +from plane.db.models import Workspace class WorkspaceLiteSerializer(BaseSerializer): class Meta: model = Workspace - fields = [ - "name", - "slug", - "id", - ] + fields = ["name", "slug", "id"] read_only_fields = fields diff --git a/apiserver/plane/space/urls/__init__.py b/apiserver/plane/space/urls/__init__.py index 3ef68006e..d9a1f6ec3 100644 --- a/apiserver/plane/space/urls/__init__.py +++ b/apiserver/plane/space/urls/__init__.py @@ -4,9 +4,4 @@ from .project import urlpatterns as project_urls from .asset import urlpatterns as asset_urls -urlpatterns = [ - *intake_urls, - *issue_urls, - *project_urls, - *asset_urls, -] +urlpatterns = [*intake_urls, *issue_urls, *project_urls, *asset_urls] diff --git a/apiserver/plane/space/urls/intake.py b/apiserver/plane/space/urls/intake.py index 350157c95..09aca16df 100644 --- a/apiserver/plane/space/urls/intake.py +++ b/apiserver/plane/space/urls/intake.py @@ -10,32 +10,18 @@ from plane.space.views import ( urlpatterns = [ path( "anchor//intakes//intake-issues/", - IntakeIssuePublicViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IntakeIssuePublicViewSet.as_view({"get": "list", "post": "create"}), name="intake-issue", ), path( "anchor//intakes//inbox-issues/", - IntakeIssuePublicViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IntakeIssuePublicViewSet.as_view({"get": "list", "post": "create"}), name="inbox-issue", ), path( "anchor//intakes//intake-issues//", IntakeIssuePublicViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="intake-issue", ), diff --git a/apiserver/plane/space/urls/issue.py b/apiserver/plane/space/urls/issue.py index 8b7dce0e6..2391fd38a 100644 --- a/apiserver/plane/space/urls/issue.py +++ b/apiserver/plane/space/urls/issue.py @@ -17,71 +17,40 @@ urlpatterns = [ ), path( "anchor//issues//comments/", - IssueCommentPublicViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueCommentPublicViewSet.as_view({"get": "list", "post": "create"}), name="issue-comments-project-board", ), path( "anchor//issues//comments//", IssueCommentPublicViewSet.as_view( - { - "get": "retrieve", - "patch": "partial_update", - "delete": "destroy", - } + {"get": "retrieve", "patch": "partial_update", "delete": "destroy"} ), name="issue-comments-project-board", ), path( "anchor//issues//reactions/", - IssueReactionPublicViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + IssueReactionPublicViewSet.as_view({"get": "list", "post": "create"}), name="issue-reactions-project-board", ), path( "anchor//issues//reactions//", - IssueReactionPublicViewSet.as_view( - { - "delete": "destroy", - } - ), + IssueReactionPublicViewSet.as_view({"delete": "destroy"}), name="issue-reactions-project-board", ), path( "anchor//comments//reactions/", - CommentReactionPublicViewSet.as_view( - { - "get": "list", - "post": "create", - } - ), + CommentReactionPublicViewSet.as_view({"get": "list", "post": "create"}), name="comment-reactions-project-board", ), path( "anchor//comments//reactions//", - CommentReactionPublicViewSet.as_view( - { - "delete": "destroy", - } - ), + CommentReactionPublicViewSet.as_view({"delete": "destroy"}), name="comment-reactions-project-board", ), path( "anchor//issues//votes/", IssueVotePublicViewSet.as_view( - { - "get": "list", - "post": "create", - "delete": "destroy", - } + {"get": "list", "post": "create", "delete": "destroy"} ), name="issue-vote-project-board", ), diff --git a/apiserver/plane/space/utils/grouper.py b/apiserver/plane/space/utils/grouper.py index 680359790..250b54e89 100644 --- a/apiserver/plane/space/utils/grouper.py +++ b/apiserver/plane/space/utils/grouper.py @@ -1,16 +1,7 @@ # Django imports from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.fields import ArrayField -from django.db.models import ( - Q, - UUIDField, - Value, - F, - Case, - When, - JSONField, - CharField, -) +from django.db.models import Q, UUIDField, Value, F, Case, When, JSONField, CharField from django.db.models.functions import Coalesce, JSONObject, Concat # Module imports @@ -27,7 +18,6 @@ from plane.db.models import ( def issue_queryset_grouper(queryset, group_by, sub_group_by): - FIELD_MAPPER = { "label_ids": "labels__id", "assignee_ids": "assignees__id", @@ -37,13 +27,11 @@ def issue_queryset_grouper(queryset, group_by, sub_group_by): annotations_map = { "assignee_ids": ( "assignees__id", - ~Q(assignees__id__isnull=True) - & Q(issue_assignee__deleted_at__isnull=True), + ~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), + ~Q(labels__id__isnull=True) & Q(label_issue__deleted_at__isnull=True), ), "module_ids": ( "issue_module__module_id", @@ -52,23 +40,17 @@ def issue_queryset_grouper(queryset, group_by, sub_group_by): } default_annotations = { key: Coalesce( - ArrayAgg( - field, - distinct=True, - filter=condition, - ), + ArrayAgg(field, distinct=True, filter=condition), Value([], output_field=ArrayField(UUIDField())), ) for key, (field, condition) in annotations_map.items() - if FIELD_MAPPER.get(key) != group_by - or FIELD_MAPPER.get(key) != sub_group_by + if FIELD_MAPPER.get(key) != group_by or FIELD_MAPPER.get(key) != sub_group_by } return queryset.annotate(**default_annotations) def issue_on_results(issues, group_by, sub_group_by): - FIELD_MAPPER = { "labels__id": "label_ids", "assignees__id": "assignee_ids", @@ -154,18 +136,14 @@ def issue_on_results(issues, group_by, sub_group_by): issue_reactions__actor__avatar_asset__isnull=False, then=Concat( Value("/api/assets/v2/static/"), - F( - "issue_reactions__actor__avatar_asset" - ), + F("issue_reactions__actor__avatar_asset"), Value("/"), ), ), default=F("issue_reactions__actor__avatar"), output_field=CharField(), ), - display_name=F( - "issue_reactions__actor__display_name" - ), + display_name=F("issue_reactions__actor__display_name"), ), ), ), @@ -183,8 +161,7 @@ def issue_on_results(issues, group_by, sub_group_by): def issue_group_values(field, slug, project_id=None, filters=dict): if field == "state_id": queryset = State.objects.filter( - is_triage=False, - workspace__slug=slug, + is_triage=False, workspace__slug=slug ).values_list("id", flat=True) if project_id: return list(queryset.filter(project_id=project_id)) @@ -201,9 +178,7 @@ def issue_group_values(field, slug, project_id=None, filters=dict): if field == "assignees__id": if project_id: return ProjectMember.objects.filter( - workspace__slug=slug, - project_id=project_id, - is_active=True, + workspace__slug=slug, project_id=project_id, is_active=True ).values_list("member_id", flat=True) else: return list( @@ -212,17 +187,17 @@ def issue_group_values(field, slug, project_id=None, filters=dict): ).values_list("member_id", flat=True) ) if field == "issue_module__module_id": - queryset = Module.objects.filter( - workspace__slug=slug, - ).values_list("id", flat=True) + queryset = Module.objects.filter(workspace__slug=slug).values_list( + "id", flat=True + ) if project_id: return list(queryset.filter(project_id=project_id)) + ["None"] else: return list(queryset) + ["None"] if field == "cycle_id": - queryset = Cycle.objects.filter( - workspace__slug=slug, - ).values_list("id", flat=True) + queryset = Cycle.objects.filter(workspace__slug=slug).values_list( + "id", flat=True + ) if project_id: return list(queryset.filter(project_id=project_id)) + ["None"] else: @@ -233,21 +208,9 @@ def issue_group_values(field, slug, project_id=None, filters=dict): ) return list(queryset) if field == "priority": - return [ - "low", - "medium", - "high", - "urgent", - "none", - ] + return ["low", "medium", "high", "urgent", "none"] if field == "state__group": - return [ - "backlog", - "unstarted", - "started", - "completed", - "cancelled", - ] + return ["backlog", "unstarted", "started", "completed", "cancelled"] if field == "target_date": queryset = ( Issue.issue_objects.filter(workspace__slug=slug) diff --git a/apiserver/plane/space/views/__init__.py b/apiserver/plane/space/views/__init__.py index 4167bdaf8..afdc1d337 100644 --- a/apiserver/plane/space/views/__init__.py +++ b/apiserver/plane/space/views/__init__.py @@ -24,8 +24,4 @@ from .state import ProjectStatesEndpoint from .label import ProjectLabelsEndpoint -from .asset import ( - EntityAssetEndpoint, - AssetRestoreEndpoint, - EntityBulkAssetEndpoint, -) +from .asset import EntityAssetEndpoint, AssetRestoreEndpoint, EntityBulkAssetEndpoint diff --git a/apiserver/plane/space/views/asset.py b/apiserver/plane/space/views/asset.py index 4ceff9089..2c6722038 100644 --- a/apiserver/plane/space/views/asset.py +++ b/apiserver/plane/space/views/asset.py @@ -19,16 +19,11 @@ from plane.bgtasks.storage_metadata_task import get_asset_object_metadata class EntityAssetEndpoint(BaseAPIView): - def get_permissions(self): if self.request.method == "GET": - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] else: - permission_classes = [ - IsAuthenticated, - ] + permission_classes = [IsAuthenticated] return [permission() for permission in permission_classes] def get(self, request, anchor, pk): @@ -54,18 +49,14 @@ class EntityAssetEndpoint(BaseAPIView): # Check if the asset is uploaded if not asset.is_uploaded: return Response( - { - "error": "The requested asset could not be found.", - }, + {"error": "The requested asset could not be found."}, status=status.HTTP_404_NOT_FOUND, ) # Get the presigned URL storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object - signed_url = storage.generate_presigned_url( - object_name=asset.asset.name, - ) + signed_url = storage.generate_presigned_url(object_name=asset.asset.name) # Redirect to the signed URL return HttpResponseRedirect(signed_url) @@ -77,8 +68,7 @@ class EntityAssetEndpoint(BaseAPIView): # Check if the project is published if not deploy_board: return Response( - {"error": "Project is not published"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project is not published"}, status=status.HTTP_404_NOT_FOUND ) # Get the asset @@ -91,10 +81,7 @@ class EntityAssetEndpoint(BaseAPIView): # Check if the entity type is allowed if entity_type not in FileAsset.EntityTypeContext.values: return Response( - { - "error": "Invalid entity type.", - "status": False, - }, + {"error": "Invalid entity type.", "status": False}, status=status.HTTP_400_BAD_REQUEST, ) @@ -114,11 +101,7 @@ class EntityAssetEndpoint(BaseAPIView): # Create a File Asset asset = FileAsset.objects.create( - attributes={ - "name": name, - "type": type, - "size": size, - }, + attributes={"name": name, "type": type, "size": size}, asset=asset_key, size=size, workspace=deploy_board.workspace, @@ -132,9 +115,7 @@ class EntityAssetEndpoint(BaseAPIView): storage = S3Storage(request=request) # Generate a presigned URL to share an S3 object presigned_url = storage.generate_presigned_post( - object_name=asset_key, - file_type=type, - file_size=size, + object_name=asset_key, file_type=type, file_size=size ) # Return the presigned URL return Response( @@ -154,8 +135,7 @@ class EntityAssetEndpoint(BaseAPIView): # Check if the project is published if not deploy_board: return Response( - {"error": "Project is not published"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project is not published"}, status=status.HTTP_404_NOT_FOUND ) # get the asset id @@ -180,14 +160,11 @@ class EntityAssetEndpoint(BaseAPIView): # Check if the project is published if not deploy_board: return Response( - {"error": "Project is not published"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project is not published"}, status=status.HTTP_404_NOT_FOUND ) # Get the asset asset = FileAsset.objects.get( - id=pk, - workspace=deploy_board.workspace, - project_id=deploy_board.project_id, + id=pk, workspace=deploy_board.workspace, project_id=deploy_board.project_id ) # Check deleted assets asset.is_deleted = True @@ -208,14 +185,11 @@ class AssetRestoreEndpoint(BaseAPIView): # Check if the project is published if not deploy_board: return Response( - {"error": "Project is not published"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project is not published"}, status=status.HTTP_404_NOT_FOUND ) # Get the asset - asset = FileAsset.all_objects.get( - id=asset_id, workspace=deploy_board.workspace - ) + asset = FileAsset.all_objects.get(id=asset_id, workspace=deploy_board.workspace) asset.is_deleted = False asset.deleted_at = None asset.save(update_fields=["is_deleted", "deleted_at"]) @@ -233,8 +207,7 @@ class EntityBulkAssetEndpoint(BaseAPIView): # Check if the project is published if not deploy_board: return Response( - {"error": "Project is not published"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project is not published"}, status=status.HTTP_404_NOT_FOUND ) asset_ids = request.data.get("asset_ids", []) @@ -242,10 +215,7 @@ class EntityBulkAssetEndpoint(BaseAPIView): # Check if the asset ids are provided if not asset_ids: return Response( - { - "error": "No asset ids provided.", - }, - status=status.HTTP_400_BAD_REQUEST, + {"error": "No asset ids provided."}, status=status.HTTP_400_BAD_REQUEST ) # get the asset id @@ -260,19 +230,12 @@ class EntityBulkAssetEndpoint(BaseAPIView): # Check if the asset is uploaded if not asset: return Response( - { - "error": "The requested asset could not be found.", - }, + {"error": "The requested asset could not be found."}, status=status.HTTP_404_NOT_FOUND, ) # Check if the entity type is allowed - if ( - asset.entity_type - == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION - ): + if asset.entity_type == FileAsset.EntityTypeContext.COMMENT_DESCRIPTION: # update the attributes - assets.update( - comment_id=entity_id, - ) + assets.update(comment_id=entity_id) return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/apiserver/plane/space/views/base.py b/apiserver/plane/space/views/base.py index 6b18a1546..82809f08d 100644 --- a/apiserver/plane/space/views/base.py +++ b/apiserver/plane/space/views/base.py @@ -41,18 +41,11 @@ class TimezoneMixin: class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): model = None - permission_classes = [ - IsAuthenticated, - ] + permission_classes = [IsAuthenticated] - filter_backends = ( - DjangoFilterBackend, - SearchFilter, - ) + filter_backends = (DjangoFilterBackend, SearchFilter) - authentication_classes = [ - BaseSessionAuthentication, - ] + authentication_classes = [BaseSessionAuthentication] filterset_fields = [] @@ -63,9 +56,7 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): return self.model.objects.all() except Exception as e: log_exception(e) - raise APIException( - "Please check the view", status.HTTP_400_BAD_REQUEST - ) + raise APIException("Please check the view", status.HTTP_400_BAD_REQUEST) def handle_exception(self, exc): """ @@ -138,22 +129,15 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator): class BaseAPIView(TimezoneMixin, APIView, BasePaginator): - permission_classes = [ - IsAuthenticated, - ] + permission_classes = [IsAuthenticated] - filter_backends = ( - DjangoFilterBackend, - SearchFilter, - ) + filter_backends = (DjangoFilterBackend, SearchFilter) filterset_fields = [] search_fields = [] - authentication_classes = [ - BaseSessionAuthentication, - ] + authentication_classes = [BaseSessionAuthentication] def filter_queryset(self, queryset): for backend in list(self.filter_backends): diff --git a/apiserver/plane/space/views/cycle.py b/apiserver/plane/space/views/cycle.py index 49cc82e89..399d626a1 100644 --- a/apiserver/plane/space/views/cycle.py +++ b/apiserver/plane/space/views/cycle.py @@ -5,23 +5,17 @@ from rest_framework.response import Response # Module imports from .base import BaseAPIView -from plane.db.models import ( - DeployBoard, - Cycle, -) +from plane.db.models import DeployBoard, Cycle class ProjectCyclesEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): deploy_board = DeployBoard.objects.filter(anchor=anchor).first() if not deploy_board: return Response( - {"error": "Invalid anchor"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Invalid anchor"}, status=status.HTTP_404_NOT_FOUND ) cycles = Cycle.objects.filter( @@ -29,7 +23,4 @@ class ProjectCyclesEndpoint(BaseAPIView): project_id=deploy_board.project_id, ).values("id", "name") - return Response( - cycles, - status=status.HTTP_200_OK, - ) + return Response(cycles, status=status.HTTP_200_OK) diff --git a/apiserver/plane/space/views/intake.py b/apiserver/plane/space/views/intake.py index d9b12b022..0d39dd276 100644 --- a/apiserver/plane/space/views/intake.py +++ b/apiserver/plane/space/views/intake.py @@ -12,14 +12,7 @@ from rest_framework.response import Response # Module imports from .base import BaseViewSet -from plane.db.models import ( - IntakeIssue, - Issue, - State, - IssueLink, - FileAsset, - DeployBoard, -) +from plane.db.models import IntakeIssue, Issue, State, IssueLink, FileAsset, DeployBoard from plane.app.serializers import ( IssueSerializer, IntakeIssueSerializer, @@ -34,9 +27,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): serializer_class = IntakeIssueSerializer model = IntakeIssue - filterset_fields = [ - "status", - ] + filterset_fields = ["status"] def get_queryset(self): project_deploy_board = DeployBoard.objects.get( @@ -48,8 +39,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): super() .get_queryset() .filter( - Q(snoozed_till__gte=timezone.now()) - | Q(snoozed_till__isnull=True), + Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), project_id=self.kwargs.get("project_id"), workspace__slug=self.kwargs.get("slug"), intake_id=self.kwargs.get("intake_id"), @@ -81,9 +71,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): .prefetch_related("assignees", "labels") .order_by("issue_intake__snoozed_till", "issue_intake__status") .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -113,10 +101,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): ) ) issues_data = IssueStateIntakeSerializer(issues, many=True).data - return Response( - issues_data, - status=status.HTTP_200_OK, - ) + return Response(issues_data, status=status.HTTP_200_OK) def create(self, request, anchor, intake_id): project_deploy_board = DeployBoard.objects.get( @@ -130,8 +115,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): if not request.data.get("issue", {}).get("name", False): return Response( - {"error": "Name is required"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Name is required"}, status=status.HTTP_400_BAD_REQUEST ) # Check for valid priority @@ -143,8 +127,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): "none", ]: return Response( - {"error": "Invalid priority"}, - status=status.HTTP_400_BAD_REQUEST, + {"error": "Invalid priority"}, status=status.HTTP_400_BAD_REQUEST ) # Create or get state @@ -229,9 +212,7 @@ class IntakeIssuePublicViewSet(BaseViewSet): "description": issue_data.get("description", issue.description), } - issue_serializer = IssueCreateSerializer( - issue, data=issue_data, partial=True - ) + issue_serializer = IssueCreateSerializer(issue, data=issue_data, partial=True) if issue_serializer.is_valid(): current_instance = issue @@ -245,16 +226,13 @@ class IntakeIssuePublicViewSet(BaseViewSet): issue_id=str(issue.id), project_id=str(project_deploy_board.project_id), current_instance=json.dumps( - IssueSerializer(current_instance).data, - cls=DjangoJSONEncoder, + IssueSerializer(current_instance).data, cls=DjangoJSONEncoder ), epoch=int(timezone.now().timestamp()), ) issue_serializer.save() return Response(issue_serializer.data, status=status.HTTP_200_OK) - return Response( - issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST - ) + return Response(issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, anchor, intake_id, pk): project_deploy_board = DeployBoard.objects.get( diff --git a/apiserver/plane/space/views/issue.py b/apiserver/plane/space/views/issue.py index f67b8ebf7..a1ab332f9 100644 --- a/apiserver/plane/space/views/issue.py +++ b/apiserver/plane/space/views/issue.py @@ -42,10 +42,7 @@ from plane.space.utils.grouper import ( from plane.utils.order_queryset import order_issue_queryset -from plane.utils.paginator import ( - GroupedOffsetPaginator, - SubGroupedOffsetPaginator, -) +from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator from plane.app.serializers import ( CommentReactionSerializer, IssueCommentSerializer, @@ -70,9 +67,7 @@ from plane.utils.issue_filters import issue_filters class ProjectIssuesPublicEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): filters = issue_filters(request.query_params, "GET") @@ -83,17 +78,14 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): ).first() if not deploy_board: return Response( - {"error": "Project is not published"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Project is not published"}, status=status.HTTP_404_NOT_FOUND ) project_id = deploy_board.entity_identifier slug = deploy_board.workspace.slug issue_queryset = ( - Issue.issue_objects.filter( - workspace__slug=slug, project_id=project_id - ) + Issue.issue_objects.filter(workspace__slug=slug, project_id=project_id) .select_related("workspace", "project", "state", "parent") .prefetch_related("assignees", "labels", "issue_module__module") .prefetch_related( @@ -103,10 +95,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): ) ) .prefetch_related( - Prefetch( - "votes", - queryset=IssueVote.objects.select_related("actor"), - ) + Prefetch("votes", queryset=IssueVote.objects.select_related("actor")) ) .annotate( cycle_id=Subquery( @@ -131,9 +120,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): .values("count") ) .annotate( - sub_issues_count=Issue.issue_objects.filter( - parent=OuterRef("id") - ) + sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id")) .order_by() .annotate(count=Func(F("id"), function="Count")) .values("count") @@ -144,8 +131,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): # Issue queryset issue_queryset, order_by_param = order_issue_queryset( - issue_queryset=issue_queryset, - order_by_param=order_by_param, + issue_queryset=issue_queryset, order_by_param=order_by_param ) # Group by @@ -154,9 +140,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): # issue queryset issue_queryset = issue_queryset_grouper( - queryset=issue_queryset, - group_by=group_by, - sub_group_by=sub_group_by, + queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by ) if group_by: @@ -174,9 +158,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=SubGroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -209,9 +191,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): order_by=order_by_param, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), paginator_cls=GroupedOffsetPaginator, group_by_fields=issue_group_values( @@ -236,9 +216,7 @@ class ProjectIssuesPublicEndpoint(BaseAPIView): request=request, queryset=issue_queryset, on_results=lambda issues: issue_on_results( - group_by=group_by, - issues=issues, - sub_group_by=sub_group_by, + group_by=group_by, issues=issues, sub_group_by=sub_group_by ), ) @@ -247,28 +225,20 @@ class IssueCommentPublicViewSet(BaseViewSet): serializer_class = IssueCommentSerializer model = IssueComment - filterset_fields = [ - "issue__id", - "workspace__id", - ] + filterset_fields = ["issue__id", "workspace__id"] def get_permissions(self): if self.action in ["list", "retrieve"]: - self.permission_classes = [ - AllowAny, - ] + self.permission_classes = [AllowAny] else: - self.permission_classes = [ - IsAuthenticated, - ] + self.permission_classes = [IsAuthenticated] return super(IssueCommentPublicViewSet, self).get_permissions() def get_queryset(self): try: project_deploy_board = DeployBoard.objects.get( - anchor=self.kwargs.get("anchor"), - entity_name="project", + anchor=self.kwargs.get("anchor"), entity_name="project" ) if project_deploy_board.is_comments_enabled: return self.filter_queryset( @@ -317,9 +287,7 @@ class IssueCommentPublicViewSet(BaseViewSet): ) issue_activity.delay( type="comment.activity.created", - requested_data=json.dumps( - serializer.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(serializer.data, cls=DjangoJSONEncoder), actor_id=str(request.user.id), issue_id=str(issue_id), project_id=str(project_deploy_board.project_id), @@ -333,8 +301,7 @@ class IssueCommentPublicViewSet(BaseViewSet): ).exists(): # Add the user for workspace tracking _ = ProjectPublicMember.objects.get_or_create( - project_id=project_deploy_board.project_id, - member=request.user, + project_id=project_deploy_board.project_id, member=request.user ) return Response(serializer.data, status=status.HTTP_201_CREATED) @@ -351,9 +318,7 @@ class IssueCommentPublicViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) comment = IssueComment.objects.get(pk=pk, actor=request.user) - serializer = IssueCommentSerializer( - comment, data=request.data, partial=True - ) + serializer = IssueCommentSerializer(comment, data=request.data, partial=True) if serializer.is_valid(): serializer.save() issue_activity.delay( @@ -363,8 +328,7 @@ class IssueCommentPublicViewSet(BaseViewSet): issue_id=str(issue_id), project_id=str(project_deploy_board.project_id), current_instance=json.dumps( - IssueCommentSerializer(comment).data, - cls=DjangoJSONEncoder, + IssueCommentSerializer(comment).data, cls=DjangoJSONEncoder ), epoch=int(timezone.now().timestamp()), ) @@ -381,10 +345,7 @@ class IssueCommentPublicViewSet(BaseViewSet): {"error": "Comments are not enabled for this project"}, status=status.HTTP_400_BAD_REQUEST, ) - comment = IssueComment.objects.get( - pk=pk, - actor=request.user, - ) + comment = IssueComment.objects.get(pk=pk, actor=request.user) issue_activity.delay( type="comment.activity.deleted", requested_data=json.dumps({"comment_id": str(pk)}), @@ -392,8 +353,7 @@ class IssueCommentPublicViewSet(BaseViewSet): issue_id=str(issue_id), project_id=str(project_deploy_board.project_id), current_instance=json.dumps( - IssueCommentSerializer(comment).data, - cls=DjangoJSONEncoder, + IssueCommentSerializer(comment).data, cls=DjangoJSONEncoder ), epoch=int(timezone.now().timestamp()), ) @@ -450,14 +410,11 @@ class IssueReactionPublicViewSet(BaseViewSet): ).exists(): # Add the user for workspace tracking _ = ProjectPublicMember.objects.get_or_create( - project_id=project_deploy_board.project_id, - member=request.user, + project_id=project_deploy_board.project_id, member=request.user ) issue_activity.delay( type="issue_reaction.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(project_deploy_board.project_id), @@ -490,10 +447,7 @@ class IssueReactionPublicViewSet(BaseViewSet): issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(project_deploy_board.project_id), current_instance=json.dumps( - { - "reaction": str(reaction_code), - "identifier": str(issue_reaction.id), - } + {"reaction": str(reaction_code), "identifier": str(issue_reaction.id)} ), epoch=int(timezone.now().timestamp()), ) @@ -549,14 +503,11 @@ class CommentReactionPublicViewSet(BaseViewSet): ).exists(): # Add the user for workspace tracking _ = ProjectPublicMember.objects.get_or_create( - project_id=project_deploy_board.project_id, - member=request.user, + project_id=project_deploy_board.project_id, member=request.user ) issue_activity.delay( type="comment_reaction.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=None, project_id=str(self.kwargs.get("project_id", None)), @@ -609,8 +560,7 @@ class IssueVotePublicViewSet(BaseViewSet): def get_queryset(self): try: project_deploy_board = DeployBoard.objects.get( - workspace__slug=self.kwargs.get("anchor"), - entity_name="project", + workspace__slug=self.kwargs.get("anchor"), entity_name="project" ) if project_deploy_board.is_votes_enabled: return ( @@ -640,16 +590,13 @@ class IssueVotePublicViewSet(BaseViewSet): is_active=True, ).exists(): _ = ProjectPublicMember.objects.get_or_create( - project_id=project_deploy_board.project_id, - member=request.user, + project_id=project_deploy_board.project_id, member=request.user ) issue_vote.vote = request.data.get("vote", 1) issue_vote.save() issue_activity.delay( type="issue_vote.activity.created", - requested_data=json.dumps( - self.request.data, cls=DjangoJSONEncoder - ), + requested_data=json.dumps(self.request.data, cls=DjangoJSONEncoder), actor_id=str(self.request.user.id), issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(project_deploy_board.project_id), @@ -676,10 +623,7 @@ class IssueVotePublicViewSet(BaseViewSet): issue_id=str(self.kwargs.get("issue_id", None)), project_id=str(project_deploy_board.project_id), current_instance=json.dumps( - { - "vote": str(issue_vote.vote), - "identifier": str(issue_vote.id), - } + {"vote": str(issue_vote.vote), "identifier": str(issue_vote.id)} ), epoch=int(timezone.now().timestamp()), ) @@ -688,9 +632,7 @@ class IssueVotePublicViewSet(BaseViewSet): class IssueRetrievePublicEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor, issue_id): deploy_board = DeployBoard.objects.get(anchor=anchor) @@ -717,7 +659,7 @@ class IssueRetrievePublicEndpoint(BaseAPIView): distinct=True, filter=Q( ~Q(labels__id__isnull=True) - & Q(label_issue__deleted_at__isnull=True), + & Q(label_issue__deleted_at__isnull=True) ), ), Value([], output_field=ArrayField(UUIDField())), @@ -748,16 +690,11 @@ class IssueRetrievePublicEndpoint(BaseAPIView): .prefetch_related( Prefetch( "issue_reactions", - queryset=IssueReaction.objects.select_related( - "issue", "actor" - ), + queryset=IssueReaction.objects.select_related("issue", "actor"), ) ) .prefetch_related( - Prefetch( - "votes", - queryset=IssueVote.objects.select_related("actor"), - ) + Prefetch("votes", queryset=IssueVote.objects.select_related("actor")) ) .annotate( vote_items=ArrayAgg( @@ -775,12 +712,8 @@ class IssueRetrievePublicEndpoint(BaseAPIView): When( votes__actor__avatar_asset__isnull=False, then=Concat( - Value( - "/api/assets/v2/static/" - ), - F( - "votes__actor__avatar_asset" - ), + Value("/api/assets/v2/static/"), + F("votes__actor__avatar_asset"), Value("/"), ), ), @@ -791,9 +724,7 @@ class IssueRetrievePublicEndpoint(BaseAPIView): default=Value(None), output_field=CharField(), ), - display_name=F( - "votes__actor__display_name" - ), + display_name=F("votes__actor__display_name"), ), ), ), @@ -815,23 +746,15 @@ class IssueRetrievePublicEndpoint(BaseAPIView): reaction=F("issue_reactions__reaction"), actor_details=JSONObject( id=F("issue_reactions__actor__id"), - first_name=F( - "issue_reactions__actor__first_name" - ), - last_name=F( - "issue_reactions__actor__last_name" - ), + first_name=F("issue_reactions__actor__first_name"), + last_name=F("issue_reactions__actor__last_name"), avatar=F("issue_reactions__actor__avatar"), avatar_url=Case( When( votes__actor__avatar_asset__isnull=False, then=Concat( - Value( - "/api/assets/v2/static/" - ), - F( - "votes__actor__avatar_asset" - ), + Value("/api/assets/v2/static/"), + F("votes__actor__avatar_asset"), Value("/"), ), ), diff --git a/apiserver/plane/space/views/label.py b/apiserver/plane/space/views/label.py index 2e0f99b7c..ad0c8f0ca 100644 --- a/apiserver/plane/space/views/label.py +++ b/apiserver/plane/space/views/label.py @@ -5,23 +5,17 @@ from rest_framework.permissions import AllowAny # Module imports from .base import BaseAPIView -from plane.db.models import ( - DeployBoard, - Label, -) +from plane.db.models import DeployBoard, Label class ProjectLabelsEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): deploy_board = DeployBoard.objects.filter(anchor=anchor).first() if not deploy_board: return Response( - {"error": "Invalid anchor"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Invalid anchor"}, status=status.HTTP_404_NOT_FOUND ) labels = Label.objects.filter( @@ -29,7 +23,4 @@ class ProjectLabelsEndpoint(BaseAPIView): project_id=deploy_board.project_id, ).values("id", "name", "color", "parent") - return Response( - labels, - status=status.HTTP_200_OK, - ) + return Response(labels, status=status.HTTP_200_OK) diff --git a/apiserver/plane/space/views/module.py b/apiserver/plane/space/views/module.py index f52f42331..7db676537 100644 --- a/apiserver/plane/space/views/module.py +++ b/apiserver/plane/space/views/module.py @@ -5,23 +5,17 @@ from rest_framework.response import Response # Module imports from .base import BaseAPIView -from plane.db.models import ( - DeployBoard, - Module, -) +from plane.db.models import DeployBoard, Module class ProjectModulesEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): deploy_board = DeployBoard.objects.filter(anchor=anchor).first() if not deploy_board: return Response( - {"error": "Invalid anchor"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Invalid anchor"}, status=status.HTTP_404_NOT_FOUND ) modules = Module.objects.filter( @@ -29,7 +23,4 @@ class ProjectModulesEndpoint(BaseAPIView): project_id=deploy_board.project_id, ).values("id", "name") - return Response( - modules, - status=status.HTTP_200_OK, - ) + return Response(modules, status=status.HTTP_200_OK) diff --git a/apiserver/plane/space/views/project.py b/apiserver/plane/space/views/project.py index f1606a798..1574871ef 100644 --- a/apiserver/plane/space/views/project.py +++ b/apiserver/plane/space/views/project.py @@ -1,8 +1,5 @@ # Django imports -from django.db.models import ( - Exists, - OuterRef, -) +from django.db.models import Exists, OuterRef # Third Party imports from rest_framework.response import Response @@ -16,9 +13,7 @@ from plane.db.models import Project, DeployBoard, ProjectMember class ProjectDeployBoardPublicSettingsEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): project_deploy_board = DeployBoard.objects.get( @@ -29,9 +24,7 @@ class ProjectDeployBoardPublicSettingsEndpoint(BaseAPIView): class WorkspaceProjectDeployBoardEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): deploy_board = DeployBoard.objects.filter( @@ -42,9 +35,7 @@ class WorkspaceProjectDeployBoardEndpoint(BaseAPIView): .annotate( is_public=Exists( DeployBoard.objects.filter( - anchor=anchor, - project_id=OuterRef("pk"), - entity_name="project", + anchor=anchor, project_id=OuterRef("pk"), entity_name="project" ) ) ) @@ -63,9 +54,7 @@ class WorkspaceProjectDeployBoardEndpoint(BaseAPIView): class WorkspaceProjectAnchorEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, slug, project_id): project_deploy_board = DeployBoard.objects.get( @@ -76,9 +65,7 @@ class WorkspaceProjectAnchorEndpoint(BaseAPIView): class ProjectMembersEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): deploy_board = DeployBoard.objects.filter(anchor=anchor).first() diff --git a/apiserver/plane/space/views/state.py b/apiserver/plane/space/views/state.py index 7ffcef5b9..39f2b1bfd 100644 --- a/apiserver/plane/space/views/state.py +++ b/apiserver/plane/space/views/state.py @@ -8,23 +8,17 @@ from rest_framework.response import Response # Module imports from .base import BaseAPIView -from plane.db.models import ( - DeployBoard, - State, -) +from plane.db.models import DeployBoard, State class ProjectStatesEndpoint(BaseAPIView): - permission_classes = [ - AllowAny, - ] + permission_classes = [AllowAny] def get(self, request, anchor): deploy_board = DeployBoard.objects.filter(anchor=anchor).first() if not deploy_board: return Response( - {"error": "Invalid anchor"}, - status=status.HTTP_404_NOT_FOUND, + {"error": "Invalid anchor"}, status=status.HTTP_404_NOT_FOUND ) states = State.objects.filter( @@ -33,7 +27,4 @@ class ProjectStatesEndpoint(BaseAPIView): project_id=deploy_board.project_id, ).values("name", "group", "color", "id", "sequence") - return Response( - states, - status=status.HTTP_200_OK, - ) + return Response(states, status=status.HTTP_200_OK) diff --git a/apiserver/plane/tests/api/base.py b/apiserver/plane/tests/api/base.py index f6843c1b6..e3209a281 100644 --- a/apiserver/plane/tests/api/base.py +++ b/apiserver/plane/tests/api/base.py @@ -8,9 +8,7 @@ from plane.app.views.authentication import get_tokens_for_user class BaseAPITest(APITestCase): def setUp(self): - self.client = APIClient( - HTTP_USER_AGENT="plane/test", REMOTE_ADDR="10.10.10.10" - ) + self.client = APIClient(HTTP_USER_AGENT="plane/test", REMOTE_ADDR="10.10.10.10") class AuthenticatedAPITest(BaseAPITest): diff --git a/apiserver/plane/tests/api/test_authentication.py b/apiserver/plane/tests/api/test_authentication.py index 36a0f7a24..5d7beabdf 100644 --- a/apiserver/plane/tests/api/test_authentication.py +++ b/apiserver/plane/tests/api/test_authentication.py @@ -28,9 +28,7 @@ class SignInEndpointTests(BaseAPITest): def test_email_validity(self): url = reverse("sign-in") response = self.client.post( - url, - {"email": "useremail.com", "password": "user@123"}, - format="json", + url, {"email": "useremail.com", "password": "user@123"}, format="json" ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( @@ -40,9 +38,7 @@ class SignInEndpointTests(BaseAPITest): def test_password_validity(self): url = reverse("sign-in") response = self.client.post( - url, - {"email": "user@plane.so", "password": "user123"}, - format="json", + url, {"email": "user@plane.so", "password": "user123"}, format="json" ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual( @@ -55,9 +51,7 @@ class SignInEndpointTests(BaseAPITest): def test_user_exists(self): url = reverse("sign-in") response = self.client.post( - url, - {"email": "user@email.so", "password": "user123"}, - format="json", + url, {"email": "user@email.so", "password": "user123"}, format="json" ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual( @@ -71,16 +65,11 @@ class SignInEndpointTests(BaseAPITest): url = reverse("sign-in") response = self.client.post( - url, - {"email": "user@plane.so", "password": "user@123"}, - format="json", + url, {"email": "user@plane.so", "password": "user@123"}, format="json" ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data.get("user").get("email"), - "user@plane.so", - ) + self.assertEqual(response.data.get("user").get("email"), "user@plane.so") class MagicLinkGenerateEndpointTests(BaseAPITest): @@ -97,9 +86,7 @@ class MagicLinkGenerateEndpointTests(BaseAPITest): def test_email_validity(self): url = reverse("magic-generate") - response = self.client.post( - url, {"email": "useremail.com"}, format="json" - ) + response = self.client.post(url, {"email": "useremail.com"}, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( response.data, {"error": "Please provide a valid email address."} @@ -111,9 +98,7 @@ class MagicLinkGenerateEndpointTests(BaseAPITest): ri = redis_instance() ri.delete("magic_user@plane.so") - response = self.client.post( - url, {"email": "user@plane.so"}, format="json" - ) + response = self.client.post(url, {"email": "user@plane.so"}, format="json") self.assertEqual(response.status_code, status.HTTP_200_OK) def test_max_generate_attempt(self): @@ -123,22 +108,13 @@ class MagicLinkGenerateEndpointTests(BaseAPITest): ri.delete("magic_user@plane.so") for _ in range(4): - response = self.client.post( - url, - {"email": "user@plane.so"}, - format="json", - ) + response = self.client.post(url, {"email": "user@plane.so"}, format="json") - response = self.client.post( - url, - {"email": "user@plane.so"}, - format="json", - ) + response = self.client.post(url, {"email": "user@plane.so"}, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data, - {"error": "Max attempts exhausted. Please try again later."}, + response.data, {"error": "Max attempts exhausted. Please try again later."} ) @@ -153,9 +129,7 @@ class MagicSignInEndpointTests(BaseAPITest): url = reverse("magic-sign-in") response = self.client.post(url, {}, format="json") self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) - self.assertEqual( - response.data, {"error": "User token and key are required"} - ) + self.assertEqual(response.data, {"error": "User token and key are required"}) def test_expired_invalid_magic_link(self): ri = redis_instance() @@ -169,8 +143,7 @@ class MagicSignInEndpointTests(BaseAPITest): ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data, - {"error": "The magic code/link has expired please try again"}, + response.data, {"error": "The magic code/link has expired please try again"} ) def test_invalid_magic_code(self): @@ -188,8 +161,7 @@ class MagicSignInEndpointTests(BaseAPITest): ) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual( - response.data, - {"error": "Your login code was incorrect. Please try again."}, + response.data, {"error": "Your login code was incorrect. Please try again."} ) def test_magic_code_sign_in(self): @@ -205,12 +177,7 @@ class MagicSignInEndpointTests(BaseAPITest): url = reverse("magic-sign-in") response = self.client.post( - url, - {"key": "magic_user@plane.so", "token": token}, - format="json", + url, {"key": "magic_user@plane.so", "token": token}, format="json" ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data.get("user").get("email"), - "user@plane.so", - ) + self.assertEqual(response.data.get("user").get("email"), "user@plane.so") diff --git a/apiserver/plane/urls.py b/apiserver/plane/urls.py index aac6459b3..e3870a393 100644 --- a/apiserver/plane/urls.py +++ b/apiserver/plane/urls.py @@ -1,6 +1,4 @@ -"""plane URL Configuration - -""" +"""plane URL Configuration""" from django.conf import settings from django.urls import include, path, re_path @@ -24,7 +22,7 @@ if settings.DEBUG: import debug_toolbar urlpatterns = [ - re_path(r"^__debug__/", include(debug_toolbar.urls)), + re_path(r"^__debug__/", include(debug_toolbar.urls)) ] + urlpatterns except ImportError: pass diff --git a/apiserver/plane/utils/analytics_plot.py b/apiserver/plane/utils/analytics_plot.py index 2bf2210ec..7527a3524 100644 --- a/apiserver/plane/utils/analytics_plot.py +++ b/apiserver/plane/utils/analytics_plot.py @@ -4,16 +4,7 @@ from itertools import groupby # Django import from django.db import models -from django.db.models import ( - Case, - CharField, - Count, - F, - Sum, - Value, - When, - FloatField, -) +from django.db.models import Case, CharField, Count, F, Sum, Value, When, FloatField from django.db.models.functions import ( Coalesce, Concat, @@ -41,9 +32,7 @@ def annotate_with_monthly_dimension(queryset, field_name, attribute): def extract_axis(queryset, x_axis): # Format the dimension when the axis is in date if x_axis in ["created_at", "start_date", "target_date", "completed_at"]: - queryset = annotate_with_monthly_dimension( - queryset, x_axis, "dimension" - ) + queryset = annotate_with_monthly_dimension(queryset, x_axis, "dimension") return queryset, "dimension" else: return queryset.annotate(dimension=F(x_axis)), "dimension" @@ -68,9 +57,7 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): # if segment in ["created_at", "start_date", "target_date", "completed_at"]: - queryset = annotate_with_monthly_dimension( - queryset, segment, "segmented" - ) + queryset = annotate_with_monthly_dimension(queryset, segment, "segmented") segment = "segmented" queryset = queryset.values(x_axis) @@ -85,9 +72,7 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): ), dimension_ex=Coalesce("dimension", Value("null")), ).values("dimension") - queryset = ( - queryset.annotate(segment=F(segment)) if segment else queryset - ) + queryset = queryset.annotate(segment=F(segment)) if segment else queryset queryset = ( queryset.values("dimension", "segment") if segment @@ -100,9 +85,7 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): queryset = queryset.annotate( estimate=Sum(Cast("estimate_point__value", FloatField())) ).order_by(x_axis) - queryset = ( - queryset.annotate(segment=F(segment)) if segment else queryset - ) + queryset = queryset.annotate(segment=F(segment)) if segment else queryset queryset = ( queryset.values("dimension", "segment", "estimate") if segment @@ -112,22 +95,13 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): result_values = list(queryset) grouped_data = { str(key): list(items) - for key, items in groupby( - result_values, key=lambda x: x[str("dimension")] - ) + for key, items in groupby(result_values, key=lambda x: x[str("dimension")]) } return sort_data(grouped_data, temp_axis) -def burndown_plot( - queryset, - slug, - project_id, - plot_type, - cycle_id=None, - module_id=None, -): +def burndown_plot(queryset, slug, project_id, plot_type, cycle_id=None, module_id=None): # Total Issues in Cycle or Module total_issues = queryset.total_issues # check whether the estimate is a point or not @@ -166,9 +140,7 @@ def burndown_plot( # Get all dates between the two dates date_range = [ (queryset.start_date + timedelta(days=x)).date() - for x in range( - (queryset.end_date - queryset.start_date).days + 1 - ) + for x in range((queryset.end_date - queryset.start_date).days + 1) ] else: date_range = [] @@ -208,9 +180,7 @@ def burndown_plot( # Get all dates between the two dates date_range = [ (queryset.start_date + timedelta(days=x)) - for x in range( - (queryset.target_date - queryset.start_date).days + 1 - ) + for x in range((queryset.target_date - queryset.start_date).days + 1) ] chart_data = {str(date): 0 for date in date_range} diff --git a/apiserver/plane/utils/cache.py b/apiserver/plane/utils/cache.py index bda942899..1b3e2cb1c 100644 --- a/apiserver/plane/utils/cache.py +++ b/apiserver/plane/utils/cache.py @@ -28,16 +28,16 @@ def cache_response(timeout=60 * 60, path=None, user=True): auth_header = ( None if request.user.is_anonymous - else str(request.user.id) if user else None + else str(request.user.id) + if user + else None ) custom_path = path if path is not None else request.get_full_path() key = generate_cache_key(custom_path, auth_header) cached_result = cache.get(key) if cached_result is not None: - return Response( - cached_result["data"], status=cached_result["status"] - ) + return Response(cached_result["data"], status=cached_result["status"]) response = view_func(instance, request, *args, **kwargs) if response.status_code == 200 and not settings.DEBUG: cache.set( @@ -67,7 +67,9 @@ def invalidate_cache_directly( auth_header = ( None if request and request.user.is_anonymous - else str(request.user.id) if user else None + else str(request.user.id) + if user + else None ) key = generate_cache_key(custom_path, auth_header) diff --git a/apiserver/plane/utils/global_paginator.py b/apiserver/plane/utils/global_paginator.py index e9ed735d5..338d86117 100644 --- a/apiserver/plane/utils/global_paginator.py +++ b/apiserver/plane/utils/global_paginator.py @@ -20,9 +20,7 @@ class PaginateCursor: try: bits = value.split(":") if len(bits) != 3: - raise ValueError( - "Cursor must be in the format 'value:offset:is_prev'" - ) + raise ValueError("Cursor must be in the format 'value:offset:is_prev'") return self(int(bits[0]), int(bits[1]), int(bits[2])) except (TypeError, ValueError) as e: raise ValueError(f"Invalid cursor format: {e}") diff --git a/apiserver/plane/utils/grouper.py b/apiserver/plane/utils/grouper.py index 38ac74a16..e139cdcc5 100644 --- a/apiserver/plane/utils/grouper.py +++ b/apiserver/plane/utils/grouper.py @@ -27,13 +27,11 @@ def issue_queryset_grouper(queryset, group_by, sub_group_by): annotations_map = { "assignee_ids": ( "assignees__id", - ~Q(assignees__id__isnull=True) - & Q(issue_assignee__deleted_at__isnull=True), + ~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), + ~Q(labels__id__isnull=True) & Q(label_issue__deleted_at__isnull=True), ), "module_ids": ( "issue_module__module_id", @@ -46,16 +44,11 @@ def issue_queryset_grouper(queryset, group_by, sub_group_by): } default_annotations = { key: Coalesce( - ArrayAgg( - field, - distinct=True, - filter=condition, - ), + ArrayAgg(field, distinct=True, filter=condition), Value([], output_field=ArrayField(UUIDField())), ) for key, (field, condition) in annotations_map.items() - if FIELD_MAPPER.get(key) != group_by - or FIELD_MAPPER.get(key) != sub_group_by + if FIELD_MAPPER.get(key) != group_by or FIELD_MAPPER.get(key) != sub_group_by } return queryset.annotate(**default_annotations) @@ -111,8 +104,7 @@ def issue_on_results(issues, group_by, sub_group_by): def issue_group_values(field, slug, project_id=None, filters=dict): if field == "state_id": queryset = State.objects.filter( - is_triage=False, - workspace__slug=slug, + is_triage=False, workspace__slug=slug ).values_list("id", flat=True) if project_id: return list(queryset.filter(project_id=project_id)) @@ -129,9 +121,7 @@ def issue_group_values(field, slug, project_id=None, filters=dict): if field == "assignees__id": if project_id: return ProjectMember.objects.filter( - workspace__slug=slug, - project_id=project_id, - is_active=True, + workspace__slug=slug, project_id=project_id, is_active=True ).values_list("member_id", flat=True) else: return list( @@ -140,17 +130,17 @@ def issue_group_values(field, slug, project_id=None, filters=dict): ).values_list("member_id", flat=True) ) if field == "issue_module__module_id": - queryset = Module.objects.filter( - workspace__slug=slug, - ).values_list("id", flat=True) + queryset = Module.objects.filter(workspace__slug=slug).values_list( + "id", flat=True + ) if project_id: return list(queryset.filter(project_id=project_id)) + ["None"] else: return list(queryset) + ["None"] if field == "cycle_id": - queryset = Cycle.objects.filter( - workspace__slug=slug, - ).values_list("id", flat=True) + queryset = Cycle.objects.filter(workspace__slug=slug).values_list( + "id", flat=True + ) if project_id: return list(queryset.filter(project_id=project_id)) + ["None"] else: @@ -161,21 +151,9 @@ def issue_group_values(field, slug, project_id=None, filters=dict): ) return list(queryset) if field == "priority": - return [ - "low", - "medium", - "high", - "urgent", - "none", - ] + return ["low", "medium", "high", "urgent", "none"] if field == "state__group": - return [ - "backlog", - "unstarted", - "started", - "completed", - "cancelled", - ] + return ["backlog", "unstarted", "started", "completed", "cancelled"] if field == "target_date": queryset = ( Issue.issue_objects.filter(workspace__slug=slug) diff --git a/apiserver/plane/utils/imports.py b/apiserver/plane/utils/imports.py index 89753ef1d..81de0203b 100644 --- a/apiserver/plane/utils/imports.py +++ b/apiserver/plane/utils/imports.py @@ -7,9 +7,7 @@ def import_submodules(context, root_module, path): Import all submodules and register them in the ``context`` namespace. >>> import_submodules(locals(), __name__, __path__) """ - for loader, module_name, is_pkg in pkgutil.walk_packages( - path, root_module + "." - ): + for loader, module_name, is_pkg in pkgutil.walk_packages(path, root_module + "."): # this causes a Runtime error with model conflicts # module = loader.find_module(module_name).load_module(module_name) module = __import__(module_name, globals(), locals(), ["__name__"]) diff --git a/apiserver/plane/utils/issue_filters.py b/apiserver/plane/utils/issue_filters.py index 5b1e70e44..01b2024e4 100644 --- a/apiserver/plane/utils/issue_filters.py +++ b/apiserver/plane/utils/issue_filters.py @@ -22,9 +22,7 @@ def filter_valid_uuids(uuid_list): # Get the 2_weeks, 3_months -def string_date_filter( - issue_filter, duration, subsequent, term, date_filter, offset -): +def string_date_filter(issue_filter, duration, subsequent, term, date_filter, offset): now = timezone.now().date() if term == "months": if subsequent == "after": @@ -48,22 +46,14 @@ 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): @@ -97,9 +87,7 @@ def date_filter(issue_filter, date_term, queries): def filter_state(params, issue_filter, method, prefix=""): if method == "GET": - states = [ - item for item in params.get("state").split(",") if item != "null" - ] + states = [item for item in params.get("state").split(",") if item != "null"] states = filter_valid_uuids(states) if len(states) and "" not in states: issue_filter[f"{prefix}state__in"] = states @@ -116,9 +104,7 @@ def filter_state(params, issue_filter, method, prefix=""): def filter_state_group(params, issue_filter, method, prefix=""): if method == "GET": state_group = [ - item - for item in params.get("state_group").split(",") - if item != "null" + item for item in params.get("state_group").split(",") if item != "null" ] if len(state_group) and "" not in state_group: issue_filter[f"{prefix}state__group__in"] = state_group @@ -128,18 +114,14 @@ 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 def filter_estimate_point(params, issue_filter, method, prefix=""): if method == "GET": estimate_points = [ - item - for item in params.get("estimate_point").split(",") - if item != "null" + item for item in params.get("estimate_point").split(",") if item != "null" ] if len(estimate_points) and "" not in estimate_points: issue_filter[f"{prefix}estimate_point__in"] = estimate_points @@ -149,18 +131,14 @@ def filter_estimate_point(params, issue_filter, method, prefix=""): and len(params.get("estimate_point")) and params.get("estimate_point") != "null" ): - issue_filter[f"{prefix}estimate_point__in"] = params.get( - "estimate_point" - ) + issue_filter[f"{prefix}estimate_point__in"] = params.get("estimate_point") return issue_filter def filter_priority(params, issue_filter, method, prefix=""): if method == "GET": priorities = [ - item - for item in params.get("priority").split(",") - if item != "null" + item for item in params.get("priority").split(",") if item != "null" ] if len(priorities) and "" not in priorities: issue_filter[f"{prefix}priority__in"] = priorities @@ -176,9 +154,7 @@ def filter_priority(params, issue_filter, method, prefix=""): def filter_parent(params, issue_filter, method, prefix=""): if method == "GET": - parents = [ - item for item in params.get("parent").split(",") if item != "null" - ] + parents = [item for item in params.get("parent").split(",") if item != "null"] if "None" in parents: issue_filter[f"{prefix}parent__isnull"] = True parents = filter_valid_uuids(parents) @@ -196,9 +172,7 @@ def filter_parent(params, issue_filter, method, prefix=""): def filter_labels(params, issue_filter, method, prefix=""): if method == "GET": - labels = [ - item for item in params.get("labels").split(",") if item != "null" - ] + labels = [item for item in params.get("labels").split(",") if item != "null"] if "None" in labels: issue_filter[f"{prefix}labels__isnull"] = True labels = filter_valid_uuids(labels) @@ -217,9 +191,7 @@ def filter_labels(params, issue_filter, method, prefix=""): def filter_assignees(params, issue_filter, method, prefix=""): if method == "GET": assignees = [ - item - for item in params.get("assignees").split(",") - if item != "null" + item for item in params.get("assignees").split(",") if item != "null" ] if "None" in assignees: issue_filter[f"{prefix}assignees__isnull"] = True @@ -240,9 +212,7 @@ def filter_assignees(params, issue_filter, method, prefix=""): def filter_mentions(params, issue_filter, method, prefix=""): if method == "GET": mentions = [ - item - for item in params.get("mentions").split(",") - if item != "null" + item for item in params.get("mentions").split(",") if item != "null" ] mentions = filter_valid_uuids(mentions) if len(mentions) and "" not in mentions: @@ -253,8 +223,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 @@ -262,9 +232,7 @@ def filter_mentions(params, issue_filter, method, prefix=""): def filter_created_by(params, issue_filter, method, prefix=""): if method == "GET": created_bys = [ - item - for item in params.get("created_by").split(",") - if item != "null" + item for item in params.get("created_by").split(",") if item != "null" ] if "None" in created_bys: issue_filter[f"{prefix}created_by__isnull"] = True @@ -365,9 +333,7 @@ def filter_completed_at(params, issue_filter, method, prefix=""): queries=completed_ats, ) else: - if params.get("completed_at", None) and len( - params.get("completed_at") - ): + if params.get("completed_at", None) and len(params.get("completed_at")): date_filter( issue_filter=issue_filter, date_term=f"{prefix}completed_at__date", @@ -390,9 +356,7 @@ def filter_issue_state_type(params, issue_filter, method, prefix=""): def filter_project(params, issue_filter, method, prefix=""): if method == "GET": - projects = [ - item for item in params.get("project").split(",") if item != "null" - ] + projects = [item for item in params.get("project").split(",") if item != "null"] projects = filter_valid_uuids(projects) if len(projects) and "" not in projects: issue_filter[f"{prefix}project__in"] = projects @@ -408,9 +372,7 @@ def filter_project(params, issue_filter, method, prefix=""): def filter_cycle(params, issue_filter, method, prefix=""): if method == "GET": - cycles = [ - item for item in params.get("cycle").split(",") if item != "null" - ] + cycles = [item for item in params.get("cycle").split(",") if item != "null"] if "None" in cycles: issue_filter[f"{prefix}issue_cycle__cycle_id__isnull"] = True cycles = filter_valid_uuids(cycles) @@ -422,18 +384,14 @@ 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 def filter_module(params, issue_filter, method, prefix=""): if method == "GET": - modules = [ - item for item in params.get("module").split(",") if item != "null" - ] + modules = [item for item in params.get("module").split(",") if item != "null"] if "None" in modules: issue_filter[f"{prefix}issue_module__module_id__isnull"] = True modules = filter_valid_uuids(modules) @@ -445,9 +403,7 @@ def filter_module(params, issue_filter, method, prefix=""): and len(params.get("module")) and params.get("module") != "null" ): - issue_filter[f"{prefix}issue_module__module_id__in"] = params.get( - "module" - ) + 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 @@ -455,9 +411,7 @@ def filter_module(params, issue_filter, method, prefix=""): def filter_intake_status(params, issue_filter, method, prefix=""): if method == "GET": status = [ - item - for item in params.get("intake_status").split(",") - if item != "null" + item for item in params.get("intake_status").split(",") if item != "null" ] if len(status) and "" not in status: issue_filter[f"{prefix}issue_intake__status__in"] = status @@ -476,9 +430,7 @@ def filter_intake_status(params, issue_filter, method, prefix=""): def filter_inbox_status(params, issue_filter, method, prefix=""): if method == "GET": status = [ - item - for item in params.get("inbox_status").split(",") - if item != "null" + item for item in params.get("inbox_status").split(",") if item != "null" ] if len(status) and "" not in status: issue_filter[f"{prefix}issue_intake__status__in"] = status @@ -509,23 +461,19 @@ def filter_sub_issue_toggle(params, issue_filter, method, prefix=""): def filter_subscribed_issues(params, issue_filter, method, prefix=""): if method == "GET": subscribers = [ - item - for item in params.get("subscriber").split(",") - if item != "null" + item for item in params.get("subscriber").split(",") if item != "null" ] subscribers = filter_valid_uuids(subscribers) if len(subscribers) and "" not in subscribers: - issue_filter[f"{prefix}issue_subscribers__subscriber_id__in"] = ( - subscribers - ) + issue_filter[f"{prefix}issue_subscribers__subscriber_id__in"] = subscribers else: if ( params.get("subscriber", None) and len(params.get("subscriber")) and params.get("subscriber") != "null" ): - issue_filter[f"{prefix}issue_subscribers__subscriber_id__in"] = ( - params.get("subscriber") + issue_filter[f"{prefix}issue_subscribers__subscriber_id__in"] = params.get( + "subscriber" ) return issue_filter @@ -541,9 +489,7 @@ def filter_start_target_date_issues(params, issue_filter, method, prefix=""): def filter_logged_by(params, issue_filter, method, prefix=""): if method == "GET": logged_bys = [ - item - for item in params.get("logged_by").split(",") - if item != "null" + item for item in params.get("logged_by").split(",") if item != "null" ] if "None" in logged_bys: issue_filter[f"{prefix}logged_by__isnull"] = True diff --git a/apiserver/plane/utils/issue_search.py b/apiserver/plane/utils/issue_search.py index 74d1e8019..1e7543d88 100644 --- a/apiserver/plane/utils/issue_search.py +++ b/apiserver/plane/utils/issue_search.py @@ -17,6 +17,4 @@ def search_issues(query, queryset): q |= Q(**{"sequence_id": sequence_id}) else: q |= Q(**{f"{field}__icontains": query}) - return queryset.filter( - q, - ).distinct() + return queryset.filter(q).distinct() diff --git a/apiserver/plane/utils/order_queryset.py b/apiserver/plane/utils/order_queryset.py index 920e5b1e0..174637b74 100644 --- a/apiserver/plane/utils/order_queryset.py +++ b/apiserver/plane/utils/order_queryset.py @@ -1,20 +1,8 @@ -from django.db.models import ( - Case, - CharField, - Min, - Value, - When, -) +from django.db.models import Case, CharField, Min, Value, When # Custom ordering for priority and state PRIORITY_ORDER = ["urgent", "high", "medium", "low", "none"] -STATE_ORDER = [ - "backlog", - "unstarted", - "started", - "completed", - "cancelled", -] +STATE_ORDER = ["backlog", "unstarted", "started", "completed", "cancelled"] def order_issue_queryset(issue_queryset, order_by_param="-created_at"): @@ -30,15 +18,10 @@ def order_issue_queryset(issue_queryset, order_by_param="-created_at"): ) ).order_by("priority_order") order_by_param = ( - "priority_order" - if order_by_param.startswith("-") - else "-priority_order" + "priority_order" if order_by_param.startswith("-") else "-priority_order" ) # State Ordering - elif order_by_param in [ - "state__group", - "-state__group", - ]: + elif order_by_param in ["state__group", "-state__group"]: state_order = ( STATE_ORDER if order_by_param in ["state__name", "state__group"] @@ -72,9 +55,7 @@ def order_issue_queryset(issue_queryset, order_by_param="-created_at"): if order_by_param.startswith("-") else order_by_param ) - ).order_by( - "-min_values" if order_by_param.startswith("-") else "min_values" - ) + ).order_by("-min_values" if order_by_param.startswith("-") else "min_values") order_by_param = ( "-min_values" if order_by_param.startswith("-") else "min_values" ) diff --git a/apiserver/plane/utils/paginator.py b/apiserver/plane/utils/paginator.py index c6712cacb..6bec093e7 100644 --- a/apiserver/plane/utils/paginator.py +++ b/apiserver/plane/utils/paginator.py @@ -47,9 +47,7 @@ class Cursor: try: bits = value.split(":") if len(bits) != 3: - raise ValueError( - "Cursor must be in the format 'value:offset:is_prev'" - ) + raise ValueError("Cursor must be in the format 'value:offset:is_prev'") value = float(bits[0]) if "." in bits[0] else int(bits[0]) return cls(value, int(bits[1]), bool(int(bits[2]))) @@ -185,7 +183,6 @@ class OffsetPaginator: class GroupedOffsetPaginator(OffsetPaginator): - # Field mappers - list m2m fields here FIELD_MAPPER = { "labels__id": "label_ids", @@ -248,18 +245,14 @@ class GroupedOffsetPaginator(OffsetPaginator): nulls_last=True ) # order by desc if desc is set if self.desc - else F(*self.key).asc( - nulls_last=True - ) # Order by asc if set + else F(*self.key).asc(nulls_last=True) # Order by asc if set ), F("created_at").desc(), ), ) ) # Filter the results by row number - results = queryset.filter( - row_number__gt=offset, row_number__lt=stop - ).order_by( + results = queryset.filter(row_number__gt=offset, row_number__lt=stop).order_by( ( F(*self.key).desc(nulls_last=True) if self.desc @@ -270,19 +263,11 @@ class GroupedOffsetPaginator(OffsetPaginator): # Adjust cursors based on the grouped results for pagination next_cursor = Cursor( - limit, - page + 1, - False, - queryset.filter(row_number__gte=stop).exists(), + limit, page + 1, False, queryset.filter(row_number__gte=stop).exists() ) # Add previous cursors - prev_cursor = Cursor( - limit, - page - 1, - True, - page > 0, - ) + prev_cursor = Cursor(limit, page - 1, True, page > 0) # Count the queryset count = queryset.count() @@ -292,13 +277,7 @@ class GroupedOffsetPaginator(OffsetPaginator): if results: max_hits = math.ceil( queryset.values(self.group_by_field_name) - .annotate( - count=Count( - "id", - filter=self.count_filter, - distinct=True, - ) - ) + .annotate(count=Count("id", filter=self.count_filter, distinct=True)) .order_by("-count")[0]["count"] / limit ) @@ -316,13 +295,7 @@ class GroupedOffsetPaginator(OffsetPaginator): # Get total items for each group return ( self.queryset.values(self.group_by_field_name) - .annotate( - count=Count( - "id", - filter=self.count_filter, - distinct=True, - ) - ) + .annotate(count=Count("id", filter=self.count_filter, distinct=True)) .order_by() ) @@ -331,9 +304,7 @@ class GroupedOffsetPaginator(OffsetPaginator): total_group_dict = {} for group in self.__get_total_queryset(): total_group_dict[str(group.get(self.group_by_field_name))] = ( - total_group_dict.get( - str(group.get(self.group_by_field_name)), 0 - ) + total_group_dict.get(str(group.get(self.group_by_field_name)), 0) + (1 if group.get("count") == 0 else group.get("count")) ) return total_group_dict @@ -499,9 +470,7 @@ class SubGroupedOffsetPaginator(OffsetPaginator): ) # Filter the results - results = queryset.filter( - row_number__gt=offset, row_number__lt=stop - ).order_by( + results = queryset.filter(row_number__gt=offset, row_number__lt=stop).order_by( ( F(*self.key).desc(nulls_last=True) if self.desc @@ -512,19 +481,11 @@ class SubGroupedOffsetPaginator(OffsetPaginator): # Adjust cursors based on the grouped results for pagination next_cursor = Cursor( - limit, - page + 1, - False, - queryset.filter(row_number__gte=stop).exists(), + limit, page + 1, False, queryset.filter(row_number__gte=stop).exists() ) # Add previous cursors - prev_cursor = Cursor( - limit, - page - 1, - True, - page > 0, - ) + prev_cursor = Cursor(limit, page - 1, True, page > 0) # Count the queryset count = queryset.count() @@ -534,13 +495,7 @@ class SubGroupedOffsetPaginator(OffsetPaginator): if results: max_hits = math.ceil( queryset.values(self.group_by_field_name) - .annotate( - count=Count( - "id", - filter=self.count_filter, - distinct=True, - ) - ) + .annotate(count=Count("id", filter=self.count_filter, distinct=True)) .order_by("-count")[0]["count"] / limit ) @@ -559,29 +514,17 @@ class SubGroupedOffsetPaginator(OffsetPaginator): return ( self.queryset.order_by(self.group_by_field_name) .values(self.group_by_field_name) - .annotate( - count=Count( - "id", - filter=self.count_filter, - distinct=True, - ) - ) + .annotate(count=Count("id", filter=self.count_filter, distinct=True)) .distinct() ) def __get_subgroup_total_queryset(self): # Get subgroup totals return ( - self.queryset.values( - self.group_by_field_name, self.sub_group_by_field_name - ) - .annotate( - count=Count("id", filter=self.count_filter, distinct=True) - ) + self.queryset.values(self.group_by_field_name, self.sub_group_by_field_name) + .annotate(count=Count("id", filter=self.count_filter, distinct=True)) .order_by() - .values( - self.group_by_field_name, self.sub_group_by_field_name, "count" - ) + .values(self.group_by_field_name, self.sub_group_by_field_name, "count") ) def __get_total_dict(self): @@ -590,9 +533,7 @@ class SubGroupedOffsetPaginator(OffsetPaginator): total_sub_group_dict = {} for group in self.__get_group_total_queryset(): total_group_dict[str(group.get(self.group_by_field_name))] = ( - total_group_dict.get( - str(group.get(self.group_by_field_name)), 0 - ) + total_group_dict.get(str(group.get(self.group_by_field_name)), 0) + (1 if group.get("count") == 0 else group.get("count")) ) @@ -625,9 +566,9 @@ class SubGroupedOffsetPaginator(OffsetPaginator): "results": { str(sub_group): { "results": [], - "total_results": total_sub_group_dict.get( - str(group) - ).get(str(sub_group), 0), + "total_results": total_sub_group_dict.get(str(group)).get( + str(sub_group), 0 + ), } for sub_group in total_sub_group_dict.get(str(group), []) }, @@ -667,8 +608,7 @@ class SubGroupedOffsetPaginator(OffsetPaginator): if ( group_value in processed_results - and sub_group_value - in processed_results[str(group_value)]["results"] + and sub_group_value in processed_results[str(group_value)]["results"] ): if self.group_by_field_name in self.FIELD_MAPPER: # for multi grouper @@ -677,17 +617,15 @@ class SubGroupedOffsetPaginator(OffsetPaginator): [] if "None" in group_ids else group_ids ) if self.sub_group_by_field_name in self.FIELD_MAPPER: - sub_group_ids = list( - result_sub_group_mapping[str(result_id)] - ) + sub_group_ids = list(result_sub_group_mapping[str(result_id)]) # for multi groups - result[ - self.FIELD_MAPPER.get(self.sub_group_by_field_name) - ] = ([] if "None" in sub_group_ids else sub_group_ids) + result[self.FIELD_MAPPER.get(self.sub_group_by_field_name)] = ( + [] if "None" in sub_group_ids else sub_group_ids + ) # If a result belongs to multiple groups, add it to each group - processed_results[str(group_value)]["results"][ - str(sub_group_value) - ]["results"].append(result) + processed_results[str(group_value)]["results"][str(sub_group_value)][ + "results" + ].append(result) return processed_results @@ -764,7 +702,7 @@ class BasePaginator: input_cursor = None try: input_cursor = cursor_cls.from_string( - request.GET.get(self.cursor_name, f"{per_page}:0:0"), + request.GET.get(self.cursor_name, f"{per_page}:0:0") ) except ValueError: raise ParseError(detail="Invalid cursor parameter.") @@ -779,16 +717,12 @@ class BasePaginator: paginator_kwargs["sub_group_by_field_name"] = ( sub_group_by_field_name ) - paginator_kwargs["sub_group_by_fields"] = ( - sub_group_by_fields - ) + paginator_kwargs["sub_group_by_fields"] = sub_group_by_fields paginator = paginator_cls(**paginator_kwargs) try: - cursor_result = paginator.get_result( - limit=per_page, cursor=input_cursor - ) + cursor_result = paginator.get_result(limit=per_page, cursor=input_cursor) except BadPaginationError: raise ParseError(detail="Error in parsing") diff --git a/apiserver/plane/utils/user_timezone_converter.py b/apiserver/plane/utils/user_timezone_converter.py index c946cfb27..550abfe99 100644 --- a/apiserver/plane/utils/user_timezone_converter.py +++ b/apiserver/plane/utils/user_timezone_converter.py @@ -1,5 +1,6 @@ import pytz + def user_timezone_converter(queryset, datetime_fields, user_timezone): # Create a timezone object for the user's timezone user_tz = pytz.timezone(user_timezone) diff --git a/apiserver/plane/web/urls.py b/apiserver/plane/web/urls.py index 24a3e7b57..512d4a258 100644 --- a/apiserver/plane/web/urls.py +++ b/apiserver/plane/web/urls.py @@ -1,6 +1,4 @@ from django.urls import path from django.views.generic import TemplateView -urlpatterns = [ - path("about/", TemplateView.as_view(template_name="about.html")) -] +urlpatterns = [path("about/", TemplateView.as_view(template_name="about.html"))] diff --git a/apiserver/pyproject.toml b/apiserver/pyproject.toml index a6c07b855..15df2d996 100644 --- a/apiserver/pyproject.toml +++ b/apiserver/pyproject.toml @@ -1,25 +1,97 @@ -[tool.black] -line-length = 79 -target-version = ['py36'] -include = '\.pyi?$' -exclude = ''' - /( - \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - | venv - )/ -''' +[project] +name = "your-project-name" +version = "0.1.0" +description = "Your project description" +requires-python = ">=3.8" [tool.ruff] -line-length = 79 +# Exclude a variety of commonly ignored directories. exclude = [ - "**/__init__.py", + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".mypy_cache", + ".nox", + ".pants.d", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "venv", + "**/migrations/*", ] +# Same as Black. +line-length = 88 +indent-width = 4 + +[tool.ruff.format] +# Use double quotes for strings. +quote-style = "double" + +# Indent with spaces, rather than tabs. +indent-style = "space" + +# Respect magic trailing commas. +skip-magic-trailing-comma = true + +# Automatically detect the appropriate line ending. +line-ending = "auto" + +[tool.ruff.lint] +# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default. +select = ["E", "F"] +ignore = [] + +# Allow autofix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + +# Allow unused variables when underscore-prefixed. +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" + +[tool.ruff.lint.pep8-naming] +# Allow lowercase variables like "id" +classmethod-decorators = ["classmethod", "validator", "root_validator"] + +[tool.ruff.lint.per-file-ignores] +# Ignore specific rules for tests +"tests/*" = ["E402", "F401", "F811"] +# Ignore imported but unused in __init__.py files +"__init__.py" = ["F401"] + +[tool.ruff.lint.mccabe] +# Unlike Flake8, default to a complexity level of 10. +max-complexity = 10 + +[tool.ruff.lint.isort] +combine-as-imports = true +detect-same-package = true +force-wrap-aliases = true +known-first-party = ["plane"] +known-third-party = ["rest_framework"] +relative-imports-order = "closest-to-furthest" + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "parents" + +[tool.ruff.lint.pycodestyle] +ignore-overlong-task-comments = true +max-doc-length = 88 + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.pylint] +max-args = 8 +max-statements = 50