Repository: carpentries/amy

Issue:
Stalled workshops should not show up in workshops with issues
See for example
https://amy.software-carpentry.org/workshops/event/2016-12-07-bnl



Discussion:

Resolving patch:
diff --git a/workshops/filters.py b/workshops/filters.py
--- a/workshops/filters.py
+++ b/workshops/filters.py
@@ -64,7 +64,7 @@ def filter(self, qs, value):
         # no need to check if value exists in self.extra['choices'] because
         # validation is done by django_filters
         try:
-            return getattr(qs, "{}_events".format(value))()
+            return getattr(qs, value)()
         except AttributeError:
             return qs
 
@@ -92,11 +92,13 @@ class EventFilter(FilterSetWithoutHelpText):
 
     STATUS_CHOICES = [
         ('', 'All'),
-        ('past', 'Past'),
-        ('ongoing', 'Ongoing'),
-        ('upcoming', 'Upcoming'),
-        ('unpublished', 'Unpublished'),
-        ('uninvoiced', 'Uninvoiced'),
+        ('active', 'Active'),
+        ('past_events', 'Past'),
+        ('ongoing_events', 'Ongoing'),
+        ('upcoming_events', 'Upcoming'),
+        ('unpublished_events', 'Unpublished'),
+        ('published_events', 'Published'),
+        ('uninvoiced_events', 'Uninvoiced'),
     ]
     status = EventStateFilter(choices=STATUS_CHOICES)
 
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -467,15 +467,16 @@ def __str__(self):
 
 #------------------------------------------------------------
 
+
 # In order to make our custom filters chainable, we have to
 # define them on the QuerySet, not the Manager - see
 # http://www.dabapps.com/blog/higher-level-query-api-django-orm/
 class EventQuerySet(models.query.QuerySet):
     '''Handles finding past, ongoing and upcoming events'''
 
-    def no_stalled(self):
-        """Exclude events marked as 'stalled'."""
-        return self.exclude(tags__name='stalled')
+    def active(self):
+        """Exclude inactive events (stalled or completed)."""
+        return self.exclude(tags__name='stalled').exclude(completed=True)
 
     def past_events(self):
         '''Return past events.
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -147,9 +147,9 @@ def dashboard(request):
     '''Home page.'''
     current_events = (
         Event.objects.upcoming_events() | Event.objects.ongoing_events()
-    ).no_stalled()
-    uninvoiced_events = Event.objects.uninvoiced_events().no_stalled()
-    unpublished_events = Event.objects.unpublished_events().no_stalled() \
+    ).active()
+    uninvoiced_events = Event.objects.active().uninvoiced_events()
+    unpublished_events = Event.objects.active().unpublished_events() \
                                       .select_related('host')
 
     assigned_to, is_admin = assignment_selection(request)
@@ -1684,7 +1684,7 @@ def all_activity_over_time(request):
 def workshop_issues(request):
     '''Display workshops in the database whose records need attention.'''
 
-    events = Event.objects.past_events().filter(
+    events = Event.objects.active().past_events().filter(
         Q(attendance=None) | Q(attendance=0) |
         Q(country=None) |
         Q(venue=None) | Q(venue__exact='') |


Test patch:
diff --git a/workshops/test/test_landing_page.py b/workshops/test/test_landing_page.py
--- a/workshops/test/test_landing_page.py
+++ b/workshops/test/test_landing_page.py
@@ -25,16 +25,22 @@ def test_has_upcoming_events(self):
         assert all([('upcoming' in e.slug or 'ongoing' in e.slug)
                     for e in events])
 
-    def test_no_stalled_events_on_dashboard(self):
-        """Make sure we don't display stalled events on the dashboard."""
-        stalled = Tag.objects.get(name='stalled')
-        e = Event.objects.create(
+    def test_no_inactive_events_on_dashboard(self):
+        """Make sure we don't display stalled or completed events on the
+        dashboard."""
+        stalled_tag = Tag.objects.get(name='stalled')
+        stalled = Event.objects.create(
             slug='stalled-event', host=Host.objects.first(),
         )
-        e.tags.add(stalled)
+        stalled.tags.add(stalled_tag)
+        completed = Event.objects.create(slug='completed-event',
+                                         completed=True,
+                                         host=Host.objects.first())
 
         # stalled event appears in unfiltered list of events
-        self.assertIn(e, Event.objects.unpublished_events())
+        self.assertIn(stalled, Event.objects.unpublished_events())
+        self.assertIn(completed, Event.objects.unpublished_events())
 
         response = self.client.get(reverse('dashboard'))
-        self.assertNotIn(e, response.context['unpublished_events'])
+        self.assertNotIn(stalled, response.context['unpublished_events'])
+        self.assertNotIn(completed, response.context['unpublished_events'])