From 770a9dd6931fb4d4fb6eb131feea6adf2d845846 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 17 Jul 2024 16:13:03 +0530 Subject: [PATCH 1/4] feat: recent visited --- apiserver/plane/app/views/cycle/base.py | 8 ++++ apiserver/plane/app/views/issue/base.py | 10 +++- apiserver/plane/app/views/module/base.py | 8 ++++ apiserver/plane/app/views/page/base.py | 9 +++- apiserver/plane/app/views/project/base.py | 9 ++++ apiserver/plane/app/views/view/base.py | 41 ++++++++++++++-- .../plane/bgtasks/recent_visited_task.py | 47 +++++++++++++++++++ apiserver/plane/db/models/__init__.py | 1 + apiserver/plane/db/models/issue.py | 35 +++++++++++++- 9 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 apiserver/plane/bgtasks/recent_visited_task.py diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 2583913aa57..48abd2a26eb 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -49,6 +49,7 @@ Project, ) from plane.utils.analytics_plot import burndown_plot +from plane.bgtasks.recent_visited_task import recent_visited_task # Module imports from .. import BaseAPIView, BaseViewSet @@ -768,6 +769,13 @@ def retrieve(self, request, slug, project_id, pk): queryset = ( self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk) ) + recent_visited_task.delay( + slug=slug, + entity_name="cycle", + entity_identifier=pk, + actor_id=request.user.id, + project_id=project_id, + ) data = ( self.get_queryset() .filter(pk=pk) diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index b9155f9d4a9..d472dc32ddc 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -58,8 +58,7 @@ ) from .. import BaseAPIView, BaseViewSet from plane.utils.user_timezone_converter import user_timezone_converter - -# Module imports +from plane.bgtasks.recent_visited_task import recent_visited_task class IssueListEndpoint(BaseAPIView): @@ -410,6 +409,13 @@ def create(self, request, slug, project_id): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, slug, project_id, pk=None): + recent_visited_task.delay( + slug=slug, + entity_name="issue", + entity_identifier=pk, + actor_id=request.user.id, + project_id=project_id, + ) issue = ( self.get_queryset() .filter(pk=pk) diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 08420459559..4333744ed42 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -53,6 +53,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): @@ -442,6 +443,13 @@ def retrieve(self, request, slug, project_id, pk): .values("count") ) ) + recent_visited_task.delay( + slug=slug, + entity_name="module", + entity_identifier=pk, + actor_id=request.user.id, + project_id=project_id, + ) estimate_type = Project.objects.filter( workspace__slug=slug, diff --git a/apiserver/plane/app/views/page/base.py b/apiserver/plane/app/views/page/base.py index 1b2944294c5..16d5bcdec99 100644 --- a/apiserver/plane/app/views/page/base.py +++ b/apiserver/plane/app/views/page/base.py @@ -39,7 +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): # Your SQL query @@ -210,6 +210,13 @@ def partial_update(self, request, slug, project_id, pk): def retrieve(self, request, slug, project_id, pk=None): page = self.get_queryset().filter(pk=pk).first() + recent_visited_task.delay( + slug=slug, + entity_name="page", + entity_identifier=pk, + actor_id=request.user.id, + project_id=project_id, + ) if page is None: return Response( {"error": "Page not found"}, diff --git a/apiserver/plane/app/views/project/base.py b/apiserver/plane/app/views/project/base.py index bca8236a98b..7ab8f37142a 100644 --- a/apiserver/plane/app/views/project/base.py +++ b/apiserver/plane/app/views/project/base.py @@ -50,6 +50,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): @@ -240,6 +241,14 @@ def retrieve(self, request, slug, pk): ) ).first() + recent_visited_task.delay( + slug=slug, + project_id=pk, + entity_name="project", + entity_identifier=pk, + actor_id=request.user.id, + ) + if project is None: return Response( {"error": "Project does not exist"}, diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 079430129a2..edbe343f442 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 ( ProjectEntityPermission, WorkspaceEntityPermission, @@ -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, ) @@ -111,6 +110,23 @@ def partial_update(self, request, slug, pk): serializer.errors, status=status.HTTP_400_BAD_REQUEST ) + 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=None, + entity_name="view", + entity_identifier=pk, + actor_id=request.user.id, + ) + return Response( + serializer.data, + status=status.HTTP_200_OK, + ) + def destroy(self, request, slug, pk): workspace_view = IssueView.objects.get( pk=pk, @@ -374,6 +390,23 @@ def list(self, request, slug, project_id): ).data return Response(views, status=status.HTTP_200_OK) + 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, + actor_id=request.user.id, + ) + return Response( + serializer.data, + status=status.HTTP_200_OK, + ) + def partial_update(self, request, slug, project_id, pk): with transaction.atomic(): issue_view = IssueView.objects.select_for_update().get( diff --git a/apiserver/plane/bgtasks/recent_visited_task.py b/apiserver/plane/bgtasks/recent_visited_task.py new file mode 100644 index 00000000000..216b1f51d8d --- /dev/null +++ b/apiserver/plane/bgtasks/recent_visited_task.py @@ -0,0 +1,47 @@ +# Python imports +from django.utils import timezone + +# Third party imports +from celery import shared_task + +# Module imports +from plane.db.models import RecentVisited, Workspace +from plane.utils.exception_logger import log_exception + + +@shared_task +def recent_visited_task( + entity_name, entity_identifier, actor_id, project_id, slug +): + try: + workspace = Workspace.objects.get(slug=slug) + recent_visited = RecentVisited.objects.filter( + entity_name=entity_name, + entity_identifier=entity_identifier, + actor_id=actor_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_activity = RecentVisited.objects.create( + entity_name=entity_name, + entity_identifier=entity_identifier, + actor_id=actor_id, + visited_at=timezone.now(), + project_id=project_id, + workspace_id=workspace.id, + ) + recent_activity.created_by_id = actor_id + recent_activity.updated_by_id = actor_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 cee0e18a2c1..ed4260eaee5 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -36,6 +36,7 @@ IssueSubscriber, IssueVote, Label, + RecentVisited, ) from .module import ( Module, diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 0c4373303f1..3053b4d8a2f 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -7,14 +7,13 @@ from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models, transaction -from django.db.models.signals import post_save -from django.dispatch import receiver from django.utils import timezone # Module imports from plane.utils.html_processor import strip_tags from .project import ProjectBaseModel +from .workspace import WorkspaceBaseModel def get_default_properties(): @@ -686,3 +685,35 @@ class Meta: def __str__(self): return f"{self.issue.name} {self.actor.email}" + + +class RecentVisited(WorkspaceBaseModel): + TYPE_CHOICES = ( + ("view", "View"), + ("page", "Page"), + ("issue", "Issue"), + ("cycle", "Cycle"), + ("module", "Module"), + ("project", "Project"), + ) + entity_identifier = models.UUIDField(null=True) + entity_name = models.CharField( + max_length=30, + choices=TYPE_CHOICES, + verbose_name="Type", + ) + actor = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="recent_visited", + ) + visited_at = models.DateTimeField(auto_now=True) + + class Meta: + verbose_name = "Recent Visited" + verbose_name_plural = "Recent Visits" + db_table = "recent_visits" + ordering = ("-created_at",) + + def __str__(self): + return f"{self.entity_name} {self.actor.email}" From fb733c329b4b45e72f94a2609f48038ddc910c13 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 18 Jul 2024 12:59:12 +0530 Subject: [PATCH 2/4] chore: recent visited 20 records --- apiserver/plane/bgtasks/recent_visited_task.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apiserver/plane/bgtasks/recent_visited_task.py b/apiserver/plane/bgtasks/recent_visited_task.py index 216b1f51d8d..d3851d89cf4 100644 --- a/apiserver/plane/bgtasks/recent_visited_task.py +++ b/apiserver/plane/bgtasks/recent_visited_task.py @@ -27,6 +27,16 @@ def recent_visited_task( recent_visited.visited_at = timezone.now() recent_visited.save(update_fields=["visited_at"]) else: + recent_visited_count = RecentVisited.objects.filter( + actor_id=actor_id, workspace_id=workspace.id + ).count() + + if recent_visited_count == 20: + recent_visited = RecentVisited.objects.filter( + actor_id=actor_id, workspace_id=workspace.id + ).order_by("created").first() + recent_visited.delete() + recent_activity = RecentVisited.objects.create( entity_name=entity_name, entity_identifier=entity_identifier, From 883df3d2f997bf0f698f3cd7b2bc07f92ed59124 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 24 Jul 2024 01:03:03 +0530 Subject: [PATCH 3/4] chore: removed the old table --- apiserver/plane/app/views/cycle/base.py | 14 ++++---- apiserver/plane/app/views/issue/base.py | 31 +++++++++++++---- apiserver/plane/app/views/module/base.py | 15 +++++---- apiserver/plane/app/views/page/base.py | 15 +++++---- apiserver/plane/app/views/project/base.py | 14 ++++---- apiserver/plane/app/views/view/base.py | 4 +-- .../plane/bgtasks/recent_visited_task.py | 32 ++++++++++-------- apiserver/plane/db/models/__init__.py | 3 +- apiserver/plane/db/models/issue.py | 33 ------------------- 9 files changed, 75 insertions(+), 86 deletions(-) diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 3ec5ea682e8..bc723003e60 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -775,13 +775,6 @@ def retrieve(self, request, slug, project_id, pk): queryset = ( self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk) ) - recent_visited_task.delay( - slug=slug, - entity_name="cycle", - entity_identifier=pk, - actor_id=request.user.id, - project_id=project_id, - ) data = ( self.get_queryset() .filter(pk=pk) @@ -1041,6 +1034,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 d472dc32ddc..1e6c36a14b8 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -133,6 +133,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 @@ -254,6 +262,14 @@ 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 group_by: if sub_group_by: if group_by == sub_group_by: @@ -409,13 +425,6 @@ def create(self, request, slug, project_id): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def retrieve(self, request, slug, project_id, pk=None): - recent_visited_task.delay( - slug=slug, - entity_name="issue", - entity_identifier=pk, - actor_id=request.user.id, - project_id=project_id, - ) issue = ( self.get_queryset() .filter(pk=pk) @@ -483,6 +492,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 4333744ed42..5367852196b 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -443,13 +443,6 @@ def retrieve(self, request, slug, project_id, pk): .values("count") ) ) - recent_visited_task.delay( - slug=slug, - entity_name="module", - entity_identifier=pk, - actor_id=request.user.id, - project_id=project_id, - ) estimate_type = Project.objects.filter( workspace__slug=slug, @@ -667,6 +660,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 7e8d667f810..5dc4dfa8da5 100644 --- a/apiserver/plane/app/views/page/base.py +++ b/apiserver/plane/app/views/page/base.py @@ -41,6 +41,7 @@ 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): # Your SQL query sql = """ @@ -210,13 +211,6 @@ def partial_update(self, request, slug, project_id, pk): def retrieve(self, request, slug, project_id, pk=None): page = self.get_queryset().filter(pk=pk).first() - recent_visited_task.delay( - slug=slug, - entity_name="page", - entity_identifier=pk, - actor_id=request.user.id, - project_id=project_id, - ) if page is None: return Response( {"error": "Page not found"}, @@ -228,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 7ab8f37142a..2e5dacfbc09 100644 --- a/apiserver/plane/app/views/project/base.py +++ b/apiserver/plane/app/views/project/base.py @@ -241,20 +241,20 @@ def retrieve(self, request, slug, pk): ) ).first() + if project is None: + return Response( + {"error": "Project does not exist"}, + status=status.HTTP_404_NOT_FOUND, + ) + recent_visited_task.delay( slug=slug, project_id=pk, entity_name="project", entity_identifier=pk, - actor_id=request.user.id, + user_id=request.user.id, ) - if project is None: - return Response( - {"error": "Project does not exist"}, - status=status.HTTP_404_NOT_FOUND, - ) - 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 edbe343f442..57e8ba7cdc1 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -120,7 +120,7 @@ def retrieve(self, request, slug, project_id, pk): project_id=None, entity_name="view", entity_identifier=pk, - actor_id=request.user.id, + user_id=request.user.id, ) return Response( serializer.data, @@ -400,7 +400,7 @@ def retrieve(self, request, slug, project_id, pk): project_id=project_id, entity_name="view", entity_identifier=pk, - actor_id=request.user.id, + user_id=request.user.id, ) return Response( serializer.data, diff --git a/apiserver/plane/bgtasks/recent_visited_task.py b/apiserver/plane/bgtasks/recent_visited_task.py index d3851d89cf4..9569abf5ef1 100644 --- a/apiserver/plane/bgtasks/recent_visited_task.py +++ b/apiserver/plane/bgtasks/recent_visited_task.py @@ -5,20 +5,20 @@ from celery import shared_task # Module imports -from plane.db.models import RecentVisited, Workspace +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, actor_id, project_id, slug + entity_name, entity_identifier, user_id, project_id, slug ): try: workspace = Workspace.objects.get(slug=slug) - recent_visited = RecentVisited.objects.filter( + recent_visited = UserRecentVisit.objects.filter( entity_name=entity_name, entity_identifier=entity_identifier, - actor_id=actor_id, + user_id=user_id, project_id=project_id, workspace_id=workspace.id, ).first() @@ -27,26 +27,30 @@ def recent_visited_task( recent_visited.visited_at = timezone.now() recent_visited.save(update_fields=["visited_at"]) else: - recent_visited_count = RecentVisited.objects.filter( - actor_id=actor_id, workspace_id=workspace.id - ).count() + recent_visited_count = UserRecentVisit.objects.filter( + user_id=user_id, workspace_id=workspace.id + ).count() if recent_visited_count == 20: - recent_visited = RecentVisited.objects.filter( - actor_id=actor_id, workspace_id=workspace.id - ).order_by("created").first() + recent_visited = ( + UserRecentVisit.objects.filter( + user_id=user_id, workspace_id=workspace.id + ) + .order_by("created_at") + .first() + ) recent_visited.delete() - recent_activity = RecentVisited.objects.create( + recent_activity = UserRecentVisit.objects.create( entity_name=entity_name, entity_identifier=entity_identifier, - actor_id=actor_id, + user_id=user_id, visited_at=timezone.now(), project_id=project_id, workspace_id=workspace.id, ) - recent_activity.created_by_id = actor_id - recent_activity.updated_by_id = actor_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"] ) diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index 307a79c7808..e7def641d5c 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -36,7 +36,6 @@ IssueSubscriber, IssueVote, Label, - RecentVisited, ) from .module import ( Module, @@ -112,4 +111,4 @@ from .issue_type import IssueType -from .recent_visit import UserRecentVisit \ No newline at end of file +from .recent_visit import UserRecentVisit diff --git a/apiserver/plane/db/models/issue.py b/apiserver/plane/db/models/issue.py index 979542c37f8..b4c2db9ff67 100644 --- a/apiserver/plane/db/models/issue.py +++ b/apiserver/plane/db/models/issue.py @@ -13,7 +13,6 @@ from plane.utils.html_processor import strip_tags from .project import ProjectBaseModel -from .workspace import WorkspaceBaseModel def get_default_properties(): @@ -687,35 +686,3 @@ class Meta: def __str__(self): return f"{self.issue.name} {self.actor.email}" - - -class RecentVisited(WorkspaceBaseModel): - TYPE_CHOICES = ( - ("view", "View"), - ("page", "Page"), - ("issue", "Issue"), - ("cycle", "Cycle"), - ("module", "Module"), - ("project", "Project"), - ) - entity_identifier = models.UUIDField(null=True) - entity_name = models.CharField( - max_length=30, - choices=TYPE_CHOICES, - verbose_name="Type", - ) - actor = models.ForeignKey( - settings.AUTH_USER_MODEL, - on_delete=models.CASCADE, - related_name="recent_visited", - ) - visited_at = models.DateTimeField(auto_now=True) - - class Meta: - verbose_name = "Recent Visited" - verbose_name_plural = "Recent Visits" - db_table = "recent_visits" - ordering = ("-created_at",) - - def __str__(self): - return f"{self.entity_name} {self.actor.email}" From 47d52c2c28a7e7e9180d527a0d56e9fecd5fc2da Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Wed, 24 Jul 2024 01:12:17 +0530 Subject: [PATCH 4/4] chore: view detail endpoint --- apiserver/plane/app/views/view/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apiserver/plane/app/views/view/base.py b/apiserver/plane/app/views/view/base.py index 57e8ba7cdc1..80529d2a187 100644 --- a/apiserver/plane/app/views/view/base.py +++ b/apiserver/plane/app/views/view/base.py @@ -110,9 +110,9 @@ def partial_update(self, request, slug, pk): serializer.errors, status=status.HTTP_400_BAD_REQUEST ) - def retrieve(self, request, slug, project_id, pk): + def retrieve(self, request, slug, pk): issue_view = ( - self.get_queryset().filter(pk=pk, project_id=project_id).first() + self.get_queryset().filter(pk=pk).first() ) serializer = IssueViewSerializer(issue_view) recent_visited_task.delay(