internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: carpentries/amy

Issue:
Find workshop staff: broken Select2 widget widths
![screenshot from 2018-06-01 21-56-25](https://user-images.githubusercontent.com/72821/40860742-c4b38724-65e6-11e8-99a9-afdda7cdd94e.png)
![screenshot from 2018-06-01 21-57-08](https://user-images.githubusercontent.com/72821/40860743-c4dc58a2-65e6-11e8-9762-fd702953204d.png)



Discussion:

Resolving patch:
diff --git a/workshops/filters.py b/workshops/filters.py
--- a/workshops/filters.py
+++ b/workshops/filters.py
@@ -6,7 +6,7 @@
 from django.forms import widgets
 from django_countries import Countries
 
-from workshops.forms import bootstrap_helper_filter
+from workshops.forms import bootstrap_helper_filter, SIDEBAR_DAL_WIDTH
 from workshops.models import (
     Event,
     Organization,
@@ -25,13 +25,6 @@
 
 EMPTY_SELECTION = (None, '---------')
 
-# settings for Select2
-# this makes it possible for autocomplete widget to fit in low-width sidebar
-SIDEBAR_DAL_WIDTH = {
-    'data-width': '100%',
-    'width': 'style',
-}
-
 
 class AllCountriesFilter(django_filters.ChoiceFilter):
     @property
diff --git a/workshops/forms.py b/workshops/forms.py
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -45,6 +45,14 @@
 )
 
 
+# settings for Select2
+# this makes it possible for autocomplete widget to fit in low-width sidebar
+SIDEBAR_DAL_WIDTH = {
+    'data-width': '100%',
+    'width': 'style',
+}
+
+
 class BootstrapHelper(FormHelper):
     """Layout and behavior for crispy-displayed forms."""
     html5_required = True
@@ -227,20 +235,28 @@ class WorkshopStaffForm(forms.Form):
         label='Airport',
         required=False,
         queryset=Airport.objects.all(),
-        widget=autocomplete.ModelSelect2(url='airport-lookup')
+        widget=autocomplete.ModelSelect2(
+            url='airport-lookup',
+            attrs=SIDEBAR_DAL_WIDTH,
+        )
     )
     languages = forms.ModelMultipleChoiceField(
         label='Languages',
         required=False,
         queryset=Language.objects.all(),
-        widget=autocomplete.ModelSelect2Multiple(url='language-lookup')
+        widget=autocomplete.ModelSelect2Multiple(
+            url='language-lookup',
+            attrs=SIDEBAR_DAL_WIDTH,
+        )
     )
 
     country = forms.MultipleChoiceField(choices=[])
 
-    lessons = forms.ModelMultipleChoiceField(queryset=Lesson.objects.all(),
-                                             widget=CheckboxSelectMultiple(),
-                                             required=False)
+    lessons = forms.ModelMultipleChoiceField(
+        queryset=Lesson.objects.all(),
+        widget=SelectMultiple(),
+        required=False,
+    )
 
     INSTRUCTOR_BADGE_CHOICES = (
         ('swc-instructor', 'Software Carpentry Instructor'),
@@ -278,7 +294,6 @@ def __init__(self, *args, **kwargs):
                                                            required=False)
 
         self.helper = FormHelper(self)
-        self.helper.form_class = 'form-inline'
         self.helper.form_method = 'get'
         self.helper.layout = Layout(
             Div(


Test patch:
Repository: carpentries/amy

Issue:
Instructor view should show all activity
Instructors can log in to their own profiles and see the workshops they have taught at.  This view should also show all other roles they have played (helper, host, learner, etc)


Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -2973,7 +2973,7 @@ def trainee_dashboard(request):
         badge__name='dc-instructor').exists()
 
     # Workshops person taught at
-    workshops = request.user.task_set.filter(role__name='instructor')
+    workshops = request.user.task_set.all()
 
     if request.method == 'POST' and 'swc-submit' in request.POST:
         requirement = TrainingRequirement.objects.get(name='SWC Homework')


Test patch:
Repository: carpentries/amy

Issue:
Add search by github handle to API persons endpoint
Would it be possible to add the functionality to search for people using their github handle through the API? i.e. `https://amy.software-carpentry.org/api/v1/persons/?github=fmichonneau`


Discussion:
Hello @fmichonneau. It's possible to add another filter to this endpoint, but with #1273 I was going to remove unused parts of API. I thought `/persons/` was unused.

Let's make this issue in current release (v1.12), and let's postpone #1273 to the next release.

Resolving patch:
diff --git a/api/filters.py b/api/filters.py
--- a/api/filters.py
+++ b/api/filters.py
@@ -59,7 +59,7 @@ class Meta:
         model = Person
         fields = (
             'badges', 'username', 'personal', 'middle', 'family', 'email',
-            'may_contact', 'publish_profile',
+            'may_contact', 'publish_profile', 'github',
         )
         order_by = (
             'lastname', '-lastname', 'firstname', '-firstname', 'email',


Test patch:
Repository: carpentries/amy

Issue:
add ORCID to API persons endpoint
could you please add the ORCID field to the data coming from the `persons` endpoint in the API? Thanks!


Discussion:

Resolving patch:
diff --git a/api/serializers.py b/api/serializers.py
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -212,8 +212,8 @@ class Meta:
         model = Person
         fields = (
             'personal', 'middle', 'family', 'email', 'gender', 'may_contact',
-            'publish_profile',
-            'airport', 'github', 'twitter', 'url', 'username', 'notes',
+            'publish_profile', 'airport',
+            'github', 'twitter', 'url', 'orcid', 'username', 'notes',
             'affiliation', 'badges', 'lessons', 'domains', 'awards', 'tasks',
         )
 


Test patch:
Repository: carpentries/amy

Issue:
list of languages should be alphabetized
In a Person's profile, they can select the languages they speak.  The list seems to be in random order; this should be alphabetized.




Discussion:

Resolving patch:
diff --git a/workshops/migrations/0157_auto_20181006_0859.py b/workshops/migrations/0157_auto_20181006_0859.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0157_auto_20181006_0859.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.1 on 2018-10-06 13:59
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0156_auto_20180927_1516'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='language',
+            options={'ordering': ['name']},
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -919,6 +919,9 @@ class Language(models.Model):
     def __str__(self):
         return self.name
 
+    class Meta:
+        ordering = ["name"]
+
 #------------------------------------------------------------
 
 # In order to make our custom filters chainable, we have to


Test patch:
Repository: carpentries/amy

Issue:
500 Server Error when filtering on Events API
https://amy.software-carpentry.org/api/v1/events/published/ API throws 500 Server error when using parameters to filter the call. 

For example: https://amy.software-carpentry.org/api/v1/events/?completed=&tag=SWC&start=&start_before=&start_after=&end=&end_before=&end_after=&order_by=start

I tried with several parameter combinations and all threw 500 Server error.


Discussion:

Resolving patch:
diff --git a/api/filters.py b/api/filters.py
--- a/api/filters.py
+++ b/api/filters.py
@@ -8,22 +8,16 @@
 from workshops.models import Event, Task, Tag, Person, Badge
 
 
-def filter_tag_by_name(queryset, name, values):
-    # tags = Tag.objects.filter(name__in=values)
-    # for tag in tags:
-    #     queryset = queryset.filter(tags=tag)
-    # return queryset
-    return Tag.objects.all()
-
-
 class EventFilter(filters.FilterSet):
     start_after = filters.DateFilter(field_name='start', lookup_expr='gte')
     start_before = filters.DateFilter(field_name='start', lookup_expr='lte')
     end_after = filters.DateFilter(field_name='end', lookup_expr='gte')
     end_before = filters.DateFilter(field_name='end', lookup_expr='lte')
-    TAG_CHOICES = Tag.objects.all().values_list('name', 'name')
-    tag = filters.MultipleChoiceFilter(
-        choices=TAG_CHOICES, field_name='tags', method=filter_tag_by_name,
+    tags = filters.ModelMultipleChoiceFilter(
+        field_name='tags__name',
+        to_field_name='name',
+        queryset=Tag.objects.all(),
+        conjoined=True,
     )
     order_by = filters.OrderingFilter(
         fields=(
@@ -36,7 +30,7 @@ class EventFilter(filters.FilterSet):
     class Meta:
         model = Event
         fields = (
-            'completed', 'tag',
+            'completed', 'tags',
             'start', 'start_before', 'start_after',
             'end', 'end_before', 'end_after',
         )
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -1073,7 +1073,9 @@ class Event(AssignmentMixin, models.Model):
         null=True, blank=True,
         help_text=PUBLISHED_HELP_TEXT,
     )
-    end        = models.DateField(null=True, blank=True)
+    end = models.DateField(
+        null=True, blank=True,
+    )
     slug = models.SlugField(
         max_length=STR_LONG, unique=True,
         help_text='Use <code>YYYY-MM-DD-location</code> format, where '


Test patch:
Repository: carpentries/amy

Issue:
Active member sites not showing up in report
When I go here:   https://amy.software-carpentry.org/workshops/reports/membership_trainings_stats/
and type "stanford" for organization name, I see this page with memberships listed for the past years:  https://amy.software-carpentry.org/workshops/reports/membership_trainings_stats/?organization_name=stanford&order_by=

However when I check "Only show active memberships" no memberships show up even though there is an active membership.

I see similar behavior when I search for other sites, while some other sites work as expected.


Discussion:

Resolving patch:
diff --git a/workshops/filters.py b/workshops/filters.py
--- a/workshops/filters.py
+++ b/workshops/filters.py
@@ -251,12 +251,12 @@ class Meta:
         ]
 
 
-def filter_training_seats_only(queryset, name, active):
+def filter_active_memberships_only(queryset, name, active):
     """Limit Memberships to only active entries."""
     if active:
         today = date.today()
-        return queryset.filter(agreement_start__gte=today,
-                               agreement_end__lte=today)
+        return queryset.filter(agreement_start__lte=today,
+                               agreement_end__gte=today)
     else:
         return queryset
 
@@ -291,7 +291,7 @@ class MembershipFilter(AMYFilterSet):
 
     active_only = django_filters.BooleanFilter(
         label='Only show active memberships',
-        method=filter_training_seats_only,
+        method=filter_active_memberships_only,
         widget=widgets.CheckboxInput)
 
     training_seats_only = django_filters.BooleanFilter(
@@ -332,7 +332,7 @@ class MembershipTrainingsFilter(AMYFilterSet):
 
     active_only = django_filters.BooleanFilter(
         label='Only show active memberships',
-        method=filter_training_seats_only,
+        method=filter_active_memberships_only,
         widget=widgets.CheckboxInput)
 
     training_seats_only = django_filters.BooleanFilter(


Test patch:
Repository: carpentries/amy

Issue:
Instructor badges don't display
On the workshop page, when all affiliated people are listed, badged people are supposed to display the badge next to their name.  This does not show up.


Discussion:

Resolving patch:
diff --git a/api/serializers.py b/api/serializers.py
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -344,7 +344,7 @@ def get_awards(self, obj):
     def get_training_tasks(self, obj):
         if obj.person:
             return ", ".join(
-                map(lambda x: x.event.slug, obj.person.task_set.all())
+                map(lambda x: x.event.slug, obj.person.training_tasks)
             )
         else:
             return ""
diff --git a/api/views.py b/api/views.py
--- a/api/views.py
+++ b/api/views.py
@@ -331,13 +331,10 @@ class TrainingRequests(ListAPIView):
                     queryset=Award.objects.select_related('badge'),
                 ),
                 Prefetch('person__task_set',
-                    # We change the attribute "person.task_set" instead of
-                    # directing this filtered tasks to a new attribute, because
-                    # DRF fails to recognize it in the serializer.
-                    # to_attr='training_tasks',
                     queryset=Task.objects
                         .filter(role__name='learner', event__tags__name='TTT')
-                        .select_related('event')
+                        .select_related('event'),
+                    to_attr='training_tasks',
                 ),
             )
         )
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -1051,8 +1051,8 @@ def event_details(request, slug):
 
     person_instructor_badges = Prefetch(
         'person__badges',
-        to_attr='person_instructor_badges',
-        queryset=Badge.objects.filter(name__in=Badge.INSTRUCTOR_BADGES)
+        to_attr='instructor_badges',
+        queryset=Badge.objects.instructor_badges()
     )
     tasks = (
         Task.objects


Test patch:
Repository: carpentries/amy

Issue:
Persons API bug - duplicate entries returned
Think I discovered a possible bug in persons API - possibly other parts of the API are affected. Certain number of duplicated entries are returned, e.g. when searching for UK instructors at page 2 of results there are two entries for "m.wallis@leeds.ac.uk", on page 3 there are 2 entries for "m.croucher@sheffield.ac.uk", and so on.

See for example page 2 of results:
https://amy.software-carpentry.org/api/v1/persons/?country=GB&email=&family=&github=&is_instructor=true&may_contact=&middle=&order_by=&page=2&personal=&publish_profile=&username= 


Discussion:

Resolving patch:
diff --git a/api/views.py b/api/views.py
--- a/api/views.py
+++ b/api/views.py
@@ -736,7 +736,8 @@ class PersonViewSet(viewsets.ReadOnlyModelViewSet):
     """List many people or retrieve only one person."""
     permission_classes = (IsAuthenticated, IsAdmin)
     queryset = Person.objects.all().select_related('airport') \
-                     .prefetch_related('badges', 'domains', 'lessons')
+                     .prefetch_related('badges', 'domains', 'lessons') \
+                     .distinct()
     serializer_class = PersonSerializer
     pagination_class = StandardResultsSetPagination
     filterset_class = PersonFilter


Test patch:
Repository: carpentries/amy

Issue:
Make selection boxes in user permissions expandable
The selection boxes here

https://amy.software-carpentry.org/workshops/person/XXX/permissions/

should be expandable so users can make them bigger to more easily view many or all options.


Discussion:

Resolving patch:
diff --git a/amy/workshops/forms.py b/amy/workshops/forms.py
--- a/amy/workshops/forms.py
+++ b/amy/workshops/forms.py
@@ -3,6 +3,7 @@
 from crispy_forms.helper import FormHelper
 from crispy_forms.layout import Layout, Div, HTML, Submit, Button, Field
 from django import forms
+from django.contrib.auth.models import Permission
 from django.forms import (
     SelectMultiple,
     CheckboxSelectMultiple,
@@ -595,6 +596,17 @@ class Meta(PersonForm.Meta):
 
 
 class PersonPermissionsForm(forms.ModelForm):
+    helper = BootstrapHelper(add_cancel_button=False)
+
+    user_permissions = forms.ModelMultipleChoiceField(
+        label=Person._meta.get_field('user_permissions').verbose_name,
+        help_text=Person._meta.get_field('user_permissions').help_text,
+        required=False,
+        queryset=Permission.objects.select_related('content_type'),
+    )
+    user_permissions.widget.attrs.update({'class': 'resizable-vertical',
+                                          'size': '20'})
+
     class Meta:
         model = Person
         # only display administration-related fields: groups, permissions,
diff --git a/amy/workshops/views.py b/amy/workshops/views.py
--- a/amy/workshops/views.py
+++ b/amy/workshops/views.py
@@ -13,6 +13,7 @@
     PermissionRequiredMixin,
     UserPassesTestMixin,
 )
+from django.contrib.auth.models import Permission
 from django.contrib.auth.views import logout_then_login
 from django.core.exceptions import (
     ObjectDoesNotExist,
@@ -581,9 +582,12 @@ class PersonDelete(OnlyForAdminsMixin, PermissionRequiredMixin,
 class PersonPermissions(OnlyForAdminsMixin, PermissionRequiredMixin,
                         AMYUpdateView):
     permission_required = 'workshops.change_person'
-    model = Person
     form_class = PersonPermissionsForm
     pk_url_kwarg = 'person_id'
+    queryset = Person.objects.prefetch_related('groups', Prefetch(
+            'user_permissions',
+            queryset=Permission.objects.select_related('content_type'),
+        ))
 
 
 @login_required


Test patch:
Repository: carpentries/amy

Issue:
Badge selector on "Find workshop staff" should be "OR" not "AND"
When you go to [Find Workshop Staff](https://amy.software-carpentry.org/workshops/workshop_staff/) if neither badge (SWC nor DC) is selected the query returns unbadged people.

If both badges (SWC and DC) are selected the query returns people with both badges.  This should change so if both badges are selected this should return people with either badge.  

Once Library Carpentry is included as a [badge](https://github.com/swcarpentry/amy/issues/1417), selecting all badges should return people with any Instructor badge (SWC, DC, or LC).


Discussion:

Resolving patch:
diff --git a/amy/workshops/views.py b/amy/workshops/views.py
--- a/amy/workshops/views.py
+++ b/amy/workshops/views.py
@@ -1453,8 +1453,10 @@ def workshop_staff(request):
                 people = people.filter(gender=data['gender'])
 
             if data['instructor_badges']:
+                instr_badges_q = Q()
                 for badge in data['instructor_badges']:
-                    people = people.filter(badges__name=badge)
+                    instr_badges_q |= Q(badges__name=badge)
+                people = people.filter(instr_badges_q)
 
             # it's faster to count role=helper occurences than to check if user
             # had a role=helper


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
@@ -1,5 +1,3 @@
-import unittest
-
 from django.urls import reverse
 
 from workshops.tests.base import TestBase
@@ -126,7 +124,9 @@ def test_match_gender(self):
         self.assertIn(self.blackwidow, response.context['persons'])
 
     def test_instructor_badges(self):
-        """Ensure people with instructor badges are returned by search."""
+        """Ensure people with instructor badges are returned by search. The
+        search is OR'ed, so should return people with any of the selected
+        badges."""
         response = self.client.get(
             reverse('workshop_staff'),
             {'airport': self.airport_0_0.pk,
@@ -136,10 +136,9 @@ def test_instructor_badges(self):
         self.assertEqual(response.status_code, 200)
 
         # instructors
-        self.assertIn(self.hermione, response.context['persons'])
-        self.assertIn(self.harry, response.context['persons'])
-        # Ron doesn't have a DC badge
-        self.assertNotIn(self.ron, response.context['persons'])
+        self.assertIn(self.hermione, response.context['persons'])  # SWC, DC
+        self.assertIn(self.harry, response.context['persons'])  # SWC, DC
+        self.assertIn(self.ron, response.context['persons'])  # SWC only
         # non-instructors
         self.assertNotIn(self.spiderman, response.context['persons'])
         self.assertNotIn(self.ironman, response.context['persons'])
Repository: carpentries/amy

Issue:
Membership edit fails due to form errors
The error is due to missing `comment` field in `MembershipForm`.

`MembershipForm.save()` should be placed in `MembershipCreateForm`, because that's where `comment` field is and that's what should trigger the signal.


Discussion:

Resolving patch:
diff --git a/amy/fiscal/forms.py b/amy/fiscal/forms.py
--- a/amy/fiscal/forms.py
+++ b/amy/fiscal/forms.py
@@ -92,16 +92,6 @@ class Meta:
             'additional_instructor_training_seats',
         ]
 
-    def save(self, *args, **kwargs):
-        res = super().save(*args, **kwargs)
-
-        create_comment_signal.send(sender=self.__class__,
-                                   content_object=res,
-                                   comment=self.cleaned_data['comment'],
-                                   timestamp=None)
-
-        return res
-
 
 class MembershipCreateForm(MembershipForm):
     comment = MarkdownxFormField(
@@ -116,6 +106,16 @@ class Meta(MembershipForm.Meta):
         fields = MembershipForm.Meta.fields.copy()
         fields.append('comment')
 
+    def save(self, *args, **kwargs):
+        res = super().save(*args, **kwargs)
+
+        create_comment_signal.send(sender=self.__class__,
+                                   content_object=res,
+                                   comment=self.cleaned_data['comment'],
+                                   timestamp=None)
+
+        return res
+
 
 class SponsorshipForm(WidgetOverrideMixin, forms.ModelForm):
 


Test patch:
diff --git a/amy/fiscal/tests/test_membership.py b/amy/fiscal/tests/test_membership.py
--- a/amy/fiscal/tests/test_membership.py
+++ b/amy/fiscal/tests/test_membership.py
@@ -4,7 +4,7 @@
 from django.urls import reverse
 from django_comments.models import Comment
 
-from fiscal.forms import MembershipCreateForm
+from fiscal.forms import MembershipCreateForm, MembershipForm
 from workshops.tests.base import TestBase
 from workshops.models import Membership, Organization, Event, Role, Tag, Task
 
@@ -210,3 +210,23 @@ def test_creating_event_with_comment(self):
         comment = Comment.objects.first()
         self.assertEqual(comment.comment, 'This is a test comment.')
         self.assertIn(comment, Comment.objects.for_model(obj))
+
+    def test_workshop_edit_form_no_comment(self):
+        """Ensure workshop edit form works and doesn't provide `comment` field.
+
+        This is a regression test against #1437:
+        https://github.com/swcarpentry/amy/issues/1437
+        """
+        self.assertNotIn('comment', MembershipForm.Meta.fields)
+
+        self.assertEqual(Comment.objects.count(), 0)
+        data = {
+            'organization': self.org_alpha.pk,
+            'variant': 'partner',
+            'contribution_type': 'financial',
+            'additional_instructor_training_seats': 0,
+            'seats_instructor_training': 0,
+        }
+        form = MembershipForm(data, instance=self.current)
+        form.save()
+        self.assertEqual(Comment.objects.count(), 0)