Repository: carpentries/amy

Issue:
TaskForm not working correctly due to prefix change
@pbanaszkiewicz 
Adding an instructor for a workshop causes 500 Server Error. Just tried to do so for workshop: https://amy.carpentries.org/workshops/event/2020-04-06-ucl-online. Editing other workshop details worked fine.


Discussion:
Perhaps a related issue - I also got a 500 Server Error when trying to accept a workshop request (e.g. https://amy.carpentries.org/requests/selforganised_submission/552/) but then on repeated attempts accepting some of the problematic requests worked so not sure what was happening but thought I should report it. This one is still causing problems: https://amy.carpentries.org/requests/selforganised_submission/552/
`TaskForm` send from edit event page has specific prefix, but the endpoint getting data (`TaskCreate` view) doesn't expect it.

Resolving patch:
diff --git a/amy/workshops/forms.py b/amy/workshops/forms.py
--- a/amy/workshops/forms.py
+++ b/amy/workshops/forms.py
@@ -894,9 +894,6 @@ class PersonsMergeForm(forms.Form):
 
 
 class AwardForm(WidgetOverrideMixin, forms.ModelForm):
-
-    helper = BootstrapHelper(add_cancel_button=False)
-
     class Meta:
         model = Award
         fields = '__all__'
@@ -909,6 +906,11 @@ class Meta:
                                              attrs=SELECT2_SIDEBAR),
         }
 
+    def __init__(self, *args, **kwargs):
+        form_tag = kwargs.pop('form_tag', True)
+        super().__init__(*args, **kwargs)
+        self.helper = BootstrapHelper(add_cancel_button=False, form_tag=form_tag)
+
 
 class EventLookupForm(forms.Form):
     event = forms.ModelChoiceField(
diff --git a/amy/workshops/views.py b/amy/workshops/views.py
--- a/amy/workshops/views.py
+++ b/amy/workshops/views.py
@@ -582,10 +582,10 @@ def get_context_data(self, **kwargs):
         context.update({
             'awards': self.object.award_set.select_related('event', 'badge')
                           .order_by('badge__name'),
-            'award_form': AwardForm(**kwargs),
             'tasks': self.object.task_set.select_related('role', 'event')
                          .order_by('-event__slug'),
-            'task_form': TaskForm(**kwargs),
+            'award_form': AwardForm(form_tag=False, prefix="award", **kwargs),
+            'task_form': TaskForm(form_tag=False, prefix="task", **kwargs),
         })
         return context
 
@@ -1363,6 +1363,13 @@ class TaskCreate(OnlyForAdminsMixin, PermissionRequiredMixin,
     model = Task
     form_class = TaskForm
 
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs.update({
+            'prefix': "task"
+        })
+        return kwargs
+
     def post(self, request, *args, **kwargs):
         """Save request in `self.request`."""
         self.request = request
@@ -1531,6 +1538,13 @@ class MockAwardCreate(OnlyForAdminsMixin, PermissionRequiredMixin,
     form_class = AwardForm
     populate_fields = ['badge', 'person']
 
+    def get_form_kwargs(self):
+        kwargs = super().get_form_kwargs()
+        kwargs.update({
+            'prefix': "award"
+        })
+        return kwargs
+
     def get_initial(self, **kwargs):
         initial = super().get_initial(**kwargs)
 


Test patch:
diff --git a/amy/workshops/tests/test_badge.py b/amy/workshops/tests/test_badge.py
--- a/amy/workshops/tests/test_badge.py
+++ b/amy/workshops/tests/test_badge.py
@@ -40,8 +40,8 @@ def test_badge_award(self):
         )
         award_add = swc_badge.click('Award new', index=0)
         form = award_add.forms[2]
-        self.assertSelected(form['badge'], 'Software Carpentry Instructor')
-        form['person'].force_value(self.spiderman.id)
+        self.assertSelected(form['award-badge'], 'Software Carpentry Instructor')
+        form['award-person'].force_value(self.spiderman.id)
         assert self.swc_instructor.award_set.count() == 3
         form.submit()
         assert self.swc_instructor.award_set.count() == 4
diff --git a/amy/workshops/tests/test_event.py b/amy/workshops/tests/test_event.py
--- a/amy/workshops/tests/test_event.py
+++ b/amy/workshops/tests/test_event.py
@@ -593,9 +593,9 @@ def test_number_of_attendees_increasing(self):
         self.assertEqual(event.attendance, 0)
 
         data = {
-            'role': self.learner.pk,
-            'event': event.pk,
-            'person': self.spiderman.pk,
+            'task-role': self.learner.pk,
+            'task-event': event.pk,
+            'task-person': self.spiderman.pk,
         }
         self.client.post(reverse('task_add'), data)
 
diff --git a/amy/workshops/tests/test_person.py b/amy/workshops/tests/test_person.py
--- a/amy/workshops/tests/test_person.py
+++ b/amy/workshops/tests/test_person.py
@@ -97,7 +97,7 @@ def test_person_award_badge(self):
         url = reverse('person_edit', args=[self.spiderman.pk])
         person_edit = self.app.get(url, user='admin')
         award_form = person_edit.forms[2]
-        award_form['badge'] = self.swc_instructor.pk
+        award_form['award-badge'] = self.swc_instructor.pk
 
         self.assertEqual(self.spiderman.award_set.count(), 0)
         self.assertRedirects(award_form.submit(), url)
@@ -111,9 +111,9 @@ def test_person_add_task(self):
 
         url = reverse('person_edit', args=[self.spiderman.pk])
         person_edit = self.app.get(url, user='admin')
-        task_form = person_edit.forms[4]
-        task_form['event'].force_value(Event.objects.first().pk)
-        task_form['role'] = role.pk
+        task_form = person_edit.forms[3]
+        task_form['task-event'].force_value(Event.objects.first().pk)
+        task_form['task-role'] = role.pk
 
         self.assertEqual(self.spiderman.task_set.count(), 0)
         self.assertRedirects(task_form.submit(), url)
@@ -380,11 +380,11 @@ def test_awarding_instructor_badge_workflow(self):
 
         # Test workflow starting from clicking at "instructor badge" label
         swc_res = trainees.click('^<strike>instructor badge</strike>$')
-        self.assertSelected(swc_res.forms['main-form']['badge'],
+        self.assertSelected(swc_res.forms['main-form']['award-badge'],
                             '---------')
-        self.assertEqual(int(swc_res.forms['main-form']['event'].value),
+        self.assertEqual(int(swc_res.forms['main-form']['award-event'].value),
                          training.pk)
-        swc_res.forms['main-form']['badge'].select(self.swc_instructor.pk)
+        swc_res.forms['main-form']['award-badge'].select(self.swc_instructor.pk)
         res = swc_res.forms['main-form'].submit()
         self.assertRedirects(res, reverse('all_trainees'))
         self.assertEqual(trainee.award_set.last().badge, self.swc_instructor)
@@ -395,11 +395,11 @@ def test_awarding_instructor_badge_workflow(self):
 
         # Test workflow starting from clicking at "instructor badge" label
         dc_res = trainees.click('^<strike>instructor badge</strike>$')
-        self.assertSelected(dc_res.forms['main-form']['badge'],
+        self.assertSelected(dc_res.forms['main-form']['award-badge'],
                             '---------')
-        self.assertEqual(int(dc_res.forms['main-form']['event'].value),
+        self.assertEqual(int(dc_res.forms['main-form']['award-event'].value),
                          training.pk)
-        dc_res.forms['main-form']['badge'].select(self.dc_instructor.pk)
+        dc_res.forms['main-form']['award-badge'].select(self.dc_instructor.pk)
         res = dc_res.forms['main-form'].submit()
         self.assertRedirects(res, reverse('all_trainees'))
         self.assertEqual(trainee.award_set.last().badge, self.dc_instructor)
diff --git a/amy/workshops/tests/test_tasks.py b/amy/workshops/tests/test_tasks.py
--- a/amy/workshops/tests/test_tasks.py
+++ b/amy/workshops/tests/test_tasks.py
@@ -398,9 +398,9 @@ def test_job_scheduled(self):
 
         self.client.force_login(self.admin)
         data = {
-            'event': self.test_event_1.pk,
-            'person': self.test_person_1.pk,
-            'role': role.pk,
+            'task-event': self.test_event_1.pk,
+            'task-person': self.test_person_1.pk,
+            'task-role': role.pk,
         }
         response = self.client.post(reverse('task_add'), data, follow=True)
         # with open('test.html', 'w', encoding='utf-8') as f:
@@ -720,9 +720,9 @@ def test_job_unscheduled(self):
 
         self.client.force_login(self.admin)
         data = {
-            'event': self.event_1.pk,
-            'person': self.person_1.pk,
-            'role': self.instructor.pk,
+            'task-event': self.event_1.pk,
+            'task-person': self.person_1.pk,
+            'task-role': self.instructor.pk,
         }
         response = self.client.post(reverse('task_add'), data, follow=True)
         self.assertContains(response, "New email was scheduled")