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

Issue:
Events should be ordered by start date, not by insertion order
The default display of events should sort them by start date (or equivalently slug), not by table insertion order.  (Right now, training cohorts are converted to events after actual events have been added, so all the training cohorts show up out of time order.)



Discussion:
> sort them by start date

Ascending or descending?

Most recent first, since that's most likely to be interesting.
Thanks,
Greg


Resolving patch:
diff --git a/workshops/migrations/0040_auto_20150120_1610.py b/workshops/migrations/0040_auto_20150120_1610.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0040_auto_20150120_1610.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0039_remove_tag_slug'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='event',
+            options={'ordering': ('-start',)},
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -225,6 +225,9 @@ class Event(models.Model):
     admin_fee  = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
     notes      = models.TextField(default="", blank=True)
 
+    class Meta:
+        ordering = ('-start', )
+
     # Set the custom manager
     objects = EventManager()
 
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -249,7 +249,7 @@ class PersonUpdate(UpdateViewContext):
 def all_events(request):
     '''List all events.'''
 
-    all_events = Event.objects.order_by('id')
+    all_events = Event.objects.all()
     events = _get_pagination_items(request, all_events)
     for e in events:
         e.num_instructors = e.task_set.filter(role__name='instructor').count()


Test patch:
Repository: carpentries/amy

Issue:
Crash when viewing upcoming event
This is either a data import error or a bug in Amy; either way, Amy should give something better than a stack trace when something goes wrong.
1.  `make clean`
2.  `make database` to import faked data.
3.  `make serve` to run.
4.  go to http://127.0.0.1:8000/workshops/
5.  fifth event down under 'upcoming events' is labelled 2015-04-xx-ttt-nih - click on it.
6.  boom: "ObjectDoesNotExist at /workshops/event/2015-04-xx-ttt-nih"

Other upcoming events display properly, and occurs as of b2e0c6058.



Discussion:
Regexp here: https://github.com/swcarpentry/amy/blob/a3db9535a289d44c3cf429f61c3a1beca1665d63/workshops/models.py#L234 fails to catch YYYY-MM pattern, I'll provide a PR for that soon


Resolving patch:
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -228,10 +228,13 @@ def get_by_ident(ident):
         The real indicator is the 'published' flag.
         '''
 
+        # match event ID
         if re.match(r'^\d+$', ident):
             return Event.objects.get(id=ident)
 
-        if re.match(r'^\d{4}-\d{2}-\d{2}-.+$', ident):
+        # match event slug in format YYYY-MM-**** (ie. this works for
+        # YYYY-MMMM-DD-**** too)
+        if re.match(r'^\d{4}-\d{2}-.+$', ident):
             return Event.objects.get(slug=ident)
 
         raise ObjectDoesNotExist(ident)


Test patch:
Repository: carpentries/amy

Issue:
Remove the groups list.
The "new person" form shouldn't mention groups unless/until we start using Django's permission groups.



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -187,6 +187,13 @@ class AirportUpdate(LoginRequiredMixin, UpdateViewContext):
 #------------------------------------------------------------
 
 
+PERSON_FIELDS = [
+        field.name for field in Person._meta.fields
+    ] + [
+        'user_permissions',
+    ]
+
+
 @login_required
 def all_persons(request):
     '''List all persons.'''
@@ -380,12 +387,12 @@ def person_bulk_add_confirmation(request):
 
 class PersonCreate(LoginRequiredMixin, CreateViewContext):
     model = Person
-    fields = '__all__'
+    fields = PERSON_FIELDS
 
 
 class PersonUpdate(LoginRequiredMixin, UpdateViewContext):
     model = Person
-    fields = '__all__'
+    fields = PERSON_FIELDS
     pk_url_kwarg = 'person_id'
 
 


Test patch:
Repository: carpentries/amy

Issue:
Must not require people to fill in 'Notes' when creating new sites and events
The `notes` field in `Site` and `Event` is optional - people must not be required to fill it in when creating new sites or events.



Discussion:
On Thu, Feb 19, 2015 at 06:14:57AM -0800, Greg Wilson wrote:

> The `notes` field in `Site` and `Event` is optional - people must
> not be required to fill it in when creating new sites or events.

I'd expect this to be covered (at least for Event) by #195, and I
can't reproduce the problem in local Event testing.


Resolving patch:
diff --git a/workshops/migrations/0002_auto_20150219_1305.py b/workshops/migrations/0002_auto_20150219_1305.py
new file mode 100644
--- /dev/null
+++ b/workshops/migrations/0002_auto_20150219_1305.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('workshops', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='site',
+            name='notes',
+            field=models.TextField(blank=True, default=''),
+            preserve_default=True,
+        ),
+    ]
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -23,7 +23,7 @@ class Site(models.Model):
     domain     = models.CharField(max_length=STR_LONG, unique=True)
     fullname   = models.CharField(max_length=STR_LONG, unique=True)
     country    = models.CharField(max_length=STR_LONG, null=True)
-    notes      = models.TextField(default="")
+    notes      = models.TextField(default="", blank=True)
 
     def __str__(self):
         return self.domain


Test patch:
Repository: carpentries/amy

Issue:
Bulk-upload: New User added even in case of existing user
According to #225 , If a existing user with case insensitive email Id is uploaded with a new task, a new user is being added with that case insensitive email and no task is being added to the existing user.



Discussion:

Resolving patch:
diff --git a/workshops/util.py b/workshops/util.py
--- a/workshops/util.py
+++ b/workshops/util.py
@@ -184,7 +184,7 @@ def create_uploaded_persons_tasks(data):
                 if fields['email']:
                     # we should use existing Person or create one
                     p, created = Person.objects.get_or_create(
-                        email=fields['email'], defaults=fields
+                        email__iexact=fields['email'], defaults=fields
                     )
 
                     if created:


Test patch:
Repository: carpentries/amy

Issue:
Link to debrief page + datepicker
1. Add link to `/debrief/` page
2. Add Bootstrap's datepicker to that page

This should land after #221.



Discussion:

Resolving patch:
diff --git a/workshops/forms.py b/workshops/forms.py
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -72,10 +72,14 @@ class SearchForm(forms.Form):
                                     required=False,
                                     initial=True)
 
+
 class DebriefForm(forms.Form):
     '''Represent general debrief form.'''
-
-    begin_date = forms.CharField(label='Begin date as YYYY-MM-DD',
-                           max_length=10)
-    end_date = forms.CharField(label='End date as YYYY-MD-DD',
-                           max_length=10)
+    begin_date = forms.DateField(
+        label='Begin date as YYYY-MM-DD',
+        input_formats=['%Y-%m-%d', ]
+    )
+    end_date = forms.DateField(
+        label='End date as YYYY-MD-DD',
+        input_formats=['%Y-%m-%d', ]
+    )
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -609,26 +609,25 @@ def search(request):
 def debrief(request):
     '''Show who taught between begin_date and end_date.'''
 
+    tasks = None
+
     if request.method == 'POST':
         form = DebriefForm(request.POST)
         if form.is_valid():
             tasks = Task.objects.filter(
-                    event__end__gte=form.cleaned_data['begin_date'],
-                    event__start__lte=form.cleaned_data['end_date'],
-                    role__name='instructor',
-                    person__may_contact=True,
-                    ).order_by('event', 'person', 'role')
-        else:
-            pass # FIXME: error message
+                event__end__gte=form.cleaned_data['begin_date'],
+                event__start__lte=form.cleaned_data['end_date'],
+                role__name='instructor',
+                person__may_contact=True,
+            ).order_by('event', 'person', 'role')
 
-    # if a GET (or any other method) we'll create a blank form
     else:
+        # if a GET (or any other method) we'll create a blank form
         form = DebriefForm()
-        tasks = None
 
-    context = {'title' : 'Debrief',
-               'form' : form,
-               'all_tasks' : tasks}
+    context = {'title': 'Debrief',
+               'form': form,
+               'all_tasks': tasks}
     return render(request, 'workshops/debrief.html', context)
 
 #------------------------------------------------------------


Test patch:
Repository: carpentries/amy

Issue:
Use Bower for JS, CSS dependencies
We need something to manage our JavaScript (and perhaps CSS) dependencies and this tool is called [Bower](http://bower.io).

@wking [wrote](https://github.com/swcarpentry/amy/pull/239#issuecomment-89585714) some handy instructions on getting Bower up and running.

Bower packages we'd use for now:
- `bootstrap`
- `bootstrap-datepicker`

(This should land after #239)



Discussion:
We now have an additional dependency: a jQuery UI Combobox. @neon-ninja do you have a link to the source of that jQ plugin?

@wking do you perhaps have any opinions on https://github.com/nvbn/django-bower?

On Sat, Apr 18, 2015 at 05:36:28AM -0700, Piotr Banaszkiewicz wrote:

> @wking do you perhaps have any opinions on
> https://github.com/nvbn/django-bower?

I don't think it's worth the trouble of a separate layer [1](https://github.com/swcarpentry/amy/pull/105#issuecomment-67906486).  Just
have Bower install to workshops/static [2](https://github.com/swcarpentry/amy/pull/239#issuecomment-89585714) before doing any
static-manipulation in Django.


Resolving patch:
diff --git a/amy/settings.py b/amy/settings.py
--- a/amy/settings.py
+++ b/amy/settings.py
@@ -119,6 +119,10 @@
 # https://docs.djangoproject.com/en/1.7/howto/static-files/
 
 STATIC_URL = '/static/'
+STATIC_ROOT = os.path.join(BASE_DIR, 'static')
+STATICFILES_DIRS = (
+    os.path.join(BASE_DIR, 'bower_components'),
+)
 
 # Warn viewers of invalid template strings
 TEMPLATE_STRING_IF_INVALID = 'XXX-unset-variable-XXX'


Test patch:
Repository: carpentries/amy

Issue:
Some workshops at the database are missing the Organizer field



Discussion:
Yup - in many cases, we don't know who to list, and in others we just 
haven't had time to enter the data.

I just raise this because it is a required field. @gvwilson you can close this if you want.

Yeah, 'organizer' should be made optional, not mandatory - in the case 
of self-organized workshops, we can guess that the organizer is the same 
as the host institution, but it isn't always, and it doesn't really 
matter.  I'll PR.
Thanks,
Greg


Resolving patch:
diff --git a/workshops/forms.py b/workshops/forms.py
--- a/workshops/forms.py
+++ b/workshops/forms.py
@@ -133,7 +133,7 @@ class EventForm(forms.ModelForm):
     organizer = selectable.AutoCompleteSelectField(
         lookup_class=lookups.SiteLookup,
         label='Organizer',
-        required=True,
+        required=False,
         help_text=AUTOCOMPLETE_HELP_TEXT,
         widget=selectable.AutoComboboxSelectWidget,
     )


Test patch:
Repository: carpentries/amy

Issue:
Change name of app
Having the same name for a person and the app is confusing, especially since we both work in the same areas. Suggest "amybot" for the app.



Discussion:
There's ["AMY - AdMinister Yourself"](https://github.com/swcarpentry/amy/pull/276#issuecomment-107134760) suggestion, too.

Pros:
- no need to rename repositories
- not much work if we switch (we only need to uppercase all mentions in docs)

Cons:
- I'm not 100% sold on the acronym, maybe someone can figure something better

Yeah, all uppercase would be fine, I guess -- at least it would disambiguate in text, if not in speech. (When do we ever speak to each other anyway?!)  We don't have to think of an acronym. :)

@gvwilson it's your call, what do we do? Do we change the name from "Amy" to "AMY" after v0.3 release, ie. for v0.4?

But I _like_ calling it Amy... and the confusion is likely to be 
temporary...


Resolving patch:
diff --git a/workshops/templatetags/breadcrumbs.py b/workshops/templatetags/breadcrumbs.py
--- a/workshops/templatetags/breadcrumbs.py
+++ b/workshops/templatetags/breadcrumbs.py
@@ -105,7 +105,7 @@ def breadcrumb_main_page():
     Example usage:
         {% breadcrumb_main_page %}
     '''
-    title = 'Amy'
+    title = 'AMY'
     url = reverse('index')
     return create_crumb(title, url)
 


Test patch:
diff --git a/workshops/test/base.py b/workshops/test/base.py
--- a/workshops/test/base.py
+++ b/workshops/test/base.py
@@ -22,7 +22,7 @@
 
 
 class TestBase(TestCase):
-    '''Base class for Amy test cases.'''
+    '''Base class for AMY test cases.'''
 
     ERR_DIR = 'htmlerror' # where to save error HTML files
Repository: carpentries/amy

Issue:
Add administrative page
The application needs an administrative page so that we can add admin accounts, etc.



Discussion:
Note that this connects with other authentication issues #70 and #133.

@gvwilson is adding admin accounts from command line enough for v1.0?

Yes.

Then this is deferred to v1.1 for now.

Still needed?

We have some admin stuff enabled, but I think we can get rid of it, at least for now. Let me submit a PR.


Resolving patch:
diff --git a/amy/settings.py b/amy/settings.py
--- a/amy/settings.py
+++ b/amy/settings.py
@@ -79,7 +79,6 @@
     'workshops',
     # this should be after 'workshops' because templates in
     # 'templates/registration/' clash
-    'django.contrib.admin',
     'crispy_forms',
     'selectable',
     'django_countries',
diff --git a/amy/urls.py b/amy/urls.py
--- a/amy/urls.py
+++ b/amy/urls.py
@@ -3,7 +3,6 @@
 
 urlpatterns = patterns('',
     url(r'^workshops/', include('workshops.urls')),
-    url(r'^admin/', include(admin.site.urls)),
     # url(r'^account/', include('django.contrib.auth.urls')),
 
     # django views for authentication
diff --git a/workshops/admin.py b/workshops/admin.py
deleted file mode 100644
--- a/workshops/admin.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.contrib import admin
-from workshops.models import Airport
-from workshops.models import Site
-
-admin.site.register(Airport)
-admin.site.register(Site)


Test patch:
Repository: carpentries/amy

Issue:
Paginate display of airports



Discussion:
Probably part of the DataTable transition, see #129 and #134.  I think
we can close this issue in favor of those existing issues.


Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -186,6 +186,7 @@ class SiteUpdate(LoginRequiredMixin, UpdateViewContext):
 def all_airports(request):
     '''List all airports.'''
     airports = Airport.objects.order_by('iata')
+    airports = _get_pagination_items(request, airports)
     user_can_add = request.user.has_perm('edit')
     context = {'title' : 'All Airports',
                'all_airports' : airports,


Test patch:
Repository: carpentries/amy

Issue:
Search doesn't seem to include "site"
To replicate, search for "MSU". The event **2015-05-30-hastac-swc** isn't returned even though it was held at MSU and the site is "msu.edu".



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -820,7 +820,9 @@ def search(request):
             if form.cleaned_data['in_events']:
                 events = Event.objects.filter(
                     Q(slug__contains=term) |
-                    Q(notes__contains=term))
+                    Q(notes__contains=term) |
+                    Q(site__domain__contains=term) |
+                    Q(site__fullname__contains=term))
             if form.cleaned_data['in_persons']:
                 persons = Person.objects.filter(
                     Q(personal__contains=term) |


Test patch:
Repository: carpentries/amy

Issue:
Display skills and knowledge domains on Person page
The page showing details of an individual person should also show the things they're able to teach, and the domains they're knowledgeable in.



Discussion:

Resolving patch:
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -278,12 +278,18 @@ def all_persons(request):
 def person_details(request, person_id):
     '''List details of a particular person.'''
     person = Person.objects.get(id=person_id)
-    awards = Award.objects.filter(person__id=person_id)
-    tasks = Task.objects.filter(person__id=person_id)
-    context = {'title' : 'Person {0}'.format(person),
-               'person' : person,
-               'awards' : awards,
-               'tasks' : tasks}
+    awards = person.award_set.all()
+    tasks = person.task_set.all()
+    lessons = person.lessons.all()
+    domains = person.domains.all()
+    context = {
+        'title': 'Person {0}'.format(person),
+        'person': person,
+        'awards': awards,
+        'tasks': tasks,
+        'lessons': lessons,
+        'domains': domains,
+    }
     return render(request, 'workshops/person.html', context)
 
 


Test patch:
Repository: carpentries/amy

Issue:
Improvements to bulk person upload
1.  The checking page should clearly show which people are being recycled (i.e., are in the database) and which are being created (i.e., aren't).
2.  The final confirmation page should show the same thing: "10 persons and 21 tasks created" is kind of helpful, but it would be nice to know _which_ persons and _which_ tasks.



Discussion:

Resolving patch:
diff --git a/workshops/util.py b/workshops/util.py
--- a/workshops/util.py
+++ b/workshops/util.py
@@ -96,19 +96,22 @@ def verify_upload_person_task(data):
     errors_occur = False
     for item in data:
         errors = []
+        info = []
 
         event = item.get('event', None)
+        existing_event = None
         if event:
             try:
-                Event.objects.get(slug=event)
+                existing_event = Event.objects.get(slug=event)
             except Event.DoesNotExist:
                 errors.append(u'Event with slug {0} does not exist.'
                               .format(event))
 
         role = item.get('role', None)
+        existing_role = None
         if role:
             try:
-                Role.objects.get(name=role)
+                existing_role = Role.objects.get(name=role)
             except Role.DoesNotExist:
                 errors.append(u'Role with name {0} does not exist.'
                               .format(role))
@@ -142,7 +145,19 @@ def verify_upload_person_task(data):
 
             except Person.DoesNotExist:
                 # in this case we need to add the user
-                pass
+                info.append('Person and task will be created.')
+
+            else:
+                if existing_event and person and existing_role:
+                    # person, their role and a corresponding event exist, so
+                    # let's check if the task exists
+                    try:
+                        Task.objects.get(event=existing_event, person=person,
+                                         role=existing_role)
+                    except Task.DoesNotExist:
+                        info.append('Task will be created')
+                    else:
+                        info.append('Task already exists')
 
         if person:
             if not any([event, role]):
@@ -150,13 +165,16 @@ def verify_upload_person_task(data):
                               " the user to was provided")
 
         if (event and not role) or (role and not event):
-            errors.append("Must have both/either event ({0}) and role ({1})"
+            errors.append("Must have both: event ({0}) and role ({1})"
                           .format(event, role))
 
         if errors:
             errors_occur = True
             item['errors'] = errors
 
+        if info:
+            item['info'] = info
+
     return errors_occur
 
 


Test patch:
Repository: carpentries/amy

Issue:
Skills should be displayed in sorted order
Skills are currently displayed in a more-or-less random order (based I presume on the order in which they were added to the DB).  They should be sorted, either alphabetically (which would put all 'dc' before all 'swc') or by second part (e.g., by what's after the '/').



Discussion:

Resolving patch:
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -391,6 +391,9 @@ class Lesson(models.Model):
     def __str__(self):
         return self.name
 
+    class Meta:
+        ordering = ['name']
+
 #------------------------------------------------------------
 
 class Qualification(models.Model):


Test patch:
diff --git a/workshops/test/test_person.py b/workshops/test/test_person.py
--- a/workshops/test/test_person.py
+++ b/workshops/test/test_person.py
@@ -425,8 +425,8 @@ def setUp(self):
         self.badge1 = Badge.objects.create(name='Badge1')
         self.badge2 = Badge.objects.create(name='Badge2')
 
-        self.lesson1 = Lesson.objects.get(name='swc/python')
-        self.lesson2 = Lesson.objects.get(name='dc/spreadsheets')
+        self.lesson1 = Lesson.objects.get(name='dc/spreadsheets')
+        self.lesson2 = Lesson.objects.get(name='swc/python')
 
         self.domain1 = KnowledgeDomain.objects.get(pk=1)  # space sciences
         self.domain2 = KnowledgeDomain.objects.get(pk=2)  # geo* sciences