Browse Source
chore(hadis): normalize book edition contributors to 1 researcher and 1 editor
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 editionmaster
4 changed files with 93 additions and 49 deletions
-
6apps/hadis/management/commands/generate_editions_and_volumes.py
-
88apps/hadis/management/commands/normalize_edition_contributors.py
-
24apps/hadis/management/commands/seed_edition_editors.py
-
24apps/hadis/management/commands/seed_edition_researchers.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)" |
|||
) |
|||
) |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue