from django.db.models import Count from django.utils import timezone from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import status from rest_framework.generics import GenericAPIView from rest_framework.mixins import UpdateModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListModelMixin from rest_framework.response import Response from rest_framework.views import APIView from .querysets import ALL_UBC_QUERYSET from .serializers import UsersBooksCollectionsSerializer, UsersBooksCollectionsPostSerializer from api.permissions import AllowAny, IsAuthenticated, IsAdmin from content.models import UsersBooksCollections, Collection, Book, Author, Genre from ..author.serializers import AuthorSerializer from ..genre.serializers import GenreSerializer from ..permissions import IsStaffOrAdmin # region base class UBCAPIView(GenericAPIView): queryset = ALL_UBC_QUERYSET serializer_class = UsersBooksCollectionsSerializer permission_classes = [AllowAny] ordering_fields = ["created_at"] ordering = ["-created_at"] class UBCMEAPIView(UBCAPIView): permission_classes = [IsAuthenticated,] def get_queryset(self): return ALL_UBC_QUERYSET.filter(user=self.request.user) class UBCMECOLLAPIView(UBCAPIView): permission_classes = [IsAuthenticated,] def get_queryset(self): collection_uuid = self.kwargs["collection_uuid"] return ALL_UBC_QUERYSET.filter(user=self.request.user, collection__uuid=collection_uuid) # endregion # region private class UBCListView(ListModelMixin, CreateModelMixin, UBCAPIView): permission_classes = [IsStaffOrAdmin] def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) # endregion # region public class TopReadAPIView(APIView): permission_classes = [IsAuthenticated,] def get(self, request): collections = Collection.objects.filter(title__in=["Читаемое", "Прочитано"]) books = Book.objects.filter( usersbookscollections__user=request.user, usersbookscollections__collection__in=collections ).prefetch_related('genres', 'authors') top_authors = ( books.values('authors__id', 'authors__name') .annotate(read_count=Count('usersbookscollections')) .order_by('-read_count')[:3] ) top_genres = ( books.values('genres__id', 'genres__title') .annotate(read_count=Count('usersbookscollections')) .order_by('-read_count')[:3] ) top_authors_data = [ {'id': author['authors__id'], 'name': author['authors__name']} for author in top_authors ] top_genres_data = [ {'id': genre['genres__id'], 'name': genre['genres__title']} for genre in top_genres ] return Response({ 'top_authors': top_authors_data, 'top_genres': top_genres_data }) class CheckBookInBookmarksView(APIView): permission_classes = [IsAuthenticated,] def get(self, request, book_uuid): user = request.user try: bookmark = UsersBooksCollections.objects.get(user=user, book__uuid=book_uuid, collection__title='Закладки') return Response({"exists": True}, status=status.HTTP_200_OK) except UsersBooksCollections.DoesNotExist: return Response({"exists": False}, status=status.HTTP_200_OK) class UBCMEListView(UBCMEAPIView, ListModelMixin): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) class UBCMECreateView(UBCMEAPIView, CreateModelMixin): serializer_class = UsersBooksCollectionsPostSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) return Response(serializer.data, status=status.HTTP_201_CREATED) def perform_create(self, serializer): serializer.save(user=self.request.user) class UBCMECOLLListView(ListModelMixin, UBCMECOLLAPIView): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) class UBCMERetrieveUpdateDestroyView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, UBCMEAPIView): def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) def perform_destroy(self, instance): instance.delete() class CountReadBooksView(APIView): permission_classes = [IsAuthenticated,] @swagger_auto_schema( manual_parameters=[ openapi.Parameter( 'start_date', openapi.IN_QUERY, description="Начальная дата в формате ISO 8601 (2023-12-31T23:59:59)", type=openapi.TYPE_STRING, format=openapi.FORMAT_DATETIME, required=True ), openapi.Parameter( 'end_date', openapi.IN_QUERY, description="Конечная дата в формате ISO 8601 (2023-12-31T23:59:59)", type=openapi.TYPE_STRING, format=openapi.FORMAT_DATETIME, required=True ), ], ) def get(self, request): user = request.user start_date = request.query_params.get('start_date') end_date = request.query_params.get('end_date') if not start_date or not end_date: return Response({"error": "Диапазон дат не предоставлен"}, status=status.HTTP_400_BAD_REQUEST) try: start_date = timezone.datetime.fromisoformat(start_date) end_date = timezone.datetime.fromisoformat(end_date) except ValueError: return Response({"error": "Некорректный формат переданных дат"}, status=status.HTTP_400_BAD_REQUEST) count = UsersBooksCollections.objects.filter( user=user, collection__title='Прочитано', created_at__range=(start_date, end_date) ).count() return Response({"count": count}, status=status.HTTP_200_OK) # endregion