anon-iclr-submission/benchname-bug-localization huggingface dataset:
Repository: carpentries/amy
Issue: Change the way "reset" buttons work

- Rename button from "reset" to "cancel".
- Make the button white instead of red.
- Redirect back to the last page in history.


Changed files:
pydata/forms.py, workshops/forms.py

diff --git a/pydata/forms.py b/pydata/forms.py
index 511dce61..cac972df 100644
--- a/pydata/forms.py
+++ b/pydata/forms.py
@@ -21,7 +21,7 @@ class BaseModelAddFormSet(forms.models.BaseModelFormSet):
     validate_min = False
 
     helper = BootstrapHelper(
-        form_tag=False, add_submit_button=False, add_reset_button=False)
+        form_tag=False, add_submit_button=False, add_cancel_button=False)
 
     def __init__(self, *args, **kwargs):
         # Override the default form helper
diff --git a/workshops/forms.py b/workshops/forms.py
index 6671789d..d619a249 100644
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -3,7 +3,7 @@ import re
 from captcha.fields import ReCaptchaField
 from crispy_forms.bootstrap import FormActions
 from crispy_forms.helper import FormHelper
-from crispy_forms.layout import Layout, Div, HTML, Submit, Reset, Field
+from crispy_forms.layout import Layout, Div, HTML, Submit, Button, Field
 from crispy_forms.bootstrap import AccordionGroup, Accordion
 from django import forms
 from django.core.exceptions import ValidationError
@@ -40,7 +40,7 @@ class BootstrapHelper(FormHelper):
                  wider_labels=False,
                  add_submit_button=True,
                  add_delete_button=False,
-                 add_reset_button=True,
+                 add_cancel_button=True,
                  additional_form_class='',
                  form_tag=True,
                  display_labels=True,
@@ -103,9 +103,11 @@ class BootstrapHelper(FormHelper):
                 css_class='btn-danger',
                 style='float: right;'))
 
-        if add_reset_button:
-            self.add_input(
-                Reset('reset', 'Reset', css_class='pull-right btn-danger'))
+        if add_cancel_button:
+            self.add_input(Button(
+                'cancel', 'Cancel',
+                css_class='btn-default pull-right',
+                onclick='window.history.back()'))
 
         self.form_class = 'form-horizontal ' + additional_form_class
 
@@ -285,7 +287,7 @@ class SearchForm(forms.Form):
                                      required=False,
                                      initial=True)
 
-    helper = BootstrapHelper(add_reset_button=False)
+    helper = BootstrapHelper(add_cancel_button=False)
 
 
 class DebriefForm(forms.Form):
Repository: carpentries/amy
Issue: Server error when submitting trainee-match form without selected trainees

1. Go to the page for matching trainees to training sessions.
2. Select a training session, but do _not_ select a person.
3. Submit.
4. Boom - 500 error.


Changed files:
workshops/forms.py

diff --git a/workshops/forms.py b/workshops/forms.py
index 28456ab8..3ed0fbc7 100644
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -1410,7 +1410,7 @@ class BulkMatchTrainingRequestForm(forms.Form):
     def clean(self):
         super().clean()
 
-        if any(r.person is None for r in self.cleaned_data['requests']):
+        if any(r.person is None for r in self.cleaned_data.get('requests', [])):
             raise ValidationError('Some of the requests are not matched '
                                   'to a trainee yet. Before matching them to '
                                   'a training, you need to accept them '
Repository: carpentries/amy
Issue: "Unresponsive" workshops should not show up in "Workshop issues" attendance report 

If a workshop is marked "unresponsive" that means that the host/organizer/instructor has not responded to multiple attempts to get attendance data.

These should no longer show up in the attendance column of workshop issues.


Changed files:
workshops/views.py, workshops/test/test_issues.py

diff --git a/workshops/test/test_issues.py b/workshops/test/test_issues.py
index 49fc32cf..bb4d1fc5 100644
--- a/workshops/test/test_issues.py
+++ b/workshops/test/test_issues.py
@@ -2,7 +2,7 @@ from datetime import timedelta, date
 
 from django.core.urlresolvers import reverse
 
-from ..models import Event, Organization, Role, Person
+from ..models import Event, Organization, Role, Person, Tag
 from .base import TestBase
 
 
@@ -10,6 +10,7 @@ class TestIssuesViews(TestBase):
     def setUp(self):
         super()._setUpUsersAndLogin()
         super()._setUpRoles()
+        super()._setUpTags()
 
         self.url = reverse('workshop_issues')
         self.today = date.today()
@@ -62,9 +63,19 @@ class TestIssuesViews(TestBase):
             country='US', address='A', venue='B', latitude=89, longitude=179)
         no_attendance.task_set.create(person=Person.objects.first(),
                                       role=self.instructor_role)
+        no_attendance_unresponsive = Event.objects.create(
+            slug='unresponsive-event-with-no-attendance',
+            start=self.weekago, end=self.yesterday,
+            attendance=0, host=Organization.objects.first(),
+            country='US', address='A', venue='B', latitude=89, longitude=179)
+        no_attendance_unresponsive.task_set.create(
+            person=Person.objects.first(), role=self.instructor_role)
+        no_attendance_unresponsive.tags.add(
+            Tag.objects.get(name='unresponsive'))
 
         rv = self.client.get(self.url)
         self.assertNotIn(attendance, rv.context['events'])
+        self.assertNotIn(no_attendance_unresponsive, rv.context['events'])
         self.assertIn(no_attendance, rv.context['events'])
 
     def test_workshop_issues_no_location(self):
diff --git a/workshops/views.py b/workshops/views.py
index 500d9a48..9c0abc6b 100644
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -2191,8 +2191,9 @@ def workshop_issues(request):
             )
         )
     )
+    no_attendance = Q(attendance=None) | Q(attendance=0)
     events = events.filter(
-        Q(attendance=None) | Q(attendance=0) |
+        (no_attendance & ~Q(tags__name='unresponsive')) |
         Q(country=None) |
         Q(venue=None) | Q(venue__exact='') |
         Q(address=None) | Q(address__exact='') |
Repository: carpentries/amy
Issue: Incorrect title on Badge Detail page

The title of the `badge_detail` page for the badge `maintainer` is `Airport Maintainer`, i.e. `Airport` is appended to the title of each badge.

Is this the expected functionality?

Changed files:
workshops/views.py

diff --git a/workshops/views.py b/workshops/views.py
index e23d4096..1d539f29 100644
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -1616,7 +1616,7 @@ class BadgeDetails(OnlyForAdminsMixin, AMYDetailView):
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
 
-        context['title'] = 'Airport {0}'.format(self.object)
+        context['title'] = 'Badge {0}'.format(self.object)
         filter = BadgeAwardsFilter(
             self.request.GET,
             queryset=self.object.award_set.select_related(
Repository: carpentries/amy
Issue: cancelled workshops should not show up as not invoiced in dashboard

Title says it all.
This probably relates to #1064 


Changed files:
workshops/models.py

diff --git a/workshops/models.py b/workshops/models.py
index f6b60888..49ec56d0 100644
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -838,8 +838,9 @@ class EventQuerySet(models.query.QuerySet):
         These are marked as uninvoiced, and have occurred.
         Events are sorted oldest first.'''
 
-        return self.past_events().filter(invoice_status='not-invoiced') \\
-                                 .order_by('start')
+        return self.not_cancelled().past_events() \\
+                   .filter(invoice_status='not-invoiced') \\
+                   .order_by('start')
 
     def metadata_changed(self):
         """Return events for which remote metatags have been updated."""
Repository: carpentries/amy
Issue: Can't edit person

I lost the access(?) to edit a person. I.e. for some of the Instructor Training Trainees I wanted to add a note that they will be late with checkout because of whatever reason they emailed me about but when I click "Edit" on the person I get 403 Forbidden. I used to be able to edit (I think I'm an admin).

Changed files:
workshops/test/test_person.py, workshops/views.py

diff --git a/workshops/test/test_person.py b/workshops/test/test_person.py
index fbb92767..0219f6a3 100644
--- a/workshops/test/test_person.py
+++ b/workshops/test/test_person.py
@@ -174,6 +174,26 @@ class TestPerson(TestBase):
             self._check_status_code_and_parse(response, 200)
             assert False, 'expected 302 redirect after post'
 
+    def test_1185_regression(self):
+        """Ensure that admins without superuser privileges,
+        but with 'change_person' permission can edit other people.
+
+        Regression test against
+        https://github.com/swcarpentry/amy/issues/1185."""
+
+        manager = Person.objects.create_user(
+            username='manager', personal='Manager', family='Manager',
+            email='manager@example.org', password='manager')
+        can_change_person = Permission.objects.get(codename='change_person')
+        manager.user_permissions.add(can_change_person)
+        bob = Person.objects.create_user(
+            username='bob', personal='Bob', family='Smith',
+            email='bob@example.org', password='bob')
+
+        bob_edit_url = reverse('person_edit', args=[bob.id])
+        res = self.app.get(bob_edit_url, user='manager')
+        self.assertEqual(res.status_code, 200)
+
     def test_person_award_badge(self):
         """Ensure that we can add an award from `person_edit` view"""
         url = reverse('person_edit', args=[self.spiderman.pk])
diff --git a/workshops/views.py b/workshops/views.py
index 826096aa..0bb0ef16 100644
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -727,7 +727,7 @@ class PersonUpdate(OnlyForAdminsMixin, UserPassesTestMixin,
     template_name = 'workshops/person_edit_form.html'
 
     def test_func(self):
-        if not (self.request.user.has_perm('workshops.edit_person') or \\
+        if not (self.request.user.has_perm('workshops.change_person') or \\
             self.request.user == self.get_object()):
             raise PermissionDenied
         return True
Repository: carpentries/amy
Issue: Wrong name of permissions to accept & create event for Workshop Inquiry and Self-Organised Submission

Permissions for accepting `WorkshopInquiryRequest` and `SelfOrganisedSubmission` where prefixed with `workshops.` app name, while they should be prefixed with `extrequests`.

Changed files:
amy/extrequests/views.py

diff --git a/amy/extrequests/views.py b/amy/extrequests/views.py
index dffbb2fd..45bdcbaa 100644
--- a/amy/extrequests/views.py
+++ b/amy/extrequests/views.py
@@ -279,7 +279,7 @@ class WorkshopInquirySetState(OnlyForAdminsMixin, ChangeRequestStateView):
 
 class WorkshopInquiryAcceptEvent(OnlyForAdminsMixin, PermissionRequiredMixin,
                                  AMYCreateAndFetchObjectView):
-    permission_required = ['workshops.change_workshopinquiryrequest',
+    permission_required = ['extrequests.change_workshopinquiryrequest',
                            'workshops.add_event']
     model = Event
     form_class = EventCreateForm
@@ -400,7 +400,7 @@ class SelfOrganisedSubmissionSetState(OnlyForAdminsMixin,
 class SelfOrganisedSubmissionAcceptEvent(OnlyForAdminsMixin,
                                          PermissionRequiredMixin,
                                          AMYCreateAndFetchObjectView):
-    permission_required = ['workshops.change_selforganisedsubmission',
+    permission_required = ['extrequests.change_selforganisedsubmission',
                            'workshops.add_event']
     model = Event
     form_class = EventCreateForm
Repository: carpentries/amy
Issue: Names show up multiple times in assignment pulldown

1. Go to an event.
2. Try to assign to assign to someone other than yourself.
3. Selection dialog with pulldown appears so that you can choose person.
4. Some names (currently Greg Wilson and Tracy Teal, possibly others) show up multiple times in that list.


Changed files:
workshops/lookups.py

diff --git a/workshops/lookups.py b/workshops/lookups.py
index 2fef53b0..8f1108d3 100644
--- a/workshops/lookups.py
+++ b/workshops/lookups.py
@@ -83,7 +83,7 @@ class AdminLookup(ModelLookup):
         admin_group = Group.objects.get(name='administrators')
         results = results.filter(
             Q(is_superuser=True) | Q(groups__in=[admin_group])
-        )
+        ).distinct()
         return results
Repository: carpentries/amy
Issue: Change logo on Data Carpentry workshop request form

On the Data Carpentry workshop request form, there's the Software Carpentry logo. Can we change that to the Data Carpentry logo?

It's the DC1_logo from here https://github.com/datacarpentry/logos/


Changed files:
workshops/views.py

diff --git a/workshops/views.py b/workshops/views.py
index 7fdd1b9b..69affb52 100644
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -2071,7 +2071,7 @@ class SWCEventRequestConfirm(TemplateView):
 class DCEventRequest(SWCEventRequest):
     form_class = DCEventRequestForm
     page_title = 'Request a Data Carpentry Workshop'
-    form_template = 'forms/workshop_dc_request.html'
+    template_name = 'forms/workshop_dc_request.html'
     success_url = reverse_lazy('dc_workshop_request_confirm')
Repository: carpentries/amy
Issue: Workshops "Update from URL" not updating

When I try to update the workshop website in AMY it doesn't do anything. It isn't providing me with a new "Comment Box" showing me the updated instructors/helpers. 

I get the message: **Success: 2020-04-28-genentech was updated successfully** but nothing happens

Changed files:
amy/workshops/views.py

diff --git a/amy/workshops/views.py b/amy/workshops/views.py
index f83b816a..9f8f2ab9 100644
--- a/amy/workshops/views.py
+++ b/amy/workshops/views.py
@@ -1067,7 +1067,7 @@ class EventUpdate(
         return context
 
     def get_form_class(self):
-        return partial(EventForm, show_lessons=True, add_comment=False)
+        return partial(EventForm, show_lessons=True, add_comment=True)
 
     def get_logger(self):
         return logger
Repository: carpentries/amy
Issue: Bug: Membership person roles formset mixup

`fiscal/membership/X/tasks/` in roles contains member roles, instead of membership person roles.

`fiscal/membership/X/members/` contains wrong title.

Changed files:
amy/fiscal/forms.py, amy/fiscal/views.py, amy/workshops/lookups.py

diff --git a/amy/fiscal/forms.py b/amy/fiscal/forms.py
index 8ea153ec..88095be5 100644
--- a/amy/fiscal/forms.py
+++ b/amy/fiscal/forms.py
@@ -245,7 +245,7 @@ class MembershipTaskForm(forms.ModelForm):
         ]
         widgets = {
             "person": ModelSelect2Widget(data_view="person-lookup"),
-            "role": ModelSelect2Widget(data_view="memberrole-lookup"),
+            "role": ModelSelect2Widget(data_view="membershippersonrole-lookup"),
         }
 
     def __init__(self, *args, **kwargs):
diff --git a/amy/fiscal/views.py b/amy/fiscal/views.py
index 953c1bcb..0475a8e0 100644
--- a/amy/fiscal/views.py
+++ b/amy/fiscal/views.py
@@ -282,7 +282,7 @@ class MembershipMembers(OnlyForAdminsMixin, MembershipFormsetView):
 
     def get_context_data(self, **kwargs):
         if "title" not in kwargs:
-            kwargs["title"] = "Change person roles for {}".format(self.membership)
+            kwargs["title"] = "Change members for {}".format(self.membership)
         return super().get_context_data(**kwargs)
 
 
diff --git a/amy/workshops/lookups.py b/amy/workshops/lookups.py
index e54f838e..bdb862f1 100644
--- a/amy/workshops/lookups.py
+++ b/amy/workshops/lookups.py
@@ -8,6 +8,7 @@ from django.contrib.auth.models import Group
 from django.conf.urls import url
 from django.db.models import Q, Count
 
+from fiscal.models import MembershipPersonRole
 from workshops import models
 from workshops.util import OnlyForAdminsNoRedirectMixin, LoginNotRequiredMixin
 
@@ -124,6 +125,16 @@ class MemberRoleLookupView(OnlyForAdminsNoRedirectMixin, AutoResponseView):
         return q
 
 
+class MembershipPersonRoleLookupView(OnlyForAdminsNoRedirectMixin, AutoResponseView):
+    def get_queryset(self):
+        q = MembershipPersonRole.objects.all()
+        if self.term:
+            return q.filter(
+                Q(name__icontains=self.term) | Q(verbose_name__icontains=self.term)
+            )
+        return q
+
+
 class PersonLookupView(OnlyForAdminsNoRedirectMixin, AutoResponseView):
     def get_queryset(self):
         results = models.Person.objects.all()
@@ -260,6 +271,11 @@ urlpatterns = [
     ),
     url(r"^memberships/$", MembershipLookupView.as_view(), name="membership-lookup"),
     url(r"^member-roles/$", MemberRoleLookupView.as_view(), name="memberrole-lookup"),
+    url(
+        r"^membership-person-roles/$",
+        MembershipPersonRoleLookupView.as_view(),
+        name="membershippersonrole-lookup",
+    ),
     url(r"^persons/$", PersonLookupView.as_view(), name="person-lookup"),
     url(r"^admins/$", AdminLookupView.as_view(), name="admin-lookup"),
     url(r"^airports/$", AirportLookupView.as_view(), name="airport-lookup"),
Repository: carpentries/amy
Issue: Admin: Missing Bootstrap CSS

Django admin is missing bootstrap css. In the settings.py we have changed the default message tags `MESSAGE_TAGS`. So the default css that shows up in django admin does not work. 

Changed files:
config/settings.py

diff --git a/config/settings.py b/config/settings.py
index 000e51fe..3dc6e494 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -443,10 +443,11 @@ MANAGERS = ADMINS
 from django.contrib.messages import constants as message_constants  # noqa
 
 MESSAGE_TAGS = {
-    message_constants.INFO: "alert-info",
-    message_constants.SUCCESS: "alert-success",
-    message_constants.WARNING: "alert-warning",
-    message_constants.ERROR: "alert-danger",
+    message_constants.DEBUG: "debug",
+    message_constants.INFO: "info alert-info",
+    message_constants.SUCCESS: "success alert-success",
+    message_constants.WARNING: "warning alert-warning",
+    message_constants.ERROR: "error alert-danger",
 }
 
 # django-countries
Repository: carpentries/amy
Issue: Training request form should use <a>s in help texts

Fields at the bottom of the form use `help_text`s but don't replace links with <a>links</a>.


Changed files:
workshops/forms.py

diff --git a/workshops/forms.py b/workshops/forms.py
index f7ec0695..59815f7c 100644
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -962,14 +962,17 @@ class TrainingRequestForm(forms.ModelForm):
         required=True,
         initial=False,
         label='I agree to abide by Software Carpentry\\'s Code of Conduct',
-        help_text='The Code of Conduct can be found at http://software-carpentry.org/conduct/',
+        help_text='The Code of Conduct can be found at '
+                  '<a href="http://software-carpentry.org/conduct/">'
+                  'http://software-carpentry.org/conduct/</a>',
     )
     agreed_to_complete_training = forms.BooleanField(
         required=True,
         initial=False,
         label='I agree to complete this training within three months of the Training Course',
-        help_text='The completion steps are described '
-                  'at http://swcarpentry.github.io/instructor-training/checkout/ '
+        help_text='The completion steps are described at '
+                  '<a href="http://swcarpentry.github.io/instructor-training/checkout/">'
+                  'http://swcarpentry.github.io/instructor-training/checkout/</a> '
                   'and take a total of approximately two hours.',
     )
     agreed_to_teach_workshops = forms.BooleanField(
Repository: carpentries/amy
Issue: No reverse match for rest_framework namespace

The error for a very strange reason shows when accessing these URLs:
https://github.com/swcarpentry/amy/blob/develop/api/urls.py#L57

I wasn't able to get rid of it; it's not being used at all, so maybe it should be removed…?


Changed files:
api/urls.py

diff --git a/api/urls.py b/api/urls.py
index 4eecbc2b..ad8aa1df 100644
--- a/api/urls.py
+++ b/api/urls.py
@@ -52,10 +52,4 @@ urlpatterns = [
     url('^', include(todos_router.urls)),
 ]
 
-# for login-logout functionality
-urlpatterns += [
-    url(r'^api-auth/',
-        include('rest_framework.urls', namespace='rest_framework')),
-]
-
 urlpatterns = format_suffix_patterns(urlpatterns)  # allow to specify format
Repository: carpentries/amy
Issue: Remove slider widget from attendance count & invoice amount

Just let it accept a inputted number.
I've never used it, and actually had it cause problems when my scroll wheel activates it and changes the value.


Changed files:
workshops/forms.py

diff --git a/workshops/forms.py b/workshops/forms.py
index 7f149250..53850637 100644
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -2,7 +2,7 @@ import re
 
 from django import forms
 from django.core.validators import RegexValidator
-from django.forms import HiddenInput, CheckboxSelectMultiple
+from django.forms import HiddenInput, CheckboxSelectMultiple, TextInput
 
 from captcha.fields import ReCaptchaField
 from crispy_forms.helper import FormHelper
@@ -258,7 +258,7 @@ class EventForm(forms.ModelForm):
     )
 
     admin_fee = forms.DecimalField(min_value=0, decimal_places=2,
-                                   required=False)
+                                   required=False, widget=TextInput)
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -311,6 +311,12 @@ class EventForm(forms.ModelForm):
         # WARNING: don't change put any fields between 'country' and
         #          'longitude' that don't relate to the venue of the event
 
+        widgets = {
+            'attendance': TextInput,
+            'latitude': TextInput,
+            'longitude': TextInput,
+        }
+
     class Media:
         # thanks to this, {{ form.media }} in the template will generate
         # a <link href=""> (for CSS files) or <script src=""> (for JS files)