diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index e75c29c12..93dd9b90c 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -38,6 +38,8 @@ from plane.api.views import ( AddTeamToProjectEndpoint, UserLastProjectWithWorkspaceEndpoint, UserWorkspaceInvitationEndpoint, + UserActivityGraphEndpoint, + UserIssueCompletedGraphEndpoint, ## End Workspaces # File Assets FileAssetEndpoint, @@ -176,6 +178,18 @@ urlpatterns = [ name="workspace", ), # user join workspace + # User Graphs + path( + "users/me/workspaces//activity-graph/", + UserActivityGraphEndpoint.as_view(), + name="user-activity-graph", + ), + path( + "users/me/workspaces//issues-completed-graph/", + UserIssueCompletedGraphEndpoint.as_view(), + name="completed-graph", + ), + ## User Graph path( "users/me/invitations/workspaces///join/", JoinWorkspaceEndpoint.as_view(), diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 2556fc7d9..c59a4f92f 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -36,6 +36,8 @@ from .workspace import ( UserLastProjectWithWorkspaceEndpoint, WorkspaceMemberUserEndpoint, WorkspaceMemberUserViewsEndpoint, + UserActivityGraphEndpoint, + UserIssueCompletedGraphEndpoint, ) from .state import StateViewSet from .shortcut import ShortCutViewSet diff --git a/apiserver/plane/api/views/workspace.py b/apiserver/plane/api/views/workspace.py index cce222605..4ab921694 100644 --- a/apiserver/plane/api/views/workspace.py +++ b/apiserver/plane/api/views/workspace.py @@ -1,6 +1,7 @@ # Python imports import jwt -from datetime import datetime +from datetime import date +from dateutil.relativedelta import relativedelta # Django imports from django.db import IntegrityError @@ -10,8 +11,9 @@ from django.utils import timezone from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.contrib.sites.shortcuts import get_current_site -from django.db.models import CharField, Count, OuterRef, Func, F -from django.db.models.functions import Cast +from django.db.models import CharField, Count, OuterRef, Func, F, Q +from django.db.models.functions import Cast, ExtractWeek +from django.db.models.fields import DateField # Third party modules from rest_framework import status @@ -37,6 +39,8 @@ from plane.db.models import ( WorkspaceMemberInvite, Team, ProjectMember, + IssueActivity, + Issue, ) from plane.api.permissions import WorkSpaceBasePermission, WorkSpaceAdminPermission from plane.bgtasks.workspace_invitation_task import workspace_invitation @@ -578,3 +582,55 @@ class WorkspaceMemberUserViewsEndpoint(BaseAPIView): {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, ) + + +class UserActivityGraphEndpoint(BaseAPIView): + def get(self, request, slug): + try: + issue_activities = ( + IssueActivity.objects.filter( + actor=request.user, + workspace__slug=slug, + created_at__date__gte=date.today() + relativedelta(months=-3), + ) + .annotate(created_date=Cast("created_at", DateField())) + .values("created_date") + .annotate(activity_count=Count("created_date")) + .order_by("created_date") + ) + + return Response(issue_activities, 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 UserIssueCompletedGraphEndpoint(BaseAPIView): + def get(self, request, slug): + try: + month = request.GET.get("month", 1) + + issues = ( + Issue.objects.filter( + assignees__in=[request.user], + workspace__slug=slug, + completed_at__month=month, + completed_at__isnull=False, + ) + .annotate(completed_week=ExtractWeek("completed_at")) + .annotate(week=F("completed_week") % 4) + .values("week") + .annotate(completed_count=Count("completed_week")) + .order_by("week") + ) + + return Response(issues, 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, + )