def setUp(self): super().setUp() self.cmd = Command()
class TestUpgradeInstructorProfile(TestBase): def setUp(self): super().setUp() self.cmd = Command() def test_domains_translation(self): "Make sure translating domains from string to list works as expected." TEST = [ ( 'Organismal biology (ecology, botany, zoology, microbiology), ' 'Genetics, genomics, bioinformatics, Computer science/' 'electrical engineering', [ 'Organismal biology (ecology, botany, zoology, ' 'microbiology)', 'Genetics, genomics, bioinformatics', 'Computer science/electrical engineering', ] ), ('Space sciences, Physics', ['Space sciences', 'Physics']), ('Chemistry', ['Chemistry']), # a case of wrong input (it's possible because instructors can put # additional domains): ('Neuroscience/Neuroimaging', ['Neuroscience/', 'Neuroimaging']), ('', []), ] for input, expected in TEST: assert self.cmd.translate_domains(input) == expected def test_lessons_translation(self): "Make sure translating lessons from string to list works as expected." TEST = [ ( 'Git (e.g., http://swcarpentry.github.io/git-novice), Make,' ' nltk', [ 'swc/git', 'swc/make', 'nltk', ] ), ( 'The Unix Shell (e.g., http://swcarpentry.github.io/shell-' 'novice), Git (e.g., http://swcarpentry.github.io/git-novice),' ' Mercurial (e.g., http://swcarpentry.github.io/hg-novice), ' 'Databases and SQL (e.g., http://swcarpentry.github.io/sql-' 'novice-survey), Programming with Python (e.g., ' 'http://swcarpentry.github.io/python-novice-inflammation), ' 'Programming with R (e.g., http://swcarpentry.github.io/r-' 'novice-inflammation)', [ 'swc/shell', 'swc/git', 'swc/hg', 'swc/sql', 'swc/python', 'swc/r', ] ), ( 'Data Organization in Spreadsheets (e.g., ' 'http://datacarpentry.github.io/excel-ecology), Data Analysis ' 'and Visualization in R (e.g., http://datacarpentry.github.io' '/R-ecology), Databases and SQL (e.g., ' 'https://github.com/datacarpentry/sql-ecology/blob/gh-pages/' 'sql.md)', [ 'dc/spreadsheets', 'dc/r', 'dc/sql', ] ), ( 'Data Analysis and Visualization in Python (e.g., ' 'http://datacarpentry.github.io/python-ecology), Programming ' 'with R: http://swcarpentry.github.io/r-novice-inflammation', [ 'dc/python', 'swc/r', ] ), ('', []), ] for input, expected in TEST: assert self.cmd.translate_lessons(input) == expected def test_gender_translation(self): """Make sure different genders yield expected output.""" TEST = [ ("Male", "M"), ("Female", "F"), ("Prefer not to say", "U"), ("Genderfluid", "O"), ("", "U"), ] for input, expected in TEST: assert self.cmd.translate_gender(input) == expected def test_entry_translation(self): """Make sure a whole entry is translated correctly.""" entry_original = { 'Timestamp': '5/26/2015 22:31:50', 'Personal (first) name': 'John', 'Family (last) name': 'Smith', 'Email address': '*****@*****.**', 'Nearest major airport': 'FRA', 'GitHub username': '******', 'Twitter username': '******', 'Personal website': 'http://john.smith.com/', 'Gender': 'Prefer not to say', 'Areas of expertise': '', 'Software Carpentry topics you are comfortable teaching': '', 'ORCID ID': '000011112222', 'Data Carpentry lessons you are comfortable teaching': '', 'Affiliation': 'Smiths CO.', 'What is your current occupation/career stage?': 'director' } entry_new = { 'timestamp': '5/26/2015 22:31:50', 'personal': 'John', 'family': 'Smith', 'email': '*****@*****.**', 'airport': 'FRA', 'github': 'johnsmith', 'twitter': 'johnsmith', 'url': 'http://john.smith.com/', 'gender': "U", 'domains': [], 'teaching': [], 'orcid': '000011112222', 'affiliation': 'Smiths CO.', 'position': 'director' } assert self.cmd.translate(entry_original) == entry_new def test_check_entry(self): """Make sure entry is well checked for any discrepancies.""" # check missing fields entry = {} correct, errors, warnings = self.cmd.check_entry(entry) assert not correct assert "Missing fields" in errors[0] # check empty required fields entry = { 'timestamp': '', 'personal': '', 'family': '', 'email': '', 'airport': '', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert not correct assert "Missing fields" not in errors[0] assert "Required field" in errors[0] and "is empty" in errors[0] # check matching person by email entry = { 'timestamp': '', 'personal': 'H', 'family': 'G', 'email': '*****@*****.**', 'airport': 'AAA', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert correct # check matching person by name entry = { 'timestamp': '', 'personal': 'Hermione', 'family': 'Granger', 'email': '*****@*****.**', 'airport': 'AAA', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert correct # check matching >=2 persons by name Person.objects.create( personal='Hermione', middle=None, family='Granger', gender='F', airport=self.airport_0_0, email='*****@*****.**' ) entry = { 'timestamp': '', 'personal': 'Hermione', 'family': 'Granger', 'email': '*****@*****.**', 'airport': 'AAA', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert not correct assert 'There are multiple users with this name' in errors[0] # check non-existing person entry = { 'timestamp': '', 'personal': 'Lord', 'family': 'Voldemort', 'email': '*****@*****.**', 'airport': 'AAA', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert not correct assert 'User with either this email' in errors[0] assert 'or this name' in errors[0] assert 'does not exist' in errors[0] # check non-instructor person entry = { 'timestamp': '', 'personal': 'H', 'family': 'G', 'email': '*****@*****.**', 'airport': 'AAA', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert correct # we want people even though they aren't instructors assert 'This person does not have an instructor badge' in warnings[0] # check non-existing airport entry = { 'timestamp': '', 'personal': 'H', 'family': 'G', 'email': '*****@*****.**', 'airport': 'ABC', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': '', 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert not correct assert 'Airport "ABC" does not exist' in errors[0] # check presence of lessons entry = { 'timestamp': '', 'personal': 'H', 'family': 'G', 'email': '*****@*****.**', 'airport': 'AAA', 'github': '', 'twitter': '', 'url': '', 'gender': '', 'domains': '', 'teaching': ['asd/python', 'swc/git'], 'orcid': '', 'affiliation': '', 'position': '', } correct, errors, warnings = self.cmd.check_entry(entry) assert not correct errors = "".join(errors) assert 'Lesson' in errors assert 'asd/python' in errors assert 'does not exist' in errors assert 'swc/git' not in errors # check correct entry correct_entry = { 'timestamp': '5/26/2015 22:31:50', 'personal': 'Hermione', 'family': 'Granger', 'email': '*****@*****.**', 'airport': 'AAA', 'github': 'hermionegranger', 'twitter': 'hermionegranger', 'url': 'http://hermione.granger.co.uk/', 'gender': "O", 'domains': [], 'teaching': [], 'orcid': '000011112222', 'affiliation': 'Hogwart CO.', 'position': 'undergraduate' } correct, errors, warnings = self.cmd.check_entry(correct_entry) assert correct assert errors == [] def test_update(self): """Make sure entries are indeed updated.""" allowed_fields = ALL_FIELDS assert self.hermione.github != 'hermionegranger' assert self.hermione.lessons.all().count() != 0 correct_entry = { 'timestamp': '5/26/2015 22:31:50', 'personal': 'Hermione', 'family': 'Granger', 'email': '*****@*****.**', 'airport': 'AAA', 'github': 'hermionegranger', 'twitter': 'hermionegranger', 'url': 'http://hermione.granger.co.uk/', 'gender': "O", 'domains': [], 'teaching': [], 'orcid': '000011112222', 'affiliation': 'Hogwart CO.', 'position': 'undergraduate' } correct, errors, warnings = self.cmd.check_entry(correct_entry) assert correct self.cmd.update(correct_entry, allowed_fields=allowed_fields) self.hermione.refresh_from_db() assert self.hermione.github == 'hermionegranger' assert self.hermione.affiliation == 'Hogwart CO.' assert self.hermione.lessons.all().count() == 0 def test_update_no_affiliation(self): """Ensure no affiliation yields correct empty string after update.""" allowed_fields = ALL_FIELDS correct_entry = { 'timestamp': '5/26/2015 22:31:50', 'personal': 'Hermione', 'family': 'Granger', 'email': '*****@*****.**', 'airport': 'AAA', 'github': 'hermionegranger', 'twitter': 'hermionegranger', 'url': 'http://hermione.granger.co.uk/', 'gender': "O", 'domains': [], 'teaching': [], 'orcid': '000011112222', 'affiliation': '', 'position': 'undergraduate' } correct, errors, warnings = self.cmd.check_entry(correct_entry) assert correct self.cmd.update(correct_entry, allowed_fields=allowed_fields) self.hermione.refresh_from_db() assert self.hermione.affiliation == '' def test_update_subset_of_fields(self): """We can update specific fields.""" allowed_fields = ['family', 'affiliation'] correct_entry = { 'timestamp': '5/26/2015 22:31:50', 'personal': 'Hermione-the-Conjurer', 'family': 'Grangerdaughter', 'email': '*****@*****.**', 'airport': 'AAA', 'github': 'hermionegranger', 'twitter': 'hermionegranger', 'url': 'http://hermione.granger.co.uk/', 'gender': "O", 'domains': [], 'teaching': [], 'orcid': '000011112222', 'affiliation': 'Hogwart The School of Wizardry', 'position': 'undergraduate' } correct, errors, warnings = self.cmd.check_entry(correct_entry) assert correct self.cmd.update(correct_entry, allowed_fields=allowed_fields) self.hermione.refresh_from_db() assert self.hermione.affiliation == 'Hogwart The School of Wizardry' assert self.hermione.personal == 'Hermione' assert self.hermione.family == 'Grangerdaughter' def test_process(self): """Make sure the Command works well with CSVs (even ill-formatted).""" # we'll only test immunity to ill-formatted CSVs, not if they succeed # in updating instructors FN = [ 'workshops/test/upgrade_instructor_profiles1.csv', 'workshops/test/upgrade_instructor_profiles2.csv', 'workshops/test/upgrade_instructor_profiles3.csv', ] correct_list = [] for fname in FN: with open(fname, 'r') as f: # A little hack: in "for-else" with generators "else" clause is # always evaluated at StopIteration, ie. when generator runs # out. # This works because our only test files contain at most only # 1 entry. correct = False for entry in self.cmd.process(f): correct, _, _ = self.cmd.check_entry(entry) else: # empty file correct_list.append(correct) assert correct_list == [True, False, False], correct_list def test_output(self): """Make sure the Command works well.""" def stringify_streams(stream1, stream2): stream1.seek(0) stream2.seek(0) return stream1.read(), stream2.read() call_args = [ ( 'workshops/test/upgrade_instructor_profiles1.csv', {'force': False} ), ( 'workshops/test/upgrade_instructor_profiles2.csv', {'force': False} ), ( 'workshops/test/upgrade_instructor_profiles3.csv', {'force': False} ), ( 'workshops/test/upgrade_instructor_profiles4.csv', {'force': True} ), ] # 1st file: all correct positional, options = call_args[0] stdout = StringIO() stderr = StringIO() call_command('upgrade_instructor_profiles', positional, stdout=stdout, stderr=stderr, **options) stdout, stderr = stringify_streams(stdout, stderr) assert 'ERROR' not in stderr assert 'WARNING' not in stdout # 2nd file: no input positional, options = call_args[1] stdout = StringIO() stderr = StringIO() call_command('upgrade_instructor_profiles', positional, stdout=stdout, stderr=stderr, **options) stdout, stderr = stringify_streams(stdout, stderr) assert 'ERROR' not in stderr assert 'WARNING' not in stdout # 3rd file: missing some fields positional, options = call_args[2] stdout = StringIO() stderr = StringIO() call_command('upgrade_instructor_profiles', positional, stdout=stdout, stderr=stderr, **options) stdout, stderr = stringify_streams(stdout, stderr) assert 'row 1' in stderr assert 'ERROR' in stderr # 4th file: first entry correct, second has missing lesson 'ruby' positional, options = call_args[3] stdout = StringIO() stderr = StringIO() call_command('upgrade_instructor_profiles', positional, stdout=stdout, stderr=stderr, **options) stdout, stderr = stringify_streams(stdout, stderr) assert 'row 2' in stderr assert 'ERROR' in stderr assert 'ruby' in stderr