Browse Source

category caches removed

master
Mohsen Taba 4 months ago
parent
commit
25d27b0007
  1. 215
      apps/hadis/management/commands/seed_category_data.py
  2. 10
      apps/hadis/urls.py

215
apps/hadis/management/commands/seed_category_data.py

@ -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 ---'))

10
apps/hadis/urls.py

@ -25,11 +25,11 @@ urlpatterns = [
path('info/', cached_view(HadisInfoView.as_view()), name='hadis-info'),
# Category paths (more specific first)
path('categories/tree/', cached_view(HadisCategoryTreeNormalView.as_view()), name='hadis-category-tree-normal'),
path('categories/<str:sect_type>/<str:slug>/<str:source_type>/', cached_view(HadisCategorySelectBySectSourceView.as_view()), name='categories-tree-by-sect-source'),
path('categories/<str:sect_type>/<str:slug>/', cached_view(HadisCategorySelectBySectView.as_view()), name='categories-tree-by-sect'),
path('categories/<str:sect_type>/', cached_view(CategoriesBySectView.as_view()), name='categories-by-sect'),
path('categories/', cached_view(CategoriesView.as_view()), name='categories'), # ← Least specific LAST
path('categories/tree/', HadisCategoryTreeNormalView.as_view(), name='hadis-category-tree-normal'),
path('categories/<str:sect_type>/<str:slug>/<str:source_type>/', HadisCategorySelectBySectSourceView.as_view(), name='categories-tree-by-sect-source'),
path('categories/<str:sect_type>/<str:slug>/', HadisCategorySelectBySectView.as_view(), name='categories-tree-by-sect'),
path('categories/<str:sect_type>/', CategoriesBySectView.as_view(), name='categories-by-sect'),
path('categories/', CategoriesView.as_view(), name='categories'), # ← Least specific LAST
# Hadis paths
path('category/<str:category_slug>/', HadisListView.as_view(), name='hadis-list'),

Loading…
Cancel
Save