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