diff --git a/pylab/core/factories.py b/pylab/core/factories.py index e106ff1..6f2ab7f 100644 --- a/pylab/core/factories.py +++ b/pylab/core/factories.py @@ -3,7 +3,7 @@ from django.contrib.auth.models import User -from pylab.core.models import Event +from pylab.core.models import Event, Project, VotingPoll from pylab.core.helpers.factories import fake, future @@ -41,3 +41,27 @@ class Meta: description = factory.LazyAttribute(fake.sentence()) starts = factory.LazyAttribute(future(days=1, hour=18, minute=0, second=0)) ends = factory.LazyAttribute(future(days=1, hour=20, minute=0, second=0)) + + +class ProjectFactory(factory.django.DjangoModelFactory): + + class Meta: + model = Project + django_get_or_create = ('slug',) + + author = factory.SubFactory(UserFactory) + title = factory.LazyAttribute(fake.company()) + slug = factory.LazyAttribute(fake.slug()) + description = factory.LazyAttribute(fake.sentence()) + + +class VotingPollFactory(factory.django.DjangoModelFactory): + + class Meta: + model = VotingPoll + django_get_or_create = ('slug',) + + author = factory.SubFactory(UserFactory) + title = factory.LazyAttribute(fake.company()) + slug = factory.LazyAttribute(fake.slug()) + description = factory.LazyAttribute(fake.sentence()) diff --git a/pylab/core/migrations/0009_votingpoll_projects.py b/pylab/core/migrations/0009_votingpoll_projects.py new file mode 100644 index 0000000..b7ab089 --- /dev/null +++ b/pylab/core/migrations/0009_votingpoll_projects.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_auto_20150809_0654'), + ] + + operations = [ + migrations.AddField( + model_name='votingpoll', + name='projects', + field=models.ManyToManyField(to='core.Project'), + ), + ] diff --git a/pylab/core/models.py b/pylab/core/models.py index 589a94b..7edda70 100644 --- a/pylab/core/models.py +++ b/pylab/core/models.py @@ -65,10 +65,17 @@ class VotingPoll(models.Model): slug = AutoSlugField(populate_from='title') title = models.CharField(_("Title"), max_length=255) description = models.TextField(_("Description"), blank=True) + projects = models.ManyToManyField(Project) def __str__(self): return self.title + def get_absolute_url(self): + return reverse('voting-poll-details', args=[self.slug]) + + def get_voting_url(self): + return reverse('voting-page', args=[self.slug]) + class Vote(models.Model): created = CreationDateTimeField() diff --git a/pylab/website/forms.py b/pylab/website/forms.py index fb0b7f2..5fb4aa9 100644 --- a/pylab/website/forms.py +++ b/pylab/website/forms.py @@ -54,16 +54,34 @@ def clean(self): self.check_existing_events(title, starts) -class VotePointsForm(forms.ModelForm): +class ProjectPointsForm(forms.ModelForm): + points = forms.IntegerField( + min_value=0, + widget=forms.NumberInput(attrs={'max': 99, 'min': 0, 'class': 'vote-points-input'}), + ) + + def __init__(self, user, voting_poll, *args, **kwargs): + self.user = user + self.voting_poll = voting_poll + super(ProjectPointsForm, self).__init__(*args, **kwargs) + + try: + vote = Vote.objects.get(voter=self.user, project__id=self.initial['id']) + self.fields['points'].initial = vote.points + except Vote.DoesNotExist: + pass + class Meta: - model = Vote - fields = ('points',) - widgets = { - 'points': forms.NumberInput(attrs={'max': 99, 'class': 'vote-points-input'}), - } + model = Project + fields = tuple() - def save(self, commit=True, *args, **kwargs): - vote = super(VotePointsForm, self).save(commit=False, *args, **kwargs) + def save(self, commit=True, *args, **kwargs): # pylint: disable=unused-argument + vote, created = Vote.objects.get_or_create( # pylint: disable=unused-variable + voter=self.user, + project=self.instance, + voting_poll=self.voting_poll + ) + vote.points = self.cleaned_data['points'] vote.voted = datetime.datetime.now() vote.save() @@ -74,7 +92,7 @@ def clean(self, *args, **kwargs): total_points = 0 for form in self.forms: - if form.cleaned_data['points']: + if form.cleaned_data.get('points'): total_points += form.cleaned_data['points'] if total_points < 0 or total_points > 15: diff --git a/pylab/website/static/css/main.scss b/pylab/website/static/css/main.scss index e920baa..c5d4c40 100644 --- a/pylab/website/static/css/main.scss +++ b/pylab/website/static/css/main.scss @@ -52,7 +52,7 @@ } .vote-points-input { - width: 38px; + width: 42px; text-align: right; font-weight: bold; } diff --git a/pylab/website/templates/website/voting_page.html b/pylab/website/templates/website/voting_page.html index 2967c91..22246de 100644 --- a/pylab/website/templates/website/voting_page.html +++ b/pylab/website/templates/website/voting_page.html @@ -20,7 +20,6 @@

{{ voting_poll.title }}

{{ formset.management_form }} {% for form in formset %}
- {{ form.errors }}

{% for hidden in form.hidden_fields %} {{ hidden }} @@ -28,7 +27,7 @@

{{ voting_poll.title }}

{% for field in form.visible_fields %}
{{ field.errors }} - {{ field }} {{ form.instance.project.title }} + {{ field }} {{ form.instance.title }}
{% endfor %}

diff --git a/pylab/website/tests/test_voting.py b/pylab/website/tests/test_voting.py index 052f5f6..c656f14 100644 --- a/pylab/website/tests/test_voting.py +++ b/pylab/website/tests/test_voting.py @@ -2,25 +2,24 @@ from django_webtest import WebTest -from django.contrib.auth.models import User - -from pylab.core.models import Project, VotingPoll, Vote +from pylab.core.factories import UserFactory, VotingPollFactory, ProjectFactory +from pylab.core.models import Vote class VotingTests(WebTest): - def test_voting_page(self): - u1 = User.objects.create(username='u1') - - p1 = Project.objects.create(author=u1, title='Test title 1', description='Test description') - p2 = Project.objects.create(author=u1, title='Test title 2', description='Test description') + def test_voting_and_voting_poll_details_page(self): + u1 = UserFactory() + u2 = UserFactory() - vp = VotingPoll.objects.create(author=u1, title='Test voting poll', description='Test description') + p1 = ProjectFactory(author=u1) + p2 = ProjectFactory(author=u1) - Vote.objects.create(voter=u1, voting_poll=vp, project=p1) - Vote.objects.create(voter=u1, voting_poll=vp, project=p2) + vp = VotingPollFactory(author=u1) + vp.projects.add(p1) + vp.projects.add(p2) - resp = self.app.get('/vote/test-voting-poll/', user='u1') + resp = self.app.get(vp.get_voting_url(), user=u1) self.assertEqual(resp.status_int, 200) time_before_vote = datetime.datetime.now() @@ -32,38 +31,32 @@ def test_voting_page(self): time_after_vote = datetime.datetime.now() - self.assertEqual(list(Vote.objects.values_list('points', flat=True)), [3, 2]) + self.assertEqual(list(Vote.objects.filter(voter=u1).values_list('points', flat=True)), [3, 2]) - for v in Vote.objects.all(): + for v in Vote.objects.filter(voter=u1): self.assertLess(time_before_vote, v.voted) self.assertGreater(time_after_vote, v.voted) - resp = self.app.get('/vote/test-voting-poll/', user='u1') + resp = self.app.get(vp.get_voting_url(), user=u2) self.assertEqual(resp.status_int, 200) time_before_vote = datetime.datetime.now() - resp.form['form-0-points'].value = 30 # Vote points sum should be less or equal to 15 - resp.form['form-1-points'].value = 20 + resp.form['form-0-points'].value = 6 # Vote points sum should be less or equal to 15 + resp.form['form-1-points'].value = 5 resp = resp.form.submit() - self.assertEqual(resp.status_int, 200) - - def test_voting_poll_details(self): - u1 = User.objects.create(username='u1') - u2 = User.objects.create(username='u2') - - p1 = Project.objects.create(author=u1, title='Test title 1', description='Test description') - p2 = Project.objects.create(author=u1, title='Test title 2', description='Test description') + self.assertEqual(resp.status_int, 302) - vp = VotingPoll.objects.create(author=u1, title='Test voting poll', description='Test description') + time_after_vote = datetime.datetime.now() - Vote.objects.create(voter=u1, voting_poll=vp, project=p1, points=1) - Vote.objects.create(voter=u1, voting_poll=vp, project=p2, points=2) - Vote.objects.create(voter=u2, voting_poll=vp, project=p1, points=3) - Vote.objects.create(voter=u2, voting_poll=vp, project=p2, points=4) + self.assertEqual(list(Vote.objects.filter(voter=u2).values_list('points', flat=True)), [6, 5]) - resp = self.app.get('/voting-poll/test-voting-poll/', user='u1') + for v in Vote.objects.filter(voter=u2): + self.assertLess(time_before_vote, v.voted) + self.assertGreater(time_after_vote, v.voted) + resp = self.app.get(vp.get_absolute_url(), user=u1) self.assertEqual(resp.status_int, 200) - self.assertTrue(b'4' in resp.content) - self.assertTrue(b'6' in resp.content) + + self.assertTrue(b'9' in resp.content) + self.assertTrue(b'7' in resp.content) diff --git a/pylab/website/views.py b/pylab/website/views.py index 50b1fff..b3718e7 100644 --- a/pylab/website/views.py +++ b/pylab/website/views.py @@ -1,3 +1,5 @@ +from django.utils.functional import curry + from django.contrib import messages from django.contrib.auth.decorators import login_required from django.forms.models import modelformset_factory @@ -7,7 +9,7 @@ from django.utils.translation import ugettext from django.db.models import Sum -from pylab.core.models import Project, Event, Vote, VotingPoll +from pylab.core.models import Project, Event, VotingPoll from pylab.website.helpers import formrenderer from pylab.website.helpers.decorators import superuser_required from pylab.website.services import weeklyevents @@ -103,22 +105,25 @@ def create_weekly_event(request, year, month, day, slug): def voting_page(request, voting_poll_slug): voting_poll = get_object_or_404(VotingPoll, slug=voting_poll_slug) total_points = 15 - VotePointsFormSet = modelformset_factory( - Vote, - form=website_forms.VotePointsForm, + ProjectPointsFormSet = modelformset_factory( + Project, + form=website_forms.ProjectPointsForm, formset=website_forms.BaseTotalPointsFormset, extra=0, ) - vote_qs = Vote.objects.filter(voter=request.user, voting_poll__slug=voting_poll_slug) + ProjectPointsFormSet.form = staticmethod(curry( + website_forms.ProjectPointsForm, + user=request.user, + voting_poll=voting_poll)) if request.method == 'POST': - formset = VotePointsFormSet(request.POST, queryset=vote_qs) + formset = ProjectPointsFormSet(request.POST, queryset=voting_poll.projects.all()) if formset.is_valid(): formset.save() messages.success(request, ugettext("Vote for ā€ž%sā€œ voting poll was saved successfully." % voting_poll)) - return redirect('project-list') + return redirect(voting_poll) else: - formset = VotePointsFormSet(queryset=vote_qs) + formset = ProjectPointsFormSet(queryset=voting_poll.projects.all()) return render(request, 'website/voting_page.html', { 'voting_poll': voting_poll,