# HG changeset patch
# User Brian Neal
# Date 1404523814 18000
# Node ID 5977b43499f719e82cfe7ebb4887cf14c06719ce
# Parent 5d48fd80f27d5265d5b9297e8a6cfcc925e26676
Modified contests to have multiple winners.
diff -r 5d48fd80f27d -r 5977b43499f7 contests/admin.py
--- 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"
diff -r 5d48fd80f27d -r 5977b43499f7 contests/models.py
--- 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):
diff -r 5d48fd80f27d -r 5977b43499f7 contests/tests/test_models.py
--- 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)
+
diff -r 5d48fd80f27d -r 5977b43499f7 contests/tests/test_views.py
--- 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
diff -r 5d48fd80f27d -r 5977b43499f7 contests/urls.py
--- 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[\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'),
)
diff -r 5d48fd80f27d -r 5977b43499f7 sg101/templates/contests/contest_detail.html
--- 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 @@
{% endif %}
-Contest Winner
+Contest Winners
+{% if contest.win_date %}
-{% 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 {{ contest.winner.username }}!
+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 %}
+
+
+ {% for winner in contest.winners.all %}
+ - {% profile_link winner.username %}
+ {% endfor %}
+
+Congratulations to the winners!
{% else %}
-A contest winner will be determined sometime on or after {{ contest.end_date|date:"l, F d, Y" }}.
-Please check back later.
+
+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.
+
{% endif %}
-
{% social_sharing contest.title contest.get_absolute_url %}
{% endblock %}
diff -r 5d48fd80f27d -r 5977b43499f7 sg101/templates/contests/contest_list.html
--- 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 %}
{% else %}
- {% if contest.winner %}
- {% profile_link contest.winner.username %}
+ {% if contest.win_date %}
+
+ {% for winner in contest.winners.all %}
+ - {% profile_link winner.username %}
+ {% endfor %}
+
{% else %}
{% endif %}