chore: added estimate bulk endpoint for retrieving and updating (#919)

* chore: added estimate bulk endpoint for retrieving and updating

* chore: estimate endpoints

* fix: retrieve project estimate

* dev: handle integrity error check
This commit is contained in:
pablohashescobar 2023-04-22 01:04:20 +05:30 committed by GitHub
parent 0d264838a9
commit 33a904bc3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 167 deletions

View File

@ -68,4 +68,4 @@ from .importer import ImporterSerializer
from .page import PageSerializer, PageBlockSerializer, PageFavoriteSerializer from .page import PageSerializer, PageBlockSerializer, PageFavoriteSerializer
from .estimate import EstimateSerializer, EstimatePointSerializer from .estimate import EstimateSerializer, EstimatePointSerializer, EstimateReadSerializer

View File

@ -23,3 +23,16 @@ class EstimatePointSerializer(BaseSerializer):
"workspace", "workspace",
"project", "project",
] ]
class EstimateReadSerializer(BaseSerializer):
points = EstimatePointSerializer(read_only=True, many=True)
class Meta:
model = Estimate
fields = "__all__"
read_only_fields = [
"points",
"name",
"description",
]

View File

@ -82,8 +82,6 @@ from plane.api.views import (
StateDeleteIssueCheckEndpoint, StateDeleteIssueCheckEndpoint,
## End States ## End States
# Estimates # Estimates
EstimateViewSet,
EstimatePointViewSet,
ProjectEstimatePointEndpoint, ProjectEstimatePointEndpoint,
BulkEstimatePointEndpoint, BulkEstimatePointEndpoint,
## End Estimates ## End Estimates
@ -516,67 +514,34 @@ urlpatterns = [
name="state-delete-check", name="state-delete-check",
), ),
# End States ## # End States ##
# States # Estimates
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/",
EstimateViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="project-estimates",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:pk>/",
EstimateViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"patch": "partial_update",
"delete": "destroy",
}
),
name="project-estimates",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/estimate-points/",
EstimatePointViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="project-estimate-points",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/estimate-points/<uuid:pk>/",
EstimatePointViewSet.as_view(
{
"get": "retrieve",
"put": "update",
"patch": "partial_update",
"delete": "destroy",
}
),
name="project-estimates",
),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/project-estimates/", "workspaces/<str:slug>/projects/<uuid:project_id>/project-estimates/",
ProjectEstimatePointEndpoint.as_view(), ProjectEstimatePointEndpoint.as_view(),
name="project-estimate-points", name="project-estimate-points",
), ),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/bulk-estimate-points/", "workspaces/<str:slug>/projects/<uuid:project_id>/estimates/",
BulkEstimatePointEndpoint.as_view(), BulkEstimatePointEndpoint.as_view(
{
"get": "list",
"post": "create",
}
),
name="bulk-create-estimate-points", name="bulk-create-estimate-points",
), ),
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/bulk-estimate-points/", "workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/",
BulkEstimatePointEndpoint.as_view(), BulkEstimatePointEndpoint.as_view(
{
"get": "retrieve",
"patch": "partial_update",
"delete": "destroy",
}
),
name="bulk-create-estimate-points", name="bulk-create-estimate-points",
), ),
# End States ## # End Estimates ##
# Shortcuts # Shortcuts
path( path(
"workspaces/<str:slug>/projects/<uuid:project_id>/shortcuts/", "workspaces/<str:slug>/projects/<uuid:project_id>/shortcuts/",

View File

@ -133,8 +133,6 @@ from .search import GlobalSearchEndpoint, IssueSearchEndpoint
from .gpt import GPTIntegrationEndpoint from .gpt import GPTIntegrationEndpoint
from .estimate import ( from .estimate import (
EstimateViewSet,
EstimatePointViewSet,
ProjectEstimatePointEndpoint, ProjectEstimatePointEndpoint,
BulkEstimatePointEndpoint, BulkEstimatePointEndpoint,
) )

View File

@ -10,110 +10,11 @@ from sentry_sdk import capture_exception
from .base import BaseViewSet, BaseAPIView from .base import BaseViewSet, BaseAPIView
from plane.api.permissions import ProjectEntityPermission from plane.api.permissions import ProjectEntityPermission
from plane.db.models import Project, Estimate, EstimatePoint from plane.db.models import Project, Estimate, EstimatePoint
from plane.api.serializers import EstimateSerializer, EstimatePointSerializer from plane.api.serializers import (
EstimateSerializer,
EstimatePointSerializer,
class EstimateViewSet(BaseViewSet): EstimateReadSerializer,
permission_classes = [ )
ProjectEntityPermission,
]
model = Estimate
serializer_class = EstimateSerializer
def get_queryset(self):
return (
super()
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id"))
.filter(project__project_projectmember__member=self.request.user)
.select_related("project")
.select_related("workspace")
.distinct()
)
def perform_create(self, serializer):
serializer.save(project_id=self.kwargs.get("project_id"))
class EstimatePointViewSet(BaseViewSet):
permission_classes = [
ProjectEntityPermission,
]
model = EstimatePoint
serializer_class = EstimatePointSerializer
def get_queryset(self):
return (
super()
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(project_id=self.kwargs.get("project_id"))
.filter(project__project_projectmember__member=self.request.user)
.filter(estimate_id=self.kwargs.get("estimate_id"))
.select_related("project")
.select_related("workspace")
.distinct()
)
def perform_create(self, serializer):
serializer.save(
project_id=self.kwargs.get("project_id"),
estimate_id=self.kwargs.get("estimate_id"),
)
def create(self, request, slug, project_id, estimate_id):
try:
serializer = EstimatePointSerializer(data=request.data)
if serializer.is_valid():
serializer.save(estimate_id=estimate_id, project_id=project_id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except IntegrityError as e:
if "already exists" in str(e):
return Response(
{"error": "The estimate point is already taken"},
status=status.HTTP_410_GONE,
)
else:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def partial_update(self, request, slug, project_id, estimate_id, pk):
try:
estimate_point = EstimatePoint.objects.get(
pk=pk,
estimate_id=estimate_id,
project_id=project_id,
workspace__slug=slug,
)
serializer = EstimatePointSerializer(
estimate_point, data=request.data, partial=True
)
if serializer.is_valid():
serializer.save(estimate_id=estimate_id, project_id=project_id)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except EstimatePoint.DoesNotExist:
return Response(
{"error": "Estimate Point does not exist"},
status=status.HTTP_404_NOT_FOUND,
)
except IntegrityError as e:
if "already exists" in str(e):
return Response(
{"error": "The estimate point value is already taken"},
status=status.HTTP_410_GONE,
)
else:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
class ProjectEstimatePointEndpoint(BaseAPIView): class ProjectEstimatePointEndpoint(BaseAPIView):
@ -141,12 +42,28 @@ class ProjectEstimatePointEndpoint(BaseAPIView):
) )
class BulkEstimatePointEndpoint(BaseAPIView): class BulkEstimatePointEndpoint(BaseViewSet):
permission_classes = [ permission_classes = [
ProjectEntityPermission, ProjectEntityPermission,
] ]
model = Estimate
serializer_class = EstimateSerializer
def post(self, request, slug, project_id): def list(self, request, slug, project_id):
try:
estimates = Estimate.objects.filter(
workspace__slug=slug, project_id=project_id
).prefetch_related("points")
serializer = EstimateReadSerializer(estimates, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
except Exception as e:
print(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def create(self, request, slug, project_id):
try: try:
if not request.data.get("estimate", False): if not request.data.get("estimate", False):
return Response( return Response(
@ -215,14 +132,58 @@ class BulkEstimatePointEndpoint(BaseAPIView):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
def patch(self, request, slug, project_id, estimate_id): def retrieve(self, request, slug, project_id, estimate_id):
try: try:
estimate = Estimate.objects.get(
pk=estimate_id, workspace__slug=slug, project_id=project_id
)
serializer = EstimateReadSerializer(estimate)
return Response(
serializer.data,
status=status.HTTP_200_OK,
)
except Estimate.DoesNotExist:
return Response(
{"error": "Estimate does not exist"}, status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def partial_update(self, request, slug, project_id, estimate_id):
try:
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(
{"error": "Estimate points are required"}, {"error": "Estimate points are required"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
estimate = 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
)
try:
estimate = estimate_serializer.save()
except IntegrityError:
return Response(
{"errror": "Estimate with the name already exists"},
status=status.HTTP_400_BAD_REQUEST,
)
estimate_points_data = request.data.get("estimate_points", []) estimate_points_data = request.data.get("estimate_points", [])
estimate_points = EstimatePoint.objects.filter( estimate_points = EstimatePoint.objects.filter(
@ -242,18 +203,30 @@ class BulkEstimatePointEndpoint(BaseAPIView):
for point in estimate_points_data for point in estimate_points_data
if point.get("id") == str(estimate_point.id) if point.get("id") == str(estimate_point.id)
] ]
print(estimate_point_data)
if len(estimate_point_data): if len(estimate_point_data):
estimate_point.value = estimate_point_data[0].get( estimate_point.value = estimate_point_data[0].get(
"value", estimate_point.value "value", estimate_point.value
) )
updated_estimate_points.append(estimate_point) updated_estimate_points.append(estimate_point)
EstimatePoint.objects.bulk_update( try:
updated_estimate_points, ["value"], batch_size=10 EstimatePoint.objects.bulk_update(
updated_estimate_points, ["value"], batch_size=10
)
except IntegrityError as e:
return Response(
{"error": "Values need to be unique for each key"},
status=status.HTTP_400_BAD_REQUEST,
)
estimate_point_serializer = EstimatePointSerializer(estimate_points, many=True)
return Response(
{
"estimate": estimate_serializer.data,
"estimate_points": estimate_point_serializer.data,
},
status=status.HTTP_200_OK,
) )
serializer = EstimatePointSerializer(estimate_points, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
except Estimate.DoesNotExist: except Estimate.DoesNotExist:
return Response( return Response(
{"error": "Estimate does not exist"}, status=status.HTTP_400_BAD_REQUEST {"error": "Estimate does not exist"}, status=status.HTTP_400_BAD_REQUEST
@ -264,3 +237,17 @@ class BulkEstimatePointEndpoint(BaseAPIView):
{"error": "Something went wrong please try again later"}, {"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
def destroy(self, request, slug, project_id, estimate_id):
try:
estimate = Estimate.objects.get(
pk=estimate_id, workspace__slug=slug, project_id=project_id
)
estimate.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)