Repository: carpentries/amy

Issue:
Adding a person without a surname causes 500 Server Error



Discussion:

Resolving patch:
diff --git a/amy/workshops/migrations/0215_auto_20200808_1356.py b/amy/workshops/migrations/0215_auto_20200808_1356.py
new file mode 100644
--- /dev/null
+++ b/amy/workshops/migrations/0215_auto_20200808_1356.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.2.13 on 2020-08-08 13:56
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0214_auto_20200713_1757'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='person',
+            name='family',
+            field=models.CharField(blank=True, default='', max_length=100, verbose_name='Family (last) name'),
+        ),
+        migrations.AlterField(
+            model_name='person',
+            name='middle',
+            field=models.CharField(blank=True, default='', max_length=100, verbose_name='Middle name'),
+        ),
+    ]
diff --git a/amy/workshops/models.py b/amy/workshops/models.py
--- a/amy/workshops/models.py
+++ b/amy/workshops/models.py
@@ -445,10 +445,10 @@ class Person(
         max_length=STR_LONG, verbose_name="Personal (first) name",
     )
     middle = models.CharField(
-        max_length=STR_LONG, blank=True, verbose_name="Middle name",
+        max_length=STR_LONG, blank=True, default="", verbose_name="Middle name",
     )
     family = models.CharField(
-        max_length=STR_LONG, blank=True, null=True, verbose_name="Family (last) name",
+        max_length=STR_LONG, blank=True, default="", verbose_name="Family (last) name",
     )
     email = models.CharField(  # emailfield?
         max_length=STR_LONG,
diff --git a/amy/workshops/util.py b/amy/workshops/util.py
--- a/amy/workshops/util.py
+++ b/amy/workshops/util.py
@@ -161,9 +161,9 @@ def verify_upload_person_task(data, match=False):
 
         # check if the user exists, and if so: check if existing user's
         # personal and family names are the same as uploaded
-        email = item.get("email", None)
-        personal = item.get("personal", None)
-        family = item.get("family", None)
+        email = item.get("email", "")
+        personal = item.get("personal", "")
+        family = item.get("family", "")
         person_id = item.get("existing_person_id", None)
         person = None
 
@@ -501,7 +501,7 @@ def update_manual_score(cleaned_data):
 
 def create_username(personal, family, tries=NUM_TRIES):
     """Generate unique username."""
-    stem = normalize_name(family) + "_" + normalize_name(personal)
+    stem = normalize_name(family or "") + "_" + normalize_name(personal or "")
 
     counter = None
     for i in range(tries):  # let's limit ourselves to only 100 tries


Test patch:
diff --git a/amy/workshops/tests/test_person.py b/amy/workshops/tests/test_person.py
--- a/amy/workshops/tests/test_person.py
+++ b/amy/workshops/tests/test_person.py
@@ -42,6 +42,19 @@ def setUp(self):
         super().setUp()
         self._setUpUsersAndLogin()
 
+    def test_family_name_notnull(self):
+        """This is a regression test against #1682
+        (https://github.com/carpentries/amy/issues/1682).
+
+        The error was: family name was allowed to be null, which caused 500 errors
+        when trying to save person without the family name.
+        The actual error happened in the name normalization (util.normalize_name)."""
+        p1 = Person.objects.create(personal="Harry", username="hp")
+        p2 = Person.objects.create(personal="Hermione", username="hg")
+
+        self.assertEqual(p1.family, "")
+        self.assertEqual(p2.family, "")
+
     def test_login_with_email(self):
         """ Make sure we can login with user's email too, not only with the
         username."""
diff --git a/amy/workshops/tests/test_util.py b/amy/workshops/tests/test_util.py
--- a/amy/workshops/tests/test_util.py
+++ b/amy/workshops/tests/test_util.py
@@ -930,6 +930,15 @@ def test_hyphenated_name(self):
         username = create_username(personal='Andy', family='Blanking-Crush')
         self.assertEqual(username, 'blanking-crush_andy')
 
+    def test_noned_names(self):
+        """This is a regression test against #1682
+        (https://github.com/carpentries/amy/issues/1682).
+
+        The error was: family name was allowed to be null, which caused 500 errors
+        when trying to save person without the family name due to name normalization."""
+        username = create_username(personal=None, family=None)
+        self.assertEqual(username, "_")
+
 
 class TestPaginatorSections(TestBase):
     def make_paginator(self, num_pages, page_index=None):