Import contacts with per-row error isolation

Python · Python · intermediate · greenfield

Adds the contact importer used by the CSV upload flow: validates and normalizes each row independently, collecting per-row errors instead of aborting the batch. Ran it against an export with missing names, blank emails and mixed-case addresses — good rows import, bad rows land in the error report with their 1-based row numbers.

Requirements

Files touched

--- app/crm/importer.py
+def import_contacts(rows):
+    """Import contact rows, isolating per-row failures.
+
+    Returns ``(imported, errors)``: normalized contacts plus a list of
+    ``(row_number, message)`` tuples, row numbers 1-based over the input.
+    """
+    imported = []
+    errors = []
+    for number, row in enumerate(rows, start=1):
+        name = (row.get("name") or "").strip()
+        email = (row.get("email") or "").strip().lower()
+        if not name:
+            errors.append((number, "name is required"))
+            continue
+        if "@" not in email:
+            errors.append((number, "email is invalid"))
+            continue
+        imported.append({"name": name, "email": email})
+    return imported, errors
+

Review this PR

Python practice