Repository: carpentries/amy

Issue:
Update bulk add training progress form
The bulk add training progress form should work for the Get Involved step.

Extra note/bug: submitting the form for a Welcome Session added the session twice to the selected trainees, but only once? Perhaps I double-clicked submit...

Created automatically from an Asana task.


Discussion:

Resolving patch:
diff --git a/amy/trainings/forms.py b/amy/trainings/forms.py
--- a/amy/trainings/forms.py
+++ b/amy/trainings/forms.py
@@ -95,6 +95,13 @@ class BulkAddTrainingProgressForm(forms.ModelForm):
         required=True,
     )
 
+    involvement_type = forms.ModelChoiceField(
+        label="Type of involvement",
+        required=False,
+        queryset=Involvement.objects.default_order().filter(archived_at__isnull=True),
+        widget=RadioSelect(),
+    )
+
     helper = BootstrapHelper(
         additional_form_class="training-progress",
         submit_label="Add",
@@ -106,8 +113,10 @@ class BulkAddTrainingProgressForm(forms.ModelForm):
         # the template where this form is used
         "requirement",
         "state",
+        "involvement_type",
         "event",
         "url",
+        "date",
         "notes",
     )
 
@@ -117,11 +126,16 @@ class Meta:
             # no 'trainees'
             "requirement",
             "state",
+            "involvement_type",
             "event",
             "url",
+            "date",
             "notes",
         ]
         widgets = {
             "state": RadioSelect,
             "notes": TextInput,
         }
+
+    class Media:
+        js = ("trainingprogress_form.js",)


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
@@ -28,10 +28,14 @@ def setUp(self):
         self._setUpRoles()
 
         self.training = TrainingRequirement.objects.get(name="Training")
-        self.lesson_contribution, _ = TrainingRequirement.objects.get_or_create(
-            name="Lesson Contribution", defaults={"url_required": True}
+        self.get_involved, _ = TrainingRequirement.objects.get_or_create(
+            name="Get Involved", defaults={"involvement_required": True}
         )
         self.welcome = TrainingRequirement.objects.get(name="Welcome Session")
+        self.demo = TrainingRequirement.objects.get(name="Demo")
+        self.involvement, _ = Involvement.objects.get_or_create(
+            name="GitHub Contribution", defaults={"url_required": True}
+        )
 
         self.ttt_event = Event.objects.create(
             start=datetime(2018, 7, 14),
@@ -40,11 +44,23 @@ def setUp(self):
         )
         self.ttt_event.tags.add(Tag.objects.get(name="TTT"))
 
+        # add some training tasks
+        self.ironman.task_set.create(
+            event=self.ttt_event,
+            role=Role.objects.get(name="learner"),
+        )
+        self.spiderman.task_set.create(
+            event=self.ttt_event,
+            role=Role.objects.get(name="learner"),
+        )
+
     def test_view_loads(self):
         rv = self.client.get(reverse("all_trainees"))
         self.assertEqual(rv.status_code, 200)
 
-    def test_bulk_add_progress(self):
+    def test_bulk_add_progress__welcome(self):
+        # Arrange
+        # create a pre-existing progress to ensure bulk adding doesn't interfere
         TrainingProgress.objects.create(
             trainee=self.spiderman, requirement=self.welcome, state="n"
         )
@@ -55,22 +71,15 @@ def test_bulk_add_progress(self):
             "submit": "",
         }
 
-        # all trainees need to have a training task to assign a training
-        # progress to them
-        self.ironman.task_set.create(
-            event=self.ttt_event,
-            role=Role.objects.get(name="learner"),
-        )
-        self.spiderman.task_set.create(
-            event=self.ttt_event,
-            role=Role.objects.get(name="learner"),
-        )
-
+        # Act
         rv = self.client.post(reverse("all_trainees"), data, follow=True)
 
+        # Assert
+        self.assertEqual(rv.status_code, 200)
         self.assertEqual(rv.resolver_match.view_name, "all_trainees")
         msg = "Successfully changed progress of all selected trainees."
         self.assertContains(rv, msg)
+
         got = set(
             TrainingProgress.objects.values_list("trainee", "requirement", "state")
         )
@@ -81,6 +90,111 @@ def test_bulk_add_progress(self):
         }
         self.assertEqual(got, expected)
 
+    def test_bulk_add_progress__training(self):
+        # Arrange
+        # create a pre-existing progress to ensure bulk adding doesn't interfere
+        TrainingProgress.objects.create(
+            trainee=self.spiderman, requirement=self.training, state="n"
+        )
+        data = {
+            "trainees": [self.spiderman.pk, self.ironman.pk],
+            "requirement": self.training.pk,
+            "state": "a",
+            "event": self.ttt_event.pk,
+            "submit": "",
+        }
+
+        # Act
+        rv = self.client.post(reverse("all_trainees"), data, follow=True)
+
+        # Assert
+        self.assertEqual(rv.status_code, 200)
+        self.assertEqual(rv.resolver_match.view_name, "all_trainees")
+        msg = "Successfully changed progress of all selected trainees."
+        self.assertContains(rv, msg)
+
+        got = set(
+            TrainingProgress.objects.values_list("trainee", "requirement", "state")
+        )
+        expected = {
+            (self.spiderman.pk, self.training.pk, "n"),
+            (self.spiderman.pk, self.training.pk, "a"),
+            (self.ironman.pk, self.training.pk, "a"),
+        }
+        self.assertEqual(got, expected)
+
+    def test_bulk_add_progress__demo(self):
+        # Arrange
+        # create a pre-existing progress to ensure bulk adding doesn't interfere
+        TrainingProgress.objects.create(
+            trainee=self.spiderman, requirement=self.demo, state="n"
+        )
+        data = {
+            "trainees": [self.spiderman.pk, self.ironman.pk],
+            "requirement": self.demo.pk,
+            "state": "a",
+            "submit": "",
+        }
+
+        # Act
+        rv = self.client.post(reverse("all_trainees"), data, follow=True)
+
+        # Assert
+        self.assertEqual(rv.status_code, 200)
+        self.assertEqual(rv.resolver_match.view_name, "all_trainees")
+        msg = "Successfully changed progress of all selected trainees."
+        self.assertContains(rv, msg)
+
+        got = set(
+            TrainingProgress.objects.values_list("trainee", "requirement", "state")
+        )
+        expected = {
+            (self.spiderman.pk, self.demo.pk, "n"),
+            (self.spiderman.pk, self.demo.pk, "a"),
+            (self.ironman.pk, self.demo.pk, "a"),
+        }
+        self.assertEqual(got, expected)
+
+    def test_bulk_add_progress__get_involved(self):
+        # Arrange
+        # create a pre-existing progress to ensure bulk adding doesn't interfere
+        TrainingProgress.objects.create(
+            trainee=self.spiderman,
+            requirement=self.get_involved,
+            state="n",
+            involvement_type=self.involvement,
+            url="https://example.org",
+            date=date(2022, 5, 3),
+        )
+        data = {
+            "trainees": [self.spiderman.pk, self.ironman.pk],
+            "requirement": self.get_involved.pk,
+            "state": "a",
+            "involvement_type": self.involvement.pk,
+            "url": "https://example.org",
+            "date": "2023-6-21",
+            "submit": "",
+        }
+
+        # Act
+        rv = self.client.post(reverse("all_trainees"), data, follow=True)
+
+        # Assert
+        self.assertEqual(rv.status_code, 200)
+        self.assertEqual(rv.resolver_match.view_name, "all_trainees")
+        msg = "Successfully changed progress of all selected trainees."
+        self.assertContains(rv, msg)
+
+        got = set(
+            TrainingProgress.objects.values_list("trainee", "requirement", "state")
+        )
+        expected = {
+            (self.spiderman.pk, self.get_involved.pk, "n"),
+            (self.spiderman.pk, self.get_involved.pk, "a"),
+            (self.ironman.pk, self.get_involved.pk, "a"),
+        }
+        self.assertEqual(got, expected)
+
 
 class TestFilterTraineesByInstructorStatus(TestBase):
     def _setUpPermissions(self):