def test_matched_similar_persons(self): """Ensure function finds matching persons.""" data = [ { 'personal': 'Harry', 'family': 'Potter', 'username': '******', 'email': '*****@*****.**', 'event': '', 'role': '', }, { 'personal': 'Romuald', 'family': 'Weazel', 'username': '', 'email': '*****@*****.**', 'event': '', 'role': '', } ] verify_upload_person_task(data) self.assertEqual(len(data[0]['similar_persons']), 1) self.assertEqual(len(data[1]['similar_persons']), 1) self.assertEqual(data[0]['similar_persons'][0][0], self.harry.pk) self.assertEqual(data[1]['similar_persons'][0][0], self.ron.pk)
def test_matched_similar_persons(self): """Ensure function finds matching persons.""" data = [ { "personal": "Harry", "family": "Potter", "username": "******", "email": "*****@*****.**", "event": "", "role": "", }, { "personal": "Romuald", "family": "Weazel", "username": "", "email": "*****@*****.**", "event": "", "role": "", }, ] verify_upload_person_task(data) self.assertEqual(len(data[0]["similar_persons"]), 1) self.assertEqual(len(data[1]["similar_persons"]), 1) self.assertEqual(data[0]["similar_persons"][0][0], self.harry.pk) self.assertEqual(data[1]["similar_persons"][0][0], self.ron.pk)
def test_duplicate_errors(self): """Ensure errors about duplicate person in the database are present.""" data = self.make_data() data[0]["personal"] = "Harry" data[0]["family"] = "Potter" data[0]["email"] = "*****@*****.**" verify_upload_person_task(data) self.assertEqual(len(data[0]["errors"]), 2) self.assertIn( "Person with this email address already exists.", data[0]["errors"] ) self.assertIn("Person with this username already exists.", data[0]["errors"])
def test_duplicate_errors(self): """Ensure errors about duplicate person in the database are present.""" data = self.make_data() data[0]['personal'] = 'Harry' data[0]['family'] = 'Potter' data[0]['email'] = '*****@*****.**' verify_upload_person_task(data) self.assertEqual(len(data[0]['errors']), 2) self.assertIn('Person with this email address already exists.', data[0]['errors']) self.assertIn('Person with this username already exists.', data[0]['errors'])
def test_duplicate_errors(self): """Ensure errors about duplicate person in the database are present.""" data = self.make_data() data[0]['personal'] = 'Harry' data[0]['family'] = 'Potter' data[0]['email'] = '*****@*****.**' verify_upload_person_task(data) self.assertEqual(len(data[0]['errors']), 2) self.assertIn('Person with this email address already exists.', data[0]['errors']) self.assertIn('Person with this username already exists.', data[0]['errors'])
def test_username_from_nonexisting_person(self): """Make sure the username is not being changed.""" data = [{ 'personal': 'Harry', 'family': 'Frotter', 'username': '******', 'email': '*****@*****.**', 'event': '', 'role': '', 'existing_person_id': None, }] verify_upload_person_task(data) self.assertEqual('supplied_username', data[0]['username'])
def test_username_from_nonexisting_person(self): """Make sure the username is not being changed.""" data = [ { 'personal': 'Harry', 'family': 'Frotter', 'username': '******', 'email': '*****@*****.**', 'event': '', 'role': '', 'existing_person_id': None, } ] verify_upload_person_task(data) self.assertEqual('supplied_username', data[0]['username'])
def test_username_from_nonexisting_person(self): """Make sure the username is not being changed.""" data = [ { "personal": "Harry", "family": "Frotter", "username": "******", "email": "*****@*****.**", "event": "", "role": "", "existing_person_id": None, } ] verify_upload_person_task(data) self.assertEqual("supplied_username", data[0]["username"])
def test_username_from_existing_person(self): """Make sure the username is being changed for correct one.""" data = [ { "personal": "Harry", "family": "Potter", "username": "******", "email": "*****@*****.**", "event": "", "role": "", "existing_person_id": Person.objects.get(email="*****@*****.**").pk, } ] verify_upload_person_task(data) self.assertEqual("potter_harry", data[0]["username"])
def test_verify_with_good_data(self): good_data = self.make_data() has_errors = verify_upload_person_task(good_data, match=True) self.assertFalse(has_errors) # make sure 'errors' wasn't set # 'errors' may be an empty list, which evaluates to False self.assertFalse(good_data[0]['errors'])
def test_verify_with_good_data(self): good_data = self.make_data() has_errors = verify_upload_person_task(good_data, match=True) self.assertFalse(has_errors) # make sure 'errors' wasn't set # 'errors' may be an empty list, which evaluates to False self.assertFalse(good_data[0]['errors'])
def test_username_from_existing_person(self): """Make sure the username is being changed for correct one.""" data = [ { 'personal': 'Harry', 'family': 'Potter', 'username': '******', 'email': '*****@*****.**', 'event': '', 'role': '', 'existing_person_id': Person.objects .get(email='*****@*****.**') .pk, } ] verify_upload_person_task(data) self.assertEqual('potter_harry', data[0]['username'])
def test_verify_event_doesnt_exist(self): bad_data = self.make_data() bad_data[0]['event'] = 'no-such-event' has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]['errors'] self.assertEqual(len(errors), 1) self.assertTrue('Event with slug' in errors[0])
def test_verify_event_doesnt_exist(self): bad_data = self.make_data() bad_data[0]['event'] = 'no-such-event' has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]['errors'] self.assertEqual(len(errors), 1) self.assertTrue('Event with slug' in errors[0])
def test_verify_event_doesnt_exist(self): bad_data = self.make_data() bad_data[0]["event"] = "no-such-event" has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]["errors"] self.assertEqual(len(errors), 1) self.assertTrue("Event with slug" in errors[0])
def test_verify_role_doesnt_exist(self): bad_data = self.make_data() bad_data[0]['role'] = 'foobar' has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]['errors'] self.assertTrue(len(errors) == 1) self.assertTrue('Role with name' in errors[0])
def test_verify_email_caseinsensitive_matches(self): bad_data = self.make_data() # test both matching and case-insensitive matching for email in ("*****@*****.**", "*****@*****.**"): bad_data[0]["email"] = email bad_data[0]["personal"] = "Harry" bad_data[0]["family"] = "Potter" has_errors = verify_upload_person_task(bad_data, match=True) self.assertFalse(has_errors, "Bad email: {}".format(email))
def test_verify_role_doesnt_exist(self): bad_data = self.make_data() bad_data[0]['role'] = 'foobar' has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]['errors'] self.assertTrue(len(errors) == 1) self.assertTrue('Role with name' in errors[0])
def test_verify_email_caseinsensitive_matches(self): bad_data = self.make_data() # test both matching and case-insensitive matching for email in ('*****@*****.**', '*****@*****.**'): bad_data[0]['email'] = email bad_data[0]['personal'] = 'Harry' bad_data[0]['family'] = 'Potter' has_errors = verify_upload_person_task(bad_data, match=True) self.assertFalse(has_errors, 'Bad email: {}'.format(email))
def test_verify_email_caseinsensitive_matches(self): bad_data = self.make_data() # test both matching and case-insensitive matching for email in ('*****@*****.**', '*****@*****.**'): bad_data[0]['email'] = email bad_data[0]['personal'] = 'Harry' bad_data[0]['family'] = 'Potter' has_errors = verify_upload_person_task(bad_data, match=True) self.assertFalse(has_errors, 'Bad email: {}'.format(email))
def test_verify_role_doesnt_exist(self): bad_data = self.make_data() bad_data[0]["role"] = "foobar" has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]["errors"] self.assertTrue(len(errors) == 1) self.assertTrue("Role with name" in errors[0])
def test_email_missing(self): """This tests against regression in: https://github.com/swcarpentry/amy/issues/1394 The issue: entries without emails (email=None) were caught in `Person.objects.get(email=email)`, which returned multiple objects (because we have many users with empty emails, and it doesn't violate UNIQUE constraint.""" # make existing users loose their emails usernames = ["potter_harry", "granger_hermione", "weasley_ron"] Person.objects.filter(username__in=usernames).update(email=None) bad_data = [ { "email": None, "personal": "Harry", "family": "Potter", "event": "foobar", "role": "learner", }, { "email": None, "personal": "Hermione", "family": "Granger", "event": "foobar", "role": "learner", }, { "email": None, "personal": "Ron", "family": "Weasley", "event": "foobar", "role": "learner", }, ] # test for first occurrence of the error has_errors = verify_upload_person_task(bad_data, match=True) self.assertFalse(has_errors) # test for second occurrence of the error has_errors = verify_upload_person_task(bad_data, match=False) self.assertFalse(has_errors)
def test_email_missing(self): """This tests against regression in: https://github.com/swcarpentry/amy/issues/1394 The issue: entries without emails (email=None) were caught in `Person.objects.get(email=email)`, which returned multiple objects (because we have many users with empty emails, and it doesn't violate UNIQUE constraint.""" # make existing users loose their emails usernames = ['potter_harry', 'granger_hermione', 'weasley_ron'] Person.objects.filter(username__in=usernames).update(email=None) bad_data = [ { 'email': None, 'personal': 'Harry', 'family': 'Potter', 'event': 'foobar', 'role': 'learner' }, { 'email': None, 'personal': 'Hermione', 'family': 'Granger', 'event': 'foobar', 'role': 'learner' }, { 'email': None, 'personal': 'Ron', 'family': 'Weasley', 'event': 'foobar', 'role': 'learner' }, ] # test for first occurrence of the error has_errors = verify_upload_person_task(bad_data, match=True) self.assertFalse(has_errors) # test for second occurrence of the error has_errors = verify_upload_person_task(bad_data, match=False) self.assertFalse(has_errors)
def test_email_missing(self): """This tests against regression in: https://github.com/swcarpentry/amy/issues/1394 The issue: entries without emails (email=None) were caught in `Person.objects.get(email=email)`, which returned multiple objects (because we have many users with empty emails, and it doesn't violate UNIQUE constraint.""" # make existing users loose their emails usernames = ['potter_harry', 'granger_hermione', 'weasley_ron'] Person.objects.filter(username__in=usernames).update(email=None) bad_data = [ { 'email': None, 'personal': 'Harry', 'family': 'Potter', 'event': 'foobar', 'role': 'learner' }, { 'email': None, 'personal': 'Hermione', 'family': 'Granger', 'event': 'foobar', 'role': 'learner' }, { 'email': None, 'personal': 'Ron', 'family': 'Weasley', 'event': 'foobar', 'role': 'learner' }, ] # test for first occurrence of the error has_errors = verify_upload_person_task(bad_data, match=True) self.assertFalse(has_errors) # test for second occurrence of the error has_errors = verify_upload_person_task(bad_data, match=False) self.assertFalse(has_errors)
def test_verify_existing_user_has_workshop_role_provided(self): bad_data = [{ 'email': '*****@*****.**', 'personal': 'Harry', 'family': 'Potter', 'event': '', 'role': '', }] has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]['errors'] self.assertEqual(len(errors), 2) self.assertIn('Must have a role', errors[0]) self.assertIn('Must have an event', errors[1])
def test_verify_existing_user_has_workshop_role_provided(self): bad_data = [ { "email": "*****@*****.**", "personal": "Harry", "family": "Potter", "event": "", "role": "", } ] has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]["errors"] self.assertEqual(len(errors), 2) self.assertIn("Must have a role", errors[0]) self.assertIn("Must have an event", errors[1])
def test_verify_existing_user_has_workshop_role_provided(self): bad_data = [ { 'email': '*****@*****.**', 'personal': 'Harry', 'family': 'Potter', 'event': '', 'role': '', } ] has_errors = verify_upload_person_task(bad_data, match=True) self.assertTrue(has_errors) errors = bad_data[0]['errors'] self.assertEqual(len(errors), 2) self.assertIn('Must have a role', errors[0]) self.assertIn('Must have an event', errors[1])
def person_bulk_add_confirmation(request): """ This view allows for manipulating and saving session-stored upload data. """ persons_tasks = request.session.get('bulk-add-people') # if the session is empty, add message and redirect if not persons_tasks: messages.warning(request, "Could not locate CSV data, please try the upload again.") return redirect('person_bulk_add') if request.method == 'POST': # update values if user wants to change them personals = request.POST.getlist("personal") middles = request.POST.getlist("middle") families = request.POST.getlist("family") emails = request.POST.getlist("email") events = request.POST.getlist("event") roles = request.POST.getlist("role") data_update = zip(personals, middles, families, emails, events, roles) for k, record in enumerate(data_update): personal, middle, family, email, event, role = record # "field or None" converts empty strings to None values persons_tasks[k] = { 'personal': personal, 'middle': middle or None, 'family': family, 'email': email or None } # when user wants to drop related event they will send empty string # so we should unconditionally accept new value for event even if # it's an empty string persons_tasks[k]['event'] = event persons_tasks[k]['role'] = role persons_tasks[k]['errors'] = None # reset here # save updated data to the session request.session['bulk-add-people'] = persons_tasks # check if user wants to verify or save, or cancel if request.POST.get('verify', None): # if there's "verify" in POST, then do only verification any_errors = verify_upload_person_task(persons_tasks) if any_errors: messages.add_message(request, messages.ERROR, "Please make sure to fix all errors " "listed below.") context = {'title': 'Confirm uploaded data', 'persons_tasks': persons_tasks} return render(request, 'workshops/person_bulk_add_results.html', context) # there must be "confirm" and no "cancel" in POST in order to save elif (request.POST.get('confirm', None) and not request.POST.get('cancel', None)): try: # verification now makes something more than database # constraints so we should call it first verify_upload_person_task(persons_tasks) persons_created, tasks_created = \ create_uploaded_persons_tasks(persons_tasks) except (IntegrityError, ObjectDoesNotExist, InternalError) as e: messages.add_message(request, messages.ERROR, "Error saving data to the database: {}. " "Please make sure to fix all errors " "listed below.".format(e)) verify_upload_person_task(persons_tasks) context = {'title': 'Confirm uploaded data', 'persons_tasks': persons_tasks} return render(request, 'workshops/person_bulk_add_results.html', context, status=400) else: request.session['bulk-add-people'] = None messages.add_message(request, messages.SUCCESS, "Successfully uploaded {0} persons and {1} tasks." .format(len(persons_created), len(tasks_created))) return redirect('person_bulk_add') else: # any "cancel" or no "confirm" in POST cancels the upload request.session['bulk-add-people'] = None return redirect('person_bulk_add') else: # alters persons_tasks via reference verify_upload_person_task(persons_tasks) context = {'title': 'Confirm uploaded data', 'persons_tasks': persons_tasks} return render(request, 'workshops/person_bulk_add_results.html', context)
def person_bulk_add_confirmation(request): """ This view allows for manipulating and saving session-stored upload data. """ persons_tasks = request.session.get('bulk-add-people') # if the session is empty, add message and redirect if not persons_tasks: messages.warning( request, "Could not locate CSV data, please try the upload again.") return redirect('person_bulk_add') if request.method == 'POST': # update values if user wants to change them personals = request.POST.getlist("personal") middles = request.POST.getlist("middle") families = request.POST.getlist("family") emails = request.POST.getlist("email") events = request.POST.getlist("event") roles = request.POST.getlist("role") data_update = zip(personals, middles, families, emails, events, roles) for k, record in enumerate(data_update): personal, middle, family, email, event, role = record persons_tasks[k]['person'] = { 'personal': personal, 'middle': middle, 'family': family, 'email': email } # when user wants to drop related event they will send empty string # so we should unconditionally accept new value for event even if # it's an empty string persons_tasks[k]['event'] = event persons_tasks[k]['role'] = role persons_tasks[k]['errors'] = None # reset here # save updated data to the session request.session['bulk-add-people'] = persons_tasks # check if user wants to verify or save, or cancel if request.POST.get('verify', None): # if there's "verify" in POST, then do only verification any_errors = verify_upload_person_task(persons_tasks) if any_errors: messages.add_message( request, messages.ERROR, "Please make sure to fix all errors " "listed below.") context = { 'title': 'Confirm uploaded data', 'persons_tasks': persons_tasks } return render(request, 'workshops/person_bulk_add_results.html', context) elif (request.POST.get('confirm', None) and not request.POST.get('cancel', None)): # there must be "confirm" and no "cancel" in POST in order to save try: records = 0 with transaction.atomic(): for row in persons_tasks: # create person p = Person(**row['person']) p.save() records += 1 # create task if data supplied 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() records += 1 except (IntegrityError, ObjectDoesNotExist) as e: messages.add_message( request, messages.ERROR, "Error saving data to the database: {}. " "Please make sure to fix all errors " "listed below.".format(e)) verify_upload_person_task(persons_tasks) context = { 'title': 'Confirm uploaded data', 'persons_tasks': persons_tasks } return render(request, 'workshops/person_bulk_add_results.html', context) else: request.session['bulk-add-people'] = None messages.add_message( request, messages.SUCCESS, "Successfully bulk-loaded {} records.".format(records)) return redirect('person_bulk_add') else: # any "cancel" or no "confirm" in POST cancels the upload request.session['bulk-add-people'] = None return redirect('person_bulk_add') else: # alters persons_tasks via reference verify_upload_person_task(persons_tasks) context = { 'title': 'Confirm uploaded data', 'persons_tasks': persons_tasks } return render(request, 'workshops/person_bulk_add_results.html', context)