From 3e4bb74f179d32cc59f99c4123401baea4a0f0be Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Sat, 4 Jul 2026 17:32:55 +0330 Subject: [PATCH] transmitters orginal texts and opinions apis created for dashboard --- ...ter_transmitteropinion_options_and_more.py | 31 ++++++++++ apps/hadis/models/transmitter.py | 5 +- apps/hadis/serializers/serializers_admin.py | 43 ++++++++++++- apps/hadis/urls.py | 5 +- apps/hadis/views_admin.py | 62 ++++++++++++++++++- 5 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 apps/hadis/migrations/0028_alter_transmitteropinion_options_and_more.py diff --git a/apps/hadis/migrations/0028_alter_transmitteropinion_options_and_more.py b/apps/hadis/migrations/0028_alter_transmitteropinion_options_and_more.py new file mode 100644 index 0000000..f9b7cc0 --- /dev/null +++ b/apps/hadis/migrations/0028_alter_transmitteropinion_options_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.12 on 2026-07-04 17:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0027_transmitterrelative_relation_value_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='transmitteropinion', + options={'ordering': ('order', 'id'), 'verbose_name': 'Transmitter Opinion', 'verbose_name_plural': 'Transmitter Opinions'}, + ), + migrations.AlterModelOptions( + name='transmitteroriginaltext', + options={'ordering': ('order', 'id'), 'verbose_name': 'Transmitter Original Text', 'verbose_name_plural': 'Transmitter Original Text'}, + ), + migrations.AddField( + model_name='transmitteropinion', + name='order', + field=models.IntegerField(default=0, verbose_name='order'), + ), + migrations.AddField( + model_name='transmitteroriginaltext', + name='order', + field=models.IntegerField(default=0, verbose_name='order'), + ), + ] diff --git a/apps/hadis/models/transmitter.py b/apps/hadis/models/transmitter.py index 917c7dd..8b3c504 100644 --- a/apps/hadis/models/transmitter.py +++ b/apps/hadis/models/transmitter.py @@ -612,6 +612,7 @@ class TransmitterOpinion(models.Model): blank = True, null=True ) + order = models.IntegerField(default=0, verbose_name=_('order')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) @@ -622,7 +623,7 @@ class TransmitterOpinion(models.Model): ] verbose_name = _('Transmitter Opinion') verbose_name_plural = _('Transmitter Opinions') - ordering = ('-created_at',) + ordering = ('order', 'id') def __str__(self): scholar = self.scholar_name[0]['text'] if self.scholar_name else "Unknown Scholar" @@ -675,6 +676,7 @@ class TransmitterOriginalText(models.Model): translation = models.JSONField(verbose_name=_('translation'), default=list) share_link = models.CharField(max_length=255, verbose_name=_('share link'), null=True, blank=True) embedded_in = models.JSONField(default=list, blank=True) + order = models.IntegerField(default=0, verbose_name=_('order')) class Meta: indexes = [ @@ -683,6 +685,7 @@ class TransmitterOriginalText(models.Model): ] verbose_name = _('Transmitter Original Text') verbose_name_plural = _('Transmitter Original Text') + ordering = ('order', 'id') def __str__(self): title = self.title[0]['text'] if self.title else "Untitled" diff --git a/apps/hadis/serializers/serializers_admin.py b/apps/hadis/serializers/serializers_admin.py index 0a3a066..456873a 100644 --- a/apps/hadis/serializers/serializers_admin.py +++ b/apps/hadis/serializers/serializers_admin.py @@ -5,7 +5,7 @@ from utils.image_compression import maybe_compress_uploaded_file from ..models import ( Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, - Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus + Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText ) from .category import LocalizedField @@ -759,3 +759,44 @@ class AdminHadisTransmitterSerializer(serializers.ModelSerializer): "order", "is_gap", ] + + +class AdminTransmitterOpinionSerializer(serializers.ModelSerializer): + scholar_name = serializers.JSONField(required=False, default=list) + opinion_text = serializers.JSONField(required=False, default=list) + status_detail = AdminTransmitterReliabilitySerializer(source="status", read_only=True) + + class Meta: + model = TransmitterOpinion + fields = [ + "id", + "transmitter", + "scholar", + "scholar_name", + "opinion_text", + "status", + "status_detail", + "order", + "created_at", + "updated_at", + ] + + +class AdminTransmitterOriginalTextSerializer(serializers.ModelSerializer): + title = serializers.JSONField(required=False, default=list) + text = serializers.JSONField(required=False, default=list) + translation = serializers.JSONField(required=False, default=list) + + class Meta: + model = TransmitterOriginalText + fields = [ + "id", + "transmitter", + "slug", + "title", + "text", + "translation", + "share_link", + "embedded_in", + "order", + ] diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index 0bfea93..d338f3d 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -18,7 +18,8 @@ from .views_admin import ( AdminBookSubjectAreaViewSet, AdminBookTypeViewSet, AdminBookAuthorViewSet, AdminBookAttributeViewSet, AdminBookReferenceImageViewSet, AdminBookReferenceDocumentViewSet, AdminBookReferenceViewSet, AdminTransmitterViewSet, AdminTransmitterReliabilityViewSet, DovodiBookSubjectAreaViewSet, DovodiBookTypeViewSet, DovodiNarratorLayerViewSet, DovodiTransmitterReliabilityViewSet, DovodiOpinionStatusViewSet, - DovodiHadisSectViewSet, DovodiHadisCategoryViewSet, AdminCategoryQuranVerseViewSet, AdminHadisTransmitterViewSet + DovodiHadisSectViewSet, DovodiHadisCategoryViewSet, AdminCategoryQuranVerseViewSet, AdminHadisTransmitterViewSet, + AdminTransmitterOpinionViewSet, AdminTransmitterOriginalTextViewSet ) from .views.reference_v2 import BookAuthorListView, BookAuthorDetailView, AuthorReferencesListView, BookAuthorSyncView from django.views.decorators.cache import cache_page @@ -54,6 +55,8 @@ admin_router.register(r'dovodi-narrator-layers', DovodiNarratorLayerViewSet, bas admin_router.register(r'dovodi-transmitter-reliabilities', DovodiTransmitterReliabilityViewSet, basename='dovodi-transmitter-reliabilities') admin_router.register(r'dovodi-opinion-statuses', DovodiOpinionStatusViewSet, basename='dovodi-opinion-statuses') admin_router.register(r'hadis-transmitters', AdminHadisTransmitterViewSet, basename='admin-hadis-transmitters') +admin_router.register(r'transmitter-opinions', AdminTransmitterOpinionViewSet, basename='admin-transmitter-opinions') +admin_router.register(r'transmitter-original-texts', AdminTransmitterOriginalTextViewSet, basename='admin-transmitter-original-texts') # Hide admin viewsets from swagger for prefix, viewset, basename in admin_router.registry: diff --git a/apps/hadis/views_admin.py b/apps/hadis/views_admin.py index 68fd3c8..a19215a 100644 --- a/apps/hadis/views_admin.py +++ b/apps/hadis/views_admin.py @@ -13,7 +13,7 @@ from utils.pagination import StandardResultsSetPagination from .models import ( Hadis, HadisCategory, HadisCollection, HadisCorrection, HadisInterpretation, HadisStatus, HadisTag, HadisSect, HadisTransmitter, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, - Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus + Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText ) from .serializers.serializers_admin import ( AdminHadisCategorySerializer, @@ -40,6 +40,8 @@ from .serializers.serializers_admin import ( AdminNarratorLayerSerializer, AdminOpinionStatusSerializer, AdminHadisTransmitterSerializer, + AdminTransmitterOpinionSerializer, + AdminTransmitterOriginalTextSerializer, ) @@ -574,3 +576,61 @@ class AdminHadisTransmitterViewSet(ModelViewSet): 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 + pagination_class = None + + def get_queryset(self): + queryset = TransmitterOriginalText.objects.all() + 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"})