diff --git a/apps/hadis/docs.py b/apps/hadis/docs.py index 97e07ed..b1d1878 100644 --- a/apps/hadis/docs.py +++ b/apps/hadis/docs.py @@ -1105,11 +1105,11 @@ transmitter_original_text_swagger = swagger_auto_schema( "translation": [ { "language_code": "en", - "title": "Actions are but by intention, and every person will have what they intended. So whoever emigrated for Allah and His Messenger, then his emigration is for Allah and His Messenger. And whoever emigrated for worldly gain or to marry a woman, then his emigration is for that which he emigrated." + "text": "Actions are but by intention, and every person will have what they intended. So whoever emigrated for Allah and His Messenger, then his emigration is for Allah and His Messenger. And whoever emigrated for worldly gain or to marry a woman, then his emigration is for that which he emigrated." }, { "language_code": "fa", - "title": "اعمال به نیت ها است و هر کس آنچه را که قصد کرده است خواهد داشت. پس هر کس برای خدا و پیامبرش هجرت کند، هجرت او برای خدا و پیامبرش است. و هر کس برای منافع دنیوی یا ازدواج با زنی هجرت کند، هجرت او برای همان چیزی است که برای آن هجرت کرده است." + "text": "اعمال به نیت ها است و هر کس آنچه را که قصد کرده است خواهد داشت. پس هر کس برای خدا و پیامبرش هجرت کند، هجرت او برای خدا و پیامبرش است. و هر کس برای منافع دنیوی یا ازدواج با زنی هجرت کند، هجرت او برای همان چیزی است که برای آن هجرت کرده است." } ], "share_link": "http://example.com/narrators/56/texts/1" @@ -1121,11 +1121,11 @@ transmitter_original_text_swagger = swagger_auto_schema( "translation": [ { "language_code": "en", - "title": "Seeking knowledge is obligatory upon every Muslim, male and female" + "text": "Seeking knowledge is obligatory upon every Muslim, male and female" }, { "language_code": "fa", - "title": "طلب علم بر هر مسلمان و مسلمانی واجب است" + "text": "طلب علم بر هر مسلمان و مسلمانی واجب است" } ], "share_link": "http://example.com/narrators/56/texts/2" diff --git a/apps/hadis/management/commands/seed_books.py b/apps/hadis/management/commands/seed_books.py new file mode 100644 index 0000000..a321a3c --- /dev/null +++ b/apps/hadis/management/commands/seed_books.py @@ -0,0 +1,276 @@ +""" +Django management command to seed mock data for hadith book references. +Place this file in: yourapp/management/commands/seed_books.py + +Usage: python manage.py seed_books +""" + +import os +from pathlib import Path +from django.core.management.base import BaseCommand +from django.core.files.base import ContentFile +from django.utils.text import slugify +from apps.hadis.models.reference import ( + BookReference, + BookReferenceImage, + BookAuthor, + BookAttribute +) + + +class Command(BaseCommand): + help = 'Seed the database with mock hadith book reference data' + + def add_arguments(self, parser): + parser.add_argument( + '--clear', + action='store_true', + help='Clear existing data before seeding', + ) + + def handle(self, *args, **options): + if options['clear']: + self.stdout.write(self.style.WARNING('Clearing existing data...')) + BookAttribute.objects.all().delete() + BookReferenceImage.objects.all().delete() + BookAuthor.objects.all().delete() + BookReference.objects.all().delete() + self.stdout.write(self.style.SUCCESS('Data cleared successfully!')) + + # Create authors first + authors_data = [ + {"name": "Imam Muhammad al-Bukhari"}, + {"name": "Imam Muslim ibn al-Hajjaj"}, + {"name": "Imam Abu Dawood as-Sijistani"}, + {"name": "Imam At-Tirmidhi"}, + {"name": "Imam Ibn Majah"}, + {"name": "Imam Ahmad ibn Hanbal"}, + {"name": "Imam Al-Hakim"}, + {"name": "Imam Ad-Daraqutni"}, + ] + + authors = {} + for author_data in authors_data: + author, created = BookAuthor.objects.get_or_create( + name=author_data['name'] + ) + authors[author_data['name']] = author + if created: + self.stdout.write(self.style.SUCCESS(f'Created author: {author.name}')) + + # Create book references + books_data = [ + { + "title": "Sahih al-Bukhari", + "description": "The most authentic collection of hadith, compiled by Imam Muhammad al-Bukhari. Contains 7,563 ahadith.", + "language": "Arabic", + "isbn": "978-1-86043-009-6", + "volume": "9 volumes", + "year_of_publication": "1870", + "number_page": 1200, + "publisher": "Dar al-Kutub al-Ilmiyah", + "rate": 5.00, + "authors": ["Imam Muhammad al-Bukhari"], + "image_order": 1, + "attributes": { + "Collection Type": "Hadith Compilation", + "Number of Hadith": "7,563", + "Classification": "6 Books", + "Authenticity Grade": "Sahih (Authentic)", + "Compilation Period": "16 years", + } + }, + { + "title": "Sahih Muslim", + "description": "Second most authentic hadith collection compiled by Imam Muslim ibn al-Hajjaj. Contains 9,200 traditions.", + "language": "Arabic", + "isbn": "978-1-86043-010-2", + "volume": "5 volumes", + "year_of_publication": "1875", + "number_page": 1500, + "publisher": "Dar Ihya at-Turath al-Arabi", + "rate": 4.95, + "authors": ["Imam Muslim ibn al-Hajjaj"], + "image_order": 2, + "attributes": { + "Collection Type": "Hadith Compilation", + "Number of Hadith": "9,200", + "Classification": "43 Books", + "Authenticity Grade": "Sahih (Authentic)", + "Unique Hadith": "Approximately 4,000", + } + }, + { + "title": "Sunan Abu Dawood", + "description": "A comprehensive collection of hadith containing jurisprudential material, compiled by Imam Abu Dawood as-Sijistani.", + "language": "Arabic", + "isbn": "978-1-86043-011-9", + "volume": "4 volumes", + "year_of_publication": "1880", + "number_page": 1400, + "publisher": "Islamic Digital Library", + "rate": 4.80, + "authors": ["Imam Abu Dawood as-Sijistani"], + "image_order": 3, + "attributes": { + "Collection Type": "Sunan (Practice)", + "Number of Hadith": "5,274", + "Focus": "Jurisprudential Traditions", + "Number of Books": "43", + "Authenticity Grade": "Hasan to Sahih", + } + }, + { + "title": "Jami' at-Tirmidhi", + "description": "A major collection of hadith compiled by Imam At-Tirmidhi with his commentary and grading of narrations.", + "language": "Arabic", + "isbn": "978-1-86043-012-6", + "volume": "5 volumes", + "year_of_publication": "1892", + "number_page": 1350, + "publisher": "Dar ar-Risalah al-Alamiyah", + "rate": 4.85, + "authors": ["Imam At-Tirmidhi"], + "image_order": 4, + "attributes": { + "Collection Type": "Jami (Comprehensive)", + "Number of Hadith": "3,956", + "Notable Feature": "Grades each hadith", + "Categories": "63 Chapters", + "Authenticity Grade": "Various Grades", + } + }, + { + "title": "Sunan Ibn Majah", + "description": "A collection of hadith compiled by Imam Ibn Majah, one of the Six Canonical Hadith Collections.", + "language": "Arabic", + "isbn": "978-1-86043-013-3", + "volume": "2 volumes", + "year_of_publication": "1888", + "number_page": 900, + "publisher": "Dar Ihya al-Kutub al-Arabiyah", + "rate": 4.75, + "authors": ["Imam Ibn Majah"], + "image_order": 5, + "attributes": { + "Collection Type": "Sunan (Practice)", + "Number of Hadith": "4,341", + "Number of Books": "32", + "Notable Content": "Includes rare narrations", + "Authenticity Grade": "Mixed - requires verification", + } + }, + ] + + books = {} + for book_data in books_data: + # Extract author names + author_names = book_data.pop('authors', []) + image_order = book_data.pop('image_order', 1) + attributes_dict = book_data.pop('attributes', {}) + + # Create or get the book + book, created = BookReference.objects.get_or_create( + title=book_data['title'], + defaults=book_data + ) + + if created: + self.stdout.write(self.style.SUCCESS(f'Created book: {book.title}')) + else: + # Update existing book + for key, value in book_data.items(): + setattr(book, key, value) + book.save() + self.stdout.write(self.style.WARNING(f'Updated book: {book.title}')) + + books[book.title] = book + + # Add authors to book + for author_name in author_names: + author = authors.get(author_name) + if author: + book.authors.add(author) + + # Add book image + image_path = self._get_image_path(image_order) + if image_path and os.path.exists(image_path): + # Check if image already exists for this book + if not book.images.exists(): + with open(image_path, 'rb') as img_file: + image_name = f'book{image_order}.png' + book_image = BookReferenceImage.objects.create( + book_reference=book, + order=1, + description=f"Cover image for {book.title}" + ) + book_image.image.save( + image_name, + ContentFile(img_file.read()), + save=True + ) + self.stdout.write( + self.style.SUCCESS(f'Added image to: {book.title}') + ) + else: + self.stdout.write( + self.style.WARNING( + f'Image not found at {image_path} for {book.title}' + ) + ) + + # Add attributes + for attr_title, attr_value in attributes_dict.items(): + attribute, created = BookAttribute.objects.get_or_create( + book_reference=book, + title=attr_title, + defaults={'value': attr_value} + ) + if created: + self.stdout.write( + self.style.SUCCESS( + f'Added attribute: {attr_title} to {book.title}' + ) + ) + + self.stdout.write( + self.style.SUCCESS( + f'\n✓ Successfully seeded {len(books)} books with all relations!' + ) + ) + self._print_summary() + + def _get_image_path(self, book_number): + """ + Find the image file for the given book number. + Checks multiple possible locations. + """ + possible_paths = [ + Path('seeds/images') / f'book{book_number}.png', + Path('seed_data/images') / f'book{book_number}.png', + Path('static/images') / f'book{book_number}.png', + Path('.') / 'seeds' / 'images' / f'book{book_number}.png', + ] + + for path in possible_paths: + if path.exists(): + return path + + return None + + def _print_summary(self): + """Print a summary of created data""" + self.stdout.write("\n" + "="*60) + self.stdout.write(self.style.SUCCESS("DATABASE SUMMARY")) + self.stdout.write("="*60) + + books_count = BookReference.objects.count() + authors_count = BookAuthor.objects.count() + images_count = BookReferenceImage.objects.count() + attributes_count = BookAttribute.objects.count() + + self.stdout.write(f"📚 Total Books: {books_count}") + self.stdout.write(f"✍️ Total Authors: {authors_count}") + self.stdout.write(f"🖼️ Total Images: {images_count}") + self.stdout.write(f"🏷️ Total Attributes: {attributes_count}") + self.stdout.write("="*60 + "\n") \ No newline at end of file diff --git a/apps/hadis/management/commands/seed_category_data.py b/apps/hadis/management/commands/seed_category_data.py new file mode 100644 index 0000000..19dab69 --- /dev/null +++ b/apps/hadis/management/commands/seed_category_data.py @@ -0,0 +1,497 @@ +""" +Django management command to seed mock HadisCategory trees and new Hadis +for history, fatwa and quote sources. + +Place this file in: hadis/management/commands/seed_categories.py + +Usage: + python manage.py seed_categories + python manage.py seed_categories --clear +""" + +from django.core.management.base import BaseCommand +from django.utils.text import slugify +from django.conf import settings + +from apps.hadis.models.category import HadisCategory, HadisSect +from apps.hadis.models.hadis import Hadis, HadisStatus, HadisTag + + +class Command(BaseCommand): + help = "Seed HadisCategory trees (history, fatwa, quote) and new Hadis connected to them." + + def add_arguments(self, parser): + parser.add_argument( + '--clear', + action='store_true', + help='Clear ONLY categories & hadises created by this command (history/fatwa/quote).', + ) + + def handle(self, *args, **options): + if options['clear']: + self._clear_seeded_data() + + # Ensure sects exist + try: + shia_sect = HadisSect.objects.get(sect_type=HadisSect.SectType.SHIA) + except HadisSect.DoesNotExist: + shia_sect = HadisSect.objects.create( + sect_type=HadisSect.SectType.SHIA, + title="Shia", + description="Default Shia sect" + ) + + try: + sunni_sect = HadisSect.objects.get(sect_type=HadisSect.SectType.SUNNI) + except HadisSect.DoesNotExist: + sunni_sect = HadisSect.objects.create( + sect_type=HadisSect.SectType.SUNNI, + title="Sunni", + description="Default Sunni sect" + ) + + # Optional: ensure a couple of HadisStatus entries exist + default_status, _ = HadisStatus.objects.get_or_create( + title="Authentic / Accepted", + defaults={"color": HadisStatus.ColorChoices.GREEN, "order": 1}, + ) + weak_status, _ = HadisStatus.objects.get_or_create( + title="Weak / Needs Review", + defaults={"color": HadisStatus.ColorChoices.YELLOW, "order": 2}, + ) + + # Ensure some tags exist + tags = self._ensure_tags() + + # 1) Seed HISTORY categories & hadises + self.stdout.write(self.style.SUCCESS("\n📚 Creating HISTORY categories and hadiths...")) + self._seed_history_tree(sunni_sect, default_status, tags) + + # 2) Seed FATWA categories & hadises + self.stdout.write(self.style.SUCCESS("\n📜 Creating FATWA categories and hadiths...")) + self._seed_fatwa_tree(sunni_sect, default_status, weak_status, tags) + + # 3) Seed QUOTE categories & hadises + self.stdout.write(self.style.SUCCESS("\n💬 Creating QUOTE categories and hadiths...")) + self._seed_quote_tree(shia_sect, default_status, tags) + + self._print_summary() + + # ------------------------------------------------------------------ # + # Helpers + # ------------------------------------------------------------------ # + + def _clear_seeded_data(self): + """ + Clear only categories with source_type in (history, fatwa, quote) + and hadises that are attached to those categories. + """ + self.stdout.write(self.style.WARNING("Clearing previously seeded history/fatwa/quote data...")) + leaf_categories = HadisCategory.objects.filter( + source_type__in=[ + HadisCategory.SourceType.HISTORY, + HadisCategory.SourceType.FATWA, + HadisCategory.SourceType.QUOTE, + ] + ) + Hadis.objects.filter(category__in=leaf_categories).delete() + # Delete categories (MPTT will handle tree structure) + HadisCategory.objects.filter( + source_type__in=[ + HadisCategory.SourceType.HISTORY, + HadisCategory.SourceType.FATWA, + HadisCategory.SourceType.QUOTE, + ] + ).delete() + self.stdout.write(self.style.SUCCESS("✓ Cleared seeded history/fatwa/quote categories and hadiths.")) + + def _ensure_tags(self): + base_titles = [ + "history", + "biography", + "battle", + "ethics", + "jurisprudence", + "family", + "wisdom", + "short quote", + ] + tags = [] + for title in base_titles: + tag, _ = HadisTag.objects.get_or_create(title=title) + tags.append(tag) + return tags + + # ---------------- HISTORY ---------------- # + + def _seed_history_tree(self, sect, default_status, tags): + """ + Create a small HISTORY tree: + History of Islam + ├─ Early Caliphate (leaf) + └─ Battles (leaf) + And add several hadith-like historical reports. + """ + root, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.HISTORY, + title="History of Islam", + defaults={ + "description": "High-level historical themes related to early Islamic history.", + "order": 1, + }, + ) + + early_caliphate, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.HISTORY, + parent=root, + title="Early Caliphate", + defaults={ + "description": "Events and reports from the period of the first caliphs.", + "order": 1, + }, + ) + + battles, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.HISTORY, + parent=root, + title="Battles and Expeditions", + defaults={ + "description": "Key battles and expeditions in early Islamic history.", + "order": 2, + }, + ) + + # Create some historical “hadith” entries (reports) + # For simplicity, use new numbers starting from 1900+ + self._create_hadis( + number=1900, + category=early_caliphate, + title="The Consultation of the Companions", + text=( + "It is reported in historical works that the companions would gather and " + "consult one another regarding major affairs of the community." + ), + translation_en=( + "Historical reports mention that the companions would consult one another " + "on major communal matters." + ), + status=default_status, + tags=[t for t in tags if t.title in ["history", "biography"]], + ) + + self._create_hadis( + number=1901, + category=early_caliphate, + title="Establishment of the Public Treasury", + text=( + "Some historians narrate that during the early caliphate a public treasury " + "was organized to administer charity and public funds." + ), + translation_en=( + "Historical sources state that a public treasury was set up to manage " + "charity and communal funds." + ), + status=default_status, + tags=[t for t in tags if t.title in ["history", "jurisprudence"]], + ) + + self._create_hadis( + number=1910, + category=battles, + title="Report of the Battle Preparations", + text=( + "Chronicles record that the believers prepared carefully before major battles, " + "ensuring justice and discipline in their ranks." + ), + translation_en=( + "Historical chronicles record careful preparations and discipline before major battles." + ), + status=default_status, + tags=[t for t in tags if t.title in ["history", "battle"]], + ) + + self._create_hadis( + number=1911, + category=battles, + title="Mercy Shown After Victory", + text=( + "Historical narrations mention that after some victories, clemency and mercy were " + "shown to prisoners and civilians." + ), + translation_en=( + "Historical narrations mention mercy and clemency after some military victories." + ), + status=default_status, + tags=[t for t in tags if t.title in ["history", "ethics"]], + ) + + # ---------------- FATWA ---------------- # + + def _seed_fatwa_tree(self, sect, default_status, weak_status, tags): + """ + Create a FATWA tree like: + Contemporary Fatwas + ├─ Worship (leaf) + └─ Family Issues (leaf) + Plus a second root: + Financial Fatwas + └─ Trade and Contracts (leaf) + """ + root_contemporary, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.FATWA, + title="Contemporary Fatwas", + defaults={ + "description": "Modern juristic responses to contemporary questions.", + "order": 1, + }, + ) + + worship, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.FATWA, + parent=root_contemporary, + title="Worship and Rituals", + defaults={ + "description": "Fatwas about prayer, fasting and other acts of worship.", + "order": 1, + }, + ) + + family_issues, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.FATWA, + parent=root_contemporary, + title="Family Issues", + defaults={ + "description": "Fatwas regarding marriage, divorce and family obligations.", + "order": 2, + }, + ) + + financial_root, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.FATWA, + title="Financial Fatwas", + defaults={ + "description": "Juristic rulings about trade, contracts and modern finance.", + "order": 2, + }, + ) + + trade_contracts, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.FATWA, + parent=financial_root, + title="Trade and Contracts", + defaults={ + "description": "Fatwas related to buying, selling and contractual agreements.", + "order": 1, + }, + ) + + # Create some “fatwa-style” hadith entries (answers / rulings) + self._create_hadis( + number=1920, + category=worship, + title="Fatwa on Combining Prayers While Traveling", + text=( + "A contemporary juristic council has ruled that combining prayers while traveling " + "is permitted when hardship is present, following classical precedents." + ), + translation_en=( + "A modern fatwa allows combining prayers during travel where hardship exists, " + "based on earlier jurisprudence." + ), + status=default_status, + tags=[t for t in tags if t.title in ["jurisprudence", "ethics"]], + ) + + self._create_hadis( + number=1921, + category=worship, + title="Fatwa on Using Local Calculations for Prayer Times", + text=( + "Modern scholars have issued fatwas permitting the use of accurate astronomical " + "calculations for determining prayer times." + ), + translation_en=( + "Contemporary fatwas permit using precise astronomical calculations for prayer times." + ), + status=weak_status, + tags=[t for t in tags if t.title in ["jurisprudence", "knowledge"]], + ) + + self._create_hadis( + number=1930, + category=family_issues, + title="Fatwa on Upholding Family Ties", + text=( + "A fatwa committee emphasized that maintaining family ties is obligatory and that " + "cutting off relatives without valid reason is sinful." + ), + translation_en=( + "A fatwa stresses that keeping family ties is obligatory and severing them without cause is sinful." + ), + status=default_status, + tags=[t for t in tags if t.title in ["family", "ethics"]], + ) + + self._create_hadis( + number=1940, + category=trade_contracts, + title="Fatwa on Transparent Business Contracts", + text=( + "Scholars have ruled that contracts must be transparent and free from deception in order " + "to be valid in Islamic law." + ), + translation_en=( + "A modern fatwa requires transparency and absence of deception in business contracts." + ), + status=default_status, + tags=[t for t in tags if t.title in ["business", "jurisprudence"]] if any( + t.title == "business" for t in tags + ) else [t for t in tags if t.title in ["jurisprudence"]], + ) + + # ---------------- QUOTE ---------------- # + + def _seed_quote_tree(self, sect, default_status, tags): + """ + Create a QUOTE tree: + Wisdom Quotes + ├─ Short Wisdom (leaf) + └─ On Knowledge (leaf) + """ + root, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.QUOTE, + title="Wisdom Quotes", + defaults={ + "description": "Short wise sayings and inspirational quotes.", + "order": 1, + }, + ) + + short_wisdom, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.QUOTE, + parent=root, + title="Short Wisdom", + defaults={ + "description": "Very short, memorable quotes on character and behavior.", + "order": 1, + }, + ) + + on_knowledge, _ = HadisCategory.objects.get_or_create( + sect=sect, + source_type=HadisCategory.SourceType.QUOTE, + parent=root, + title="On Knowledge", + defaults={ + "description": "Quotes emphasizing the virtue of knowledge and learning.", + "order": 2, + }, + ) + + # Short quote-style entries + self._create_hadis( + number=1950, + category=short_wisdom, + title="The Measure of a Heart", + text="It is said: The worth of a person is in what their heart carries of mercy and truth.", + translation_en="It is said: A person’s value is measured by the mercy and truth in their heart.", + status=default_status, + tags=[t for t in tags if t.title in ["wisdom", "short quote", "ethics"]], + ) + + self._create_hadis( + number=1951, + category=short_wisdom, + title="Silence and Wisdom", + text="One of the wise said: Many people would be considered wise if they knew when to remain silent.", + translation_en="A wise saying: Many would be counted wise if they knew when to be silent.", + status=default_status, + tags=[t for t in tags if t.title in ["wisdom", "short quote"]], + ) + + self._create_hadis( + number=1960, + category=on_knowledge, + title="Seeking Knowledge as Light", + text="It is narrated from the scholars: Knowledge is a light that guides the heart towards what benefits it.", + translation_en="Scholars say: Knowledge is a light guiding the heart to what benefits it.", + status=default_status, + tags=[t for t in tags if t.title in ["knowledge", "wisdom"]], + ) + + self._create_hadis( + number=1961, + category=on_knowledge, + title="Learning until the End", + text="One sage said: Continue to seek knowledge until the last day of your life, for ignorance is a darkness.", + translation_en="A sage said: Seek knowledge until your last day, for ignorance is darkness.", + status=default_status, + tags=[t for t in tags if t.title in ["knowledge", "wisdom"]], + ) + + # ---------------- utility to create Hadis ---------------- # + + def _create_hadis(self, number, category, title, text, translation_en, status, tags): + """ + Create or update a Hadis with the given number and category. + Translation uses {'en': '...'} structure as requested. + """ + hadis, created = Hadis.objects.get_or_create( + number=number, + category=category, + defaults={ + "title": title, + "title_narrator": None, + "description": "", + "text": text, + "translation": {"en": translation_en}, + "status": True, + "hadis_status": status, + "hadis_status_text": status.title if status else "", + "address": "", + "links": {}, + "explanation": "", + }, + ) + if not created: + hadis.title = title + hadis.text = text + hadis.translation = {"en": translation_en} + hadis.hadis_status = status + hadis.hadis_status_text = status.title if status else "" + hadis.category = category + hadis.save() + + if tags: + hadis.tags.set(tags) + return hadis + + def _print_summary(self): + from apps.hadis.models import HadisCategory, Hadis + + self.stdout.write("\n" + "=" * 70) + self.stdout.write(self.style.SUCCESS("CATEGORY & HADIS SUMMARY")) + self.stdout.write("=" * 70) + + history_count = HadisCategory.objects.filter(source_type=HadisCategory.SourceType.HISTORY).count() + fatwa_count = HadisCategory.objects.filter(source_type=HadisCategory.SourceType.FATWA).count() + quote_count = HadisCategory.objects.filter(source_type=HadisCategory.SourceType.QUOTE).count() + + history_hadis = Hadis.objects.filter(category__source_type=HadisCategory.SourceType.HISTORY).count() + fatwa_hadis = Hadis.objects.filter(category__source_type=HadisCategory.SourceType.FATWA).count() + quote_hadis = Hadis.objects.filter(category__source_type=HadisCategory.SourceType.QUOTE).count() + + self.stdout.write(f"📂 HISTORY categories: {history_count} | hadith-like records: {history_hadis}") + self.stdout.write(f"📂 FATWA categories: {fatwa_count} | fatwa records: {fatwa_hadis}") + self.stdout.write(f"📂 QUOTE categories: {quote_count} | quote records: {quote_hadis}") + self.stdout.write("=" * 70 + "\n") diff --git a/apps/hadis/management/commands/seed_hadis_2.py b/apps/hadis/management/commands/seed_hadis_2.py new file mode 100644 index 0000000..8341218 --- /dev/null +++ b/apps/hadis/management/commands/seed_hadis_2.py @@ -0,0 +1,432 @@ +""" +Django management command to seed mock data for hadith collections, references, and corrections. +Place this file in: yourapp/management/commands/seed_hadis.py + +Usage: python manage.py seed_hadis + python manage.py seed_hadis --clear +""" + +import os +from pathlib import Path +from django.core.management.base import BaseCommand +from django.core.files.base import ContentFile +from django.utils.text import slugify +from django.conf import settings +from apps.hadis.models.hadis import ( + HadisCollection, + HadisInCollection, + HadisReference, + ReferenceImage, + HadisCorrection, + Hadis +) +from apps.hadis.models.reference import BookReference + + +class Command(BaseCommand): + help = 'Seed the database with mock hadith collection and reference data' + + def add_arguments(self, parser): + parser.add_argument( + '--clear', + action='store_true', + help='Clear existing data before seeding', + ) + + def handle(self, *args, **options): + if options['clear']: + self.stdout.write(self.style.WARNING('Clearing existing hadith data...')) + ReferenceImage.objects.all().delete() + HadisCorrection.objects.all().delete() + HadisReference.objects.all().delete() + HadisInCollection.objects.all().delete() + HadisCollection.objects.all().delete() + self.stdout.write(self.style.SUCCESS('Data cleared successfully!')) + + # Step 1: Create Hadis Collections + self.stdout.write(self.style.SUCCESS('\n📚 Creating Hadis Collections...')) + collections_data = [ + { + "title": "Purification & Prayer", + "summary": "Collection of ahadith related to purification (Wudu, Ghusl) and prayer rituals.", + "status": True, + "order": 1, + }, + { + "title": "Zakat & Charity", + "summary": "Ahadith discussing the obligation of zakat and charitable giving in Islam.", + "status": True, + "order": 2, + }, + { + "title": "Fasting & Ramadan", + "summary": "Collection of authentic ahadith about fasting, its virtues, and Ramadan practices.", + "status": True, + "order": 3, + }, + { + "title": "Hajj & Umrah", + "summary": "Ahadith related to the pilgrimage to Mecca and the spiritual practices involved.", + "status": True, + "order": 4, + }, + { + "title": "Ethics & Morality", + "summary": "Ahadith emphasizing Islamic ethics, good character, and moral conduct.", + "status": True, + "order": 5, + }, + { + "title": "Knowledge & Learning", + "summary": "Collection emphasizing the importance of seeking knowledge in Islam.", + "status": True, + "order": 6, + }, + { + "title": "Family & Relations", + "summary": "Ahadith about family relationships, rights of parents, spouses, and children.", + "status": True, + "order": 7, + }, + { + "title": "Business & Trade", + "summary": "Ahadith related to ethical business practices and commerce in Islam.", + "status": True, + "order": 8, + }, + ] + + collections = {} + for collection_data in collections_data: + collection, created = HadisCollection.objects.get_or_create( + title=collection_data['title'], + defaults=collection_data + ) + collections[collection_data['title']] = collection + status = '✓ Created' if created else '• Updated' + self.stdout.write(f"{status}: {collection.title}") + + # Step 2: Get book references (we'll use all available books) + self.stdout.write(self.style.SUCCESS('\n📖 Getting Book References...')) + book_references = BookReference.objects.all() + if not book_references.exists(): + self.stdout.write(self.style.WARNING('⚠️ No book references found. Run seed_books first!')) + return + + book_references_list = list(book_references) + self.stdout.write(f"✓ Found {len(book_references_list)} book references") + + # Step 3: Create Hadis References + self.stdout.write(self.style.SUCCESS('\n🔗 Creating Hadis References...')) + + hadis_ids = list(range(1800, 1853)) + reference_descriptions = [ + "This hadith is recorded in the collection with full chain of narration.", + "The narration is found in multiple reliable sources with slight variations.", + "This is a widely transmitted hadith with consistent narration across sources.", + "The hadith is reported with authentic chain and acceptable wording.", + "This tradition appears in the primary sources with confirmed authenticity.", + "The narration demonstrates the scholarly consensus on this issue.", + "The hadith is part of the foundational traditions in this category.", + "This is one of the most frequently cited ahadith on this topic.", + ] + + hadis_references = {} + reference_counter = 0 + + for idx, hadis_id in enumerate(hadis_ids): + # Get or create Hadis object + try: + hadis = Hadis.objects.get(pk=hadis_id) + except Hadis.DoesNotExist: + self.stdout.write( + self.style.WARNING(f"⚠️ Hadis with ID {hadis_id} not found, skipping...") + ) + continue + + # Randomly assign 1-3 book references per hadith + num_refs = (idx % 3) + 1 + selected_books = book_references_list[idx % len(book_references_list):(idx % len(book_references_list)) + num_refs] + + for book_idx, book_ref in enumerate(selected_books): + hadis_ref, created = HadisReference.objects.get_or_create( + hadis=hadis, + book_reference=book_ref, + defaults={ + 'description': reference_descriptions[reference_counter % len(reference_descriptions)] + } + ) + if created: + hadis_references[hadis_id] = hadis_ref + reference_counter += 1 + self.stdout.write( + self.style.SUCCESS( + f"✓ Reference: Hadis {hadis_id} <- {book_ref.title}" + ) + ) + + # Step 4: Add Reference Images + self.stdout.write(self.style.SUCCESS('\n🖼️ Adding Reference Images...')) + ref_images_added = self._add_reference_images(hadis_references) + self.stdout.write(f"✓ Added {ref_images_added} reference images") + + # Step 5: Create Hadis In Collection + self.stdout.write(self.style.SUCCESS('\n📋 Adding Hadis to Collections...')) + + collection_assignments = [ + { + "collection_title": "Purification & Prayer", + "hadis_ids": [1800, 1801, 1802, 1803, 1804], + }, + { + "collection_title": "Zakat & Charity", + "hadis_ids": [1805, 1806, 1807, 1808], + }, + { + "collection_title": "Fasting & Ramadan", + "hadis_ids": [1809, 1810, 1811, 1812, 1813], + }, + { + "collection_title": "Hajj & Umrah", + "hadis_ids": [1814, 1815, 1816, 1817], + }, + { + "collection_title": "Ethics & Morality", + "hadis_ids": [1818, 1819, 1820, 1821, 1822, 1823], + }, + { + "collection_title": "Knowledge & Learning", + "hadis_ids": [1824, 1825, 1826, 1827], + }, + { + "collection_title": "Family & Relations", + "hadis_ids": [1828, 1829, 1830, 1831, 1832, 1833], + }, + { + "collection_title": "Business & Trade", + "hadis_ids": [1834, 1835, 1836, 1837, 1838], + }, + ] + + hadis_in_collection_count = 0 + for assignment in collection_assignments: + collection = collections.get(assignment['collection_title']) + if not collection: + continue + + for order, hadis_id in enumerate(assignment['hadis_ids']): + try: + hadis = Hadis.objects.get(pk=hadis_id) + hic, created = HadisInCollection.objects.get_or_create( + hadis=hadis, + collection=collection, + defaults={'order': order} + ) + if created: + hadis_in_collection_count += 1 + self.stdout.write( + self.style.SUCCESS( + f"✓ Added Hadis {hadis_id} to {collection.title} (Order {order})" + ) + ) + except Hadis.DoesNotExist: + pass + + # Step 6: Create Hadis Corrections + self.stdout.write(self.style.SUCCESS('\n✏️ Creating Hadis Corrections...')) + + corrections_data = [ + { + "hadis_id": 1800, + "title": "Clarification on Wudu Sequence", + "description": "Some scholars differ on the exact sequence of washing body parts during ablution.", + "translation": [ + {"lang": "en", "text": "The order of washing in wudu should follow the established practice."}, + {"lang": "ar", "text": "ترتيب غسل الأعضاء في الوضوء يجب أن يتبع السنة المثبتة."} + ], + }, + { + "hadis_id": 1802, + "title": "Prayer Time Variations", + "description": "Different schools of thought have varying opinions on prayer times.", + "translation": [ + {"lang": "en", "text": "Prayer times may vary slightly depending on geographical location."}, + {"lang": "ar", "text": "أوقات الصلاة قد تختلف قليلاً حسب الموقع الجغرافي."} + ], + }, + { + "hadis_id": 1805, + "title": "Zakat Calculation Methods", + "description": "Various methods exist for calculating zakat on different types of wealth.", + "translation": [ + {"lang": "en", "text": "The calculation of zakat follows specific rules for different assets."}, + {"lang": "ar", "text": "حساب الزكاة يتبع قواعد محددة لأنواع مختلفة من الأموال."} + ], + }, + { + "hadis_id": 1810, + "title": "Fasting in Different Climates", + "description": "Scholars discuss fasting practices in regions with extreme daylight variations.", + "translation": [ + {"lang": "en", "text": "Fasting guidelines adapt to extreme daylight conditions in certain regions."}, + {"lang": "ar", "text": "تتكيف إرشادات الصيام مع ظروف الإضاءة الشديدة في بعض المناطق."} + ], + }, + { + "hadis_id": 1815, + "title": "Hajj Routes and Methods", + "description": "Multiple routes and methods for performing Hajj rituals are recognized.", + "translation": [ + {"lang": "en", "text": "Different schools recognize various methods for performing Hajj rituals."}, + {"lang": "ar", "text": "تعترف المذاهب المختلفة بطرق متعددة لأداء مناسك الحج."} + ], + }, + { + "hadis_id": 1820, + "title": "Ethical Conduct in Commerce", + "description": "Islamic principles of honesty and fairness in business transactions.", + "translation": [ + {"lang": "en", "text": "Islamic business ethics emphasize honesty and mutual fairness."}, + {"lang": "ar", "text": "تؤكد أخلاقيات الأعمال الإسلامية على الصدق والإنصاف المتبادل."} + ], + }, + { + "hadis_id": 1825, + "title": "Hadith Authentication Methods", + "description": "Scholars use various methodologies for authenticating hadith narrations.", + "translation": [ + {"lang": "en", "text": "Hadith scholars employ rigorous methodology in authentication."}, + {"lang": "ar", "text": "يستخدم علماء الحديث منهجية صارمة في التحقق من الصحة."} + ], + }, + { + "hadis_id": 1830, + "title": "Family Rights and Obligations", + "description": "The Islamic framework defines rights and responsibilities within families.", + "translation": [ + {"lang": "en", "text": "Islam defines clear rights and obligations for family members."}, + {"lang": "ar", "text": "يحدد الإسلام حقوقاً والتزامات واضحة لأفراد الأسرة."} + ], + }, + ] + + corrections_count = 0 + for correction_data in corrections_data: + hadis_id = correction_data.pop('hadis_id') + try: + hadis = Hadis.objects.get(pk=hadis_id) + correction, created = HadisCorrection.objects.get_or_create( + hadis=hadis, + title=correction_data['title'], + defaults=correction_data + ) + if created: + corrections_count += 1 + self.stdout.write( + self.style.SUCCESS( + f"✓ Correction created: {correction_data['title']} for Hadis {hadis_id}" + ) + ) + except Hadis.DoesNotExist: + pass + + self.stdout.write( + self.style.SUCCESS( + f'\n✓ Successfully seeded hadith data!' + ) + ) + self._print_summary( + collections, + hadis_in_collection_count, + len(hadis_references), + ref_images_added, + corrections_count + ) + + def _add_reference_images(self, hadis_references): + """Add reference images from seeds/images folder""" + images_added = 0 + ref_images = ['ref1.png', 'ref2.png', 'ref3.png', 'ref4.png'] + image_counter = 0 + + for hadis_id, hadis_ref in hadis_references.items(): + # Add 1-2 images per reference + num_images = (image_counter % 2) + 1 + + for img_idx in range(num_images): + image_name = ref_images[image_counter % len(ref_images)] + image_path = self._get_image_path(image_name) + + if image_path and os.path.exists(image_path): + # Check if image already exists + existing_images_count = hadis_ref.images.count() + + with open(image_path, 'rb') as img_file: + ref_image, created = ReferenceImage.objects.get_or_create( + reference=hadis_ref, + priority=existing_images_count + img_idx, + defaults={ + 'thumbnail': None + } + ) + if created: + # Save the image file + ref_image.thumbnail.save( + image_name, + ContentFile(img_file.read()), + save=True + ) + images_added += 1 + self.stdout.write( + self.style.SUCCESS( + f"✓ Image added: {image_name} to Hadis {hadis_id} reference" + ) + ) + + image_counter += 1 + + return images_added + + def _get_image_path(self, image_name): + """Find the image file for the given image name""" + possible_paths = [ + Path('seeds/images') / image_name, + Path('seed_data/images') / image_name, + Path('static/images') / image_name, + Path('.') / 'seeds' / 'images' / image_name, + ] + + for path in possible_paths: + if path.exists(): + return path + + return None + + def _print_summary(self, collections, hadis_in_collection_count, references_count, images_count, corrections_count): + """Print a summary of created data""" + self.stdout.write("\n" + "="*70) + self.stdout.write(self.style.SUCCESS("HADITH DATABASE SUMMARY")) + self.stdout.write("="*70) + + collections_count = HadisCollection.objects.count() + total_hadis_in_collection = HadisInCollection.objects.count() + total_references = HadisReference.objects.count() + total_ref_images = ReferenceImage.objects.count() + total_corrections = HadisCorrection.objects.count() + + self.stdout.write(f"📚 Hadis Collections: {collections_count}") + self.stdout.write(f"📋 Hadis in Collections: {total_hadis_in_collection}") + self.stdout.write(f"🔗 Hadis References: {total_references}") + self.stdout.write(f"🖼️ Reference Images: {total_ref_images}") + self.stdout.write(f"✏️ Hadis Corrections: {total_corrections}") + + self.stdout.write("\n" + "="*70) + self.stdout.write(self.style.WARNING("Collections Created:")) + self.stdout.write("="*70) + for collection_title in list(collections.keys()): + hadis_count = HadisInCollection.objects.filter( + collection__title=collection_title + ).count() + self.stdout.write(f" • {collection_title} ({hadis_count} ahadith)") + + self.stdout.write("="*70 + "\n") \ No newline at end of file diff --git a/apps/hadis/management/commands/seed_transmitters.py b/apps/hadis/management/commands/seed_transmitters.py new file mode 100644 index 0000000..5ae9cef --- /dev/null +++ b/apps/hadis/management/commands/seed_transmitters.py @@ -0,0 +1,613 @@ +""" +Django management command to seed mock data for hadith transmitters and related models. +Place this file in: yourapp/management/commands/seed_transmitters.py + +Usage: python manage.py seed_transmitters + python manage.py seed_transmitters --clear +""" + +import json +from django.core.management.base import BaseCommand +from django.utils.text import slugify +from apps.hadis.models.transmitter import ( + NarratorLayer, + Transmitters, + HadisTransmitter, + TransmitterOpinion, + TransmitterOriginalText +) + + +class Command(BaseCommand): + help = 'Seed the database with mock hadith transmitter data' + + def add_arguments(self, parser): + parser.add_argument( + '--clear', + action='store_true', + help='Clear existing data before seeding', + ) + + def handle(self, *args, **options): + if options['clear']: + self.stdout.write(self.style.WARNING('Clearing existing transmitter data...')) + TransmitterOriginalText.objects.all().delete() + TransmitterOpinion.objects.all().delete() + HadisTransmitter.objects.all().delete() + Transmitters.objects.all().delete() + NarratorLayer.objects.all().delete() + self.stdout.write(self.style.SUCCESS('Data cleared successfully!')) + + # Step 1: Create Narrator Layers (Tabaqat) + self.stdout.write(self.style.SUCCESS('\n📚 Creating Narrator Layers...')) + layers_data = [ + { + "name": "Sahaba (Companions)", + "number": 1, + "description": "The companions of Prophet Muhammad (PBUH) who heard directly from him." + }, + { + "name": "Tabi'un (Successors)", + "number": 2, + "description": "Narrators who lived after the Prophet's era and heard from the companions." + }, + { + "name": "Taba' Tabi'in (Followers of Successors)", + "number": 3, + "description": "Narrators who heard from the Successors." + }, + { + "name": "Later Generations", + "number": 4, + "description": "Narrators from later Islamic centuries, known for their scholarship." + }, + { + "name": "Modern Era Scholars", + "number": 5, + "description": "Contemporary scholars who have specialized knowledge of hadith narration." + }, + ] + + layers = {} + for layer_data in layers_data: + layer, created = NarratorLayer.objects.get_or_create( + number=layer_data['number'], + defaults={ + 'name': layer_data['name'], + 'description': layer_data['description'], + 'slug': slugify(layer_data['name']) + } + ) + layers[layer_data['number']] = layer + status = '✓ Created' if created else '• Updated' + self.stdout.write(f"{status}: {layer.name} (Layer {layer.number})") + + # Step 2: Create Transmitters (Hadith Narrators) + self.stdout.write(self.style.SUCCESS('\n👥 Creating Transmitters...')) + transmitters_data = [ + { + "full_name": "Abu Hurayrah", + "kunya": "Abu Hurayrah", + "known_as": "Abd al-Rahman ibn Sakhr", + "nickname": "Father of the Kitten", + "origin": "Yamama, Arabia", + "lived_in": "Medina, Syria", + "died_in": "Medina", + "birth_year_hijri": 7, + "death_year_hijri": 58, + "age_at_death": 78, + "generation": 1, + "reliability": "very_reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "One of the most prolific hadith narrators, reported 5,374 ahadith.", + "narrator_layer": 1, + }, + { + "full_name": "Aisha bint Abu Bakr", + "kunya": "Umm al-Mu'minin", + "known_as": "Mother of the Believers", + "nickname": "Al-Siddiqa", + "origin": "Medina", + "lived_in": "Medina", + "died_in": "Medina", + "birth_year_hijri": None, + "death_year_hijri": 58, + "age_at_death": 66, + "generation": 1, + "reliability": "very_reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Wife of the Prophet Muhammad (PBUH), a major source of hadith about daily life.", + "narrator_layer": 1, + }, + { + "full_name": "Jabir ibn Abdullah al-Ansari", + "kunya": "Abu Abdullah", + "known_as": "Jabir", + "nickname": "Al-Ansari", + "origin": "Medina", + "lived_in": "Medina", + "died_in": "Medina", + "birth_year_hijri": 10, + "death_year_hijri": 74, + "age_at_death": 94, + "generation": 1, + "reliability": "very_reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "One of the long-lived companions, reported numerous ahadith on various topics.", + "narrator_layer": 1, + }, + { + "full_name": "Imam Malik ibn Anas", + "kunya": "Abu Abdullah", + "known_as": "Malik", + "nickname": "Imam of Imams", + "origin": "Medina", + "lived_in": "Medina", + "died_in": "Medina", + "birth_year_hijri": 93, + "death_year_hijri": 179, + "age_at_death": 86, + "generation": 3, + "reliability": "very_reliable", + "madhhab": "maliki", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Founder of the Maliki school of Islamic jurisprudence, compiler of Al-Muwatta.", + "narrator_layer": 3, + }, + { + "full_name": "Al-Qasim ibn Muhammad", + "kunya": "Abu Muhammad", + "known_as": "Al-Qasim", + "nickname": "Son of the Rightly Guided", + "origin": "Medina", + "lived_in": "Medina", + "died_in": "Medina", + "birth_year_hijri": 38, + "death_year_hijri": 106, + "age_at_death": 68, + "generation": 2, + "reliability": "reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Son of Amir al-Mu'minin, known for his knowledge of Islamic jurisprudence.", + "narrator_layer": 2, + }, + { + "full_name": "Urwa ibn al-Zubayr", + "kunya": "Abu Abdullah", + "known_as": "Urwa", + "nickname": "The Jurist", + "origin": "Medina", + "lived_in": "Medina, Mecca", + "died_in": "Medina", + "birth_year_hijri": 23, + "death_year_hijri": 94, + "age_at_death": 71, + "generation": 2, + "reliability": "very_reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Prominent Tabi'un scholar and transmitter of hadith from Aisha.", + "narrator_layer": 2, + }, + { + "full_name": "Abdullah ibn Abbas", + "kunya": "Abu Abbas", + "known_as": "Ibn Abbas", + "nickname": "Hibr al-Ummah (The Learned Scholar of the Nation)", + "origin": "Medina", + "lived_in": "Medina, Mecca, Basra", + "died_in": "Taif", + "birth_year_hijri": 3, + "death_year_hijri": 68, + "age_at_death": 71, + "generation": 1, + "reliability": "very_reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Cousin of the Prophet (PBUH), famous for Quranic exegesis and hadith knowledge.", + "narrator_layer": 1, + }, + { + "full_name": "Nafi' (Mawla of Ibn Umar)", + "kunya": "Abu Abdullah", + "known_as": "Nafi'", + "nickname": "The Freed Slave", + "origin": "Ethiopia", + "lived_in": "Medina", + "died_in": "Medina", + "birth_year_hijri": 25, + "death_year_hijri": 117, + "age_at_death": 92, + "generation": 2, + "reliability": "very_reliable", + "madhhab": "sunni", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Freed slave of Abdullah ibn Umar, transmitted numerous hadith from him.", + "narrator_layer": 2, + }, + { + "full_name": "Imam Ahmad ibn Hanbal", + "kunya": "Abu Abdullah", + "known_as": "Ahmad", + "nickname": "Shaykh al-Islam", + "origin": "Khorasan", + "lived_in": "Baghdad", + "died_in": "Baghdad", + "birth_year_hijri": 164, + "death_year_hijri": 241, + "age_at_death": 77, + "generation": 4, + "reliability": "very_reliable", + "madhhab": "hanbali", + "in_sahih_muslim": False, + "in_sahih_bukhari": False, + "description": "Founder of the Hanbali school, compiler of Musnad Ahmad with 40,000+ hadith.", + "narrator_layer": 3, + }, + { + "full_name": "Sufyan ibn Uyayna", + "kunya": "Abu Muhammad", + "known_as": "Sufyan", + "nickname": "Amir al-Mu'minin fil-Hadith", + "origin": "Kufa", + "lived_in": "Mecca, Kufa", + "died_in": "Mecca", + "birth_year_hijri": 107, + "death_year_hijri": 198, + "age_at_death": 91, + "generation": 3, + "reliability": "very_reliable", + "madhhab": "shafii", + "in_sahih_muslim": True, + "in_sahih_bukhari": True, + "description": "Great hadith scholar and judge, known for his exceptional memory.", + "narrator_layer": 3, + }, + ] + + transmitters = {} + for trans_data in transmitters_data: + narrator_layer_num = trans_data.pop('narrator_layer') + narrator_layer = layers.get(narrator_layer_num) + + transmitter, created = Transmitters.objects.get_or_create( + full_name=trans_data['full_name'], + defaults=trans_data + ) + transmitters[trans_data['full_name']] = transmitter + status = '✓ Created' if created else '• Updated' + self.stdout.write(f"{status}: {transmitter.full_name}") + + # Step 3: Create Transmitter Opinions + self.stdout.write(self.style.SUCCESS('\n💬 Creating Transmitter Opinions...')) + opinions_data = [ + { + "transmitter_name": "Abu Hurayrah", + "scholar_name": "Imam al-Bukhari", + "opinion_text": "Abu Hurayrah is one of the most reliable narrators with a perfect memory and integrity.", + "status": "confirmed" + }, + { + "transmitter_name": "Abu Hurayrah", + "scholar_name": "Imam Muslim", + "opinion_text": "His narrations are authentic and widely accepted in the Islamic jurisprudence.", + "status": "confirmed" + }, + { + "transmitter_name": "Aisha bint Abu Bakr", + "scholar_name": "Imam al-Bukhari", + "opinion_text": "The Mother of the Believers is the most reliable source for hadith about the Prophet's household.", + "status": "confirmed" + }, + { + "transmitter_name": "Jabir ibn Abdullah al-Ansari", + "scholar_name": "Ibn Hajar al-Asqalani", + "opinion_text": "His longevity allowed him to transmit from many companions and successors.", + "status": "confirmed" + }, + { + "transmitter_name": "Imam Malik ibn Anas", + "scholar_name": "Imam ash-Shafi'i", + "opinion_text": "Malik is among the most knowledgeable of the Medinese scholars in jurisprudence.", + "status": "confirmed" + }, + { + "transmitter_name": "Imam Malik ibn Anas", + "scholar_name": "Imam Ahmad ibn Hanbal", + "opinion_text": "Malik's narrations form the basis of sound Islamic jurisprudence.", + "status": "confirmed" + }, + { + "transmitter_name": "Al-Qasim ibn Muhammad", + "scholar_name": "Ibn Hajar al-Asqalani", + "opinion_text": "Al-Qasim is a trustworthy narrator from the Tabi'un generation with reliable traditions.", + "status": "confirmed" + }, + { + "transmitter_name": "Urwa ibn al-Zubayr", + "scholar_name": "Imam al-Bukhari", + "opinion_text": "Urwa is among the most knowledgeable about the traditions of the Prophet's household.", + "status": "confirmed" + }, + { + "transmitter_name": "Abdullah ibn Abbas", + "scholar_name": "Imam at-Tirmidhi", + "opinion_text": "Ibn Abbas has exceptional knowledge in Quranic interpretation and hadith narration.", + "status": "confirmed" + }, + { + "transmitter_name": "Nafi' (Mawla of Ibn Umar)", + "scholar_name": "Imam Muslim", + "opinion_text": "Nafi' is one of the most reliable freed slaves who transmitted authentic traditions.", + "status": "confirmed" + }, + { + "transmitter_name": "Imam Ahmad ibn Hanbal", + "scholar_name": "Ibn Hajar al-Asqalani", + "opinion_text": "Ahmad ibn Hanbal's knowledge of hadith is unparalleled in his era.", + "status": "confirmed" + }, + { + "transmitter_name": "Sufyan ibn Uyayna", + "scholar_name": "Imam al-Bukhari", + "opinion_text": "Sufyan is a highly reliable hadith scholar with exceptional memory.", + "status": "confirmed" + }, + ] + + for opinion_data in opinions_data: + transmitter_name = opinion_data.pop('transmitter_name') + transmitter = transmitters.get(transmitter_name) + if transmitter: + opinion, created = TransmitterOpinion.objects.get_or_create( + transmitter=transmitter, + scholar_name=opinion_data['scholar_name'], + defaults=opinion_data + ) + if created: + self.stdout.write( + self.style.SUCCESS( + f"✓ Opinion added: {opinion_data['scholar_name']} on {transmitter_name}" + ) + ) + + # Step 4: Create Original Texts + self.stdout.write(self.style.SUCCESS('\n📜 Creating Transmitter Original Texts...')) + original_texts_data = [ + { + "transmitter_name": "Abu Hurayrah", + "title": "His Narration on Zakat", + "text": "حدثنا أبو هريرة قال: قال رسول الله صلى الله عليه وسلم: من آمن بالله واليوم الآخر فليؤد الزكاة", + "translation": [ + {"language_code": "en", "text": "Abu Hurayrah narrated: The Messenger of Allah (PBUH) said: Whoever believes in Allah and the Last Day, let him pay the Zakat (alms)."}, + {"language_code": "ar", "text": "أبو هريرة: من آمن بالله واليوم الآخر فليؤد الزكاة"} + ], + "share_link": "https://hadith.example.com/abu-hurayrah/zakat-1" + }, + { + "transmitter_name": "Aisha bint Abu Bakr", + "title": "Her Account of the Prophet's Night Prayer", + "text": "قالت عائشة: كان النبي صلى الله عليه وسلم يقوم الليل فيصلي ثلاث عشرة ركعة", + "translation": [ + {"language_code": "en", "text": "Aisha said: The Prophet (PBUH) used to pray at night thirteen rak'ahs."}, + {"language_code": "ar", "text": "عائشة: كان النبي يقوم الليل بثلاث عشرة ركعة"} + ], + "share_link": "https://hadith.example.com/aisha/night-prayer-1" + }, + { + "transmitter_name": "Jabir ibn Abdullah al-Ansari", + "title": "The Farewell Hajj Narration", + "text": "قال جابر: خرجنا مع رسول الله صلى الله عليه وسلم في حجة الوداع", + "translation": [ + {"language_code": "en", "text": "Jabir narrated: We went out with the Messenger of Allah (PBUH) for the Farewell Hajj."}, + {"language_code": "ar", "text": "جابر: خرجنا مع رسول الله في حجة الوداع"} + ], + "share_link": "https://hadith.example.com/jabir/farewell-hajj-1" + }, + { + "transmitter_name": "Imam Malik ibn Anas", + "title": "Narration on Purity and Prayer", + "text": "قال مالك: الطهارة شرط من شروط الصلاة", + "translation": [ + {"language_code": "en", "text": "Malik said: Purification is a condition for the validity of prayer."}, + {"language_code": "ar", "text": "مالك: الطهارة من شروط صحة الصلاة"} + ], + "share_link": "https://hadith.example.com/malik/purity-1" + }, + { + "transmitter_name": "Abdullah ibn Abbas", + "title": "His Commentary on Divine Justice", + "text": "قال ابن عباس: إن الله تعالى عدل لا يظلم أحدا", + "translation": [ + {"language_code": "en", "text": "Ibn Abbas said: Truly Allah is Just and does not oppress anyone."}, + {"language_code": "ar", "text": "ابن عباس: الله عدل لا يظلم أحدا"} + ], + "share_link": "https://hadith.example.com/ibn-abbas/justice-1" + }, + { + "transmitter_name": "Sufyan ibn Uyayna", + "title": "Teaching on Knowledge Seeking", + "text": "قال سفيان بن عيينة: طلب العلم فريضة على كل مسلم", + "translation": [ + {"language_code": "en", "text": "Sufyan ibn Uyayna said: Seeking knowledge is an obligation for every Muslim."}, + {"language_code": "ar", "text": "سفيان: طلب العلم فريضة على كل مسلم"} + ], + "share_link": "https://hadith.example.com/sufyan/knowledge-1" + }, + ] + + for text_data in original_texts_data: + transmitter_name = text_data.pop('transmitter_name') + transmitter = transmitters.get(transmitter_name) + if transmitter: + original_text, created = TransmitterOriginalText.objects.get_or_create( + transmitter=transmitter, + title=text_data['title'], + defaults=text_data + ) + if created: + self.stdout.write( + self.style.SUCCESS( + f"✓ Original text added: {text_data['title']} by {transmitter_name}" + ) + ) + + # Step 5: Create Hadis Transmitters (Chain of narration) + self.stdout.write(self.style.SUCCESS('\n⛓️ Creating Hadis Transmitters (Chains)...')) + + # Sample hadis IDs to use (you can have any ID from 1800 to 1852) + hadis_ids = list(range(1800, 1853)) + + # Create chains for different hadis + chains_data = [ + { + "hadis_id": hadis_ids[0], + "chain": [ + {"transmitter_name": "Abu Hurayrah", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Imam Malik ibn Anas", "order": 2, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[1], + "chain": [ + {"transmitter_name": "Aisha bint Abu Bakr", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Urwa ibn al-Zubayr", "order": 2, "layer": 2, "status": "reliable"}, + {"transmitter_name": "Imam Malik ibn Anas", "order": 3, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[2], + "chain": [ + {"transmitter_name": "Jabir ibn Abdullah al-Ansari", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Al-Qasim ibn Muhammad", "order": 2, "layer": 2, "status": "reliable"}, + {"transmitter_name": "Sufyan ibn Uyayna", "order": 3, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[3], + "chain": [ + {"transmitter_name": "Abdullah ibn Abbas", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Nafi' (Mawla of Ibn Umar)", "order": 2, "layer": 2, "status": "reliable"}, + {"transmitter_name": "Imam Ahmad ibn Hanbal", "order": 3, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[4], + "chain": [ + {"transmitter_name": "Abu Hurayrah", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Sufyan ibn Uyayna", "order": 2, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[5], + "chain": [ + {"transmitter_name": "Aisha bint Abu Bakr", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Al-Qasim ibn Muhammad", "order": 2, "layer": 2, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[6], + "chain": [ + {"transmitter_name": "Jabir ibn Abdullah al-Ansari", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Nafi' (Mawla of Ibn Umar)", "order": 2, "layer": 2, "status": "reliable"}, + {"transmitter_name": "Imam Malik ibn Anas", "order": 3, "layer": 3, "status": "reliable"}, + {"transmitter_name": "Sufyan ibn Uyayna", "order": 4, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[7], + "chain": [ + {"transmitter_name": "Abdullah ibn Abbas", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Urwa ibn al-Zubayr", "order": 2, "layer": 2, "status": "reliable"}, + {"transmitter_name": "Imam Ahmad ibn Hanbal", "order": 3, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[8], + "chain": [ + {"transmitter_name": "Abu Hurayrah", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Al-Qasim ibn Muhammad", "order": 2, "layer": 2, "status": "reliable"}, + {"transmitter_name": "Imam Malik ibn Anas", "order": 3, "layer": 3, "status": "reliable"}, + {"transmitter_name": "Sufyan ibn Uyayna", "order": 4, "layer": 3, "status": "reliable"}, + ] + }, + { + "hadis_id": hadis_ids[9], + "chain": [ + {"transmitter_name": "Aisha bint Abu Bakr", "order": 1, "layer": 1, "status": "reliable"}, + {"transmitter_name": "Nafi' (Mawla of Ibn Umar)", "order": 2, "layer": 2, "status": "reliable"}, + ] + }, + ] + + hadis_transmitter_count = 0 + for chain_data in chains_data: + hadis_id = chain_data['hadis_id'] + chain = chain_data['chain'] + + for item in chain: + transmitter = transmitters.get(item['transmitter_name']) + layer = layers.get(item['layer']) + + if transmitter and layer: + hadis_trans, created = HadisTransmitter.objects.get_or_create( + hadis_id=hadis_id, + transmitter=transmitter, + order=item['order'], + defaults={ + 'narrator_layer': layer, + 'status': item['status'], + 'is_gap': False + } + ) + if created: + hadis_transmitter_count += 1 + self.stdout.write( + self.style.SUCCESS( + f"✓ Chain link: Hadis {hadis_id} <- {item['transmitter_name']} (Order {item['order']})" + ) + ) + + self.stdout.write( + self.style.SUCCESS( + f'\n✓ Successfully seeded transmitter data!' + ) + ) + self._print_summary(transmitters, hadis_transmitter_count) + + def _print_summary(self, transmitters, hadis_transmitter_count): + """Print a summary of created data""" + self.stdout.write("\n" + "="*70) + self.stdout.write(self.style.SUCCESS("TRANSMITTER DATABASE SUMMARY")) + self.stdout.write("="*70) + + layers_count = NarratorLayer.objects.count() + transmitters_count = Transmitters.objects.count() + opinions_count = TransmitterOpinion.objects.count() + original_texts_count = TransmitterOriginalText.objects.count() + + self.stdout.write(f"📚 Narrator Layers: {layers_count}") + self.stdout.write(f"👥 Total Transmitters: {transmitters_count}") + self.stdout.write(f"💬 Transmitter Opinions: {opinions_count}") + self.stdout.write(f"📜 Original Texts: {original_texts_count}") + self.stdout.write(f"⛓️ Hadis Transmitter Chains: {hadis_transmitter_count}") + + self.stdout.write("\n" + "="*70) + self.stdout.write(self.style.WARNING("Notable Transmitters Created:")) + self.stdout.write("="*70) + for name in list(transmitters.keys())[:5]: + self.stdout.write(f" • {name}") + self.stdout.write(f" ... and {max(0, len(transmitters) - 5)} more") + self.stdout.write("="*70 + "\n") \ No newline at end of file diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index c1d7ee6..d32969d 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -47,15 +47,16 @@ class HadisSyncSerializer(serializers.ModelSerializer): def get_translations(self, obj): """Get all translations as {lang: text}""" - translations_dict = {} - if obj.translation and isinstance(obj.translation, list): - for tr in obj.translation: - if isinstance(tr, dict): - lang_code = tr.get('language_code') - title = tr.get('title') - if lang_code: - translations_dict[lang_code] = title - return translations_dict + # translations_dict = {} + # if obj.translation and isinstance(obj.translation, list): + # for tr in obj.translation: + # if isinstance(tr, dict): + # lang_code = tr.get('language_code') + # title = tr.get('title') + # if lang_code: + # translations_dict[lang_code] = title + # return translations_dict + return obj.translation def get_detail(self, obj): """Detail group: address, status, share, links, tags, references, reference_images""" @@ -170,7 +171,7 @@ class HadisListSerializer(serializers.ModelSerializer): """Get translation based on request language""" request = self.context.get('request') language_code = getattr(request, 'LANGUAGE_CODE', 'en') - return obj.get_translation(language_code) + return obj.translation.get(language_code) class HadisStatusSerializer(serializers.ModelSerializer): @@ -213,7 +214,7 @@ class TransmitterOpinionSerializer(serializers.ModelSerializer): class Meta: model = TransmitterOpinion - fields = '__all__' + fields = ['id','transmitter','scholar_name','opinion_text','status'] class TransmitterOriginalTextSerializer(serializers.ModelSerializer): """ Serializer for TransmitterOriginalText """ @@ -418,7 +419,7 @@ class HadisCorrectionSerializer(serializers.ModelSerializer): """Get translation based on request language""" request = self.context.get('request') language_code = getattr(request, 'LANGUAGE_CODE', 'en') - return obj.translation.get(language_code, '') + return obj.translation.get(language_code) class HadisBasicSerializer(serializers.ModelSerializer): @@ -437,7 +438,7 @@ class HadisBasicSerializer(serializers.ModelSerializer): """Get translation based on request language""" request = self.context.get('request') language_code = getattr(request, 'LANGUAGE_CODE', 'en') - return obj.get_translation(language_code) + return obj.translation.get(language_code) def get_category(self, obj): """Get category id and title""" if obj.category: @@ -492,4 +493,4 @@ class HadisDetailSerializer(serializers.ModelSerializer): """Get translation based on request language""" request = self.context.get('request') language_code = getattr(request, 'LANGUAGE_CODE', 'en') - return obj.get_translation(language_code) + return obj.translation.get(language_code) diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index a123c9a..00c8dbf 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -36,8 +36,8 @@ class HadisSyncView(ListAPIView): ).prefetch_related( 'tags', 'transmitters__transmitter', - 'references__book_reference__images', - 'references__book_reference__authors', + 'references__images', + 'references__book_reference', 'hadiscorrection_set' ).order_by('id') diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/seed_books.py b/scripts/seed_books.py new file mode 100644 index 0000000..a321a3c --- /dev/null +++ b/scripts/seed_books.py @@ -0,0 +1,276 @@ +""" +Django management command to seed mock data for hadith book references. +Place this file in: yourapp/management/commands/seed_books.py + +Usage: python manage.py seed_books +""" + +import os +from pathlib import Path +from django.core.management.base import BaseCommand +from django.core.files.base import ContentFile +from django.utils.text import slugify +from apps.hadis.models.reference import ( + BookReference, + BookReferenceImage, + BookAuthor, + BookAttribute +) + + +class Command(BaseCommand): + help = 'Seed the database with mock hadith book reference data' + + def add_arguments(self, parser): + parser.add_argument( + '--clear', + action='store_true', + help='Clear existing data before seeding', + ) + + def handle(self, *args, **options): + if options['clear']: + self.stdout.write(self.style.WARNING('Clearing existing data...')) + BookAttribute.objects.all().delete() + BookReferenceImage.objects.all().delete() + BookAuthor.objects.all().delete() + BookReference.objects.all().delete() + self.stdout.write(self.style.SUCCESS('Data cleared successfully!')) + + # Create authors first + authors_data = [ + {"name": "Imam Muhammad al-Bukhari"}, + {"name": "Imam Muslim ibn al-Hajjaj"}, + {"name": "Imam Abu Dawood as-Sijistani"}, + {"name": "Imam At-Tirmidhi"}, + {"name": "Imam Ibn Majah"}, + {"name": "Imam Ahmad ibn Hanbal"}, + {"name": "Imam Al-Hakim"}, + {"name": "Imam Ad-Daraqutni"}, + ] + + authors = {} + for author_data in authors_data: + author, created = BookAuthor.objects.get_or_create( + name=author_data['name'] + ) + authors[author_data['name']] = author + if created: + self.stdout.write(self.style.SUCCESS(f'Created author: {author.name}')) + + # Create book references + books_data = [ + { + "title": "Sahih al-Bukhari", + "description": "The most authentic collection of hadith, compiled by Imam Muhammad al-Bukhari. Contains 7,563 ahadith.", + "language": "Arabic", + "isbn": "978-1-86043-009-6", + "volume": "9 volumes", + "year_of_publication": "1870", + "number_page": 1200, + "publisher": "Dar al-Kutub al-Ilmiyah", + "rate": 5.00, + "authors": ["Imam Muhammad al-Bukhari"], + "image_order": 1, + "attributes": { + "Collection Type": "Hadith Compilation", + "Number of Hadith": "7,563", + "Classification": "6 Books", + "Authenticity Grade": "Sahih (Authentic)", + "Compilation Period": "16 years", + } + }, + { + "title": "Sahih Muslim", + "description": "Second most authentic hadith collection compiled by Imam Muslim ibn al-Hajjaj. Contains 9,200 traditions.", + "language": "Arabic", + "isbn": "978-1-86043-010-2", + "volume": "5 volumes", + "year_of_publication": "1875", + "number_page": 1500, + "publisher": "Dar Ihya at-Turath al-Arabi", + "rate": 4.95, + "authors": ["Imam Muslim ibn al-Hajjaj"], + "image_order": 2, + "attributes": { + "Collection Type": "Hadith Compilation", + "Number of Hadith": "9,200", + "Classification": "43 Books", + "Authenticity Grade": "Sahih (Authentic)", + "Unique Hadith": "Approximately 4,000", + } + }, + { + "title": "Sunan Abu Dawood", + "description": "A comprehensive collection of hadith containing jurisprudential material, compiled by Imam Abu Dawood as-Sijistani.", + "language": "Arabic", + "isbn": "978-1-86043-011-9", + "volume": "4 volumes", + "year_of_publication": "1880", + "number_page": 1400, + "publisher": "Islamic Digital Library", + "rate": 4.80, + "authors": ["Imam Abu Dawood as-Sijistani"], + "image_order": 3, + "attributes": { + "Collection Type": "Sunan (Practice)", + "Number of Hadith": "5,274", + "Focus": "Jurisprudential Traditions", + "Number of Books": "43", + "Authenticity Grade": "Hasan to Sahih", + } + }, + { + "title": "Jami' at-Tirmidhi", + "description": "A major collection of hadith compiled by Imam At-Tirmidhi with his commentary and grading of narrations.", + "language": "Arabic", + "isbn": "978-1-86043-012-6", + "volume": "5 volumes", + "year_of_publication": "1892", + "number_page": 1350, + "publisher": "Dar ar-Risalah al-Alamiyah", + "rate": 4.85, + "authors": ["Imam At-Tirmidhi"], + "image_order": 4, + "attributes": { + "Collection Type": "Jami (Comprehensive)", + "Number of Hadith": "3,956", + "Notable Feature": "Grades each hadith", + "Categories": "63 Chapters", + "Authenticity Grade": "Various Grades", + } + }, + { + "title": "Sunan Ibn Majah", + "description": "A collection of hadith compiled by Imam Ibn Majah, one of the Six Canonical Hadith Collections.", + "language": "Arabic", + "isbn": "978-1-86043-013-3", + "volume": "2 volumes", + "year_of_publication": "1888", + "number_page": 900, + "publisher": "Dar Ihya al-Kutub al-Arabiyah", + "rate": 4.75, + "authors": ["Imam Ibn Majah"], + "image_order": 5, + "attributes": { + "Collection Type": "Sunan (Practice)", + "Number of Hadith": "4,341", + "Number of Books": "32", + "Notable Content": "Includes rare narrations", + "Authenticity Grade": "Mixed - requires verification", + } + }, + ] + + books = {} + for book_data in books_data: + # Extract author names + author_names = book_data.pop('authors', []) + image_order = book_data.pop('image_order', 1) + attributes_dict = book_data.pop('attributes', {}) + + # Create or get the book + book, created = BookReference.objects.get_or_create( + title=book_data['title'], + defaults=book_data + ) + + if created: + self.stdout.write(self.style.SUCCESS(f'Created book: {book.title}')) + else: + # Update existing book + for key, value in book_data.items(): + setattr(book, key, value) + book.save() + self.stdout.write(self.style.WARNING(f'Updated book: {book.title}')) + + books[book.title] = book + + # Add authors to book + for author_name in author_names: + author = authors.get(author_name) + if author: + book.authors.add(author) + + # Add book image + image_path = self._get_image_path(image_order) + if image_path and os.path.exists(image_path): + # Check if image already exists for this book + if not book.images.exists(): + with open(image_path, 'rb') as img_file: + image_name = f'book{image_order}.png' + book_image = BookReferenceImage.objects.create( + book_reference=book, + order=1, + description=f"Cover image for {book.title}" + ) + book_image.image.save( + image_name, + ContentFile(img_file.read()), + save=True + ) + self.stdout.write( + self.style.SUCCESS(f'Added image to: {book.title}') + ) + else: + self.stdout.write( + self.style.WARNING( + f'Image not found at {image_path} for {book.title}' + ) + ) + + # Add attributes + for attr_title, attr_value in attributes_dict.items(): + attribute, created = BookAttribute.objects.get_or_create( + book_reference=book, + title=attr_title, + defaults={'value': attr_value} + ) + if created: + self.stdout.write( + self.style.SUCCESS( + f'Added attribute: {attr_title} to {book.title}' + ) + ) + + self.stdout.write( + self.style.SUCCESS( + f'\n✓ Successfully seeded {len(books)} books with all relations!' + ) + ) + self._print_summary() + + def _get_image_path(self, book_number): + """ + Find the image file for the given book number. + Checks multiple possible locations. + """ + possible_paths = [ + Path('seeds/images') / f'book{book_number}.png', + Path('seed_data/images') / f'book{book_number}.png', + Path('static/images') / f'book{book_number}.png', + Path('.') / 'seeds' / 'images' / f'book{book_number}.png', + ] + + for path in possible_paths: + if path.exists(): + return path + + return None + + def _print_summary(self): + """Print a summary of created data""" + self.stdout.write("\n" + "="*60) + self.stdout.write(self.style.SUCCESS("DATABASE SUMMARY")) + self.stdout.write("="*60) + + books_count = BookReference.objects.count() + authors_count = BookAuthor.objects.count() + images_count = BookReferenceImage.objects.count() + attributes_count = BookAttribute.objects.count() + + self.stdout.write(f"📚 Total Books: {books_count}") + self.stdout.write(f"✍️ Total Authors: {authors_count}") + self.stdout.write(f"🖼️ Total Images: {images_count}") + self.stdout.write(f"🏷️ Total Attributes: {attributes_count}") + self.stdout.write("="*60 + "\n") \ No newline at end of file diff --git a/seeds/images/image 1208.png b/seeds/images/image 1208.png new file mode 100644 index 0000000..49d329d Binary files /dev/null and b/seeds/images/image 1208.png differ diff --git a/seeds/images/ref1.png b/seeds/images/ref1.png new file mode 100644 index 0000000..7e3e4e7 Binary files /dev/null and b/seeds/images/ref1.png differ diff --git a/seeds/images/ref2.png b/seeds/images/ref2.png new file mode 100644 index 0000000..49d329d Binary files /dev/null and b/seeds/images/ref2.png differ diff --git a/seeds/images/ref3.png b/seeds/images/ref3.png new file mode 100644 index 0000000..0a7a4b5 Binary files /dev/null and b/seeds/images/ref3.png differ diff --git a/seeds/images/ref4.png b/seeds/images/ref4.png new file mode 100644 index 0000000..49d329d Binary files /dev/null and b/seeds/images/ref4.png differ