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.
 
 

252 lines
10 KiB

#!/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)