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