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.
 
 
 
 

106 lines
4.6 KiB

import time
from django.core.management.base import BaseCommand
from django.db import transaction, connection
from django.core.cache import cache
from apps.hadis.models import HadisCategory
class Command(BaseCommand):
help = (
'Diversify topic source_types across parent categories so that '
'sub-topics under each category have distinct, varied source types '
'(hadith, quran, history, fatwa, quote).'
)
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Simulate diversification without modifying the database.'
)
def handle(self, *args, **options):
dry_run = options['dry_run']
if dry_run:
self.stdout.write(self.style.WARNING("=== RUNNING IN DRY-RUN MODE (No changes will be saved) ==="))
else:
self.stdout.write(self.style.NOTICE("=== STARTING TOPIC SOURCE DIVERSIFICATION ==="))
# Ensure database connection with retry
for attempt in range(5):
try:
connection.ensure_connection()
break
except Exception as e:
self.stdout.write(f"Connection attempt {attempt+1} failed: {e}. Retrying in 2s...")
time.sleep(2)
source_choices = [
HadisCategory.SourceType.HADITH,
HadisCategory.SourceType.QURAN,
HadisCategory.SourceType.HISTORY,
HadisCategory.SourceType.FATWA,
HadisCategory.SourceType.QUOTE,
]
num_choices = len(source_choices)
parents = HadisCategory.objects.filter(is_topic=False).select_related('sect').order_by('sect_id', 'tree_id', 'lft', 'id')
parents_with_topics = [p for p in parents if p.children.filter(is_topic=True).exists()]
self.stdout.write(f"Total parent categories with topics: {len(parents_with_topics)}")
updated_topics_count = 0
total_topics_count = 0
global_rotation_idx = 0
with transaction.atomic():
for parent in parents_with_topics:
topics = list(parent.children.filter(is_topic=True).order_by('order', 'id'))
topic_count = len(topics)
total_topics_count += topic_count
sect_name = getattr(parent.sect, 'sect_type', 'unknown')
safe_slug = parent.slug.encode('ascii', 'replace').decode('ascii')
self.stdout.write(
f"\nParent [{sect_name}] ID={parent.id} ({safe_slug}) - {topic_count} topics:"
)
# For this parent's topics, pick distinct source types from the rotation
for i, topic in enumerate(topics):
new_source = source_choices[(global_rotation_idx + i) % num_choices]
old_source = topic.source_type
safe_topic_slug = topic.slug.encode('ascii', 'replace').decode('ascii') if topic.slug else 'no-slug'
if old_source != new_source:
updated_topics_count += 1
self.stdout.write(
f" -> Topic ID={topic.id:4d} | '{safe_topic_slug}' : {old_source} -> {new_source}"
)
if not dry_run:
topic.source_type = new_source
topic.save(update_fields=['source_type'])
else:
self.stdout.write(
f" -> Topic ID={topic.id:4d} | '{safe_topic_slug}' : {new_source} (unchanged)"
)
# Advance global rotation index
global_rotation_idx = (global_rotation_idx + topic_count) % num_choices
if dry_run:
transaction.set_rollback(True)
self.stdout.write("")
self.stdout.write(self.style.SUCCESS("=== DIVERSIFICATION SUMMARY ==="))
self.stdout.write(f" Parent categories processed: {len(parents_with_topics)}")
self.stdout.write(f" Total topics inspected: {total_topics_count}")
self.stdout.write(f" Topics updated: {updated_topics_count}")
if not dry_run:
# Clear cache so new source types are immediately served
cache.clear()
self.stdout.write(self.style.SUCCESS("Django cache cleared successfully!"))
self.stdout.write(self.style.SUCCESS("All topic source types updated successfully!"))
else:
self.stdout.write(self.style.WARNING("Dry run finished. Run without --dry-run to commit changes."))