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.
1038 lines
42 KiB
1038 lines
42 KiB
from django.db.models import Q, Exists, OuterRef
|
|
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, OptionalPagination
|
|
|
|
from .models import (
|
|
Hadis, HadisCategory, HadisCollection, HadisInCollection, HadisCorrection, HadisInterpretation, HadisStatus, HadisTag, HadisSect, HadisTransmitter, HadisReference, ReferenceImage,
|
|
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookTag, BookEdition, BookVolume,
|
|
Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText,
|
|
CorrectionReference,
|
|
ContentRelease
|
|
)
|
|
from .serializers.serializers_admin import (
|
|
AdminHadisCategorySerializer,
|
|
AdminHadisSectArchiveSerializer,
|
|
AdminHadisCategoryArchiveSerializer,
|
|
AdminHadisCategoryArchiveDetailSerializer,
|
|
AdminHadisCorrectionSerializer,
|
|
AdminCorrectionReferenceSerializer,
|
|
AdminHadisDetailSerializer,
|
|
AdminHadisInterpretationSerializer,
|
|
AdminHadisCollectionSerializer,
|
|
AdminHadisListSerializer,
|
|
AdminHadisBriefSerializer,
|
|
AdminHadisStatusSerializer,
|
|
AdminHadisTagSerializer,
|
|
AdminBookSubjectAreaSerializer,
|
|
AdminBookTypeSerializer,
|
|
AdminBookTagSerializer,
|
|
AdminBookAuthorSerializer,
|
|
AdminBookAttributeSerializer,
|
|
AdminBookReferenceImageSerializer,
|
|
AdminHadisReferenceImageSerializer,
|
|
AdminBookReferenceDocumentSerializer,
|
|
AdminBookReferenceListSerializer,
|
|
AdminBookReferenceDetailSerializer,
|
|
AdminTransmitterListSerializer,
|
|
AdminTransmitterDetailSerializer,
|
|
AdminTransmitterReliabilitySerializer,
|
|
AdminNarratorLayerSerializer,
|
|
AdminOpinionStatusSerializer,
|
|
AdminHadisTransmitterSerializer,
|
|
AdminTransmitterOpinionSerializer,
|
|
AdminTransmitterOriginalTextSerializer,
|
|
AdminBookEditionSerializer,
|
|
AdminBookVolumeSerializer,
|
|
AdminContentReleaseSerializer,
|
|
)
|
|
|
|
|
|
class AdminHadisCategoryViewSet(ReadOnlyModelViewSet):
|
|
serializer_class = AdminHadisCategorySerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
pagination_class = OptionalPagination
|
|
|
|
def get_queryset(self):
|
|
hadis_subquery = Hadis.objects.filter(category=OuterRef("pk"))
|
|
children_subquery = HadisCategory.objects.filter(parent=OuterRef("pk"))
|
|
queryset = HadisCategory.objects.select_related("parent", "sect").annotate(
|
|
annotated_has_hadith=Exists(hadis_subquery),
|
|
annotated_has_children=Exists(children_subquery),
|
|
)
|
|
source_type = self.request.query_params.get("source_type")
|
|
if source_type:
|
|
queryset = queryset.filter(source_type=source_type)
|
|
search = self.request.query_params.get("search")
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(title__icontains=search) | Q(slug__icontains=search)
|
|
)
|
|
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_serializer_class(self):
|
|
if self.action in ["retrieve"]:
|
|
return AdminHadisCategoryArchiveDetailSerializer
|
|
return AdminHadisCategoryArchiveSerializer
|
|
|
|
def get_queryset(self):
|
|
hadis_subquery = Hadis.objects.filter(category=OuterRef("pk"))
|
|
children_subquery = HadisCategory.objects.filter(parent=OuterRef("pk"))
|
|
queryset = HadisCategory.objects.select_related("parent", "sect").annotate(
|
|
annotated_has_hadith=Exists(hadis_subquery),
|
|
annotated_has_children=Exists(children_subquery),
|
|
)
|
|
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("-id")
|
|
|
|
|
|
class AdminHadisStatusViewSet(ModelViewSet):
|
|
serializer_class = AdminHadisStatusSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
pagination_class = OptionalPagination
|
|
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("-created_at", "-id")
|
|
|
|
@action(detail=True, methods=["post"], url_path="assign-items")
|
|
def assign_items(self, request, pk=None):
|
|
collection = self.get_object()
|
|
add_ids = request.data.get("add_ids", []) or request.data.get("item_ids", [])
|
|
remove_ids = request.data.get("remove_ids", [])
|
|
|
|
if add_ids:
|
|
new_items = [
|
|
HadisInCollection(hadis_id=hid, collection_id=collection.id)
|
|
for hid in add_ids
|
|
]
|
|
HadisInCollection.objects.bulk_create(new_items, ignore_conflicts=True)
|
|
|
|
if remove_ids:
|
|
HadisInCollection.objects.filter(collection_id=collection.id, hadis_id__in=remove_ids).delete()
|
|
|
|
return Response({"status": "success", "attached_count": collection.hadis_items.count()})
|
|
|
|
|
|
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.select_related("hadis").all()
|
|
hadis_id = self.request.query_params.get("hadis") or self.request.query_params.get("hadis_id")
|
|
if hadis_id:
|
|
queryset = queryset.filter(hadis_id=hadis_id)
|
|
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)
|
|
| Q(slug__icontains=search_query)
|
|
| Q(legacy_id__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("-created_at", "-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":
|
|
if self.request.query_params.get("brief") == "true":
|
|
return AdminHadisBriefSerializer
|
|
return AdminHadisListSerializer
|
|
return AdminHadisDetailSerializer
|
|
|
|
def get_queryset(self):
|
|
queryset = (
|
|
Hadis.objects.all()
|
|
.select_related("category", "hadis_status")
|
|
.prefetch_related("collection_items__collection")
|
|
)
|
|
|
|
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)
|
|
|
|
collection_id = self.request.query_params.get("collection") or self.request.query_params.get("collection_id")
|
|
if collection_id and collection_id != "all":
|
|
queryset = queryset.filter(collection_items__collection_id=collection_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.order_by("-id")
|
|
|
|
|
|
class AdminHadisCorrectionViewSet(ModelViewSet):
|
|
serializer_class = AdminHadisCorrectionSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
parser_classes = (MultiPartParser, FormParser, JSONParser)
|
|
pagination_class = OptionalPagination
|
|
|
|
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)
|
|
search = self.request.query_params.get("search")
|
|
if search:
|
|
from django.db.models import Q
|
|
queryset = queryset.filter(
|
|
Q(title__icontains=search) | Q(text__icontains=search)
|
|
)
|
|
return queryset.order_by("-created_at", "-id")
|
|
|
|
def perform_create(self, serializer):
|
|
priority = self.request.data.get("priority", 1)
|
|
try:
|
|
priority = int(priority)
|
|
except (ValueError, TypeError):
|
|
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 AdminCorrectionReferenceViewSet(ModelViewSet):
|
|
serializer_class = AdminCorrectionReferenceSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
parser_classes = (MultiPartParser, FormParser, JSONParser)
|
|
pagination_class = OptionalPagination
|
|
|
|
def get_queryset(self):
|
|
qs = CorrectionReference.objects.all().select_related(
|
|
"book_reference__author", "edition", "book_volume"
|
|
).prefetch_related("images")
|
|
correction_id = self.request.query_params.get("correction_id")
|
|
if correction_id:
|
|
qs = qs.filter(correction_id=correction_id)
|
|
return qs.order_by("id")
|
|
|
|
|
|
class AdminHadisInterpretationViewSet(ModelViewSet):
|
|
serializer_class = AdminHadisInterpretationSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
parser_classes = (MultiPartParser, FormParser, JSONParser)
|
|
pagination_class = OptionalPagination
|
|
|
|
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)
|
|
search = self.request.query_params.get("search")
|
|
if search:
|
|
from django.db.models import Q
|
|
queryset = queryset.filter(
|
|
Q(title__icontains=search) | Q(text__icontains=search)
|
|
)
|
|
return queryset.order_by("-created_at", "-id")
|
|
|
|
def perform_create(self, serializer):
|
|
priority = self.request.data.get("priority", 1)
|
|
try:
|
|
priority = int(priority)
|
|
except (ValueError, TypeError):
|
|
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("-created_at", "-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("-created_at", "-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("-created_at", "-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("-created_at", "-id")
|
|
|
|
|
|
class AdminBookAuthorViewSet(ModelViewSet):
|
|
serializer_class = AdminBookAuthorSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
pagination_class = None
|
|
parser_classes = (MultiPartParser, FormParser, JSONParser)
|
|
|
|
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("-created_at", "-id")
|
|
|
|
@action(detail=True, methods=["post"], url_path="assign-references")
|
|
def assign_references(self, request, pk=None):
|
|
author = self.get_object()
|
|
add_ids = request.data.get("add_ids", []) or request.data.get("book_ids", []) or request.data.get("item_ids", [])
|
|
remove_ids = request.data.get("remove_ids", [])
|
|
|
|
if add_ids:
|
|
BookReference.objects.filter(id__in=add_ids).update(author=author)
|
|
|
|
if remove_ids:
|
|
BookReference.objects.filter(id__in=remove_ids, author=author).update(author=None)
|
|
|
|
return Response({"status": "success", "references_count": author.book_references.count()})
|
|
|
|
|
|
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")
|
|
|
|
@action(detail=False, methods=["post"])
|
|
def reorder(self, request):
|
|
image_ids = request.data.get("image_ids", []) or request.data.get("ids", [])
|
|
if not image_ids:
|
|
return Response({"error": "image_ids list is required"}, status=400)
|
|
|
|
for index, img_id in enumerate(image_ids, start=1):
|
|
BookReferenceImage.objects.filter(id=img_id).update(order=index)
|
|
|
|
return Response({"status": "success"})
|
|
|
|
|
|
class AdminHadisReferenceImageViewSet(ModelViewSet):
|
|
serializer_class = AdminHadisReferenceImageSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
parser_classes = (MultiPartParser, FormParser, JSONParser)
|
|
pagination_class = None
|
|
|
|
def get_queryset(self):
|
|
queryset = ReferenceImage.objects.all().select_related("reference")
|
|
hadis_id = self.request.query_params.get("hadis_id")
|
|
if hadis_id:
|
|
queryset = queryset.filter(reference__hadis_id=hadis_id)
|
|
reference_id = self.request.query_params.get("reference_id")
|
|
if reference_id:
|
|
queryset = queryset.filter(reference_id=reference_id)
|
|
return queryset.order_by("priority", "id")
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
hadis_id = request.data.get("hadis_id")
|
|
reference_id = request.data.get("reference_id")
|
|
thumbnail = request.FILES.get("thumbnail") or request.FILES.get("image")
|
|
priority = request.data.get("priority", 0)
|
|
|
|
if not thumbnail:
|
|
return Response({"error": "thumbnail image is required"}, status=400)
|
|
|
|
if hadis_id:
|
|
try:
|
|
hadis = Hadis.objects.get(id=hadis_id)
|
|
except Hadis.DoesNotExist:
|
|
return Response({"error": "Hadis not found"}, status=404)
|
|
reference = hadis.references.first()
|
|
if not reference:
|
|
reference = HadisReference.objects.create(hadis=hadis)
|
|
elif reference_id:
|
|
try:
|
|
reference = HadisReference.objects.get(id=reference_id)
|
|
except HadisReference.DoesNotExist:
|
|
return Response({"error": "HadisReference not found"}, status=404)
|
|
else:
|
|
return Response({"error": "hadis_id or reference_id is required"}, status=400)
|
|
|
|
if not priority:
|
|
last_img = reference.images.order_by("-priority").first()
|
|
priority = (last_img.priority + 1) if last_img else 1
|
|
else:
|
|
try:
|
|
priority = int(priority)
|
|
except (ValueError, TypeError):
|
|
priority = 1
|
|
|
|
ref_image = ReferenceImage.objects.create(
|
|
reference=reference,
|
|
thumbnail=thumbnail,
|
|
priority=priority
|
|
)
|
|
serializer = self.get_serializer(ref_image)
|
|
return Response(serializer.data, status=201)
|
|
|
|
@action(detail=False, methods=["post"])
|
|
def reorder(self, request):
|
|
image_ids = request.data.get("image_ids", []) or request.data.get("ids", [])
|
|
if not image_ids:
|
|
return Response({"error": "image_ids list is required"}, status=400)
|
|
|
|
for index, img_id in enumerate(image_ids, start=1):
|
|
ReferenceImage.objects.filter(id=img_id).update(priority=index)
|
|
|
|
return Response({"status": "success"})
|
|
|
|
@action(detail=False, methods=["post"])
|
|
def bulk_delete(self, request):
|
|
image_ids = request.data.get("ids", []) or request.data.get("image_ids", [])
|
|
if not image_ids:
|
|
return Response({"error": "ids list is required"}, status=400)
|
|
|
|
deleted_count, _ = ReferenceImage.objects.filter(id__in=image_ids).delete()
|
|
return Response({"status": "success", "deleted_count": deleted_count})
|
|
|
|
|
|
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", "volumes", "editions").select_related("type", "author")
|
|
|
|
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(editions__isbn__icontains=search_query)
|
|
| Q(editions__publisher__icontains=search_query)
|
|
| Q(description__icontains=search_query)
|
|
| Q(author__name__icontains=search_query)
|
|
| Q(tag__title__icontains=search_query)
|
|
| Q(editions__tag__title__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(author_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").prefetch_related("uncertain_transmitters")
|
|
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 AdminBookTagViewSet(ModelViewSet):
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
serializer_class = AdminBookTagSerializer
|
|
pagination_class = None
|
|
parser_classes = (JSONParser, MultiPartParser, FormParser)
|
|
|
|
def get_queryset(self):
|
|
queryset = BookTag.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 AdminBookVolumeViewSet(ModelViewSet):
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
serializer_class = AdminBookVolumeSerializer
|
|
pagination_class = None
|
|
parser_classes = (MultiPartParser, FormParser, JSONParser)
|
|
|
|
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")
|
|
|
|
|
|
import re
|
|
|
|
def compute_next_version(current_version_str: str) -> str:
|
|
"""
|
|
Increments the last integer part of a version string.
|
|
Examples:
|
|
'V 0.51' -> 'V 0.52'
|
|
'V0.5' -> 'V0.6'
|
|
'1.0.0' -> '1.0.1'
|
|
'1' -> '2'
|
|
'v1.9' -> 'v1.10'
|
|
"""
|
|
if not current_version_str or not str(current_version_str).strip():
|
|
return "V 1.0"
|
|
|
|
matches = list(re.finditer(r'\d+', str(current_version_str)))
|
|
if not matches:
|
|
return f"{str(current_version_str).strip()} 2"
|
|
|
|
last_match = matches[-1]
|
|
start, end = last_match.span()
|
|
num_str = last_match.group()
|
|
next_num = int(num_str) + 1
|
|
|
|
if len(num_str) > 1 and num_str.startswith('0'):
|
|
formatted_num = str(next_num).zfill(len(num_str))
|
|
else:
|
|
formatted_num = str(next_num)
|
|
|
|
return str(current_version_str)[:start] + formatted_num + str(current_version_str)[end:]
|
|
|
|
|
|
class AdminContentReleaseViewSet(ModelViewSet):
|
|
serializer_class = AdminContentReleaseSerializer
|
|
permission_classes = [IsAuthenticated, IsSuperAdmin]
|
|
authentication_classes = [TokenAuthentication]
|
|
pagination_class = None
|
|
|
|
def get_queryset(self):
|
|
return ContentRelease.objects.all().order_by("-published_at", "-id")
|
|
|
|
def list(self, request, *args, **kwargs):
|
|
queryset = self.get_queryset()
|
|
current_release = queryset.filter(is_active=True).first()
|
|
current_data = self.get_serializer(current_release).data if current_release else None
|
|
|
|
current_ver_name = current_release.version_name if current_release else "V 0.5"
|
|
next_suggested = compute_next_version(current_ver_name)
|
|
|
|
history_data = self.get_serializer(queryset[:50], many=True).data
|
|
|
|
return Response({
|
|
"current_version": current_data,
|
|
"next_suggested_version": next_suggested,
|
|
"history": history_data
|
|
})
|
|
|
|
@action(detail=False, methods=["post"], url_path="increment")
|
|
def increment(self, request):
|
|
current_release = self.get_queryset().filter(is_active=True).first()
|
|
current_ver_name = current_release.version_name if current_release else "V 0.5"
|
|
|
|
version_name = request.data.get("version_name")
|
|
if not version_name or not str(version_name).strip():
|
|
version_name = compute_next_version(current_ver_name)
|
|
else:
|
|
version_name = str(version_name).strip()
|
|
|
|
description = request.data.get("description", "") or ""
|
|
|
|
# Create new content release
|
|
new_release = ContentRelease.objects.create(
|
|
version_name=version_name,
|
|
description=description,
|
|
is_active=True
|
|
)
|
|
|
|
from .signals import invalidate_hadis_cache
|
|
invalidate_hadis_cache()
|
|
|
|
# تریگر خودکار ساخت و فشردهسازی دیتابیس SQLite در پسزمینه
|
|
self._trigger_sqlite_export(notes=f"Auto-generated for version {version_name}")
|
|
|
|
return Response({
|
|
"status": "success",
|
|
"message": f"Version incremented to {version_name}",
|
|
"release": self.get_serializer(new_release).data,
|
|
"next_suggested_version": compute_next_version(version_name)
|
|
})
|
|
|
|
def _trigger_sqlite_export(self, notes=""):
|
|
"""اجرای پسزمینه دستور تولید دیتابیس SQLite بدون متوقف کردن پاسخ HTTP"""
|
|
import threading
|
|
import logging
|
|
from django.core.management import call_command
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def _task():
|
|
try:
|
|
logger.info("Starting background SQLite export...")
|
|
call_command('export_hadis_sqlite', notes=notes)
|
|
logger.info("Background SQLite export completed successfully.")
|
|
except Exception as e:
|
|
logger.error(f"Failed to export SQLite in background: {e}", exc_info=True)
|
|
|
|
thread = threading.Thread(target=_task, daemon=True)
|
|
thread.start()
|
|
|
|
def perform_create(self, serializer):
|
|
instance = serializer.save()
|
|
from .signals import invalidate_hadis_cache
|
|
invalidate_hadis_cache()
|
|
self._trigger_sqlite_export(notes=f"Auto-generated for created version {instance.version_name}")
|
|
|
|
def perform_update(self, serializer):
|
|
super().perform_update(serializer)
|
|
from .signals import invalidate_hadis_cache
|
|
invalidate_hadis_cache()
|
|
|
|
def perform_destroy(self, instance):
|
|
super().perform_destroy(instance)
|
|
from .signals import invalidate_hadis_cache
|
|
invalidate_hadis_cache()
|
|
|
|
|