diff --git a/apps/course/views/admin.py b/apps/course/views/admin.py index 391b900..cf18141 100644 --- a/apps/course/views/admin.py +++ b/apps/course/views/admin.py @@ -98,9 +98,9 @@ class AdminCourseViewSet(viewsets.ModelViewSet): if category_id: queryset = queryset.filter(category_id=category_id) - professor_id = self.request.query_params.get('professor', None) - if professor_id: - queryset = queryset.filter(professors=professor_id) + professor_ids = self.request.query_params.getlist('professor') + if professor_ids: + queryset = queryset.filter(professors__in=professor_ids).distinct() level = self.request.query_params.get('level', None) if level: @@ -719,6 +719,23 @@ class AdminLiveSessionViewSet(viewsets.ModelViewSet): if course_id: queryset = queryset.filter(course_id=course_id) + professor_ids = self.request.query_params.getlist('professor') + if professor_ids: + queryset = queryset.filter(course__professors__in=professor_ids).distinct() + + time_range = self.request.query_params.get('time_range', None) + if time_range: + from datetime import timedelta + now = timezone.now() + if time_range == '3_days': + queryset = queryset.filter(started_at__gte=now - timedelta(days=3)) + elif time_range == 'week': + queryset = queryset.filter(started_at__gte=now - timedelta(days=7)) + elif time_range == 'month': + queryset = queryset.filter(started_at__gte=now - timedelta(days=30)) + elif time_range == 'year': + queryset = queryset.filter(started_at__gte=now - timedelta(days=365)) + started_after = self.request.query_params.get('started_after', None) if started_after: queryset = queryset.filter(started_at__gte=started_after) diff --git a/apps/hadis/docs.py b/apps/hadis/docs.py index cadbb44..000bb31 100644 --- a/apps/hadis/docs.py +++ b/apps/hadis/docs.py @@ -107,12 +107,40 @@ hadis_category_tree_swagger = swagger_auto_schema( "source_type": "quran", "hadis_count": 2, "has_hadis": True, + "hadis_ids": [ + 2164 + ], "order": 1, "thumbnail": None, "xmind_file": None, "has_xmind_file": False, "children_count": 3, - "children": [] + "children": [], + "quran_data": { + "id": 6, + "surah_name": "Al-Nisa", + "ayah_number": "64", + "arabic_text": "وَمَا أَرْسَلْنَا مِنْ رَسُولٍ إِلَّا لِيُطَاعَ بِإِذْنِ اللَّهِ وَلَوْ أَنَّهُمْ إِذْ ظَلَمُوا أَنْفُسَهُمْ جَاءُوكَ فَاسْتَغْفَرُوا اللَّهَ وَاسْتَغْفَرَ لَهُمُ الرَّسُولُ لَوَجَدُوا اللَّهَ تَوَّابًا رَحِيمًا", + "translation": "And We did not send any messenger except that he should be obeyed by permission of Allah. And if, when they wronged themselves, they had come to you, [O Muhammad], and asked forgiveness of Allah and the Messenger had asked forgiveness for them, they would have found Allah Accepting of repentance and Merciful.", + "translator": "Tafsir Ibn Kathir", + "secondary_translations": [ + { + "id": 16, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Tafsir Ibn Kathir" + }, + { + "id": 17, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Tafsir al-Jalalayn" + }, + { + "id": 18, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Saadi Translation" + } + ] + }, } ] } @@ -142,13 +170,41 @@ hadis_category_tree_swagger = swagger_auto_schema( "source_type": "hadith", "hadis_count": 1, "has_hadis": True, + "hadis_ids": [ + 2018 + ], "order": 1, "thumbnail": None, "xmind_file": None, "has_xmind_file": False, "children_count": 3, - "children": [] - } + "children": [], + "quran_data": { + "id": 6, + "surah_name": "Al-Nisa", + "ayah_number": "64", + "arabic_text": "وَمَا أَرْسَلْنَا مِنْ رَسُولٍ إِلَّا لِيُطَاعَ بِإِذْنِ اللَّهِ وَلَوْ أَنَّهُمْ إِذْ ظَلَمُوا أَنْفُسَهُمْ جَاءُوكَ فَاسْتَغْفَرُوا اللَّهَ وَاسْتَغْفَرَ لَهُمُ الرَّسُولُ لَوَجَدُوا اللَّهَ تَوَّابًا رَحِيمًا", + "translation": "And We did not send any messenger except that he should be obeyed by permission of Allah. And if, when they wronged themselves, they had come to you, [O Muhammad], and asked forgiveness of Allah and the Messenger had asked forgiveness for them, they would have found Allah Accepting of repentance and Merciful.", + "translator": "Tafsir Ibn Kathir", + "secondary_translations": [ + { + "id": 16, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Tafsir Ibn Kathir" + }, + { + "id": 17, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Tafsir al-Jalalayn" + }, + { + "id": 18, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Saadi Translation" + } + ] + }, + }, ] } } @@ -484,6 +540,31 @@ hadis_list_swagger = swagger_auto_schema( "count": 150, "next": "http://example.com/api/hadis/category/1/?page=2", "previous": None, + "quran_data": { + "id": 6, + "surah_name": "Al-Nisa", + "ayah_number": "64", + "arabic_text": "وَمَا أَرْسَلْنَا مِنْ رَسُولٍ إِلَّا لِيُطَاعَ بِإِذْنِ اللَّهِ وَلَوْ أَنَّهُمْ إِذْ ظَلَمُوا أَنْفُسَهُمْ جَاءُوكَ فَاسْتَغْفَرُوا اللَّهَ وَاسْتَغْفَرَ لَهُمُ الرَّسُولُ لَوَجَدُوا اللَّهَ تَوَّابًا رَحِيمًا", + "translation": "And We did not send any messenger except that he should be obeyed by permission of Allah. And if, when they wronged themselves, they had come to you, [O Muhammad], and asked forgiveness of Allah and the Messenger had asked forgiveness for them, they would have found Allah Accepting of repentance and Merciful.", + "translator": "Tafsir Ibn Kathir", + "secondary_translations": [ + { + "id": 16, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Tafsir Ibn Kathir" + }, + { + "id": 17, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Tafsir al-Jalalayn" + }, + { + "id": 18, + "translation_text": "Shihab Ad-Din Alusi, \"Ruh al-maani\", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).", + "translator": "Saadi Translation" + } + ] + }, "results": [ { "id": 1, diff --git a/apps/hadis/management/commands/seed_quran_verses.py b/apps/hadis/management/commands/seed_quran_verses.py new file mode 100644 index 0000000..3765ea8 --- /dev/null +++ b/apps/hadis/management/commands/seed_quran_verses.py @@ -0,0 +1,117 @@ +# backend/apps/hadis/management/commands/seed_quran_verses.py + +from django.core.management.base import BaseCommand +from django.db import transaction +from apps.hadis.models import HadisCategory +from apps.hadis.models.category import CategoryQuranVerse, QuranSecondaryTranslation + +class Command(BaseCommand): + help = 'Seed default Quran verses and secondary translations for Quran categories that contain hadiths.' + + def handle(self, *args, **options): + self.stdout.write(self.style.WARNING("Starting Quran verse seeding process...")) + + # دیتای پیش‌فرض آیه + surah_name_json = [ + {"language_code": "en", "title": "Al-Nisa"}, + {"language_code": "ru", "title": "Ан-Ниса"}, + {"language_code": "fa", "title": "سورة النساء"} + ] + + ayah_number = "64" + + arabic_text = "وَمَا أَرْسَلْنَا مِنْ رَسُولٍ إِلَّا لِيُطَاعَ بِإِذْنِ اللَّهِ وَلَوْ أَنَّهُمْ إِذْ ظَلَمُوا أَنْفُسَهُمْ جَاءُوكَ فَاسْتَغْفَرُوا اللَّهَ وَاسْتَغْفَرَ لَهُمُ الرَّسُولُ لَوَجَدُوا اللَّهَ تَوَّابًا رَحِيمًا" + + primary_translation_json = [ + { + "language_code": "en", + "title": "And We did not send any messenger except that he should be obeyed by permission of Allah. And if, when they wronged themselves, they had come to you, [O Muhammad], and asked forgiveness of Allah and the Messenger had asked forgiveness for them, they would have found Allah Accepting of repentance and Merciful." + }, + { + "language_code": "ru", + "title": "Если бы они, поступив несправедливо по отношению к самим себе, пришли к тебе, чтобы молить прощения у Аллаха и чтобы Посланник молил о прощении для них, то они обнаружили бы, что Аллах — Принимающий покаяние и Милосердный" + } + ] + + primary_translator_json = [ + {"language_code": "en", "title": "Tafsir Ibn Kathir"}, + {"language_code": "ru", "title": "Тафсир Ибн Касир"} + ] + + # دیتای متن ترجمه‌های ثانویه (متن برای هر ۳ مورد طبق گفته شما یکی است) + secondary_text_json = [ + { + "language_code": "en", + "title": 'Shihab Ad-Din Alusi, "Ruh al-maani", vol. 11, p. 195, commentary on the 33rd verse, 33rd surah, research: Ali Abd al-Bari Atiya, Dar al-tubut al-ilmiya, Beirut, Lebanon, first edition, 1415, in 16 volumes (15 volumes and one volume of contents).' + }, + { + "language_code": "ru", + "title": 'Шихаб ад-Дин Алуси, «Рух аль-маани», т. 11, с. 195, комментарий к 33-му аяту 33-й суры, исследование: Али Абд аль-Бари Атия, Дар аль-тубут аль-ильмия, Бейрут, Ливан, первое издание, 1415 г., в 16 томах (15 томов и один том оглавления).' + } + ] + + # لیست مترجمان ثانویه + secondary_translators = [ + [ + {"language_code": "en", "title": "Tafsir Ibn Kathir"}, + {"language_code": "ru", "title": "Тафсир Ибн Касир"} + ], + [ + {"language_code": "en", "title": "Tafsir al-Jalalayn"}, + {"language_code": "ru", "title": "Тафсир аль-Джалалайн"} + ], + [ + {"language_code": "en", "title": "Saadi Translation"}, + {"language_code": "ru", "title": "Перевод Саади"} + ] + ] + + # پیدا کردن دسته‌بندی‌هایی که منبع آن‌ها قرآن است و حداقل یک حدیثِ فعال دارند + target_categories = HadisCategory.objects.filter( + source_type=HadisCategory.SourceType.QURAN, + hadis__status=True # فیلتر از طریق reverse relation (یک دسته باید حداقل یه حدیث فعال داشته باشه) + ).distinct() + + if not target_categories.exists(): + self.stdout.write(self.style.WARNING("No Quran categories with active hadiths found.")) + return + + verses_created = 0 + translations_created = 0 + + with transaction.atomic(): + for category in target_categories: + # ساخت یا آپدیت آیه اصلی برای این کتگوری + verse_obj, created = CategoryQuranVerse.objects.update_or_create( + category=category, + defaults={ + 'surah_name': surah_name_json, + 'ayah_number': ayah_number, + 'arabic_text': arabic_text, + 'translation': primary_translation_json, + 'translator': primary_translator_json + } + ) + + if created: + verses_created += 1 + + # پاک کردن ترجمه‌های ثانویه قبلی (در صورت وجود) برای جلوگیری از تکرار + verse_obj.secondary_translations.all().delete() + + # ساخت ۳ ترجمه ثانویه + for translator_json in secondary_translators: + QuranSecondaryTranslation.objects.create( + quran_verse=verse_obj, + translation_text=secondary_text_json, + translator=translator_json + ) + translations_created += 1 + + self.stdout.write(self.style.SUCCESS(f"Seeded Quran data for category: {category.id}")) + + self.stdout.write( + self.style.SUCCESS( + f"\n🎉 Successfully created/updated {verses_created} Quran Verses and added {translations_created} Secondary Translations!" + ) + ) \ No newline at end of file diff --git a/apps/hadis/migrations/0015_categoryquranverse_quransecondarytranslation.py b/apps/hadis/migrations/0015_categoryquranverse_quransecondarytranslation.py new file mode 100644 index 0000000..2013a6a --- /dev/null +++ b/apps/hadis/migrations/0015_categoryquranverse_quransecondarytranslation.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.12 on 2026-06-18 11:09 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0014_hadiscorrection_priority_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='CategoryQuranVerse', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('surah_name', models.JSONField(default=list, verbose_name='Surah Name')), + ('ayah_number', models.CharField(max_length=50, verbose_name='Ayah Number')), + ('arabic_text', models.TextField(verbose_name='Arabic Text')), + ('translation', models.JSONField(default=list, verbose_name='Primary Translation')), + ('translator', models.JSONField(default=list, verbose_name='Primary Translator')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), + ('category', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='quran_verse', to='hadis.hadiscategory', verbose_name='Category')), + ], + options={ + 'verbose_name': 'Category Quran Verse', + 'verbose_name_plural': 'Category Quran Verses', + }, + ), + migrations.CreateModel( + name='QuranSecondaryTranslation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('translation_text', models.JSONField(default=list, verbose_name='Translation Text')), + ('translator', models.JSONField(default=list, verbose_name='Translator Name')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')), + ('quran_verse', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='secondary_translations', to='hadis.categoryquranverse', verbose_name='Quran Verse')), + ], + options={ + 'verbose_name': 'Quran Secondary Translation', + 'verbose_name_plural': 'Quran Secondary Translations', + 'ordering': ['id'], + }, + ), + ] diff --git a/apps/hadis/models/category.py b/apps/hadis/models/category.py index 9dcf51c..9f7898d 100644 --- a/apps/hadis/models/category.py +++ b/apps/hadis/models/category.py @@ -213,3 +213,66 @@ class HadisCategory(MPTTModel): return None + +class CategoryQuranVerse(models.Model): + """ + Model to store Quranic verse details for a HadisCategory + when its source_type is 'quran'. + """ + category = models.OneToOneField( + HadisCategory, + on_delete=models.CASCADE, + related_name='quran_verse', + verbose_name=_('Category') + ) + surah_name = models.JSONField(default=list, verbose_name=_('Surah Name')) + # برای پشتیبانی از رِنج آیات (مثل 12-15) از CharField استفاده می‌کنیم + ayah_number = models.CharField(max_length=50, verbose_name=_('Ayah Number')) + + arabic_text = models.TextField(verbose_name=_('Arabic Text')) + translation = models.JSONField(default=list, verbose_name=_('Primary Translation')) + translator = models.JSONField(default=list, verbose_name=_('Primary Translator')) + + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created At')) + updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated At')) + + class Meta: + verbose_name = _('Category Quran Verse') + verbose_name_plural = _('Category Quran Verses') + + def __str__(self): + # تلاش برای استخراج اسم سوره جهت نمایش در ادمین پنل + try: + surah = self.surah_name[0]['text'] if self.surah_name else "Unknown Surah" + except (IndexError, KeyError, TypeError): + surah = "Unknown Surah" + return f"{self.category} -> {surah} : {self.ayah_number}" + + +class QuranSecondaryTranslation(models.Model): + """ + Model to store multiple secondary translations for a Quran verse. + """ + quran_verse = models.ForeignKey( + CategoryQuranVerse, + on_delete=models.CASCADE, + related_name='secondary_translations', + verbose_name=_('Quran Verse') + ) + translation_text = models.JSONField(default=list, verbose_name=_('Translation Text')) + translator = models.JSONField(default=list, verbose_name=_('Translator Name')) + + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created At')) + updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated At')) + + class Meta: + verbose_name = _('Quran Secondary Translation') + verbose_name_plural = _('Quran Secondary Translations') + ordering = ['id'] + + def __str__(self): + try: + translator_name = self.translator[0]['text'] if self.translator else "Unknown Translator" + except (IndexError, KeyError, TypeError): + translator_name = "Unknown Translator" + return f"Translation by {translator_name}" \ No newline at end of file diff --git a/apps/hadis/serializers/category.py b/apps/hadis/serializers/category.py index 2dbb7c1..955a276 100644 --- a/apps/hadis/serializers/category.py +++ b/apps/hadis/serializers/category.py @@ -255,4 +255,32 @@ class CategorySerializer(serializers.ModelSerializer): # # ✅ CALL THE MODEL METHOD! # return obj.get_title(lang) - + + +from ..models.category import CategoryQuranVerse, QuranSecondaryTranslation + +class QuranSecondaryTranslationSerializer(serializers.ModelSerializer): + translation_text = LocalizedField() + translator = LocalizedField() + + class Meta: + model = QuranSecondaryTranslation + fields = ['id', 'translation_text', 'translator'] + +class CategoryQuranVerseSerializer(serializers.ModelSerializer): + surah_name = LocalizedField() + translation = LocalizedField() + translator = LocalizedField() + secondary_translations = QuranSecondaryTranslationSerializer(many=True, read_only=True) + + class Meta: + model = CategoryQuranVerse + fields = [ + 'id', + 'surah_name', + 'ayah_number', + 'arabic_text', + 'translation', + 'translator', + 'secondary_translations' + ] \ No newline at end of file diff --git a/apps/hadis/views/category.py b/apps/hadis/views/category.py index 4fa26e9..a49b200 100644 --- a/apps/hadis/views/category.py +++ b/apps/hadis/views/category.py @@ -72,6 +72,7 @@ class HadisCategoryTreeView(ListAPIView): def list(self, request, *args, **kwargs): queryset = self.get_queryset() + # 1. Map Hadis IDs hadis_ids_map = defaultdict(list) hadis_pairs = ( Hadis.objects @@ -82,7 +83,21 @@ class HadisCategoryTreeView(ListAPIView): for category_id, hadis_id in hadis_pairs: hadis_ids_map[category_id].append(hadis_id) - # 1. Build a mapping of all categories and their children + # 👇 2. Map Quran Data (Bulk fetch to prevent N+1 Queries) + quran_data_map = {} + from ..models.category import CategoryQuranVerse + from ..serializers.category import CategoryQuranVerseSerializer + + quran_verses = CategoryQuranVerse.objects.filter( + category__in=queryset + ).prefetch_related('secondary_translations') + + for verse in quran_verses: + quran_data_map[verse.category_id] = CategoryQuranVerseSerializer( + verse, context={'request': request} + ).data + + # 3. Build a mapping of all categories and their children category_map = {cat.id: cat for cat in queryset} children_map = {} roots = [] @@ -95,8 +110,7 @@ class HadisCategoryTreeView(ListAPIView): children_map[cat.parent_id] = [] children_map[cat.parent_id].append(cat) - # 2. Pre-calculate recursive hadis counts (total hadiths in sub-tree) - # This matches CategorySerializer.children_count logic + # 4. Pre-calculate recursive hadis counts recursive_counts = {} def get_recursive_count(cat_id): @@ -115,7 +129,7 @@ class HadisCategoryTreeView(ListAPIView): for cat in queryset: get_recursive_count(cat.id) - # 3. Build grouped structure + # 5. Build grouped structure grouped_data = {} for root in roots: @@ -130,8 +144,6 @@ class HadisCategoryTreeView(ListAPIView): # Add sect info sect_id = str(root.sect.id) if sect_id not in grouped_data[sect_type]['sects']: - # Optimistic source_types fetch - though still a query per sect - # but better than before source_types = HadisCategory.objects.filter( sect=root.sect ).values_list('source_type', flat=True).order_by().distinct() @@ -152,10 +164,10 @@ class HadisCategoryTreeView(ListAPIView): children_map, recursive_counts, hadis_ids_map, + quran_data_map, # 👇 پاس دادن مپِ قرآن به تابع بازگشتی ) grouped_data[sect_type]['categories'].append(category_data) - # Count total categories total_count = len(queryset) response_data = { @@ -165,14 +177,13 @@ class HadisCategoryTreeView(ListAPIView): return Response(response_data) - def _build_tree_recursive(self, category, request, children_map, recursive_counts, hadis_ids_map): + def _build_tree_recursive(self, category, request, children_map, recursive_counts, hadis_ids_map, quran_data_map): """ Build tree from flat mapping """ children = children_map.get(category.id, []) has_hadis = category.direct_hadis_count > 0 - # Align fields with CategorySerializer return { 'id': category.id, 'title': get_localized_text(category.title, request), @@ -181,10 +192,12 @@ class HadisCategoryTreeView(ListAPIView): 'source_type': category.source_type, 'sect_id': category.sect_id, 'sect_type': category.sect.sect_type, - 'hadis_count': category.direct_hadis_count, # Direct count - 'children_count': recursive_counts.get(category.id, 0), # Total in sub-tree (matches normal endpoint) + 'hadis_count': category.direct_hadis_count, + 'children_count': recursive_counts.get(category.id, 0), 'has_hadis': has_hadis, 'hadis_ids': hadis_ids_map.get(category.id, []) if has_hadis else [], + # 👇 کلید جدید: تزریق دیتای قرآن دقیقاً داخل نودِ کتگوری + 'quran_data': quran_data_map.get(category.id, None) if category.source_type == HadisCategory.SourceType.QURAN else None, 'order': category.order, 'thumbnail': self._get_thumbnail_url(category, request), 'xmind_file': self._get_xmind_url(category, request), @@ -196,6 +209,7 @@ class HadisCategoryTreeView(ListAPIView): children_map, recursive_counts, hadis_ids_map, + quran_data_map, # 👇 ) for child in children ] diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 9818b94..020d96d 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -148,7 +148,7 @@ class HadisSyncView(ListAPIView): 'results': serializer.data }) - +from ..serializers.category import CategoryQuranVerseSerializer class HadisListView(ListAPIView): """ API view to list Hadis by category_id @@ -161,6 +161,9 @@ class HadisListView(ListAPIView): def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + def list(self, request, *args, **kwargs): # 1. Run the standard list logic (get pagination, filter, results) response = super().list(request, *args, **kwargs) @@ -170,18 +173,40 @@ class HadisListView(ListAPIView): category_obj = get_object_or_404(HadisCategory, slug=category_slug) # 3. Serialize this single category for the Hero section - # You might need a simple serializer just for titles/descriptions category_data = SimpleCategory(category_obj).data - # 4. Inject it into the response data - # Note: We access response.data because we are using DRF's Response object + # 👇 4. NEW LOGIC: Check if it's a Quran Category and fetch verse data + quran_data = None + if category_obj.source_type == HadisCategory.SourceType.QURAN: + # سعی می‌کنیم آبجکت قرآن متصل به این کتگوری رو بگیریم + try: + # استفاده از prefetch_related برای ترجمه‌های ثانویه جهت جلوگیری از N+1 + from ..models.category import CategoryQuranVerse + verse_obj = CategoryQuranVerse.objects.prefetch_related( + 'secondary_translations' + ).get(category=category_obj) + + # سریالایز کردن دیتای قرآن (با کانتکست ریکوئست برای زبان) + from ..serializers.category import CategoryQuranVerseSerializer + quran_data = CategoryQuranVerseSerializer( + verse_obj, + context={'request': request} + ).data + except CategoryQuranVerse.DoesNotExist: + pass + + # 5. Inject it into the response data if isinstance(response.data, dict): - # Reorder the response to place current_category before results ordered_data = {} for key in ['count', 'next', 'previous']: if key in response.data: ordered_data[key] = response.data[key] + ordered_data['current_category'] = category_data + + # 👇 دیتای قرآن را تزریق می‌کنیم + ordered_data['quran_data'] = quran_data + if 'results' in response.data: ordered_data['results'] = response.data['results'] response.data = ordered_data