We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bulk-upload: Not checking whether it is a valid csv file
When a file with first line in valid .csv format and if the following lines are not in a valid format, 

for example if the file uploaded is :
"""
personal,middle,family,email
This is a test file
"""

There is no error being displayed for the wrong file uploaded, instead a ticket is being displayed. I think there should be a check to validate csv file before trying to upload the data.
Am I right?
![screenshot from 2015-03-17 13 28 34](https://cloud.githubusercontent.com/assets/6889955/6683386/8f7ff0ec-ccab-11e4-97af-4f72e51b9d82.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `workshops/util.py`
Content:
```
1 # coding: utf-8
2 from math import pi, sin, cos, acos
3 import csv
4 
5 from django.core.exceptions import ObjectDoesNotExist
6 from django.db import IntegrityError, transaction
7 
8 from .models import Event, Role, Person, Task
9 
10 
11 class InternalError(Exception):
12     pass
13 
14 
15 def earth_distance(pos1, pos2):
16     '''Taken from http://www.johndcook.com/python_longitude_latitude.html.'''
17 
18     # Extract fields.
19     lat1, long1 = pos1
20     lat2, long2 = pos2
21 
22     # Convert latitude and longitude to spherical coordinates in radians.
23     degrees_to_radians = pi/180.0
24 
25     # phi = 90 - latitude
26     phi1 = (90.0 - lat1) * degrees_to_radians
27     phi2 = (90.0 - lat2) * degrees_to_radians
28 
29     # theta = longitude
30     theta1 = long1 * degrees_to_radians
31     theta2 = long2 * degrees_to_radians
32 
33     # Compute spherical distance from spherical coordinates.
34     # For two locations in spherical coordinates
35     # (1, theta, phi) and (1, theta, phi)
36     # cosine( arc length ) = sin phi sin phi' cos(theta-theta') + cos phi cos phi'
37     # distance = rho * arc length
38     c = sin(phi1) * sin(phi2) * cos(theta1 - theta2) + cos(phi1) * cos(phi2)
39     arc = acos(c)
40 
41     # Multiply by 6373 to get distance in km.
42     return arc * 6373
43 
44 
45 def upload_person_task_csv(stream):
46     """Read people from CSV and return a JSON-serializable list of dicts.
47 
48     The input `stream` should be a file-like object that returns
49     Unicode data.
50 
51     "Serializability" is required because we put this data into session.  See
52     https://docs.djangoproject.com/en/1.7/topics/http/sessions/ for details.
53 
54     Also return a list of fields from Person.PERSON_UPLOAD_FIELDS for which
55     no data was given.
56     """
57 
58     result = []
59     reader = csv.DictReader(stream)
60     empty_fields = set()
61 
62     for row in reader:
63         entry = {}
64         for col in Person.PERSON_UPLOAD_FIELDS:
65             if col in row:
66                 entry[col] = row[col].strip()
67             else:
68                 entry[col] = None
69                 empty_fields.add(col)
70 
71         for col in Person.PERSON_TASK_EXTRA_FIELDS:
72             entry[col] = row.get(col, None)
73         entry['errors'] = None
74 
75         result.append(entry)
76 
77     return result, list(empty_fields)
78 
79 
80 def verify_upload_person_task(data):
81     """
82     Verify that uploaded data is correct.  Show errors by populating ``errors``
83     dictionary item.  This function changes ``data`` in place.
84     """
85 
86     errors_occur = False
87     for item in data:
88         errors = []
89 
90         event = item.get('event', None)
91         if event:
92             try:
93                 Event.objects.get(slug=event)
94             except Event.DoesNotExist:
95                 errors.append(u'Event with slug {0} does not exist.'
96                               .format(event))
97 
98         role = item.get('role', None)
99         if role:
100             try:
101                 Role.objects.get(name=role)
102             except Role.DoesNotExist:
103                 errors.append(u'Role with name {0} does not exist.'
104                               .format(role))
105             except Role.MultipleObjectsReturned:
106                 errors.append(u'More than one role named {0} exists.'
107                               .format(role))
108 
109         # check if the user exists, and if so: check if existing user's
110         # personal and family names are the same as uploaded
111         email = item.get('email', None)
112         personal = item.get('personal', None)
113         middle = item.get('middle', None)
114         family = item.get('family', None)
115         person = None
116         if email:
117             # we don't have to check if the user exists in the database
118             # but we should check if, in case the email matches, family and
119             # personal names match, too
120 
121             try:
122                 person = Person.objects.get(email__iexact=email)
123 
124                 assert person.personal == personal
125                 assert person.middle == middle
126                 assert person.family == family
127 
128             except Person.DoesNotExist:
129                 # in this case we need to add the user
130                 pass
131 
132             except AssertionError:
133                 errors.append(
134                     "Personal, middle or family name of existing user don't"
135                     " match: {0} vs {1}, {2} vs {3}, {4} vs {5}"
136                     .format(personal, person.personal, middle, person.middle,
137                             family, person.family)
138                 )
139 
140         if person:
141             if not any([event, role]):
142                 errors.append("User exists but no event and role to assign"
143                               " the user to was provided")
144 
145             else:
146                 # check for duplicate Task
147                 try:
148                     Task.objects.get(event__slug=event, role__name=role,
149                                      person=person)
150                 except Task.DoesNotExist:
151                     pass
152                 else:
153                     errors.append("Existing person {2} already has role {0}"
154                                   " in event {1}".format(role, event, person))
155 
156         if (event and not role) or (role and not event):
157             errors.append("Must have both or either of event ({0}) and role"
158                           " ({1})".format(event, role))
159 
160         if errors:
161             errors_occur = True
162             item['errors'] = errors
163 
164     return errors_occur
165 
166 
167 def create_uploaded_persons_tasks(data):
168     """
169     Create persons and tasks from upload data.
170     """
171 
172     # Quick sanity check.
173     if any([row.get('errors') for row in data]):
174         raise InternalError('Uploaded data contains errors, cancelling upload')
175 
176     persons_created = []
177     tasks_created = []
178     with transaction.atomic():
179         for row in data:
180             try:
181                 fields = {key: row[key] for key in Person.PERSON_UPLOAD_FIELDS}
182                 fields['username'] = create_username(row['personal'],
183                                                      row['family'])
184                 if fields['email']:
185                     # we should use existing Person or create one
186                     p, created = Person.objects.get_or_create(
187                         email=fields['email'], defaults=fields
188                     )
189 
190                     if created:
191                         persons_created.append(p)
192 
193                 else:
194                     # we should create a new Person without any email provided
195                     p = Person(**fields)
196                     p.save()
197                     persons_created.append(p)
198 
199                 if row['event'] and row['role']:
200                     e = Event.objects.get(slug=row['event'])
201                     r = Role.objects.get(name=row['role'])
202                     t = Task(person=p, event=e, role=r)
203                     t.save()
204                     tasks_created.append(t)
205 
206             except IntegrityError as e:
207                 raise IntegrityError('{0} (for {1})'.format(str(e), row))
208 
209             except ObjectDoesNotExist as e:
210                 raise ObjectDoesNotExist('{0} (for {1})'.format(str(e), row))
211 
212     return persons_created, tasks_created
213 
214 
215 def create_username(personal, family):
216     '''Generate unique username.'''
217     stem = normalize_name(family) + '.' + normalize_name(personal)
218     counter = None
219     while True:
220         try:
221             if counter is None:
222                 username = stem
223                 counter = 1
224             else:
225                 counter += 1
226                 username = '{0}.{1}'.format(stem, counter)
227             Person.objects.get(username=username)
228         except ObjectDoesNotExist:
229             break
230 
231     if any([ord(c) >= 128 for c in username]):
232         raise InternalError('Normalized username still contains non-normal '
233                             'characters "{0}"'.format(username))
234 
235     return username
236 
237 
238 def normalize_name(name):
239     '''Get rid of spaces, funky characters, etc.'''
240     name = name.strip()
241     for (accented, flat) in [(' ', '-')]:
242         name = name.replace(accented, flat)
243 
244     # We should use lower-cased username, because it directly corresponds to
245     # some files Software Carpentry stores about some people - and, as we know,
246     # some filesystems are not case-sensitive.
247     return name.lower()
248
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/workshops/util.py b/workshops/util.py
--- a/workshops/util.py
+++ b/workshops/util.py
@@ -62,9 +62,12 @@
     for row in reader:
         entry = {}
         for col in Person.PERSON_UPLOAD_FIELDS:
-            if col in row:
+            try:
                 entry[col] = row[col].strip()
-            else:
+            except (KeyError, IndexError, AttributeError):
+                # either `col` is not in `entry`, or not in `row`, or
+                # `.strip()` doesn't work (e.g. `row[col]` gives `None` instead
+                # of string)
                 entry[col] = None
                 empty_fields.add(col)
 
@@ -139,20 +142,9 @@
 
         if person:
             if not any([event, role]):
-                errors.append("User exists but no event and role to assign"
+                errors.append("User exists but no event and role to assign to"
                               " the user to was provided")
 
-            else:
-                # check for duplicate Task
-                try:
-                    Task.objects.get(event__slug=event, role__name=role,
-                                     person=person)
-                except Task.DoesNotExist:
-                    pass
-                else:
-                    errors.append("Existing person {2} already has role {0}"
-                                  " in event {1}".format(role, event, person))
-
         if (event and not role) or (role and not event):
             errors.append("Must have both or either of event ({0}) and role"
                           " ({1})".format(event, role))
@@ -199,9 +191,10 @@
                 if row['event'] and row['role']:
                     e = Event.objects.get(slug=row['event'])
                     r = Role.objects.get(name=row['role'])
-                    t = Task(person=p, event=e, role=r)
-                    t.save()
-                    tasks_created.append(t)
+                    t, created = Task.objects.get_or_create(person=p, event=e,
+                                                            role=r)
+                    if created:
+                        tasks_created.append(t)
 
             except IntegrityError as e:
                 raise IntegrityError('{0} (for {1})'.format(str(e), row))

{"golden_diff": "diff --git a/workshops/util.py b/workshops/util.py\n--- a/workshops/util.py\n+++ b/workshops/util.py\n@@ -62,9 +62,12 @@\n     for row in reader:\n         entry = {}\n         for col in Person.PERSON_UPLOAD_FIELDS:\n-            if col in row:\n+            try:\n                 entry[col] = row[col].strip()\n-            else:\n+            except (KeyError, IndexError, AttributeError):\n+                # either `col` is not in `entry`, or not in `row`, or\n+                # `.strip()` doesn't work (e.g. `row[col]` gives `None` instead\n+                # of string)\n                 entry[col] = None\n                 empty_fields.add(col)\n \n@@ -139,20 +142,9 @@\n \n         if person:\n             if not any([event, role]):\n-                errors.append(\"User exists but no event and role to assign\"\n+                errors.append(\"User exists but no event and role to assign to\"\n                               \" the user to was provided\")\n \n-            else:\n-                # check for duplicate Task\n-                try:\n-                    Task.objects.get(event__slug=event, role__name=role,\n-                                     person=person)\n-                except Task.DoesNotExist:\n-                    pass\n-                else:\n-                    errors.append(\"Existing person {2} already has role {0}\"\n-                                  \" in event {1}\".format(role, event, person))\n-\n         if (event and not role) or (role and not event):\n             errors.append(\"Must have both or either of event ({0}) and role\"\n                           \" ({1})\".format(event, role))\n@@ -199,9 +191,10 @@\n                 if row['event'] and row['role']:\n                     e = Event.objects.get(slug=row['event'])\n                     r = Role.objects.get(name=row['role'])\n-                    t = Task(person=p, event=e, role=r)\n-                    t.save()\n-                    tasks_created.append(t)\n+                    t, created = Task.objects.get_or_create(person=p, event=e,\n+                                                            role=r)\n+                    if created:\n+                        tasks_created.append(t)\n \n             except IntegrityError as e:\n                 raise IntegrityError('{0} (for {1})'.format(str(e), row))\n", "issue": "Bulk-upload: Not checking whether it is a valid csv file\nWhen a file with first line in valid .csv format and if the following lines are not in a valid format, \n\nfor example if the file uploaded is :\n\"\"\"\npersonal,middle,family,email\nThis is a test file\n\"\"\"\n\nThere is no error being displayed for the wrong file uploaded, instead a ticket is being displayed. I think there should be a check to validate csv file before trying to upload the data.\nAm I right?\n![screenshot from 2015-03-17 13 28 34](https://cloud.githubusercontent.com/assets/6889955/6683386/8f7ff0ec-ccab-11e4-97af-4f72e51b9d82.png)\n\n", "before_files": [{"content": "# coding: utf-8\nfrom math import pi, sin, cos, acos\nimport csv\n\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import IntegrityError, transaction\n\nfrom .models import Event, Role, Person, Task\n\n\nclass InternalError(Exception):\n    pass\n\n\ndef earth_distance(pos1, pos2):\n    '''Taken from http://www.johndcook.com/python_longitude_latitude.html.'''\n\n    # Extract fields.\n    lat1, long1 = pos1\n    lat2, long2 = pos2\n\n    # Convert latitude and longitude to spherical coordinates in radians.\n    degrees_to_radians = pi/180.0\n\n    # phi = 90 - latitude\n    phi1 = (90.0 - lat1) * degrees_to_radians\n    phi2 = (90.0 - lat2) * degrees_to_radians\n\n    # theta = longitude\n    theta1 = long1 * degrees_to_radians\n    theta2 = long2 * degrees_to_radians\n\n    # Compute spherical distance from spherical coordinates.\n    # For two locations in spherical coordinates\n    # (1, theta, phi) and (1, theta, phi)\n    # cosine( arc length ) = sin phi sin phi' cos(theta-theta') + cos phi cos phi'\n    # distance = rho * arc length\n    c = sin(phi1) * sin(phi2) * cos(theta1 - theta2) + cos(phi1) * cos(phi2)\n    arc = acos(c)\n\n    # Multiply by 6373 to get distance in km.\n    return arc * 6373\n\n\ndef upload_person_task_csv(stream):\n    \"\"\"Read people from CSV and return a JSON-serializable list of dicts.\n\n    The input `stream` should be a file-like object that returns\n    Unicode data.\n\n    \"Serializability\" is required because we put this data into session.  See\n    https://docs.djangoproject.com/en/1.7/topics/http/sessions/ for details.\n\n    Also return a list of fields from Person.PERSON_UPLOAD_FIELDS for which\n    no data was given.\n    \"\"\"\n\n    result = []\n    reader = csv.DictReader(stream)\n    empty_fields = set()\n\n    for row in reader:\n        entry = {}\n        for col in Person.PERSON_UPLOAD_FIELDS:\n            if col in row:\n                entry[col] = row[col].strip()\n            else:\n                entry[col] = None\n                empty_fields.add(col)\n\n        for col in Person.PERSON_TASK_EXTRA_FIELDS:\n            entry[col] = row.get(col, None)\n        entry['errors'] = None\n\n        result.append(entry)\n\n    return result, list(empty_fields)\n\n\ndef verify_upload_person_task(data):\n    \"\"\"\n    Verify that uploaded data is correct.  Show errors by populating ``errors``\n    dictionary item.  This function changes ``data`` in place.\n    \"\"\"\n\n    errors_occur = False\n    for item in data:\n        errors = []\n\n        event = item.get('event', None)\n        if event:\n            try:\n                Event.objects.get(slug=event)\n            except Event.DoesNotExist:\n                errors.append(u'Event with slug {0} does not exist.'\n                              .format(event))\n\n        role = item.get('role', None)\n        if role:\n            try:\n                Role.objects.get(name=role)\n            except Role.DoesNotExist:\n                errors.append(u'Role with name {0} does not exist.'\n                              .format(role))\n            except Role.MultipleObjectsReturned:\n                errors.append(u'More than one role named {0} exists.'\n                              .format(role))\n\n        # check if the user exists, and if so: check if existing user's\n        # personal and family names are the same as uploaded\n        email = item.get('email', None)\n        personal = item.get('personal', None)\n        middle = item.get('middle', None)\n        family = item.get('family', None)\n        person = None\n        if email:\n            # we don't have to check if the user exists in the database\n            # but we should check if, in case the email matches, family and\n            # personal names match, too\n\n            try:\n                person = Person.objects.get(email__iexact=email)\n\n                assert person.personal == personal\n                assert person.middle == middle\n                assert person.family == family\n\n            except Person.DoesNotExist:\n                # in this case we need to add the user\n                pass\n\n            except AssertionError:\n                errors.append(\n                    \"Personal, middle or family name of existing user don't\"\n                    \" match: {0} vs {1}, {2} vs {3}, {4} vs {5}\"\n                    .format(personal, person.personal, middle, person.middle,\n                            family, person.family)\n                )\n\n        if person:\n            if not any([event, role]):\n                errors.append(\"User exists but no event and role to assign\"\n                              \" the user to was provided\")\n\n            else:\n                # check for duplicate Task\n                try:\n                    Task.objects.get(event__slug=event, role__name=role,\n                                     person=person)\n                except Task.DoesNotExist:\n                    pass\n                else:\n                    errors.append(\"Existing person {2} already has role {0}\"\n                                  \" in event {1}\".format(role, event, person))\n\n        if (event and not role) or (role and not event):\n            errors.append(\"Must have both or either of event ({0}) and role\"\n                          \" ({1})\".format(event, role))\n\n        if errors:\n            errors_occur = True\n            item['errors'] = errors\n\n    return errors_occur\n\n\ndef create_uploaded_persons_tasks(data):\n    \"\"\"\n    Create persons and tasks from upload data.\n    \"\"\"\n\n    # Quick sanity check.\n    if any([row.get('errors') for row in data]):\n        raise InternalError('Uploaded data contains errors, cancelling upload')\n\n    persons_created = []\n    tasks_created = []\n    with transaction.atomic():\n        for row in data:\n            try:\n                fields = {key: row[key] for key in Person.PERSON_UPLOAD_FIELDS}\n                fields['username'] = create_username(row['personal'],\n                                                     row['family'])\n                if fields['email']:\n                    # we should use existing Person or create one\n                    p, created = Person.objects.get_or_create(\n                        email=fields['email'], defaults=fields\n                    )\n\n                    if created:\n                        persons_created.append(p)\n\n                else:\n                    # we should create a new Person without any email provided\n                    p = Person(**fields)\n                    p.save()\n                    persons_created.append(p)\n\n                if row['event'] and row['role']:\n                    e = Event.objects.get(slug=row['event'])\n                    r = Role.objects.get(name=row['role'])\n                    t = Task(person=p, event=e, role=r)\n                    t.save()\n                    tasks_created.append(t)\n\n            except IntegrityError as e:\n                raise IntegrityError('{0} (for {1})'.format(str(e), row))\n\n            except ObjectDoesNotExist as e:\n                raise ObjectDoesNotExist('{0} (for {1})'.format(str(e), row))\n\n    return persons_created, tasks_created\n\n\ndef create_username(personal, family):\n    '''Generate unique username.'''\n    stem = normalize_name(family) + '.' + normalize_name(personal)\n    counter = None\n    while True:\n        try:\n            if counter is None:\n                username = stem\n                counter = 1\n            else:\n                counter += 1\n                username = '{0}.{1}'.format(stem, counter)\n            Person.objects.get(username=username)\n        except ObjectDoesNotExist:\n            break\n\n    if any([ord(c) >= 128 for c in username]):\n        raise InternalError('Normalized username still contains non-normal '\n                            'characters \"{0}\"'.format(username))\n\n    return username\n\n\ndef normalize_name(name):\n    '''Get rid of spaces, funky characters, etc.'''\n    name = name.strip()\n    for (accented, flat) in [(' ', '-')]:\n        name = name.replace(accented, flat)\n\n    # We should use lower-cased username, because it directly corresponds to\n    # some files Software Carpentry stores about some people - and, as we know,\n    # some filesystems are not case-sensitive.\n    return name.lower()\n", "path": "workshops/util.py"}], "after_files": [{"content": "# coding: utf-8\nfrom math import pi, sin, cos, acos\nimport csv\n\nfrom django.core.exceptions import ObjectDoesNotExist\nfrom django.db import IntegrityError, transaction\n\nfrom .models import Event, Role, Person, Task\n\n\nclass InternalError(Exception):\n    pass\n\n\ndef earth_distance(pos1, pos2):\n    '''Taken from http://www.johndcook.com/python_longitude_latitude.html.'''\n\n    # Extract fields.\n    lat1, long1 = pos1\n    lat2, long2 = pos2\n\n    # Convert latitude and longitude to spherical coordinates in radians.\n    degrees_to_radians = pi/180.0\n\n    # phi = 90 - latitude\n    phi1 = (90.0 - lat1) * degrees_to_radians\n    phi2 = (90.0 - lat2) * degrees_to_radians\n\n    # theta = longitude\n    theta1 = long1 * degrees_to_radians\n    theta2 = long2 * degrees_to_radians\n\n    # Compute spherical distance from spherical coordinates.\n    # For two locations in spherical coordinates\n    # (1, theta, phi) and (1, theta, phi)\n    # cosine( arc length ) = sin phi sin phi' cos(theta-theta') + cos phi cos phi'\n    # distance = rho * arc length\n    c = sin(phi1) * sin(phi2) * cos(theta1 - theta2) + cos(phi1) * cos(phi2)\n    arc = acos(c)\n\n    # Multiply by 6373 to get distance in km.\n    return arc * 6373\n\n\ndef upload_person_task_csv(stream):\n    \"\"\"Read people from CSV and return a JSON-serializable list of dicts.\n\n    The input `stream` should be a file-like object that returns\n    Unicode data.\n\n    \"Serializability\" is required because we put this data into session.  See\n    https://docs.djangoproject.com/en/1.7/topics/http/sessions/ for details.\n\n    Also return a list of fields from Person.PERSON_UPLOAD_FIELDS for which\n    no data was given.\n    \"\"\"\n\n    result = []\n    reader = csv.DictReader(stream)\n    empty_fields = set()\n\n    for row in reader:\n        entry = {}\n        for col in Person.PERSON_UPLOAD_FIELDS:\n            try:\n                entry[col] = row[col].strip()\n            except (KeyError, IndexError, AttributeError):\n                # either `col` is not in `entry`, or not in `row`, or\n                # `.strip()` doesn't work (e.g. `row[col]` gives `None` instead\n                # of string)\n                entry[col] = None\n                empty_fields.add(col)\n\n        for col in Person.PERSON_TASK_EXTRA_FIELDS:\n            entry[col] = row.get(col, None)\n        entry['errors'] = None\n\n        result.append(entry)\n\n    return result, list(empty_fields)\n\n\ndef verify_upload_person_task(data):\n    \"\"\"\n    Verify that uploaded data is correct.  Show errors by populating ``errors``\n    dictionary item.  This function changes ``data`` in place.\n    \"\"\"\n\n    errors_occur = False\n    for item in data:\n        errors = []\n\n        event = item.get('event', None)\n        if event:\n            try:\n                Event.objects.get(slug=event)\n            except Event.DoesNotExist:\n                errors.append(u'Event with slug {0} does not exist.'\n                              .format(event))\n\n        role = item.get('role', None)\n        if role:\n            try:\n                Role.objects.get(name=role)\n            except Role.DoesNotExist:\n                errors.append(u'Role with name {0} does not exist.'\n                              .format(role))\n            except Role.MultipleObjectsReturned:\n                errors.append(u'More than one role named {0} exists.'\n                              .format(role))\n\n        # check if the user exists, and if so: check if existing user's\n        # personal and family names are the same as uploaded\n        email = item.get('email', None)\n        personal = item.get('personal', None)\n        middle = item.get('middle', None)\n        family = item.get('family', None)\n        person = None\n        if email:\n            # we don't have to check if the user exists in the database\n            # but we should check if, in case the email matches, family and\n            # personal names match, too\n\n            try:\n                person = Person.objects.get(email__iexact=email)\n\n                assert person.personal == personal\n                assert person.middle == middle\n                assert person.family == family\n\n            except Person.DoesNotExist:\n                # in this case we need to add the user\n                pass\n\n            except AssertionError:\n                errors.append(\n                    \"Personal, middle or family name of existing user don't\"\n                    \" match: {0} vs {1}, {2} vs {3}, {4} vs {5}\"\n                    .format(personal, person.personal, middle, person.middle,\n                            family, person.family)\n                )\n\n        if person:\n            if not any([event, role]):\n                errors.append(\"User exists but no event and role to assign to\"\n                              \" the user to was provided\")\n\n        if (event and not role) or (role and not event):\n            errors.append(\"Must have both or either of event ({0}) and role\"\n                          \" ({1})\".format(event, role))\n\n        if errors:\n            errors_occur = True\n            item['errors'] = errors\n\n    return errors_occur\n\n\ndef create_uploaded_persons_tasks(data):\n    \"\"\"\n    Create persons and tasks from upload data.\n    \"\"\"\n\n    # Quick sanity check.\n    if any([row.get('errors') for row in data]):\n        raise InternalError('Uploaded data contains errors, cancelling upload')\n\n    persons_created = []\n    tasks_created = []\n    with transaction.atomic():\n        for row in data:\n            try:\n                fields = {key: row[key] for key in Person.PERSON_UPLOAD_FIELDS}\n                fields['username'] = create_username(row['personal'],\n                                                     row['family'])\n                if fields['email']:\n                    # we should use existing Person or create one\n                    p, created = Person.objects.get_or_create(\n                        email=fields['email'], defaults=fields\n                    )\n\n                    if created:\n                        persons_created.append(p)\n\n                else:\n                    # we should create a new Person without any email provided\n                    p = Person(**fields)\n                    p.save()\n                    persons_created.append(p)\n\n                if row['event'] and row['role']:\n                    e = Event.objects.get(slug=row['event'])\n                    r = Role.objects.get(name=row['role'])\n                    t, created = Task.objects.get_or_create(person=p, event=e,\n                                                            role=r)\n                    if created:\n                        tasks_created.append(t)\n\n            except IntegrityError as e:\n                raise IntegrityError('{0} (for {1})'.format(str(e), row))\n\n            except ObjectDoesNotExist as e:\n                raise ObjectDoesNotExist('{0} (for {1})'.format(str(e), row))\n\n    return persons_created, tasks_created\n\n\ndef create_username(personal, family):\n    '''Generate unique username.'''\n    stem = normalize_name(family) + '.' + normalize_name(personal)\n    counter = None\n    while True:\n        try:\n            if counter is None:\n                username = stem\n                counter = 1\n            else:\n                counter += 1\n                username = '{0}.{1}'.format(stem, counter)\n            Person.objects.get(username=username)\n        except ObjectDoesNotExist:\n            break\n\n    if any([ord(c) >= 128 for c in username]):\n        raise InternalError('Normalized username still contains non-normal '\n                            'characters \"{0}\"'.format(username))\n\n    return username\n\n\ndef normalize_name(name):\n    '''Get rid of spaces, funky characters, etc.'''\n    name = name.strip()\n    for (accented, flat) in [(' ', '-')]:\n        name = name.replace(accented, flat)\n\n    # We should use lower-cased username, because it directly corresponds to\n    # some files Software Carpentry stores about some people - and, as we know,\n    # some filesystems are not case-sensitive.\n    return name.lower()\n", "path": "workshops/util.py"}]}