From 5077ecffca8d60bde73257680c4a8556d805a3fb Mon Sep 17 00:00:00 2001 From: Joshua Laymon Date: Sat, 16 Aug 2025 16:46:16 +0000 Subject: [PATCH] Update web/core/views.py --- web/core/views.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/web/core/views.py b/web/core/views.py index 6c97e34..6e36e59 100644 --- a/web/core/views.py +++ b/web/core/views.py @@ -14,6 +14,7 @@ from .forms import ImportForm, EntryForm from .utils import terms, has_wildcards, wildcard_to_regex, import_csv_bytes from .scripture_normalizer import normalize_scripture_field # <-- NEW from .source_normalizer import normalize_source_field # NEW +from .subject_normalizer import normalize_subject_field # NEW # Order + labels used in the Search UI FIELD_ORDER = [ @@ -638,4 +639,75 @@ def stats_page(request): "top_refs": top_refs, "book_distribution": book_distribution, }, + ) + @login_required +@user_passes_test(is_admin) +@require_http_methods(["GET", "POST"]) +def normalize_subjects(request): + """ + GET -> dry-run preview (summary + first 100 examples) + POST -> apply changes to all entries' subject (batched) + Optional ?limit= for preview subset. + """ + apply = request.method == "POST" + limit = int(request.GET.get("limit", "0") or "0") + + qs = Entry.objects.all().order_by("id") + if limit: + qs = qs[:limit] + + changed = 0 + warnings_total = 0 + preview = [] + + if apply: + # write in batches to keep transactions short + from django.db import transaction + batch, pending = 500, [] + for e in qs.iterator(): + original = (e.subject or "").strip() + normalized, warns = normalize_subject_field(original) + warnings_total += len(warns) + if normalized != original: + changed += 1 + preview.append((e.id, original, normalized)) + e.subject = normalized + pending.append(e) + if len(pending) >= batch: + with transaction.atomic(): + for obj in pending: + obj.save(update_fields=["subject"]) + pending.clear() + if pending: + from django.db import transaction + with transaction.atomic(): + for obj in pending: + obj.save(update_fields=["subject"]) + else: + # dry-run only + for e in qs.iterator(): + original = (e.subject or "").strip() + normalized, warns = normalize_subject_field(original) + warnings_total += len(warns) + if normalized != original: + changed += 1 + preview.append((e.id, original, normalized)) + + preview = preview[:100] # keep table reasonable + + messages.info( + request, + f"{'Applied' if apply else 'Dry-run'}: {changed} entries " + f"{'changed' if apply else 'would change'}; {warnings_total} warnings." + ) + return render( + request, + "normalize_subjects_result.html", + { + "applied": apply, + "changed": changed, + "warnings_total": warnings_total, + "preview": preview, + "limit": limit, + }, ) \ No newline at end of file