feat: module favourites for user (#370)

This commit is contained in:
pablohashescobar 2023-03-06 19:00:00 +05:30 committed by GitHub
parent cb8b6b43dc
commit d28fe930a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 150 additions and 4 deletions

View File

@ -45,6 +45,7 @@ from .module import (
ModuleSerializer, ModuleSerializer,
ModuleIssueSerializer, ModuleIssueSerializer,
ModuleLinkSerializer, ModuleLinkSerializer,
ModuleFavoriteSerializer,
) )
from .api_token import APITokenSerializer from .api_token import APITokenSerializer

View File

@ -7,7 +7,7 @@ from .user import UserLiteSerializer
from .project import ProjectSerializer from .project import ProjectSerializer
from .issue import IssueStateSerializer from .issue import IssueStateSerializer
from plane.db.models import User, Module, ModuleMember, ModuleIssue, ModuleLink from plane.db.models import User, Module, ModuleMember, ModuleIssue, ModuleLink, ModuleFavorite
class ModuleWriteSerializer(BaseSerializer): class ModuleWriteSerializer(BaseSerializer):
@ -135,6 +135,7 @@ class ModuleSerializer(BaseSerializer):
members_detail = UserLiteSerializer(read_only=True, many=True, source="members") members_detail = UserLiteSerializer(read_only=True, many=True, source="members")
issue_module = ModuleIssueSerializer(read_only=True, many=True) issue_module = ModuleIssueSerializer(read_only=True, many=True)
link_module = ModuleLinkSerializer(read_only=True, many=True) link_module = ModuleLinkSerializer(read_only=True, many=True)
is_favorite = serializers.BooleanField(read_only=True)
class Meta: class Meta:
model = Module model = Module
@ -147,3 +148,15 @@ class ModuleSerializer(BaseSerializer):
"created_at", "created_at",
"updated_at", "updated_at",
] ]
class ModuleFavoriteSerializer(BaseSerializer):
module_detail = ModuleFlatSerializer(source="module", read_only=True)
class Meta:
model = ModuleFavorite
fields = "__all__"
read_only_fields = [
"workspace",
"project",
"user",
]

View File

@ -90,6 +90,7 @@ from plane.api.views import (
# Modules # Modules
ModuleViewSet, ModuleViewSet,
ModuleIssueViewSet, ModuleIssueViewSet,
ModuleFavoriteViewSet,
## End Modules ## End Modules
# Api Tokens # Api Tokens
ApiTokenEndpoint, ApiTokenEndpoint,
@ -802,6 +803,25 @@ urlpatterns = [
), ),
name="project-issue-module-links", name="project-issue-module-links",
), ),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/user-favorite-modules/",
ModuleFavoriteViewSet.as_view(
{
"get": "list",
"post": "create",
}
),
name="user-favorite-module",
),
path(
"workspaces/<str:slug>/projects/<uuid:project_id>/user-favorite-modules/<uuid:module_id>/",
ModuleFavoriteViewSet.as_view(
{
"delete": "destroy",
}
),
name="user-favorite-module",
),
## End Modules ## End Modules
# API Tokens # API Tokens
path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"), path("api-tokens/", ApiTokenEndpoint.as_view(), name="api-tokens"),

View File

@ -80,7 +80,12 @@ from .authentication import (
MagicSignInGenerateEndpoint, MagicSignInGenerateEndpoint,
) )
from .module import ModuleViewSet, ModuleIssueViewSet, ModuleLinkViewSet from .module import (
ModuleViewSet,
ModuleIssueViewSet,
ModuleLinkViewSet,
ModuleFavoriteViewSet,
)
from .api_token import ApiTokenEndpoint from .api_token import ApiTokenEndpoint

View File

@ -3,7 +3,7 @@ import json
# Django Imports # Django Imports
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import Prefetch, F, OuterRef, Func from django.db.models import Prefetch, F, OuterRef, Func, Exists
from django.core import serializers from django.core import serializers
# Third party imports # Third party imports
@ -18,6 +18,7 @@ from plane.api.serializers import (
ModuleSerializer, ModuleSerializer,
ModuleIssueSerializer, ModuleIssueSerializer,
ModuleLinkSerializer, ModuleLinkSerializer,
ModuleFavoriteSerializer,
) )
from plane.api.permissions import ProjectEntityPermission from plane.api.permissions import ProjectEntityPermission
from plane.db.models import ( from plane.db.models import (
@ -26,6 +27,7 @@ from plane.db.models import (
Project, Project,
Issue, Issue,
ModuleLink, ModuleLink,
ModuleFavorite,
) )
from plane.bgtasks.issue_activites_task import issue_activity from plane.bgtasks.issue_activites_task import issue_activity
from plane.utils.grouper import group_results from plane.utils.grouper import group_results
@ -99,6 +101,23 @@ class ModuleViewSet(BaseViewSet):
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
def list(self, request, slug, project_id):
try:
subquery = ModuleFavorite.objects.filter(
user=self.request.user,
module_id=OuterRef("pk"),
project_id=project_id,
workspace__slug=slug,
)
modules = self.get_queryset().annotate(is_favorite=Exists(subquery))
return Response(ModuleSerializer(modules, many=True).data)
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): class ModuleIssueViewSet(BaseViewSet):
serializer_class = ModuleIssueSerializer serializer_class = ModuleIssueSerializer
@ -285,3 +304,65 @@ class ModuleLinkViewSet(BaseViewSet):
.filter(project__project_projectmember__member=self.request.user) .filter(project__project_projectmember__member=self.request.user)
.distinct() .distinct()
) )
class ModuleFavoriteViewSet(BaseViewSet):
serializer_class = ModuleFavoriteSerializer
model = ModuleFavorite
def get_queryset(self):
return self.filter_queryset(
super()
.get_queryset()
.filter(workspace__slug=self.kwargs.get("slug"))
.filter(user=self.request.user)
.select_related("module")
)
def create(self, request, slug, project_id):
try:
serializer = ModuleFavoriteSerializer(data=request.data)
if serializer.is_valid():
serializer.save(user=request.user, 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 module is already added to favorites"},
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,
)
except Exception as e:
capture_exception(e)
return Response(
{"error": "Something went wrong please try again later"},
status=status.HTTP_400_BAD_REQUEST,
)
def destroy(self, request, slug, project_id, module_id):
try:
module_favorite = ModuleFavorite.objects.get(
project=project_id,
user=request.user,
workspace__slug=slug,
module_id=module_id,
)
module_favorite.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
except ModuleFavorite.DoesNotExist:
return Response(
{"error": "Module is not in favorites"},
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,
)

View File

@ -45,7 +45,7 @@ from .shortcut import Shortcut
from .view import View from .view import View
from .module import Module, ModuleMember, ModuleIssue, ModuleLink from .module import Module, ModuleMember, ModuleIssue, ModuleLink, ModuleFavorite
from .api_token import APIToken from .api_token import APIToken

View File

@ -100,3 +100,29 @@ class ModuleLink(ProjectBaseModel):
def __str__(self): def __str__(self):
return f"{self.module.name} {self.url}" return f"{self.module.name} {self.url}"
class ModuleFavorite(ProjectBaseModel):
"""_summary_
ModuleFavorite (model): To store all the module favorite of the user
"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="module_favorites",
)
module = models.ForeignKey(
"db.Module", on_delete=models.CASCADE, related_name="module_favorites"
)
class Meta:
unique_together = ["module", "user"]
verbose_name = "Module Favorite"
verbose_name_plural = "Module Favorites"
db_table = "module_favorites"
ordering = ("-created_at",)
def __str__(self):
"""Return user and the module"""
return f"{self.user.email} <{self.module.name}>"