From 4b542757801d45633af9e854fe1b33cf72e6c431 Mon Sep 17 00:00:00 2001 From: Joshua Laymon Date: Fri, 22 Aug 2025 02:54:30 +0000 Subject: [PATCH] Update web/core/views.py --- web/core/views.py | 98 ++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/web/core/views.py b/web/core/views.py index 8c0610e..e197703 100644 --- a/web/core/views.py +++ b/web/core/views.py @@ -342,50 +342,78 @@ def import_wizard(request): "Date", "Date Edited", ]) - if request.method == "POST": - form = ImportForm(request.POST, request.FILES) - if form.is_valid(): - try: - raw = form.cleaned_data["file"].read() +if request.method == "POST": + form = ImportForm(request.POST, request.FILES) + if form.is_valid(): + try: + raw = form.cleaned_data["file"].read() - # --- header check (tolerant: utf-8-sig removes BOM, strip spaces, case-insensitive) --- - import io, csv as _csv - sio = io.StringIO(raw.decode("utf-8-sig", errors="replace")) + # --- super‑defensive header peek (never raises) --- + import io, csv as _csv + sio = io.StringIO(raw.decode("utf-8-sig", errors="replace")) + + header_ok = False + try: rdr = _csv.reader(sio) first_row = next(rdr, []) - norm = [c.strip().lower() for c in first_row] - expected_norm = [c.lower() for c in _EXPECTED_HEADERS] - # If the first row doesn’t look like our header, allow import to proceed, - # but show a gentle warning in the result page. + def _clean(s): + s = "" if s is None else str(s) + # strip quotes and odd prefixes like r:"Talk Title" + s = s.strip().strip("'").strip('"') + if s.lower().startswith("r:"): + s = s[2:].lstrip() + return s.strip().lower() + + norm = [_clean(c) for c in first_row] + + # Pull expected headers from globals if defined, else use defaults + expected = ( + globals().get("_EXPECTED_HEADERS") + or globals().get("EXPECTED_HEADERS") + or [ + "Subject", "Illustration", "Application", "Scripture", "Source", + "Talk Title", "Talk Number", "Code", "Date", "Date Edited", + ] + ) + expected_norm = [h.strip().lower() for h in expected] header_ok = (norm == expected_norm) - if not hasattr(core_utils, "EXPECTED_HEADERS"): - core_utils.EXPECTED_HEADERS = _EXPECTED_HEADERS - # hand off to your existing robust importer - report = import_csv_bytes( - raw, - dry_run=form.cleaned_data["dry_run"], - ) + except Exception: + header_ok = False + finally: + # Rewind so the real importer reads the full file + sio.seek(0) - # attach header check info (if your template shows report dicts) - report = report or {} - report["header_ok"] = header_ok - if not header_ok: - messages.warning( - request, - "The first row does not match the expected header; assuming the file has no header." - ) + # Make sure the utils module also knows the expected headers + from . import utils as core_utils + if not hasattr(core_utils, "EXPECTED_HEADERS"): + core_utils.EXPECTED_HEADERS = expected - return render( + # Hand off to your robust importer + report = import_csv_bytes( + raw, + dry_run=form.cleaned_data["dry_run"], + ) + + # Attach header check info for the result template + report = report or {} + report["header_ok"] = header_ok + if not header_ok: + messages.warning( request, - "import_result.html", - {"report": report, "dry_run": form.cleaned_data["dry_run"]}, + "The first row does not match the expected header; assuming the file has no header." ) - except Exception as e: - messages.error(request, f"Import failed: {e}") - else: - form = ImportForm() - return render(request, "import_wizard.html", {"form": form}) + + return render( + request, + "import_result.html", + {"report": report, "dry_run": form.cleaned_data["dry_run"]}, + ) + except Exception as e: + messages.error(request, f"Import failed: {e}") +else: + form = ImportForm() +return render(request, "import_wizard.html", {"form": form}) @login_required