diff --git a/apps/hadis/management/commands/reformat_data.py b/apps/hadis/management/commands/reformat_data.py new file mode 100644 index 0000000..dc2e5f7 --- /dev/null +++ b/apps/hadis/management/commands/reformat_data.py @@ -0,0 +1,129 @@ +import random +from django.core.management.base import BaseCommand +from django.db import transaction +# REPLACE 'apps.hadis.models' with your actual app path +from apps.hadis.models import Hadis + +class Command(BaseCommand): + help = 'Updates explanation and address fields with structured multilingual dummy data' + + def handle(self, *args, **options): + self.stdout.write("Starting bulk update of Explanation and Address fields...") + + hadis_qs = Hadis.objects.all() + total_hadis = hadis_qs.count() + + if total_hadis == 0: + self.stdout.write(self.style.WARNING("No Hadis found to update.")) + return + + self.stdout.write(f"Found {total_hadis} Hadis records.") + + # --- DATA POOLS FOR RANDOM VARIATION --- + + # 1. Address Data (Hierarchical strings: Book -> Chapter -> Section) + address_pool = [ + { + "en": ["Sahih al-Bukhari", "Book of Revelation", "Chapter 1"], + "fa": ["صحیح بخاری", "کتاب وحی", "باب ۱"], + "ru": ["Сахих аль-Бухари", "Книга откровения", "Глава 1"] + }, + { + "en": ["Riyad as-Salihin", "The Book of Knowledge", "Virtues of Seeking Knowledge"], + "fa": ["ریاض الصالحین", "کتاب علم", "فضیلت طلب علم"], + "ru": ["Рияд ас-Салихин", "Книга знаний", "Достоинства поиска знаний"] + }, + { + "en": ["Sunan Abi Dawud", "Book of Prayer", "Details of Prostration"], + "fa": ["سنن ابی داوود", "کتاب صلاه", "جزئیات سجده"], + "ru": ["Сунан Абу Дауд", "Книга молитвы", "Детали земного поклона"] + } + ] + + # 2. Explanation Data (Title/Detail objects) + explanation_pool = [ + { + "en": [ + {"title": "Vocabulary", "detail": "The term 'Niyyah' implies the resolve of the heart."}, + {"title": "Context", "detail": "Narrated by Omar bin Al-Khattab regarding migration to Medina."} + ], + "fa": [ + {"title": "لغات", "detail": "واژه «نیت» به معنای قصد و عزم قلبی است."}, + {"title": "شأن نزول", "detail": "این حدیث از عمر بن خطاب در مورد مهاجرت به مدینه روایت شده است."} + ], + "ru": [ + {"title": "Словарь", "detail": "Термин «Ният» подразумевает решимость сердца."}, + {"title": "Контекст", "detail": "Передано Омаром ибн аль-Хаттабом относительно переселения в Медину."} + ] + }, + { + "en": [ + {"title": "Legal Ruling", "detail": "Scholars agree that this action is Mustahabb (recommended)."}, + {"title": "Benefits", "detail": "Increases spirituality and mindfulness in daily life."} + ], + "fa": [ + {"title": "حکم فقهی", "detail": "علما اتفاق نظر دارند که این عمل مستحب است."}, + {"title": "فواید", "detail": "باعث افزایش معنویت و توجه در زندگی روزمره می‌شود."} + ], + "ru": [ + {"title": "Правoвое решение", "detail": "Ученые согласны, что это действие является желательным (мустахаб)."}, + {"title": "Польза", "detail": "Повышает духовность и осознанность в повседневной жизни."} + ] + } + ] + + updated_count = 0 + + # Iterate and Update + for i, hadis in enumerate(hadis_qs, 1): + if i % 100 == 0: + self.stdout.write(f"Processing {i}/{total_hadis}...") + + # Pick random samples to make data look diverse + addr_sample = random.choice(address_pool) + exp_sample = random.choice(explanation_pool) + + # --- CONSTRUCT ADDRESS FORMAT (List of Strings) --- + # Format: [{"language_code": "en", "text": ["A", "B"]}, ...] + new_address = [ + { + "language_code": "en", + "text": addr_sample["en"] + }, + { + "language_code": "fa", + "text": addr_sample["fa"] + }, + { + "language_code": "ru", + "text": addr_sample["ru"] + } + ] + + # --- CONSTRUCT EXPLANATION FORMAT (List of Objects) --- + # Format: [{"language_code": "en", "text": [{"title":.., "detail":..}]}, ...] + new_explanation = [ + { + "language_code": "en", + "text": exp_sample["en"] + }, + { + "language_code": "fa", + "text": exp_sample["fa"] + }, + { + "language_code": "ru", + "text": exp_sample["ru"] + } + ] + + # Assign to fields + hadis.address = new_address + hadis.explanation = new_explanation + + # Save (Using save() ensures signals/dates update, though slower than bulk_update) + # Since we modify JSON fields, simple save is safest. + hadis.save() + updated_count += 1 + + self.stdout.write(self.style.SUCCESS(f"Successfully updated {updated_count} Hadis records with new format.")) \ No newline at end of file diff --git a/apps/hadis/management/commands/seed_corrections.py b/apps/hadis/management/commands/seed_corrections.py index e072d9e..5221b1b 100644 --- a/apps/hadis/management/commands/seed_corrections.py +++ b/apps/hadis/management/commands/seed_corrections.py @@ -1,14 +1,13 @@ import random from django.core.management.base import BaseCommand -from django.db import transaction -# REPLACE 'apps.hadis.models' with the actual path to your models if different +from django.db import transaction, IntegrityError +# REPLACE 'apps.hadis.models' with your actual path from apps.hadis.models import Hadis, HadisCorrection class Command(BaseCommand): - help = 'Seeds HadisCorrection instances to ensure every Hadis has at least 4 corrections' + help = 'Seeds HadisCorrection instances ensuring unique titles to prevent slug collisions' def create_json_field(self, en_text, ru_text): - """Helper to construct the multilingual JSON structure""" return [ {"text": en_text, "language_code": "en"}, {"text": ru_text, "language_code": "ru"} @@ -17,19 +16,17 @@ class Command(BaseCommand): def handle(self, *args, **options): self.stdout.write("Starting HadisCorrection seeding...") - # 1. Fetch all Hadis hadis_qs = Hadis.objects.all() total_hadis = hadis_qs.count() if total_hadis == 0: - self.stdout.write(self.style.WARNING("No Hadis found to correct!")) + self.stdout.write(self.style.WARNING("No Hadis found!")) return - self.stdout.write(f"Found {total_hadis} Hadis records. Checking correction counts...") + self.stdout.write(f"Found {total_hadis} Hadis records.") total_created = 0 - # 2. Iterate and Create for i, hadis in enumerate(hadis_qs, 1): if i % 50 == 0: self.stdout.write(f"Processing {i}/{total_hadis}...") @@ -41,44 +38,45 @@ class Command(BaseCommand): if needed <= 0: continue - with transaction.atomic(): - for k in range(needed): - # Calculate a visual number for titles (e.g., Correction 1, Correction 2) - correction_num = current_count + k + 1 + # We use a try-except block inside the loop to handle potential rare collisions gracefully + try: + with transaction.atomic(): + for k in range(needed): + correction_num = current_count + k + 1 - # --- Generate Content --- - - # Titles: specific to grammar, context, or translation - topics_en = ["Grammar Correction", "Context Clarification", "Alternative Translation", "Scholar Note"] - topics_ru = ["Исправление грамматики", "Уточнение контекста", "Альтернативный перевод", "Заметка ученого"] - - topic_idx = (correction_num - 1) % len(topics_en) - - title_data = self.create_json_field( - f"{topics_en[topic_idx]} #{correction_num}", - f"{topics_ru[topic_idx]} #{correction_num}" - ) + topics_en = ["Grammar Correction", "Context Clarification", "Alternative Translation", "Scholar Note"] + topics_ru = ["Исправление грамматики", "Уточнение контекста", "Альтернативный перевод", "Заметка ученого"] + + topic_idx = (correction_num - 1) % len(topics_en) + + # --- CRITICAL FIX: Add Hadis Number to Title --- + # This makes the title unique per Hadis, producing unique slugs naturally + # e.g., "Grammar Correction #1 (Hadis 2050)" -> "grammar-correction-1-hadis-2050" + title_en = f"{topics_en[topic_idx]} #{correction_num} (Hadis {hadis.number})" + title_ru = f"{topics_ru[topic_idx]} #{correction_num} (Хадис {hadis.number})" + + title_data = self.create_json_field(title_en, title_ru) - desc_data = self.create_json_field( - f"This is a detailed explanation regarding the {topics_en[topic_idx].lower()} for Hadith {hadis.number}.", - f"Это подробное объяснение, касающееся {topics_ru[topic_idx].lower()} для Хадиса {hadis.number}." - ) + desc_data = self.create_json_field( + f"Detailed explanation regarding the {topics_en[topic_idx].lower()} for Hadith {hadis.number}.", + f"Подробное объяснение касательно {topics_ru[topic_idx].lower()} для Хадиса {hadis.number}." + ) - # Simulating a corrected translation snippet - translation_data = self.create_json_field( - f"Revised text segment for correction {correction_num}...", - f"Пересмотренный фрагмент текста для исправления {correction_num}..." - ) + translation_data = self.create_json_field( + f"Revised text segment...", + f"Пересмотренный фрагмент..." + ) - # --- Create Instance --- - # We use .create() so the model's .save() method is called. - # This ensures 'slug' and 'share_link' are automatically generated by your model logic. - HadisCorrection.objects.create( - hadis=hadis, - title=title_data, - description=desc_data, - translation=translation_data - ) - total_created += 1 + HadisCorrection.objects.create( + hadis=hadis, + title=title_data, + description=desc_data, + translation=translation_data + ) + total_created += 1 + + except IntegrityError as e: + self.stdout.write(self.style.ERROR(f"Skipped Hadis {hadis.id} due to slug collision: {e}")) + continue - self.stdout.write(self.style.SUCCESS(f"Done! Successfully created {total_created} new HadisCorrection instances.")) \ No newline at end of file + self.stdout.write(self.style.SUCCESS(f"Done! Created {total_created} new corrections.")) \ No newline at end of file diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index 0e9f9b5..bd31654 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -510,23 +510,24 @@ class HadisTransmitterListSerializer(serializers.ModelSerializer): def get_layer_names(self, obj): """Get list of localized narrator layer names""" request = self.context.get('request') - - # Get distinct narrator layer objects (not just IDs) - layers = obj.transmitters.values_list( - 'narrator_layer', flat=True - ).distinct() - + # Import here to get actual objects from apps.hadis.models import NarratorLayer - - layer_objects = NarratorLayer.objects.filter(id__in=layers) - + + # Get ALL distinct narrator layer IDs for this hadis (not filtered) + # This ensures layer names are returned regardless of layer filtering + all_layers_for_hadis = HadisTransmitter.objects.filter( + hadis=obj + ).values_list('narrator_layer', flat=True).distinct() + + layer_objects = NarratorLayer.objects.filter(id__in=all_layers_for_hadis) + # Extract localized names layer_names = [ get_localized_text(layer.name, request=request) for layer in layer_objects ] - + return layer_names class ReferenceImageSerializer(serializers.ModelSerializer):