2 changed files with 83 additions and 142 deletions
@ -1,154 +1,95 @@ |
|||
import random |
|||
from django.core.management.base import BaseCommand |
|||
from django.db import transaction |
|||
# Adjust the import path if your app name is different |
|||
from apps.hadis.models import Hadis, HadisCategory, HadisSect |
|||
from apps.hadis.models import Hadis, HadisCategory |
|||
|
|||
class Command(BaseCommand): |
|||
help = 'Seeds HadisCategories with rich Islamic data and links existing Hadiths' |
|||
help = 'Part 2: Assigns Hadiths to Leaves and cleans empty categories' |
|||
|
|||
def handle(self, *args, **options): |
|||
self.stdout.write(self.style.WARNING('--- Starting Islamic Category Seeding ---')) |
|||
self.stdout.write(self.style.WARNING('--- Part 2: Starting Hadith Assignment ---')) |
|||
|
|||
# 1. Fetch or Create Sects |
|||
shia_sect, _ = HadisSect.objects.get_or_create( |
|||
sect_type=HadisSect.SectType.SHIA, |
|||
defaults={'title': [{'text': 'Shia (Twelver)', 'language_code': 'en'}], 'is_active': True} |
|||
) |
|||
sunni_sect, _ = HadisSect.objects.get_or_create( |
|||
sect_type=HadisSect.SectType.SUNNI, |
|||
defaults={'title': [{'text': 'Sunni (Ahlus Sunnah)', 'language_code': 'en'}], 'is_active': True} |
|||
) |
|||
# 1. FETCH LEAF CATEGORIES |
|||
# A leaf is a category that has NO children |
|||
# We assume Script 1 has run, so we just query the DB. |
|||
# Note: Related name for children defaults to 'children' usually, check your model if different. |
|||
# If your model doesn't have a 'children' reverse relation explicitly, |
|||
# Django usually provides 'hadiscategory_set' or similar. |
|||
# Standard approach for adjacency list parent: |
|||
|
|||
leaf_categories = [] |
|||
all_cats = HadisCategory.objects.all() |
|||
|
|||
self.stdout.write("identifying leaf categories...") |
|||
for cat in all_cats: |
|||
if not HadisCategory.objects.filter(parent=cat).exists(): |
|||
leaf_categories.append(cat) |
|||
|
|||
if not leaf_categories: |
|||
self.stdout.write(self.style.ERROR("No leaf categories found! Did you run Part 1?")) |
|||
return |
|||
|
|||
# 2. Rich Islamic Data Dictionary |
|||
ISLAMIC_DATA = { |
|||
HadisCategory.SourceType.QURAN: { |
|||
HadisSect.SectType.SHIA: [ |
|||
("Quranic Sciences", "Thematic Exegesis", ["Verses of Wilayah", "Ahlulbayt in Quran", "Moral Teachings in Surah Yusuf"]), |
|||
("Tafsir Studies", "Interpretation Principles", ["Tafsir al-Mizan Topics", "Esoteric Meanings (Ta'wil)"]) |
|||
], |
|||
HadisSect.SectType.SUNNI: [ |
|||
("Quranic Studies", "Tafsir Methodology", ["Asbab al-Nuzul", "Stories of the Prophets", "Legislative Verses (Ahkam)"]), |
|||
("Recitation (Tajweed)", "Qira'at", ["Hafs an Asim", "Warsh an Nafi", "Rules of Nun Sakinah"]) |
|||
] |
|||
}, |
|||
HadisCategory.SourceType.HADITH: { |
|||
HadisSect.SectType.SHIA: [ |
|||
("The Four Books", "Usul al-Kafi", ["Book of Intellect", "Book of Divine Proof", "Book of Belief"]), |
|||
("Nahj al-Balagha", "Sermons of Imam Ali", ["The Shiqshiqiyyah Sermon", "Letter to Malik al-Ashtar", "Aphorisms of Wisdom"]) |
|||
], |
|||
HadisSect.SectType.SUNNI: [ |
|||
("The Six Books", "Sahih al-Bukhari", ["Book of Revelation", "Book of Belief", "Book of Knowledge"]), |
|||
("Sunan Collections", "Sunan Abu Dawood", ["Book of Prayer", "Book of Zakat", "Book of Jihad"]) |
|||
] |
|||
}, |
|||
HadisCategory.SourceType.HISTORY: { |
|||
HadisSect.SectType.SHIA: [ |
|||
("Life of the Infallibles", "The Tragedy of Karbala", ["Day of Ashura", "The Sermon of Zaynab (SA)", "Journey of Captives"]), |
|||
("Occultation (Ghaybah)", "Major Occultation", ["Signs of Reappearance", "Duties of Believers", "Deputies of the Imam"]) |
|||
], |
|||
HadisSect.SectType.SUNNI: [ |
|||
("Seerah of the Prophet", "Meccan Period", ["The First Revelation", "Migration to Abyssinia", "The Year of Sorrow"]), |
|||
("The Rashidun Caliphate", "Conquests and Expansion", ["Caliphate of Abu Bakr", "Caliphate of Umar", "Battles of Yarmouk"]) |
|||
] |
|||
}, |
|||
HadisCategory.SourceType.FATWA: { |
|||
HadisSect.SectType.SHIA: [ |
|||
("Jurisprudence (Fiqh)", "Acts of Worship", ["Rules of Taqlid", "Khums and Zakat", "Rules of Salatul Layl"]), |
|||
("Modern Legal Issues", "Medical Jurisprudence", ["Organ Donation", "Assisted Reproduction", "Gender Reassignment"]) |
|||
], |
|||
HadisSect.SectType.SUNNI: [ |
|||
("Fiqh Schools", "Shafi'i School", ["Rules of Prayer", "Inheritance Laws", "Marriage Contracts"]), |
|||
("Contemporary Fatawa", "Islamic Finance", ["Prohibition of Riba", "Islamic Banking Principles", "Crypto-currency Rulings"]) |
|||
] |
|||
}, |
|||
HadisCategory.SourceType.QUOTE: { |
|||
HadisSect.SectType.SHIA: [ |
|||
("Wisdom of the Imams", "Supplications (Du'a)", ["Du'a Kumayl Themes", "Whispered Prayers (Munajat)", "Du'a Arafah"]), |
|||
("Mysticism (Irfan)", "Spiritual Wayfaring", ["Combat with the Self", "Degrees of Piety", "Love for the Divine"]) |
|||
], |
|||
HadisSect.SectType.SUNNI: [ |
|||
("Sayings of the Companions", "Wisdom of Abu Bakr & Umar", ["Justice in Governance", "Fear of Allah", "Humility"]), |
|||
("Sufi Wisdom", "Spiritual Purification", ["Tazkiyat al-Nafs", "Remembrance of Death", "Reliance on Allah (Tawakkul)"]) |
|||
] |
|||
} |
|||
} |
|||
self.stdout.write(f"Found {len(leaf_categories)} leaf categories.") |
|||
|
|||
created_leaves = [] |
|||
# 2. FETCH HADITHS |
|||
all_hadiths = list(Hadis.objects.all()) |
|||
total_hadiths = len(all_hadiths) |
|||
|
|||
if total_hadiths < 3: |
|||
self.stdout.write(self.style.ERROR('Not enough Hadiths in DB (need at least 3).')) |
|||
return |
|||
|
|||
with transaction.atomic(): |
|||
self.stdout.write('Building Rich Islamic Category Tree...') |
|||
|
|||
for source_code, sect_data in ISLAMIC_DATA.items(): |
|||
for sect_type_enum, categories_list in sect_data.items(): |
|||
|
|||
current_sect = shia_sect if sect_type_enum == HadisSect.SectType.SHIA else sunni_sect |
|||
|
|||
for root_title, sub_title, leaf_titles in categories_list: |
|||
|
|||
# --- Level 1: Root --- |
|||
# ADDED: description to defaults |
|||
root_cat, _ = HadisCategory.objects.get_or_create( |
|||
sect=current_sect, |
|||
source_type=source_code, |
|||
title=[{'text': root_title, 'language_code': 'en'}], |
|||
defaults={ |
|||
'parent': None, |
|||
'description': [{'text': f'Root category for {root_title}', 'language_code': 'en'}] |
|||
} |
|||
) |
|||
|
|||
# --- Level 2: Sub --- |
|||
# ADDED: description to defaults |
|||
sub_cat, _ = HadisCategory.objects.get_or_create( |
|||
sect=current_sect, |
|||
source_type=source_code, |
|||
title=[{'text': sub_title, 'language_code': 'en'}], |
|||
defaults={ |
|||
'parent': root_cat, |
|||
'description': [{'text': f'Sub-category for {sub_title}', 'language_code': 'en'}] |
|||
} |
|||
) |
|||
|
|||
# --- Level 3: Leaves --- |
|||
for leaf_title in leaf_titles: |
|||
# ADDED: description to defaults |
|||
leaf_cat, _ = HadisCategory.objects.get_or_create( |
|||
sect=current_sect, |
|||
source_type=source_code, |
|||
title=[{'text': leaf_title, 'language_code': 'en'}], |
|||
defaults={ |
|||
'parent': sub_cat, |
|||
'description': [{'text': f'Leaf category for {leaf_title}', 'language_code': 'en'}] |
|||
} |
|||
) |
|||
created_leaves.append(leaf_cat) |
|||
self.stdout.write(f"Created Leaf: {leaf_title} ({current_sect.get_title('en')})") |
|||
|
|||
self.stdout.write(self.style.SUCCESS(f'Successfully structured {len(created_leaves)} leaf categories.')) |
|||
# 3. DISTRIBUTE |
|||
self.stdout.write(f'Distributing {total_hadiths} Hadiths among leaves (Target: 3 per leaf)...') |
|||
|
|||
random.shuffle(all_hadiths) |
|||
hadith_index = 0 |
|||
assigned_count = 0 |
|||
|
|||
# Loop through leaves and assign |
|||
for leaf in leaf_categories: |
|||
# Cycle logic: restart index if we run out of unique hadiths |
|||
if hadith_index + 3 > total_hadiths: |
|||
hadith_index = 0 |
|||
|
|||
batch = all_hadiths[hadith_index : hadith_index + 3] |
|||
hadith_index += 3 |
|||
|
|||
# 3. Connect Hadiths to Leaves |
|||
all_hadiths = list(Hadis.objects.all()) |
|||
total_hadiths = len(all_hadiths) |
|||
for hadis_obj in batch: |
|||
hadis_obj.category = leaf |
|||
hadis_obj.save() |
|||
assigned_count += 1 |
|||
|
|||
if total_hadiths < 3: |
|||
self.stdout.write(self.style.ERROR('Not enough Hadiths in DB. Please seed Hadiths first (need at least 3).')) |
|||
return |
|||
# Print a dot every 5 leaves to show progress without spamming |
|||
if leaf_categories.index(leaf) % 5 == 0: |
|||
self.stdout.write(".", ending="") |
|||
|
|||
self.stdout.write(f'Distributing {total_hadiths} Hadiths among leaves...') |
|||
self.stdout.write(f"\nAssigned {assigned_count} hadith links.") |
|||
|
|||
# 4. CLEANUP EMPTY CATEGORIES |
|||
# Constraint: "every category is either have hadiths , or childs" |
|||
self.stdout.write("Running cleanup for empty categories...") |
|||
|
|||
empty_deleted_count = 0 |
|||
|
|||
# Re-fetch all categories to ensure fresh state |
|||
all_cats_check = HadisCategory.objects.all() |
|||
|
|||
for cat in all_cats_check: |
|||
# Check for children |
|||
has_children = HadisCategory.objects.filter(parent=cat).exists() |
|||
|
|||
random.shuffle(all_hadiths) |
|||
hadith_index = 0 |
|||
# Check for hadiths |
|||
# Ensure 'category' matches your field name in Hadis model |
|||
has_hadiths = Hadis.objects.filter(category=cat).exists() |
|||
|
|||
for leaf in created_leaves: |
|||
if hadith_index + 3 > total_hadiths: |
|||
hadith_index = 0 |
|||
if not has_children and not has_hadiths: |
|||
# self.stdout.write(f"Deleting empty: {cat.id}") # Optional debug |
|||
cat.delete() |
|||
empty_deleted_count += 1 |
|||
|
|||
batch = all_hadiths[hadith_index : hadith_index + 3] |
|||
hadith_index += 3 |
|||
|
|||
for hadis_obj in batch: |
|||
hadis_obj.category = leaf |
|||
hadis_obj.save() |
|||
if empty_deleted_count > 0: |
|||
self.stdout.write(self.style.WARNING(f"Cleaned up {empty_deleted_count} empty categories.")) |
|||
else: |
|||
self.stdout.write(self.style.SUCCESS("No empty categories found.")) |
|||
|
|||
self.stdout.write(self.style.SUCCESS('--- Islamic Category Seeding Complete ---')) |
|||
self.stdout.write(self.style.SUCCESS('--- Part 2 Complete ---')) |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue