From: Aleksander Łukasz Date: Mon, 20 May 2013 13:07:34 +0000 (+0200) Subject: Introducing polls X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/9203d27cee887569ad260dd91d798df68a1d3ada?ds=inline Introducing polls --- diff --git a/apps/polls/__init__.py b/apps/polls/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/polls/admin.py b/apps/polls/admin.py new file mode 100644 index 000000000..29fc0aaec --- /dev/null +++ b/apps/polls/admin.py @@ -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 index 000000000..f9ca9078a --- /dev/null +++ b/apps/polls/forms.py @@ -0,0 +1,10 @@ +from django import forms + + +class PollForm(forms.Form): + vote = forms.ChoiceField(widget = forms.RadioSelect) + + def __init__(self, *args, **kwargs): + poll = kwargs.pop('poll', None) + super(PollForm, self).__init__(*args, **kwargs) + 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 index 000000000..d1d6fa78e 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 index 000000000..5b902f883 --- /dev/null +++ b/apps/polls/locale/pl/LC_MESSAGES/django.po @@ -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 , 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 \n" +"Language-Team: LANGUAGE \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 index 000000000..e5550b097 --- /dev/null +++ b/apps/polls/migrations/0001_initial.py @@ -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 index 000000000..e69de29bb diff --git a/apps/polls/models.py b/apps/polls/models.py new file mode 100644 index 000000000..d63e5aa3e --- /dev/null +++ b/apps/polls/models.py @@ -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') + + 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 index 000000000..0dd10fadf --- /dev/null +++ b/apps/polls/static/polls/polls.css @@ -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 index 000000000..28cd5a5cf --- /dev/null +++ b/apps/polls/static/polls/polls.scss @@ -0,0 +1,21 @@ +.poll { + width: 400px; + 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/templates/polls/poll.html b/apps/polls/templates/polls/poll.html new file mode 100644 index 000000000..edb14cf7b --- /dev/null +++ b/apps/polls/templates/polls/poll.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% load i18n %} +{% load polls_tags %} + +{% block titleextra %}{{poll.question}}{% endblock %} + + +{% block body %} +
+ {% if voted_already %} +

{% trans "Thanks for voting! You can see current results below." %}

+ {% endif %} + {% poll poll %} +
+{% 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 index 000000000..355348aad --- /dev/null +++ b/apps/polls/templates/polls/tags/poll.html @@ -0,0 +1,31 @@ +{% load i18n %} + +{% if poll %} + {% if voted_already %} + {% if show_results %} +
+

{{poll.question}}

+
    + {% for item in poll.items.all %} +
  • + {{item.content}} +
    +
  • + {% endfor %} +
+
+ {% endif %} + {% else %} +
+

{{poll.question}}

+
{% csrf_token %} + {{ form.vote }} + +
+
+ {% 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 index 000000000..e69de29bb diff --git a/apps/polls/templatetags/polls_tags.py b/apps/polls/templatetags/polls_tags.py new file mode 100644 index 000000000..e69a7ce96 --- /dev/null +++ b/apps/polls/templatetags/polls_tags.py @@ -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 index 000000000..87738d5a0 --- /dev/null +++ b/apps/polls/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls import patterns, url, include + + +urlpatterns = patterns('polls.views', + url(r'^(?P[^/]+)$', 'poll', name='poll'), +) diff --git a/apps/polls/views.py b/apps/polls/views.py new file mode 100644 index 000000000..ac1c36028 --- /dev/null +++ b/apps/polls/views.py @@ -0,0 +1,34 @@ +from django.views.decorators.http import require_http_methods +from django.shortcuts import get_object_or_404, redirect, render_to_response +from django.core.urlresolvers import reverse +from django.template import RequestContext + +from models import Poll, PollItem +from forms import PollForm + + +@require_http_methods(['GET', 'POST']) +def poll(request, slug): + + poll = get_object_or_404(Poll, slug = slug) + + if request.method == 'POST': + redirect_to = reverse('poll', args = [slug]) + form = PollForm(request.POST, poll = poll) + if form.is_valid(): + if not poll.voted(request.session): + try: + poll_item = PollItem.objects.filter(pk = form.cleaned_data['vote'], poll = poll).get() + except PollItem.DoesNotExist: + pass + else: + poll_item.vote(request.session) + return redirect(redirect_to) + elif request.method == 'GET': + context = RequestContext(request) + context['poll'] = poll + context['voted_already'] = poll.voted(request.session) + return render_to_response('polls/poll.html', context) + + + \ No newline at end of file diff --git a/wolnelektury/settings/__init__.py b/wolnelektury/settings/__init__.py index b3950fccb..4300aafa5 100644 --- a/wolnelektury/settings/__init__.py +++ b/wolnelektury/settings/__init__.py @@ -66,6 +66,7 @@ INSTALLED_APPS_OUR = [ 'search', 'oai', 'funding', + 'polls' ] GETPAID_BACKENDS = ( diff --git a/wolnelektury/settings/static.py b/wolnelektury/settings/static.py index 960b800fc..e5bd333f6 100644 --- a/wolnelektury/settings/static.py +++ b/wolnelektury/settings/static.py @@ -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', diff --git a/wolnelektury/urls.py b/wolnelektury/urls.py index 696a8e87c..fd8ed63bf 100644 --- a/wolnelektury/urls.py +++ b/wolnelektury/urls.py @@ -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'),