Merge branch 'revamp-estimates' of gurusainath:makeplane/plane into revamp-estimates

This commit is contained in:
guru_sainath 2024-05-24 15:07:59 +05:30
commit 07e905f879
5 changed files with 110 additions and 35 deletions

View File

@ -4,6 +4,7 @@ from django.urls import path
from plane.app.views import ( from plane.app.views import (
ProjectEstimatePointEndpoint, ProjectEstimatePointEndpoint,
BulkEstimatePointEndpoint, BulkEstimatePointEndpoint,
DeleteEstimatePoint,
) )
@ -34,4 +35,9 @@ urlpatterns = [
), ),
name="bulk-create-estimate-points", name="bulk-create-estimate-points",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/estimate-point/<estimate_point_id>/",
DeleteEstimatePoint.as_view({"patch": "partial_update"}),
name="delete-estimate-points",
),
] ]

View File

@ -189,6 +189,7 @@ from .external.base import (
from .estimate.base import ( from .estimate.base import (
ProjectEstimatePointEndpoint, ProjectEstimatePointEndpoint,
BulkEstimatePointEndpoint, BulkEstimatePointEndpoint,
DeleteEstimatePoint,
) )
from .inbox.base import InboxViewSet, InboxIssueViewSet from .inbox.base import InboxViewSet, InboxIssueViewSet

View File

@ -1,3 +1,6 @@
import random
import string
# Third party imports # Third party imports
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
@ -5,7 +8,7 @@ from rest_framework import status
# Module imports # Module imports
from ..base import BaseViewSet, BaseAPIView from ..base import BaseViewSet, BaseAPIView
from plane.app.permissions import ProjectEntityPermission from plane.app.permissions import ProjectEntityPermission
from plane.db.models import Project, Estimate, EstimatePoint from plane.db.models import Project, Estimate, EstimatePoint, Issue
from plane.app.serializers import ( from plane.app.serializers import (
EstimateSerializer, EstimateSerializer,
EstimatePointSerializer, EstimatePointSerializer,
@ -13,6 +16,12 @@ from plane.app.serializers import (
) )
from plane.utils.cache import invalidate_cache from plane.utils.cache import invalidate_cache
def generate_random_name(length=10):
letters = string.ascii_lowercase
return "".join(random.choice(letters) for i in range(length))
class ProjectEstimatePointEndpoint(BaseAPIView): class ProjectEstimatePointEndpoint(BaseAPIView):
permission_classes = [ permission_classes = [
ProjectEntityPermission, ProjectEntityPermission,
@ -49,13 +58,14 @@ class BulkEstimatePointEndpoint(BaseViewSet):
serializer = EstimateReadSerializer(estimates, many=True) serializer = EstimateReadSerializer(estimates, many=True)
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
@invalidate_cache(path="/api/workspaces/:slug/estimates/", url_params=True, user=False) @invalidate_cache(
path="/api/workspaces/:slug/estimates/", url_params=True, user=False
)
def create(self, request, slug, project_id): def create(self, request, slug, project_id):
if not request.data.get("estimate", False): estimate_name = generate_random_name()
return Response( estimate = Estimate.objects.create(
{"error": "Estimate is required"}, name=estimate_name, project_id=project_id
status=status.HTTP_400_BAD_REQUEST, )
)
estimate_points = request.data.get("estimate_points", []) estimate_points = request.data.get("estimate_points", [])
@ -67,14 +77,6 @@ class BulkEstimatePointEndpoint(BaseViewSet):
serializer.errors, status=status.HTTP_400_BAD_REQUEST serializer.errors, status=status.HTTP_400_BAD_REQUEST
) )
estimate_serializer = EstimateSerializer(
data=request.data.get("estimate")
)
if not estimate_serializer.is_valid():
return Response(
estimate_serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
estimate = estimate_serializer.save(project_id=project_id)
estimate_points = EstimatePoint.objects.bulk_create( estimate_points = EstimatePoint.objects.bulk_create(
[ [
EstimatePoint( EstimatePoint(
@ -99,7 +101,6 @@ class BulkEstimatePointEndpoint(BaseViewSet):
return Response( return Response(
{ {
"estimate": estimate_serializer.data,
"estimate_points": estimate_point_serializer.data, "estimate_points": estimate_point_serializer.data,
}, },
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
@ -115,13 +116,10 @@ class BulkEstimatePointEndpoint(BaseViewSet):
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
@invalidate_cache(path="/api/workspaces/:slug/estimates/", url_params=True, user=False) @invalidate_cache(
path="/api/workspaces/:slug/estimates/", url_params=True, user=False
)
def partial_update(self, request, slug, project_id, estimate_id): def partial_update(self, request, slug, project_id, estimate_id):
if not request.data.get("estimate", False):
return Response(
{"error": "Estimate is required"},
status=status.HTTP_400_BAD_REQUEST,
)
if not len(request.data.get("estimate_points", [])): if not len(request.data.get("estimate_points", [])):
return Response( return Response(
@ -129,17 +127,7 @@ class BulkEstimatePointEndpoint(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
estimate = Estimate.objects.get(pk=estimate_id) _ = Estimate.objects.get(pk=estimate_id)
estimate_serializer = EstimateSerializer(
estimate, data=request.data.get("estimate"), partial=True
)
if not estimate_serializer.is_valid():
return Response(
estimate_serializer.errors, status=status.HTTP_400_BAD_REQUEST
)
estimate = estimate_serializer.save()
estimate_points_data = request.data.get("estimate_points", []) estimate_points_data = request.data.get("estimate_points", [])
@ -178,16 +166,65 @@ class BulkEstimatePointEndpoint(BaseViewSet):
) )
return Response( return Response(
{ {
"estimate": estimate_serializer.data,
"estimate_points": estimate_point_serializer.data, "estimate_points": estimate_point_serializer.data,
}, },
status=status.HTTP_200_OK, status=status.HTTP_200_OK,
) )
@invalidate_cache(path="/api/workspaces/:slug/estimates/", url_params=True, user=False) @invalidate_cache(
path="/api/workspaces/:slug/estimates/", url_params=True, user=False
)
def destroy(self, request, slug, project_id, estimate_id): def destroy(self, request, slug, project_id, estimate_id):
estimate = Estimate.objects.get( estimate = Estimate.objects.get(
pk=estimate_id, workspace__slug=slug, project_id=project_id pk=estimate_id, workspace__slug=slug, project_id=project_id
) )
estimate.delete() estimate.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class DeleteEstimatePoint(BaseViewSet):
permission_classes = [
ProjectEntityPermission,
]
def partial_update(
self, request, slug, project_id, estimate_id, estimate_point_id
):
new_estimate_id = request.data.get("new_estimate_id", None)
estimate_points = EstimatePoint.objects.filter(
estimate_id=estimate_id,
project_id=project_id,
workspace__slug=slug,
)
# update all the issues with the new estimate
if new_estimate_id:
_ = Issue.objects.filter(
project_id=project_id,
workspace__slug=slug,
estimate_id=estimate_point_id,
).update(estimate_id=new_estimate_id)
# delete the estimate point
old_estimate_point = EstimatePoint.objects.filter(
pk=estimate_point_id
).first()
# rearrange the estimate points
updated_estimate_points = []
for estimate_point in estimate_points:
if estimate_point.key > old_estimate_point.key:
estimate_point.key -= 1
updated_estimate_points.append(estimate_point)
EstimatePoint.objects.bulk_update(
updated_estimate_points,
["key"],
batch_size=10,
)
old_estimate_point.delete()
return Response(
EstimatePointSerializer(updated_estimate_points, many=True).data,
status=status.HTTP_200_OK,
)

View File

@ -0,0 +1,24 @@
# Generated by Django 4.2.7 on 2024-05-22 09:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("db", "0065_auto_20240415_0937"),
]
operations = [
migrations.AddField(
model_name="issue",
name="estimate",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="issue_estimate",
to="db.estimatepoint",
),
),
]

View File

@ -124,6 +124,13 @@ class Issue(ProjectBaseModel):
null=True, null=True,
blank=True, blank=True,
) )
estimate = models.ForeignKey(
"db.Estimate",
on_delete=models.SET_NULL,
related_name="issue_estimate",
null=True,
blank=True,
)
name = models.CharField(max_length=255, verbose_name="Issue Name") name = models.CharField(max_length=255, verbose_name="Issue Name")
description = models.JSONField(blank=True, default=dict) description = models.JSONField(blank=True, default=dict)
description_html = models.TextField(blank=True, default="<p></p>") description_html = models.TextField(blank=True, default="<p></p>")