Compare commits

...

5 Commits

Author SHA1 Message Date
NarayanBavisetti
67777c1787 fix: added url and resolved bugs 2023-11-06 12:10:26 +05:30
NarayanBavisetti
05a6f972b2 Merge branch 'develop' of github.com:makeplane/plane into feat/bulk_issue_operations 2023-11-05 23:21:07 +05:30
NarayanBavisetti
76b3aaa0a1 chore: issue viewset change 2023-11-03 19:19:12 +05:30
NarayanBavisetti
e5eeb11899 Merge branch 'develop' of github.com:makeplane/plane into feat/bulk_issue_operations 2023-11-03 19:15:15 +05:30
pablohashescobar
374e52e75a feat: bulk issue operations 2023-09-21 14:20:45 +05:30
6 changed files with 250 additions and 0 deletions

View File

@ -21,6 +21,7 @@ from plane.api.views import (
IssueArchiveViewSet, IssueArchiveViewSet,
IssueRelationViewSet, IssueRelationViewSet,
IssueDraftViewSet, IssueDraftViewSet,
BulkIssueOperationsEndpoint,
) )
@ -74,6 +75,11 @@ urlpatterns = [
BulkCreateIssueLabelsEndpoint.as_view(), BulkCreateIssueLabelsEndpoint.as_view(),
name="project-bulk-labels", name="project-bulk-labels",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/bulk-operation-issues/",
BulkIssueOperationsEndpoint.as_view(),
name="bulk-issue-operation",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/bulk-delete-issues/", "workspaces/<str:slug>/projects/<uuid:project_id>/bulk-delete-issues/",
BulkDeleteIssuesEndpoint.as_view(), BulkDeleteIssuesEndpoint.as_view(),
@ -312,4 +318,5 @@ urlpatterns = [
), ),
name="project-issue-draft", name="project-issue-draft",
), ),
## End Issue Drafts
] ]

View File

@ -185,6 +185,9 @@ from plane.api.views import (
## Exporter ## Exporter
ExportIssuesEndpoint, ExportIssuesEndpoint,
## End Exporter ## End Exporter
# Bulk Issue Operations
BulkIssueOperationsEndpoint,
## End Bulk Issue Operations
# Configuration # Configuration
ConfigurationEndpoint, ConfigurationEndpoint,
## End Configuration ## End Configuration
@ -1730,6 +1733,11 @@ urlpatterns = [
name="workspace-project-boards", name="workspace-project-boards",
), ),
## End Public Boards ## End Public Boards
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/bulk-operation-issues/",
BulkIssueOperationsEndpoint.as_view(),
name="bulk-issue-operation",
),
# Configuration # Configuration
path( path(
"configs/", "configs/",

View File

@ -88,6 +88,7 @@ from .issue import (
IssueRetrievePublicEndpoint, IssueRetrievePublicEndpoint,
ProjectIssuesPublicEndpoint, ProjectIssuesPublicEndpoint,
IssueDraftViewSet, IssueDraftViewSet,
BulkIssueOperationsEndpoint,
) )
from .auth_extended import ( from .auth_extended import (

View File

@ -78,6 +78,8 @@ from plane.db.models import (
IssueVote, IssueVote,
IssueRelation, IssueRelation,
ProjectPublicMember, ProjectPublicMember,
IssueAssignee,
IssueLabel,
) )
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.grouper import group_results from plane.utils.grouper import group_results
@ -2243,3 +2245,215 @@ class IssueDraftViewSet(BaseViewSet):
epoch=int(timezone.now().timestamp()), epoch=int(timezone.now().timestamp()),
) )
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class BulkIssueOperationsEndpoint(BaseAPIView):
permission_classes = [
ProjectEntityPermission,
]
def post(self, request, slug, project_id):
issue_ids = request.data.get("issue_ids", [])
# Get all the issues
issues = (
Issue.objects.filter(
workspace__slug=slug, project_id=project_id, pk__in=issue_ids
)
.select_related("state")
.prefetch_related("labels", "assignees")
)
# Current epoch
epoch = int(timezone.now().timestamp())
# Project details
project = Project.objects.get(workspace__slug=slug, pk=project_id)
workspace_id = project.workspace_id
# Initialize arrays
bulk_update_issues = []
bulk_issue_activities = []
bulk_update_issue_labels = []
bulk_update_issue_assignees = []
properties = request.data.get("properties", {})
for issue in issues:
# Priority
if properties.get("priority", False):
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"priority": properties.get("priority")}
),
"current_instance": json.dumps(
{"priority": (issue.priority)}
),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
issue.priority = properties.get("priority")
# State
if properties.get("state", False):
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"state": properties.get("state")}
),
"current_instance": json.dumps({"state": str(issue.state_id)}),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
issue.state_id = properties.get("state")
# Start date
if properties.get("start_date", False):
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"start_date": properties.get("start_date")}
),
"current_instance": json.dumps(
{"start_date": str(issue.start_date)}
),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
issue.start_date = properties.get("start_date")
# Target date
if properties.get("target_date", False):
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"target_date": properties.get("target_date")}
),
"current_instance": json.dumps(
{"target_date": str(issue.target_date)}
),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
issue.target_date = properties.get("target_date")
# Estimate point
if properties.get("estimate_point", False):
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"estimate_point": properties.get("estimate_point")}
),
"current_instance": json.dumps(
{"estimate_point": (issue.estimate_point)}
),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
issue.estimate_point = properties.get("estimate_point")
bulk_update_issues.append(issue)
# Labels
if properties.get("labels", []):
for label_id in properties.get("labels", []):
bulk_update_issue_labels.append(
IssueLabel(
issue=issue,
label_id=label_id,
created_by=request.user,
project_id=project_id,
workspace_id=workspace_id,
)
)
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"labels": properties.get("labels", [])}
),
"current_instance": json.dumps(
{"labels": [str(label.id) for label in issue.labels.all()]}
),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
# Assignees
if properties.get("assignees", []):
for assignee_id in properties.get(
"assignees", issue.assignees
):
bulk_update_issue_assignees.append(
IssueAssignee(
issue=issue,
assignee_id=assignee_id,
created_by=request.user,
project_id=project_id,
workspace_id=workspace_id,
)
)
bulk_issue_activities.append(
{
"type": "issue.activity.updated",
"requested_data": json.dumps(
{"assignees": properties.get("assignees", [])}
),
"current_instance": json.dumps(
{
"assignees": [
str(assignee.id)
for assignee in issue.assignees.all()
]
}
),
"issue_id": str(issue.id),
"actor_id": str(request.user.id),
"project_id": str(project_id),
"epoch": epoch,
}
)
# Bulk update all the objects
Issue.objects.bulk_update(
bulk_update_issues,
["priority", "estimate_point", "start_date", "target_date", "state"],
batch_size=100,
)
# Create new labels
IssueLabel.objects.bulk_create(
bulk_update_issue_labels,
ignore_conflicts=True,
batch_size=100,
)
# Create new assignees
IssueAssignee.objects.bulk_create(
bulk_update_issue_assignees,
ignore_conflicts=True,
batch_size=100,
)
# update the issue activity
[issue_activity.delay(**activity) for activity in bulk_issue_activities]
return Response(status=status.HTTP_204_NO_CONTENT)

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.3 on 2023-09-21 08:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('db', '0046_auto_20230919_1421'),
]
operations = [
migrations.AlterUniqueTogether(
name='issuelabel',
unique_together={('issue', 'label')},
),
]

View File

@ -452,6 +452,7 @@ class IssueLabel(ProjectBaseModel):
) )
class Meta: class Meta:
unique_together = ["issue", "label"]
verbose_name = "Issue Label" verbose_name = "Issue Label"
verbose_name_plural = "Issue Labels" verbose_name_plural = "Issue Labels"
db_table = "issue_labels" db_table = "issue_labels"