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.
 
 

315 lines
11 KiB

from django.db.models import Count, Q
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.generics import ListAPIView, RetrieveAPIView, CreateAPIView
from rest_framework.filters import SearchFilter
from rest_framework import status
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
from apps.library.pagination import NoPagination
from utils.pagination import StandardResultsSetPagination
from apps.library.models import *
from apps.library.serializers import *
from apps.library.doc import (
book_list_swagger,
book_detail_swagger,
category_list_swagger,
pinned_collection_list_swagger,
middle_collection_list_swagger
)
from utils.pagination import StandardResultsSetPagination
class CategoryListView(ListAPIView):
"""
API view to list all book categories
"""
serializer_class = CategorySerializer
permission_classes = (IsAuthenticated,)
pagination_class = StandardResultsSetPagination
@category_list_swagger
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def get_queryset(self):
return Category.objects.filter(
status=True
).annotate(
books_count_annotation=Count('related_categories')
).order_by('title')
class PinnedBookCollectionListView(ListAPIView):
"""
API view to list pinned book collections with their top 3 book covers
"""
serializer_class = PinnedBookCollectionSerializer
permission_classes = (IsAuthenticated,)
pagination_class = NoPagination
@pinned_collection_list_swagger
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def get_queryset(self):
return BookCollection.objects.filter(
status=True,
display_position=BookCollection.DisplayPosition.PINNED
).order_by('-order', '-id')
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
categories_count = Category.objects.filter(status=True).count()
from apps.bookmark.models import Bookmark
bookmarks_count = Bookmark.objects.filter(
service=Bookmark.ServiceChoices.LIBRARY,
).count()
downloads_count = BookDownload.objects.all().count()
info = {
"categories_count": categories_count,
"bookmarks_count": bookmarks_count,
"downloads_count": downloads_count
}
data = {
"count": response.data.get("count"),
"next": response.data.get("next"),
"previous": response.data.get("previous"),
"info": info,
"results": response.data.get("results")
}
return Response(data, status=status.HTTP_200_OK)
class BookListView(ListAPIView):
"""
API view to list books with filtering and search capabilities
"""
serializer_class = BookSerializer
permission_classes = (IsAuthenticated,)
filter_backends = [SearchFilter]
search_fields = ['title', 'summary', 'publisher', 'isbn']
pagination_class = StandardResultsSetPagination
@book_list_swagger
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def get_queryset(self):
queryset = Book.objects.filter(status=True)
# Filter by collection if provided
collection_id = self.request.query_params.get('collection_id')
if collection_id:
queryset = queryset.filter(collections__id=collection_id)
# Filter by category if provided
category = self.request.query_params.get('category')
if category:
# Support both single slug and comma-separated list of slugs
category_slugs = [slug.strip() for slug in category.split(',')]
queryset = queryset.filter(categories__slug__in=category_slugs).distinct()
# Filter by middle collection if requested
# if self.request.query_params.get('middle'):
# middle_collections = BookCollection.objects.filter(
# status=True,
# display_position=BookCollection.DisplayPosition.MIDDLE
# )
# if middle_collections.exists():
# queryset = queryset.filter(collections__in=middle_collections)
# Filter by bottom collection if requested
# if self.request.query_params.get('bottom'):
# bottom_collections = BookCollection.objects.filter(
# status=True,
# display_position=BookCollection.DisplayPosition.BOTTOM
# )
# if bottom_collections.exists():
# queryset = queryset.filter(collections__in=bottom_collections)
# Filter by bookmarked books if requested
is_bookmark = self.request.query_params.get('is_bookmark', '').lower()
if is_bookmark == 'true':
# Import Bookmark model here to avoid circular imports
from apps.bookmark.models import Bookmark
# Get all bookmarked book IDs for the current user
bookmarked_ids = Bookmark.objects.filter(
user=self.request.user,
service=Bookmark.ServiceChoices.LIBRARY,
status=True
).values_list('content_id', flat=True)
# Filter books by these IDs
queryset = queryset.filter(id__in=bookmarked_ids)
# Sort by parameter
sort = self.request.query_params.get('sort', '-pin,-created_at')
# Allowed sort fields
allowed_sorts = [
'created_at', '-created_at', 'view_count', '-view_count',
'download_count', '-download_count', 'title', '-title',
'pin', '-pin', '-pin,-created_at'
]
if sort in allowed_sorts:
# Handle multiple sort fields (e.g., '-pin,-created_at')
if ',' in sort:
queryset = queryset.order_by(*sort.split(','))
else:
queryset = queryset.order_by(sort)
else:
queryset = queryset.order_by('-pin', '-created_at')
return queryset
class BookDetailView(RetrieveAPIView):
"""
API view to retrieve detailed information about a specific book
"""
serializer_class = BookSerializer
permission_classes = (IsAuthenticated,)
queryset = Book.objects.filter(status=True)
@book_detail_swagger
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
# Increment view count when book details are viewed
instance.increment_view_count()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class MiddleBookCollectionListView(ListAPIView):
"""
API view to list middle section book collections with their books
"""
serializer_class = MiddleBookCollectionSerializer
permission_classes = (IsAuthenticated,)
pagination_class = NoPagination
@middle_collection_list_swagger
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def get_queryset(self):
return BookCollection.objects.filter(
status=True,
display_position=BookCollection.DisplayPosition.MIDDLE
).order_by('order')
class DownloadedBooksListView(ListAPIView):
"""
API view to list books that have been downloaded by the current user
"""
serializer_class = BookSerializer
permission_classes = (IsAuthenticated,)
filter_backends = [SearchFilter]
search_fields = ['title', 'summary', 'publisher', 'isbn']
pagination_class = StandardResultsSetPagination
@swagger_auto_schema(
operation_id="list_downloaded_books",
operation_description="""
Retrieve a list of books that have been downloaded by the current user.
This endpoint returns a paginated list of books that the authenticated user has downloaded.
The results are not cached to ensure real-time accuracy of the download list.
You can search for downloaded books by title, summary, publisher, or ISBN using the 'search' query parameter.
""",
operation_summary="List Downloaded Books",
tags=["Dobodbi - Library"],
manual_parameters=[
openapi.Parameter(
'search',
openapi.IN_QUERY,
description="Search downloaded books by title, summary, publisher, or ISBN",
type=openapi.TYPE_STRING,
required=False
)
],
responses={
200: "List of downloaded books with pagination",
401: "Authentication credentials were not provided or are invalid",
500: "Internal server error occurred"
}
)
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def get_queryset(self):
# Get all downloaded book IDs for the current user
downloaded_ids = BookDownload.objects.filter(
user=self.request.user,
status=True
).values_list('book_id', flat=True)
# Return books that match these IDs
return Book.objects.filter(
id__in=downloaded_ids,
status=True
).order_by('-created_at')
class BookDownloadCreateAPIView(CreateAPIView):
"""
API view to create a book download record and increment the book's download count
"""
serializer_class = BookDownloadSerializer
permission_classes = (IsAuthenticated,)
@swagger_auto_schema(
operation_id="download_book",
operation_description="""
Create a book download record and increment the book's download count.
This endpoint creates a record of a book download by the current user and increments
the book's download count. It requires the book ID in the request body.
If the user has already downloaded the book, the existing record will be updated
with the current timestamp.
""",
operation_summary="Download Book",
tags=["Dobodbi - Library"],
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'book_id': openapi.Schema(
type=openapi.TYPE_INTEGER,
description="ID of the book to download"
)
},
required=['book_id']
),
responses={
201: openapi.Response(
description="Book download record created successfully",
schema=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'id': openapi.Schema(type=openapi.TYPE_INTEGER),
'created_at': openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_DATETIME),
'updated_at': openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_DATETIME),
'status': openapi.Schema(type=openapi.TYPE_BOOLEAN)
}
)
),
400: "Invalid request data or book not found",
401: "Authentication credentials were not provided or are invalid",
500: "Internal server error occurred"
}
)
def post(self, request, *args, **kwargs):
return super().post(request, *args, **kwargs)