diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 55cb24b85b0..86f9de0ef62 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -49,6 +49,7 @@ ProjectMember, ) from plane.utils.analytics_plot import burndown_plot +from plane.bgtasks.recent_visited_task import recent_visited_task # Module imports from .. import BaseAPIView, BaseViewSet @@ -1028,6 +1029,13 @@ def retrieve(self, request, slug, project_id, pk): cycle_id=pk, ) + recent_visited_task.delay( + slug=slug, + entity_name="cycle", + entity_identifier=pk, + user_id=request.user.id, + project_id=project_id, + ) return Response( data, status=status.HTTP_200_OK, diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index 6a4f3ce2b74..429eb0b408d 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -56,6 +56,7 @@ ) from .. import BaseAPIView, BaseViewSet from plane.utils.user_timezone_converter import user_timezone_converter +from plane.bgtasks.recent_visited_task import recent_visited_task class IssueListEndpoint(BaseAPIView): @@ -127,6 +128,14 @@ def get(self, request, slug, project_id): sub_group_by=sub_group_by, ) + recent_visited_task.delay( + slug=slug, + project_id=project_id, + entity_name="project", + entity_identifier=project_id, + user_id=request.user.id, + ) + if self.fields or self.expand: issues = IssueSerializer( queryset, many=True, fields=self.fields, expand=self.expand @@ -247,6 +256,13 @@ def list(self, request, slug, project_id): sub_group_by=sub_group_by, ) + recent_visited_task.delay( + slug=slug, + project_id=project_id, + entity_name="project", + entity_identifier=project_id, + user_id=request.user.id, + ) if ProjectMember.objects.filter( workspace__slug=slug, project_id=project_id, @@ -484,6 +500,14 @@ def retrieve(self, request, slug, project_id, pk=None): status=status.HTTP_404_NOT_FOUND, ) + recent_visited_task.delay( + slug=slug, + entity_name="issue", + entity_identifier=pk, + user_id=request.user.id, + project_id=project_id, + ) + serializer = IssueDetailSerializer(issue, expand=self.expand) return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 6f19de6c2a4..975b6359cdf 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -55,6 +55,7 @@ from plane.utils.user_timezone_converter import user_timezone_converter from plane.bgtasks.webhook_task import model_activity from .. import BaseAPIView, BaseViewSet +from plane.bgtasks.recent_visited_task import recent_visited_task class ModuleViewSet(BaseViewSet): @@ -670,6 +671,14 @@ def retrieve(self, request, slug, project_id, pk): module_id=pk, ) + recent_visited_task.delay( + slug=slug, + entity_name="module", + entity_identifier=pk, + user_id=request.user.id, + project_id=project_id, + ) + return Response( data, status=status.HTTP_200_OK, diff --git a/apiserver/plane/app/views/page/base.py b/apiserver/plane/app/views/page/base.py index cda44b227b9..fdd1f2cca22 100644 --- a/apiserver/plane/app/views/page/base.py +++ b/apiserver/plane/app/views/page/base.py @@ -39,6 +39,7 @@ from plane.bgtasks.page_transaction_task import page_transaction from plane.bgtasks.page_version_task import page_version +from plane.bgtasks.recent_visited_task import recent_visited_task def unarchive_archive_page_and_descendants(page_id, archived_at): @@ -221,6 +222,13 @@ def retrieve(self, request, slug, project_id, pk=None): ).values_list("entity_identifier", flat=True) data = PageDetailSerializer(page).data data["issue_ids"] = issue_ids + recent_visited_task.delay( + slug=slug, + entity_name="page", + entity_identifier=pk, + user_id=request.user.id, + project_id=project_id, + ) return Response( data, status=status.HTTP_200_OK, diff --git a/apiserver/plane/app/views/project/base.py b/apiserver/plane/app/views/project/base.py index 2aec4b3bba2..f0497758977 100644 --- a/apiserver/plane/app/views/project/base.py +++ b/apiserver/plane/app/views/project/base.py @@ -52,6 +52,7 @@ ) from plane.utils.cache import cache_response from plane.bgtasks.webhook_task import model_activity +from plane.bgtasks.recent_visited_task import recent_visited_task class ProjectViewSet(BaseViewSet): @@ -264,6 +265,14 @@ def retrieve(self, request, slug, pk): status=status.HTTP_404_NOT_FOUND, ) + recent_visited_task.delay( + slug=slug, + project_id=pk, + entity_name="project", + entity_identifier=pk, + user_id=request.user.id, + ) + serializer = ProjectListSerializer(project) return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 96b4242a9c5..c16f7ef5d8f 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -13,12 +13,13 @@ from django.db.models.functions import Coalesce from django.utils.decorators import method_decorator from django.views.decorators.gzip import gzip_page -from rest_framework import status from django.db import transaction # Third party imports +from rest_framework import status from rest_framework.response import Response +# Module imports from plane.app.permissions import ( allow_permission, ROLE, @@ -46,10 +47,8 @@ GroupedOffsetPaginator, SubGroupedOffsetPaginator, ) - -# Module imports +from plane.bgtasks.recent_visited_task import recent_visited_task from .. import BaseViewSet - from plane.db.models import ( UserFavorite, ) @@ -134,6 +133,21 @@ def partial_update(self, request, slug, pk): serializer.errors, status=status.HTTP_400_BAD_REQUEST ) + def retrieve(self, request, slug, pk): + issue_view = self.get_queryset().filter(pk=pk).first() + serializer = IssueViewSerializer(issue_view) + recent_visited_task.delay( + slug=slug, + project_id=None, + entity_name="view", + entity_identifier=pk, + user_id=request.user.id, + ) + return Response( + serializer.data, + status=status.HTTP_200_OK, + ) + @allow_permission( allowed_roles=[ROLE.ADMIN], level="WORKSPACE", @@ -444,6 +458,27 @@ def list(self, request, slug, project_id): ).data return Response(views, status=status.HTTP_200_OK) + allow_permission( + allowed_roles=[ROLE.ADMIN, ROLE.MEMBER, ROLE.VIEWER, ROLE.GUEST] + ) + + def retrieve(self, request, slug, project_id, pk): + issue_view = ( + self.get_queryset().filter(pk=pk, project_id=project_id).first() + ) + serializer = IssueViewSerializer(issue_view) + recent_visited_task.delay( + slug=slug, + project_id=project_id, + entity_name="view", + entity_identifier=pk, + user_id=request.user.id, + ) + return Response( + serializer.data, + status=status.HTTP_200_OK, + ) + allow_permission(allowed_roles=[], creator=True, model=IssueView) def partial_update(self, request, slug, project_id, pk): diff --git a/apiserver/plane/bgtasks/recent_visited_task.py b/apiserver/plane/bgtasks/recent_visited_task.py new file mode 100644 index 00000000000..9569abf5ef1 --- /dev/null +++ b/apiserver/plane/bgtasks/recent_visited_task.py @@ -0,0 +1,61 @@ +# Python imports +from django.utils import timezone + +# Third party imports +from celery import shared_task + +# Module imports +from plane.db.models import UserRecentVisit, Workspace +from plane.utils.exception_logger import log_exception + + +@shared_task +def recent_visited_task( + entity_name, entity_identifier, user_id, project_id, slug +): + try: + workspace = Workspace.objects.get(slug=slug) + recent_visited = UserRecentVisit.objects.filter( + entity_name=entity_name, + entity_identifier=entity_identifier, + user_id=user_id, + project_id=project_id, + workspace_id=workspace.id, + ).first() + + if recent_visited: + recent_visited.visited_at = timezone.now() + recent_visited.save(update_fields=["visited_at"]) + else: + + recent_visited_count = UserRecentVisit.objects.filter( + user_id=user_id, workspace_id=workspace.id + ).count() + if recent_visited_count == 20: + recent_visited = ( + UserRecentVisit.objects.filter( + user_id=user_id, workspace_id=workspace.id + ) + .order_by("created_at") + .first() + ) + recent_visited.delete() + + recent_activity = UserRecentVisit.objects.create( + entity_name=entity_name, + entity_identifier=entity_identifier, + user_id=user_id, + visited_at=timezone.now(), + project_id=project_id, + workspace_id=workspace.id, + ) + recent_activity.created_by_id = user_id + recent_activity.updated_by_id = user_id + recent_activity.save( + update_fields=["created_by_id", "updated_by_id"] + ) + + return + except Exception as e: + log_exception(e) + return diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index 4874902a420..e7def641d5c 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -111,4 +111,4 @@ from .issue_type import IssueType -from .recent_visit import UserRecentVisit \ No newline at end of file +from .recent_visit import UserRecentVisit