Browse Source

Enhance hadis data seeding command with improved slug handling and error management

- Refactored the slug generation process for NarratorLayer, TransmitterReliability, and OpinionStatus models to handle empty or invalid slugs more effectively.
- Implemented checks for existing records to prevent duplicates during the seeding process, improving data integrity.
- Enhanced error handling during the creation of authors and books, ensuring existing records are utilized when available.
- Updated logging to provide clearer feedback on the seeding process, including details on existing records and errors encountered.
master
mortezaei 4 months ago
parent
commit
1e7a03fd40
  1. 271
      apps/hadis/management/commands/seed_complete_hadis_data.py

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

Loading…
Cancel
Save