From bcd515ea9d4f041eebb2ce38299e774256066047 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Tue, 9 Jun 2026 14:36:20 +0330 Subject: [PATCH] hadis filters endpoint added --- apps/hadis/urls.py | 3 +- apps/hadis/views/hadis.py | 156 ++++++++++++++++++++++++++++++++++++++ entrypoint.sh | 2 +- 3 files changed, 159 insertions(+), 2 deletions(-) diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index a4a4037..9746cbe 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,PinnedHadisCollectionListView +from .views.hadis import HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView,HadisMainListView, HadisFiltersView, HadisLayersView,PinnedHadisCollectionListView, HadisFiltersSyncAPIView from .views.transmitter import TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView from .views.version import ContentReleaseSyncView @@ -23,6 +23,7 @@ urlpatterns = [ path('sync/narrators/', TransmitterSyncView.as_view(), name='transmitter-sync'), path('sync/references/', BookReferenceSyncView.as_view(), name='reference-sync'), path('sync/version/', ContentReleaseSyncView.as_view(), name='content-release-sync'), + path('sync/filters/', HadisFiltersSyncAPIView.as_view(), name='hadis-filters-sync'), path('info/', HadisInfoView.as_view(), name='hadis-info'), # Category paths (more specific first) diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 5cb3762..22349fe 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -620,3 +620,159 @@ class HadisFiltersView(ListAPIView): } return Response(response_data) + +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.permissions import AllowAny +from rest_framework.authentication import TokenAuthentication +from drf_yasg.utils import swagger_auto_schema +from django.utils.translation import get_language + +from apps.hadis.models import HadisStatus, HadisCategory, BookReference, Transmitters +from drf_yasg import openapi +class HadisFiltersSyncAPIView(APIView): + """ + API view to return all filter data for hadis (Sync API for Mobile App) + Returns statuses, categories, sources (books), and narrators. + Optimized for offline client-side searching and sorting. + """ + permission_classes = [AllowAny] + authentication_classes = [TokenAuthentication] + + @swagger_auto_schema( + operation_description=( + "Sync API for Flutter/mobile clients: returns flat hadis filter data for offline " + "searching and local filtering. Titles are localized from multilingual fields based " + "on the active request language." + ), + operation_summary="Sync Hadis Filters", + tags=['Dobodbi - Hadis'], + manual_parameters=[ + openapi.Parameter( + 'Accept-Language', + openapi.IN_HEADER, + description="Language code for localized titles, for example `en`, `fa`, `ar`, `ur`, or `ru`.", + type=openapi.TYPE_STRING, + required=False, + ) + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="Flat filter payload for hadis categories, statuses, sources, and narrators.", + examples={ + "application/json": { + "statuses": [ + { + "id": 133, + "slug": "sahih", + "title": "Authentic", + "order": 1, + "color": "green", + "main_color_code": "#1DAC43" + } + ], + "categories": [ + { + "id": 1036, + "slug": "general-hadiths", + "title": "General Hadiths", + "order": 1 + } + ], + "sources": [ + { + "id": 2538, + "slug": "man-la-yahduruhu-al-faqih", + "title": "Man La Yahduruhu al-Faqih", + "search_keywords": "Shaykh al-Saduq" + } + ], + "narrators": [ + { + "id": 501, + "slug": "muhammad-ibn-muslim", + "title": "Muhammad ibn Muslim", + "search_aliases": "Abu Ja'far" + } + ] + } + } + ) + } + ) + def get(self, request, *args, **kwargs): + # تشخیص زبان برای ارسال دیتای لوکالایز شده + lang = getattr(request, "LANGUAGE_CODE", None) or get_language() or "ru" + + # ۱. استتوس‌ها (مرتب‌شده بر اساس فیلد order) + statuses = [ + { + "id": status.id, + "slug": status.slug, + "title": get_localized_text(status.title, request=request, language_code=lang) or "", + "order": status.order, + "color": status.color, + "main_color_code": status.main_color_code, + } + for status in HadisStatus.objects.all().order_by('order') + ] + + # ۲. دسته‌بندی‌ها (مرتب‌شده بر اساس فیلد order) + categories = [ + { + "id": cat.id, + "slug": cat.slug, + "title": get_localized_text(cat.title, request=request, language_code=lang) or "", + "order": cat.order, + } + for cat in HadisCategory.objects.all().order_by('order') + ] + + # ۳. منابع / کتاب‌ها + sources = [] + # استفاده از prefetch_related برای جلوگیری از مشکل N+1 Query هنگام گرفتن نویسنده‌ها + books = BookReference.objects.prefetch_related('authors').all().order_by('order', '-id') + + for book in books: + # استخراج نام نویسنده‌ها برای سرچ آفلاین فلاتر + authors_names = [ + get_localized_text(author.name, request=request, language_code=lang) or "" + for author in book.authors.all() + ] + + sources.append({ + "id": book.id, + "slug": book.slug, + "title": get_localized_text(book.title, request=request, language_code=lang) or "", + # تمام اسم‌های نویسنده‌ها را به هم می‌چسبانیم تا فلاتر بتواند روی آن‌ها سرچ بزند + "search_keywords": " ".join(authors_names).strip() + }) + + # ۴. راویان (نریتورها) + narrators = [] + transmitters = Transmitters.objects.all().order_by('id') + + for narrator in transmitters: + # گرفتن نام‌های مستعار + known_as = get_localized_text(narrator.known_as, request=request, language_code=lang) or "" + nickname = get_localized_text(narrator.nickname, request=request, language_code=lang) or "" + kunya = get_localized_text(narrator.kunya, request=request, language_code=lang) or "" + + # تجمیع نام‌های جایگزین + search_aliases = f"{known_as} {nickname} {kunya}".strip() + + narrators.append({ + "id": narrator.id, + "slug": narrator.slug, + "title": get_localized_text(narrator.full_name, request=request, language_code=lang) or "", + # فلاتر روی این فیلد + title سرچ می‌زند + "search_aliases": search_aliases + }) + + # خروجی نهایی بدون پجینیشن + return Response({ + "statuses": statuses, + "categories": categories, + "sources": sources, + "narrators": narrators + }) diff --git a/entrypoint.sh b/entrypoint.sh index 8f8d826..5dedfa4 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,7 +5,7 @@ python manage.py migrate # python manage.py seed_images # python manage.py compilemessages python manage.py collectstatic --noinput -python manage.py set_hadiscorrection_fixed_text +# python manage.py set_hadiscorrection_fixed_text # Seed Russian data (only runs once, skips if data exists) # python manage.py seed_corrections # python manage.py seed_russian_data