fix: renamed inbox to intake (#5967)

* feat: intake

* chore: intake model migration changes

* dev: update dummy data

* dev: add duplicate apis for inbox

* dev: fix external apis

* fix: external apis

* chore: migration file changes

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
This commit is contained in:
Bavisetti Narayan 2024-11-08 17:10:24 +05:30 committed by GitHub
parent 1743717351
commit 1d314dd25f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 697 additions and 458 deletions

View file

@ -18,4 +18,4 @@ from .module import (
ModuleIssueSerializer, ModuleIssueSerializer,
ModuleLiteSerializer, ModuleLiteSerializer,
) )
from .inbox import InboxIssueSerializer from .intake import IntakeIssueSerializer

View file

@ -1,15 +1,17 @@
# Module improts # Module improts
from .base import BaseSerializer from .base import BaseSerializer
from .issue import IssueExpandSerializer from .issue import IssueExpandSerializer
from plane.db.models import InboxIssue from plane.db.models import IntakeIssue
from rest_framework import serializers
class InboxIssueSerializer(BaseSerializer): class IntakeIssueSerializer(BaseSerializer):
issue_detail = IssueExpandSerializer(read_only=True, source="issue") issue_detail = IssueExpandSerializer(read_only=True, source="issue")
inbox = serializers.UUIDField(source="intake.id", read_only=True)
class Meta: class Meta:
model = InboxIssue model = IntakeIssue
fields = "__all__" fields = "__all__"
read_only_fields = [ read_only_fields = [
"id", "id",

View file

@ -20,6 +20,7 @@ class ProjectSerializer(BaseSerializer):
member_role = serializers.IntegerField(read_only=True) member_role = serializers.IntegerField(read_only=True)
is_deployed = serializers.BooleanField(read_only=True) is_deployed = serializers.BooleanField(read_only=True)
cover_image_url = serializers.CharField(read_only=True) cover_image_url = serializers.CharField(read_only=True)
inbox_view = serializers.BooleanField(read_only=True, source="intake_view")
class Meta: class Meta:
model = Project model = Project

View file

@ -3,7 +3,7 @@ from .state import urlpatterns as state_patterns
from .issue import urlpatterns as issue_patterns from .issue import urlpatterns as issue_patterns
from .cycle import urlpatterns as cycle_patterns from .cycle import urlpatterns as cycle_patterns
from .module import urlpatterns as module_patterns from .module import urlpatterns as module_patterns
from .inbox import urlpatterns as inbox_patterns from .intake import urlpatterns as intake_patterns
from .member import urlpatterns as member_patterns from .member import urlpatterns as member_patterns
urlpatterns = [ urlpatterns = [
@ -12,6 +12,6 @@ urlpatterns = [
*issue_patterns, *issue_patterns,
*cycle_patterns, *cycle_patterns,
*module_patterns, *module_patterns,
*inbox_patterns, *intake_patterns,
*member_patterns, *member_patterns,
] ]

View file

@ -1,17 +0,0 @@
from django.urls import path
from plane.api.views import InboxIssueAPIEndpoint
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/",
InboxIssueAPIEndpoint.as_view(),
name="inbox-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/<uuid:issue_id>/",
InboxIssueAPIEndpoint.as_view(),
name="inbox-issue",
),
]

View file

@ -0,0 +1,27 @@
from django.urls import path
from plane.api.views import IntakeIssueAPIEndpoint
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/",
IntakeIssueAPIEndpoint.as_view(),
name="inbox-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/<uuid:issue_id>/",
IntakeIssueAPIEndpoint.as_view(),
name="inbox-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intake-issues/",
IntakeIssueAPIEndpoint.as_view(),
name="intake-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intake-issues/<uuid:issue_id>/",
IntakeIssueAPIEndpoint.as_view(),
name="intake-issue",
),
]

View file

@ -27,5 +27,4 @@ from .module import (
from .member import ProjectMemberAPIEndpoint from .member import ProjectMemberAPIEndpoint
from .inbox import InboxIssueAPIEndpoint from .intake import IntakeIssueAPIEndpoint

View file

@ -14,12 +14,12 @@ from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
# Module imports # Module imports
from plane.api.serializers import InboxIssueSerializer, IssueSerializer from plane.api.serializers import IntakeIssueSerializer, IssueSerializer
from plane.app.permissions import ProjectLitePermission from plane.app.permissions import ProjectLitePermission
from plane.bgtasks.issue_activities_task import issue_activity from plane.bgtasks.issue_activities_task import issue_activity
from plane.db.models import ( from plane.db.models import (
Inbox, Intake,
InboxIssue, IntakeIssue,
Issue, Issue,
Project, Project,
ProjectMember, ProjectMember,
@ -29,10 +29,10 @@ from plane.db.models import (
from .base import BaseAPIView from .base import BaseAPIView
class InboxIssueAPIEndpoint(BaseAPIView): class IntakeIssueAPIEndpoint(BaseAPIView):
""" """
This viewset automatically provides `list`, `create`, `retrieve`, This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions related to inbox issues. `update` and `destroy` actions related to intake issues.
""" """
@ -40,15 +40,15 @@ class InboxIssueAPIEndpoint(BaseAPIView):
ProjectLitePermission, ProjectLitePermission,
] ]
serializer_class = InboxIssueSerializer serializer_class = IntakeIssueSerializer
model = InboxIssue model = IntakeIssue
filterset_fields = [ filterset_fields = [
"status", "status",
] ]
def get_queryset(self): def get_queryset(self):
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
workspace__slug=self.kwargs.get("slug"), workspace__slug=self.kwargs.get("slug"),
project_id=self.kwargs.get("project_id"), project_id=self.kwargs.get("project_id"),
).first() ).first()
@ -58,16 +58,16 @@ class InboxIssueAPIEndpoint(BaseAPIView):
pk=self.kwargs.get("project_id"), pk=self.kwargs.get("project_id"),
) )
if inbox is None and not project.inbox_view: if intake is None and not project.intake_view:
return InboxIssue.objects.none() return IntakeIssue.objects.none()
return ( return (
InboxIssue.objects.filter( IntakeIssue.objects.filter(
Q(snoozed_till__gte=timezone.now()) Q(snoozed_till__gte=timezone.now())
| Q(snoozed_till__isnull=True), | Q(snoozed_till__isnull=True),
workspace__slug=self.kwargs.get("slug"), workspace__slug=self.kwargs.get("slug"),
project_id=self.kwargs.get("project_id"), project_id=self.kwargs.get("project_id"),
inbox_id=inbox.id, intake_id=intake.id,
) )
.select_related("issue", "workspace", "project") .select_related("issue", "workspace", "project")
.order_by(self.kwargs.get("order_by", "-created_at")) .order_by(self.kwargs.get("order_by", "-created_at"))
@ -75,22 +75,22 @@ class InboxIssueAPIEndpoint(BaseAPIView):
def get(self, request, slug, project_id, issue_id=None): def get(self, request, slug, project_id, issue_id=None):
if issue_id: if issue_id:
inbox_issue_queryset = self.get_queryset().get(issue_id=issue_id) intake_issue_queryset = self.get_queryset().get(issue_id=issue_id)
inbox_issue_data = InboxIssueSerializer( intake_issue_data = IntakeIssueSerializer(
inbox_issue_queryset, intake_issue_queryset,
fields=self.fields, fields=self.fields,
expand=self.expand, expand=self.expand,
).data ).data
return Response( return Response(
inbox_issue_data, intake_issue_data,
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
issue_queryset = self.get_queryset() issue_queryset = self.get_queryset()
return self.paginate( return self.paginate(
request=request, request=request,
queryset=(issue_queryset), queryset=(issue_queryset),
on_results=lambda inbox_issues: InboxIssueSerializer( on_results=lambda intake_issues: IntakeIssueSerializer(
inbox_issues, intake_issues,
many=True, many=True,
fields=self.fields, fields=self.fields,
expand=self.expand, expand=self.expand,
@ -104,7 +104,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
@ -113,11 +113,11 @@ class InboxIssueAPIEndpoint(BaseAPIView):
pk=project_id, pk=project_id,
) )
# Inbox view # Intake view
if inbox is None and not project.inbox_view: if intake is None and not project.intake_view:
return Response( return Response(
{ {
"error": "Inbox is not enabled for this project enable it through the project's api" "error": "Intake is not enabled for this project enable it through the project's api"
}, },
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
@ -139,7 +139,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
state, _ = State.objects.get_or_create( state, _ = State.objects.get_or_create(
name="Triage", name="Triage",
group="triage", group="triage",
description="Default state for managing all Inbox Issues", description="Default state for managing all Intake Issues",
project_id=project_id, project_id=project_id,
color="#ff7700", color="#ff7700",
is_triage=True, is_triage=True,
@ -157,12 +157,12 @@ class InboxIssueAPIEndpoint(BaseAPIView):
state=state, state=state,
) )
# create an inbox issue # create an intake issue
inbox_issue = InboxIssue.objects.create( intake_issue = IntakeIssue.objects.create(
inbox_id=inbox.id, intake_id=intake.id,
project_id=project_id, project_id=project_id,
issue=issue, issue=issue,
source=request.data.get("source", "in-app"), source=request.data.get("source", "IN-APP"),
) )
# Create an Issue Activity # Create an Issue Activity
issue_activity.delay( issue_activity.delay(
@ -173,32 +173,37 @@ class InboxIssueAPIEndpoint(BaseAPIView):
project_id=str(project_id), project_id=str(project_id),
current_instance=None, current_instance=None,
epoch=int(timezone.now().timestamp()), epoch=int(timezone.now().timestamp()),
inbox=str(inbox_issue.id), intake=str(intake_issue.id),
) )
serializer = InboxIssueSerializer(inbox_issue) serializer = IntakeIssueSerializer(intake_issue)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
def patch(self, request, slug, project_id, issue_id): def patch(self, request, slug, project_id, issue_id):
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
# Inbox view project = Project.objects.get(
if inbox is None: workspace__slug=slug,
pk=project_id,
)
# Intake view
if intake is None and not project.intake_view:
return Response( return Response(
{ {
"error": "Inbox is not enabled for this project enable it through the project's api" "error": "Intake is not enabled for this project enable it through the project's api"
}, },
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
# Get the inbox issue # Get the intake issue
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
issue_id=issue_id, issue_id=issue_id,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
inbox_id=inbox.id, intake_id=intake.id,
) )
# Get the project member # Get the project member
@ -210,11 +215,11 @@ class InboxIssueAPIEndpoint(BaseAPIView):
) )
# Only project members admins and created_by users can access this endpoint # Only project members admins and created_by users can access this endpoint
if project_member.role <= 5 and str(inbox_issue.created_by_id) != str( if project_member.role <= 5 and str(intake_issue.created_by_id) != str(
request.user.id request.user.id
): ):
return Response( return Response(
{"error": "You cannot edit inbox issues"}, {"error": "You cannot edit intake issues"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
@ -283,7 +288,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
cls=DjangoJSONEncoder, cls=DjangoJSONEncoder,
), ),
epoch=int(timezone.now().timestamp()), epoch=int(timezone.now().timestamp()),
inbox=(inbox_issue.id), intake=(intake_issue.id),
) )
issue_serializer.save() issue_serializer.save()
else: else:
@ -291,13 +296,13 @@ class InboxIssueAPIEndpoint(BaseAPIView):
issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST
) )
# Only project admins and members can edit inbox issue attributes # Only project admins and members can edit intake issue attributes
if project_member.role > 15: if project_member.role > 15:
serializer = InboxIssueSerializer( serializer = IntakeIssueSerializer(
inbox_issue, data=request.data, partial=True intake_issue, data=request.data, partial=True
) )
current_instance = json.dumps( current_instance = json.dumps(
InboxIssueSerializer(inbox_issue).data, cls=DjangoJSONEncoder IntakeIssueSerializer(intake_issue).data, cls=DjangoJSONEncoder
) )
if serializer.is_valid(): if serializer.is_valid():
@ -340,7 +345,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
# create a activity for status change # create a activity for status change
issue_activity.delay( issue_activity.delay(
type="inbox.activity.created", type="intake.activity.created",
requested_data=json.dumps( requested_data=json.dumps(
request.data, cls=DjangoJSONEncoder request.data, cls=DjangoJSONEncoder
), ),
@ -351,7 +356,7 @@ class InboxIssueAPIEndpoint(BaseAPIView):
epoch=int(timezone.now().timestamp()), epoch=int(timezone.now().timestamp()),
notification=False, notification=False,
origin=request.META.get("HTTP_ORIGIN"), origin=request.META.get("HTTP_ORIGIN"),
inbox=str(inbox_issue.id), intake=str(intake_issue.id),
) )
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
@ -360,12 +365,12 @@ class InboxIssueAPIEndpoint(BaseAPIView):
) )
else: else:
return Response( return Response(
InboxIssueSerializer(inbox_issue).data, IntakeIssueSerializer(intake_issue).data,
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
def delete(self, request, slug, project_id, issue_id): def delete(self, request, slug, project_id, issue_id):
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
@ -374,25 +379,25 @@ class InboxIssueAPIEndpoint(BaseAPIView):
pk=project_id, pk=project_id,
) )
# Inbox view # Intake view
if inbox is None and not project.inbox_view: if intake is None and not project.intake_view:
return Response( return Response(
{ {
"error": "Inbox is not enabled for this project enable it through the project's api" "error": "Intake is not enabled for this project enable it through the project's api"
}, },
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
# Get the inbox issue # Get the intake issue
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
issue_id=issue_id, issue_id=issue_id,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
inbox_id=inbox.id, intake_id=intake.id,
) )
# Check the issue status # Check the issue status
if inbox_issue.status in [-2, -1, 0, 2]: if intake_issue.status in [-2, -1, 0, 2]:
# Delete the issue also # Delete the issue also
issue = Issue.objects.filter( issue = Issue.objects.filter(
workspace__slug=slug, project_id=project_id, pk=issue_id workspace__slug=slug, project_id=project_id, pk=issue_id
@ -412,5 +417,5 @@ class InboxIssueAPIEndpoint(BaseAPIView):
) )
issue.delete() issue.delete()
inbox_issue.delete() intake_issue.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)

View file

@ -18,7 +18,7 @@ from plane.app.permissions import ProjectBasePermission
# Module imports # Module imports
from plane.db.models import ( from plane.db.models import (
Cycle, Cycle,
Inbox, Intake,
IssueUserProperty, IssueUserProperty,
Module, Module,
Project, Project,
@ -285,6 +285,11 @@ class ProjectAPIEndpoint(BaseAPIView):
current_instance = json.dumps( current_instance = json.dumps(
ProjectSerializer(project).data, cls=DjangoJSONEncoder ProjectSerializer(project).data, cls=DjangoJSONEncoder
) )
intake_view = request.data.get(
"inbox_view", request.data.get("intake_view", False)
)
if project.archived_at: if project.archived_at:
return Response( return Response(
{"error": "Archived project cannot be updated"}, {"error": "Archived project cannot be updated"},
@ -293,21 +298,24 @@ class ProjectAPIEndpoint(BaseAPIView):
serializer = ProjectSerializer( serializer = ProjectSerializer(
project, project,
data={**request.data}, data={
**request.data,
"intake_view": intake_view,
},
context={"workspace_id": workspace.id}, context={"workspace_id": workspace.id},
partial=True, partial=True,
) )
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
if serializer.data["inbox_view"]: if serializer.data["intake_view"]:
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
project=project, project=project,
is_default=True, is_default=True,
).first() ).first()
if not inbox: if not intake:
Inbox.objects.create( Intake.objects.create(
name=f"{project.name} Inbox", name=f"{project.name} Intake",
project=project, project=project,
is_default=True, is_default=True,
) )
@ -316,7 +324,7 @@ class ProjectAPIEndpoint(BaseAPIView):
State.objects.get_or_create( State.objects.get_or_create(
name="Triage", name="Triage",
group="triage", group="triage",
description="Default state for managing all Inbox Issues", description="Default state for managing all Intake Issues",
project_id=pk, project_id=pk,
color="#ff7700", color="#ff7700",
is_triage=True, is_triage=True,

View file

@ -57,7 +57,7 @@ from .issue import (
IssueFlatSerializer, IssueFlatSerializer,
IssueStateSerializer, IssueStateSerializer,
IssueLinkSerializer, IssueLinkSerializer,
IssueInboxSerializer, IssueIntakeSerializer,
IssueLiteSerializer, IssueLiteSerializer,
IssueAttachmentSerializer, IssueAttachmentSerializer,
IssueSubscriberSerializer, IssueSubscriberSerializer,
@ -102,12 +102,12 @@ from .estimate import (
WorkspaceEstimateSerializer, WorkspaceEstimateSerializer,
) )
from .inbox import ( from .intake import (
InboxSerializer, IntakeSerializer,
InboxIssueSerializer, IntakeIssueSerializer,
IssueStateInboxSerializer, IssueStateIntakeSerializer,
InboxIssueLiteSerializer, IntakeIssueLiteSerializer,
InboxIssueDetailSerializer, IntakeIssueDetailSerializer,
) )
from .analytic import AnalyticViewSerializer from .analytic import AnalyticViewSerializer

View file

@ -60,7 +60,7 @@ class DynamicBaseSerializer(BaseSerializer):
CycleIssueSerializer, CycleIssueSerializer,
IssueLiteSerializer, IssueLiteSerializer,
IssueRelationSerializer, IssueRelationSerializer,
InboxIssueLiteSerializer, IntakeIssueLiteSerializer,
IssueReactionLiteSerializer, IssueReactionLiteSerializer,
IssueLinkLiteSerializer, IssueLinkLiteSerializer,
RelatedIssueSerializer, RelatedIssueSerializer,
@ -84,8 +84,8 @@ class DynamicBaseSerializer(BaseSerializer):
"issue_cycle": CycleIssueSerializer, "issue_cycle": CycleIssueSerializer,
"parent": IssueLiteSerializer, "parent": IssueLiteSerializer,
"issue_relation": IssueRelationSerializer, "issue_relation": IssueRelationSerializer,
"issue_intake": IntakeIssueLiteSerializer,
"issue_related": RelatedIssueSerializer, "issue_related": RelatedIssueSerializer,
"issue_inbox": InboxIssueLiteSerializer,
"issue_reactions": IssueReactionLiteSerializer, "issue_reactions": IssueReactionLiteSerializer,
"issue_link": IssueLinkLiteSerializer, "issue_link": IssueLinkLiteSerializer,
"sub_issues": IssueLiteSerializer, "sub_issues": IssueLiteSerializer,
@ -102,7 +102,7 @@ class DynamicBaseSerializer(BaseSerializer):
"labels", "labels",
"issue_cycle", "issue_cycle",
"issue_relation", "issue_relation",
"issue_inbox", "issue_intake",
"issue_reactions", "issue_reactions",
"issue_attachment", "issue_attachment",
"issue_link", "issue_link",
@ -132,7 +132,7 @@ class DynamicBaseSerializer(BaseSerializer):
LabelSerializer, LabelSerializer,
CycleIssueSerializer, CycleIssueSerializer,
IssueRelationSerializer, IssueRelationSerializer,
InboxIssueLiteSerializer, IntakeIssueLiteSerializer,
IssueLiteSerializer, IssueLiteSerializer,
IssueReactionLiteSerializer, IssueReactionLiteSerializer,
IssueAttachmentLiteSerializer, IssueAttachmentLiteSerializer,
@ -158,8 +158,8 @@ class DynamicBaseSerializer(BaseSerializer):
"issue_cycle": CycleIssueSerializer, "issue_cycle": CycleIssueSerializer,
"parent": IssueLiteSerializer, "parent": IssueLiteSerializer,
"issue_relation": IssueRelationSerializer, "issue_relation": IssueRelationSerializer,
"issue_intake": IntakeIssueLiteSerializer,
"issue_related": RelatedIssueSerializer, "issue_related": RelatedIssueSerializer,
"issue_inbox": InboxIssueLiteSerializer,
"issue_reactions": IssueReactionLiteSerializer, "issue_reactions": IssueReactionLiteSerializer,
"issue_attachment": IssueAttachmentLiteSerializer, "issue_attachment": IssueAttachmentLiteSerializer,
"issue_link": IssueLinkLiteSerializer, "issue_link": IssueLinkLiteSerializer,

View file

@ -4,22 +4,22 @@ from rest_framework import serializers
# Module imports # Module imports
from .base import BaseSerializer from .base import BaseSerializer
from .issue import ( from .issue import (
IssueInboxSerializer, IssueIntakeSerializer,
LabelLiteSerializer, LabelLiteSerializer,
IssueDetailSerializer, IssueDetailSerializer,
) )
from .project import ProjectLiteSerializer from .project import ProjectLiteSerializer
from .state import StateLiteSerializer from .state import StateLiteSerializer
from .user import UserLiteSerializer from .user import UserLiteSerializer
from plane.db.models import Inbox, InboxIssue, Issue from plane.db.models import Intake, IntakeIssue, Issue
class InboxSerializer(BaseSerializer): class IntakeSerializer(BaseSerializer):
project_detail = ProjectLiteSerializer(source="project", read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True)
pending_issue_count = serializers.IntegerField(read_only=True) pending_issue_count = serializers.IntegerField(read_only=True)
class Meta: class Meta:
model = Inbox model = Intake
fields = "__all__" fields = "__all__"
read_only_fields = [ read_only_fields = [
"project", "project",
@ -27,11 +27,11 @@ class InboxSerializer(BaseSerializer):
] ]
class InboxIssueSerializer(BaseSerializer): class IntakeIssueSerializer(BaseSerializer):
issue = IssueInboxSerializer(read_only=True) issue = IssueIntakeSerializer(read_only=True)
class Meta: class Meta:
model = InboxIssue model = IntakeIssue
fields = [ fields = [
"id", "id",
"status", "status",
@ -53,14 +53,14 @@ class InboxIssueSerializer(BaseSerializer):
return super().to_representation(instance) return super().to_representation(instance)
class InboxIssueDetailSerializer(BaseSerializer): class IntakeIssueDetailSerializer(BaseSerializer):
issue = IssueDetailSerializer(read_only=True) issue = IssueDetailSerializer(read_only=True)
duplicate_issue_detail = IssueInboxSerializer( duplicate_issue_detail = IssueIntakeSerializer(
read_only=True, source="duplicate_to" read_only=True, source="duplicate_to"
) )
class Meta: class Meta:
model = InboxIssue model = IntakeIssue
fields = [ fields = [
"id", "id",
"status", "status",
@ -85,14 +85,14 @@ class InboxIssueDetailSerializer(BaseSerializer):
return super().to_representation(instance) return super().to_representation(instance)
class InboxIssueLiteSerializer(BaseSerializer): class IntakeIssueLiteSerializer(BaseSerializer):
class Meta: class Meta:
model = InboxIssue model = IntakeIssue
fields = ["id", "status", "duplicate_to", "snoozed_till", "source"] fields = ["id", "status", "duplicate_to", "snoozed_till", "source"]
read_only_fields = fields read_only_fields = fields
class IssueStateInboxSerializer(BaseSerializer): class IssueStateIntakeSerializer(BaseSerializer):
state_detail = StateLiteSerializer(read_only=True, source="state") state_detail = StateLiteSerializer(read_only=True, source="state")
project_detail = ProjectLiteSerializer(read_only=True, source="project") project_detail = ProjectLiteSerializer(read_only=True, source="project")
label_details = LabelLiteSerializer( label_details = LabelLiteSerializer(
@ -102,7 +102,7 @@ class IssueStateInboxSerializer(BaseSerializer):
read_only=True, source="assignees", many=True read_only=True, source="assignees", many=True
) )
sub_issues_count = serializers.IntegerField(read_only=True) sub_issues_count = serializers.IntegerField(read_only=True)
issue_inbox = InboxIssueLiteSerializer(read_only=True, many=True) issue_intake = IntakeIssueLiteSerializer(read_only=True, many=True)
class Meta: class Meta:
model = Issue model = Issue

View file

@ -645,7 +645,7 @@ class IssueStateSerializer(DynamicBaseSerializer):
fields = "__all__" fields = "__all__"
class IssueInboxSerializer(DynamicBaseSerializer): class IssueIntakeSerializer(DynamicBaseSerializer):
label_ids = serializers.ListField( label_ids = serializers.ListField(
child=serializers.UUIDField(), child=serializers.UUIDField(),
required=False, required=False,

View file

@ -12,6 +12,7 @@ class NotificationSerializer(BaseSerializer):
read_only=True, source="triggered_by" read_only=True, source="triggered_by"
) )
is_inbox_issue = serializers.BooleanField(read_only=True) is_inbox_issue = serializers.BooleanField(read_only=True)
is_intake_issue = serializers.BooleanField(read_only=True)
is_mentioned_notification = serializers.BooleanField(read_only=True) is_mentioned_notification = serializers.BooleanField(read_only=True)
class Meta: class Meta:

View file

@ -22,6 +22,7 @@ class ProjectSerializer(BaseSerializer):
workspace_detail = WorkspaceLiteSerializer( workspace_detail = WorkspaceLiteSerializer(
source="workspace", read_only=True source="workspace", read_only=True
) )
inbox_view = serializers.BooleanField(read_only=True, source="intake_view")
class Meta: class Meta:
model = Project model = Project
@ -119,6 +120,7 @@ class ProjectListSerializer(DynamicBaseSerializer):
anchor = serializers.CharField(read_only=True) anchor = serializers.CharField(read_only=True)
members = serializers.SerializerMethodField() members = serializers.SerializerMethodField()
cover_image_url = serializers.CharField(read_only=True) cover_image_url = serializers.CharField(read_only=True)
inbox_view = serializers.BooleanField(read_only=True, source="intake_view")
def get_members(self, obj): def get_members(self, obj):
project_members = getattr(obj, "members_list", None) project_members = getattr(obj, "members_list", None)

View file

@ -5,7 +5,7 @@ from .cycle import urlpatterns as cycle_urls
from .dashboard import urlpatterns as dashboard_urls from .dashboard import urlpatterns as dashboard_urls
from .estimate import urlpatterns as estimate_urls from .estimate import urlpatterns as estimate_urls
from .external import urlpatterns as external_urls from .external import urlpatterns as external_urls
from .inbox import urlpatterns as inbox_urls from .intake import urlpatterns as intake_urls
from .issue import urlpatterns as issue_urls from .issue import urlpatterns as issue_urls
from .module import urlpatterns as module_urls from .module import urlpatterns as module_urls
from .notification import urlpatterns as notification_urls from .notification import urlpatterns as notification_urls
@ -25,7 +25,7 @@ urlpatterns = [
*dashboard_urls, *dashboard_urls,
*estimate_urls, *estimate_urls,
*external_urls, *external_urls,
*inbox_urls, *intake_urls,
*issue_urls, *issue_urls,
*module_urls, *module_urls,
*notification_urls, *notification_urls,

View file

@ -1,53 +0,0 @@
from django.urls import path
from plane.app.views import (
InboxViewSet,
InboxIssueViewSet,
)
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inboxes/",
InboxViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="inbox",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inboxes/<uuid:pk>/",
InboxViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="inbox",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/",
InboxIssueViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="inbox-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/<uuid:pk>/",
InboxIssueViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="inbox-issue",
),
]

View file

@ -0,0 +1,95 @@
from django.urls import path
from plane.app.views import (
IntakeViewSet,
IntakeIssueViewSet,
)
urlpatterns = [
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intakes/",
IntakeViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="intake",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intakes/<uuid:pk>/",
IntakeViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="intake",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intake-issues/",
IntakeIssueViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="intake-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/intake-issues/<uuid:pk>/",
IntakeIssueViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="intake-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inboxes/",
IntakeViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="inbox",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inboxes/<uuid:pk>/",
IntakeViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="inbox",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/",
IntakeIssueViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="inbox-issue",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/inbox-issues/<uuid:pk>/",
IntakeIssueViewSet.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="inbox-issue",
),
]

View file

@ -220,7 +220,7 @@ from .estimate.base import (
EstimatePointEndpoint, EstimatePointEndpoint,
) )
from .inbox.base import InboxViewSet, InboxIssueViewSet from .intake.base import IntakeViewSet, IntakeIssueViewSet
from .analytic.base import ( from .analytic.base import (
AnalyticsEndpoint, AnalyticsEndpoint,

View file

@ -12,6 +12,7 @@ from django.views.decorators.gzip import gzip_page
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
# Module imports # Module imports
from .. import BaseViewSet from .. import BaseViewSet
from plane.app.serializers import ( from plane.app.serializers import (
@ -39,6 +40,7 @@ from plane.utils.paginator import (
from plane.app.permissions import allow_permission, ROLE from plane.app.permissions import allow_permission, ROLE
class CycleIssueViewSet(BaseViewSet): class CycleIssueViewSet(BaseViewSet):
serializer_class = CycleIssueSerializer serializer_class = CycleIssueSerializer
model = CycleIssue model = CycleIssue
@ -194,10 +196,10 @@ class CycleIssueViewSet(BaseViewSet):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -223,10 +225,10 @@ class CycleIssueViewSet(BaseViewSet):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -18,8 +18,8 @@ from rest_framework.response import Response
from ..base import BaseViewSet from ..base import BaseViewSet
from plane.app.permissions import allow_permission, ROLE from plane.app.permissions import allow_permission, ROLE
from plane.db.models import ( from plane.db.models import (
Inbox, Intake,
InboxIssue, IntakeIssue,
Issue, Issue,
State, State,
IssueLink, IssueLink,
@ -31,18 +31,18 @@ from plane.db.models import (
from plane.app.serializers import ( from plane.app.serializers import (
IssueCreateSerializer, IssueCreateSerializer,
IssueSerializer, IssueSerializer,
InboxSerializer, IntakeSerializer,
InboxIssueSerializer, IntakeIssueSerializer,
InboxIssueDetailSerializer, IntakeIssueDetailSerializer,
) )
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
from plane.bgtasks.issue_activities_task import issue_activity from plane.bgtasks.issue_activities_task import issue_activity
class InboxViewSet(BaseViewSet): class IntakeViewSet(BaseViewSet):
serializer_class = InboxSerializer serializer_class = IntakeSerializer
model = Inbox model = Intake
def get_queryset(self): def get_queryset(self):
return ( return (
@ -54,8 +54,8 @@ class InboxViewSet(BaseViewSet):
) )
.annotate( .annotate(
pending_issue_count=Count( pending_issue_count=Count(
"issue_inbox", "issue_intake",
filter=Q(issue_inbox__status=-2), filter=Q(issue_intake__status=-2),
) )
) )
.select_related("workspace", "project") .select_related("workspace", "project")
@ -63,9 +63,9 @@ class InboxViewSet(BaseViewSet):
@allow_permission([ROLE.ADMIN, ROLE.MEMBER]) @allow_permission([ROLE.ADMIN, ROLE.MEMBER])
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
inbox = self.get_queryset().first() intake = self.get_queryset().first()
return Response( return Response(
InboxSerializer(inbox).data, IntakeSerializer(intake).data,
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
@ -75,26 +75,26 @@ class InboxViewSet(BaseViewSet):
@allow_permission([ROLE.ADMIN, ROLE.MEMBER]) @allow_permission([ROLE.ADMIN, ROLE.MEMBER])
def destroy(self, request, slug, project_id, pk): def destroy(self, request, slug, project_id, pk):
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
workspace__slug=slug, project_id=project_id, pk=pk workspace__slug=slug, project_id=project_id, pk=pk
).first() ).first()
# Handle default inbox delete # Handle default intake delete
if inbox.is_default: if intake.is_default:
return Response( return Response(
{"error": "You cannot delete the default inbox"}, {"error": "You cannot delete the default intake"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
inbox.delete() intake.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class InboxIssueViewSet(BaseViewSet): class IntakeIssueViewSet(BaseViewSet):
serializer_class = InboxIssueSerializer serializer_class = IntakeIssueSerializer
model = InboxIssue model = IntakeIssue
filterset_fields = [ filterset_fields = [
"status", "statulls",
] ]
def get_queryset(self): def get_queryset(self):
@ -107,8 +107,8 @@ class InboxIssueViewSet(BaseViewSet):
.prefetch_related("assignees", "labels", "issue_module__module") .prefetch_related("assignees", "labels", "issue_module__module")
.prefetch_related( .prefetch_related(
Prefetch( Prefetch(
"issue_inbox", "issue_intake",
queryset=InboxIssue.objects.only( queryset=IntakeIssue.objects.only(
"status", "duplicate_to", "snoozed_till", "source" "status", "duplicate_to", "snoozed_till", "source"
), ),
) )
@ -184,14 +184,14 @@ class InboxIssueViewSet(BaseViewSet):
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST]) @allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
def list(self, request, slug, project_id): def list(self, request, slug, project_id):
inbox_id = Inbox.objects.filter( intake_id = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
project = Project.objects.get(pk=project_id) project = Project.objects.get(pk=project_id)
filters = issue_filters(request.GET, "GET", "issue__") filters = issue_filters(request.GET, "GET", "issue__")
inbox_issue = ( intake_issue = (
InboxIssue.objects.filter( IntakeIssue.objects.filter(
inbox_id=inbox_id.id, project_id=project_id, **filters intake_id=intake_id.id, project_id=project_id, **filters
) )
.select_related("issue") .select_related("issue")
.prefetch_related( .prefetch_related(
@ -211,14 +211,14 @@ class InboxIssueViewSet(BaseViewSet):
) )
) )
).order_by(request.GET.get("order_by", "-issue__created_at")) ).order_by(request.GET.get("order_by", "-issue__created_at"))
# inbox status filter # Intake status filter
inbox_status = [ intake_status = [
item item
for item in request.GET.get("status", "-2").split(",") for item in request.GET.get("status", "-2").split(",")
if item != "null" if item != "null"
] ]
if inbox_status: if intake_status:
inbox_issue = inbox_issue.filter(status__in=inbox_status) intake_issue = intake_issue.filter(status__in=intake_status)
if ( if (
ProjectMember.objects.filter( ProjectMember.objects.filter(
@ -230,12 +230,12 @@ class InboxIssueViewSet(BaseViewSet):
).exists() ).exists()
and not project.guest_view_all_features and not project.guest_view_all_features
): ):
inbox_issue = inbox_issue.filter(created_by=request.user) intake_issue = intake_issue.filter(created_by=request.user)
return self.paginate( return self.paginate(
request=request, request=request,
queryset=(inbox_issue), queryset=(intake_issue),
on_results=lambda inbox_issues: InboxIssueSerializer( on_results=lambda intake_issues: IntakeIssueSerializer(
inbox_issues, intake_issues,
many=True, many=True,
).data, ).data,
) )
@ -261,16 +261,6 @@ class InboxIssueViewSet(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
# Create or get state
state, _ = State.objects.get_or_create(
name="Triage",
group="triage",
description="Default state for managing all Inbox Issues",
project_id=project_id,
color="#ff7700",
is_triage=True,
)
# create an issue # create an issue
project = Project.objects.get(pk=project_id) project = Project.objects.get(pk=project_id)
serializer = IssueCreateSerializer( serializer = IssueCreateSerializer(
@ -283,15 +273,15 @@ class InboxIssueViewSet(BaseViewSet):
) )
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
inbox_id = Inbox.objects.filter( intake_id = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
# create an inbox issue # create an intake issue
inbox_issue = InboxIssue.objects.create( intake_issue = IntakeIssue.objects.create(
inbox_id=inbox_id.id, intake_id=intake_id.id,
project_id=project_id, project_id=project_id,
issue_id=serializer.data["id"], issue_id=serializer.data["id"],
source=request.data.get("source", "in-app"), source=request.data.get("source", "IN-APP"),
) )
# Create an Issue Activity # Create an Issue Activity
issue_activity.delay( issue_activity.delay(
@ -304,10 +294,10 @@ class InboxIssueViewSet(BaseViewSet):
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"),
inbox=str(inbox_issue.id), intake=str(intake_issue.id),
) )
inbox_issue = ( intake_issue = (
InboxIssue.objects.select_related("issue") IntakeIssue.objects.select_related("issue")
.prefetch_related( .prefetch_related(
"issue__labels", "issue__labels",
"issue__assignees", "issue__assignees",
@ -339,12 +329,12 @@ class InboxIssueViewSet(BaseViewSet):
), ),
) )
.get( .get(
inbox_id=inbox_id.id, intake_id=intake_id.id,
issue_id=serializer.data["id"], issue_id=serializer.data["id"],
project_id=project_id, project_id=project_id,
) )
) )
serializer = InboxIssueDetailSerializer(inbox_issue) serializer = IntakeIssueDetailSerializer(intake_issue)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
else: else:
return Response( return Response(
@ -353,14 +343,14 @@ class InboxIssueViewSet(BaseViewSet):
@allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=Issue) @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=Issue)
def partial_update(self, request, slug, project_id, pk): def partial_update(self, request, slug, project_id, pk):
inbox_id = Inbox.objects.filter( intake_id = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
issue_id=pk, issue_id=pk,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
inbox_id=inbox_id, intake_id=intake_id,
) )
# Get the project member # Get the project member
project_member = ProjectMember.objects.get( project_member = ProjectMember.objects.get(
@ -370,11 +360,11 @@ class InboxIssueViewSet(BaseViewSet):
is_active=True, is_active=True,
) )
# Only project members admins and created_by users can access this endpoint # Only project members admins and created_by users can access this endpoint
if project_member.role <= 5 and str(inbox_issue.created_by_id) != str( if project_member.role <= 5 and str(intake_issue.created_by_id) != str(
request.user.id request.user.id
): ):
return Response( return Response(
{"error": "You cannot edit inbox issues"}, {"error": "You cannot edit intake issues"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
@ -405,7 +395,7 @@ class InboxIssueViewSet(BaseViewSet):
Value([], output_field=ArrayField(UUIDField())), Value([], output_field=ArrayField(UUIDField())),
), ),
).get( ).get(
pk=inbox_issue.issue_id, pk=intake_issue.issue_id,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
) )
@ -443,7 +433,7 @@ class InboxIssueViewSet(BaseViewSet):
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"),
inbox=str(inbox_issue.id), intake=str(intake_issue.id),
) )
issue_serializer.save() issue_serializer.save()
else: else:
@ -451,20 +441,20 @@ class InboxIssueViewSet(BaseViewSet):
issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST
) )
# Only project admins and members can edit inbox issue attributes # Only project admins and members can edit intake issue attributes
if project_member.role > 15: if project_member.role > 15:
serializer = InboxIssueSerializer( serializer = IntakeIssueSerializer(
inbox_issue, data=request.data, partial=True intake_issue, data=request.data, partial=True
) )
current_instance = json.dumps( current_instance = json.dumps(
InboxIssueSerializer(inbox_issue).data, cls=DjangoJSONEncoder IntakeIssueSerializer(intake_issue).data, cls=DjangoJSONEncoder
) )
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
# Update the issue state if the issue is rejected or marked as duplicate # Update the issue state if the issue is rejected or marked as duplicate
if serializer.data["status"] in [-1, 2]: if serializer.data["status"] in [-1, 2]:
issue = Issue.objects.get( issue = Issue.objects.get(
pk=inbox_issue.issue_id, pk=intake_issue.issue_id,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
) )
@ -480,7 +470,7 @@ class InboxIssueViewSet(BaseViewSet):
# Update the issue state if it is accepted # Update the issue state if it is accepted
if serializer.data["status"] in [1]: if serializer.data["status"] in [1]:
issue = Issue.objects.get( issue = Issue.objects.get(
pk=inbox_issue.issue_id, pk=intake_issue.issue_id,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
) )
@ -498,7 +488,7 @@ class InboxIssueViewSet(BaseViewSet):
issue.save() issue.save()
# create a activity for status change # create a activity for status change
issue_activity.delay( issue_activity.delay(
type="inbox.activity.created", type="intake.activity.created",
requested_data=json.dumps( requested_data=json.dumps(
request.data, cls=DjangoJSONEncoder request.data, cls=DjangoJSONEncoder
), ),
@ -509,11 +499,11 @@ class InboxIssueViewSet(BaseViewSet):
epoch=int(timezone.now().timestamp()), epoch=int(timezone.now().timestamp()),
notification=False, notification=False,
origin=request.META.get("HTTP_ORIGIN"), origin=request.META.get("HTTP_ORIGIN"),
inbox=(inbox_issue.id), intake=(intake_issue.id),
) )
inbox_issue = ( intake_issue = (
InboxIssue.objects.select_related("issue") IntakeIssue.objects.select_related("issue")
.prefetch_related( .prefetch_related(
"issue__labels", "issue__labels",
"issue__assignees", "issue__assignees",
@ -547,18 +537,18 @@ class InboxIssueViewSet(BaseViewSet):
), ),
) )
.get( .get(
inbox_id=inbox_id.id, intake_id=intake_id.id,
issue_id=pk, issue_id=pk,
project_id=project_id, project_id=project_id,
) )
) )
serializer = InboxIssueDetailSerializer(inbox_issue).data serializer = IntakeIssueDetailSerializer(intake_issue).data
return Response(serializer, status=status.HTTP_200_OK) return Response(serializer, 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:
serializer = InboxIssueDetailSerializer(inbox_issue).data serializer = IntakeIssueDetailSerializer(intake_issue).data
return Response(serializer, status=status.HTTP_200_OK) return Response(serializer, status=status.HTTP_200_OK)
@allow_permission( @allow_permission(
@ -571,12 +561,12 @@ class InboxIssueViewSet(BaseViewSet):
model=Issue, model=Issue,
) )
def retrieve(self, request, slug, project_id, pk): def retrieve(self, request, slug, project_id, pk):
inbox_id = Inbox.objects.filter( intake_id = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
project = Project.objects.get(pk=project_id) project = Project.objects.get(pk=project_id)
inbox_issue = ( intake_issue = (
InboxIssue.objects.select_related("issue") IntakeIssue.objects.select_related("issue")
.prefetch_related( .prefetch_related(
"issue__labels", "issue__labels",
"issue__assignees", "issue__assignees",
@ -605,7 +595,7 @@ class InboxIssueViewSet(BaseViewSet):
Value([], output_field=ArrayField(UUIDField())), Value([], output_field=ArrayField(UUIDField())),
), ),
) )
.get(inbox_id=inbox_id.id, issue_id=pk, project_id=project_id) .get(intake_id=intake_id.id, issue_id=pk, project_id=project_id)
) )
if ( if (
ProjectMember.objects.filter( ProjectMember.objects.filter(
@ -616,13 +606,13 @@ class InboxIssueViewSet(BaseViewSet):
is_active=True, is_active=True,
).exists() ).exists()
and not project.guest_view_all_features and not project.guest_view_all_features
and not inbox_issue.created_by == request.user and not intake_issue.created_by == request.user
): ):
return Response( return Response(
{"error": "You are not allowed to view this issue"}, {"error": "You are not allowed to view this issue"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
issue = InboxIssueDetailSerializer(inbox_issue).data issue = IntakeIssueDetailSerializer(intake_issue).data
return Response( return Response(
issue, issue,
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
@ -630,23 +620,23 @@ class InboxIssueViewSet(BaseViewSet):
@allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=Issue) @allow_permission(allowed_roles=[ROLE.ADMIN], creator=True, model=Issue)
def destroy(self, request, slug, project_id, pk): def destroy(self, request, slug, project_id, pk):
inbox_id = Inbox.objects.filter( intake_id = Intake.objects.filter(
workspace__slug=slug, project_id=project_id workspace__slug=slug, project_id=project_id
).first() ).first()
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
issue_id=pk, issue_id=pk,
workspace__slug=slug, workspace__slug=slug,
project_id=project_id, project_id=project_id,
inbox_id=inbox_id, intake_id=intake_id,
) )
# Check the issue status # Check the issue status
if inbox_issue.status in [-2, -1, 0, 2]: if intake_issue.status in [-2, -1, 0, 2]:
# Delete the issue also # Delete the issue also
issue = Issue.objects.filter( issue = Issue.objects.filter(
workspace__slug=slug, project_id=project_id, pk=pk workspace__slug=slug, project_id=project_id, pk=pk
).first() ).first()
issue.delete() issue.delete()
inbox_issue.delete() intake_issue.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)

View file

@ -171,10 +171,10 @@ class IssueArchiveViewSet(BaseViewSet):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -200,10 +200,10 @@ class IssueArchiveViewSet(BaseViewSet):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -336,10 +336,10 @@ class IssueViewSet(BaseViewSet):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -364,10 +364,10 @@ class IssueViewSet(BaseViewSet):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -160,10 +160,10 @@ class ModuleIssueViewSet(BaseViewSet):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -189,10 +189,10 @@ class ModuleIssueViewSet(BaseViewSet):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -53,9 +53,9 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
mentioned = request.GET.get("mentioned", False) mentioned = request.GET.get("mentioned", False)
q_filters = Q() q_filters = Q()
inbox_issue = Issue.objects.filter( intake_issue = Issue.objects.filter(
pk=OuterRef("entity_identifier"), pk=OuterRef("entity_identifier"),
issue_inbox__status__in=[0, 2, -2], issue_intake__status__in=[0, 2, -2],
workspace__slug=self.kwargs.get("slug"), workspace__slug=self.kwargs.get("slug"),
) )
@ -64,7 +64,8 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
workspace__slug=slug, receiver_id=request.user.id workspace__slug=slug, receiver_id=request.user.id
) )
.filter(entity_name="issue") .filter(entity_name="issue")
.annotate(is_inbox_issue=Exists(inbox_issue)) .annotate(is_inbox_issue=Exists(intake_issue))
.annotate(is_intake_issue=Exists(intake_issue))
.annotate( .annotate(
is_mentioned_notification=Case( is_mentioned_notification=Case(
When(sender__icontains="mentioned", then=True), When(sender__icontains="mentioned", then=True),

View file

@ -38,7 +38,7 @@ from plane.app.permissions import (
from plane.db.models import ( from plane.db.models import (
UserFavorite, UserFavorite,
Cycle, Cycle,
Inbox, Intake,
DeployBoard, DeployBoard,
IssueUserProperty, IssueUserProperty,
Issue, Issue,
@ -449,14 +449,14 @@ class ProjectViewSet(BaseViewSet):
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
if serializer.data["inbox_view"]: if serializer.data["intake_view"] or request.data.get("inbox_view", False):
inbox = Inbox.objects.filter( intake = Intake.objects.filter(
project=project, project=project,
is_default=True, is_default=True,
).first() ).first()
if not inbox: if not intake:
Inbox.objects.create( Intake.objects.create(
name=f"{project.name} Inbox", name=f"{project.name} Intake",
project=project, project=project,
is_default=True, is_default=True,
) )
@ -465,7 +465,7 @@ class ProjectViewSet(BaseViewSet):
State.objects.get_or_create( State.objects.get_or_create(
name="Triage", name="Triage",
group="triage", group="triage",
description="Default state for managing all Inbox Issues", description="Default state for managing all Intake Issues",
project_id=pk, project_id=pk,
color="#ff7700", color="#ff7700",
is_triage=True, is_triage=True,
@ -759,7 +759,7 @@ class DeployBoardViewSet(BaseViewSet):
def create(self, request, slug, project_id): def create(self, request, slug, project_id):
comments = request.data.get("is_comments_enabled", False) comments = request.data.get("is_comments_enabled", False)
reactions = request.data.get("is_reactions_enabled", False) reactions = request.data.get("is_reactions_enabled", False)
inbox = request.data.get("inbox", None) intake = request.data.get("intake", None)
votes = request.data.get("is_votes_enabled", False) votes = request.data.get("is_votes_enabled", False)
views = request.data.get( views = request.data.get(
"views", "views",
@ -777,7 +777,7 @@ class DeployBoardViewSet(BaseViewSet):
entity_identifier=project_id, entity_identifier=project_id,
project_id=project_id, project_id=project_id,
) )
project_deploy_board.inbox = inbox project_deploy_board.intake = intake
project_deploy_board.view_props = views project_deploy_board.view_props = views
project_deploy_board.is_votes_enabled = votes project_deploy_board.is_votes_enabled = votes
project_deploy_board.is_comments_enabled = comments project_deploy_board.is_comments_enabled = comments

View file

@ -370,10 +370,10 @@ class WorkspaceViewIssuesViewSet(BaseViewSet):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -399,10 +399,10 @@ class WorkspaceViewIssuesViewSet(BaseViewSet):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -204,10 +204,10 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -231,10 +231,10 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__isnull=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -30,8 +30,8 @@ from plane.db.models import (
Page, Page,
ProjectPage, ProjectPage,
PageLabel, PageLabel,
Inbox, Intake,
InboxIssue, IntakeIssue,
) )
@ -47,7 +47,7 @@ def create_project(workspace, user_id):
: random.randint(2, 12 if len(name) - 1 >= 12 else len(name) - 1) : random.randint(2, 12 if len(name) - 1 >= 12 else len(name) - 1)
].upper(), ].upper(),
created_by_id=user_id, created_by_id=user_id,
inbox_view=True, intake_view=True,
) )
# Add current member as project member # Add current member as project member
@ -406,18 +406,18 @@ def create_issues(workspace, project, user_id, issue_count):
return issues return issues
def create_inbox_issues(workspace, project, user_id, inbox_issue_count): def create_intake_issues(workspace, project, user_id, intake_issue_count):
issues = create_issues(workspace, project, user_id, inbox_issue_count) issues = create_issues(workspace, project, user_id, intake_issue_count)
inbox, create = Inbox.objects.get_or_create( intake, create = Intake.objects.get_or_create(
name="Inbox", name="Intake",
project=project, project=project,
is_default=True, is_default=True,
) )
InboxIssue.objects.bulk_create( IntakeIssue.objects.bulk_create(
[ [
InboxIssue( IntakeIssue(
issue=issue, issue=issue,
inbox=inbox, intake=intake,
status=(status := [-2, -1, 0, 1, 2][random.randint(0, 4)]), status=(status := [-2, -1, 0, 1, 2][random.randint(0, 4)]),
snoozed_till=( snoozed_till=(
datetime.now() + timedelta(days=random.randint(1, 30)) datetime.now() + timedelta(days=random.randint(1, 30))
@ -599,7 +599,7 @@ def create_dummy_data(
cycle_count, cycle_count,
module_count, module_count,
pages_count, pages_count,
inbox_issue_count, intake_issue_count,
): ):
workspace = Workspace.objects.get(slug=slug) workspace = Workspace.objects.get(slug=slug)
@ -660,12 +660,12 @@ def create_dummy_data(
issue_count=issue_count, issue_count=issue_count,
) )
# create inbox issues # create intake issues
create_inbox_issues( create_intake_issues(
workspace=workspace, workspace=workspace,
project=project, project=project,
user_id=user_id, user_id=user_id,
inbox_issue_count=inbox_issue_count, intake_issue_count=intake_issue_count,
) )
# create issue parent # create issue parent

View file

@ -1567,7 +1567,7 @@ def delete_draft_issue_activity(
) )
def create_inbox_activity( def create_intake_activity(
requested_data, requested_data,
current_instance, current_instance,
issue_id, issue_id,
@ -1596,8 +1596,8 @@ def create_inbox_activity(
issue_id=issue_id, issue_id=issue_id,
project_id=project_id, project_id=project_id,
workspace_id=workspace_id, workspace_id=workspace_id,
comment="updated the inbox status", comment="updated the intake status",
field="inbox", field="intake",
verb=requested_data.get("status"), verb=requested_data.get("status"),
actor_id=actor_id, actor_id=actor_id,
epoch=epoch, epoch=epoch,
@ -1620,7 +1620,7 @@ def issue_activity(
subscriber=True, subscriber=True,
notification=False, notification=False,
origin=None, origin=None,
inbox=None, intake=None,
): ):
try: try:
issue_activities = [] issue_activities = []
@ -1668,7 +1668,7 @@ def issue_activity(
"issue_draft.activity.created": create_draft_issue_activity, "issue_draft.activity.created": create_draft_issue_activity,
"issue_draft.activity.updated": update_draft_issue_activity, "issue_draft.activity.updated": update_draft_issue_activity,
"issue_draft.activity.deleted": delete_draft_issue_activity, "issue_draft.activity.deleted": delete_draft_issue_activity,
"inbox.activity.created": create_inbox_activity, "intake.activity.created": create_intake_activity,
} }
func = ACTIVITY_MAPPER.get(type) func = ACTIVITY_MAPPER.get(type)
@ -1695,12 +1695,12 @@ def issue_activity(
event=( event=(
"issue_comment" "issue_comment"
if activity.field == "comment" if activity.field == "comment"
else "inbox_issue" if inbox else "issue" else "intake_issue" if intake else "issue"
), ),
event_id=( event_id=(
activity.issue_comment_id activity.issue_comment_id
if activity.field == "comment" if activity.field == "comment"
else inbox if inbox else activity.issue_id else intake if intake else activity.issue_id
), ),
verb=activity.verb, verb=activity.verb,
field=( field=(

View file

@ -51,10 +51,10 @@ def archive_old_issues():
& Q(issue_module__isnull=False) & Q(issue_module__isnull=False)
), ),
).filter( ).filter(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True) | Q(issue_intake__isnull=True)
) )
# Check if Issues # Check if Issues
@ -129,10 +129,10 @@ def close_old_issues():
& Q(issue_module__isnull=False) & Q(issue_module__isnull=False)
), ),
).filter( ).filter(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True) | Q(issue_intake__isnull=True)
) )
# Check if Issues # Check if Issues

View file

@ -27,7 +27,7 @@ from plane.api.serializers import (
ModuleSerializer, ModuleSerializer,
ProjectSerializer, ProjectSerializer,
UserLiteSerializer, UserLiteSerializer,
InboxIssueSerializer, IntakeIssueSerializer,
) )
from plane.db.models import ( from plane.db.models import (
Cycle, Cycle,
@ -40,7 +40,7 @@ from plane.db.models import (
User, User,
Webhook, Webhook,
WebhookLog, WebhookLog,
InboxIssue, IntakeIssue,
) )
from plane.license.utils.instance_value import get_email_configuration from plane.license.utils.instance_value import get_email_configuration
from plane.utils.exception_logger import log_exception from plane.utils.exception_logger import log_exception
@ -54,7 +54,7 @@ SERIALIZER_MAPPER = {
"module_issue": ModuleIssueSerializer, "module_issue": ModuleIssueSerializer,
"issue_comment": IssueCommentSerializer, "issue_comment": IssueCommentSerializer,
"user": UserLiteSerializer, "user": UserLiteSerializer,
"inbox_issue": InboxIssueSerializer, "intake_issue": IntakeIssueSerializer,
} }
MODEL_MAPPER = { MODEL_MAPPER = {
@ -66,7 +66,7 @@ MODEL_MAPPER = {
"module_issue": ModuleIssue, "module_issue": ModuleIssue,
"issue_comment": IssueComment, "issue_comment": IssueComment,
"user": User, "user": User,
"inbox_issue": InboxIssue, "intake_issue": IntakeIssue,
} }

View file

@ -62,13 +62,15 @@ class Command(BaseCommand):
project_count = int(input("Number of projects to be created: ")) project_count = int(input("Number of projects to be created: "))
for i in range(project_count): 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: ")) issue_count = int(input("Number of issues to be created: "))
cycle_count = int(input("Number of cycles to be created: ")) cycle_count = int(input("Number of cycles to be created: "))
module_count = int(input("Number of modules to be created: ")) module_count = int(input("Number of modules to be created: "))
pages_count = int(input("Number of pages to be created: ")) pages_count = int(input("Number of pages to be created: "))
inbox_issue_count = int( intake_issue_count = int(
input("Number of inbox issues to be created: ") input("Number of intake issues to be created: ")
) )
from plane.bgtasks.dummy_data_task import create_dummy_data from plane.bgtasks.dummy_data_task import create_dummy_data
@ -81,7 +83,7 @@ class Command(BaseCommand):
cycle_count=cycle_count, cycle_count=cycle_count,
module_count=module_count, module_count=module_count,
pages_count=pages_count, pages_count=pages_count,
inbox_issue_count=inbox_issue_count, intake_issue_count=intake_issue_count,
) )
self.stdout.write( self.stdout.write(

View file

@ -0,0 +1,141 @@
# Generated by Django 4.2.15 on 2024-11-06 08:41
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('db', '0084_remove_label_label_unique_name_project_when_deleted_at_null_and_more'),
]
operations = [
migrations.RenameModel(
old_name="Inbox",
new_name="Intake",
),
migrations.AlterModelTable(
name="Intake",
table="intakes",
),
migrations.AlterModelOptions(
name="Intake",
options={
"verbose_name": "Intake",
"verbose_name_plural": "Intakes",
"ordering": ("name",),
},
),
migrations.AlterField(
model_name="Intake",
name="description",
field=models.TextField(
blank=True, verbose_name="Intake Description"
),
),
migrations.RenameModel(
old_name="InboxIssue",
new_name="IntakeIssue",
),
# Rename the 'inbox' field to 'intake'
migrations.RenameField(
model_name="IntakeIssue",
old_name="inbox",
new_name="intake",
),
# Update ForeignKey related_name for 'intake'
migrations.AlterField(
model_name="IntakeIssue",
name="intake",
field=models.ForeignKey(
"db.Intake",
related_name="issue_intake",
on_delete=django.db.models.deletion.CASCADE,
),
),
# Update ForeignKey related_name for 'issue'
migrations.AlterField(
model_name="IntakeIssue",
name="issue",
field=models.ForeignKey(
"db.Issue",
related_name="issue_intake",
on_delete=django.db.models.deletion.CASCADE,
),
),
# Update ForeignKey related_name for 'duplicate_to'
migrations.AlterField(
model_name="IntakeIssue",
name="duplicate_to",
field=models.ForeignKey(
"db.Issue",
related_name="intake_duplicate",
on_delete=django.db.models.deletion.SET_NULL,
null=True,
),
),
# Update Meta options
migrations.AlterModelOptions(
name="IntakeIssue",
options={
"verbose_name": "IntakeIssue",
"verbose_name_plural": "IntakeIssues",
"ordering": ("-created_at",),
},
),
# Update db_table
migrations.AlterModelTable(
name="IntakeIssue",
table="intake_issues",
),
migrations.RenameField(
model_name="project",
old_name="inbox_view",
new_name="intake_view",
),
migrations.RenameField(
model_name="deployboard",
old_name="inbox",
new_name="intake",
),
migrations.RenameField(
model_name="projectdeployboard",
old_name="inbox",
new_name="intake",
),
migrations.RemoveConstraint(
model_name="intake",
name="inbox_unique_name_project_when_deleted_at_null",
),
migrations.AlterField(
model_name="deployboard",
name="intake",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="publish_intake",
to="db.intake",
),
),
migrations.AlterField(
model_name="projectdeployboard",
name="intake",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="board_intake",
to="db.intake",
),
),
migrations.AddConstraint(
model_name="intake",
constraint=models.UniqueConstraint(
condition=models.Q(("deleted_at__isnull", True)),
fields=("name", "project"),
name="intake_unique_name_project_when_deleted_at_null",
),
),
]

View file

@ -15,7 +15,7 @@ from .draft import (
from .estimate import Estimate, EstimatePoint from .estimate import Estimate, EstimatePoint
from .exporter import ExporterHistory from .exporter import ExporterHistory
from .importer import Importer from .importer import Importer
from .inbox import Inbox, InboxIssue from .intake import Intake, IntakeIssue
from .integration import ( from .integration import (
GithubCommentSync, GithubCommentSync,
GithubIssueSync, GithubIssueSync,
@ -93,7 +93,7 @@ from .page import Page, PageLog, PageLabel
from .estimate import Estimate, EstimatePoint from .estimate import Estimate, EstimatePoint
from .inbox import Inbox, InboxIssue from .intake import Intake, IntakeIssue
from .analytic import AnalyticView from .analytic import AnalyticView

View file

@ -34,9 +34,9 @@ class DeployBoard(WorkspaceBaseModel):
) )
is_comments_enabled = models.BooleanField(default=False) is_comments_enabled = models.BooleanField(default=False)
is_reactions_enabled = models.BooleanField(default=False) is_reactions_enabled = models.BooleanField(default=False)
inbox = models.ForeignKey( intake = models.ForeignKey(
"db.Inbox", "db.Intake",
related_name="board_inbox", related_name="publish_intake",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
) )

View file

@ -5,17 +5,17 @@ from django.db import models
from plane.db.models.project import ProjectBaseModel from plane.db.models.project import ProjectBaseModel
class Inbox(ProjectBaseModel): class Intake(ProjectBaseModel):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
description = models.TextField( description = models.TextField(
verbose_name="Inbox Description", blank=True verbose_name="Intake Description", blank=True
) )
is_default = models.BooleanField(default=False) is_default = models.BooleanField(default=False)
view_props = models.JSONField(default=dict) view_props = models.JSONField(default=dict)
logo_props = models.JSONField(default=dict) logo_props = models.JSONField(default=dict)
def __str__(self): def __str__(self):
"""Return name of the Inbox""" """Return name of the intake"""
return f"{self.name} <{self.project.name}>" return f"{self.name} <{self.project.name}>"
class Meta: class Meta:
@ -24,21 +24,21 @@ class Inbox(ProjectBaseModel):
models.UniqueConstraint( models.UniqueConstraint(
fields=["name", "project"], fields=["name", "project"],
condition=models.Q(deleted_at__isnull=True), condition=models.Q(deleted_at__isnull=True),
name="inbox_unique_name_project_when_deleted_at_null", name="intake_unique_name_project_when_deleted_at_null",
) )
] ]
verbose_name = "Inbox" verbose_name = "Intake"
verbose_name_plural = "Inboxes" verbose_name_plural = "Intakes"
db_table = "inboxes" db_table = "intakes"
ordering = ("name",) ordering = ("name",)
class InboxIssue(ProjectBaseModel): class IntakeIssue(ProjectBaseModel):
inbox = models.ForeignKey( intake = models.ForeignKey(
"db.Inbox", related_name="issue_inbox", on_delete=models.CASCADE "db.Intake", related_name="issue_intake", on_delete=models.CASCADE
) )
issue = models.ForeignKey( issue = models.ForeignKey(
"db.Issue", related_name="issue_inbox", on_delete=models.CASCADE "db.Issue", related_name="issue_intake", on_delete=models.CASCADE
) )
status = models.IntegerField( status = models.IntegerField(
choices=( choices=(
@ -53,7 +53,7 @@ class InboxIssue(ProjectBaseModel):
snoozed_till = models.DateTimeField(null=True) snoozed_till = models.DateTimeField(null=True)
duplicate_to = models.ForeignKey( duplicate_to = models.ForeignKey(
"db.Issue", "db.Issue",
related_name="inbox_duplicate", related_name="intake_duplicate",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
) )
@ -69,11 +69,11 @@ class InboxIssue(ProjectBaseModel):
extra = models.JSONField(default=dict) extra = models.JSONField(default=dict)
class Meta: class Meta:
verbose_name = "InboxIssue" verbose_name = "IntakeIssue"
verbose_name_plural = "InboxIssues" verbose_name_plural = "IntakeIssues"
db_table = "inbox_issues" db_table = "intake_issues"
ordering = ("-created_at",) ordering = ("-created_at",)
def __str__(self): def __str__(self):
"""Return name of the Issue""" """Return name of the Issue"""
return f"{self.issue.name} <{self.inbox.name}>" return f"{self.issue.name} <{self.intake.name}>"

View file

@ -86,10 +86,10 @@ class IssueManager(SoftDeletionManager):
super() super()
.get_queryset() .get_queryset()
.filter( .filter(
models.Q(issue_inbox__status=1) models.Q(issue_intake__status=1)
| models.Q(issue_inbox__status=-1) | models.Q(issue_intake__status=-1)
| models.Q(issue_inbox__status=2) | models.Q(issue_intake__status=2)
| models.Q(issue_inbox__isnull=True) | models.Q(issue_intake__isnull=True)
) )
.filter(deleted_at__isnull=True) .filter(deleted_at__isnull=True)
.filter(state__is_triage=False) .filter(state__is_triage=False)

View file

@ -95,7 +95,7 @@ class Project(BaseModel):
cycle_view = models.BooleanField(default=True) cycle_view = models.BooleanField(default=True)
issue_views_view = models.BooleanField(default=True) issue_views_view = models.BooleanField(default=True)
page_view = models.BooleanField(default=True) page_view = models.BooleanField(default=True)
inbox_view = models.BooleanField(default=False) intake_view = models.BooleanField(default=False)
is_time_tracking_enabled = models.BooleanField(default=False) is_time_tracking_enabled = models.BooleanField(default=False)
is_issue_type_enabled = models.BooleanField(default=False) is_issue_type_enabled = models.BooleanField(default=False)
guest_view_all_features = models.BooleanField(default=False) guest_view_all_features = models.BooleanField(default=False)
@ -309,9 +309,9 @@ class ProjectDeployBoard(ProjectBaseModel):
) )
comments = models.BooleanField(default=False) comments = models.BooleanField(default=False)
reactions = models.BooleanField(default=False) reactions = models.BooleanField(default=False)
inbox = models.ForeignKey( intake = models.ForeignKey(
"db.Inbox", "db.Intake",
related_name="bord_inbox", related_name="board_intake",
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
) )

View file

@ -9,16 +9,16 @@ from .project import ProjectLiteSerializer
from .issue import IssueFlatSerializer, LabelLiteSerializer from .issue import IssueFlatSerializer, LabelLiteSerializer
from plane.db.models import ( from plane.db.models import (
Issue, Issue,
InboxIssue, IntakeIssue,
) )
class InboxIssueSerializer(BaseSerializer): class IntakeIssueSerializer(BaseSerializer):
issue_detail = IssueFlatSerializer(source="issue", read_only=True) issue_detail = IssueFlatSerializer(source="issue", read_only=True)
project_detail = ProjectLiteSerializer(source="project", read_only=True) project_detail = ProjectLiteSerializer(source="project", read_only=True)
class Meta: class Meta:
model = InboxIssue model = IntakeIssue
fields = "__all__" fields = "__all__"
read_only_fields = [ read_only_fields = [
"project", "project",
@ -26,14 +26,14 @@ class InboxIssueSerializer(BaseSerializer):
] ]
class InboxIssueLiteSerializer(BaseSerializer): class IntakeIssueLiteSerializer(BaseSerializer):
class Meta: class Meta:
model = InboxIssue model = IntakeIssue
fields = ["id", "status", "duplicate_to", "snoozed_till", "source"] fields = ["id", "status", "duplicate_to", "snoozed_till", "source"]
read_only_fields = fields read_only_fields = fields
class IssueStateInboxSerializer(BaseSerializer): class IssueStateIntakeSerializer(BaseSerializer):
state_detail = StateLiteSerializer(read_only=True, source="state") state_detail = StateLiteSerializer(read_only=True, source="state")
project_detail = ProjectLiteSerializer(read_only=True, source="project") project_detail = ProjectLiteSerializer(read_only=True, source="project")
label_details = LabelLiteSerializer( label_details = LabelLiteSerializer(
@ -44,7 +44,7 @@ class IssueStateInboxSerializer(BaseSerializer):
) )
sub_issues_count = serializers.IntegerField(read_only=True) sub_issues_count = serializers.IntegerField(read_only=True)
bridge_id = serializers.UUIDField(read_only=True) bridge_id = serializers.UUIDField(read_only=True)
issue_inbox = InboxIssueLiteSerializer(read_only=True, many=True) issue_intake = IntakeIssueLiteSerializer(read_only=True, many=True)
class Meta: class Meta:
model = Issue model = Issue

View file

@ -1,11 +1,11 @@
from .inbox import urlpatterns as inbox_urls from .intake import urlpatterns as intake_urls
from .issue import urlpatterns as issue_urls from .issue import urlpatterns as issue_urls
from .project import urlpatterns as project_urls from .project import urlpatterns as project_urls
from .asset import urlpatterns as asset_urls from .asset import urlpatterns as asset_urls
urlpatterns = [ urlpatterns = [
*inbox_urls, *intake_urls,
*issue_urls, *issue_urls,
*project_urls, *project_urls,
*asset_urls, *asset_urls,

View file

@ -2,15 +2,26 @@ from django.urls import path
from plane.space.views import ( from plane.space.views import (
InboxIssuePublicViewSet, IntakeIssuePublicViewSet,
IssueVotePublicViewSet,
WorkspaceProjectDeployBoardEndpoint, WorkspaceProjectDeployBoardEndpoint,
) )
urlpatterns = [ urlpatterns = [
path( path(
"anchor/<str:anchor>/inboxes/<uuid:inbox_id>/inbox-issues/", "anchor/<str:anchor>/intakes/<uuid:intake_id>/intake-issues/",
InboxIssuePublicViewSet.as_view( IntakeIssuePublicViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="intake-issue",
),
path(
"anchor/<str:anchor>/intakes/<uuid:intake_id>/inbox-issues/",
IntakeIssuePublicViewSet.as_view(
{ {
"get": "list", "get": "list",
"post": "create", "post": "create",
@ -19,15 +30,15 @@ urlpatterns = [
name="inbox-issue", name="inbox-issue",
), ),
path( path(
"anchor/<str:anchor>/inboxes/<uuid:inbox_id>/inbox-issues/<uuid:pk>/", "anchor/<str:anchor>/intakes/<uuid:intake_id>/intake-issues/<uuid:pk>/",
InboxIssuePublicViewSet.as_view( IntakeIssuePublicViewSet.as_view(
{ {
"get": "retrieve", "get": "retrieve",
"patch": "partial_update", "patch": "partial_update",
"delete": "destroy", "delete": "destroy",
} }
), ),
name="inbox-issue", name="intake-issue",
), ),
path( path(
"workspaces/<str:slug>/project-boards/", "workspaces/<str:slug>/project-boards/",

View file

@ -14,7 +14,7 @@ from .issue import (
ProjectIssuesPublicEndpoint, ProjectIssuesPublicEndpoint,
) )
from .inbox import InboxIssuePublicViewSet from .intake import IntakeIssuePublicViewSet
from .cycle import ProjectCyclesEndpoint from .cycle import ProjectCyclesEndpoint

View file

@ -13,7 +13,7 @@ from rest_framework.response import Response
# Module imports # Module imports
from .base import BaseViewSet from .base import BaseViewSet
from plane.db.models import ( from plane.db.models import (
InboxIssue, IntakeIssue,
Issue, Issue,
State, State,
IssueLink, IssueLink,
@ -22,17 +22,17 @@ from plane.db.models import (
) )
from plane.app.serializers import ( from plane.app.serializers import (
IssueSerializer, IssueSerializer,
InboxIssueSerializer, IntakeIssueSerializer,
IssueCreateSerializer, IssueCreateSerializer,
IssueStateInboxSerializer, IssueStateIntakeSerializer,
) )
from plane.utils.issue_filters import issue_filters from plane.utils.issue_filters import issue_filters
from plane.bgtasks.issue_activities_task import issue_activity from plane.bgtasks.issue_activities_task import issue_activity
class InboxIssuePublicViewSet(BaseViewSet): class IntakeIssuePublicViewSet(BaseViewSet):
serializer_class = InboxIssueSerializer serializer_class = IntakeIssueSerializer
model = InboxIssue model = IntakeIssue
filterset_fields = [ filterset_fields = [
"status", "status",
@ -52,34 +52,34 @@ class InboxIssuePublicViewSet(BaseViewSet):
| Q(snoozed_till__isnull=True), | Q(snoozed_till__isnull=True),
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"),
inbox_id=self.kwargs.get("inbox_id"), intake_id=self.kwargs.get("intake_id"),
) )
.select_related("issue", "workspace", "project") .select_related("issue", "workspace", "project")
) )
return InboxIssue.objects.none() return IntakeIssue.objects.none()
def list(self, request, anchor, inbox_id): def list(self, request, anchor, intake_id):
project_deploy_board = DeployBoard.objects.get( project_deploy_board = DeployBoard.objects.get(
anchor=anchor, entity_name="project" anchor=anchor, entity_name="project"
) )
if project_deploy_board.inbox is None: if project_deploy_board.intake is None:
return Response( return Response(
{"error": "Inbox is not enabled for this Project Board"}, {"error": "Intake is not enabled for this Project Board"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
filters = issue_filters(request.query_params, "GET") filters = issue_filters(request.query_params, "GET")
issues = ( issues = (
Issue.objects.filter( Issue.objects.filter(
issue_inbox__inbox_id=inbox_id, issue_intake__intake_id=intake_id,
workspace_id=project_deploy_board.workspace_id, workspace_id=project_deploy_board.workspace_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
) )
.filter(**filters) .filter(**filters)
.annotate(bridge_id=F("issue_inbox__id")) .annotate(bridge_id=F("issue_intake__id"))
.select_related("workspace", "project", "state", "parent") .select_related("workspace", "project", "state", "parent")
.prefetch_related("assignees", "labels") .prefetch_related("assignees", "labels")
.order_by("issue_inbox__snoozed_till", "issue_inbox__status") .order_by("issue_intake__snoozed_till", "issue_intake__status")
.annotate( .annotate(
sub_issues_count=Issue.issue_objects.filter( sub_issues_count=Issue.issue_objects.filter(
parent=OuterRef("id") parent=OuterRef("id")
@ -105,26 +105,26 @@ class InboxIssuePublicViewSet(BaseViewSet):
) )
.prefetch_related( .prefetch_related(
Prefetch( Prefetch(
"issue_inbox", "issue_intake",
queryset=InboxIssue.objects.only( queryset=IntakeIssue.objects.only(
"status", "duplicate_to", "snoozed_till", "source" "status", "duplicate_to", "snoozed_till", "source"
), ),
) )
) )
) )
issues_data = IssueStateInboxSerializer(issues, many=True).data issues_data = IssueStateIntakeSerializer(issues, many=True).data
return Response( return Response(
issues_data, issues_data,
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
def create(self, request, anchor, inbox_id): def create(self, request, anchor, intake_id):
project_deploy_board = DeployBoard.objects.get( project_deploy_board = DeployBoard.objects.get(
anchor=anchor, entity_name="project" anchor=anchor, entity_name="project"
) )
if project_deploy_board.inbox is None: if project_deploy_board.intake is None:
return Response( return Response(
{"error": "Inbox is not enabled for this Project Board"}, {"error": "Intake is not enabled for this Project Board"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
@ -151,7 +151,7 @@ class InboxIssuePublicViewSet(BaseViewSet):
state, _ = State.objects.get_or_create( state, _ = State.objects.get_or_create(
name="Triage", name="Triage",
group="backlog", group="backlog",
description="Default state for managing all Inbox Issues", description="Default state for managing all Intake Issues",
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
color="#ff7700", color="#ff7700",
) )
@ -178,37 +178,37 @@ class InboxIssuePublicViewSet(BaseViewSet):
current_instance=None, current_instance=None,
epoch=int(timezone.now().timestamp()), epoch=int(timezone.now().timestamp()),
) )
# create an inbox issue # create an intake issue
InboxIssue.objects.create( IntakeIssue.objects.create(
inbox_id=inbox_id, intake_id=intake_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
issue=issue, issue=issue,
source=request.data.get("source", "in-app"), source=request.data.get("source", "IN-APP"),
) )
serializer = IssueStateInboxSerializer(issue) serializer = IssueStateIntakeSerializer(issue)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
def partial_update(self, request, anchor, inbox_id, pk): def partial_update(self, request, anchor, intake_id, pk):
project_deploy_board = DeployBoard.objects.get( project_deploy_board = DeployBoard.objects.get(
anchor=anchor, entity_name="project" anchor=anchor, entity_name="project"
) )
if project_deploy_board.inbox is None: if project_deploy_board.intake is None:
return Response( return Response(
{"error": "Inbox is not enabled for this Project Board"}, {"error": "Intake is not enabled for this Project Board"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
pk=pk, pk=pk,
workspace_id=project_deploy_board.workspace_id, workspace_id=project_deploy_board.workspace_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
inbox_id=inbox_id, intake_id=intake_id,
) )
# Get the project member # Get the project member
if str(inbox_issue.created_by_id) != str(request.user.id): if str(intake_issue.created_by_id) != str(request.user.id):
return Response( return Response(
{"error": "You cannot edit inbox issues"}, {"error": "You cannot edit intake issues"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
@ -216,7 +216,7 @@ class InboxIssuePublicViewSet(BaseViewSet):
issue_data = request.data.pop("issue", False) issue_data = request.data.pop("issue", False)
issue = Issue.objects.get( issue = Issue.objects.get(
pk=inbox_issue.issue_id, pk=intake_issue.issue_id,
workspace_id=project_deploy_board.workspace_id, workspace_id=project_deploy_board.workspace_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
) )
@ -256,52 +256,52 @@ class InboxIssuePublicViewSet(BaseViewSet):
issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST issue_serializer.errors, status=status.HTTP_400_BAD_REQUEST
) )
def retrieve(self, request, anchor, inbox_id, pk): def retrieve(self, request, anchor, intake_id, pk):
project_deploy_board = DeployBoard.objects.get( project_deploy_board = DeployBoard.objects.get(
anchor=anchor, entity_name="project" anchor=anchor, entity_name="project"
) )
if project_deploy_board.inbox is None: if project_deploy_board.intake is None:
return Response( return Response(
{"error": "Inbox is not enabled for this Project Board"}, {"error": "Intake is not enabled for this Project Board"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
pk=pk, pk=pk,
workspace_id=project_deploy_board.workspace_id, workspace_id=project_deploy_board.workspace_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
inbox_id=inbox_id, intake_id=intake_id,
) )
issue = Issue.objects.get( issue = Issue.objects.get(
pk=inbox_issue.issue_id, pk=intake_issue.issue_id,
workspace_id=project_deploy_board.workspace_id, workspace_id=project_deploy_board.workspace_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
) )
serializer = IssueStateInboxSerializer(issue) serializer = IssueStateIntakeSerializer(issue)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
def destroy(self, request, anchor, inbox_id, pk): def destroy(self, request, anchor, intake_id, pk):
project_deploy_board = DeployBoard.objects.get( project_deploy_board = DeployBoard.objects.get(
anchor=anchor, entity_name="project" anchor=anchor, entity_name="project"
) )
if project_deploy_board.inbox is None: if project_deploy_board.intake is None:
return Response( return Response(
{"error": "Inbox is not enabled for this Project Board"}, {"error": "Intake is not enabled for this Project Board"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
inbox_issue = InboxIssue.objects.get( intake_issue = IntakeIssue.objects.get(
pk=pk, pk=pk,
workspace_id=project_deploy_board.workspace_id, workspace_id=project_deploy_board.workspace_id,
project_id=project_deploy_board.project_id, project_id=project_deploy_board.project_id,
inbox_id=inbox_id, intake_id=intake_id,
) )
if str(inbox_issue.created_by_id) != str(request.user.id): if str(intake_issue.created_by_id) != str(request.user.id):
return Response( return Response(
{"error": "You cannot delete inbox issue"}, {"error": "You cannot delete intake issue"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
inbox_issue.delete() intake_issue.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)

View file

@ -194,10 +194,10 @@ class ProjectIssuesPublicEndpoint(BaseAPIView):
group_by_field_name=group_by, group_by_field_name=group_by,
sub_group_by_field_name=sub_group_by, sub_group_by_field_name=sub_group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__status=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),
@ -222,10 +222,10 @@ class ProjectIssuesPublicEndpoint(BaseAPIView):
), ),
group_by_field_name=group_by, group_by_field_name=group_by,
count_filter=Q( count_filter=Q(
Q(issue_inbox__status=1) Q(issue_intake__status=1)
| Q(issue_inbox__status=-1) | Q(issue_intake__status=-1)
| Q(issue_inbox__status=2) | Q(issue_intake__status=2)
| Q(issue_inbox__isnull=True), | Q(issue_intake__status=True),
archived_at__isnull=True, archived_at__isnull=True,
is_draft=False, is_draft=False,
), ),

View file

@ -451,6 +451,27 @@ def filter_module(params, issue_filter, method, prefix=""):
return issue_filter return issue_filter
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"
]
if len(status) and "" not in status:
issue_filter[f"{prefix}issue_intake__status__in"] = status
else:
if (
params.get("intake_status", None)
and len(params.get("intake_status"))
and params.get("intake_status") != "null"
):
issue_filter[f"{prefix}issue_intake__status__in"] = params.get(
"inbox_status"
)
return issue_filter
def filter_inbox_status(params, issue_filter, method, prefix=""): def filter_inbox_status(params, issue_filter, method, prefix=""):
if method == "GET": if method == "GET":
status = [ status = [
@ -459,14 +480,14 @@ def filter_inbox_status(params, issue_filter, method, prefix=""):
if item != "null" if item != "null"
] ]
if len(status) and "" not in status: if len(status) and "" not in status:
issue_filter[f"{prefix}issue_inbox__status__in"] = status issue_filter[f"{prefix}issue_intake__status__in"] = status
else: else:
if ( if (
params.get("inbox_status", None) params.get("inbox_status", None)
and len(params.get("inbox_status")) and len(params.get("inbox_status"))
and params.get("inbox_status") != "null" and params.get("inbox_status") != "null"
): ):
issue_filter[f"{prefix}issue_inbox__status__in"] = params.get( issue_filter[f"{prefix}issue_intake__status__in"] = params.get(
"inbox_status" "inbox_status"
) )
return issue_filter return issue_filter
@ -562,6 +583,7 @@ def issue_filters(query_params, method, prefix=""):
"project": filter_project, "project": filter_project,
"cycle": filter_cycle, "cycle": filter_cycle,
"module": filter_module, "module": filter_module,
"intake_status": filter_intake_status,
"inbox_status": filter_inbox_status, "inbox_status": filter_inbox_status,
"sub_issue": filter_sub_issue_toggle, "sub_issue": filter_sub_issue_toggle,
"subscriber": filter_subscribed_issues, "subscriber": filter_subscribed_issues,