From 12d0d801ea14baf580dcb669c3e7d1b86b6a7b80 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Mon, 15 Jun 2026 10:43:21 +0330 Subject: [PATCH] endpoints for admin archive page for narators and references added --- apps/hadis/serializers/serializers_admin.py | 159 ++++++++++++++++- apps/hadis/urls.py | 14 +- apps/hadis/views_admin.py | 178 +++++++++++++++++++- 3 files changed, 336 insertions(+), 15 deletions(-) diff --git a/apps/hadis/serializers/serializers_admin.py b/apps/hadis/serializers/serializers_admin.py index 577ced0..4465410 100644 --- a/apps/hadis/serializers/serializers_admin.py +++ b/apps/hadis/serializers/serializers_admin.py @@ -1,8 +1,11 @@ from rest_framework import serializers +from django.core.files.uploadedfile import SimpleUploadedFile + +from utils.image_compression import maybe_compress_uploaded_file from ..models import ( - Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisSect, + Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, - Transmitters, TransmitterReliability + Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus ) from .category import LocalizedField @@ -27,8 +30,8 @@ class AdminHadisCategorySerializer(serializers.ModelSerializer): class AdminHadisStatusSerializer(serializers.ModelSerializer): - title = LocalizedField() - description = LocalizedField() + title = serializers.JSONField() + description = serializers.JSONField() class Meta: model = HadisStatus @@ -36,7 +39,7 @@ class AdminHadisStatusSerializer(serializers.ModelSerializer): class AdminHadisTagSerializer(serializers.ModelSerializer): - title = LocalizedField() + title = serializers.JSONField() class Meta: model = HadisTag @@ -137,11 +140,14 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer): class AdminHadisCorrectionSerializer(serializers.ModelSerializer): + hadis_detail = AdminHadisListSerializer(source="hadis", read_only=True) + class Meta: model = HadisCorrection fields = [ "id", "hadis", + "hadis_detail", "title", "slug", "text", @@ -154,11 +160,14 @@ class AdminHadisCorrectionSerializer(serializers.ModelSerializer): class AdminHadisInterpretationSerializer(serializers.ModelSerializer): + category_detail = AdminHadisCategorySerializer(source="category", read_only=True) + class Meta: model = HadisInterpretation fields = [ "id", "category", + "category_detail", "title", "text", "translation", @@ -191,12 +200,146 @@ class AbsoluteFileField(serializers.FileField): return url +class AdminHadisSectArchiveSerializer(serializers.ModelSerializer): + title = serializers.JSONField() + description = serializers.JSONField() + + class Meta: + model = HadisSect + fields = ["id", "sect_type", "title", "description", "is_active", "order"] + + +class AdminHadisCategoryArchiveParentSerializer(serializers.ModelSerializer): + title = serializers.JSONField() + + class Meta: + model = HadisCategory + fields = ["id", "title", "slug"] + + +class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer): + parent_detail = AdminHadisCategoryArchiveParentSerializer(source="parent", read_only=True) + sect_detail = AdminHadisSectArchiveSerializer(source="sect", read_only=True) + title = serializers.JSONField() + description = serializers.JSONField() + xmind_file = AbsoluteFileField(required=False, allow_null=True) + remove_xmind_file = serializers.BooleanField(write_only=True, required=False, default=False) + + parent = serializers.PrimaryKeyRelatedField( + queryset=HadisCategory.objects.all(), + required=False, + allow_null=True, + ) + sect = serializers.PrimaryKeyRelatedField( + queryset=HadisSect.objects.all(), + required=True, + ) + + class Meta: + model = HadisCategory + fields = [ + "id", + "parent", + "parent_detail", + "sect", + "sect_detail", + "source_type", + "title", + "description", + "order", + "xmind_file", + "remove_xmind_file", + "slug", + "share_link", + "xmind_share_link", + ] + read_only_fields = ["id", "share_link", "xmind_share_link"] + + def create(self, validated_data): + validated_data.pop("remove_xmind_file", False) + return super().create(validated_data) + + def update(self, instance, validated_data): + remove_xmind_file = validated_data.pop("remove_xmind_file", False) + if remove_xmind_file and instance.xmind_file: + instance.xmind_file.delete(save=False) + instance.xmind_file = None + return super().update(instance, validated_data) + + +class AdminHadisCollectionSerializer(serializers.ModelSerializer): + title = serializers.JSONField() + summary = serializers.JSONField() + thumbnail = AbsoluteImageField(required=False, allow_null=True) + remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False) + + class Meta: + model = HadisCollection + fields = [ + "id", + "title", + "slug", + "summary", + "status", + "pin_top", + "order", + "thumbnail", + "remove_thumbnail", + "created_at", + "updated_at", + ] + read_only_fields = ["id", "created_at", "updated_at"] + + def to_internal_value(self, data): + mutable_data = data.copy() if hasattr(data, "copy") else data + thumbnail = mutable_data.get("thumbnail") if hasattr(mutable_data, "get") else None + if thumbnail and hasattr(thumbnail, "read"): + compressed_bytes = maybe_compress_uploaded_file(thumbnail) + if compressed_bytes is not None: + mutable_data = mutable_data.copy() if hasattr(mutable_data, "copy") else mutable_data + mutable_data["thumbnail"] = SimpleUploadedFile( + name=getattr(thumbnail, "name", "image"), + content=compressed_bytes, + content_type=getattr(thumbnail, "content_type", None), + ) + return super().to_internal_value(mutable_data) + + def create(self, validated_data): + validated_data.pop("remove_thumbnail", False) + return super().create(validated_data) + + def update(self, instance, validated_data): + remove_thumbnail = validated_data.pop("remove_thumbnail", False) + if remove_thumbnail and instance.thumbnail: + instance.thumbnail.delete(save=False) + instance.thumbnail = None + return super().update(instance, validated_data) + + class AdminTransmitterReliabilitySerializer(serializers.ModelSerializer): class Meta: model = TransmitterReliability fields = ["id", "title", "slug", "color", "main_color_code"] +class AdminNarratorLayerSerializer(serializers.ModelSerializer): + name = serializers.JSONField() + description = serializers.JSONField() + + class Meta: + model = NarratorLayer + fields = ["id", "name", "number", "description", "slug", "created_at", "updated_at"] + read_only_fields = ["id", "created_at", "updated_at"] + + +class AdminOpinionStatusSerializer(serializers.ModelSerializer): + title = serializers.JSONField() + + class Meta: + model = OpinionStatus + fields = ["id", "title", "slug", "color", "main_color_code"] + + class AdminTransmitterListSerializer(serializers.ModelSerializer): reliability_detail = AdminTransmitterReliabilitySerializer(source="reliability", read_only=True) full_name = serializers.JSONField() @@ -279,13 +422,15 @@ class AdminTransmitterDetailSerializer(serializers.ModelSerializer): class AdminBookSubjectAreaSerializer(serializers.ModelSerializer): class Meta: model = BookSubjectArea - fields = ["id", "title"] + fields = ["id", "title", "created_at", "updated_at"] + read_only_fields = ["id", "created_at", "updated_at"] class AdminBookTypeSerializer(serializers.ModelSerializer): class Meta: model = BookType - fields = ["id", "title"] + fields = ["id", "title", "created_at", "updated_at"] + read_only_fields = ["id", "created_at", "updated_at"] class AdminBookAuthorSerializer(serializers.ModelSerializer): diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index 19f64ca..7548dfe 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -7,9 +7,11 @@ from .views.reference import BookDetailView, BookReferencesView, BookReferenceSy from .views.version import ContentReleaseSyncView from .views.info import HadisInfoView from .views_admin import ( - AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, + AdminHadisCategoryViewSet, AdminHadisViewSet, AdminHadisCorrectionViewSet, AdminHadisInterpretationViewSet, AdminHadisStatusViewSet, AdminHadisTagViewSet, AdminHadisCollectionViewSet, AdminBookSubjectAreaViewSet, AdminBookTypeViewSet, AdminBookAuthorViewSet, AdminBookAttributeViewSet, AdminBookReferenceImageViewSet, AdminBookReferenceDocumentViewSet, AdminBookReferenceViewSet, - AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet + AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet, DovodiBookSubjectAreaViewSet, DovodiBookTypeViewSet, + DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, + DovodiHadisSectViewSet, DovodiHadisCategoryViewSet ) from django.views.decorators.cache import cache_page from django.views.decorators.vary import vary_on_headers @@ -21,19 +23,27 @@ def cached_view(view_func): admin_router = SimpleRouter() admin_router.register(r'hadises', AdminHadisViewSet, basename='admin-hadises') admin_router.register(r'categories', AdminHadisCategoryViewSet, basename='admin-hadis-categories') +admin_router.register(r'dovodi-sects', DovodiHadisSectViewSet, basename='dovodi-hadis-sects') +admin_router.register(r'dovodi-categories', DovodiHadisCategoryViewSet, basename='dovodi-hadis-categories') admin_router.register(r'corrections', AdminHadisCorrectionViewSet, basename='admin-hadis-corrections') admin_router.register(r'interpretations', AdminHadisInterpretationViewSet, basename='admin-hadis-interpretations') admin_router.register(r'statuses', AdminHadisStatusViewSet, basename='admin-hadis-statuses') admin_router.register(r'tags', AdminHadisTagViewSet, basename='admin-hadis-tags') +admin_router.register(r'collections', AdminHadisCollectionViewSet, basename='admin-hadis-collections') admin_router.register(r'references', AdminBookReferenceViewSet, basename='admin-book-references') admin_router.register(r'subject-areas', AdminBookSubjectAreaViewSet, basename='admin-book-subject-areas') admin_router.register(r'book-types', AdminBookTypeViewSet, basename='admin-book-types') +admin_router.register(r'dovodi-subject-areas', DovodiBookSubjectAreaViewSet, basename='dovodi-book-subject-areas') +admin_router.register(r'dovodi-book-types', DovodiBookTypeViewSet, basename='dovodi-book-types') admin_router.register(r'book-authors', AdminBookAuthorViewSet, basename='admin-book-authors') admin_router.register(r'book-attributes', AdminBookAttributeViewSet, basename='admin-book-attributes') admin_router.register(r'book-images', AdminBookReferenceImageViewSet, basename='admin-book-images') admin_router.register(r'book-documents', AdminBookReferenceDocumentViewSet, basename='admin-book-documents') admin_router.register(r'transmitters', AdminTransmitterViewSet, basename='admin-transmitters') admin_router.register(r'transmitter-reliabilities', AdminTransmitterReliabilityViewSet, basename='admin-transmitter-reliabilities') +admin_router.register(r'dovodi-narrator-layers', DovodiNarratorLayerViewSet, basename='dovodi-narrator-layers') +admin_router.register(r'dovodi-transmitter-reliabilities', DovodiTransmitterReliabilityViewSet, basename='dovodi-transmitter-reliabilities') +admin_router.register(r'dovodi-opinion-statuses', DovodiOpinionStatusViewSet, basename='dovodi-opinion-statuses') urlpatterns = [ # Admin endpoints diff --git a/apps/hadis/views_admin.py b/apps/hadis/views_admin.py index db7894a..9ae331c 100644 --- a/apps/hadis/views_admin.py +++ b/apps/hadis/views_admin.py @@ -1,4 +1,5 @@ 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 @@ -10,15 +11,18 @@ from apps.account.permissions import IsSuperAdmin from utils.pagination import StandardResultsSetPagination from .models import ( - Hadis, HadisCategory, HadisCorrection, HadisInterpretation, HadisStatus, HadisTag, + Hadis, HadisCategory, HadisCollection, HadisCorrection, HadisInterpretation, HadisStatus, HadisTag, HadisSect, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, - Transmitters, TransmitterReliability + Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus ) from .serializers.serializers_admin import ( AdminHadisCategorySerializer, + AdminHadisSectArchiveSerializer, + AdminHadisCategoryArchiveSerializer, AdminHadisCorrectionSerializer, AdminHadisDetailSerializer, AdminHadisInterpretationSerializer, + AdminHadisCollectionSerializer, AdminHadisListSerializer, AdminHadisStatusSerializer, AdminHadisTagSerializer, @@ -33,6 +37,8 @@ from .serializers.serializers_admin import ( AdminTransmitterListSerializer, AdminTransmitterDetailSerializer, AdminTransmitterReliabilitySerializer, + AdminNarratorLayerSerializer, + AdminOpinionStatusSerializer, ) @@ -46,24 +52,99 @@ class AdminHadisCategoryViewSet(ReadOnlyModelViewSet): return HadisCategory.objects.all().order_by("order", "id") -class AdminHadisStatusViewSet(ReadOnlyModelViewSet): +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) + ) + 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): - return HadisStatus.objects.all().order_by("order", "id") + 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(ReadOnlyModelViewSet): +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): - return HadisTag.objects.all().order_by("id") + 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): @@ -76,6 +157,61 @@ class AdminTransmitterReliabilityViewSet(ReadOnlyModelViewSet): 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] @@ -229,6 +365,36 @@ class AdminBookTypeViewSet(ReadOnlyModelViewSet): 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]