From def7e033b1e5e216232a5b771bed8e99742641f7 Mon Sep 17 00:00:00 2001 From: mortezaei Date: Mon, 19 Jan 2026 18:09:46 +0330 Subject: [PATCH] Add Russian data seeding command and update entrypoint script - Introduced a new management command to seed Russian language data for Hadis, HadisCategory, and HadisSect. - Updated entrypoint script to include the new command for seeding Russian data during initialization. --- .../management/commands/seed_russian_data.py | 346 ++++++++++++++++++ entrypoint.sh | 3 + 2 files changed, 349 insertions(+) create mode 100644 apps/hadis/management/commands/seed_russian_data.py diff --git a/apps/hadis/management/commands/seed_russian_data.py b/apps/hadis/management/commands/seed_russian_data.py new file mode 100644 index 0000000..b890c61 --- /dev/null +++ b/apps/hadis/management/commands/seed_russian_data.py @@ -0,0 +1,346 @@ +import random +import uuid +from django.core.management.base import BaseCommand +from django.db import transaction +from apps.hadis.models import Hadis, HadisCategory, HadisSect + + +# Russian word patterns for random combination +RUSSIAN_CATEGORY_PREFIXES = [ + "Основы", "Принципы", "Учение", "Заповеди", "Наставления", + "Мудрость", "Знания", "Истина", "Путь", "Свет", + "Благочестие", "Праведность", "Духовность", "Вера", "Надежда", +] + +RUSSIAN_CATEGORY_TOPICS = [ + "молитвы", "поста", "милостыни", "паломничества", "джихада", + "брака", "семьи", "торговли", "справедливости", "терпения", + "благодарности", "покаяния", "прощения", "любви", "мира", + "знания", "мудрости", "веры", "надежды", "смирения", +] + +RUSSIAN_CATEGORY_SUFFIXES = [ + "в исламе", "для верующих", "и праведность", "и благочестие", + "и духовность", "и истина", "и свет", "и путь", +] + +RUSSIAN_HADIS_OPENINGS = [ + "Пророк (мир ему) сказал:", + "Имам Али (мир ему) сказал:", + "Имам Садик (мир ему) сказал:", + "Имам Хусейн (мир ему) сказал:", + "Передается от Пророка (мир ему):", + "Рассказывает имам Бакир (мир ему):", + "Сообщается от имама Казима (мир ему):", +] + +RUSSIAN_HADIS_BODIES = [ + "Лучший из вас тот, кто учится и учит других.", + "Ищите знания от колыбели до могилы.", + "Терпение - ключ к облегчению.", + "Молитва - столп религии.", + "Знание - свет для сердца.", + "Доброта к родителям - обязанность верующего.", + "Справедливость - основа власти.", + "Правда ведет к благочестию.", + "Смирение возвышает человека.", + "Благодарность увеличивает благословения.", + "Прощение - признак силы.", + "Терпение приносит награду.", + "Вера укрепляется добрыми делами.", + "Знание без действия бесполезно.", + "Лучшее богатство - довольство.", + "Самый сильный - тот, кто владеет собой в гневе.", + "Сосед имеет права на тебя.", + "Улыбка - это милостыня.", + "Чистота - половина веры.", + "Намерение определяет ценность дела.", +] + +RUSSIAN_HADIS_ENDINGS = [ + "И это истина от Всевышнего.", + "Так учит нас ислам.", + "Это путь праведных.", + "Запомните это наставление.", + "Следуйте этому пути.", + "Это мудрость пророков.", + "Берегите это знание.", + "", +] + +RUSSIAN_SECT_DATA = { + 'shia': { + 'title': 'Шиизм', + 'description': 'Шиитское направление ислама, следующее за семьей Пророка (мир ему и его семье).', + }, + 'sunni': { + 'title': 'Суннизм', + 'description': 'Суннитское направление ислама, следующее сунне Пророка (мир ему).', + }, +} + + +def make_json_field(text, lang='ru'): + """Create JSON field in the format used by the models.""" + return [{'text': text, 'language_code': lang}] + + +def generate_unique_slug(prefix, number): + """Generate a unique slug with prefix and number.""" + unique_suffix = uuid.uuid4().hex[:8] + return f"{prefix}-{number}-{unique_suffix}" + + +def generate_category_title(): + """Generate a random Russian category title.""" + prefix = random.choice(RUSSIAN_CATEGORY_PREFIXES) + topic = random.choice(RUSSIAN_CATEGORY_TOPICS) + suffix = random.choice(RUSSIAN_CATEGORY_SUFFIXES) if random.random() > 0.5 else "" + return f"{prefix} {topic} {suffix}".strip() + + +def generate_category_description(title): + """Generate a description based on the title.""" + descriptions = [ + f"Раздел посвящен теме: {title}. Здесь собраны важные хадисы и наставления.", + f"В этом разделе вы найдете хадисы о {title.lower()}.", + f"Категория содержит материалы по теме: {title}.", + f"Изучайте {title.lower()} через достоверные хадисы.", + ] + return random.choice(descriptions) + + +def generate_hadis_text(): + """Generate a random hadis text in Russian.""" + opening = random.choice(RUSSIAN_HADIS_OPENINGS) + body = random.choice(RUSSIAN_HADIS_BODIES) + ending = random.choice(RUSSIAN_HADIS_ENDINGS) + return f"{opening} {body} {ending}".strip() + + +def generate_hadis_title(): + """Generate a random hadis title.""" + topics = [ + "О терпении и награде", + "О знании и мудрости", + "О молитве и поклонении", + "О справедливости и праведности", + "О семье и воспитании", + "О доброте и милосердии", + "О вере и благочестии", + "О покаянии и прощении", + "О благодарности Аллаху", + "О смирении и скромности", + "О правде и честности", + "О соседях и обществе", + "О торговле и справедливости", + "О чистоте и гигиене", + "О намерении и искренности", + ] + return random.choice(topics) + + +class Command(BaseCommand): + help = 'Seed Russian language data for HadisSect, HadisCategory (3 levels), and Hadis (up to 500 records)' + + # Configuration constants + MAX_HADIS_RECORDS = 500 + HADIS_PER_CATEGORY = 10 + + def add_arguments(self, parser): + parser.add_argument( + '--clear', + action='store_true', + help='Clear existing data before seeding', + ) + parser.add_argument( + '--force', + action='store_true', + help='Force seeding even if data already exists', + ) + + def handle(self, *args, **options): + self.stdout.write(self.style.WARNING('=== Starting Russian Data Seeding ===')) + + # Check if data already exists (prevent re-running in loops) + existing_hadis = Hadis.objects.count() + existing_categories = HadisCategory.objects.count() + + if existing_hadis >= self.MAX_HADIS_RECORDS and not options['force'] and not options['clear']: + self.stdout.write(self.style.SUCCESS( + f'Data already seeded ({existing_hadis} hadis, {existing_categories} categories). ' + 'Use --force to override or --clear to reset.' + )) + return + + if options['clear']: + self.stdout.write(self.style.WARNING('Clearing existing data...')) + Hadis.objects.all().delete() + HadisCategory.objects.all().delete() + HadisSect.objects.all().delete() + self.stdout.write(self.style.SUCCESS('Existing data cleared.')) + + try: + with transaction.atomic(): + # Step 1: Create Sects + sects = self.create_sects() + + # Step 2: Create Categories (3 levels) + categories = self.create_categories(sects) + + # Step 3: Create Hadis (up to 500 records, distributed across categories) + self.create_hadis(categories) + + self.print_statistics() + self.stdout.write(self.style.SUCCESS('=== Russian Data Seeding Complete ===')) + + except Exception as e: + self.stdout.write(self.style.ERROR(f'Error during seeding: {str(e)}')) + raise + + def create_sects(self): + """Create or update HadisSect entries.""" + self.stdout.write('Creating sects...') + sects = {} + + for sect_type, data in RUSSIAN_SECT_DATA.items(): + sect, created = HadisSect.objects.update_or_create( + sect_type=sect_type, + defaults={ + 'title': make_json_field(data['title']), + 'description': make_json_field(data['description']), + 'is_active': True, + 'order': 1 if sect_type == 'shia' else 2, + } + ) + sects[sect_type] = sect + status = 'Created' if created else 'Updated' + self.stdout.write(f" {status} sect: {data['title']}") + + return sects + + def create_categories(self, sects): + """Create 3-level category tree for each sect and source type.""" + self.stdout.write('Creating category tree (3 levels)...') + all_leaf_categories = [] + + # Limit source types to control total number of categories + source_types = ['hadith', 'quran'] + + for sect_type, sect in sects.items(): + for source_type in source_types: + self.stdout.write(f" Creating categories for {sect_type}/{source_type}...") + + # Level 1: Root categories (2 per source type to limit total) + for i in range(2): + level1_title = generate_category_title() + level1_cat = HadisCategory.objects.create( + parent=None, + sect=sect, + source_type=source_type, + title=make_json_field(level1_title), + description=make_json_field(generate_category_description(level1_title)), + order=i + 1, + ) + + # Level 2: Child categories (2 per level 1) + for j in range(2): + level2_title = generate_category_title() + level2_cat = HadisCategory.objects.create( + parent=level1_cat, + sect=sect, + source_type=source_type, + title=make_json_field(level2_title), + description=make_json_field(generate_category_description(level2_title)), + order=j + 1, + ) + + # Level 3: Leaf categories (2-3 per level 2) + num_level3 = random.randint(2, 3) + for k in range(num_level3): + level3_title = generate_category_title() + level3_cat = HadisCategory.objects.create( + parent=level2_cat, + sect=sect, + source_type=source_type, + title=make_json_field(level3_title), + description=make_json_field(generate_category_description(level3_title)), + order=k + 1, + ) + all_leaf_categories.append(level3_cat) + + total_categories = HadisCategory.objects.count() + self.stdout.write(self.style.SUCCESS(f" Created {total_categories} categories total")) + self.stdout.write(self.style.SUCCESS(f" Leaf categories: {len(all_leaf_categories)}")) + + return all_leaf_categories + + def create_hadis(self, leaf_categories): + """Create hadis records distributed across categories, up to MAX_HADIS_RECORDS.""" + self.stdout.write(f'Creating hadis entries (max {self.MAX_HADIS_RECORDS})...') + hadis_count = 0 + hadis_number = 1 + + # Calculate hadis per category to reach ~500 total + num_categories = len(leaf_categories) + if num_categories == 0: + self.stdout.write(self.style.WARNING('No leaf categories found!')) + return + + hadis_per_cat = max(self.HADIS_PER_CATEGORY, self.MAX_HADIS_RECORDS // num_categories) + + for idx, category in enumerate(leaf_categories): + # Stop if we've reached the limit + if hadis_count >= self.MAX_HADIS_RECORDS: + break + + # Create hadis for this category + for i in range(hadis_per_cat): + if hadis_count >= self.MAX_HADIS_RECORDS: + break + + title = generate_hadis_title() + text = generate_hadis_text() + translation = text # Same as text since it's already in Russian + + # Generate unique slug to avoid duplicates + slug = generate_unique_slug('hadis-ru', hadis_number) + + Hadis.objects.create( + category=category, + number=hadis_number, + slug=slug, + title=make_json_field(title), + title_narrator=make_json_field(random.choice(RUSSIAN_HADIS_OPENINGS).rstrip(':')), + description=make_json_field(f"Хадис номер {hadis_number} из категории {category.title[0]['text']}"), + text=text, + translation=make_json_field(translation), + status=True, + address=make_json_field(f"Том {random.randint(1, 10)}, страница {random.randint(1, 500)}"), + explanation=make_json_field(f"Этот хадис учит нас важности {title.lower()}."), + ) + hadis_number += 1 + hadis_count += 1 + + # Progress indicator every 5 categories + if idx % 5 == 0: + self.stdout.write(f" Progress: {hadis_count}/{self.MAX_HADIS_RECORDS} hadis created...") + + self.stdout.write(self.style.SUCCESS(f" Created {hadis_count} hadis entries")) + + def print_statistics(self): + """Print final statistics.""" + self.stdout.write("\n=== Statistics ===") + self.stdout.write(f"Sects: {HadisSect.objects.count()}") + self.stdout.write(f"Categories: {HadisCategory.objects.count()}") + self.stdout.write(f"Hadis: {Hadis.objects.count()}") + + # Show hadis per category stats + leaf_cats = [] + for cat in HadisCategory.objects.all(): + if not HadisCategory.objects.filter(parent=cat).exists(): + leaf_cats.append(cat) + + if leaf_cats: + hadis_counts = [Hadis.objects.filter(category=cat).count() for cat in leaf_cats] + self.stdout.write(f"Hadis per leaf category: min={min(hadis_counts)}, max={max(hadis_counts)}, avg={sum(hadis_counts)/len(hadis_counts):.1f}") diff --git a/entrypoint.sh b/entrypoint.sh index a037895..b8bc026 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,6 +5,9 @@ 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 populate_books # python manage.py populate_book_reference # python manage.py populate_refrence_images