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

Issue:
Instructor report broken
1.  Go to https://amy.software-carpentry.org/workshops/reports/instructor_num_taught
2.  The entries in the table are URLs, not people.



Discussion:
I've got some bad news for you… it's a feature, not a bug.

Fixable?  Because the report is pretty useless as-is.

Depends. How would you like it to be?

The new API is hyperlinked and I'd like to keep it that way. Or really I'd like to have what GitHub [has](https://developer.github.com/v3/): a mix of hyperlinked and included objects (at once), but I'm unable to do it with [django-rest-framework](www.django-rest-framework.org) - or I just didn't find it.

So, Greg, can you go through the API and think what should be done and how? Thanks a lot.

I'd like to see user IDs rather than object numbers - I think 
`https://amy.software-carpentry.org/api/v1/persons/wilson_g/` should 
work as well as `https://amy.software-carpentry.org/api/v1/persons/8777/`.


Resolving patch:
diff --git a/api/serializers.py b/api/serializers.py
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -115,6 +115,7 @@ class InstructorNumTaughtSerializer(serializers.Serializer):
     person = serializers.HyperlinkedRelatedField(
         read_only=True, view_name='api:person-detail', lookup_field='pk',
         source='*')
+    name = serializers.CharField(source='get_full_name')
     num_taught = serializers.IntegerField()
 
 


Test patch:
Repository: carpentries/amy

Issue:
Typo on new event page
https://amy.software-carpentry.org/workshops/events/add/

Pre-workshop assessment survey for instructors
is listed twice.
Second should be "Post-workshop assessment...."



Discussion:

Resolving patch:
diff --git a/workshops/migrations/0084_auto_20160326_0402.py b/workshops/migrations/0084_auto_20160326_0402.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0084_auto_20160326_0402.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.2 on 2016-03-26 09:02
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0083_auto_20160323_1554'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='event',
+            name='instructors_post',
+            field=models.URLField(blank=True, default='', verbose_name='Post-workshop assessment survey for instructors'),
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -663,7 +663,7 @@ class Event(AssignmentMixin, models.Model):
         verbose_name="Pre-workshop assessment survey for instructors")
     instructors_post = models.URLField(
         blank=True, default="",
-        verbose_name="Pre-workshop assessment survey for instructors")
+        verbose_name="Post-workshop assessment survey for instructors")
     learners_longterm = models.URLField(
         blank=True, default="",
         verbose_name="Long-term assessment survey for learners")


Test patch:
Repository: carpentries/amy

Issue:
Escaped tags in every revision page
![screenshot from 2016-04-02 21 38 15](https://cloud.githubusercontent.com/assets/72821/14228635/3bd5c514-f91b-11e5-8953-753dbd8249ed.png)

Escaped tags in every revision page
![screenshot from 2016-04-02 21 38 15](https://cloud.githubusercontent.com/assets/72821/14228635/3bd5c514-f91b-11e5-8953-753dbd8249ed.png)



Discussion:

Resolving patch:
diff --git a/workshops/templatetags/diff.py b/workshops/templatetags/diff.py
--- a/workshops/templatetags/diff.py
+++ b/workshops/templatetags/diff.py
@@ -1,4 +1,5 @@
 from django import template
+from django.utils.safestring import mark_safe
 
 from reversion.helpers import generate_patch_html
 
@@ -7,4 +8,4 @@
 
 @register.simple_tag
 def semantic_diff(left, right, field):
-    return generate_patch_html(left, right, field, cleanup='semantic')
+    return mark_safe(generate_patch_html(left, right, field, cleanup='semantic'))


Test patch:
Repository: carpentries/amy

Issue:
Permission `add_todoitem` required to view event
People without ability of adding TODO items should be able to view the events.



Discussion:
A noob question here: I cannot login into any account other than `admin` even after granting them staff and superuser permission.

Am I missing something here?

Person has to be active (`Person.is_active = True`) in order to be able to log in.


Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -937,7 +937,6 @@ def all_events(request):
 
 
 @login_required
-@permission_required('workshops.add_todoitem', raise_exception=True)
 def event_details(request, event_ident):
     '''List details of a particular event.'''
     try:
@@ -953,7 +952,8 @@ def event_details(request, event_ident):
         'event': event,
     })
 
-    if request.method == "POST":
+    if request.method == "POST" and request.user.has_perm('workshops.add_todoitem'):
+        # Create ToDo items on todo_form submission only when user has permission
         todo_form = SimpleTodoForm(request.POST, prefix='todo', initial={
             'event': event,
         })


Test patch:
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/



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -2071,7 +2071,7 @@ def get_context_data(self, **kwargs):
 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')
 
 


Test patch:
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.



Discussion:

Resolving patch:
diff --git a/workshops/lookups.py b/workshops/lookups.py
--- a/workshops/lookups.py
+++ b/workshops/lookups.py
@@ -83,7 +83,7 @@ def get_query(self, request, term):
         admin_group = Group.objects.get(name='administrators')
         results = results.filter(
             Q(is_superuser=True) | Q(groups__in=[admin_group])
-        )
+        ).distinct()
         return results
 
 


Test patch:
Repository: carpentries/amy

Issue:
Show how often taught on instructor page
Results of this report https://amy.software-carpentry.org/workshops/instructors_by_date
show often each instructor has taught.

This number should also display at bottom of Task list on each instructor profile page.



Discussion:
@narayanaditya95 will tackle this

@maneesha Would you like me to list the number of times the person has assumed all possible roles or are we only interested in role of an instructor.


Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -470,7 +470,29 @@ def all_persons(request):
 @login_required
 def person_details(request, person_id):
     '''List details of a particular person.'''
-    person = get_object_or_404(Person, id=person_id)
+    try:
+        person = Person.objects.annotate(
+            num_taught=Count(
+                 Case(
+                    When(task__role__name='instructor', then=Value(1)),
+                    output_field=IntegerField()
+                )
+            ),
+            num_helper=Count(
+                Case(
+                    When(task__role__name='helper', then=Value(1)),
+                    output_field=IntegerField()
+                )
+            ),
+            num_learner=Count(
+                Case(
+                    When(task__role__name='learner', then=Value(1)),
+                     output_field=IntegerField()
+                )
+            )
+        ).get(id=person_id)
+    except Person.DoesNotExist:
+        raise Http404('Person matching query does not exist.')
     awards = person.award_set.all()
     tasks = person.task_set.all()
     lessons = person.lessons.all()


Test patch:
Repository: carpentries/amy

Issue:
Default view of all activity over time is not useful
1. Go to https://amy.software-carpentry.org/workshops/reports/all_activity_over_time
2. Display shows 'None' for all headings because dates aren't filled in.

Default dates should be anything reasonable - last 12 months, for example.

Report all activity log: inputs should have 'dateinput' class
This class allows date selector widget to pop up.



Discussion:
@narayanaditya95 will tackle this



Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -1986,6 +1986,8 @@ def all_activity_over_time(request):
     context = {
         'api_endpoint': reverse('api:reports-all-activity-over-time'),
         'title': 'All activity over time',
+        'start': datetime.date.today() - datetime.timedelta(days=365),
+        'end': datetime.date.today(),
     }
     return render(request, 'workshops/all_activity_over_time.html', context)
 


Test patch:
Repository: carpentries/amy

Issue:
Email instructors from search results
When I search for instructors using https://amy.software-carpentry.org/workshops/instructors I would like to be able to capture all their email addresses so I can send them an email.



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -1794,6 +1794,7 @@ def workshop_staff(request):
                 people = people.filter(q, task__role__name='learner') \
                                .exclude(badges__in=instructor_badges)
 
+    emails = people.filter(may_contact=True).values_list('email', flat=True)
     people = get_pagination_items(request, people)
     context = {
         'title': 'Find Workshop Staff',
@@ -1802,6 +1803,7 @@ def workshop_staff(request):
         'lessons': lessons,
         'instructor_badges': instructor_badges,
         'trainees': trainees,
+        'emails': emails,
     }
     return render(request, 'workshops/workshop_staff.html', context)
 


Test patch:
Repository: carpentries/amy

Issue:
What is the sort order for displaying Person's Tasks?
See for example 
https://amy.software-carpentry.org/workshops/person/2436

It is not chronological nor grouped by Task.



Discussion:
That's a good question - what sorting order would you like?

I would say chronological grouped by Task. 


Resolving patch:
diff --git a/workshops/migrations/0097_auto_20160618_1132.py b/workshops/migrations/0097_auto_20160618_1132.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0097_auto_20160618_1132.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.6 on 2016-06-18 16:32
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0096_change_help_text_in_training_request'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='task',
+            options={'ordering': ('role__name', 'event')},
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -1121,6 +1121,7 @@ class Task(models.Model):
 
     class Meta:
         unique_together = ("event", "person", "role")
+        ordering = ("role__name", "event")
 
     def __str__(self):
         return '{0}/{1}={2}'.format(self.event, self.person, self.role)


Test patch:
Repository: carpentries/amy

Issue:
Default invoice status 
Default invoice status when creating a new event should be "Not Invoiced" -- currently it is "Unknown"



Discussion:

Resolving patch:
diff --git a/workshops/migrations/0104_auto_20160629_1247.py b/workshops/migrations/0104_auto_20160629_1247.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0104_auto_20160629_1247.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.6 on 2016-06-29 17:47
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0103_merge'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='event',
+            name='invoice_status',
+            field=models.CharField(choices=[('unknown', 'Unknown'), ('invoiced', 'Invoiced'), ('not-invoiced', 'Not invoiced'), ('na-historic', 'Not applicable for historical reasons'), ('na-member', 'Not applicable because of membership'), ('na-self-org', 'Not applicable because self-organized'), ('na-waiver', 'Not applicable because waiver granted'), ('na-other', 'Not applicable because other arrangements made')], default='not-invoiced', max_length=40, verbose_name='Invoice status'),
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -762,7 +762,7 @@ class Event(AssignmentMixin, models.Model):
         max_length=STR_MED,
         choices=INVOICED_CHOICES,
         verbose_name='Invoice status',
-        default='unknown', blank=False,
+        default='not-invoiced', blank=False,
     )
     notes      = models.TextField(default="", blank=True)
     contact = models.CharField(max_length=STR_LONGEST, default="", blank=True)


Test patch:
diff --git a/workshops/test/base.py b/workshops/test/base.py
--- a/workshops/test/base.py
+++ b/workshops/test/base.py
@@ -302,7 +302,8 @@ def _setUpEvents(self):
         self.num_uninvoiced_events = 0
         self.num_upcoming = 0
         for e in Event.objects.all():
-            if e.invoice_status == 'not-invoiced' and e.start < today:
+            e.is_past_event = e.start < today and (e.end is None or e.end < today)
+            if e.invoice_status == 'not-invoiced' and e.is_past_event:
                 self.num_uninvoiced_events += 1
             if e.url and (e.start > today):
                 self.num_upcoming += 1
Repository: carpentries/amy

Issue:
Add navigation bar link to "update profile"
Navigation menu currently doesn't link to profile update page - it should be under "Hi, name".



Discussion:
@chrismedrela are you on it? If not, I'll add this now

Not yet.


Resolving patch:
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -422,8 +422,8 @@ def is_admin(user):
     if user is None or user.is_anonymous():
         return False
     else:
-        has_admin_group = user.groups.filter(name='administrators').exists()
-        return has_admin_group or user.is_superuser
+        return (user.is_superuser or
+                user.groups.filter(name='administrators').exists())
 
 
 class ProfileUpdateRequest(ActiveMixin, CreatedUpdatedMixin, models.Model):


Test patch:
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>.



Discussion:

Resolving patch:
diff --git a/workshops/forms.py b/workshops/forms.py
--- 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(


Test patch:
Repository: carpentries/amy

Issue:
"Cancel" button
Any place there is a "Submit" button I'd also like to see a "cancel" button so that any changes accidentally made while in editing mode are not saved.



Discussion:
A simple force reload of the page will refetch the content of form fields from the server.
It can be achieved through `Ctrl + Shift + R` or alternatively we can have a button which calls `location.reload(True)`.

@narayanaditya95 yes, that works but I'd rather have an explicit "cancel" button than to rely on browser activity like back or refresh (which is what I do now).

@maneesha Acknowledged. I would add a button that mimics the behaviour.

@chrismedrela 
I noticed that `BootstrapHelper` now adds support for `add_delete_button`. However, the delete button does not generate any functionality except for showing an alert.
Am I missing something?


Resolving patch:
diff --git a/workshops/forms.py b/workshops/forms.py
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -3,7 +3,7 @@
 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
+from crispy_forms.layout import Layout, Div, HTML, Submit, Reset
 from django import forms
 from django.core.exceptions import ValidationError
 from django.core.validators import RegexValidator
@@ -45,6 +45,7 @@ def __init__(self,
                  wider_labels=False,
                  add_submit_button=True,
                  add_delete_button=False,
+                 add_reset_button=True,
                  additional_form_class='',
                  form_tag=True,
                  display_labels=True,
@@ -107,6 +108,10 @@ def __init__(self,
                 css_class='btn-danger',
                 style='float: right;'))
 
+        if add_reset_button:
+            self.add_input(
+                Reset('reset', 'Reset', css_class='pull-right btn-danger'))
+
         self.form_class = 'form-horizontal ' + additional_form_class
 
         self.form_tag = form_tag
@@ -277,7 +282,7 @@ class SearchForm(forms.Form):
                                      required=False,
                                      initial=True)
 
-    helper = bootstrap_helper
+    helper = BootstrapHelper(add_reset_button=False)
 
 
 class DebriefForm(forms.Form):


Test patch:
Repository: carpentries/amy

Issue:
Date(?) stamp when updating record from url
When updating an event from url, the list of instructors/helpers is recorded in the notes field with a heading that looks like a date but isn't.  For example an update I made today has the heading "UPDATE 2016-5-4:"

What is this heading supposed to be?  Can we make it be the real date of the update (i.e., UPDATE 2016-06-16:")



Discussion:
@maneesha the reason why this is so ugly is because we're translating month and day numbers as integers from JavaScript into text. JavaScript is unbelievably stupid in this area (just see how many different and unpleasant solutions are to this particular problem: http://stackoverflow.com/questions/3605214/javascript-add-leading-zeroes-to-date).

We'll deal with it for v1.7.


Resolving patch:
diff --git a/workshops/forms.py b/workshops/forms.py
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -359,6 +359,7 @@ class Media:
         # thanks to this, {{ form.media }} in the template will generate
         # a <link href=""> (for CSS files) or <script src=""> (for JS files)
         js = (
+            'date_yyyymmdd.js',
             'import_from_url.js', 'update_from_url.js',
             'online_country.js',
         )


Test patch:
Repository: carpentries/amy

Issue:
Unset variable on Host detail page
If a `Membership` object has the field `workshops_without_admin_fee_per_year` set to null, the corresponding `host_detail` page shows `None` in the imposed column and `xxx-unset...` in the remaining column of the Membership table.



Discussion:
@pbanaszkiewicz 
All `Membership` objects in the prod database have non-null `workshops_without_admin_fee_per_year` field.
Should we enforce `null=False` on the `workshops_without_admin_fee_per_year` field to circumvent the issue?
P.S. If there exists a use case for `null` IntegerFields, we can edit the template.
What should be the approach?

Can someone please either close or implement?

@narayanaditya95 I think we need to add a `|default:"—"` to the template so that `xxx-unset…` disappears.


Resolving patch:
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -134,6 +134,8 @@ def workshops_without_admin_fee_per_year_completed(self):
     def workshops_without_admin_fee_per_year_remaining(self):
         """Count remaining workshops w/o admin fee for the year agreement
         started."""
+        if not self.workshops_without_admin_fee_per_year:
+            return None
         a = self.workshops_without_admin_fee_per_year
         b = self.workshops_without_admin_fee_per_year_completed
         return a - b
@@ -152,6 +154,8 @@ def self_organized_workshops_per_year_completed(self):
     def self_organized_workshops_per_year_remaining(self):
         """Count remaining self-organized workshops for the year agreement
         started."""
+        if not self.self_organized_workshops_per_year:
+            return None
         a = self.self_organized_workshops_per_year
         b = self.self_organized_workshops_per_year_completed
         return a - b


Test patch:
Repository: carpentries/amy

Issue:
API still calls event.get_ident
Which triggers an error for every admin on the dashboard :(



Discussion:

Resolving patch:
diff --git a/api/serializers.py b/api/serializers.py
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -96,7 +96,7 @@ def get_content(self, obj):
 
         return '<a href="{url}">{event}</a><br><small>{todo}</small>'.format(
             url=obj.event.get_absolute_url(),
-            event=obj.event.get_ident(),
+            event=obj.event.slug,
             todo=obj.title,
         )
 


Test patch:
Repository: carpentries/amy

Issue:
Bad links in training event display
1. Go to https://amy.software-carpentry.org/workshops/trainings?page=2
2. Click on any of the links that supposedly lead to a training event.
3. 404

I understand why we want to retain this view - it's helpful to look at summary stats - but:
1. The links need to resolve.
2. We need to be able to sort chronologically with most recent first.



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -1847,7 +1847,7 @@ def all_trainings(request):
         .values('event').annotate(finished=Count('person'))
     finished = {f['event']: f['finished'] for f in finished}
 
-    trainings = Task.objects.filter(role=learner).filter(event__tags=ttt).order_by('event__start') \
+    trainings = Task.objects.filter(role=learner).filter(event__tags=ttt).order_by('-event__start') \
         .values('event', 'event__slug').annotate(trainees=Count('person'))
     for t in trainings:
         event_id = t['event']


Test patch: