diff --git a/apps/hadis/admin/hadis.py b/apps/hadis/admin/hadis.py index 4acc62f..de86b23 100644 --- a/apps/hadis/admin/hadis.py +++ b/apps/hadis/admin/hadis.py @@ -75,6 +75,57 @@ class HadisAdminForm(forms.ModelForm): } } + # Schema for explanations JSON field (array of objects with title and description) + explanations_schema = { + "type": "array", + "title": "Explanations", + "items": { + "type": "object", + "title": "Explanation", + "properties": { + "language_code": { + "type": "string", + "title": "Language Code", + "enum": ["en", "fa", "ar", "ur", "ru"], + "options": { + "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] + } + }, + "title": { + "type": "string", + "title": "Title" + }, + "description": { + "type": "string", + "title": "Description" + } + }, + "required": ["language_code", "title", "description"] + } + } + + # Schema for address_details JSON field (array of objects with text and priority) + address_details_schema = { + "type": "array", + "title": "Address Details", + "items": { + "type": "object", + "title": "Address Detail", + "properties": { + "text": { + "type": "string", + "title": "Address Text" + }, + "priority": { + "type": "integer", + "title": "Priority", + "minimum": 0 + } + }, + "required": ["text", "priority"] + } + } + # Apply JSON editor widgets self.fields['translation'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(translation_schema), @@ -86,6 +137,16 @@ class HadisAdminForm(forms.ModelForm): 'title': 'Links' }) + self.fields['explanations'].widget = JsonEditorWidget(attrs={ + 'schema': json.dumps(explanations_schema), + 'title': 'Explanations' + }) + + self.fields['address_details'].widget = JsonEditorWidget(attrs={ + 'schema': json.dumps(address_details_schema), + 'title': 'Address Details' + }) + # Inline Admin Classes class ReferenceImageInline(TabularInline): @@ -153,13 +214,13 @@ class HadisAdmin(ModelAdmin): 'fields': ('category', 'number', 'title', 'status') }), (_('Content'), { - 'fields': ('text', 'translation', 'explanation') + 'fields': ('text', 'translation', 'explanation', 'explanations') }), (_('Status & Classification'), { 'fields': ('hadis_status', 'hadis_status_text', 'tags') }), (_('Additional Information'), { - 'fields': ('address', 'links', 'share_link'), + 'fields': ('address', 'address_details', 'links', 'share_link'), 'classes': ('collapse',) }), (_('Timestamps'), { diff --git a/apps/hadis/management/commands/seed_complete_hadis_data.py b/apps/hadis/management/commands/seed_complete_hadis_data.py new file mode 100644 index 0000000..45a75c4 --- /dev/null +++ b/apps/hadis/management/commands/seed_complete_hadis_data.py @@ -0,0 +1,602 @@ +""" +Comprehensive fake data generation script for all Hadis-related models. +Generates Russian language data for all existing Hadis and creates 100 new complete Hadis records. + +Usage: + python manage.py seed_complete_hadis_data +""" + +import random +from django.core.management.base import BaseCommand +from django.db import transaction +from django.db import models +from apps.hadis.models import ( + Hadis, HadisCategory, HadisSect, HadisStatus, HadisTag, + HadisReference, ReferenceImage, HadisCollection, HadisInCollection, + HadisCorrection, HadisTransmitter, Transmitters, NarratorLayer, + TransmitterReliability, OpinionStatus, TransmitterOpinion, + TransmitterOriginalText, BookReference, BookAuthor +) + + +# ============================================================================ +# RUSSIAN DATA TEMPLATES +# ============================================================================ + +RUSSIAN_TRANSMITTER_NAMES = [ + "Абу Абдуллах Мухаммад", "Али ибн Хусейн", "Хасан ибн Али", + "Мухаммад ибн Якуб", "Ахмад ибн Ханбал", "Малик ибн Анас", + "Абу Ханифа Нуман", "Мухаммад аш-Шафии", "Абу Бакр ас-Сиддик", + "Умар ибн аль-Хаттаб", "Усман ибн Аффан", "Джафар ас-Садик", + "Муса аль-Казим", "Али ар-Рида", "Мухаммад ат-Таки", + "Хасан аль-Аскари", "Фатима Захра", "Зейнаб бинт Али", + "Абдуллах ибн Аббас", "Анас ибн Малик", "Абу Хурайра", + "Аиша бинт Абу Бакр", "Салман аль-Фариси", "Биляль аль-Хабаши", + "Халид ибн Валид", "Саад ибн Аби Ваккас", "Амр ибн аль-Ас", + "Зейд ибн Харис", "Абу Убайда ибн аль-Джаррах", "Тальха ибн Убайдуллах" +] + +RUSSIAN_KUNYA = [ + "Абу Мухаммад", "Абу Али", "Абу Хасан", "Абу Бакр", + "Абу Умар", "Абу Абдуллах", "Абу Джафар", "Абу Муса", + "Абу Саид", "Абу Зарр", "Умм Салама", "Умм Кульсум" +] + +RUSSIAN_ORIGINS = [ + "Медина", "Мекка", "Куфа", "Басра", "Багдад", "Дамаск", + "Каир", "Кордова", "Бухара", "Самарканд", "Найсабур", + "Рей", "Ширас", "Исфахан", "Хорасан" +] + +RUSSIAN_RELIABILITY_LEVELS = [ + {"title": "Очень надежный", "slug": "very-reliable", "color": "green"}, + {"title": "Надежный", "slug": "reliable", "color": "blue"}, + {"title": "Приемлемый", "slug": "acceptable", "color": "yellow"}, + {"title": "Слабый", "slug": "weak", "color": "orange"}, + {"title": "Очень слабый", "slug": "very-weak", "color": "red"}, + {"title": "Неизвестный", "slug": "unknown", "color": "gray"}, + {"title": "Заслуживающий доверия", "slug": "trustworthy", "color": "green"}, +] + +RUSSIAN_HADIS_STATUSES = [ + {"title": "Достоверный", "slug": "sahih", "color": "green"}, + {"title": "Хороший", "slug": "hasan", "color": "blue"}, + {"title": "Слабый", "slug": "daif", "color": "orange"}, + {"title": "Выдуманный", "slug": "mawdu", "color": "red"}, + {"title": "Подтвержденный", "slug": "confirmed", "color": "green"}, +] + +RUSSIAN_TAGS = [ + "Молитва", "Пост", "Знание", "Терпение", "Справедливость", + "Милосердие", "Благочестие", "Семья", "Воспитание", "Покаяние", + "Благодарность", "Смирение", "Честность", "Доброта", "Мудрость", + "Вера", "Надежда", "Любовь", "Мир", "Истина" +] + +RUSSIAN_HADIS_OPENINGS = [ + "Пророк (мир ему) сказал:", + "Имам Али (мир ему) сказал:", + "Имам Джафар ас-Садик (мир ему) сказал:", + "Имам Хусейн (мир ему) сказал:", + "Передается от Пророка (мир ему):", + "Рассказывает имам Бакир (мир ему):", + "Сообщается от имама Казима (мир ему):", + "Имам Риза (мир ему) сказал:", +] + +RUSSIAN_HADIS_BODIES = [ + "Лучший из вас тот, кто учится и учит других.", + "Ищите знания от колыбели до могилы.", + "Терпение - ключ к облегчению.", + "Молитва - столп религии.", + "Знание - свет для сердца.", + "Доброта к родителям - обязанность верующего.", + "Справедливость - основа власти.", + "Правда ведет к благочестию.", + "Смирение возвышает человека.", + "Благодарность увеличивает благословения.", + "Прощение - признак силы.", + "Терпение приносит награду.", + "Вера укрепляется добрыми делами.", + "Знание без действия бесполезно.", + "Лучшее богатство - довольство.", + "Самый сильный - тот, кто владеет собой в гневе.", + "Сосед имеет права на тебя.", + "Улыбка - это милостыня.", + "Чистота - половина веры.", + "Намерение определяет ценность дела.", + "Мудрость - потерянное сокровище верующего.", + "Лучший джихад - борьба с собственной душой.", + "Рай находится под ногами матерей.", + "Молчание - мудрость, но мало кто молчит.", + "Верующий не кусает дважды из одной норы.", +] + +RUSSIAN_HADIS_TITLES = [ + "О терпении и награде", "О знании и мудрости", "О молитве и поклонении", + "О справедливости и праведности", "О семье и воспитании", + "О доброте и милосердии", "О вере и благочестии", "О покаянии и прощении", + "О благодарности Аллаху", "О смирении и скромности", "О правде и честности", + "О соседях и обществе", "О торговле и справедливости", + "О чистоте и гигиене", "О намерении и искренности", "О любви к Пророку", + "О следовании сунне", "О важности молитвы", "О посте и его пользе", + "О закяте и милостыне", "О паломничестве", "О борьбе с нафсом", +] + +RUSSIAN_BOOK_TITLES = [ + "Основы веры", "Путь праведных", "Свет истины", "Книга знания", + "Сады праведников", "Сокровища мудрости", "Источник благочестия", + "Ключи к раю", "Путеводитель верующих", "Книга о молитве", + "Собрание хадисов", "Энциклопедия ислама", "Наставления имамов", + "Мудрость пророков", "Книга о терпении", "Путь к совершенству", + "Духовные сокровища", "Книга о справедливости", "Свет веры", + "Драгоценности знания" +] + +RUSSIAN_AUTHOR_NAMES = [ + "Шейх Мухаммад аль-Кулайни", "Имам ан-Навави", "Ибн Хаджар аль-Аскаляни", + "Имам аль-Бухари", "Имам Муслим", "Абу Дауд", "Ат-Тирмизи", + "Ибн Маджа", "Ахмад ибн Ханбал", "Имам Малик", "Имам аш-Шафии", + "Аллама Маджлиси", "Шейх Муфид", "Шейх Туси", "Алламе Хилли", + "Сайид Муртаза", "Шейх Садук", "Мирза Мухаммад Таги", + "Аятолла Хои", "Аятолла Систани" +] + +RUSSIAN_NARRATOR_LAYERS = [ + {"number": 1, "name": "Сподвижники Пророка", "description": "Первое поколение - сподвижники Пророка Мухаммада (мир ему)"}, + {"number": 2, "name": "Табиин", "description": "Второе поколение - последователи сподвижников"}, + {"number": 3, "name": "Таба табиин", "description": "Третье поколение - последователи последователей"}, + {"number": 4, "name": "Четвертое поколение", "description": "Четвертое поколение передатчиков"}, + {"number": 5, "name": "Пятое поколение", "description": "Пятое поколение передатчиков"}, + {"number": 6, "name": "Шестое поколение", "description": "Шестое поколение передатчиков"}, + {"number": 7, "name": "Седьмое поколение", "description": "Седьмое поколение передатчиков"}, + {"number": 8, "name": "Восьмое поколение", "description": "Восьмое поколение передатчиков"}, +] + +RUSSIAN_SCHOLAR_NAMES = [ + "Имам ан-Навави", "Ибн Хаджар", "Аз-Захаби", "Ибн Хиббан", + "Яхья ибн Маин", "Ахмад ибн Ханбал", "Али ибн аль-Мадини", + "Абу Хатим ар-Рази", "Имам аль-Бухари", "Имам Муслим" +] + +RUSSIAN_OPINIONS = [ + "Надежный передатчик, заслуживающий доверия", + "Слабый в передаче, но правдивый", + "Очень точный и надежный рассказчик", + "Сомнительный передатчик, следует проявлять осторожность", + "Выдающийся ученый своего времени", + "Известен своей памятью и точностью", + "Слабый в некоторых передачах", + "Заслуживающий доверия, но делает ошибки", +] + +RUSSIAN_OPINION_STATUSES = [ + {"title": "Подтвержденный", "slug": "confirmed", "color": "green"}, + {"title": "Смешанный", "slug": "mixed", "color": "yellow"}, + {"title": "Отклоненный", "slug": "rejected", "color": "red"}, +] + +RUSSIAN_COLLECTION_NAMES = [ + "Молитва и поклонение", "Знание и мудрость", "Семья и воспитание", + "Справедливость и права", "Терпение и благодарность", + "Покаяние и прощение", "Любовь и милосердие", "Социальные отношения", + "Торговля и экономика", "Духовное очищение" +] + +RUSSIAN_ADDRESSES = [ + "Аль-Кафи, том 2, глава 3, хадис 15", + "Сахих аль-Бухари, книга веры, хадис 52", + "Сахих Муслим, книга молитвы, хадис 125", + "Сунан Абу Дауд, книга чистоты, хадис 87", + "Джами ат-Тирмизи, книга знания, хадис 45", + "Муснад Ахмад, том 3, страница 245", + "Бихар аль-Анвар, том 75, страница 123", +] + + +class Command(BaseCommand): + help = 'Generate comprehensive fake data for all Hadis-related models in Russian' + + def __init__(self): + super().__init__() + self.created_counts = {} + self.reliability_statuses = [] + self.opinion_statuses = [] + self.narrator_layers = [] + self.transmitters = [] + self.books = [] + self.authors = [] + self.categories = [] + self.hadis_statuses = [] + self.tags = [] + + def handle(self, *args, **options): + self.stdout.write(self.style.SUCCESS('Starting comprehensive Hadis data generation...')) + + try: + with transaction.atomic(): + # Phase 1: Foundation + self.stdout.write('Phase 1: Creating foundation data...') + self.create_sects_and_categories() + + # Phase 2: Supporting models + self.stdout.write('Phase 2: Creating supporting models...') + self.create_statuses_and_tags() + + # Phase 3: Narrator infrastructure + self.stdout.write('Phase 3: Creating narrator infrastructure...') + self.create_narrator_layers() + self.create_reliability_statuses() + self.create_opinion_statuses() + + # Phase 4: Transmitters + self.stdout.write('Phase 4: Creating transmitters...') + self.create_transmitters(count=250) + + # Phase 5: Books and authors + self.stdout.write('Phase 5: Creating books and authors...') + self.create_books_and_authors() + + # Phase 6: Populate existing hadiths + self.stdout.write('Phase 6: Populating existing hadiths...') + self.populate_existing_hadiths() + + # Phase 7: Create new hadiths + self.stdout.write('Phase 7: Creating new hadiths...') + self.create_new_hadiths(count=100) + + # Phase 8: Create collections + self.stdout.write('Phase 8: Creating collections...') + self.create_collections() + + # Phase 9: Create corrections + self.stdout.write('Phase 9: Creating corrections...') + self.create_corrections() + + # Summary + self.print_summary() + + except Exception as e: + self.stdout.write(self.style.ERROR(f'Error: {str(e)}')) + raise + + def create_sects_and_categories(self): + """Create or get existing sects and categories""" + # Get existing categories + self.categories = list(HadisCategory.objects.all()[:10]) + + if not self.categories: + # Create a default category if none exist + sect, _ = HadisSect.objects.get_or_create( + sect_type='shia', + defaults={'title': [{'language_code': 'ru', 'text': 'Шиизм'}]} + ) + category = HadisCategory.objects.create( + title=[{'language_code': 'ru', 'text': 'Общие хадисы'}], + sect=sect, + source_type='hadith' + ) + self.categories.append(category) + + self.created_counts['categories'] = len(self.categories) + + def create_statuses_and_tags(self): + """Create hadis statuses and tags""" + # Create statuses + for status_data in RUSSIAN_HADIS_STATUSES: + status, created = HadisStatus.objects.get_or_create( + slug=status_data['slug'], + defaults={ + 'title': [{'language_code': 'ru', 'text': status_data['title']}], + 'color': status_data['color'], + 'order': len(self.hadis_statuses) + } + ) + self.hadis_statuses.append(status) + + # Create tags + for i, tag_name in enumerate(RUSSIAN_TAGS): + tag, created = HadisTag.objects.get_or_create( + title__0__text=tag_name, + defaults={ + 'title': [{'language_code': 'ru', 'text': tag_name}], + 'status': True + } + ) + if created: + self.tags.append(tag) + + # Get existing tags if we didn't create enough + if len(self.tags) < 5: + self.tags = list(HadisTag.objects.all()[:20]) + + self.created_counts['statuses'] = len(self.hadis_statuses) + self.created_counts['tags'] = len(self.tags) + + def create_narrator_layers(self): + """Create narrator layers""" + for layer_data in RUSSIAN_NARRATOR_LAYERS: + layer, created = NarratorLayer.objects.get_or_create( + number=layer_data['number'], + defaults={ + 'name': [{'language_code': 'ru', 'text': layer_data['name']}], + 'description': [{'language_code': 'ru', 'text': layer_data['description']}] + } + ) + self.narrator_layers.append(layer) + + self.created_counts['narrator_layers'] = len(self.narrator_layers) + + def create_reliability_statuses(self): + """Create transmitter reliability statuses""" + for reliability_data in RUSSIAN_RELIABILITY_LEVELS: + reliability, created = TransmitterReliability.objects.get_or_create( + slug=reliability_data['slug'], + defaults={ + 'title': [{'language_code': 'ru', 'text': reliability_data['title']}], + 'color': reliability_data['color'] + } + ) + self.reliability_statuses.append(reliability) + + self.created_counts['reliability_statuses'] = len(self.reliability_statuses) + + def create_opinion_statuses(self): + """Create opinion statuses""" + for opinion_data in RUSSIAN_OPINION_STATUSES: + opinion_status, created = OpinionStatus.objects.get_or_create( + slug=opinion_data['slug'], + defaults={ + 'title': [{'language_code': 'ru', 'text': opinion_data['title']}], + 'color': opinion_data['color'] + } + ) + self.opinion_statuses.append(opinion_status) + + self.created_counts['opinion_statuses'] = len(self.opinion_statuses) + + def create_transmitters(self, count=250): + """Create transmitters with full biographical data""" + for i in range(count): + name = random.choice(RUSSIAN_TRANSMITTER_NAMES) + kunya = random.choice(RUSSIAN_KUNYA) + origin = random.choice(RUSSIAN_ORIGINS) + + # Random dates + birth_year = random.randint(1, 300) + death_year = birth_year + random.randint(40, 90) + age = death_year - birth_year + + transmitter = Transmitters.objects.create( + full_name=[{'language_code': 'ru', 'text': f"{name} ибн {random.choice(RUSSIAN_TRANSMITTER_NAMES.split()[0] if ' ' in random.choice(RUSSIAN_TRANSMITTER_NAMES) else 'Абдуллах')}"}], + kunya=[{'language_code': 'ru', 'text': kunya}], + known_as=[{'language_code': 'ru', 'text': f"{name} {origin}ский"}], + nickname=[{'language_code': 'ru', 'text': f"{random.choice(['аль-Муфассир', 'аль-Хафиз', 'аль-Факих', 'аль-Мухаддис'])}"}], + origin=[{'language_code': 'ru', 'text': origin}], + lived_in=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_ORIGINS)}], + died_in=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_ORIGINS)}], + birth_year_hijri=birth_year, + death_year_hijri=death_year, + age_at_death=age, + generation=random.randint(1, 8), + reliability=random.choice(self.reliability_statuses), + madhhab=random.choice(['shia', 'sunni', 'hanafi', 'maliki', 'shafii']), + in_sahih_muslim=random.choice([True, False]), + in_sahih_bukhari=random.choice([True, False]), + description=[{ + 'language_code': 'ru', + 'text': f"{name} был известным передатчиком хадисов из {origin}. Он изучал знания у великих ученых своего времени и передал множество достоверных хадисов." + }] + ) + self.transmitters.append(transmitter) + + # Add opinions for some transmitters + if random.random() > 0.5: + for _ in range(random.randint(1, 3)): + TransmitterOpinion.objects.create( + transmitter=transmitter, + scholar_name=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_SCHOLAR_NAMES)}], + opinion_text=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_OPINIONS)}], + status=random.choice(self.opinion_statuses) if self.opinion_statuses else None + ) + + # Add original texts for some transmitters + if random.random() > 0.7: + TransmitterOriginalText.objects.create( + transmitter=transmitter, + title=[{'language_code': 'ru', 'text': f"Текст от {name}"}], + text=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_HADIS_BODIES)}], + translation=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_HADIS_BODIES)}] + ) + + self.created_counts['transmitters'] = count + + def create_books_and_authors(self): + """Create books and authors""" + # Create authors + for author_name in RUSSIAN_AUTHOR_NAMES[:15]: + author = BookAuthor.objects.create( + name=[{'language_code': 'ru', 'text': author_name}], + slug=None # Will be auto-generated + ) + self.authors.append(author) + + # Create books + for book_title in RUSSIAN_BOOK_TITLES[:20]: + book = BookReference.objects.create( + title=[{'language_code': 'ru', 'text': book_title}], + description=f"Классическое исламское произведение - {book_title}", + slug=None # Will be auto-generated + ) + # Add random authors + book.authors.add(*random.sample(self.authors, random.randint(1, 2))) + self.books.append(book) + + self.created_counts['authors'] = len(self.authors) + self.created_counts['books'] = len(self.books) + + def populate_existing_hadiths(self): + """Populate all existing hadiths with related data""" + existing_hadiths = Hadis.objects.all() + count = 0 + + for hadis in existing_hadiths: + self._populate_hadis_relations(hadis) + count += 1 + + self.created_counts['existing_hadiths_populated'] = count + + def create_new_hadiths(self, count=100): + """Create new complete hadiths""" + max_number = Hadis.objects.aggregate(models.Max('number'))['number__max'] or 0 + + for i in range(count): + hadis = Hadis.objects.create( + category=random.choice(self.categories) if self.categories else None, + number=max_number + i + 1, + title=[{'language_code': 'ru', 'text': random.choice(RUSSIAN_HADIS_TITLES)}], + title_narrator=[{'language_code': 'ru', 'text': f"Передано от {random.choice(RUSSIAN_TRANSMITTER_NAMES)}"}], + description=[{ + 'language_code': 'ru', + 'text': f"Этот хадис о важности {random.choice(['молитвы', 'знания', 'терпения', 'справедливости'])} в жизни верующего." + }], + text=f"{random.choice(RUSSIAN_HADIS_OPENINGS)} {random.choice(RUSSIAN_HADIS_BODIES)}", + translation=[{ + 'language_code': 'ru', + 'text': f"{random.choice(RUSSIAN_HADIS_OPENINGS)} {random.choice(RUSSIAN_HADIS_BODIES)}" + }], + status=True, + hadis_status=random.choice(self.hadis_statuses) if self.hadis_statuses else None, + hadis_status_text=[{ + 'language_code': 'ru', + 'text': f"Этот хадис классифицирован как {random.choice(['достоверный', 'хороший', 'слабый'])} согласно критериям ученых." + }], + address=[{ + 'language_code': 'ru', + 'text': random.choice(RUSSIAN_ADDRESSES) + }], + explanation=[{ + 'language_code': 'ru', + 'text': f"Этот хадис учит нас о важности {random.choice(['веры', 'знания', 'терпения', 'благочестия'])} в нашей повседневной жизни." + }], + explanations=[ + { + 'language_code': 'ru', + 'title': 'Контекст хадиса', + 'description': 'Этот хадис был передан в контексте обучения сподвижников основам ислама и праведного поведения.' + }, + { + 'language_code': 'ru', + 'title': 'Практическое применение', + 'description': 'Верующие должны применять это учение в своей повседневной жизни через добрые дела и праведное поведение.' + } + ], + address_details=[ + { + 'text': random.choice(RUSSIAN_ADDRESSES), + 'priority': 1 + }, + { + 'text': f"Также упоминается в {random.choice(RUSSIAN_BOOK_TITLES)}", + 'priority': 2 + } + ], + links={'video': f'https://example.com/hadis/{max_number + i + 1}'} + ) + + # Add tags + hadis.tags.add(*random.sample(self.tags, random.randint(2, 5))) + + # Populate relations + self._populate_hadis_relations(hadis) + + self.created_counts['new_hadiths'] = count + + def _populate_hadis_relations(self, hadis): + """Populate all relations for a hadis""" + # Add transmitters (chain of narration) + chain_length = random.randint(3, 7) + for order in range(chain_length): + HadisTransmitter.objects.get_or_create( + hadis=hadis, + transmitter=random.choice(self.transmitters), + order=order, + defaults={ + 'narrator_layer': random.choice(self.narrator_layers) if self.narrator_layers else None, + 'status': random.choice(self.reliability_statuses) if self.reliability_statuses else None, + 'is_gap': random.choice([True, False]) if order > 0 else False + } + ) + + # Add references + for _ in range(random.randint(1, 3)): + ref, created = HadisReference.objects.get_or_create( + hadis=hadis, + book_reference=random.choice(self.books) if self.books else None, + defaults={ + 'description': [{ + 'language_code': 'ru', + 'text': f"Ссылка на {random.choice(RUSSIAN_BOOK_TITLES)}" + }] + } + ) + + def create_collections(self): + """Create hadis collections""" + all_hadiths = list(Hadis.objects.all()) + + for collection_name in RUSSIAN_COLLECTION_NAMES[:10]: + collection = HadisCollection.objects.create( + title=[{'language_code': 'ru', 'text': collection_name}], + summary=[{ + 'language_code': 'ru', + 'text': f"Коллекция хадисов о {collection_name.lower()}" + }], + status=True, + order=len(self.created_counts.get('collections', [])) + ) + + # Add random hadiths to collection + selected_hadiths = random.sample(all_hadiths, min(random.randint(5, 15), len(all_hadiths))) + for order, hadis in enumerate(selected_hadiths): + HadisInCollection.objects.create( + hadis=hadis, + collection=collection, + order=order + ) + + self.created_counts['collections'] = len(RUSSIAN_COLLECTION_NAMES[:10]) + + def create_corrections(self): + """Create corrections for some hadiths""" + all_hadiths = list(Hadis.objects.all()[:50]) # Only first 50 + count = 0 + + for hadis in random.sample(all_hadiths, min(20, len(all_hadiths))): + if random.random() > 0.6: # 40% chance + HadisCorrection.objects.create( + hadis=hadis, + title=[{'language_code': 'ru', 'text': f"Исправление к хадису {hadis.number}"}], + description=[{ + 'language_code': 'ru', + 'text': "Уточнение цепочки передатчиков и текста хадиса на основе дополнительных источников." + }], + translation=[{ + 'language_code': 'ru', + 'text': f"Исправленный перевод: {random.choice(RUSSIAN_HADIS_BODIES)}" + }] + ) + count += 1 + + self.created_counts['corrections'] = count + + def print_summary(self): + """Print summary of created data""" + self.stdout.write(self.style.SUCCESS('\n' + '='*60)) + self.stdout.write(self.style.SUCCESS('DATA GENERATION COMPLETE')) + self.stdout.write(self.style.SUCCESS('='*60)) + + for key, value in self.created_counts.items(): + self.stdout.write(f"{key.replace('_', ' ').title()}: {value}") + + self.stdout.write(self.style.SUCCESS('='*60 + '\n')) diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 6901047..f0bba40 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -192,6 +192,8 @@ class Hadis(models.Model): share_link = models.CharField(max_length=255, verbose_name=_('share link'), null=True, blank=True) explanation = models.JSONField(default = list , verbose_name=_('Explanation')) + explanations = models.JSONField(default = list , verbose_name=_('Explanations')) + address_details = models.JSONField(default = list , verbose_name=_('Address Details')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) @@ -292,6 +294,13 @@ class Hadis(models.Model): def get_explanation(self, lang): return self._get_json_field("explanation" , lang) + + def get_explanations(self, lang): + return self._get_json_field("explanations" , lang) + + def get_address_details(self, lang): + """Get address details (returns full list, not localized)""" + return self.address_details if self.address_details else [] def save(self, *args, **kwargs): # ساخت share_link قبل از ذخیره diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index 095b405..ee45fc1 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -99,8 +99,17 @@ class HadisSyncSerializer(serializers.ModelSerializer): 'priority': img.priority, }) + # Process address_details + address_details_list = [] + if obj.address_details and isinstance(obj.address_details, list): + address_details_list = sorted( + obj.address_details, + key=lambda x: x.get('priority', 0) + ) + return { 'address': get_localized_text(obj.address, request), + 'address_details': address_details_list, 'hadis_status': status_block, 'status_text': get_localized_text(obj.hadis_status_text, request), 'share_link': obj.share_link, @@ -138,7 +147,25 @@ class HadisSyncSerializer(serializers.ModelSerializer): def get_explanations(self, obj): request = self.context.get('request') - return get_localized_text(obj.explanation, request) + # Return structured explanations with title and description + explanations_list = [] + if obj.explanations and isinstance(obj.explanations, list): + for item in obj.explanations: + if isinstance(item, dict): + lang = item.get('language_code') + # Check if matches request language + request_lang = request.query_params.get('lang', 'en') if request else 'en' + if lang == request_lang: + explanations_list.append({ + 'title': item.get('title', ''), + 'description': item.get('description', '') + }) + + # If no match, return first available or fallback to old explanation field + if not explanations_list and obj.explanation: + return get_localized_text(obj.explanation, request) + + return explanations_list if explanations_list else None def get_corrections(self, obj): request = self.context.get('request') @@ -590,14 +617,31 @@ class HadisBasicSerializer(serializers.ModelSerializer): title = LocalizedField() title_narrator = LocalizedField() - explanation = LocalizedField() + explanation = LocalizedField() + explanations = serializers.SerializerMethodField() class Meta: model = Hadis fields = [ 'id', 'slug', 'title','bookmark', 'title_narrator', 'text', - 'translation', 'share_link','explanation','category' + 'translation', 'share_link','explanation', 'explanations','category' ] + + def get_explanations(self, obj): + """Get structured explanations for the requested language""" + request = self.context.get('request') + request_lang = request.query_params.get('lang', 'en') if request else 'en' + + explanations_list = [] + if obj.explanations and isinstance(obj.explanations, list): + for item in obj.explanations: + if isinstance(item, dict) and item.get('language_code') == request_lang: + explanations_list.append({ + 'title': item.get('title', ''), + 'description': item.get('description', '') + }) + + return explanations_list if explanations_list else None def get_category(self, obj): @@ -650,13 +694,20 @@ class HadisDetailSerializer(serializers.ModelSerializer): ) reference_images = SerializerMethodField() address = LocalizedField() + address_details = serializers.SerializerMethodField() class Meta: model = Hadis fields = [ 'id', 'number', 'slug','hadis_status', 'links','share_link', - 'tags','references','reference_images','address' + 'tags','references','reference_images','address', 'address_details' ] + + def get_address_details(self, obj): + """Get address details sorted by priority""" + if obj.address_details and isinstance(obj.address_details, list): + return sorted(obj.address_details, key=lambda x: x.get('priority', 0)) + return [] def get_reference_images(self, obj): """Get all reference images from all references""" diff --git a/entrypoint.sh b/entrypoint.sh index 5dbc1d0..4f52a22 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,14 +1,14 @@ #!/bin/sh sleep 20 - +python manage.py makemigrations hadis python manage.py migrate # python manage.py seed_images python manage.py collectstatic --noinput # Seed Russian data (only runs once, skips if data exists) # python manage.py seed_russian_data -python manage.py hadisreferences - +# python manage.py hadisreferences +python manage.py seed_complete_hadis_data # python manage.py populate_books # python manage.py populate_book_reference # python manage.py populate_refrence_images