From c2943a59f8e4dee4d869df62088b98d54d75fcd5 Mon Sep 17 00:00:00 2001 From: Joshua Laymon Date: Wed, 13 Aug 2025 23:54:50 +0000 Subject: [PATCH] Update web/core/views.py --- web/core/views.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/web/core/views.py b/web/core/views.py index ba8b475..5efcf6e 100644 --- a/web/core/views.py +++ b/web/core/views.py @@ -4,12 +4,14 @@ from django.contrib.auth.decorators import login_required, user_passes_test from django.http import HttpResponse from django.contrib import messages from django.db.models import Q +from django.views.decorators.http import require_http_methods from datetime import date, timedelta import csv 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 # Order + labels used in the Search UI FIELD_ORDER = [ @@ -381,4 +383,78 @@ def stats_page(request): "heights": heights, "top_subjects": top_subjects, }, + ) + + +# ========= NEW: Scripture Normalizer endpoint ========= + +@login_required +@user_passes_test(is_admin) +@require_http_methods(["GET", "POST"]) +def normalize_scripture(request): + """ + GET -> dry-run preview (summary + first 100 examples) + POST -> apply changes to all entries' scripture_raw (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.scripture_raw or "").strip() + normalized, warns = normalize_scripture_field(original) + warnings_total += len(warns) + if normalized != original: + changed += 1 + preview.append((e.id, original, normalized)) + e.scripture_raw = normalized + pending.append(e) + if len(pending) >= batch: + with transaction.atomic(): + for obj in pending: + obj.save(update_fields=["scripture_raw"]) + pending.clear() + if pending: + with transaction.atomic(): + for obj in pending: + obj.save(update_fields=["scripture_raw"]) + else: + # dry-run only + for e in qs.iterator(): + original = (e.scripture_raw or "").strip() + normalized, warns = normalize_scripture_field(original) + warnings_total += len(warns) + if normalized != original: + changed += 1 + preview.append((e.id, original, normalized)) + + preview = preview[:100] # keep the 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_result.html", + { + "applied": apply, + "changed": changed, + "warnings_total": warnings_total, + "preview": preview, + "limit": limit, + }, ) \ No newline at end of file