Repository: carpentries/amy

Issue:
Include "Trainer" badge in "Find Workshop Staff"
Right now we can search for the three Instructor badges; would also like to search for Trainer badge.

https://amy.carpentries.org/workshops/workshop_staff/

Note this should be *separate* from the three instructor badges.



Discussion:

Resolving patch:
diff --git a/amy/workshops/filters.py b/amy/workshops/filters.py
--- a/amy/workshops/filters.py
+++ b/amy/workshops/filters.py
@@ -511,6 +511,10 @@ class WorkshopStaffFilter(AMYFilterSet):
         widget=ModelSelect2Multiple(attrs=SIDEBAR_DAL_WIDTH),
         conjoined=False,  # `OR`
     )
+    is_trainer = django_filters.BooleanFilter(
+        widget=widgets.CheckboxInput,
+        method='filter_trainer',
+    )
     languages = django_filters.ModelMultipleChoiceFilter(
         label='Languages',
         queryset=Language.objects.all(),
@@ -544,6 +548,11 @@ def filter_country(self, qs, n, v):
             )
         return qs
 
+    def filter_trainer(self, qs, n, v):
+        if v:
+            return qs.filter(is_trainer__gte=1)
+        return qs
+
     def filter_helper(self, qs, n, v):
         if v:
             return qs.filter(num_helper__gte=1)
diff --git a/amy/workshops/forms.py b/amy/workshops/forms.py
--- a/amy/workshops/forms.py
+++ b/amy/workshops/forms.py
@@ -200,7 +200,7 @@ def continent_list():
     """This has to be as a callable, because otherwise Django evaluates this
     query and, if the database doesn't exist yet (e.g. during Travis-CI
     tests)."""
-    return Continent.objects.values_list('pk', 'name')
+    return [('', '')] + list(Continent.objects.values_list('pk', 'name'))
 
 
 class WorkshopStaffForm(forms.Form):
@@ -253,6 +253,10 @@ class WorkshopStaffForm(forms.Form):
         required=False,
     )
 
+    is_trainer = forms.BooleanField(
+        required=False,
+        label='Has Trainer badge')
+
     GENDER_CHOICES = ((None, '---------'), ) + Person.GENDER_CHOICES
     gender = forms.ChoiceField(choices=GENDER_CHOICES, required=False)
 
@@ -286,6 +290,7 @@ def __init__(self, *args, **kwargs):
                 css_class='card',
             ),
             'badges',
+            'is_trainer',
             HTML('<hr>'),
             'was_helper',
             'was_organizer',
diff --git a/amy/workshops/views.py b/amy/workshops/views.py
--- a/amy/workshops/views.py
+++ b/amy/workshops/views.py
@@ -1415,6 +1415,7 @@ def _workshop_staff_query(lat=None, lng=None):
                 )
             ),
             is_trainee=Count('task', filter=(Q(task__in=trainee_tasks))),
+            is_trainer=Count('badges', filter=(Q(badges__name='trainer'))),
         ).prefetch_related(
             'lessons',
             Prefetch(
@@ -1495,8 +1496,9 @@ def workshop_staff_csv(request):
     people = f.qs
 
     # first row of the CSV output
-    header_row = ('Name', 'Email', 'Instructor badges', 'Taught times',
-                  'Is trainee', 'Airport', 'Country', 'Lessons', 'Affiliation')
+    header_row = ('Name', 'Email', 'Instructor badges', 'Has Trainer badge',
+                  'Taught times', 'Is trainee', 'Airport', 'Country',
+                  'Lessons', 'Affiliation')
 
     # CSV http header
     response = HttpResponse(content_type='text/csv')
@@ -1512,6 +1514,7 @@ def workshop_staff_csv(request):
             " ".join([
                 badge.name for badge in person.instructor_badges
             ]),
+            "yes" if person.is_trainer else "no",
             person.num_taught,
             "yes" if person.is_trainee else "no",
             str(person.airport) if person.airport else "",


Test patch:
diff --git a/amy/workshops/tests/test_workshop_staff_searching.py b/amy/workshops/tests/test_workshop_staff_searching.py
--- a/amy/workshops/tests/test_workshop_staff_searching.py
+++ b/amy/workshops/tests/test_workshop_staff_searching.py
@@ -278,12 +278,11 @@ def test_form_logic(self):
         ]
 
         for form_pass, data in test_vectors:
-            with self.subTest(data=data):
-                params = dict(submit='Submit')
-                params.update(data)
-                rv = self.client.get(self.url, params)
-                form = rv.context['form']
-                self.assertEqual(form.is_valid(), form_pass, form.errors)
+            params = dict(submit='Submit')
+            params.update(data)
+            rv = self.client.get(self.url, params)
+            form = rv.context['form']
+            self.assertEqual(form.is_valid(), form_pass, form.errors)
 
     def test_searching_trainees(self):
         """Make sure finding trainees works. This test additionally checks for
@@ -341,6 +340,17 @@ def test_searching_trainees(self):
             ])
         )
 
+    def test_searching_trainers(self):
+        """Make sure people with Trainer badge are shown correctly."""
+        response = self.client.get(self.url, dict(is_trainer="on"))
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(list(response.context['persons']), [])
+        trainer = Badge.objects.get(name='trainer')
+        self.harry.award_set.create(badge=trainer)
+        response = self.client.get(self.url, dict(is_trainer="on"))
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(list(response.context['persons']), [self.harry])
+
 
 class TestWorkshopStaffCSV(TestBase):
     """Test cases for downloading workshop staff search results as CSV."""
@@ -358,8 +368,8 @@ def test_header_row(self):
         rv = self.client.get(self.url)
         first_row = rv.content.decode('utf-8').splitlines()[0]
         first_row_expected = (
-            "Name,Email,Instructor badges,Taught times,Is trainee,Airport,"
-            "Country,Lessons,Affiliation"
+            "Name,Email,Instructor badges,Has Trainer badge,Taught times,"
+            "Is trainee,Airport,Country,Lessons,Affiliation"
         )
 
         self.assertEqual(first_row, first_row_expected)
@@ -375,6 +385,8 @@ def test_results(self):
             self.assertEqual(row['Instructor badges'],
                              " ".join(map(lambda x: x.name,
                                           expected.instructor_badges)))
+            self.assertEqual(row['Has Trainer badge'],
+                             'yes' if expected.is_trainer else 'no')
             self.assertEqual(row['Taught times'], str(expected.num_taught))
             self.assertEqual(row['Is trainee'],
                              'yes' if expected.is_trainee else 'no')