# 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 %} +

+ +

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 %} New {% else %} - {% if contest.winner %} - Winner {% profile_link contest.winner.username %} + {% if contest.win_date %} + {% else %} Old {% endif %}