Browse Source

chore(hadis): normalize book edition contributors to 1 researcher and 1 editor

- Add normalize_edition_contributors management command to ensure every BookEdition has at most 1 researcher and 1 editor (and backfill missing ones)

- Update generate_editions_and_volumes, seed_edition_researchers, and seed_edition_editors commands to create exactly 1 researcher and 1 editor per edition
master
Mohsen Taba 2 days ago
parent
commit
1295939746
  1. 6
      apps/hadis/management/commands/generate_editions_and_volumes.py
  2. 88
      apps/hadis/management/commands/normalize_edition_contributors.py
  3. 24
      apps/hadis/management/commands/seed_edition_editors.py
  4. 24
      apps/hadis/management/commands/seed_edition_researchers.py

6
apps/hadis/management/commands/generate_editions_and_volumes.py

@ -192,9 +192,9 @@ class Command(BaseCommand):
) )
total_editions_created += 1 total_editions_created += 1
# Assign random researchers and editors from existing authors
# Assign 1 researcher and 1 editor from existing authors
if authors: if authors:
sample_researchers = random.sample(authors, min(len(authors), random.randint(1, 2)))
sample_researchers = random.sample(authors, 1)
for author in sample_researchers: for author in sample_researchers:
BookResearcher.objects.create( BookResearcher.objects.create(
book_edition=edition, book_edition=edition,
@ -202,7 +202,7 @@ class Command(BaseCommand):
) )
total_researchers_created += 1 total_researchers_created += 1
sample_editors = random.sample(authors, min(len(authors), random.randint(1, 2)))
sample_editors = random.sample(authors, 1)
for author in sample_editors: for author in sample_editors:
BookEditor.objects.create( BookEditor.objects.create(
book_edition=edition, book_edition=edition,

88
apps/hadis/management/commands/normalize_edition_contributors.py

@ -0,0 +1,88 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from apps.hadis.models import BookEdition, BookAuthor, BookResearcher, BookEditor
class Command(BaseCommand):
help = "Ensure every BookEdition has exactly 1 BookResearcher and 1 BookEditor."
def handle(self, *args, **options):
editions = list(BookEdition.objects.prefetch_related("researchers", "editors").order_by("id"))
authors = list(BookAuthor.objects.order_by("id"))
if not editions:
self.stdout.write(self.style.WARNING("No BookEditions found."))
return
if not authors:
self.stdout.write(self.style.ERROR("No BookAuthors found."))
return
deleted_researchers = 0
created_researchers = 0
deleted_editors = 0
created_editors = 0
with transaction.atomic():
for idx, edition in enumerate(editions):
# 1. Normalize Researchers to exactly 1
researchers = list(edition.researchers.all())
if len(researchers) > 1:
# Prefer keeping one that has an author linked
researchers.sort(key=lambda r: (0 if r.author_id else 1, r.id))
keep_r = researchers[0]
extra_r_ids = [r.id for r in researchers[1:]]
BookResearcher.objects.filter(id__in=extra_r_ids).delete()
deleted_researchers += len(extra_r_ids)
chosen_researcher_author_id = keep_r.author_id
elif len(researchers) == 1:
keep_r = researchers[0]
if not keep_r.author_id:
author_obj = authors[idx % len(authors)]
keep_r.author = author_obj
keep_r.save()
chosen_researcher_author_id = keep_r.author_id
else:
author_obj = authors[idx % len(authors)]
keep_r = BookResearcher.objects.create(
book_edition=edition,
author=author_obj,
)
created_researchers += 1
chosen_researcher_author_id = keep_r.author_id
# 2. Normalize Editors to exactly 1
editors = list(edition.editors.all())
if len(editors) > 1:
# Prefer keeping one with an author (ideally distinct from researcher author)
editors.sort(
key=lambda e: (
0 if (e.author_id and e.author_id != chosen_researcher_author_id) else (1 if e.author_id else 2),
e.id,
)
)
keep_e = editors[0]
extra_e_ids = [e.id for e in editors[1:]]
BookEditor.objects.filter(id__in=extra_e_ids).delete()
deleted_editors += len(extra_e_ids)
elif len(editors) == 1:
keep_e = editors[0]
if not keep_e.author_id:
author_obj = authors[(idx + 1) % len(authors)]
keep_e.author = author_obj
keep_e.save()
else:
author_obj = authors[(idx + 1) % len(authors)]
BookEditor.objects.create(
book_edition=edition,
author=author_obj,
)
created_editors += 1
self.stdout.write(
self.style.SUCCESS(
f"Normalized {len(editions)} BookEditions:\n"
f" - Researchers: deleted {deleted_researchers} extras, created {created_researchers} missing (now {BookResearcher.objects.count()} total)\n"
f" - Editors: deleted {deleted_editors} extras, created {created_editors} missing (now {BookEditor.objects.count()} total)"
)
)

24
apps/hadis/management/commands/seed_edition_editors.py

@ -42,29 +42,7 @@ class Command(BaseCommand):
with transaction.atomic(): with transaction.atomic():
for edition in editions: for edition in editions:
# 1. First editor: Text-only (Random Name & Slug)
name_data = random.choice(random_editor_names)
name_field = [
{"language_code": "fa", "text": name_data["fa"]},
{"language_code": "en", "text": name_data["en"]},
]
# Ensure clean slug
base_slug = slugify(name_data["en"])
slug_val = base_slug
counter = 1
while BookEditor.objects.filter(slug=slug_val).exists():
slug_val = f"{base_slug}-{counter}"
counter += 1
BookEditor.objects.create(
book_edition=edition,
name=name_field,
slug=slug_val,
author=None
)
text_only_count += 1
# 2. Second editor: Linked to a random BookAuthor
# Assign 1 editor linked to a random BookAuthor
random_author = random.choice(authors) random_author = random.choice(authors)
BookEditor.objects.create( BookEditor.objects.create(
book_edition=edition, book_edition=edition,

24
apps/hadis/management/commands/seed_edition_researchers.py

@ -42,29 +42,7 @@ class Command(BaseCommand):
with transaction.atomic(): with transaction.atomic():
for edition in editions: for edition in editions:
# 1. First researcher: Text-only (Random Name & Slug)
name_data = random.choice(random_researcher_names)
name_field = [
{"language_code": "fa", "text": name_data["fa"]},
{"language_code": "en", "text": name_data["en"]},
]
# Ensure clean slug
base_slug = slugify(name_data["en"])
slug_val = base_slug
counter = 1
while BookResearcher.objects.filter(slug=slug_val).exists():
slug_val = f"{base_slug}-{counter}"
counter += 1
BookResearcher.objects.create(
book_edition=edition,
name=name_field,
slug=slug_val,
author=None
)
text_only_count += 1
# 2. Second researcher: Linked to a random BookAuthor
# Assign 1 researcher linked to a random BookAuthor
random_author = random.choice(authors) random_author = random.choice(authors)
BookResearcher.objects.create( BookResearcher.objects.create(
book_edition=edition, book_edition=edition,

Loading…
Cancel
Save