Repository: carpentries/amy

Issue:
update criteria in view to identify people eligible for certification
Update criteria used in this view https://amy.carpentries.org/trainings/trainees/?search=&is_instructor=eligible&training=&training_request=unknown&order_by= to account for single "Instructor" award/homework/teaching demo.


Discussion:

Resolving patch:
diff --git a/amy/trainings/filters.py b/amy/trainings/filters.py
--- a/amy/trainings/filters.py
+++ b/amy/trainings/filters.py
@@ -54,33 +54,15 @@ def filter_trainees_by_training_request_presence(queryset, name, flag):
 
 
 def filter_trainees_by_instructor_status(queryset, name, choice):
-    if choice == "all":
-        return queryset.filter(
-            is_swc_instructor=True,
-            is_dc_instructor=True,
-            is_lc_instructor=True,
-        )
-    elif choice == "any":
-        return queryset.filter(
-            Q(is_swc_instructor=True)
-            | Q(is_dc_instructor=True)
-            | Q(is_lc_instructor=True)
-        )
-    elif choice == "swc":
-        return queryset.filter(is_swc_instructor=True)
-    elif choice == "dc":
-        return queryset.filter(is_dc_instructor=True)
-    elif choice == "lc":
-        return queryset.filter(is_lc_instructor=True)
+    if choice == "yes":
+        return queryset.filter(is_instructor=True)
     elif choice == "eligible":
         # Instructor eligible but without any badge.
         # This code is kept in Q()-expressions to allow for fast condition
         # change.
         return queryset.filter(Q(instructor_eligible__gte=1) & Q(is_instructor=False))
     elif choice == "no":
-        return queryset.filter(
-            is_instructor=False,
-        )
+        return queryset.filter(is_instructor=False)
     else:
         return queryset
 
@@ -121,11 +103,7 @@ class TraineeFilter(AMYFilterSet):
         method=filter_trainees_by_instructor_status,
         choices=[
             ("", "Unknown"),
-            ("all", "All instructor badges"),
-            ("any", "Any instructor badge "),
-            ("swc", "SWC instructor"),
-            ("dc", "DC instructor"),
-            ("lc", "LC instructor"),
+            ("yes", "Yes, has Instructor badge"),
             ("eligible", "No, but eligible to be certified"),
             ("no", "No instructor badge"),
         ],
diff --git a/amy/trainings/views.py b/amy/trainings/views.py
--- a/amy/trainings/views.py
+++ b/amy/trainings/views.py
@@ -93,13 +93,6 @@ class TrainingProgressDelete(RedirectSupportMixin, OnlyForAdminsMixin, AMYDelete
 
 
 def all_trainees_queryset():
-    def has_badge(badge):
-        return Sum(
-            Case(
-                When(badges__name=badge, then=1), default=0, output_field=IntegerField()
-            )
-        )
-
     return (
         Person.objects.annotate_with_instructor_eligibility()
         .prefetch_related(
@@ -117,12 +110,9 @@ def has_badge(badge):
             "trainingprogress_set__evaluated_by",
         )
         .annotate(
-            is_swc_instructor=has_badge("swc-instructor"),
-            is_dc_instructor=has_badge("dc-instructor"),
-            is_lc_instructor=has_badge("lc-instructor"),
             is_instructor=Sum(
                 Case(
-                    When(badges__name__in=Badge.INSTRUCTOR_BADGES, then=1),
+                    When(badges__name=Badge.SINGLE_INSTRUCTOR_BADGE, then=1),
                     default=0,
                     output_field=IntegerField(),
                 )
@@ -148,7 +138,7 @@ def all_trainees(request):
             for trainee in discard_form.cleaned_data["trainees"]:
                 TrainingProgress.objects.filter(trainee=trainee).update(discarded=True)
             messages.success(
-                request, "Successfully discarded progress of " "all selected trainees."
+                request, "Successfully discarded progress of all selected trainees."
             )
 
             # Raw uri contains GET parameters from django filters. We use it
@@ -173,7 +163,7 @@ def all_trainees(request):
                     notes=form.cleaned_data["notes"],
                 )
             messages.success(
-                request, "Successfully changed progress of " "all selected trainees."
+                request, "Successfully changed progress of all selected trainees."
             )
 
             return redirect(request.get_raw_uri())
@@ -196,9 +186,6 @@ def all_trainees(request):
     context = {
         "title": "Trainees",
         "all_trainees": trainees,
-        "swc": Badge.objects.get(name="swc-instructor"),
-        "dc": Badge.objects.get(name="dc-instructor"),
-        "lc": Badge.objects.get(name="lc-instructor"),
         "filter": filter,
         "form": form,
         "discard_form": discard_form,
diff --git a/amy/workshops/models.py b/amy/workshops/models.py
--- a/amy/workshops/models.py
+++ b/amy/workshops/models.py
@@ -1812,11 +1812,13 @@ class BadgeQuerySet(models.query.QuerySet):
     """Custom QuerySet that provides easy way to get instructor badges
     (we use that a lot)."""
 
+    SINGLE_INSTRUCTOR_BADGE = "instructor"
+
     INSTRUCTOR_BADGES = (
         "dc-instructor",
         "swc-instructor",
         "lc-instructor",
-        "instructor",
+        SINGLE_INSTRUCTOR_BADGE,
     )
 
     def instructor_badges(self):
@@ -1829,6 +1831,7 @@ class Badge(models.Model):
     """Represent a badge we award."""
 
     # just for easier access outside `models.py`
+    SINGLE_INSTRUCTOR_BADGE = BadgeQuerySet.SINGLE_INSTRUCTOR_BADGE
     INSTRUCTOR_BADGES = BadgeQuerySet.INSTRUCTOR_BADGES
     IMPORTANT_BADGES = INSTRUCTOR_BADGES + ("trainer",)
 


Test patch:
diff --git a/amy/trainings/tests/test_trainees.py b/amy/trainings/tests/test_trainees.py
--- a/amy/trainings/tests/test_trainees.py
+++ b/amy/trainings/tests/test_trainees.py
@@ -136,7 +136,9 @@ def _setUpInstructors(self):
             username="instructor1_instructor1",
         )
         Award.objects.create(
-            person=self.instructor1, badge=self.swc_instructor, awarded=date(2014, 1, 1)
+            person=self.instructor1,
+            badge=self.instructor_badge,
+            awarded=date(2014, 1, 1),
         )
         self.instructor2 = Person.objects.create(
             personal="Instructor2",
@@ -145,7 +147,9 @@ def _setUpInstructors(self):
             username="instructor2_instructor2",
         )
         Award.objects.create(
-            person=self.instructor2, badge=self.dc_instructor, awarded=date(2014, 1, 1)
+            person=self.instructor2,
+            badge=self.instructor_badge,
+            awarded=date(2014, 1, 1),
         )
         self.instructor3 = Person.objects.create(
             personal="Instructor3",
@@ -154,24 +158,9 @@ def _setUpInstructors(self):
             username="instructor3_instructor3",
         )
         Award.objects.create(
-            person=self.instructor3, badge=self.lc_instructor, awarded=date(2014, 1, 1)
-        )
-
-        # 1 combined instructor (SWC-DC-LC)
-        self.instructor4 = Person.objects.create(
-            personal="Instructor4",
-            family="Instructor4",
-            email="instructor4@example.org",
-            username="instructor4_instructor4",
-        )
-        Award.objects.create(
-            person=self.instructor4, badge=self.swc_instructor, awarded=date(2014, 1, 1)
-        )
-        Award.objects.create(
-            person=self.instructor4, badge=self.dc_instructor, awarded=date(2014, 1, 1)
-        )
-        Award.objects.create(
-            person=self.instructor4, badge=self.lc_instructor, awarded=date(2014, 1, 1)
+            person=self.instructor3,
+            badge=self.instructor_badge,
+            awarded=date(2014, 1, 1),
         )
 
         # 1 eligible trainee with no instructor badges
@@ -257,7 +246,7 @@ def _setUpInstructors(self):
             ]
         )
         Award.objects.create(
-            person=self.trainee2, badge=self.lc_instructor, awarded=date(2014, 1, 1)
+            person=self.trainee2, badge=self.instructor_badge, awarded=date(2014, 1, 1)
         )
         # 1 non-eligible trainee
         self.trainee3 = Person.objects.create(
@@ -313,39 +302,10 @@ def test_no_choice(self):
         self.assertEqual(rv, self.queryset)
         self.assertQuerysetEqual(rv, list(self.queryset), transform=lambda x: x)
 
-    def test_all_instructors(self):
+    def test_instructors(self):
         # only instructors who have all 3 badges should be returned
-        rv = self.filter(choice="all")
-        values = [self.instructor4]
-        self.assertQuerysetEqual(rv, values, transform=lambda x: x)
-
-    def test_any_instructors(self):
-        # any instructor should be returned
-        rv = self.filter(choice="any")
-        values = [
-            self.instructor1,
-            self.instructor2,
-            self.instructor3,
-            self.instructor4,
-        ]
-        self.assertQuerysetEqual(rv, values, transform=lambda x: x)
-
-    def test_swc_instructors(self):
-        # only SWC instructors should be returned
-        rv = self.filter(choice="swc")
-        values = [self.instructor1, self.instructor4]
-        self.assertQuerysetEqual(rv, values, transform=lambda x: x)
-
-    def test_dc_instructors(self):
-        # only DC instructors should be returned
-        rv = self.filter(choice="dc")
-        values = [self.instructor2, self.instructor4]
-        self.assertQuerysetEqual(rv, values, transform=lambda x: x)
-
-    def test_lc_instructors(self):
-        # only LC instructors should be returned
-        rv = self.filter(choice="lc")
-        values = [self.instructor3, self.instructor4]
+        rv = self.filter(choice="yes")
+        values = [self.instructor1, self.instructor2, self.instructor3]
         self.assertQuerysetEqual(rv, values, transform=lambda x: x)
 
     def test_eligible_trainees(self):
@@ -356,51 +316,30 @@ def test_eligible_trainees(self):
 
     def test_eligibility_query(self):
         # check if eligibility query works correctly
-        self.assertEqual(Person.objects.all().count(), 7)
+        self.assertEqual(Person.objects.all().count(), 6)
         rv = all_trainees_queryset().order_by("pk")
         conditions_per_person = [
             # self.instructor1
             dict(
                 username="instructor1_instructor1",
-                is_swc_instructor=1,
-                is_dc_instructor=0,
-                is_lc_instructor=0,
                 is_instructor=1,
                 instructor_eligible=0,
             ),
             # self.instructor2
             dict(
                 username="instructor2_instructor2",
-                is_swc_instructor=0,
-                is_dc_instructor=1,
-                is_lc_instructor=0,
                 is_instructor=1,
                 instructor_eligible=0,
             ),
             # self.instructor3
             dict(
                 username="instructor3_instructor3",
-                is_swc_instructor=0,
-                is_dc_instructor=0,
-                is_lc_instructor=1,
                 is_instructor=1,
                 instructor_eligible=0,
             ),
-            # self.instructor4
-            dict(
-                username="instructor4_instructor4",
-                is_swc_instructor=1,
-                is_dc_instructor=1,
-                is_lc_instructor=1,
-                is_instructor=3,
-                instructor_eligible=0,
-            ),
             # self.trainee1
             dict(
                 username="trainee1_trainee1",
-                is_swc_instructor=0,
-                is_dc_instructor=0,
-                is_lc_instructor=0,
                 is_instructor=0,
                 passed_training=1,
                 passed_discussion=1,
@@ -417,11 +356,6 @@ def test_eligibility_query(self):
             # self.trainee2
             dict(
                 username="trainee2_trainee2",
-                is_swc_instructor=0,
-                is_dc_instructor=0,
-                # no idea why this is counting this way, but
-                # bool(5) == True so we're cool?
-                is_lc_instructor=5,
                 is_instructor=5,
                 passed_training=1,
                 passed_discussion=1,
@@ -438,9 +372,6 @@ def test_eligibility_query(self):
             # self.trainee3
             dict(
                 username="trainee3_trainee3",
-                is_swc_instructor=0,
-                is_dc_instructor=0,
-                is_lc_instructor=0,
                 is_instructor=0,
                 passed_training=1,
                 passed_discussion=0,