From 4ce8f457e1e62122fed5b90ee2fa6346668f0740 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Wed, 21 Jan 2026 12:05:46 +0330 Subject: [PATCH] Hadis transmitters chain fixed fixed seed logic and removed the pagination. --- .../commands/seed_hadis_transmitter.py | 122 +++++++++--------- apps/hadis/views/hadis.py | 2 +- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/apps/hadis/management/commands/seed_hadis_transmitter.py b/apps/hadis/management/commands/seed_hadis_transmitter.py index bc5b12e..6eda0dd 100644 --- a/apps/hadis/management/commands/seed_hadis_transmitter.py +++ b/apps/hadis/management/commands/seed_hadis_transmitter.py @@ -1,93 +1,93 @@ import random -import time from django.core.management.base import BaseCommand from django.db import transaction -from django.db.models import Q -# REPLACE 'your_app' with your actual app name (e.g., apps.hadis.models) +# REPLACE 'your_app' with your actual app name from apps.hadis.models import Hadis, Transmitters, NarratorLayer, HadisTransmitter class Command(BaseCommand): - help = 'Seeds HadisTransmitter instances for ALL Hadis with 5+ transmitters spanning 2-3 layers' + help = 'Seeds HadisTransmitters: Multiple layers per Hadis, each with an independent chain of 5+ transmitters.' def handle(self, *args, **options): - self.stdout.write("Starting Global HadisTransmitter seeding...") + self.stdout.write("Starting Global HadisTransmitter seeding (Layer-based Parallel Chains)...") - # --------------------------------------------------------- - # 1. Fetch ALL Objects - # --------------------------------------------------------- + # 1. Fetch Objects layers = list(NarratorLayer.objects.all()) transmitters = list(Transmitters.objects.all()) - # Fetch all Hadis records hadis_qs = Hadis.objects.all() - # --------------------------------------------------------- - # 2. Validation - # --------------------------------------------------------- - if len(layers) < 3: - self.stdout.write(self.style.ERROR(f"Need at least 3 NarratorLayers to satisfy constraints, but found {len(layers)}.")) + # 2. Critical Validation + # We need enough pool size. Minimum requirement: 2 layers * 5 transmitters = 10 unique people. + if len(layers) < 2: + self.stdout.write(self.style.ERROR(f"Error: Need at least 2 NarratorLayers, found {len(layers)}.")) return - if len(transmitters) < 5: - self.stdout.write(self.style.ERROR(f"Need at least 5 Transmitters to satisfy chain length, but found {len(transmitters)}.")) + + # If you haven't run the transmitter seeder yet, this might fail. + if len(transmitters) < 10: + self.stdout.write(self.style.ERROR( + f"Error: Found only {len(transmitters)} transmitters. " + "To satisfy '2 layers x 5 transmitters', you need at least 10 unique transmitters. " + "Please run the transmitter seeder command first." + )) return + if not hadis_qs.exists(): - self.stdout.write(self.style.ERROR("No Hadis found in the database.")) + self.stdout.write(self.style.ERROR("No Hadis found.")) return total_hadis = hadis_qs.count() - self.stdout.write(f"Found {len(layers)} Layers, {len(transmitters)} Transmitters, and {total_hadis} Hadis.") - - # --------------------------------------------------------- - # 3. Creation Loop - # --------------------------------------------------------- - created_links_count = 0 + self.stdout.write(f"Found {len(layers)} Layers, {len(transmitters)} Transmitters. Processing {total_hadis} Hadiths...") - self.stdout.write("Beginning processing...") + # 3. Processing + created_links_count = 0 for i, hadis in enumerate(hadis_qs, 1): - # Progress indicator - if i % 100 == 0: + if i % 50 == 0: self.stdout.write(f"Processing {i}/{total_hadis}...") with transaction.atomic(): - # Clean existing transmitters for this hadis to avoid duplicates/conflicts if re-running - # Remove this line if you want to APPEND to existing data instead of resetting + # A. Wipe existing connections for a clean slate HadisTransmitter.objects.filter(hadis=hadis).delete() - # CONSTRAINT 1: Chain length must be at least 5 (let's do 5 to 7) - chain_length = random.randint(5, 7) + # B. Determine Structure + # Constraint: At least 2-3 layers + num_layers_to_use = random.randint(2, 3) + + # Don't try to use more layers than exist in DB + num_layers_to_use = min(num_layers_to_use, len(layers)) + selected_layers = random.sample(layers, num_layers_to_use) - # CONSTRAINT 2: Must have at least 2-3 narrator layers - target_unique_layers = random.randint(2, 3) + # Constraint: At least 5 transmitters per layer (let's say 5 to 7) + # We calculate total needed first to ensure we pick UNIQUE people for this specific Hadith + counts_per_layer = [random.randint(5, 7) for _ in range(num_layers_to_use)] + total_needed = sum(counts_per_layer) - # --- Logic to Select Transmitters and Layers --- - - # A. Select Transmitters - selected_transmitters = random.sample(transmitters, chain_length) + # Safety: Ensure we don't ask for more than we have in the whole DB + if total_needed > len(transmitters): + self.stdout.write(self.style.WARNING(f"Hadis {hadis.id}: Not enough total transmitters for {num_layers_to_use} layers. Skipping.")) + continue - # B. Select Layers - # First, pick the 'mandatory' unique layers to satisfy the constraint - mandatory_layers = random.sample(layers, target_unique_layers) - - # Second, fill the remaining slots in the chain - remaining_slots = chain_length - target_unique_layers - - # We can pick from ANY layer for the remaining slots (including the mandatory ones) - other_layers = [random.choice(layers) for _ in range(remaining_slots)] - - # Combine and Shuffle - final_layers_pool = mandatory_layers + other_layers - random.shuffle(final_layers_pool) + # C. Select Transmitters + # We pick a unique pool for this Hadith so the same person isn't in 2 layers + pool = random.sample(transmitters, total_needed) + + # D. Distribute and Create + cursor = 0 + for layer_idx, layer in enumerate(selected_layers): + count = counts_per_layer[layer_idx] + + # Slice the pool for this layer + layer_group = pool[cursor : cursor + count] + cursor += count - # --- Create Connections --- - for index, transmitter in enumerate(selected_transmitters): - HadisTransmitter.objects.create( - hadis=hadis, - transmitter=transmitter, - narrator_layer=final_layers_pool[index], - order=index, - # Check if transmitter has 'reliability' (ForeignKey) or if it's a direct field - status=transmitter.reliability if hasattr(transmitter, 'reliability') else None - ) - created_links_count += 1 + # Create Links with Independent Ordering (0, 1, 2...) for THIS layer + for order_index, transmitter in enumerate(layer_group): + HadisTransmitter.objects.create( + hadis=hadis, + transmitter=transmitter, + narrator_layer=layer, + order=order_index, # Resets to 0 for every layer! + status=transmitter.reliability if hasattr(transmitter, 'reliability') else None + ) + created_links_count += 1 - self.stdout.write(self.style.SUCCESS(f"Done! Created {created_links_count} HadisTransmitter connections for {total_hadis} Hadiths.")) \ No newline at end of file + self.stdout.write(self.style.SUCCESS(f"Done! Created {created_links_count} connections.")) \ No newline at end of file diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 522fbfa..d9c1cee 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -369,7 +369,7 @@ class HadisTransmittersView(RetrieveAPIView): serializer_class = HadisTransmitterListSerializer lookup_field = 'slug' lookup_url_kwarg = 'hadis_slug' - + pagination_class = NoPagination @hadis_transmitters_swagger def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs)