diff --git a/apps/hadis/management/commands/seed_complete_hadis_data.py b/apps/hadis/management/commands/seed_complete_hadis_data.py index 44b514d..9f82440 100644 --- a/apps/hadis/management/commands/seed_complete_hadis_data.py +++ b/apps/hadis/management/commands/seed_complete_hadis_data.py @@ -321,76 +321,122 @@ class Command(BaseCommand): def create_narrator_layers(self): """Create narrator layers - fixes empty slugs first""" from django.utils.text import slugify + from django.db.models import Q + + # Fix ALL existing layers with empty, null, or invalid slugs + existing_layers = NarratorLayer.objects.filter(Q(slug__isnull=True) | Q(slug='') | Q(slug=' ')) + self.stdout.write(f"Found {existing_layers.count()} layers with empty slugs to fix") - # Fix existing layers with empty or invalid slugs - existing_layers = NarratorLayer.objects.all() for layer in existing_layers: - if not layer.slug or not layer.slug.strip(): - # Generate proper slug - try: - if layer.name and isinstance(layer.name, list) and len(layer.name) > 0: - text = layer.name[0].get('text', '').strip() - if text: - new_slug = slugify(text) - else: - new_slug = f"layer-{layer.number}" + # Generate proper slug + try: + if layer.name and isinstance(layer.name, list) and len(layer.name) > 0: + text = layer.name[0].get('text', '').strip() + if text: + new_slug = slugify(text) else: new_slug = f"layer-{layer.number}" - except (IndexError, KeyError, AttributeError, TypeError): + else: new_slug = f"layer-{layer.number}" - - # Ensure uniqueness - counter = 1 - original_slug = new_slug - while NarratorLayer.objects.filter(slug=new_slug).exclude(pk=layer.pk).exists(): - new_slug = f"{original_slug}-{counter}" - counter += 1 - - layer.slug = new_slug - layer.save(update_fields=['slug']) - self.stdout.write(f"Fixed slug for layer {layer.number}: {new_slug}") + except (IndexError, KeyError, AttributeError, TypeError): + new_slug = f"layer-{layer.number}" + + # Ensure uniqueness + counter = 1 + original_slug = new_slug + while NarratorLayer.objects.filter(slug=new_slug).exclude(pk=layer.pk).exists(): + new_slug = f"{original_slug}-{counter}" + counter += 1 + + layer.slug = new_slug + layer.save(update_fields=['slug']) + self.stdout.write(f"Fixed slug for layer {layer.pk} (number={layer.number}): '{new_slug}'") - # Now create or get narrator layers + # Now create or get narrator layers - use filter().first() to avoid duplicates for layer_data in RUSSIAN_NARRATOR_LAYERS: try: - layer, created = NarratorLayer.objects.get_or_create( - number=layer_data['number'], - defaults={ - 'name': [{'language_code': 'ru', 'text': layer_data['name']}], - 'description': [{'language_code': 'ru', 'text': layer_data['description']}] - } - ) + # Try to find existing layer by number + layer = NarratorLayer.objects.filter(number=layer_data['number']).first() + + if layer: + # Layer already exists + created = False + self.stdout.write(f"Using existing layer {layer_data['number']}") + else: + # Create new layer + layer = NarratorLayer.objects.create( + number=layer_data['number'], + name=[{'language_code': 'ru', 'text': layer_data['name']}], + description=[{'language_code': 'ru', 'text': layer_data['description']}] + ) + created = True + self.stdout.write(f"Created new layer {layer_data['number']}") + self.narrator_layers.append(layer) except Exception as e: - # If creation fails, try to get existing layer by number - self.stdout.write(self.style.WARNING( - f"Warning creating layer {layer_data['number']}: {str(e)}" + self.stdout.write(self.style.ERROR( + f"Error with layer {layer_data['number']}: {str(e)}" )) + # Try one more time to get by number try: - layer = NarratorLayer.objects.get(number=layer_data['number']) - self.narrator_layers.append(layer) - except NarratorLayer.DoesNotExist: - self.stdout.write(self.style.ERROR( - f"Could not create or find layer {layer_data['number']}" - )) + layer = NarratorLayer.objects.filter(number=layer_data['number']).first() + if layer: + self.narrator_layers.append(layer) + except Exception as e2: + self.stdout.write(self.style.ERROR(f"Final attempt failed: {str(e2)}")) self.created_counts['narrator_layers'] = len(self.narrator_layers) def create_reliability_statuses(self): """Create transmitter reliability statuses - fixes duplicates first""" from django.utils.text import slugify - from django.db.models import Count + from django.db.models import Count, Q + + # Fix records with empty/null slugs first + empty_slug_records = TransmitterReliability.objects.filter(Q(slug__isnull=True) | Q(slug='') | Q(slug=' ')) + self.stdout.write(f"Found {empty_slug_records.count()} reliability records with empty slugs") + + for record in empty_slug_records: + try: + if record.title and isinstance(record.title, list) and len(record.title) > 0: + text = record.title[0].get('text', '').strip() + if text: + new_slug = slugify(text) + else: + from datetime import datetime + new_slug = f"reliability-{datetime.now().strftime('%Y%m%d%H%M%S%f')}" + else: + from datetime import datetime + new_slug = f"reliability-{datetime.now().strftime('%Y%m%d%H%M%S%f')}" + except: + from datetime import datetime + new_slug = f"reliability-{datetime.now().strftime('%Y%m%d%H%M%S%f')}" + + # Ensure uniqueness + counter = 1 + original_slug = new_slug + while TransmitterReliability.objects.filter(slug=new_slug).exclude(pk=record.pk).exists(): + new_slug = f"{original_slug}-{counter}" + counter += 1 + + record.slug = new_slug + record.save(update_fields=['slug']) + self.stdout.write(f"Fixed empty reliability slug: '{new_slug}'") # Find and fix duplicate slugs duplicates = TransmitterReliability.objects.values('slug').annotate( count=Count('id') ).filter(count__gt=1) + self.stdout.write(f"Found {duplicates.count()} duplicate reliability slugs") + for dup in duplicates: slug_value = dup['slug'] # Get all records with this slug - records = TransmitterReliability.objects.filter(slug=slug_value) - # Keep the first one, update or delete others + records = list(TransmitterReliability.objects.filter(slug=slug_value)) + self.stdout.write(f"Processing {len(records)} records with slug '{slug_value}'") + + # Keep the first one, update others for i, record in enumerate(records): if i == 0: continue # Keep first record as is @@ -420,15 +466,16 @@ class Command(BaseCommand): record.slug = new_slug record.save(update_fields=['slug']) - self.stdout.write(f"Fixed duplicate reliability slug: {new_slug}") + self.stdout.write(f"Fixed duplicate reliability slug: '{new_slug}'") - # Now create or get reliability statuses + # Now create or get reliability statuses - using filter().first() to avoid MultipleObjectsReturned for reliability_data in RUSSIAN_RELIABILITY_LEVELS: try: # Try to get by slug first reliability = TransmitterReliability.objects.filter(slug=reliability_data['slug']).first() if reliability: created = False + self.stdout.write(f"Using existing reliability: {reliability_data['slug']}") else: # Create new one reliability = TransmitterReliability.objects.create( @@ -437,11 +484,12 @@ class Command(BaseCommand): color=reliability_data['color'] ) created = True + self.stdout.write(f"Created new reliability: {reliability_data['slug']}") self.reliability_statuses.append(reliability) except Exception as e: - self.stdout.write(self.style.WARNING( - f"Warning creating reliability status {reliability_data['slug']}: {str(e)}" + self.stdout.write(self.style.ERROR( + f"Error with reliability status {reliability_data['slug']}: {str(e)}" )) self.created_counts['reliability_statuses'] = len(self.reliability_statuses) @@ -449,18 +497,53 @@ class Command(BaseCommand): def create_opinion_statuses(self): """Create opinion statuses - fixes duplicates first""" from django.utils.text import slugify - from django.db.models import Count + from django.db.models import Count, Q + + # Fix records with empty/null slugs first + empty_slug_records = OpinionStatus.objects.filter(Q(slug__isnull=True) | Q(slug='') | Q(slug=' ')) + self.stdout.write(f"Found {empty_slug_records.count()} opinion status records with empty slugs") + + for record in empty_slug_records: + try: + if record.title and isinstance(record.title, list) and len(record.title) > 0: + text = record.title[0].get('text', '').strip() + if text: + new_slug = slugify(text) + else: + from datetime import datetime + new_slug = f"opinion-{datetime.now().strftime('%Y%m%d%H%M%S%f')}" + else: + from datetime import datetime + new_slug = f"opinion-{datetime.now().strftime('%Y%m%d%H%M%S%f')}" + except: + from datetime import datetime + new_slug = f"opinion-{datetime.now().strftime('%Y%m%d%H%M%S%f')}" + + # Ensure uniqueness + counter = 1 + original_slug = new_slug + while OpinionStatus.objects.filter(slug=new_slug).exclude(pk=record.pk).exists(): + new_slug = f"{original_slug}-{counter}" + counter += 1 + + record.slug = new_slug + record.save(update_fields=['slug']) + self.stdout.write(f"Fixed empty opinion status slug: '{new_slug}'") # Find and fix duplicate slugs duplicates = OpinionStatus.objects.values('slug').annotate( count=Count('id') ).filter(count__gt=1) + self.stdout.write(f"Found {duplicates.count()} duplicate opinion status slugs") + for dup in duplicates: slug_value = dup['slug'] # Get all records with this slug - records = OpinionStatus.objects.filter(slug=slug_value) - # Keep the first one, update or delete others + records = list(OpinionStatus.objects.filter(slug=slug_value)) + self.stdout.write(f"Processing {len(records)} records with slug '{slug_value}'") + + # Keep the first one, update others for i, record in enumerate(records): if i == 0: continue # Keep first record as is @@ -490,15 +573,16 @@ class Command(BaseCommand): record.slug = new_slug record.save(update_fields=['slug']) - self.stdout.write(f"Fixed duplicate opinion status slug: {new_slug}") + self.stdout.write(f"Fixed duplicate opinion status slug: '{new_slug}'") - # Now create or get opinion statuses + # Now create or get opinion statuses - using filter().first() to avoid MultipleObjectsReturned for opinion_data in RUSSIAN_OPINION_STATUSES: try: # Try to get by slug first opinion_status = OpinionStatus.objects.filter(slug=opinion_data['slug']).first() if opinion_status: created = False + self.stdout.write(f"Using existing opinion status: {opinion_data['slug']}") else: # Create new one opinion_status = OpinionStatus.objects.create( @@ -507,11 +591,12 @@ class Command(BaseCommand): color=opinion_data['color'] ) created = True + self.stdout.write(f"Created new opinion status: {opinion_data['slug']}") self.opinion_statuses.append(opinion_status) except Exception as e: - self.stdout.write(self.style.WARNING( - f"Warning creating opinion status {opinion_data['slug']}: {str(e)}" + self.stdout.write(self.style.ERROR( + f"Error with opinion status {opinion_data['slug']}: {str(e)}" )) self.created_counts['opinion_statuses'] = len(self.opinion_statuses) @@ -578,24 +663,72 @@ class Command(BaseCommand): def create_books_and_authors(self): """Create books and authors""" - # Create authors + from django.utils.text import slugify + + # Create authors - check for existing ones first for author_name in RUSSIAN_AUTHOR_NAMES[:15]: - author = BookAuthor.objects.create( - name=[{'language_code': 'ru', 'text': author_name}], - slug=None # Will be auto-generated - ) - self.authors.append(author) + try: + # Check if author with this name already exists + existing_author = None + all_authors = BookAuthor.objects.all() + for auth in all_authors: + if auth.name and isinstance(auth.name, list) and len(auth.name) > 0: + if auth.name[0].get('text', '') == author_name: + existing_author = auth + break + + if existing_author: + author = existing_author + self.stdout.write(f"Using existing author: {author_name}") + else: + author = BookAuthor.objects.create( + name=[{'language_code': 'ru', 'text': author_name}] + ) + self.stdout.write(f"Created new author: {author_name}") + + self.authors.append(author) + except Exception as e: + self.stdout.write(self.style.ERROR( + f"Error creating author {author_name}: {str(e)}" + )) - # Create books + # Create books - check for existing ones first for book_title in RUSSIAN_BOOK_TITLES[:20]: - book = BookReference.objects.create( - title=[{'language_code': 'ru', 'text': book_title}], - description=f"Классическое исламское произведение - {book_title}", - slug=None # Will be auto-generated - ) - # Add random authors - book.authors.add(*random.sample(self.authors, random.randint(1, 2))) - self.books.append(book) + try: + # Generate slug to check for existing book + expected_slug = slugify(book_title, allow_unicode=True) + + # Try to find existing book by slug or title + book = BookReference.objects.filter(slug=expected_slug).first() + + if not book: + # Check by title as fallback + all_books = BookReference.objects.all() + for bk in all_books: + if bk.title and isinstance(bk.title, list) and len(bk.title) > 0: + if bk.title[0].get('text', '') == book_title: + book = bk + break + + if book: + self.stdout.write(f"Using existing book: {book_title}") + else: + book = BookReference.objects.create( + title=[{'language_code': 'ru', 'text': book_title}], + description=[{'language_code': 'ru', 'text': f"Классическое исламское произведение - {book_title}"}] + ) + self.stdout.write(f"Created new book: {book_title}") + + # Add random authors if we have any + if self.authors and book.authors.count() == 0: + num_authors = min(random.randint(1, 2), len(self.authors)) + book.authors.add(*random.sample(self.authors, num_authors)) + + self.books.append(book) + except Exception as e: + self.stdout.write(self.style.ERROR( + f"Error creating book {book_title}: {str(e)}" + )) self.created_counts['authors'] = len(self.authors) self.created_counts['books'] = len(self.books)