From 533a39363c07fa5e04458a7c311e60b5df3e4c88 Mon Sep 17 00:00:00 2001 From: Joshua Laymon Date: Thu, 14 Aug 2025 19:32:55 +0000 Subject: [PATCH] Update web/core/views.py --- web/core/views.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/web/core/views.py b/web/core/views.py index 58af611..6c97e34 100644 --- a/web/core/views.py +++ b/web/core/views.py @@ -13,6 +13,7 @@ from .models import Entry 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 # Order + labels used in the Search UI FIELD_ORDER = [ @@ -459,6 +460,80 @@ def normalize_scripture(request): "limit": limit, }, ) + + from django.views.decorators.http import require_http_methods +from django.contrib.auth.decorators import login_required, user_passes_test + +@login_required +@user_passes_test(is_admin) +@require_http_methods(["GET", "POST"]) +def normalize_source(request): + """ + GET -> dry-run preview (summary + first 100 examples) + POST -> apply changes to all entries' source (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: + from django.db import transaction + batch, pending = 500, [] + for e in qs.iterator(): + original = (e.source or "").strip() + normalized, warns = normalize_source_field(original) + warnings_total += len(warns) + if normalized != original: + changed += 1 + preview.append((e.id, original, normalized)) + e.source = normalized + pending.append(e) + if len(pending) >= batch: + with transaction.atomic(): + for obj in pending: + obj.save(update_fields=["source"]) + pending.clear() + if pending: + with transaction.atomic(): + for obj in pending: + obj.save(update_fields=["source"]) + else: + # dry-run + for e in qs.iterator(): + original = (e.source or "").strip() + normalized, warns = normalize_source_field(original) + warnings_total += len(warns) + if normalized != original: + changed += 1 + preview.append((e.id, original, normalized)) + + preview = preview[:100] + + 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_source_result.html", + { + "applied": apply, + "changed": changed, + "warnings_total": warnings_total, + "preview": preview, + "limit": limit, + }, + ) + @login_required def stats_page(request): from collections import Counter