from rest_framework import generics from ..models import BookReference from ..serializers.reference_v2 import BookReferenceV2DetailSerializer from drf_yasg.utils import swagger_auto_schema class BookReferenceV2DetailView(generics.RetrieveAPIView): """ V2 View to retrieve Book Reference details, formatted with tabs (Information, Excerpts, Volumes) and supporting multiple editions per book. """ queryset = BookReference.objects.all() serializer_class = BookReferenceV2DetailSerializer lookup_field = 'slug' def get_queryset(self): return ( BookReference.objects .filter(slug=self.kwargs.get('slug')) .select_related('author') .prefetch_related( 'editions__volumes__images', 'volumes__images', 'images', 'hadis_references__hadis' ) ) @swagger_auto_schema( operation_summary="Get V2 Book Reference Detail", operation_description="Retrieve structured metadata for a book reference, including its editions, related hadiths (excerpts), and volumes.", tags=['Dobodbi - Hadis (V2)'], responses={200: BookReferenceV2DetailSerializer} ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) from utils.pagination import NoPagination from ..serializers.reference_v2 import BookReferenceV2SyncSerializer class BookReferenceV2SyncView(generics.ListAPIView): """ API view to sync all V2 book reference data for offline mode Returns all book references with editions, volumes, and related hadises """ serializer_class = BookReferenceV2SyncSerializer pagination_class = NoPagination @swagger_auto_schema( operation_summary="Sync V2 Book References", operation_description="Returns all book references for offline sync.", tags=['Dobodbi - Hadis (V2)'], responses={200: BookReferenceV2SyncSerializer(many=True)} ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) def get_queryset(self): qs = BookReference.objects.select_related('author').prefetch_related( 'attributes', 'images', 'editions__volumes', 'volumes', 'hadis_references__hadis' ).distinct().order_by('id') category = self.request.query_params.get('category') if category: qs = qs.filter(subject_area__id=category) author = self.request.query_params.get('author') if author: qs = qs.filter(author_id=author) madhab = self.request.query_params.get('madhab') if madhab: if madhab in ['shia', 'sunni']: qs = qs.filter(type__sect=madhab) else: qs = qs.filter(type__id=madhab) pdf_available = self.request.query_params.get('pdf_available') if pdf_available == 'true': qs = qs.filter(documents__isnull=False) elif pdf_available == 'false': qs = qs.filter(documents__isnull=True) century = self.request.query_params.get('century') if century: if century == 'before_islam': qs = qs.filter(author__death_year_hijri__lt=0) elif century.isdigit(): century_num = int(century) start_year = (century_num - 1) * 100 + 1 end_year = century_num * 100 qs = qs.filter(author__death_year_hijri__gte=start_year, author__death_year_hijri__lte=end_year) return qs def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) serializer = self.get_serializer(queryset, many=True) categories_qs = BookSubjectArea.objects.all() categories = [{"id": c.id, "title": get_localized_text(c.title, request)} for c in categories_qs] sects_qs = BookType.objects.all() sects = [{"id": s.id, "title": get_localized_text(s.title, request)} for s in sects_qs] filters_metadata = { "categories": categories, "madhab": sects } return Response({ "count": queryset.count(), "filters": filters_metadata, "results": serializer.data }) from rest_framework.response import Response from ..serializers.reference_v2 import BookReferenceV2ListSerializer from ..models import BookAuthor, BookSubjectArea, BookType from utils.pagination import StandardResultsSetPagination from ..serializers.category import get_localized_text from drf_yasg import openapi class BookReferenceV2ListView(generics.ListAPIView): """ API view to list V2 book references with filters and metadata """ serializer_class = BookReferenceV2ListSerializer pagination_class = StandardResultsSetPagination @swagger_auto_schema( operation_summary="List V2 Book References with Filters", operation_description="Returns a paginated list of book references and available filters (categories, sects).\n" "Use query parameters to filter the results.", tags=['Dobodbi - Hadis (V2)'], manual_parameters=[ openapi.Parameter('category', openapi.IN_QUERY, description="Filter by Category/Subject Area ID(s). Supports comma-separated (e.g. '1,2,3') or multiple params", type=openapi.TYPE_STRING), openapi.Parameter('author', openapi.IN_QUERY, description="Filter by Author ID(s). Supports comma-separated (e.g. '1,2,3') or multiple params", type=openapi.TYPE_STRING), openapi.Parameter('madhab', openapi.IN_QUERY, description="Filter by Madhab ('shia' or 'sunni') or Type ID(s). Supports comma-separated", type=openapi.TYPE_STRING), openapi.Parameter('pdf_available', openapi.IN_QUERY, description="Filter by PDF availability ('true' or 'false')", type=openapi.TYPE_STRING), openapi.Parameter('century', openapi.IN_QUERY, description="Filter by Century (e.g. '1,2', 'before_islam'). Supports comma-separated", type=openapi.TYPE_STRING), openapi.Parameter('search', openapi.IN_QUERY, description="Search term for book title, description, or author name", type=openapi.TYPE_STRING), ] ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) def _get_multi_param(self, *param_names): """Extract multi-value params supporting comma-separated, repeated keys, and [] suffix.""" values = [] for name in param_names: for item in self.request.query_params.getlist(name): for val in str(item).split(','): val = val.strip() if val: values.append(val) return values def get_queryset(self): qs = BookReference.objects.select_related('author').prefetch_related('tags', 'volumes__images').order_by('-created_at') search = self.request.query_params.get('search') if search: from django.db.models import Q qs = qs.filter( Q(title__icontains=search) | Q(description__icontains=search) | Q(author__name__icontains=search) ) # Category (Multi-select) categories = self._get_multi_param('category', 'categories', 'category[]', 'categories[]') category_ids = [int(c) for c in categories if c.isdigit()] if category_ids: qs = qs.filter(subject_area__id__in=category_ids).distinct() # Author (Multi-select) authors = self._get_multi_param('author', 'authors', 'author[]', 'authors[]') author_ids = [int(a) for a in authors if a.isdigit()] if author_ids: qs = qs.filter(author_id__in=author_ids) # Madhab / Sect (Multi-select) madhabs = self._get_multi_param('madhab', 'madhabs', 'madhab[]', 'madhabs[]', 'sect', 'sects', 'sect[]', 'sects[]') if madhabs: from django.db.models import Q madhab_q = Q() type_ids = [] for m in madhabs: if m.lower() in ['shia', 'sunni']: madhab_q |= Q(type__sect=m.lower()) elif m.isdigit(): type_ids.append(int(m)) if type_ids: madhab_q |= Q(type__id__in=type_ids) if madhab_q: qs = qs.filter(madhab_q) # PDF availability pdf_available = self.request.query_params.get('pdf_available') if pdf_available == 'true': qs = qs.filter(documents__isnull=False).distinct() elif pdf_available == 'false': qs = qs.filter(documents__isnull=True) # Century (Multi-select) centuries = self._get_multi_param('century', 'centuries', 'century[]', 'centuries[]') if centuries: from django.db.models import Q century_q = Q() for c in centuries: if c == 'before_islam': century_q |= Q(author__death_year_hijri__lt=0) elif c.isdigit(): century_num = int(c) start_year = (century_num - 1) * 100 + 1 end_year = century_num * 100 century_q |= Q(author__death_year_hijri__gte=start_year, author__death_year_hijri__lte=end_year) if century_q: qs = qs.filter(century_q) return qs def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) serializer = self.get_serializer(page if page is not None else queryset, many=True) categories_qs = BookSubjectArea.objects.all() categories = [{"id": c.id, "title": get_localized_text(c.title, request)} for c in categories_qs] sects_qs = BookType.objects.all() sects = [{"id": s.id, "title": get_localized_text(s.title, request)} for s in sects_qs] filters_metadata = { "categories": categories, "madhab": sects } if page is not None: response = self.get_paginated_response(serializer.data) # Add filters directly into the response dict response.data['filters'] = filters_metadata return response return Response({ "filters": filters_metadata, "results": serializer.data }) from rest_framework.generics import ListAPIView, RetrieveAPIView from rest_framework.permissions import AllowAny from rest_framework.filters import SearchFilter from utils.pagination import StandardResultsSetPagination from apps.hadis.models.reference import BookAuthor, BookReference from apps.hadis.serializers.reference_v2 import ( BookAuthorListSerializer, BookAuthorDetailSerializer, AuthorBookReferenceSerializer, BookAuthorSyncSerializer ) class BookAuthorListView(ListAPIView): """ لیست تمام نویسندگان رفرنس """ queryset = BookAuthor.objects.all().order_by('-created_at') serializer_class = BookAuthorListSerializer pagination_class = StandardResultsSetPagination permission_classes = [AllowAny] filter_backends = [SearchFilter] search_fields = ['name', 'known_as', 'scholarity', 'tabaqa', 'rutba', 'features'] @swagger_auto_schema( operation_summary="List Book Authors", operation_description="Returns a paginated list of book authors.", tags=['Dobodbi - Hadis (V2)'], ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) class BookAuthorDetailView(RetrieveAPIView): """ دریافت اطلاعات دقیق یک نویسنده بر اساس slug """ queryset = BookAuthor.objects.select_related('related_narrator').all() serializer_class = BookAuthorDetailSerializer lookup_field = 'slug' lookup_url_kwarg = 'author_slug' permission_classes = [AllowAny] @swagger_auto_schema( operation_summary="Book Author Detail", operation_description="Returns detailed information about a specific book author.", tags=['Dobodbi - Hadis (V2)'], ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) class AuthorReferencesListView(ListAPIView): """ لیست تمام رفرنس‌ها (کتاب‌ها)یی که این نویسنده در آن‌ها تگ شده است """ serializer_class = AuthorBookReferenceSerializer pagination_class = StandardResultsSetPagination permission_classes = [AllowAny] @swagger_auto_schema( operation_summary="Author References List", operation_description="Returns a paginated list of book references for a specific author.", tags=['Dobodbi - Hadis (V2)'], ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs) def get_queryset(self): author_slug = self.kwargs.get('author_slug') # پیدا کردن رفرنس‌هایی که این نویسنده جزو authors آن‌هاست return BookReference.objects.filter( author__slug=author_slug ).select_related('author').prefetch_related('images').order_by('-created_at') class BookAuthorSyncView(generics.ListAPIView): """ API view to sync all Book Authors data for offline mode. Returns complete author data and their associated books without pagination. """ # 👇 استفاده از prefetch_related برای جلوگیری از کرش کردن دیتابیس به خاطر N+1 Query queryset = BookAuthor.objects.select_related('related_narrator').prefetch_related('book_references').order_by('id') serializer_class = BookAuthorSyncSerializer pagination_class = NoPagination permission_classes = [AllowAny] @swagger_auto_schema( operation_summary="Sync Book Authors", operation_description="Returns complete book authors data including their books for offline sync.", tags=['Dobodbi - Hadis (V2)'], ) def get(self, request, *args, **kwargs): return super().get(request, *args, **kwargs)