You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

95 lines
4.2 KiB

import os
import sys
import argparse
import django
sys.stdout.reconfigure(encoding='utf-8')
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.develop')
django.setup()
from django.db import transaction
from django.db.models import Count
from apps.hadis.models import HadisCategory, Hadis
def get_text(val):
if isinstance(val, list):
for item in val:
if isinstance(item, dict):
return item.get('text') or item.get('title') or str(item)
elif isinstance(val, dict):
return val.get('text') or val.get('title') or str(val)
return str(val or "")
def resolve_category_conflicts(dry_run=True):
print("=" * 65)
print(" CATEGORY CONFLICT RESOLVER (HADITHS vs SUBCATEGORIES) ")
print(f" Mode: {'DRY RUN (Simulation only, no DB changes)' if dry_run else 'EXECUTE (Applying changes to DB)'}")
print("=" * 65)
conflicted_categories = HadisCategory.objects.annotate(
hadis_count=Count('hadis', distinct=True),
children_count=Count('children', distinct=True)
).filter(hadis_count__gt=0, children_count__gt=0)
total_conflicts = conflicted_categories.count()
print(f"\nFound {total_conflicts} categories violating the rule (having both Hadiths & Subcategories).\n")
if total_conflicts == 0:
print("✅ No conflicts found. All categories adhere to the business rule.")
return
deleted_subcats_count = 0
reassigned_hadis_count = 0
with transaction.atomic():
for idx, parent_cat in enumerate(conflicted_categories, 1):
parent_title = get_text(parent_cat.title)
print(f"[{idx}/{total_conflicts}] Category #{parent_cat.id} ('{parent_title}') | Slug: {parent_cat.slug}")
print(f" - Direct Hadiths: {parent_cat.hadis_count}")
print(f" - Subcategories count: {parent_cat.children_count}")
# Get all descendants
descendants = list(parent_cat.get_descendants().order_by('-level'))
print(f" - Total subcategories/descendants to remove: {len(descendants)}")
for subcat in descendants:
subcat_title = get_text(subcat.title)
sub_hadis = subcat.hadis_set.all()
sub_hadis_count = sub_hadis.count()
if sub_hadis_count > 0:
print(f" ⚠️ Subcategory #{subcat.id} ('{subcat_title}') has {sub_hadis_count} Hadiths!")
print(f" -> Reassigning these {sub_hadis_count} Hadiths to parent Category #{parent_cat.id} before deletion.")
if not dry_run:
sub_hadis.update(category=parent_cat)
reassigned_hadis_count += sub_hadis_count
print(f" 🗑️ Removing Subcategory #{subcat.id} ('{subcat_title}')")
if not dry_run:
subcat.delete()
deleted_subcats_count += 1
if dry_run:
print("\n[DRY RUN SUMMARY]")
print(f" - Categories inspected: {total_conflicts}")
print(f" - Subcategories that would be deleted: {deleted_subcats_count}")
print(f" - Hadiths that would be safeguarded/reassigned: {reassigned_hadis_count}")
print(" -> No changes were written to the database.")
else:
print("\nRebuilding MPTT category tree structure...")
HadisCategory.objects.rebuild()
print("✅ MPTT category tree successfully rebuilt.")
print("\n[EXECUTION SUMMARY]")
print(f" - Conflicted categories resolved: {total_conflicts}")
print(f" - Subcategories deleted: {deleted_subcats_count}")
print(f" - Hadiths safeguarded: {reassigned_hadis_count}")
print("✅ All changes committed successfully.")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Resolve categories having both Hadiths and Subcategories")
parser.add_argument('--execute', action='store_true', help='Execute changes on database (default is dry-run)')
args = parser.parse_args()
resolve_category_conflicts(dry_run=not args.execute)