You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

394 lines
15 KiB

from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework.generics import CreateAPIView, DestroyAPIView
from rest_framework.exceptions import ValidationError
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from apps.bookmark.models import Bookmark
from apps.bookmark.serializers import BookmarkSerializer
class AddBookmarkView(CreateAPIView):
"""
Add a bookmark for a specific content in a service.
If the bookmark already exists but is inactive, it will be reactivated.
"""
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
serializer_class = BookmarkSerializer
@swagger_auto_schema(
operation_description="Add a bookmark for a specific content in a service.\
If the bookmark already exists but is inactive, it will be reactivated.\
\n Services must be choose from : \n \
'Library'\n'Podcast'\n'Podcast Playlist'\n'Hadith' \n 'Hadith Correction'\n'Video'\n'Video Playlist'\n'Article'",
tags=["Dobodbi - Bookmarks"],
request_body=BookmarkSerializer,
responses={
201: "Bookmark created successfully.",
200: "Bookmark reactivated or already active.",
400: "Invalid input."
}
)
def post(self, request, *args, **kwargs):
return super().post(request,*args, **kwargs)
def create(self, request, *args, **kwargs):
service = request.data.get('service')
content_id = request.data.get('content_id')
if not service or not content_id:
return Response(
{'error': 'Both service and content_id are required'},
status=status.HTTP_400_BAD_REQUEST
)
# Check if the bookmark already exists
bookmark = Bookmark.objects.filter(
user=request.user,
service=service,
content_id=content_id
).first()
if bookmark:
# If bookmark exists but is inactive, reactivate it
if not bookmark.status:
bookmark.status = True
bookmark.save()
serializer = self.get_serializer(bookmark)
return Response(serializer.data, status=status.HTTP_200_OK)
# If bookmark exists and is active, return it
serializer = self.get_serializer(bookmark)
return Response(serializer.data, status=status.HTTP_200_OK)
# Create a new bookmark
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class RemoveBookmarkView(DestroyAPIView):
"""
Deactivate a bookmark by setting its status to False using content_id and user.
The request should specify the content_id of the bookmark to be deactivated.
"""
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
serializer_class = BookmarkSerializer
@swagger_auto_schema(
operation_description="Deactivate a bookmark by setting its status to False using content_id and user.",
tags=["Dobodbi - Bookmarks"],
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'service': openapi.Schema(type=openapi.TYPE_STRING, description='The service associated with the bookmark.'),
'content_id': openapi.Schema(type=openapi.TYPE_STRING, description='The ID of the content to deactivate the bookmark for.')
},
required=['service', 'content_id']
),
responses={
204: "Bookmark deactivated successfully.",
400: "Invalid input.",
404: "Bookmark not found or already inactive."
}
)
def delete(self,request,*args, **kwargs):
return super().delete(request,*args, **kwargs)
def get_object(self):
service = self.request.data.get('service')
content_id = self.request.data.get('content_id')
if not service or not content_id:
raise ValidationError('Both service and content_id are required')
bookmark = Bookmark.objects.filter(
user=self.request.user,
service=service,
content_id=content_id,
status=True
).first()
if not bookmark:
raise ValidationError('Bookmark not found or already inactive')
return bookmark
def destroy(self, request, *args, **kwargs):
bookmark = self.get_object()
bookmark.status = False
bookmark.save()
return Response(status=status.HTTP_204_NO_CONTENT)
class AddBookmarkListView(APIView):
"""
Add a list of bookmarks in a single API call.
Each item can be added or reactivated.
"""
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
@swagger_auto_schema(
operation_description="Add multiple bookmarks in one request. Each item should have 'service' and 'content_id'.",
tags=["Dobodbi - Bookmarks"],
request_body=openapi.Schema(
type=openapi.TYPE_ARRAY,
items=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'service': openapi.Schema(type=openapi.TYPE_STRING, description='Service name'),
'content_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='Content ID'),
},
required=['service', 'content_id']
)
),
responses={
200: "Bookmarks processed successfully.",
400: "Invalid input."
}
)
def post(self, request, *args, **kwargs):
items = request.data
if not isinstance(items, list):
return Response({'error': 'Expected a list of bookmark items.'}, status=status.HTTP_400_BAD_REQUEST)
results = []
errors = []
for index, item in enumerate(items):
service = item.get('service')
content_id = item.get('content_id')
if not service or not content_id:
errors.append({'index': index, 'error': 'Both service and content_id are required.'})
continue
bookmark = Bookmark.objects.filter(
user=request.user,
service=service,
content_id=content_id
).first()
if bookmark:
if not bookmark.status:
bookmark.status = True
bookmark.save()
results.append({'index': index, 'service': service, 'content_id': content_id, 'action': 'reactivated'})
else:
serializer = BookmarkSerializer(data=item, context={'request': request})
if serializer.is_valid():
serializer.save(user=request.user)
results.append({'index': index, 'service': service, 'content_id': content_id, 'action': 'created'})
else:
errors.append({'index': index, 'error': serializer.errors})
response_data = {'results': results}
if errors:
response_data['errors'] = errors
return Response(response_data, status=status.HTTP_200_OK)
class RemoveBookmarkListView(APIView):
"""
Deactivate a list of bookmarks in a single API call.
"""
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
@swagger_auto_schema(
operation_description="Remove (deactivate) multiple bookmarks in one request. Each item should have 'service' and 'content_id'.",
tags=["Dobodbi - Bookmarks"],
request_body=openapi.Schema(
type=openapi.TYPE_ARRAY,
items=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'service': openapi.Schema(type=openapi.TYPE_STRING, description='Service name'),
'content_id': openapi.Schema(type=openapi.TYPE_INTEGER, description='Content ID'),
},
required=['service', 'content_id']
)
),
responses={
200: "Bookmarks processed successfully.",
400: "Invalid input."
}
)
def post(self, request, *args, **kwargs):
items = request.data
if not isinstance(items, list):
return Response({'error': 'Expected a list of bookmark items.'}, status=status.HTTP_400_BAD_REQUEST)
results = []
errors = []
for index, item in enumerate(items):
service = item.get('service')
content_id = item.get('content_id')
if not service or not content_id:
errors.append({'index': index, 'error': 'Both service and content_id are required.'})
continue
bookmark = Bookmark.objects.filter(
user=request.user,
service=service,
content_id=content_id,
status=True
).first()
if bookmark:
bookmark.status = False
bookmark.save()
results.append({'index': index, 'service': service, 'content_id': content_id, 'action': 'removed'})
else:
errors.append({'index': index, 'service': service, 'content_id': content_id, 'error': 'Bookmark not found or already inactive.'})
response_data = {'results': results}
if errors:
response_data['errors'] = errors
return Response(response_data, status=status.HTTP_200_OK)
def _get_valid_content_ids(service, content_ids):
if not content_ids:
return set()
service = (service or '').lower()
try:
if service == 'library':
from apps.library.models import Book
return set(Book.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service == 'video_playlist':
from apps.video.models import VideoPlaylist
return set(VideoPlaylist.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service in ('video', 'video_item'):
from apps.video.models import Video
return set(Video.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service == 'podcast':
from apps.podcast.models import Podcast, PodcastPlaylist
p_ids = set(Podcast.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
pl_ids = set(PodcastPlaylist.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
return p_ids | pl_ids
elif service == 'podcast_playlist':
from apps.podcast.models import PodcastPlaylist
return set(PodcastPlaylist.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service in ('article', 'blog'):
from apps.article.models import Article
return set(Article.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
elif service in ('hadith', 'hadith_correction'):
from apps.hadith.models import Hadith
return set(Hadith.objects.filter(id__in=content_ids, status=True).values_list('id', flat=True))
except Exception:
pass
return set(content_ids)
class BookmarkStatusView(APIView):
"""
Return the count of bookmarks for each service for the current user.
"""
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
@swagger_auto_schema(
operation_description="Return the count of bookmarks for each service for the current user.",
tags=["Dobodbi - Bookmarks"],
responses={
200: "A list of bookmark counts for each service.",
}
)
def get(self, request):
# Get all active bookmarks for the current user
user_bookmarks = Bookmark.objects.filter(
user=request.user,
status=True
)
# Get the count of bookmarks for each service
service_counts = {}
for service_choice in Bookmark.ServiceChoices.choices:
service_code = service_choice[0] # Get the service code (e.g., 'library')
service_name = service_choice[1] # Get the service display name (e.g., 'Library')
bms_for_service = user_bookmarks.filter(service=service_code)
content_ids = list(bms_for_service.values_list('content_id', flat=True))
valid_ids = _get_valid_content_ids(service_code, content_ids)
count = len(valid_ids)
# Add to results
service_counts[service_code] = {
'service': service_code,
'service_display': service_name,
'count': count
}
# Convert to list for response
result = list(service_counts.values())
return Response(result, status=status.HTTP_200_OK)
class BookmarkListView(APIView):
"""
Return all active bookmarks for the current user.
Optionally filter by service (e.g. ?service=hadith).
"""
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
@swagger_auto_schema(
operation_description="Return all active bookmarks for the current user. Optionally filter by service query parameter (e.g., 'hadith', 'library').",
tags=["Dobodbi - Bookmarks"],
manual_parameters=[
openapi.Parameter(
'service',
openapi.IN_QUERY,
description="Filter bookmarks by service (e.g., 'hadith', 'library', 'video', 'podcast', 'article')",
type=openapi.TYPE_STRING,
required=False
)
],
responses={
200: BookmarkSerializer(many=True)
}
)
def get(self, request, *args, **kwargs):
service = request.query_params.get('service')
bookmarks = Bookmark.objects.filter(
user=request.user,
status=True
)
if service:
bookmarks = bookmarks.filter(service=service)
bookmarks = bookmarks.order_by('-created_at')
valid_bookmarks = []
by_service = {}
for b in bookmarks:
by_service.setdefault(b.service, []).append(b)
for srv, b_list in by_service.items():
content_ids = [b.content_id for b in b_list]
valid_ids = _get_valid_content_ids(srv, content_ids)
for b in b_list:
if b.content_id in valid_ids:
valid_bookmarks.append(b)
else:
b.status = False
b.save(update_fields=['status'])
serializer = BookmarkSerializer(valid_bookmarks, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)