Introducing polls
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Mon, 20 May 2013 13:07:34 +0000 (15:07 +0200)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Mon, 3 Jun 2013 08:03:41 +0000 (10:03 +0200)
19 files changed:
apps/polls/__init__.py [new file with mode: 0644]
apps/polls/admin.py [new file with mode: 0644]
apps/polls/forms.py [new file with mode: 0644]
apps/polls/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
apps/polls/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/polls/migrations/0001_initial.py [new file with mode: 0644]
apps/polls/migrations/__init__.py [new file with mode: 0644]
apps/polls/models.py [new file with mode: 0644]
apps/polls/static/polls/polls.css [new file with mode: 0644]
apps/polls/static/polls/polls.scss [new file with mode: 0644]
apps/polls/templates/polls/poll.html [new file with mode: 0644]
apps/polls/templates/polls/tags/poll.html [new file with mode: 0644]
apps/polls/templatetags/__init__.py [new file with mode: 0644]
apps/polls/templatetags/polls_tags.py [new file with mode: 0644]
apps/polls/urls.py [new file with mode: 0644]
apps/polls/views.py [new file with mode: 0644]
wolnelektury/settings/__init__.py
wolnelektury/settings/static.py
wolnelektury/urls.py

diff --git a/apps/polls/__init__.py b/apps/polls/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/polls/admin.py b/apps/polls/admin.py
new file mode 100644 (file)
index 0000000..29fc0aa
--- /dev/null
@@ -0,0 +1,21 @@
+from django.contrib import admin
+
+from models import Poll, PollItem
+
+
+class PollItemInline(admin.TabularInline):
+    model = PollItem
+    extra = 0
+    readonly_fields = ('vote_count',)
+    
+    
+class PollAdmin(admin.ModelAdmin):
+    inlines = [PollItemInline]
+    
+
+class PollItemAdmin(admin.ModelAdmin):
+    readonly_fields = ('vote_count',)
+    
+    
+admin.site.register(Poll, PollAdmin)
+admin.site.register(PollItem, PollItemAdmin)
\ No newline at end of file
diff --git a/apps/polls/forms.py b/apps/polls/forms.py
new file mode 100644 (file)
index 0000000..f9ca907
--- /dev/null
@@ -0,0 +1,10 @@
+from django import forms\r
+\r
+\r
+class PollForm(forms.Form):\r
+    vote = forms.ChoiceField(widget = forms.RadioSelect)\r
+    \r
+    def __init__(self, *args, **kwargs):
+        poll = kwargs.pop('poll', None)\r
+        super(PollForm, self).__init__(*args, **kwargs)\r
+        self.fields['vote'].choices = [(item.id, item.content) for item in poll.items.all()]
\ No newline at end of file
diff --git a/apps/polls/locale/pl/LC_MESSAGES/django.mo b/apps/polls/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..d1d6fa7
Binary files /dev/null and b/apps/polls/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/apps/polls/locale/pl/LC_MESSAGES/django.po b/apps/polls/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..5b902f8
--- /dev/null
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-05-20 14:04+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: models.py:10
+msgid "Poll"
+msgstr "Ankieta"
+
+#: models.py:11
+msgid "Polls"
+msgstr "Ankiety"
+
+#: models.py:13
+msgid "question"
+msgstr "Pytanie"
+
+#: models.py:14
+msgid "slug"
+msgstr "slug"
+
+#: models.py:15
+msgid "open"
+msgstr "otwarta"
+
+#: models.py:19
+msgid "Slug of an open poll needs to be unique"
+msgstr "Slug otwartej ankiety musi być unikalny"
+
+#: models.py:36
+msgid "content"
+msgstr "treść"
+
+#: models.py:37
+msgid "vote count"
+msgstr "licznik głosów"
+
+#: models.py:40
+msgid "vote item"
+msgstr "Pozycja ankiety"
+
+#: models.py:41
+msgid "vote items"
+msgstr "Pozycje ankiety"
+
+#: templates/polls/poll.html:11
+msgid "Thanks for voting! You can see current results below."
+msgstr "Dziękujemy za oddanie głosu w ankiecie! Poniżej znajdują się bieżące wyniki."
+
+#: templates/polls/tags/poll.html:28
+msgid "Submit"
+msgstr "Wyślij"
diff --git a/apps/polls/migrations/0001_initial.py b/apps/polls/migrations/0001_initial.py
new file mode 100644 (file)
index 0000000..e5550b0
--- /dev/null
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Poll'
+        db.create_table(u'polls_poll', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('question', self.gf('django.db.models.fields.TextField')()),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50)),
+            ('open', self.gf('django.db.models.fields.BooleanField')(default=False)),
+        ))
+        db.send_create_signal(u'polls', ['Poll'])
+
+        # Adding model 'PollItem'
+        db.create_table(u'polls_pollitem', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('poll', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['polls.Poll'])),
+            ('content', self.gf('django.db.models.fields.TextField')()),
+            ('vote_count', self.gf('django.db.models.fields.IntegerField')(default=0)),
+        ))
+        db.send_create_signal(u'polls', ['PollItem'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Poll'
+        db.delete_table(u'polls_poll')
+
+        # Deleting model 'PollItem'
+        db.delete_table(u'polls_pollitem')
+
+
+    models = {
+        u'polls.poll': {
+            'Meta': {'object_name': 'Poll'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'open': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'question': ('django.db.models.fields.TextField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'})
+        },
+        u'polls.pollitem': {
+            'Meta': {'object_name': 'PollItem'},
+            'content': ('django.db.models.fields.TextField', [], {}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'poll': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['polls.Poll']"}),
+            'vote_count': ('django.db.models.fields.IntegerField', [], {'default': '0'})
+        }
+    }
+
+    complete_apps = ['polls']
\ No newline at end of file
diff --git a/apps/polls/migrations/__init__.py b/apps/polls/migrations/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/polls/models.py b/apps/polls/models.py
new file mode 100644 (file)
index 0000000..d63e5aa
--- /dev/null
@@ -0,0 +1,60 @@
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from django.core.exceptions import ValidationError
+from django.core.urlresolvers import reverse
+
+
+USED_POLLS_KEY = 'used_polls'
+
+
+class Poll(models.Model):
+
+    question = models.TextField(_('question'))
+    slug = models.SlugField(_('slug'))
+    open = models.BooleanField(_('open'), default = False)
+    
+    class Meta:
+        verbose_name = _('Poll')
+        verbose_name_plural = _('Polls')
+    
+    def clean(self):
+        if self.open and Poll.objects.exclude(pk = self.pk).filter(slug = self.slug).exists():
+            raise ValidationError(_('Slug of an open poll needs to be unique'))
+        return super(Poll, self).clean()
+        
+    def __unicode__(self):
+        return self.question[:100] + ' (' + self.slug + ')'
+        
+    def get_absolute_url(self):
+        return reverse('poll', args = [self.slug])
+        
+    @property
+    def vote_count(self):
+        return self.items.all().aggregate(models.Sum('vote_count'))['vote_count__sum']
+        
+    def voted(self, session):
+        return self.id in session.get(USED_POLLS_KEY, set())
+
+        
+class PollItem(models.Model):
+    
+    poll = models.ForeignKey(Poll, related_name = 'items')
+    content = models.TextField(_('content'))
+    vote_count = models.IntegerField(_('vote count'), default = 0)
+    
+    class Meta:
+        verbose_name = _('vote item')
+        verbose_name_plural = _('vote items')
+        \r
+    def __unicode__(self):
+        return self.content + ' @ ' + unicode(self.poll)
+    
+    @property
+    def vote_ratio(self):
+        return (float(self.vote_count) / self.poll.vote_count) * 100 if self.poll.vote_count else 0
+        
+    def vote(self, session):
+        self.vote_count = self.vote_count + 1
+        self.save()
+        session.setdefault(USED_POLLS_KEY, set()).add(self.poll.id)
+        session.save()
\ No newline at end of file
diff --git a/apps/polls/static/polls/polls.css b/apps/polls/static/polls/polls.css
new file mode 100644 (file)
index 0000000..0dd10fa
--- /dev/null
@@ -0,0 +1 @@
+.poll{width:400px}.poll ul{list-style-type:none}.poll-bar{border:solid 1px #ddd;height:6px;margin-bottom:10px}.poll-bar-inner{background:orange;height:6px}.poll-msg{font-weight:bold}
\ No newline at end of file
diff --git a/apps/polls/static/polls/polls.scss b/apps/polls/static/polls/polls.scss
new file mode 100644 (file)
index 0000000..28cd5a5
--- /dev/null
@@ -0,0 +1,21 @@
+.poll {\r
+    width: 400px;\r
+    ul {\r
+        list-style-type: none;\r
+    }\r
+}\r
+\r
+.poll-bar {\r
+    border: solid 1px #ddd;\r
+    height:6px;\r
+    margin-bottom: 10px;\r
+}\r
+\r
+.poll-bar-inner {\r
+    background: orange;\r
+    height: 6px;\r
+}\r
+\r
+.poll-msg {\r
+    font-weight: bold;\r
+}
\ No newline at end of file
diff --git a/apps/polls/templates/polls/poll.html b/apps/polls/templates/polls/poll.html
new file mode 100644 (file)
index 0000000..edb14cf
--- /dev/null
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load polls_tags %}
+
+{% block titleextra %}{{poll.question}}{% endblock %}
+
+
+{% block body %}
+    <div class="normal-text">
+        {% if voted_already %}
+            <p class="poll-msg">{% trans "Thanks for voting! You can see current results below." %}</p>
+        {% endif %}
+        {% poll poll %}
+    </div>
+{% endblock %}
\ No newline at end of file
diff --git a/apps/polls/templates/polls/tags/poll.html b/apps/polls/templates/polls/tags/poll.html
new file mode 100644 (file)
index 0000000..355348a
--- /dev/null
@@ -0,0 +1,31 @@
+{% load i18n %}
+
+{% if poll %}
+    {% if voted_already %}
+        {% if show_results %}
+            <div class="poll">
+                <p>{{poll.question}}</p>
+                <ul>
+                {% for item in poll.items.all %}
+                    <li>
+                        {{item.content}}
+                        <div class="poll-bar"><div class="poll-bar-inner"
+                        {% if vote_count %}
+                                style="width:{{item.vote_ratio|stringformat:"f"}}%;"
+                        {% endif %}
+                        ></div></div>
+                    </li>
+                {% endfor %}
+                </ul>
+            </div>
+        {% endif %}
+    {% else %}
+        <div class="poll">
+            <p>{{poll.question}}</p>
+            <form action="{{poll.get_absolute_url}}" method="post">{% csrf_token %}
+            {{ form.vote }}
+            <input type="submit" value="{% trans "Submit" %}" />
+            </form>
+        </div>
+    {% endif %}
+{% endif %}
\ No newline at end of file
diff --git a/apps/polls/templatetags/__init__.py b/apps/polls/templatetags/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/polls/templatetags/polls_tags.py b/apps/polls/templatetags/polls_tags.py
new file mode 100644 (file)
index 0000000..e69a7ce
--- /dev/null
@@ -0,0 +1,18 @@
+from django import template
+from ..forms import PollForm
+
+
+register = template.Library()
+
+@register.inclusion_tag('polls/tags/poll.html', takes_context = True)
+def poll(context, poll, show_results = True, redirect_to = ''):
+    form = None
+    voted_already = poll.voted(context.get('request').session)
+    if not voted_already:
+        form = PollForm(poll = poll, initial = dict(redirect_to = redirect_to))
+    return dict(poll = poll,
+                form = form,
+                voted_already = voted_already,
+                vote_count = poll.vote_count,
+                show_results = show_results
+           )
\ No newline at end of file
diff --git a/apps/polls/urls.py b/apps/polls/urls.py
new file mode 100644 (file)
index 0000000..87738d5
--- /dev/null
@@ -0,0 +1,6 @@
+from django.conf.urls import patterns, url, include
+
+
+urlpatterns = patterns('polls.views',
+    url(r'^(?P<slug>[^/]+)$', 'poll', name='poll'),
+)
diff --git a/apps/polls/views.py b/apps/polls/views.py
new file mode 100644 (file)
index 0000000..ac1c360
--- /dev/null
@@ -0,0 +1,34 @@
+from django.views.decorators.http import require_http_methods\r
+from django.shortcuts import get_object_or_404, redirect, render_to_response\r
+from django.core.urlresolvers import reverse\r
+from django.template import RequestContext
+
+from models import Poll, PollItem\r
+from forms import PollForm
+
+\r
+@require_http_methods(['GET', 'POST'])\r
+def poll(request, slug):
+    
+    poll = get_object_or_404(Poll, slug = slug)
+    
+    if request.method == 'POST':\r
+        redirect_to = reverse('poll', args = [slug])
+        form = PollForm(request.POST, poll = poll)\r
+        if form.is_valid():\r
+            if not poll.voted(request.session):\r
+                try:\r
+                    poll_item = PollItem.objects.filter(pk = form.cleaned_data['vote'], poll = poll).get()
+                except PollItem.DoesNotExist:\r
+                    pass\r
+                else:\r
+                    poll_item.vote(request.session)
+        return redirect(redirect_to)
+    elif request.method == 'GET':
+        context = RequestContext(request)
+        context['poll'] = poll\r
+        context['voted_already'] = poll.voted(request.session)
+        return render_to_response('polls/poll.html', context)
+        
+        \r
+            
\ No newline at end of file
index b3950fc..4300aaf 100644 (file)
@@ -66,6 +66,7 @@ INSTALLED_APPS_OUR = [
     'search',
     'oai',
     'funding',
+    'polls'
     ]
 
 GETPAID_BACKENDS = (
index 960b800..e5bd333 100644 (file)
@@ -31,6 +31,7 @@ PIPELINE_CSS = {
             'sponsors/css/sponsors.css',
             'css/auth.css',
             'funding/funding.scss',
+            'polls/polls.scss',
             'css/form.scss',
 
             'css/social/shelf_tags.css',
index 696a8e8..fd8ed63 100644 (file)
@@ -36,6 +36,7 @@ urlpatterns += patterns('',
     url(r'^uzytkownik/', include('allauth.urls')),
     url(r'^czekaj/', include('waiter.urls')),
     url(r'^wesprzyj/', include('funding.urls')),
+    url(r'^ankieta/', include('polls.urls')),
 
     # Admin panel
     url(r'^admin/catalogue/book/import$', 'catalogue.views.import_book', name='import_book'),