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