From 7a991720a899e07bf6fe185b43edc9afdf039760 Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Tue, 20 Jun 2023 10:27:34 +0530 Subject: [PATCH] chore: add assignee, label and burndown plot in module details (#1313) * chore: add assignee, label and burndown plot in module details * dev: fix typo and key error * dev: add avatar in module retrieve --- apiserver/plane/api/views/module.py | 83 ++++++++++++++++++++++++- apiserver/plane/utils/analytics_plot.py | 67 +++++++++++++------- 2 files changed, 128 insertions(+), 22 deletions(-) diff --git a/apiserver/plane/api/views/module.py b/apiserver/plane/api/views/module.py index 7c285c242..5a235ba8f 100644 --- a/apiserver/plane/api/views/module.py +++ b/apiserver/plane/api/views/module.py @@ -37,7 +37,7 @@ from plane.db.models import ( from plane.bgtasks.issue_activites_task import issue_activity from plane.utils.grouper import group_results from plane.utils.issue_filters import issue_filters - +from plane.utils.analytics_plot import burndown_plot class ModuleViewSet(BaseViewSet): model = Module @@ -160,6 +160,87 @@ class ModuleViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) + def retrieve(self, request, slug, project_id, pk): + try: + queryset = self.get_queryset().get(pk=pk) + + assignee_distribution = ( + Issue.objects.filter( + issue_module__module_id=pk, + workspace__slug=slug, + project_id=project_id, + ) + .annotate(first_name=F("assignees__first_name")) + .annotate(last_name=F("assignees__last_name")) + .annotate(assignee_id=F("assignees__id")) + .annotate(avatar=F("assignees__avatar")) + .values("first_name", "last_name", "assignee_id", "avatar") + .annotate(total_issues=Count("assignee_id")) + .annotate( + completed_issues=Count( + "assignee_id", + filter=Q(completed_at__isnull=False), + ) + ) + .annotate( + pending_issues=Count( + "assignee_id", + filter=Q(completed_at__isnull=True), + ) + ) + .order_by("first_name", "last_name") + ) + + label_distribution = ( + Issue.objects.filter( + issue_module__module_id=pk, + workspace__slug=slug, + project_id=project_id, + ) + .annotate(label_name=F("labels__name")) + .annotate(color=F("labels__color")) + .annotate(label_id=F("labels__id")) + .values("label_name", "color", "label_id") + .annotate(total_issues=Count("label_id")) + .annotate( + completed_issues=Count( + "label_id", + filter=Q(completed_at__isnull=False), + ) + ) + .annotate( + pending_issues=Count( + "label_id", + filter=Q(completed_at__isnull=True), + ) + ) + .order_by("label_name") + ) + + data = ModuleSerializer(queryset).data + data["distribution"] = { + "assignees": assignee_distribution, + "labels": label_distribution, + "completion_chart": {}, + } + + if queryset.start_date and queryset.target_date: + data["distribution"]["completion_chart"] = burndown_plot( + queryset=queryset, slug=slug, project_id=project_id, module_id=pk + ) + + return Response( + data, + status=status.HTTP_200_OK, + ) + + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) + class ModuleIssueViewSet(BaseViewSet): serializer_class = ModuleIssueSerializer diff --git a/apiserver/plane/utils/analytics_plot.py b/apiserver/plane/utils/analytics_plot.py index 045e2bf26..033452e0d 100644 --- a/apiserver/plane/utils/analytics_plot.py +++ b/apiserver/plane/utils/analytics_plot.py @@ -81,30 +81,55 @@ def build_graph_plot(queryset, x_axis, y_axis, segment=None): return sorted_data -def burndown_plot(queryset, slug, project_id, cycle_id): - # Get all dates between the two dates - date_range = [ - queryset.start_date + timedelta(days=x) - for x in range((queryset.end_date - queryset.start_date).days + 1) - ] - - chart_data = {str(date): 0 for date in date_range} - - # Total Issues in Cycle +def burndown_plot(queryset, slug, project_id, cycle_id=None, module_id=None): + # Total Issues in Cycle or Module total_issues = queryset.total_issues - completed_issues_distribution = ( - Issue.objects.filter( - workspace__slug=slug, - project_id=project_id, - issue_cycle__cycle_id=cycle_id, + + if cycle_id: + # Get all dates between the two dates + date_range = [ + queryset.start_date + timedelta(days=x) + for x in range((queryset.end_date - queryset.start_date).days + 1) + ] + + chart_data = {str(date): 0 for date in date_range} + + completed_issues_distribution = ( + Issue.objects.filter( + workspace__slug=slug, + project_id=project_id, + issue_cycle__cycle_id=cycle_id, + ) + .annotate(date=TruncDate("completed_at")) + .values("date") + .annotate(total_completed=Count("id")) + .values("date", "total_completed") + .order_by("date") ) - .annotate(date=TruncDate("completed_at")) - .values("date") - .annotate(total_completed=Count("id")) - .values("date", "total_completed") - .order_by("date") - ) + + if module_id: + # Get all dates between the two dates + date_range = [ + queryset.start_date + timedelta(days=x) + for x in range((queryset.target_date - queryset.start_date).days + 1) + ] + + chart_data = {str(date): 0 for date in date_range} + + completed_issues_distribution = ( + Issue.objects.filter( + workspace__slug=slug, + project_id=project_id, + issue_module__module_id=module_id, + ) + .annotate(date=TruncDate("completed_at")) + .values("date") + .annotate(total_completed=Count("id")) + .values("date", "total_completed") + .order_by("date") + ) + for date in date_range: cumulative_pending_issues = total_issues