ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: очистка stale assignees Operational Core
This commit is contained in:
parent
fa4a533d29
commit
78deab1a23
|
|
@ -14,7 +14,7 @@ from drf_spectacular.utils import (
|
|||
# Module imports
|
||||
from .base import BaseAPIView
|
||||
from plane.api.serializers import UserLiteSerializer, ProjectMemberSerializer
|
||||
from plane.db.models import User, Workspace, WorkspaceMember, ProjectMember
|
||||
from plane.db.models import IssueAssignee, User, Workspace, WorkspaceMember, ProjectMember
|
||||
from plane.utils.permissions import ProjectMemberPermission, WorkSpaceAdminPermission, ProjectAdminPermission
|
||||
from plane.utils.openapi import (
|
||||
WORKSPACE_SLUG_PARAMETER,
|
||||
|
|
@ -205,6 +205,8 @@ class ProjectMemberDetailAPIEndpoint(ProjectMemberListCreateAPIEndpoint):
|
|||
serializer = ProjectMemberSerializer(project_member, data=request.data, partial=True, context={"slug": slug})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
if serializer.instance.is_active is False:
|
||||
IssueAssignee.objects.filter(project_id=project_id, assignee_id=serializer.instance.member_id).delete()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@extend_schema(
|
||||
|
|
@ -219,4 +221,5 @@ class ProjectMemberDetailAPIEndpoint(ProjectMemberListCreateAPIEndpoint):
|
|||
project_member = ProjectMember.objects.get(project_id=project_id, workspace__slug=slug, pk=pk)
|
||||
project_member.is_active = False
|
||||
project_member.save()
|
||||
IssueAssignee.objects.filter(project_id=project_id, assignee_id=project_member.member_id).delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
|
|
|||
|
|
@ -570,7 +570,15 @@ class IssueViewSet(BaseViewSet):
|
|||
Subquery(
|
||||
IssueAssignee.objects.filter(
|
||||
issue_id=OuterRef("pk"),
|
||||
deleted_at__isnull=True,
|
||||
assignee__is_active=True,
|
||||
assignee__member_workspace__workspace_id=OuterRef("workspace_id"),
|
||||
assignee__member_workspace__is_active=True,
|
||||
assignee__member_workspace__is_banned=False,
|
||||
assignee__member_workspace__deleted_at__isnull=True,
|
||||
assignee__member_project__project_id=OuterRef("project_id"),
|
||||
assignee__member_project__is_active=True,
|
||||
assignee__member_project__deleted_at__isnull=True,
|
||||
)
|
||||
.values("issue_id")
|
||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||
|
|
@ -676,7 +684,14 @@ class IssueViewSet(BaseViewSet):
|
|||
distinct=True,
|
||||
filter=Q(
|
||||
~Q(assignees__id__isnull=True)
|
||||
& Q(assignees__is_active=True)
|
||||
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||
& Q(assignees__member_workspace__is_active=True)
|
||||
& Q(assignees__member_workspace__is_banned=False)
|
||||
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||
& Q(assignees__member_project__project_id=F("project_id"))
|
||||
& Q(assignees__member_project__is_active=True)
|
||||
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||
& Q(issue_assignee__deleted_at__isnull=True)
|
||||
),
|
||||
),
|
||||
|
|
@ -980,7 +995,15 @@ class IssuePaginatedViewSet(BaseViewSet):
|
|||
Subquery(
|
||||
IssueAssignee.objects.filter(
|
||||
issue_id=OuterRef("pk"),
|
||||
deleted_at__isnull=True,
|
||||
assignee__is_active=True,
|
||||
assignee__member_workspace__workspace_id=OuterRef("workspace_id"),
|
||||
assignee__member_workspace__is_active=True,
|
||||
assignee__member_workspace__is_banned=False,
|
||||
assignee__member_workspace__deleted_at__isnull=True,
|
||||
assignee__member_project__project_id=OuterRef("project_id"),
|
||||
assignee__member_project__is_active=True,
|
||||
assignee__member_project__deleted_at__isnull=True,
|
||||
)
|
||||
.values("issue_id")
|
||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||
|
|
@ -1315,7 +1338,14 @@ class IssueDetailIdentifierEndpoint(BaseAPIView):
|
|||
distinct=True,
|
||||
filter=Q(
|
||||
~Q(assignees__id__isnull=True)
|
||||
& Q(assignees__is_active=True)
|
||||
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||
& Q(assignees__member_workspace__is_active=True)
|
||||
& Q(assignees__member_workspace__is_banned=False)
|
||||
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||
& Q(assignees__member_project__project_id=F("project_id"))
|
||||
& Q(assignees__member_project__is_active=True)
|
||||
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||
& Q(issue_assignee__deleted_at__isnull=True)
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from plane.app.serializers import (
|
|||
|
||||
from plane.app.permissions import WorkspaceUserPermission
|
||||
|
||||
from plane.db.models import Project, ProjectMember, ProjectUserProperty, WorkspaceMember
|
||||
from plane.db.models import IssueAssignee, Project, ProjectMember, ProjectUserProperty, WorkspaceMember
|
||||
from plane.bgtasks.project_add_user_email_task import project_add_user_email
|
||||
from plane.utils.host import base_host
|
||||
from plane.app.permissions.base import allow_permission, ROLE
|
||||
|
|
@ -295,6 +295,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||
|
||||
project_member.is_active = False
|
||||
project_member.save()
|
||||
IssueAssignee.objects.filter(project_id=project_id, assignee_id=project_member.member_id).delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@allow_permission([ROLE.ADMIN, ROLE.MEMBER, ROLE.GUEST])
|
||||
|
|
@ -323,6 +324,7 @@ class ProjectMemberViewSet(BaseViewSet):
|
|||
# Deactivate the user
|
||||
project_member.is_active = False
|
||||
project_member.save()
|
||||
IssueAssignee.objects.filter(project_id=project_id, assignee_id=project_member.member_id).delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from plane.app.serializers import (
|
|||
WorkSpaceMemberSerializer,
|
||||
)
|
||||
from plane.app.views.base import BaseAPIView
|
||||
from plane.db.models import Project, ProjectMember, WorkspaceMember, DraftIssue
|
||||
from plane.db.models import IssueAssignee, Project, ProjectMember, WorkspaceMember, DraftIssue
|
||||
from plane.utils.cache import invalidate_cache
|
||||
|
||||
from .. import BaseViewSet
|
||||
|
|
@ -144,6 +144,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||
_ = ProjectMember.objects.filter(
|
||||
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
IssueAssignee.objects.filter(workspace__slug=slug, assignee_id=workspace_member.member_id).delete()
|
||||
|
||||
workspace_member.is_active = False
|
||||
workspace_member.save()
|
||||
|
|
@ -198,6 +199,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
|||
_ = ProjectMember.objects.filter(
|
||||
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
IssueAssignee.objects.filter(workspace__slug=slug, assignee_id=workspace_member.member_id).delete()
|
||||
|
||||
# # Deactivate the user
|
||||
workspace_member.is_active = False
|
||||
|
|
|
|||
|
|
@ -9,7 +9,16 @@ from django.views import View
|
|||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from plane.authentication.views.nodedc_logout import is_internal_logout_request_authorized
|
||||
from plane.db.models import ExternalIdentityLink, Profile, Project, ProjectMember, User, Workspace, WorkspaceMember
|
||||
from plane.db.models import (
|
||||
ExternalIdentityLink,
|
||||
IssueAssignee,
|
||||
Profile,
|
||||
Project,
|
||||
ProjectMember,
|
||||
User,
|
||||
Workspace,
|
||||
WorkspaceMember,
|
||||
)
|
||||
|
||||
|
||||
OIDC_PROVIDER = "authentik"
|
||||
|
|
@ -384,6 +393,10 @@ class NodeDCInternalWorkspaceMembershipRemoveEndpoint(View):
|
|||
member=user,
|
||||
is_active=True,
|
||||
).update(is_active=False)
|
||||
IssueAssignee.objects.filter(
|
||||
workspace=workspace,
|
||||
assignee=user,
|
||||
).delete()
|
||||
membership.is_active = False
|
||||
membership.save(update_fields=["is_active", "updated_at"])
|
||||
|
||||
|
|
@ -534,6 +547,7 @@ class NodeDCInternalProjectMembershipRemoveEndpoint(View):
|
|||
|
||||
project_member.is_active = False
|
||||
project_member.save(update_fields=["is_active", "updated_at"])
|
||||
IssueAssignee.objects.filter(project=project, assignee=user).delete()
|
||||
|
||||
return JsonResponse(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from django.utils.dateparse import parse_datetime
|
|||
from plane.app.views.base import BaseAPIView
|
||||
from plane.license.api.permissions import InstanceAdminPermission
|
||||
from plane.db.models import (
|
||||
IssueAssignee,
|
||||
Project,
|
||||
ProjectMember,
|
||||
Workspace,
|
||||
|
|
@ -278,6 +279,7 @@ class InstanceWorkSpaceMemberEndpoint(BaseAPIView):
|
|||
member_id=workspace_member.member_id,
|
||||
is_active=True,
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
IssueAssignee.objects.filter(workspace_id=workspace_id, assignee_id=workspace_member.member_id).delete()
|
||||
|
||||
workspace_member.is_active = False
|
||||
workspace_member.save()
|
||||
|
|
@ -332,6 +334,7 @@ class InstanceWorkSpaceMemberBanEndpoint(BaseAPIView):
|
|||
member_id=workspace_member.member_id,
|
||||
is_active=True,
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
IssueAssignee.objects.filter(workspace_id=workspace_id, assignee_id=workspace_member.member_id).delete()
|
||||
|
||||
workspace_member.is_banned = True
|
||||
workspace_member.banned_at = timezone.now()
|
||||
|
|
|
|||
|
|
@ -33,8 +33,20 @@ def issue_queryset_grouper(
|
|||
"module_ids": "issue_module__module_id",
|
||||
}
|
||||
|
||||
valid_assignee_filter = (
|
||||
Q(issue_assignee__deleted_at__isnull=True)
|
||||
& Q(assignees__is_active=True)
|
||||
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||
& Q(assignees__member_workspace__is_active=True)
|
||||
& Q(assignees__member_workspace__is_banned=False)
|
||||
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||
& Q(assignees__member_project__project_id=F("project_id"))
|
||||
& Q(assignees__member_project__is_active=True)
|
||||
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||
)
|
||||
|
||||
GROUP_FILTER_MAPPER = {
|
||||
"assignees__id": Q(issue_assignee__deleted_at__isnull=True),
|
||||
"assignees__id": valid_assignee_filter,
|
||||
"labels__id": Q(label_issue__deleted_at__isnull=True),
|
||||
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
|
||||
}
|
||||
|
|
@ -46,7 +58,7 @@ def issue_queryset_grouper(
|
|||
annotations_map = {
|
||||
"assignee_ids": (
|
||||
"assignees__id",
|
||||
~Q(assignees__id__isnull=True) & Q(issue_assignee__deleted_at__isnull=True),
|
||||
~Q(assignees__id__isnull=True) & valid_assignee_filter,
|
||||
),
|
||||
"label_ids": (
|
||||
"labels__id",
|
||||
|
|
|
|||
|
|
@ -625,7 +625,14 @@ class IssueRetrievePublicEndpoint(BaseAPIView):
|
|||
distinct=True,
|
||||
filter=Q(
|
||||
~Q(assignees__id__isnull=True)
|
||||
& Q(assignees__is_active=True)
|
||||
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||
& Q(assignees__member_workspace__is_active=True)
|
||||
& Q(assignees__member_workspace__is_banned=False)
|
||||
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||
& Q(assignees__member_project__project_id=F("project_id"))
|
||||
& Q(assignees__member_project__is_active=True)
|
||||
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||
& Q(issue_assignee__deleted_at__isnull=True)
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# Django imports
|
||||
from django.contrib.postgres.aggregates import ArrayAgg
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db.models import Q, UUIDField, Value, QuerySet, OuterRef, Subquery
|
||||
from django.db.models import F, Q, UUIDField, Value, QuerySet, OuterRef, Subquery
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
# Module imports
|
||||
|
|
@ -37,8 +37,20 @@ def issue_queryset_grouper(
|
|||
"module_ids": "issue_module__module_id",
|
||||
}
|
||||
|
||||
valid_assignee_filter = (
|
||||
Q(issue_assignee__deleted_at__isnull=True)
|
||||
& Q(assignees__is_active=True)
|
||||
& Q(assignees__member_workspace__workspace_id=F("workspace_id"))
|
||||
& Q(assignees__member_workspace__is_active=True)
|
||||
& Q(assignees__member_workspace__is_banned=False)
|
||||
& Q(assignees__member_workspace__deleted_at__isnull=True)
|
||||
& Q(assignees__member_project__project_id=F("project_id"))
|
||||
& Q(assignees__member_project__is_active=True)
|
||||
& Q(assignees__member_project__deleted_at__isnull=True)
|
||||
)
|
||||
|
||||
GROUP_FILTER_MAPPER: Dict[str, Q] = {
|
||||
"assignees__id": Q(issue_assignee__deleted_at__isnull=True),
|
||||
"assignees__id": valid_assignee_filter,
|
||||
"labels__id": Q(label_issue__deleted_at__isnull=True),
|
||||
"issue_module__module_id": Q(issue_module__deleted_at__isnull=True),
|
||||
}
|
||||
|
|
@ -51,6 +63,14 @@ def issue_queryset_grouper(
|
|||
IssueAssignee.objects.filter(
|
||||
issue_id=OuterRef("pk"),
|
||||
deleted_at__isnull=True,
|
||||
assignee__is_active=True,
|
||||
assignee__member_workspace__workspace_id=OuterRef("workspace_id"),
|
||||
assignee__member_workspace__is_active=True,
|
||||
assignee__member_workspace__is_banned=False,
|
||||
assignee__member_workspace__deleted_at__isnull=True,
|
||||
assignee__member_project__project_id=OuterRef("project_id"),
|
||||
assignee__member_project__is_active=True,
|
||||
assignee__member_project__deleted_at__isnull=True,
|
||||
)
|
||||
.values("issue_id")
|
||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||
|
|
|
|||
|
|
@ -46,7 +46,10 @@ export const InternalContourKanbanCard = observer(function InternalContourKanban
|
|||
const { t } = useTranslation();
|
||||
const { isMobile } = usePlatformOS();
|
||||
const { workspaceSlug: routerWorkspaceSlug } = useParams();
|
||||
const { getUserDetails } = useMember();
|
||||
const {
|
||||
getUserDetails,
|
||||
project: { getProjectMemberIds },
|
||||
} = useMember();
|
||||
const { getProjectById } = useProject();
|
||||
const { getStateById, getProjectStateIds } = useProjectState();
|
||||
|
||||
|
|
@ -86,7 +89,11 @@ export const InternalContourKanbanCard = observer(function InternalContourKanban
|
|||
const checkerItemsTotal = issue.checker_items_count ?? 0;
|
||||
const checkerItemsCompleted = issue.checker_items_completed_count ?? 0;
|
||||
const hasCheckerProgress = checkerBlocksTotal > 0;
|
||||
const assigneeIds = issue.assignee_ids ?? [];
|
||||
const rawAssigneeIds = issue.assignee_ids ?? [];
|
||||
const projectMemberIds = issue.project_id ? getProjectMemberIds(issue.project_id, true) : null;
|
||||
const assigneeIds = projectMemberIds
|
||||
? rawAssigneeIds.filter((assigneeId) => projectMemberIds.includes(assigneeId))
|
||||
: rawAssigneeIds;
|
||||
const visibleAssigneeIds = assigneeIds.slice(0, 3);
|
||||
const assigneeCount = assigneeIds.length;
|
||||
const assigneeStackWidthClass =
|
||||
|
|
|
|||
Loading…
Reference in New Issue