diff --git a/config/assets.cfg b/config/assets.cfg
index 703871e..1884ed4 100644
--- a/config/assets.cfg
+++ b/config/assets.cfg
@@ -12,7 +12,7 @@ hash-name = false
[bootstrap]
recipe = hexagonit.recipe.download
-url = https://github.com/twbs/bootstrap/releases/download/v3.3.1/bootstrap-3.3.1-dist.zip
+url = https://github.com/twbs/bootstrap/releases/download/v3.3.5/bootstrap-3.3.5-dist.zip
hash-name = false
strip-top-level-dir = true
diff --git a/pylab/core/migrations/0001_initial.py b/pylab/core/migrations/0001_initial.py
index 91d4dd4..73e8ef2 100644
--- a/pylab/core/migrations/0001_initial.py
+++ b/pylab/core/migrations/0001_initial.py
@@ -12,6 +12,7 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('website', '0002_auto_20150728_0303'),
]
operations = [
diff --git a/pylab/core/migrations/0006_auto_20150806_0437.py b/pylab/core/migrations/0006_auto_20150806_0437.py
new file mode 100644
index 0000000..94856ce
--- /dev/null
+++ b/pylab/core/migrations/0006_auto_20150806_0437.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+from django.conf import settings
+import django_extensions.db.fields
+import django.utils.timezone
+import autoslug.fields
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('core', '0005_auto_20150730_0231'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Vote',
+ fields=[
+ ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
+ ('created', django_extensions.db.fields.CreationDateTimeField(editable=False, default=django.utils.timezone.now, blank=True)),
+ ('modified', django_extensions.db.fields.ModificationDateTimeField(editable=False, default=django.utils.timezone.now, blank=True)),
+ ('vote_time', models.DateTimeField(null=True)),
+ ('points', models.IntegerField(verbose_name='Points', null=True)),
+ ('project', models.ForeignKey(to='core.Project')),
+ ('voter', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='VotingPoll',
+ fields=[
+ ('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
+ ('created', django_extensions.db.fields.CreationDateTimeField(editable=False, default=django.utils.timezone.now, blank=True)),
+ ('modified', django_extensions.db.fields.ModificationDateTimeField(editable=False, default=django.utils.timezone.now, blank=True)),
+ ('slug', autoslug.fields.AutoSlugField(editable=False)),
+ ('title', models.CharField(verbose_name='Title', max_length=255)),
+ ('description', models.TextField(blank=True, verbose_name='Description')),
+ ('author', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.AddField(
+ model_name='vote',
+ name='voting_poll',
+ field=models.ForeignKey(to='core.VotingPoll'),
+ ),
+ ]
diff --git a/pylab/core/migrations/0007_auto_20150808_1812.py b/pylab/core/migrations/0007_auto_20150808_1812.py
new file mode 100644
index 0000000..9bbfee6
--- /dev/null
+++ b/pylab/core/migrations/0007_auto_20150808_1812.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', '0006_auto_20150806_0437'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='vote',
+ name='points',
+ field=models.PositiveIntegerField(verbose_name='Points', null=True, blank=True),
+ ),
+ ]
diff --git a/pylab/core/migrations/0008_auto_20150809_0654.py b/pylab/core/migrations/0008_auto_20150809_0654.py
new file mode 100644
index 0000000..2a8e6c5
--- /dev/null
+++ b/pylab/core/migrations/0008_auto_20150809_0654.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', '0007_auto_20150808_1812'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='vote',
+ old_name='vote_time',
+ new_name='voted',
+ ),
+ ]
diff --git a/pylab/core/models.py b/pylab/core/models.py
index 3a76dcb..589a94b 100644
--- a/pylab/core/models.py
+++ b/pylab/core/models.py
@@ -56,3 +56,28 @@ def __str__(self):
def get_absolute_url(self):
return reverse('event-details', args=[self.starts.year, self.starts.month, self.starts.day, self.slug])
+
+
+class VotingPoll(models.Model):
+ created = CreationDateTimeField()
+ modified = ModificationDateTimeField()
+ author = models.ForeignKey(User)
+ slug = AutoSlugField(populate_from='title')
+ title = models.CharField(_("Title"), max_length=255)
+ description = models.TextField(_("Description"), blank=True)
+
+ def __str__(self):
+ return self.title
+
+
+class Vote(models.Model):
+ created = CreationDateTimeField()
+ modified = ModificationDateTimeField()
+ voted = models.DateTimeField(null=True)
+ voting_poll = models.ForeignKey(VotingPoll)
+ voter = models.ForeignKey(User)
+ project = models.ForeignKey(Project)
+ points = models.PositiveIntegerField(_("Points"), null=True, blank=True)
+
+ def __str__(self):
+ return '%s, %s' % (self.voting_poll.title, self.voter.get_full_name() or self.voter.get_username())
diff --git a/pylab/website/admin.py b/pylab/website/admin.py
index e45f3dd..654ec21 100644
--- a/pylab/website/admin.py
+++ b/pylab/website/admin.py
@@ -10,7 +10,7 @@
import allauth.socialaccount.admin as allauth
-from pylab.core.models import Project, Event
+import pylab.core.models as core_models
class AdminSite(admin.AdminSite):
@@ -50,8 +50,10 @@ def get_changeform_initial_data(self, request):
site = AdminSite()
site.register(auth_models.User, auth_admin.UserAdmin)
-site.register(Project)
-site.register(Event, EventAdmin)
+
+site.register(core_models.Project)
+site.register(core_models.Event, EventAdmin)
+site.register(core_models.VotingPoll)
site.register(allauth.SocialApp, allauth.SocialAppAdmin)
site.register(allauth.SocialToken, allauth.SocialTokenAdmin)
diff --git a/pylab/website/forms.py b/pylab/website/forms.py
index 2f8598e..fb0b7f2 100644
--- a/pylab/website/forms.py
+++ b/pylab/website/forms.py
@@ -1,8 +1,11 @@
+import datetime
+
from django import forms
+from django.forms.models import BaseModelFormSet
from django.utils.translation import ugettext
from django.utils.translation import ugettext_lazy as _
-from pylab.core.models import Project, Event
+from pylab.core.models import Project, Event, Vote
class ProjectForm(forms.ModelForm):
@@ -49,3 +52,32 @@ def clean(self):
if title and starts:
self.check_existing_events(title, starts)
+
+
+class VotePointsForm(forms.ModelForm):
+ class Meta:
+ model = Vote
+ fields = ('points',)
+ widgets = {
+ 'points': forms.NumberInput(attrs={'max': 99, 'class': 'vote-points-input'}),
+ }
+
+ def save(self, commit=True, *args, **kwargs):
+ vote = super(VotePointsForm, self).save(commit=False, *args, **kwargs)
+ vote.voted = datetime.datetime.now()
+ vote.save()
+
+
+class BaseTotalPointsFormset(BaseModelFormSet):
+ def clean(self, *args, **kwargs):
+ super(BaseTotalPointsFormset, self).clean(*args, **kwargs)
+ total_points = 0
+
+ for form in self.forms:
+ if form.cleaned_data['points']:
+ total_points += form.cleaned_data['points']
+
+ if total_points < 0 or total_points > 15:
+ raise forms.ValidationError(ugettext(
+ "Sum of voting points is out of bounds. Expected from 0 to 15, but got %s."
+ ) % total_points)
diff --git a/pylab/website/models.py b/pylab/website/models.py
deleted file mode 100644
index e69de29..0000000
diff --git a/pylab/website/static/css/main.scss b/pylab/website/static/css/main.scss
index 98687e7..e920baa 100644
--- a/pylab/website/static/css/main.scss
+++ b/pylab/website/static/css/main.scss
@@ -50,3 +50,23 @@
.event-time {
padding-left: 16px;
}
+
+.vote-points-input {
+ width: 38px;
+ text-align: right;
+ font-weight: bold;
+}
+
+.vote-points-form {
+ padding-top: 10px;
+ padding-left: 30px;
+ padding-bottom: 30px;
+}
+
+.vote-button {
+ margin-top: 15px;
+}
+
+#total-points {
+ display: none;
+}
diff --git a/pylab/website/static/js/update_points_left.js b/pylab/website/static/js/update_points_left.js
new file mode 100644
index 0000000..969d811
--- /dev/null
+++ b/pylab/website/static/js/update_points_left.js
@@ -0,0 +1,27 @@
+var total_points = Number($('#total-points').html());
+function count_points_left () {
+ var sum = 0;
+ $('.vote-points-input').each(function() { sum += Number($(this).val()); });
+ return total_points - sum;
+};
+$('#points-left').html(count_points_left());
+$('.vote-points-input').each(function() {
+ this.oldValue = this.value;
+});
+
+function clear_value_if_zero(elem) {
+ if (elem.value === "0") {
+ elem.value = "";
+ };
+};
+
+$('.vote-points-input').change(function (ev) {
+ var points_left = count_points_left();
+ if (points_left < 0) {
+ this.value = this.oldValue;
+ } else {
+ this.oldValue = this.value;
+ $('#points-left').html(points_left);
+ };
+ clear_value_if_zero(this);
+});
diff --git a/pylab/website/templates/base.html b/pylab/website/templates/base.html
index e02d853..83853cd 100644
--- a/pylab/website/templates/base.html
+++ b/pylab/website/templates/base.html
@@ -55,5 +55,7 @@
+ {% block scripts %}
+ {% endblock %}