diff --git a/apps/hadis/admin/hadis.py b/apps/hadis/admin/hadis.py index ec9191f..1d531da 100644 --- a/apps/hadis/admin/hadis.py +++ b/apps/hadis/admin/hadis.py @@ -757,7 +757,7 @@ class HadisCollectionAdmin(ModelAdmin): fieldsets = ( (None, { - 'fields': ('title', 'slug', 'summary', 'status', 'order','thumbnail') + 'fields': ('title', 'slug', 'summary', 'status','pin_top', 'order','thumbnail') }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), diff --git a/apps/hadis/migrations/0008_hadiscollection_pin_top.py b/apps/hadis/migrations/0008_hadiscollection_pin_top.py new file mode 100644 index 0000000..47267a9 --- /dev/null +++ b/apps/hadis/migrations/0008_hadiscollection_pin_top.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.12 on 2026-04-28 14:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0007_alter_hadisstatus_color_alter_opinionstatus_color_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='hadiscollection', + name='pin_top', + field=models.BooleanField(default=False, verbose_name='pin top'), + ), + ] diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index bf68b07..aa95b29 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -14,6 +14,7 @@ class HadisCollection(models.Model): slug = models.SlugField(max_length=255, unique=True, verbose_name=_('slug'), blank=True) summary = models.JSONField(default = list , verbose_name=_('Summary')) status = models.BooleanField(default=True, verbose_name=_('status')) + pin_top = models.BooleanField(default=False, verbose_name=_('pin top')) order = models.IntegerField(default=0, verbose_name=_('order')) thumbnail = models.ImageField(upload_to='hadis/collection_thumbnails/', null=True, blank=True, help_text=_('image allowed')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index 9a8f4ae..36de333 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -14,7 +14,23 @@ from ..models import ( ) - +class PinnedHadisCollectionSerializer(serializers.ModelSerializer): + title = LocalizedField() + summary = LocalizedField() + thumbnail = serializers.SerializerMethodField() + + class Meta: + model = HadisCollection + # فیلدهایی که برای نمایش در بخش پین شده (مثل اسلایدر یا لیست بالا) نیاز هست + fields = ['id', 'title', 'slug', 'summary', 'thumbnail', 'order', 'created_at'] + + def get_thumbnail(self, obj): + if obj.thumbnail: + request = self.context.get('request') + if request: + return request.build_absolute_uri(obj.thumbnail.url) + return obj.thumbnail.url + return None class HadisCollectionListSerializer(serializers.ModelSerializer): thumbnail = serializers.SerializerMethodField() diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index 9c9cc8c..a4a4037 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -1,6 +1,6 @@ from django.urls import path from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView ,test_deploy,debug_headers,HadisCategoryXMindView -from .views.hadis import HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView,HadisMainListView, HadisFiltersView, HadisLayersView +from .views.hadis import HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView,HadisMainListView, HadisFiltersView, HadisLayersView,PinnedHadisCollectionListView from .views.transmitter import TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView from .views.version import ContentReleaseSyncView @@ -15,6 +15,7 @@ def cached_view(view_func): urlpatterns = [ # Most specific first (with parameters) + path('pinned-collections/', PinnedHadisCollectionListView.as_view(), name='pinned-hadis-collection-list'), path('collections/', HadisCollectionListView.as_view(), name='hadis-collection-list'), path('sync/sects/', HadisCategorySectListView.as_view(), name='hadis-sect-list'), path('sync/categories/tree/', HadisCategoryTreeView.as_view(), name='hadis-category-tree'), diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 6498284..e864800 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -8,10 +8,74 @@ from rest_framework.response import Response from django.db.models import Count from django.db.models import Prefetch from ..serializers.category import get_localized_text -from ..models import HadisCategory, Hadis, HadisCollection,HadisTransmitter , HadisCorrection ,HadisReference, HadisStatus ,ReferenceImage -from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer,HadisCorrectionSerializer,HadisTransmitterListSerializer , SimpleCategory, NarratorLayerSerializer +from ..models import Transmitters, HadisCategory, Hadis, HadisCollection,HadisTransmitter , HadisCorrection ,HadisReference, HadisStatus ,ReferenceImage +from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer,HadisCorrectionSerializer,HadisTransmitterListSerializer , SimpleCategory, NarratorLayerSerializer , PinnedHadisCollectionSerializer from ..docs import arguments_filters_swagger ,hadis_list_swagger, hadis_detail_swagger, hadis_collections_swagger, hadis_sync_swagger, hadis_transmitters_swagger, hadis_corrections_swagger, hadis_basic_swagger, hadis_main_list_swagger, hadis_layers_swagger +from rest_framework import status + +class PinnedHadisCollectionListView(ListAPIView): + """ + API view to list pinned hadis collections with metadata info + """ + serializer_class = PinnedHadisCollectionSerializer + pagination_class = NoPagination + permission_classes = [IsAuthenticated] + authentication_classes = [TokenAuthentication] + + @hadis_collections_swagger + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def get_queryset(self): + return HadisCollection.objects.filter( + status=True, + pin_top=True + ).order_by('order', '-created_at') + + def list(self, request, *args, **kwargs): + # 1. گرفتن خروجی استاندارد لیست مجموعه‌ها + response = super().list(request, *args, **kwargs) + + # 2. محاسبه آمار (Info) + # تعداد کتگوری‌های فعال + categories_count = HadisCategory.objects.count() + + # تعداد نریتورها (راویان) + narrators_count = Transmitters.objects.count() + + # تعداد سورس‌ها (کتاب‌های منبع) + # فرض بر این است که BookReference مدل منابع شماست + from apps.hadis.models import BookReference + sources_count = BookReference.objects.count() + + # تعداد بوکمارک‌های کاربر در سرویس حدیث + from apps.bookmark.models.bookmark import Bookmark + bookmarks_count = Bookmark.objects.filter( + user=request.user, + service=Bookmark.ServiceChoices.HADITH, + status=True + ).count() + + # 3. ساختن دیکشنری info + info = { + "categories_count": categories_count, + # "bookmarks_count": bookmarks_count, + "narrators_count": narrators_count, + "sources_count": sources_count, + } + + # 4. بازسازی دیتای نهایی مشابه اپ ویدیو + custom_data = { + "count": len(response.data) if isinstance(response.data, list) else response.data.get("count"), + "next": response.data.get("next") if isinstance(response.data, dict) else None, + "previous": response.data.get("previous") if isinstance(response.data, dict) else None, + "info": info, + "results": response.data if isinstance(response.data, list) else response.data.get("results") + } + + return Response(custom_data, status=status.HTTP_200_OK) + class HadisCollectionListView(ListAPIView): """