mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
dev: update attachments for issues
This commit is contained in:
parent
cedc08bc08
commit
02e5e0da4b
@ -5,9 +5,7 @@ from .issue import (
|
||||
IssueSerializer,
|
||||
LabelSerializer,
|
||||
IssueLinkSerializer,
|
||||
IssueAttachmentSerializer,
|
||||
IssueCommentSerializer,
|
||||
IssueAttachmentSerializer,
|
||||
IssueActivitySerializer,
|
||||
IssueExpandSerializer,
|
||||
)
|
||||
|
@ -17,7 +17,6 @@ from plane.db.models import (
|
||||
IssueLabel,
|
||||
IssueLink,
|
||||
IssueComment,
|
||||
IssueAttachment,
|
||||
IssueActivity,
|
||||
ProjectMember,
|
||||
)
|
||||
@ -296,22 +295,6 @@ class IssueLinkSerializer(BaseSerializer):
|
||||
return IssueLink.objects.create(**validated_data)
|
||||
|
||||
|
||||
class IssueAttachmentSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = IssueAttachment
|
||||
fields = "__all__"
|
||||
read_only_fields = [
|
||||
"id",
|
||||
"workspace",
|
||||
"project",
|
||||
"issue",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
|
||||
class IssueCommentSerializer(BaseSerializer):
|
||||
is_member = serializers.BooleanField(read_only=True)
|
||||
|
||||
|
@ -17,7 +17,7 @@ from plane.db.models import (
|
||||
Issue,
|
||||
CycleIssue,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
)
|
||||
from plane.app.permissions import ProjectEntityPermission
|
||||
from plane.api.serializers import (
|
||||
@ -390,8 +390,9 @@ class CycleIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
@ -31,13 +31,13 @@ from plane.app.permissions import (
|
||||
)
|
||||
from plane.db.models import (
|
||||
Issue,
|
||||
IssueAttachment,
|
||||
IssueLink,
|
||||
Project,
|
||||
Label,
|
||||
ProjectMember,
|
||||
IssueComment,
|
||||
IssueActivity,
|
||||
FileAsset,
|
||||
)
|
||||
from plane.bgtasks.issue_activites_task import issue_activity
|
||||
from plane.api.serializers import (
|
||||
@ -126,8 +126,9 @@ class IssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
@ -19,8 +19,8 @@ from plane.db.models import (
|
||||
ModuleLink,
|
||||
Issue,
|
||||
ModuleIssue,
|
||||
IssueAttachment,
|
||||
IssueLink,
|
||||
FileAsset,
|
||||
)
|
||||
from plane.api.serializers import (
|
||||
ModuleSerializer,
|
||||
@ -273,8 +273,9 @@ class ModuleIssueAPIEndpoint(WebhookMixin, BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
@ -60,7 +60,6 @@ from .issue import (
|
||||
IssueStateSerializer,
|
||||
IssueLinkSerializer,
|
||||
IssueLiteSerializer,
|
||||
IssueAttachmentSerializer,
|
||||
IssueSubscriberSerializer,
|
||||
IssueReactionSerializer,
|
||||
CommentReactionSerializer,
|
||||
|
@ -25,7 +25,7 @@ from plane.db.models import (
|
||||
Module,
|
||||
ModuleIssue,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
IssueReaction,
|
||||
CommentReaction,
|
||||
IssueVote,
|
||||
@ -444,22 +444,6 @@ class IssueLinkSerializer(BaseSerializer):
|
||||
return IssueLink.objects.create(**validated_data)
|
||||
|
||||
|
||||
class IssueAttachmentSerializer(BaseFileSerializer):
|
||||
|
||||
class Meta:
|
||||
model = IssueAttachment
|
||||
fields = "__all__"
|
||||
read_only_fields = [
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"workspace",
|
||||
"project",
|
||||
"issue",
|
||||
]
|
||||
|
||||
|
||||
class IssueReactionSerializer(BaseSerializer):
|
||||
actor_detail = UserLiteSerializer(read_only=True, source="actor")
|
||||
|
||||
|
@ -117,12 +117,12 @@ urlpatterns = [
|
||||
name="project-issue-links",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/issue-attachments/",
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/attachments/",
|
||||
IssueAttachmentEndpoint.as_view(),
|
||||
name="project-issue-attachments",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/issue-attachments/<uuid:pk>/",
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/issues/<uuid:issue_id>/attachments/<uuid:workspace_id>/<str:asset_key>/",
|
||||
IssueAttachmentEndpoint.as_view(),
|
||||
name="project-issue-attachments",
|
||||
),
|
||||
|
@ -47,10 +47,10 @@ from plane.db.models import (
|
||||
Issue,
|
||||
CycleFavorite,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
Label,
|
||||
CycleUserProperties,
|
||||
IssueSubscriber,
|
||||
FileAsset,
|
||||
)
|
||||
from plane.bgtasks.issue_activites_task import issue_activity
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
@ -611,7 +611,7 @@ class CycleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
)
|
||||
.order_by()
|
||||
|
@ -32,7 +32,7 @@ from plane.db.models import (
|
||||
Dashboard,
|
||||
Project,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
IssueRelation,
|
||||
)
|
||||
from plane.app.serializers import (
|
||||
@ -117,8 +117,9 @@ def dashboard_assigned_issues(self, request, slug):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -229,8 +230,9 @@ def dashboard_created_issues(self, request, slug):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
@ -19,7 +19,7 @@ from plane.db.models import (
|
||||
Issue,
|
||||
State,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
ProjectMember,
|
||||
)
|
||||
from plane.app.serializers import (
|
||||
@ -92,7 +92,7 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
Issue.objects.filter(
|
||||
project_id=self.kwargs.get("project_id"),
|
||||
workspace__slug=self.kwargs.get("slug"),
|
||||
issue_inbox__inbox_id=self.kwargs.get("inbox_id")
|
||||
issue_inbox__inbox_id=self.kwargs.get("inbox_id"),
|
||||
)
|
||||
.select_related("workspace", "project", "state", "parent")
|
||||
.prefetch_related("assignees", "labels", "issue_module__module")
|
||||
@ -112,8 +112,9 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -131,8 +132,14 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
|
||||
def list(self, request, slug, project_id, inbox_id):
|
||||
filters = issue_filters(request.query_params, "GET")
|
||||
issue_queryset = self.get_queryset().filter(**filters).order_by("issue_inbox__snoozed_till", "issue_inbox__status")
|
||||
issues_data = IssueSerializer(issue_queryset, expand=self.expand, many=True).data
|
||||
issue_queryset = (
|
||||
self.get_queryset()
|
||||
.filter(**filters)
|
||||
.order_by("issue_inbox__snoozed_till", "issue_inbox__status")
|
||||
)
|
||||
issues_data = IssueSerializer(
|
||||
issue_queryset, expand=self.expand, many=True
|
||||
).data
|
||||
return Response(
|
||||
issues_data,
|
||||
status=status.HTTP_200_OK,
|
||||
@ -199,8 +206,8 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
source=request.data.get("source", "in-app"),
|
||||
)
|
||||
|
||||
issue = (self.get_queryset().filter(pk=issue.id).first())
|
||||
serializer = IssueSerializer(issue ,expand=self.expand)
|
||||
issue = self.get_queryset().filter(pk=issue.id).first()
|
||||
serializer = IssueSerializer(issue, expand=self.expand)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def partial_update(self, request, slug, project_id, inbox_id, issue_id):
|
||||
@ -320,20 +327,23 @@ class InboxIssueViewSet(BaseViewSet):
|
||||
if state is not None:
|
||||
issue.state = state
|
||||
issue.save()
|
||||
issue = (self.get_queryset().filter(pk=issue_id).first())
|
||||
issue = self.get_queryset().filter(pk=issue_id).first()
|
||||
serializer = IssueSerializer(issue, expand=self.expand)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
return Response(
|
||||
serializer.errors, status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
else:
|
||||
issue = (self.get_queryset().filter(pk=issue_id).first())
|
||||
serializer = IssueSerializer(issue ,expand=self.expand)
|
||||
issue = self.get_queryset().filter(pk=issue_id).first()
|
||||
serializer = IssueSerializer(issue, expand=self.expand)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def retrieve(self, request, slug, project_id, inbox_id, issue_id):
|
||||
issue = self.get_queryset().filter(pk=issue_id).first()
|
||||
serializer = IssueSerializer(issue, expand=self.expand,)
|
||||
serializer = IssueSerializer(
|
||||
issue,
|
||||
expand=self.expand,
|
||||
)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def destroy(self, request, slug, project_id, inbox_id, issue_id):
|
||||
|
@ -4,7 +4,7 @@ import random
|
||||
from itertools import chain
|
||||
|
||||
# Django imports
|
||||
from django.db import models
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils import timezone
|
||||
from django.db.models import (
|
||||
Prefetch,
|
||||
@ -29,7 +29,7 @@ from django.db import IntegrityError
|
||||
# Third Party imports
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from rest_framework.parsers import MultiPartParser, FormParser, JSONParser
|
||||
|
||||
# Module imports
|
||||
from . import BaseViewSet, BaseAPIView, WebhookMixin
|
||||
@ -43,13 +43,13 @@ from plane.app.serializers import (
|
||||
IssueFlatSerializer,
|
||||
IssueLinkSerializer,
|
||||
IssueLiteSerializer,
|
||||
IssueAttachmentSerializer,
|
||||
IssueSubscriberSerializer,
|
||||
ProjectMemberLiteSerializer,
|
||||
IssueReactionSerializer,
|
||||
CommentReactionSerializer,
|
||||
IssueRelationSerializer,
|
||||
RelatedIssueSerializer,
|
||||
FileAssetSerializer,
|
||||
)
|
||||
from plane.app.permissions import (
|
||||
ProjectEntityPermission,
|
||||
@ -58,6 +58,7 @@ from plane.app.permissions import (
|
||||
ProjectLitePermission,
|
||||
)
|
||||
from plane.db.models import (
|
||||
Workspace,
|
||||
Project,
|
||||
Issue,
|
||||
IssueActivity,
|
||||
@ -65,7 +66,7 @@ from plane.db.models import (
|
||||
IssueProperty,
|
||||
Label,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
State,
|
||||
IssueSubscriber,
|
||||
ProjectMember,
|
||||
@ -75,10 +76,12 @@ from plane.db.models import (
|
||||
IssueVote,
|
||||
IssueRelation,
|
||||
ProjectPublicMember,
|
||||
FileAsset,
|
||||
)
|
||||
from plane.bgtasks.issue_activites_task import issue_activity
|
||||
from plane.utils.grouper import group_results
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
from plane.utils.presigned_url_generator import generate_download_presigned_url
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
@ -128,8 +131,9 @@ class IssueViewSet(WebhookMixin, BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -372,8 +376,9 @@ class UserWorkSpaceIssues(BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -792,8 +797,9 @@ class SubIssuesEndpoint(BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -1010,17 +1016,21 @@ class BulkCreateIssueLabelsEndpoint(BaseAPIView):
|
||||
|
||||
|
||||
class IssueAttachmentEndpoint(BaseAPIView):
|
||||
serializer_class = IssueAttachmentSerializer
|
||||
permission_classes = [
|
||||
ProjectEntityPermission,
|
||||
]
|
||||
model = IssueAttachment
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
parser_classes = (MultiPartParser, FormParser, JSONParser,)
|
||||
|
||||
def post(self, request, slug, project_id, issue_id):
|
||||
serializer = IssueAttachmentSerializer(data=request.data)
|
||||
serializer = FileAssetSerializer(data=request.data)
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
if serializer.is_valid():
|
||||
serializer.save(project_id=project_id, issue_id=issue_id)
|
||||
serializer.save(
|
||||
workspace=workspace,
|
||||
project_id=project_id,
|
||||
entity_type="issue_attachment",
|
||||
entity_identifier=issue_id,
|
||||
)
|
||||
issue_activity.delay(
|
||||
type="attachment.activity.created",
|
||||
requested_data=None,
|
||||
@ -1038,10 +1048,19 @@ class IssueAttachmentEndpoint(BaseAPIView):
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, slug, project_id, issue_id, pk):
|
||||
issue_attachment = IssueAttachment.objects.get(pk=pk)
|
||||
issue_attachment.asset.delete(save=False)
|
||||
issue_attachment.delete()
|
||||
def delete(
|
||||
self, request, slug, project_id, issue_id, workspace_id, asset_key
|
||||
):
|
||||
key = f"{workspace_id}/{asset_key}"
|
||||
asset = FileAsset.objects.get(
|
||||
asset=key,
|
||||
entity_identifier=issue_id,
|
||||
entity_type="issue_attachment",
|
||||
workspace__slug=slug,
|
||||
project_id=project_id,
|
||||
)
|
||||
asset.is_deleted = True
|
||||
asset.save()
|
||||
issue_activity.delay(
|
||||
type="attachment.activity.deleted",
|
||||
requested_data=None,
|
||||
@ -1053,14 +1072,30 @@ class IssueAttachmentEndpoint(BaseAPIView):
|
||||
notification=True,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
)
|
||||
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
def get(self, request, slug, project_id, issue_id):
|
||||
issue_attachments = IssueAttachment.objects.filter(
|
||||
issue_id=issue_id, workspace__slug=slug, project_id=project_id
|
||||
def get(
|
||||
self,
|
||||
request,
|
||||
slug,
|
||||
project_id,
|
||||
issue_id,
|
||||
workspace_id=None,
|
||||
asset_key=None,
|
||||
):
|
||||
if workspace_id and asset_key:
|
||||
key = f"{workspace_id}/{asset_key}"
|
||||
url = generate_download_presigned_url(key)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# For listing
|
||||
issue_attachments = FileAsset.objects.filter(
|
||||
entity_type="issue_attachment",
|
||||
entity_identifier=issue_id,
|
||||
workspace__slug=slug,
|
||||
project_id=project_id,
|
||||
)
|
||||
serializer = IssueAttachmentSerializer(issue_attachments, many=True)
|
||||
serializer = FileAssetSerializer(issue_attachments, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
@ -1084,7 +1119,7 @@ class IssueArchiveViewSet(BaseViewSet):
|
||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||
.select_related("workspace", "project", "state", "parent")
|
||||
.prefetch_related("assignees", "labels", "issue_module__module")
|
||||
.annotate(cycle_id=F("issue_cycle__cycle_id"))
|
||||
.annotate(cycle_id=F("issue_cycle__cycle_id"))
|
||||
.annotate(
|
||||
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
|
||||
.order_by()
|
||||
@ -1092,8 +1127,9 @@ class IssueArchiveViewSet(BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -1131,10 +1167,7 @@ class IssueArchiveViewSet(BaseViewSet):
|
||||
|
||||
order_by_param = request.GET.get("order_by", "-created_at")
|
||||
|
||||
issue_queryset = (
|
||||
self.get_queryset()
|
||||
.filter(**filters)
|
||||
)
|
||||
issue_queryset = self.get_queryset().filter(**filters)
|
||||
|
||||
# Priority Ordering
|
||||
if order_by_param == "priority" or order_by_param == "-priority":
|
||||
@ -1579,15 +1612,17 @@ class IssueRelationViewSet(BaseViewSet):
|
||||
issue_relation = IssueRelation.objects.bulk_create(
|
||||
[
|
||||
IssueRelation(
|
||||
issue_id=issue
|
||||
if relation_type == "blocking"
|
||||
else issue_id,
|
||||
related_issue_id=issue_id
|
||||
if relation_type == "blocking"
|
||||
else issue,
|
||||
relation_type="blocked_by"
|
||||
if relation_type == "blocking"
|
||||
else relation_type,
|
||||
issue_id=(
|
||||
issue if relation_type == "blocking" else issue_id
|
||||
),
|
||||
related_issue_id=(
|
||||
issue_id if relation_type == "blocking" else issue
|
||||
),
|
||||
relation_type=(
|
||||
"blocked_by"
|
||||
if relation_type == "blocking"
|
||||
else relation_type
|
||||
),
|
||||
project_id=project_id,
|
||||
workspace_id=project.workspace_id,
|
||||
created_by=request.user,
|
||||
@ -1695,8 +1730,9 @@ class IssueDraftViewSet(BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -1733,10 +1769,7 @@ class IssueDraftViewSet(BaseViewSet):
|
||||
|
||||
order_by_param = request.GET.get("order_by", "-created_at")
|
||||
|
||||
issue_queryset = (
|
||||
self.get_queryset()
|
||||
.filter(**filters)
|
||||
)
|
||||
issue_queryset = self.get_queryset().filter(**filters)
|
||||
|
||||
# Priority Ordering
|
||||
if order_by_param == "priority" or order_by_param == "-priority":
|
||||
|
@ -37,7 +37,7 @@ from plane.db.models import (
|
||||
ModuleLink,
|
||||
ModuleFavorite,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
IssueSubscriber,
|
||||
ModuleUserProperties,
|
||||
)
|
||||
@ -331,17 +331,16 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
ProjectEntityPermission,
|
||||
]
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
Issue.objects.filter(
|
||||
project_id=self.kwargs.get("project_id"),
|
||||
workspace__slug=self.kwargs.get("slug"),
|
||||
issue_module__module_id=self.kwargs.get("module_id")
|
||||
issue_module__module_id=self.kwargs.get("module_id"),
|
||||
)
|
||||
.select_related("workspace", "project", "state", "parent")
|
||||
.prefetch_related("labels", "assignees")
|
||||
.prefetch_related('issue_module__module')
|
||||
.prefetch_related("issue_module__module")
|
||||
.annotate(cycle_id=F("issue_cycle__cycle_id"))
|
||||
.annotate(
|
||||
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
|
||||
@ -350,8 +349,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -420,10 +420,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
)
|
||||
for issue in issues
|
||||
]
|
||||
issues = (self.get_queryset().filter(pk__in=issues))
|
||||
serializer = IssueSerializer(issues , many=True)
|
||||
issues = self.get_queryset().filter(pk__in=issues)
|
||||
serializer = IssueSerializer(issues, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
# create multiple module inside an issue
|
||||
def create_issue_modules(self, request, slug, project_id, issue_id):
|
||||
@ -466,11 +465,10 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
for module in modules
|
||||
]
|
||||
|
||||
issue = (self.get_queryset().filter(pk=issue_id).first())
|
||||
issue = self.get_queryset().filter(pk=issue_id).first()
|
||||
serializer = IssueSerializer(issue)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
def destroy(self, request, slug, project_id, module_id, issue_id):
|
||||
module_issue = ModuleIssue.objects.get(
|
||||
workspace__slug=slug,
|
||||
@ -484,7 +482,9 @@ class ModuleIssueViewSet(WebhookMixin, BaseViewSet):
|
||||
actor_id=str(request.user.id),
|
||||
issue_id=str(issue_id),
|
||||
project_id=str(project_id),
|
||||
current_instance=json.dumps({"module_name": module_issue.module.name}),
|
||||
current_instance=json.dumps(
|
||||
{"module_name": module_issue.module.name}
|
||||
),
|
||||
epoch=int(timezone.now().timestamp()),
|
||||
notification=True,
|
||||
origin=request.META.get("HTTP_ORIGIN"),
|
||||
|
@ -41,7 +41,7 @@ from plane.db.models import (
|
||||
IssueViewFavorite,
|
||||
IssueReaction,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
IssueSubscriber,
|
||||
)
|
||||
from plane.utils.issue_filters import issue_filters
|
||||
@ -130,8 +130,9 @@ class GlobalViewIssuesViewSet(BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
@ -66,7 +66,7 @@ from plane.db.models import (
|
||||
Issue,
|
||||
WorkspaceTheme,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
IssueSubscriber,
|
||||
Project,
|
||||
Label,
|
||||
@ -1360,8 +1360,9 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
@ -1558,10 +1559,14 @@ class WorkspaceLogoEndpoint(BaseAPIView):
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
if serializer.is_valid():
|
||||
serializer.save(workspace=workspace)
|
||||
workspace.logo = f"/api/workspaces/{slug}/logo/{serializer.data['asset']}/"
|
||||
workspace.logo = (
|
||||
f"/api/workspaces/{slug}/logo/{serializer.data['asset']}/"
|
||||
)
|
||||
workspace.save()
|
||||
workspace_serializer = WorkSpaceSerializer(workspace)
|
||||
return Response(workspace_serializer.data, status=status.HTTP_201_CREATED)
|
||||
return Response(
|
||||
workspace_serializer.data, status=status.HTTP_201_CREATED
|
||||
)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, slug, workspace_id, logo_key):
|
||||
|
@ -224,7 +224,8 @@ class Migration(migrations.Migration):
|
||||
name="entity_type",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("issue", "Issue"),
|
||||
("issue_attachment", "Issue Attachment"),
|
||||
("issue_description", "Issue Description"),
|
||||
("comment", "Comment"),
|
||||
("page", "Page"),
|
||||
],
|
||||
|
@ -67,7 +67,7 @@ def convert_issue_description_image_sources(apps, schema_editor):
|
||||
for asset in FileAsset.objects.filter(asset__in=file_assets.keys()):
|
||||
asset.project_id = file_assets[str(asset.asset)]["project_id"]
|
||||
asset.entity_identifier = file_assets[str(asset.asset)]["issue_id"]
|
||||
asset.entity_type = "issue"
|
||||
asset.entity_type = "issue_description"
|
||||
bulk_assets.append(asset)
|
||||
|
||||
FileAsset.objects.bulk_update(
|
||||
|
@ -4,6 +4,31 @@ from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def create_attachment_assets(apps, schema_editor):
|
||||
bulk_assets = []
|
||||
|
||||
issue_attachments = {}
|
||||
|
||||
FileAsset = apps.get_model("db", "FileAsset")
|
||||
IssueAttachment = apps.get_model("db", "IssueAttachment")
|
||||
|
||||
for issue_attachment in IssueAttachment.objects.values():
|
||||
bulk_assets.append(
|
||||
FileAsset(
|
||||
workspace_id=issue_attachment["workspace_id"],
|
||||
project_id=issue_attachment["project_id"],
|
||||
entity_identifier=issue_attachment["issue_id"],
|
||||
entity_type="issue_attachment",
|
||||
asset=issue_attachment["asset"],
|
||||
attributes=issue_attachment["attributes"],
|
||||
)
|
||||
)
|
||||
issue_attachments[str(issue_attachment["asset"])] = str(
|
||||
issue_attachment["id"]
|
||||
)
|
||||
|
||||
FileAsset.objects.bulk_create(bulk_assets, batch_size=100)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@ -12,21 +37,8 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="issueattachment",
|
||||
name="asset_key",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="issue_assets",
|
||||
to="db.fileasset",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="issueattachment",
|
||||
name="type",
|
||||
field=models.PositiveSmallIntegerField(
|
||||
choices=[(0, "Attachment"), (1, "Description")], default=0
|
||||
),
|
||||
migrations.RunPython(create_attachment_assets),
|
||||
migrations.DeleteModel(
|
||||
name="IssueAttachment",
|
||||
),
|
||||
]
|
||||
|
@ -1,74 +0,0 @@
|
||||
# Generated by Django 4.2.7 on 2024-02-05 07:03
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def update_attachment_assets(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
def create_description_assets(apps, schema_editor):
|
||||
FileAsset = apps.get_model("db", "FileAsset")
|
||||
IssueAttachment = apps.get_model("db", "IssueAttachment")
|
||||
|
||||
bulk_issue_attachments = []
|
||||
for asset in FileAsset.objects.filter(entity_type="issue").values():
|
||||
bulk_issue_attachments.append(
|
||||
IssueAttachment(
|
||||
workspace_id=asset["workspace_id"],
|
||||
project_id=asset["project_id"],
|
||||
issue_id=asset["entity_identifier"],
|
||||
asset_key_id=asset["id"],
|
||||
type=1,
|
||||
)
|
||||
)
|
||||
|
||||
IssueAttachment.objects.bulk_create(bulk_issue_attachments, batch_size=100)
|
||||
|
||||
|
||||
def create_attachment_assets(apps, schema_editor):
|
||||
bulk_assets = []
|
||||
|
||||
issue_attachments = {}
|
||||
|
||||
FileAsset = apps.get_model("db", "FileAsset")
|
||||
IssueAttachment = apps.get_model("db", "IssueAttachment")
|
||||
|
||||
for issue_attachment in IssueAttachment.objects.filter(type=0).values():
|
||||
bulk_assets.append(
|
||||
FileAsset(
|
||||
workspace_id=issue_attachment["workspace_id"],
|
||||
project_id=issue_attachment["project_id"],
|
||||
entity_identifier=issue_attachment["issue_id"],
|
||||
entity_type="issue",
|
||||
asset=issue_attachment["asset"],
|
||||
attributes=issue_attachment["attributes"],
|
||||
)
|
||||
)
|
||||
issue_attachments[str(issue_attachment["asset"])] = str(issue_attachment["id"])
|
||||
|
||||
FileAsset.objects.bulk_create(bulk_assets, batch_size=100)
|
||||
|
||||
|
||||
assets = FileAsset.objects.filter(asset__in=issue_attachments.keys()).values("id", "asset")
|
||||
bulk_issue_attachments = []
|
||||
for issue_attachment in IssueAttachment.objects.filter(type=0):
|
||||
asset_key_id = [asset.get("id") for asset in assets if str(asset.get("asset")) == str(issue_attachment.asset)]
|
||||
if asset_key_id:
|
||||
issue_attachment.asset_key_id = str(asset_key_id[0])
|
||||
bulk_issue_attachments.append(issue_attachment)
|
||||
|
||||
IssueAttachment.objects.bulk_update(bulk_issue_attachments, ["asset_key"], batch_size=100)
|
||||
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("db", "0061_issueattachment_asset_key_issueattachment_type"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_description_assets),
|
||||
migrations.RunPython(create_attachment_assets),
|
||||
]
|
@ -37,7 +37,6 @@ from .issue import (
|
||||
IssueMention,
|
||||
IssueLink,
|
||||
IssueSequence,
|
||||
IssueAttachment,
|
||||
IssueSubscriber,
|
||||
IssueReaction,
|
||||
CommentReaction,
|
||||
|
@ -48,7 +48,8 @@ class FileAsset(BaseModel):
|
||||
)
|
||||
entity_type = models.CharField(
|
||||
choices=(
|
||||
("issue", "Issue"),
|
||||
("issue_attachment", "Issue Attachment"),
|
||||
("issue_description", "Issue Description"),
|
||||
("comment", "Comment"),
|
||||
("page", "Page"),
|
||||
),
|
||||
|
@ -345,42 +345,6 @@ def file_size(value):
|
||||
if value.size > settings.FILE_SIZE_LIMIT:
|
||||
raise ValidationError("File too large. Size should not exceed 5 MB.")
|
||||
|
||||
|
||||
class IssueAttachment(ProjectBaseModel):
|
||||
attributes = models.JSONField(default=dict)
|
||||
asset = models.FileField(
|
||||
upload_to=get_upload_path,
|
||||
validators=[
|
||||
file_size,
|
||||
],
|
||||
)
|
||||
asset_key = models.ForeignKey(
|
||||
"db.FileAsset",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="issue_assets",
|
||||
null=True,
|
||||
)
|
||||
issue = models.ForeignKey(
|
||||
"db.Issue", on_delete=models.CASCADE, related_name="issue_attachment"
|
||||
)
|
||||
type = models.PositiveSmallIntegerField(
|
||||
choices=(
|
||||
(0, "Attachment"),
|
||||
(1, "Description"),
|
||||
),
|
||||
default=0,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Issue Attachment"
|
||||
verbose_name_plural = "Issue Attachments"
|
||||
db_table = "issue_attachments"
|
||||
ordering = ("-created_at",)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.issue.name} {self.asset}"
|
||||
|
||||
|
||||
class IssueActivity(ProjectBaseModel):
|
||||
issue = models.ForeignKey(
|
||||
Issue,
|
||||
|
@ -22,7 +22,7 @@ from plane.db.models import (
|
||||
CycleIssue,
|
||||
ModuleIssue,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
IssueReaction,
|
||||
CommentReaction,
|
||||
IssueVote,
|
||||
@ -171,22 +171,6 @@ class IssueLinkSerializer(BaseSerializer):
|
||||
)
|
||||
return IssueLink.objects.create(**validated_data)
|
||||
|
||||
|
||||
class IssueAttachmentSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = IssueAttachment
|
||||
fields = "__all__"
|
||||
read_only_fields = [
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"workspace",
|
||||
"project",
|
||||
"issue",
|
||||
]
|
||||
|
||||
|
||||
class IssueReactionSerializer(BaseSerializer):
|
||||
actor_detail = UserLiteSerializer(read_only=True, source="actor")
|
||||
|
||||
@ -218,7 +202,6 @@ class IssueSerializer(BaseSerializer):
|
||||
issue_cycle = IssueCycleDetailSerializer(read_only=True)
|
||||
issue_module = IssueModuleDetailSerializer(read_only=True)
|
||||
issue_link = IssueLinkSerializer(read_only=True, many=True)
|
||||
issue_attachment = IssueAttachmentSerializer(read_only=True, many=True)
|
||||
sub_issues_count = serializers.IntegerField(read_only=True)
|
||||
issue_reactions = IssueReactionSerializer(read_only=True, many=True)
|
||||
|
||||
|
@ -17,7 +17,7 @@ from plane.db.models import (
|
||||
Issue,
|
||||
State,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
ProjectDeployBoard,
|
||||
)
|
||||
from plane.app.serializers import (
|
||||
@ -95,8 +95,9 @@ class InboxIssuePublicViewSet(BaseViewSet):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
@ -40,7 +40,7 @@ from plane.db.models import (
|
||||
IssueComment,
|
||||
Label,
|
||||
IssueLink,
|
||||
IssueAttachment,
|
||||
FileAsset,
|
||||
State,
|
||||
ProjectMember,
|
||||
IssueReaction,
|
||||
@ -567,8 +567,9 @@ class ProjectIssuesPublicEndpoint(BaseAPIView):
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=IssueAttachment.objects.filter(
|
||||
issue=OuterRef("id")
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
entity_identifier=OuterRef("id"),
|
||||
entity_type="issue_attachment",
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
|
Loading…
Reference in New Issue
Block a user