#!/usr/bin/env python3 """ Script to optimize Hadis Transmitter chains: 1. Limit each hadis to maximum 5 transmitter chain links 2. Remove excess transmitters if more than 5 3. Ensure exactly one transmitter has is_gap=True (minimum 1, maximum 1) """ import os import sys import django from pathlib import Path import random # Setup Django environment BASE_DIR = Path(__file__).resolve().parent.parent sys.path.append(str(BASE_DIR)) os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') django.setup() # Import models after Django setup from apps.hadis.models import Hadis, HadisTransmitter from django.db import transaction from django.db.models import Count class HadisTransmitterOptimizer: """Optimizer for Hadis Transmitter chains""" def __init__(self): self.max_transmitters = 5 self.required_gaps = 1 # Exactly one gap required def optimize_all_hadis(self): """Optimize transmitter chains for all hadis records""" print("🔧 شروع بهینه‌سازی زنجیره راویان احادیث...") print("=" * 60) # Get all hadis with transmitters hadis_with_transmitters = Hadis.objects.annotate( transmitter_count=Count('transmitters') ).filter(transmitter_count__gt=0) total_hadis = hadis_with_transmitters.count() print(f"📊 تعداد کل احادیث با راوی: {total_hadis}") print(f"⚙️ حداکثر راوی در هر حدیث: {self.max_transmitters}") print(f"🔗 تعداد گپ مورد نیاز: دقیقاً {self.required_gaps} گپ") print("-" * 60) optimized_count = 0 removed_transmitters = 0 with transaction.atomic(): for i, hadis in enumerate(hadis_with_transmitters, 1): hadis_title = hadis.title[:30] if hadis.title else f"حدیث {hadis.number}" print(f"\n[{i}/{total_hadis}] پردازش حدیث #{hadis.number}: {hadis_title}...") result = self.optimize_hadis_transmitters(hadis) if result['optimized']: optimized_count += 1 removed_transmitters += result['removed_count'] # Progress indicator if i % 25 == 0: print(f"📈 پیشرفت: {i}/{total_hadis} ({(i/total_hadis)*100:.1f}%)") print("\n" + "=" * 60) print("✅ بهینه‌سازی کامل شد!") print(f"📊 آمار:") print(f" - تعداد کل احادیث پردازش شده: {total_hadis}") print(f" - تعداد احادیث بهینه‌سازی شده: {optimized_count}") print(f" - تعداد راویان حذف شده: {removed_transmitters}") print(f" - نرخ موفقیت: {(optimized_count/total_hadis)*100:.1f}%") return { 'total_processed': total_hadis, 'optimized_count': optimized_count, 'removed_transmitters': removed_transmitters } def optimize_hadis_transmitters(self, hadis): """Optimize transmitter chain for a single hadis""" # Get all transmitters for this hadis, ordered by order field transmitters = list(hadis.transmitters.all().order_by('order')) original_count = len(transmitters) print(f" 📋 تعداد راویان اصلی: {original_count}") needs_modification = False # 1. Check if more than 5 transmitters if original_count > self.max_transmitters: needs_modification = True # Keep only first 5 transmitters (ordered by 'order' field) transmitters_to_keep = transmitters[:self.max_transmitters] transmitters_to_delete = transmitters[self.max_transmitters:] # Delete excess transmitters removed_count = len(transmitters_to_delete) for transmitter in transmitters_to_delete: transmitter_name = transmitter.transmitter.full_name if transmitter.transmitter else 'گپ' print(f" �️ حذف راوی: {transmitter_name} (ترتیب: {transmitter.order})") transmitter.delete() transmitters = transmitters_to_keep print(f" ✂️ تعداد راویان از {original_count} به {len(transmitters)} کاهش یافت") else: removed_count = 0 # 2. Ensure exactly one transmitter has is_gap=True gap_transmitters = [t for t in transmitters if t.is_gap] gap_count = len(gap_transmitters) if gap_count == 0: # No gap transmitter, set one randomly if transmitters: random_transmitter = random.choice(transmitters) random_transmitter.is_gap = True random_transmitter.save() needs_modification = True print(f" 🔗 گپ به راوی ترتیب {random_transmitter.order} اضافه شد") elif gap_count > 1: # Multiple gap transmitters, keep only one transmitter_to_keep_gap = gap_transmitters[0] for transmitter in gap_transmitters[1:]: transmitter.is_gap = False transmitter.save() needs_modification = True print(f" � گپ از {gap_count-1} راوی حذف شد، فقط راوی ترتیب {transmitter_to_keep_gap.order} گپ باقی ماند") # Reorder transmitters to ensure proper sequence if needs_modification: self._reorder_transmitters(transmitters) final_gap_count = sum(1 for t in transmitters if t.is_gap) if needs_modification: print(f" ✅ بهینه‌سازی شد: {original_count} -> {len(transmitters)} راوی") print(f" 🔗 تعداد گپ: {final_gap_count}") else: print(f" ✅ قبلاً بهینه بود (گپ: {final_gap_count})") return {'optimized': needs_modification, 'removed_count': removed_count} def _reorder_transmitters(self, transmitters): """Reorder transmitters with proper order values""" for i, transmitter in enumerate(transmitters, 1): transmitter.order = i transmitter.save() def get_statistics(self): """Get current statistics about transmitter chains""" print("\n📊 آمار فعلی زنجیره راویان:") print("-" * 50) # Total hadis with transmitters hadis_with_transmitters = Hadis.objects.annotate( transmitter_count=Count('transmitters') ).filter(transmitter_count__gt=0) total_hadis = hadis_with_transmitters.count() # Transmitter count distribution chain_lengths = {} gap_distributions = {} for hadis in hadis_with_transmitters: transmitter_count = hadis.transmitter_count gap_count = hadis.transmitters.filter(is_gap=True).count() chain_lengths[transmitter_count] = chain_lengths.get(transmitter_count, 0) + 1 gap_distributions[gap_count] = gap_distributions.get(gap_count, 0) + 1 print(f"تعداد کل احادیث با راوی: {total_hadis}") print("\nتوزیع طول زنجیره:") for length in sorted(chain_lengths.keys()): count = chain_lengths[length] percentage = (count / total_hadis) * 100 if total_hadis > 0 else 0 print(f" {length} راوی: {count} حدیث ({percentage:.1f}%)") print("\nتوزیع گپ:") for gaps in sorted(gap_distributions.keys()): count = gap_distributions[gaps] percentage = (count / total_hadis) * 100 if total_hadis > 0 else 0 print(f" {gaps} گپ: {count} حدیث ({percentage:.1f}%)") # Identify problematic hadis problematic = 0 for hadis in hadis_with_transmitters: transmitter_count = hadis.transmitter_count gap_count = hadis.transmitters.filter(is_gap=True).count() if transmitter_count > self.max_transmitters or gap_count != self.required_gaps: problematic += 1 print(f"\nاحادیث مشکل‌دار (نیاز به بهینه‌سازی): {problematic}") if total_hadis > 0: print(f"درصد نیاز به بهینه‌سازی: {(problematic/total_hadis)*100:.1f}%") def main(): """Main function""" import argparse parser = argparse.ArgumentParser(description='بهینه‌سازی زنجیره راویان احادیث') parser.add_argument('--stats-only', action='store_true', help='فقط نمایش آمار، بدون بهینه‌سازی') parser.add_argument('--dry-run', action='store_true', help='نمایش تغییرات بدون اعمال آن‌ها') args = parser.parse_args() optimizer = HadisTransmitterOptimizer() if args.stats_only: optimizer.get_statistics() else: # Show current statistics optimizer.get_statistics() if not args.dry_run: print("\n" + "="*60) confirm = input("🚨 این عملیات زنجیره راویان را تغییر خواهد داد. ادامه می‌دهید؟ (yes/no): ").strip().lower() if confirm == 'yes': optimizer.optimize_all_hadis() print(f"\n🎉 بهینه‌سازی با موفقیت کامل شد!") # Show final statistics print("\n" + "="*60) print("📊 آمار نهایی:") optimizer.get_statistics() else: print("❌ عملیات لغو شد.") else: print("\n🔍 حالت آزمایشی - هیچ تغییری اعمال نخواهد شد") print("برای اجرای واقعی، بدون --dry-run اجرا کنید") if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n❌ عملیات توسط کاربر لغو شد.") sys.exit(1) except Exception as e: print(f"\n💥 خطا: {e}") import traceback traceback.print_exc() sys.exit(1)