diff --git a/apps/hadis/management/commands/generate_editions_and_volumes.py b/apps/hadis/management/commands/generate_editions_and_volumes.py index 16aff19..ef419e8 100644 --- a/apps/hadis/management/commands/generate_editions_and_volumes.py +++ b/apps/hadis/management/commands/generate_editions_and_volumes.py @@ -192,9 +192,9 @@ class Command(BaseCommand): ) total_editions_created += 1 - # Assign random researchers and editors from existing authors + # Assign 1 researcher and 1 editor from existing 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: BookResearcher.objects.create( book_edition=edition, @@ -202,7 +202,7 @@ class Command(BaseCommand): ) 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: BookEditor.objects.create( book_edition=edition, diff --git a/apps/hadis/management/commands/normalize_edition_contributors.py b/apps/hadis/management/commands/normalize_edition_contributors.py new file mode 100644 index 0000000..66d554a --- /dev/null +++ b/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)" + ) + ) diff --git a/apps/hadis/management/commands/seed_edition_editors.py b/apps/hadis/management/commands/seed_edition_editors.py index 16560b6..c236e79 100644 --- a/apps/hadis/management/commands/seed_edition_editors.py +++ b/apps/hadis/management/commands/seed_edition_editors.py @@ -42,29 +42,7 @@ class Command(BaseCommand): with transaction.atomic(): 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) BookEditor.objects.create( book_edition=edition, diff --git a/apps/hadis/management/commands/seed_edition_researchers.py b/apps/hadis/management/commands/seed_edition_researchers.py index 6741359..035e877 100644 --- a/apps/hadis/management/commands/seed_edition_researchers.py +++ b/apps/hadis/management/commands/seed_edition_researchers.py @@ -42,29 +42,7 @@ class Command(BaseCommand): with transaction.atomic(): 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) BookResearcher.objects.create( book_edition=edition,