from django.db.models import Q from django.db.models import Q from rest_framework.authentication import TokenAuthentication from rest_framework.decorators import action from rest_framework.parsers import FormParser, JSONParser, MultiPartParser from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from apps.account.permissions import IsSuperAdmin from utils.pagination import StandardResultsSetPagination from .models import ( Hadis, HadisCategory, HadisCollection, HadisCorrection, HadisInterpretation, HadisStatus, HadisTag, HadisSect, HadisTransmitter, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText ) from .serializers.serializers_admin import ( AdminHadisCategorySerializer, AdminHadisSectArchiveSerializer, AdminHadisCategoryArchiveSerializer, AdminHadisCorrectionSerializer, AdminHadisDetailSerializer, AdminHadisInterpretationSerializer, AdminHadisCollectionSerializer, AdminHadisListSerializer, AdminHadisStatusSerializer, AdminHadisTagSerializer, AdminBookSubjectAreaSerializer, AdminBookTypeSerializer, AdminBookAuthorSerializer, AdminBookAttributeSerializer, AdminBookReferenceImageSerializer, AdminBookReferenceDocumentSerializer, AdminBookReferenceListSerializer, AdminBookReferenceDetailSerializer, AdminTransmitterListSerializer, AdminTransmitterDetailSerializer, AdminTransmitterReliabilitySerializer, AdminNarratorLayerSerializer, AdminOpinionStatusSerializer, AdminHadisTransmitterSerializer, AdminTransmitterOpinionSerializer, AdminTransmitterOriginalTextSerializer, AdminBookEditionSerializer, AdminBookVolumeSerializer, ) class AdminHadisCategoryViewSet(ReadOnlyModelViewSet): serializer_class = AdminHadisCategorySerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): queryset = HadisCategory.objects.all() source_type = self.request.query_params.get("source_type") if source_type: queryset = queryset.filter(source_type=source_type) return queryset.order_by("order", "id") class DovodiHadisSectViewSet(ModelViewSet): serializer_class = AdminHadisSectArchiveSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = HadisSect.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(description__icontains=search_query) | Q(sect_type__icontains=search_query) ) return queryset.order_by("order", "id") class DovodiHadisCategoryViewSet(ModelViewSet): serializer_class = AdminHadisCategoryArchiveSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = HadisCategory.objects.select_related("parent", "sect") search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(description__icontains=search_query) | Q(slug__icontains=search_query) | Q(source_type__icontains=search_query) ) source_type = self.request.query_params.get("source_type") if source_type: queryset = queryset.filter(source_type=source_type) return queryset.order_by("tree_id", "lft", "order", "id") class AdminHadisStatusViewSet(ModelViewSet): serializer_class = AdminHadisStatusSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = HadisStatus.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(slug__icontains=search_query) | Q(description__icontains=search_query) ) return queryset.order_by("order", "id") class AdminHadisTagViewSet(ModelViewSet): serializer_class = AdminHadisTagSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = HadisTag.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter(title__icontains=search_query) return queryset.order_by("id") class AdminHadisCollectionViewSet(ModelViewSet): serializer_class = AdminHadisCollectionSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (MultiPartParser, FormParser, JSONParser) def get_queryset(self): queryset = HadisCollection.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(slug__icontains=search_query) | Q(summary__icontains=search_query) ) status_filter = self.request.query_params.get("status") if status_filter in {"true", "false"}: queryset = queryset.filter(status=status_filter == "true") return queryset.order_by("order", "id") class AdminTransmitterReliabilityViewSet(ReadOnlyModelViewSet): serializer_class = AdminTransmitterReliabilitySerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): return TransmitterReliability.objects.all().order_by("id") class DovodiNarratorLayerViewSet(ModelViewSet): serializer_class = AdminNarratorLayerSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = NarratorLayer.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(name__icontains=search_query) | Q(description__icontains=search_query) | Q(slug__icontains=search_query) ) return queryset.order_by("number", "id") class DovodiTransmitterReliabilityViewSet(ModelViewSet): serializer_class = AdminTransmitterReliabilitySerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = TransmitterReliability.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(slug__icontains=search_query) ) return queryset.order_by("id") class DovodiOpinionStatusViewSet(ModelViewSet): serializer_class = AdminOpinionStatusSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = OpinionStatus.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(slug__icontains=search_query) ) return queryset.order_by("id") class AdminTransmitterViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination parser_classes = (MultiPartParser, FormParser, JSONParser) def get_serializer_class(self): if self.action == "list": return AdminTransmitterListSerializer return AdminTransmitterDetailSerializer def get_queryset(self): queryset = Transmitters.objects.select_related("reliability") search_query = self.request.query_params.get("search") if search_query: q_filter = ( Q(full_name__icontains=search_query) | Q(kunya__icontains=search_query) | Q(known_as__icontains=search_query) | Q(nickname__icontains=search_query) | Q(origin__icontains=search_query) | Q(lived_in__icontains=search_query) | Q(died_in__icontains=search_query) ) if search_query.isdigit(): q_filter |= Q(legacy_number=int(search_query)) | Q(generation=int(search_query)) queryset = queryset.filter(q_filter) status_filter = self.request.query_params.get("status") if status_filter and status_filter != "all": queryset = queryset.filter(reliability__slug=status_filter) madhhab_filter = self.request.query_params.get("madhhab") if madhhab_filter and madhhab_filter != "all": queryset = queryset.filter(madhhab=madhhab_filter) generation_filter = self.request.query_params.get("generation") if generation_filter and generation_filter != "all": queryset = queryset.filter(generation=generation_filter) return queryset.order_by("id") class AdminHadisViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination parser_classes = (MultiPartParser, FormParser, JSONParser) def get_serializer_class(self): if self.action == "list": return AdminHadisListSerializer return AdminHadisDetailSerializer def get_queryset(self): queryset = Hadis.objects.all().select_related("category", "hadis_status") search_query = self.request.query_params.get("search") if search_query: q_filter = ( Q(title__icontains=search_query) | Q(title_narrator__icontains=search_query) | Q(text__icontains=search_query) | Q(translation__icontains=search_query) | Q(slug__icontains=search_query) ) if search_query.isdigit(): q_filter |= Q(number=int(search_query)) queryset = queryset.filter(q_filter) category_id = self.request.query_params.get("category") if category_id and category_id != "all": queryset = queryset.filter(category_id=category_id) source_type = self.request.query_params.get("source_type") if source_type: queryset = queryset.filter(category__source_type=source_type) status_filter = self.request.query_params.get("status") if status_filter in {"true", "false"}: queryset = queryset.filter(status=status_filter == "true") return queryset.distinct().order_by("-id") class AdminHadisCorrectionViewSet(ModelViewSet): serializer_class = AdminHadisCorrectionSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (JSONParser, MultiPartParser, FormParser) pagination_class = None def get_queryset(self): from django.db.models import Count, OuterRef, Subquery, IntegerField from django.db.models.functions import Coalesce from apps.bookmark.models.bookmark import Bookmark bookmarks_subquery = Bookmark.objects.filter( service=Bookmark.ServiceChoices.HADITH_CORRECTION, content_id=OuterRef("id"), status=True ).values("content_id").annotate(count=Count("id")).values("count") queryset = HadisCorrection.objects.all().select_related("hadis").prefetch_related("references", "references__images").annotate( annotated_bookmark_count=Coalesce(Subquery(bookmarks_subquery, output_field=IntegerField()), 0) ) hadis_id = self.request.query_params.get("hadis_id") if hadis_id: queryset = queryset.filter(hadis_id=hadis_id) return queryset.order_by("priority", "created_at") def perform_create(self, serializer): priority = self.request.data.get("priority", 1) hadis_id = self.request.data.get("hadis") if hadis_id: from django.db.models import F HadisCorrection.objects.filter(hadis_id=hadis_id, priority__gte=priority).update(priority=F("priority") + 1) serializer.save() @action(detail=False, methods=["post"]) def reorder(self, request): correction_ids = request.data.get("correction_ids", []) if not correction_ids: return Response({"error": "correction_ids list is required"}, status=400) for index, correction_id in enumerate(correction_ids, start=1): HadisCorrection.objects.filter(id=correction_id).update(priority=index) return Response({"status": "success"}) class AdminHadisInterpretationViewSet(ModelViewSet): serializer_class = AdminHadisInterpretationSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (JSONParser, MultiPartParser, FormParser) pagination_class = None def get_queryset(self): queryset = HadisInterpretation.objects.all().select_related("category").prefetch_related("references", "references__images") category_id = self.request.query_params.get("category_id") if category_id: queryset = queryset.filter(category_id=category_id) return queryset.order_by("priority", "created_at") def perform_create(self, serializer): priority = self.request.data.get("priority", 1) category_id = self.request.data.get("category") if category_id: from django.db.models import F HadisInterpretation.objects.filter(category_id=category_id, priority__gte=priority).update(priority=F("priority") + 1) serializer.save() @action(detail=False, methods=["post"]) def reorder(self, request): interpretation_ids = request.data.get("interpretation_ids", []) if not interpretation_ids: return Response({"error": "interpretation_ids list is required"}, status=400) for index, interpretation_id in enumerate(interpretation_ids, start=1): HadisInterpretation.objects.filter(id=interpretation_id).update(priority=index) return Response({"status": "success"}) class AdminBookSubjectAreaViewSet(ReadOnlyModelViewSet): serializer_class = AdminBookSubjectAreaSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): return BookSubjectArea.objects.all().order_by("id") class AdminBookTypeViewSet(ReadOnlyModelViewSet): serializer_class = AdminBookTypeSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): return BookType.objects.all().order_by("id") class DovodiBookSubjectAreaViewSet(ModelViewSet): serializer_class = AdminBookSubjectAreaSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = BookSubjectArea.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter(title__icontains=search_query) return queryset.order_by("id") class DovodiBookTypeViewSet(ModelViewSet): serializer_class = AdminBookTypeSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = BookType.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter(title__icontains=search_query) return queryset.order_by("id") class AdminBookAuthorViewSet(ModelViewSet): serializer_class = AdminBookAuthorSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): queryset = BookAuthor.objects.all() search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter(name__icontains=search_query) return queryset.order_by("id") class AdminBookAttributeViewSet(ModelViewSet): serializer_class = AdminBookAttributeSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = None def get_queryset(self): queryset = BookAttribute.objects.all() book_reference_id = self.request.query_params.get("book_reference_id") if book_reference_id: queryset = queryset.filter(book_reference_id=book_reference_id) return queryset.order_by("id") class AdminBookReferenceImageViewSet(ModelViewSet): serializer_class = AdminBookReferenceImageSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (MultiPartParser, FormParser, JSONParser) pagination_class = None def get_queryset(self): queryset = BookReferenceImage.objects.all() book_reference_id = self.request.query_params.get("book_reference_id") if book_reference_id: queryset = queryset.filter(book_reference_id=book_reference_id) return queryset.order_by("order", "id") class AdminBookReferenceDocumentViewSet(ModelViewSet): serializer_class = AdminBookReferenceDocumentSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (MultiPartParser, FormParser, JSONParser) pagination_class = None def get_queryset(self): queryset = BookReferenceDocument.objects.all() book_reference_id = self.request.query_params.get("book_reference_id") if book_reference_id: queryset = queryset.filter(book_reference_id=book_reference_id) return queryset.order_by("order", "id") class AdminBookReferenceViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] pagination_class = StandardResultsSetPagination parser_classes = (MultiPartParser, FormParser, JSONParser) def get_serializer_class(self): if self.action == "list": return AdminBookReferenceListSerializer return AdminBookReferenceDetailSerializer def get_queryset(self): queryset = BookReference.objects.all().prefetch_related("subject_area", "authors", "volumes", "editions").select_related("type") search_query = self.request.query_params.get("search") if search_query: queryset = queryset.filter( Q(title__icontains=search_query) | Q(slug__icontains=search_query) | Q(isbn__icontains=search_query) | Q(publisher__icontains=search_query) | Q(description__icontains=search_query) | Q(researcher__icontains=search_query) ) type_id = self.request.query_params.get("type") if type_id and type_id != "all": queryset = queryset.filter(type_id=type_id) madhab = self.request.query_params.get("madhab") if madhab and madhab != "all": queryset = queryset.filter(type__sect=madhab) subject_area_id = self.request.query_params.get("subject_area") if subject_area_id and subject_area_id != "all": queryset = queryset.filter(subject_area__id=subject_area_id) author_id = self.request.query_params.get("author") if author_id and author_id != "all": queryset = queryset.filter(authors__id=author_id) return queryset.distinct().order_by("-created_at") class AdminCategoryQuranVerseViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (JSONParser, MultiPartParser, FormParser) def get_serializer_class(self): from .serializers.serializers_admin import AdminCategoryQuranVerseSerializer return AdminCategoryQuranVerseSerializer def get_queryset(self): from .models.category import CategoryQuranVerse queryset = CategoryQuranVerse.objects.all().prefetch_related("secondary_translations") category_id = self.request.query_params.get("category") if category_id: queryset = queryset.filter(category_id=category_id) return queryset class AdminHadisTransmitterViewSet(ModelViewSet): serializer_class = AdminHadisTransmitterSerializer permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] parser_classes = (JSONParser, MultiPartParser, FormParser) def get_queryset(self): queryset = HadisTransmitter.objects.all().select_related("transmitter", "narrator_layer", "status") hadis_id = self.request.query_params.get("hadis_id") if hadis_id: queryset = queryset.filter(hadis_id=hadis_id) return queryset.order_by("order") @action(detail=False, methods=["post"]) def reorder(self, request): from rest_framework.decorators import action from rest_framework.response import Response from django.db import transaction hadis_transmitter_ids = request.data.get("hadis_transmitter_ids", []) if not hadis_transmitter_ids: return Response({"error": "hadis_transmitter_ids list is required"}, status=400) with transaction.atomic(): # Pass 1: Set temporary positive offset order indices to avoid duplicate key violations (>= 0) for index, ht_id in enumerate(hadis_transmitter_ids): HadisTransmitter.objects.filter(id=ht_id).update(order=10000 + index) # Pass 2: Set final sequential order indices starting from 0 for index, ht_id in enumerate(hadis_transmitter_ids): HadisTransmitter.objects.filter(id=ht_id).update(order=index) return Response({"status": "success"}) class AdminTransmitterOpinionViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] serializer_class = AdminTransmitterOpinionSerializer pagination_class = None def get_queryset(self): queryset = TransmitterOpinion.objects.all().select_related("status", "scholar") transmitter_id = self.request.query_params.get("transmitter_id") if transmitter_id: queryset = queryset.filter(transmitter_id=transmitter_id) return queryset.order_by("order", "id") @action(detail=False, methods=["post"]) def reorder(self, request): opinion_ids = request.data.get("opinion_ids", []) if not opinion_ids: return Response({"error": "opinion_ids list is required"}, status=400) from django.db import transaction with transaction.atomic(): for index, opinion_id in enumerate(opinion_ids): TransmitterOpinion.objects.filter(id=opinion_id).update(order=10000 + index) for index, opinion_id in enumerate(opinion_ids): TransmitterOpinion.objects.filter(id=opinion_id).update(order=index) return Response({"status": "success"}) class AdminTransmitterOriginalTextViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] serializer_class = AdminTransmitterOriginalTextSerializer parser_classes = (JSONParser, MultiPartParser, FormParser) pagination_class = None def get_queryset(self): queryset = TransmitterOriginalText.objects.all().prefetch_related("references", "references__images") transmitter_id = self.request.query_params.get("transmitter_id") if transmitter_id: queryset = queryset.filter(transmitter_id=transmitter_id) return queryset.order_by("order", "id") @action(detail=False, methods=["post"]) def reorder(self, request): original_text_ids = request.data.get("original_text_ids", []) if not original_text_ids: return Response({"error": "original_text_ids list is required"}, status=400) from django.db import transaction with transaction.atomic(): for index, ot_id in enumerate(original_text_ids): TransmitterOriginalText.objects.filter(id=ot_id).update(order=10000 + index) for index, ot_id in enumerate(original_text_ids): TransmitterOriginalText.objects.filter(id=ot_id).update(order=index) return Response({"status": "success"}) class AdminBookEditionViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] serializer_class = AdminBookEditionSerializer pagination_class = None def get_queryset(self): queryset = BookEdition.objects.all() book_reference_id = self.request.query_params.get("book_reference_id") if book_reference_id: queryset = queryset.filter(book_reference_id=book_reference_id) return queryset.order_by("-created_at") class AdminBookVolumeViewSet(ModelViewSet): permission_classes = [IsAuthenticated, IsSuperAdmin] authentication_classes = [TokenAuthentication] serializer_class = AdminBookVolumeSerializer pagination_class = None def get_queryset(self): queryset = BookVolume.objects.all() book_reference_id = self.request.query_params.get("book_reference_id") edition_id = self.request.query_params.get("edition_id") if book_reference_id: queryset = queryset.filter(book_reference_id=book_reference_id) if edition_id: if edition_id in ["none", "null"]: queryset = queryset.filter(edition__isnull=True) else: queryset = queryset.filter(edition_id=edition_id) return queryset.order_by("title", "id")