changeset 796:5977b43499f7

Modified contests to have multiple winners.
author Brian Neal <bgneal@gmail.com>
date Fri, 04 Jul 2014 20:30:14 -0500
parents 5d48fd80f27d
children c37760a79059
files contests/admin.py contests/models.py contests/tests/test_models.py contests/tests/test_views.py contests/urls.py sg101/templates/contests/contest_detail.html sg101/templates/contests/contest_list.html
diffstat 7 files changed, 116 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/contests/admin.py	Fri Jun 06 18:28:28 2014 -0500
+++ b/contests/admin.py	Fri Jul 04 20:30:14 2014 -0500
@@ -10,13 +10,13 @@
 
 class ContestAdmin(admin.ModelAdmin):
     list_display = ['title', 'is_public', 'creation_date', 'end_date',
-            'contestant_count', 'winner']
+            'contestant_count', 'winner_count']
     list_editable = ['is_public']
     date_hierarchy = 'creation_date'
     search_fields = ['title', 'description']
     prepopulated_fields = {'slug': ['title']}
-    raw_id_fields = ['winner', 'contestants']
-    actions = ['pick_winner']
+    raw_id_fields = ['contestants', 'winners']
+    actions = ['pick_winners']
 
     class Media:
         js = (['js/contests/contests_admin.js'] +
@@ -26,7 +26,11 @@
         return obj.contestants.count()
     contestant_count.short_description = '# Entries'
 
-    def pick_winner(self, request, qs):
+    def winner_count(self, obj):
+        return obj.winners.count()
+    winner_count.short_description = '# Winners'
+
+    def pick_winners(self, request, qs):
         """
         Picks a winner on the contests selected by the admin. Note that for
         safety reasons, we only update those contests that don't have winners
@@ -35,15 +39,15 @@
         """
         count = 0
         for contest in qs:
-            if not contest.winner:
-                contest.pick_winner()
+            if not contest.win_date:
+                contest.pick_winners()
                 contest.save()
                 count += 1
 
         self.message_user(request, "%d of %d winners picked" % (count,
             qs.count()))
 
-    pick_winner.short_description = "Pick winners for selected contests"
+    pick_winners.short_description = "Pick winners for selected contests"
 
 
 
--- a/contests/models.py	Fri Jun 06 18:28:28 2014 -0500
+++ b/contests/models.py	Fri Jul 04 20:30:14 2014 -0500
@@ -35,10 +35,12 @@
     end_date = models.DateTimeField()
     contestants = models.ManyToManyField(User, related_name='contests',
             null=True, blank=True)
-    winner = models.ForeignKey(User, null=True, blank=True,
+    winners = models.ManyToManyField(User, null=True, blank=True,
             related_name='winning_contests')
     win_date = models.DateTimeField(null=True, blank=True)
     meta_description = models.TextField()
+    num_winners = models.IntegerField(default=1,
+            verbose_name='Number of winners')
 
     objects = models.Manager()
     public_objects = PublicContestManager()
@@ -69,19 +71,31 @@
 
     def can_enter(self):
         """
-        Returns True if the contest is still active and does not have a winner.
+        Returns True if the contest is still active and does not have any
+        winners.
 
         """
-        return not self.winner and self.is_active()
+        return not self.win_date and self.is_active()
 
-    def pick_winner(self):
+    def pick_winners(self):
         """
-        This function randomly picks a winner from all the contestants.
+        This function randomly picks winners from all the contestants.
 
         """
-        user_ids = self.contestants.values_list('id', flat=True)
-        winner_id = random.choice(user_ids)
-        self.winner = User.objects.get(id=winner_id)
+        user_ids = list(self.contestants.values_list('id', flat=True))
+
+        winner_count = min(len(user_ids), self.num_winners)
+        if winner_count == 0:
+            return
+
+        winner_ids = []
+        for n in xrange(winner_count):
+            winner = random.choice(user_ids)
+            winner_ids.append(winner)
+            user_ids.remove(winner)
+
+        winners = list(User.objects.filter(pk__in=winner_ids))
+        self.winners.add(*winners)
         self.win_date = datetime.datetime.now()
 
     def ogp_tags(self):
--- a/contests/tests/test_models.py	Fri Jun 06 18:28:28 2014 -0500
+++ b/contests/tests/test_models.py	Fri Jul 04 20:30:14 2014 -0500
@@ -116,26 +116,30 @@
                 is_public=True,
                 creation_date=start,
                 end_date=end,
-                winner=user,
                 win_date=now)
+        c.save()
+        c.contestants.add(user)
+        c.winners.add(user)
 
         self.assertFalse(c.can_enter())
 
         start = now - datetime.timedelta(days=7)
         end = start - datetime.timedelta(days=3)
 
-        c = Contest(title='test',
-                slug='test',
-                description='test',
+        c = Contest(title='test 2',
+                slug='test-2',
+                description='test 2',
                 is_public=True,
                 creation_date=start,
                 end_date=end,
-                winner=user,
                 win_date=end + datetime.timedelta(days=1))
+        c.save()
+        c.contestants.add(user)
+        c.winners.add(user)
 
         self.assertFalse(c.can_enter())
 
-    def test_pick_winner(self):
+    def test_pick_winners(self):
 
         now = datetime.datetime.now()
         start = now - datetime.timedelta(days=7)
@@ -146,7 +150,8 @@
                 description='test',
                 is_public=False,
                 creation_date=start,
-                end_date=end)
+                end_date=end,
+                num_winners=1)
         c.save()
 
         user1 = User.objects.create_user('test_user1', '', 'password')
@@ -158,9 +163,49 @@
 
         c.contestants.add(user1, user2, user3)
 
-        c.pick_winner()
+        c.pick_winners()
 
         self.assertTrue(datetime.datetime.now() - c.win_date <
                 datetime.timedelta(seconds=1))
-        self.assertTrue(c.winner.id in [user1.id, user2.id, user3.id])
 
+        winners = list(c.winners.all())
+        self.assertEqual(len(winners), 1)
+        self.assertTrue(winners[0].id in [user1.id, user2.id, user3.id])
+
+    def test_pick_winners2(self):
+
+        now = datetime.datetime.now()
+        start = now - datetime.timedelta(days=7)
+        end = start - datetime.timedelta(days=3)
+
+        c = Contest(title='test',
+                slug='test',
+                description='test',
+                is_public=False,
+                creation_date=start,
+                end_date=end,
+                num_winners=2)
+        c.save()
+
+        user1 = User.objects.create_user('test_user1', '', 'password')
+        user1.save()
+        user2 = User.objects.create_user('test_user2', '', 'password')
+        user2.save()
+        user3 = User.objects.create_user('test_user3', '', 'password')
+        user3.save()
+
+        c.contestants.add(user1, user2, user3)
+
+        c.pick_winners()
+
+        self.assertTrue(datetime.datetime.now() - c.win_date <
+                datetime.timedelta(seconds=1))
+
+        winners = list(c.winners.all())
+        self.assertEqual(len(winners), 2)
+
+        user_ids = set([user1.id, user2.id, user3.id])
+        self.assertTrue(winners[0].id in user_ids)
+        self.assertTrue(winners[1].id in user_ids)
+        self.assertTrue(winners[0].id != winners[1].id)
+
--- a/contests/tests/test_views.py	Fri Jun 06 18:28:28 2014 -0500
+++ b/contests/tests/test_views.py	Fri Jul 04 20:30:14 2014 -0500
@@ -39,9 +39,11 @@
                 is_public=True,
                 creation_date=start,
                 end_date=end,
-                winner=user,
-                win_date=end + datetime.timedelta(days=1))
+                win_date=end + datetime.timedelta(days=1),
+                num_winners=1)
         c.save()
+        c.contestants.add(user)
+        c.winners.add(user)
         self.contest_id = c.id
 
     def test_contests(self):
@@ -72,7 +74,8 @@
                 description='test',
                 is_public=True,
                 creation_date=start,
-                end_date=end)
+                end_date=end,
+                num_winners=1)
         c.save()
         self.contest_id = c.id
 
--- a/contests/urls.py	Fri Jun 06 18:28:28 2014 -0500
+++ b/contests/urls.py	Fri Jul 04 20:30:14 2014 -0500
@@ -12,7 +12,7 @@
    url(r'^$',
        ListView.as_view(
            context_object_name='contests',
-           queryset=Contest.public_objects.select_related('winner')),
+           queryset=Contest.public_objects.all().prefetch_related('winners')),
        name='contests-index'),
 
    url(r'^enter/$',
@@ -22,6 +22,6 @@
    url(r'^c/(?P<slug>[\w-]+)/$',
        DetailView.as_view(
            context_object_name='contest',
-           queryset=Contest.public_objects.all().select_related('winner')),
+           queryset=Contest.public_objects.all().prefetch_related('winners')),
        name='contests-contest'),
 )
--- a/sg101/templates/contests/contest_detail.html	Fri Jun 06 18:28:28 2014 -0500
+++ b/sg101/templates/contests/contest_detail.html	Fri Jul 04 20:30:14 2014 -0500
@@ -49,16 +49,24 @@
 </p>
 {% endif %}
 
-<h4>Contest Winner</h4>
+<h4>Contest Winners</h4>
+{% if contest.win_date %}
 <p>
-{% if contest.winner %}
-The winner of this contest, selected on {{ contest.win_date|date:"l, F d, Y" }}, is 
-{% profile_link contest.winner.username '.' %} Congratulations to <strong>{{ contest.winner.username }}</strong>!
+The winner{{ contest.num_winners|pluralize }} of this contest, selected on {{ contest.win_date|date:"l, F d, Y" }},
+{% if contest.num_winners == 1 %}is:{% else %}are:{% endif %}
+</p>
+<ul>
+   {% for winner in contest.winners.all %}
+   <li>{% profile_link winner.username %}</li>
+   {% endfor %}
+</ul>
+<p>Congratulations to the winners!</p>
 {% else %}
-A contest winner will be determined sometime on or after {{ contest.end_date|date:"l, F d, Y" }}.
-Please check back later.
+<p>
+We will randomly choose {{ contest.num_winners }} winner{{ contest.num_winners|pluralize }}
+sometime on or after {{ contest.end_date|date:"l, F d, Y" }}. Please check back later.
+</p>
 {% endif %}
-</p>
 <hr />
 {% social_sharing contest.title contest.get_absolute_url %}
 {% endblock %}
--- a/sg101/templates/contests/contest_list.html	Fri Jun 06 18:28:28 2014 -0500
+++ b/sg101/templates/contests/contest_list.html	Fri Jul 04 20:30:14 2014 -0500
@@ -13,8 +13,12 @@
    {% if contest.is_active %}
       <img src="{{ STATIC_URL }}icons/new.png" alt="New" />
    {% else %}
-      {% if contest.winner %}
-         <img src="{{ STATIC_URL }}icons/rosette.png" alt="Winner" /> {% profile_link contest.winner.username %}
+      {% if contest.win_date %}
+         <ul>
+         {% for winner in contest.winners.all %}
+            <li><img src="{{ STATIC_URL }}icons/rosette.png" alt="Winner" /> {% profile_link winner.username %}</li>
+         {% endfor %}
+         </ul>
       {% else %}
          <img src="{{ STATIC_URL }}icons/lock.png" alt="Old" />
       {% endif %}