From 0957de83a9191bd62eaeb6e24ff631c35e6ab44e Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Tue, 14 Feb 2012 16:31:21 +0100 Subject: [PATCH] start to rewrite the poll-api --- .../agenda/templates/agenda/overview.html | 24 +- openslides/application/models.py | 33 +- .../templates/application/poll_view.html | 79 ++--- .../templates/application/view.html | 2 +- openslides/application/urls.py | 10 +- openslides/application/views.py | 41 ++- openslides/assignment/views.py | 4 +- openslides/participant/models.py | 1 + openslides/poll/__init__.py | 12 + openslides/poll/models.py | 283 ++++++++---------- openslides/utils/pdf.py | 26 +- 11 files changed, 245 insertions(+), 270 deletions(-) diff --git a/openslides/agenda/templates/agenda/overview.html b/openslides/agenda/templates/agenda/overview.html index 67c81d835..8eefd1212 100644 --- a/openslides/agenda/templates/agenda/overview.html +++ b/openslides/agenda/templates/agenda/overview.html @@ -137,29 +137,7 @@ {% if perms.agenda.can_manage_agenda %} - {% ifequal item.type 'ItemApplication' %} - {% trans "Application" %} {{ item.cast.application.number }} - {% endifequal %} - - {% ifequal item.type 'ItemPoll' %} - {% if item.cast.poll.application %} - {% trans "Poll of Application" %} - {% endif %} - {% if item.cast.poll.assignment %} - {% trans "Poll of Election" %} - {% endif %} - {% endifequal %} - - {% ifequal item.type 'ItemAssignment' %} - {% trans "Election" %} - {% endifequal %} - - {% ifequal item.type 'ItemText' %} - Text - {% endifequal %} - {% if item.hidden %} - ({% trans 'hidden' %}) - {% endif %} + Text {% endif %} diff --git a/openslides/application/models.py b/openslides/application/models.py index cf09f713e..c2b07c2c2 100644 --- a/openslides/application/models.py +++ b/openslides/application/models.py @@ -23,6 +23,8 @@ from projector.models import Slide from participant.models import Profile from system.api import config_get from utils.utils import _propper_unicode +from poll import ChoicePoll +from poll.models import BaseOption, BasePoll class Application(models.Model, Slide): @@ -410,24 +412,28 @@ class Application(models.Model, Slide): """ Generates a poll object for the application """ - from poll.models import Poll - poll = Poll(optiondecision=True, \ - application=self) + poll = ApplicationPoll() poll.save() - poll.add_option(self) + poll.set_options([{'application': self}]) self.writelog(_("Poll created"), user) return poll + @property + def polls(self): + #todo: return an query_set + return [option.poll for option in self.applicationoption_set.all()] + @property def results(self): """ Return a list of voting results """ + # TODO: This will propably not work results = [] - for poll in self.poll_set.all(): - for option in poll.options: - if poll.votesinvalid != None and poll.votescast != None: - results.append([option.yes, option.no, option.undesided, poll.votesinvalidf, poll.votescastf]) + for poll in self.polls: + for option in poll.get_options(): + #if poll.votesinvalid != None and poll.votescast != None: + results.append([option.yes, option.no, option.undesided, poll.votesinvalidf, poll.votescastf]) return results def slide(self): @@ -484,3 +490,14 @@ class AVersion(models.Model): return self._aid register_slidemodel(Application) + + +class ApplicationOption(BaseOption): + application = models.ForeignKey(Application) + + def __unicode__(self): + return unicode(self.application) + + +class ApplicationPoll(BasePoll): + option_class = ApplicationOption diff --git a/openslides/application/templates/application/poll_view.html b/openslides/application/templates/application/poll_view.html index ab83ab86c..a2ba6ac2b 100644 --- a/openslides/application/templates/application/poll_view.html +++ b/openslides/application/templates/application/poll_view.html @@ -6,60 +6,45 @@ {% if perms.application.can_manage_application %} {% block submenu %} -{{ block.super }} -
-

{%trans "Application No." %} {{ poll.application.number }}

- + {{ block.super }} +
+

{%trans "Application No." %} {{ poll.application.number }}

+ {% endblock %} {% endif %} {% block content %} -

{%trans "Application No." %} {{ poll.application.number }} - {%trans "Vote" %}

-

{{ poll.application.title }}

+

{%trans "Application No." %} {{ application.number }} - {%trans "Vote" %}

+

{{ application.title }}

{%trans "Results of" %} {{ ballot }}. {%trans "Vote" %}

-1 := {% trans 'majority' %}, -2 := {% trans 'undocumented' %}
{% csrf_token %} - - - - - - - - - - - - - - - - - - - - - - - - - -
{%trans "Option" %}{%trans "Votes" %}
{%trans "Yes" %}{{ options.0.form.yes.errors }}{{ options.0.form.yes }}
{%trans "No" %}{{ options.0.form.no.errors }}{{ options.0.form.no }}
{%trans "Abstentions" %}{{ options.0.form.undesided.errors }}{{ options.0.form.undesided }}
{%trans "Invalid votes" %}{{ form.invalid.errors }}{{ form.invalid }}
{%trans "Votes cast" %}{{ form.votescast.errors }}{{ form.votescast }}
-

- - - - - - + + + + + + {% for value in forms.0 %} + + + + + {% endfor %} +
{% trans "Option" %}{% trans "Votes" %}
{{ value.label }}{{ value.errors }}{{ value }}
+

+ + + + +

{% endblock %} diff --git a/openslides/application/templates/application/view.html b/openslides/application/templates/application/view.html index f8b40b979..4fedb25b4 100644 --- a/openslides/application/templates/application/view.html +++ b/openslides/application/templates/application/view.html @@ -47,7 +47,7 @@ {% endfor %}

{% trans "Vote results" %}:

- {% with application.poll_set.all as polls %} + {% with application.polls as polls %} {% if polls|length == 0 %} {% if perms.application.can_manage_application %} {% if "genpoll" in actions %} diff --git a/openslides/application/urls.py b/openslides/application/urls.py index d87e8474f..64f1fb3d2 100644 --- a/openslides/application/urls.py +++ b/openslides/application/urls.py @@ -11,6 +11,10 @@ """ from django.conf.urls.defaults import * +from django.utils.translation import ugettext as _ + +from application.views import ViewPoll + urlpatterns = patterns('application.views', url(r'^application/$', 'overview', \ @@ -77,8 +81,10 @@ urlpatterns = patterns('application.views', url(r'^application/poll/(?P\d+)/print$', 'print_application_poll', \ name='print_application_poll'), - url(r'^application/poll/(?P\d+)$', 'view_poll', \ - name='application_poll_view'), + url(r'^application/poll/(?P\d+)$', + ViewPoll.as_view(), + name='application_poll_view' + ), url(r'^application/poll/(?P\d+)/del$', 'delete_poll', \ name='application_poll_delete'), diff --git a/openslides/application/views.py b/openslides/application/views.py index c0f98b0a6..52c2868de 100644 --- a/openslides/application/views.py +++ b/openslides/application/views.py @@ -23,14 +23,15 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from django.db import transaction -from openslides.agenda.models import Item -from openslides.application.models import Application, AVersion -from openslides.application.forms import ApplicationForm, \ +from agenda.models import Item +from application.models import Application, AVersion, ApplicationPoll +from application.forms import ApplicationForm, \ ApplicationManagerForm, \ ApplicationImportForm from openslides.participant.models import Profile -from openslides.poll.models import Poll -from openslides.poll.forms import OptionResultForm, PollForm + +from poll.models import PollFormView + from openslides.utils.utils import template, permission_required, \ render_to_forbitten, del_confirm_form, gen_confirm_form from openslides.utils.pdf import print_application, print_application_poll @@ -92,7 +93,7 @@ def view(request, application_id, newest=False): 'actions': actions, 'min_supporters': int(config_get('application_min_supporters')), 'version': version, - 'results': application.results + #'results': application.results } @@ -347,7 +348,7 @@ def gen_poll(request, application_id): poll = Application.objects.get(pk=application_id).gen_poll(user=request.user) messages.success(request, _("New vote was successfully created.") ) except Application.DoesNotExist: - pass + pass # TODO: do not call poll after this excaption return redirect(reverse('application_poll_view', args=[poll.id])) @@ -373,9 +374,9 @@ def view_poll(request, poll_id): """ view a poll for this application. """ - poll = Poll.objects.get(pk=poll_id) - ballot = poll.ballot - options = poll.options + poll = ApplicationPoll.objects.get(pk=poll_id) + #ballot = poll.ballot + options = poll.get_options() if request.user.has_perm('application.can_manage_application'): if request.method == 'POST': form = PollForm(request.POST, prefix="poll") @@ -408,9 +409,27 @@ def view_poll(request, poll_id): 'poll': poll, 'form': form, 'options': options, - 'ballot': ballot, + #'ballot': ballot, } + +class ViewPoll(PollFormView): + poll_class = ApplicationPoll + vote_values = [_('yes'), _('no'), _('contained')] + template_name = 'application/poll_view.html' + + def get_context_data(self, **kwargs): + context = super(ViewPoll, self).get_context_data() + self.application = self.poll.get_options()[0].application + context['application'] = self.application + return context + + def get_success_url(self): + if not 'apply' in self.request.POST: + return reverse('application_view', args=[self.application.id]) + return '' + + @permission_required('application.can_manage_application') def permit_version(request, aversion_id): aversion = AVersion.objects.get(pk=aversion_id) diff --git a/openslides/assignment/views.py b/openslides/assignment/views.py index 70e7394e7..14c32e648 100644 --- a/openslides/assignment/views.py +++ b/openslides/assignment/views.py @@ -17,8 +17,8 @@ from django.contrib.auth.decorators import login_required from django.utils.translation import ugettext as _ from openslides.agenda.models import Item -from poll.models import Poll, Option -from poll.forms import OptionResultForm, PollForm +#from poll.models import Poll, Option +#from poll.forms import OptionResultForm, PollForm from assignment.models import Assignment from assignment.forms import AssignmentForm, AssignmentRunForm from utils.utils import template, permission_required, gen_confirm_form, del_confirm_form, ajax_request diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 32c06d3a3..d1f20bfcb 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -52,3 +52,4 @@ class Profile(models.Model): ('can_see_participant', "Can see participant"), ('can_manage_participant', "Can manage participant"), ) + diff --git a/openslides/poll/__init__.py b/openslides/poll/__init__.py index e69de29bb..53035f305 100644 --- a/openslides/poll/__init__.py +++ b/openslides/poll/__init__.py @@ -0,0 +1,12 @@ +from django.utils.translation import ugettext as _ + +from models import PollFormView, BasePoll + + +class DesicionPoll(PollFormView): + vote_values = [_('yes'), _('no'), _('contained')] + poll_class = BasePoll + + +class ChoicePoll(PollFormView): + poll_class = BasePoll diff --git a/openslides/poll/models.py b/openslides/poll/models.py index 0f03c8e86..0ba4b299f 100644 --- a/openslides/poll/models.py +++ b/openslides/poll/models.py @@ -12,185 +12,142 @@ from django.db import models from django.utils.translation import ugettext as _ - -from application.models import Application -from assignment.models import Assignment -from participant.models import Profile +from django import forms +from django.views.generic import TemplateView +from django.http import HttpResponseRedirect -class Poll(models.Model): - optiondecision = models.BooleanField(default=True, verbose_name = _("Poll of decision (yes, no, abstention)")) - application = models.ForeignKey(Application, null=True, blank=True, verbose_name = _("Application")) - assignment = models.ForeignKey(Assignment, null=True, blank=True, verbose_name = _("Election")) +class OptionForm(forms.Form): + def __init__(self, *args, **kwargs): + extra = kwargs.pop('extra') + formid = kwargs.pop('formid') + kwargs['prefix'] = "option-%s" % formid + super(OptionForm, self).__init__(*args, **kwargs) + + for key, value in extra: + self.fields[key] = forms.IntegerField( + widget=forms.TextInput(attrs={'class': 'small-input'}), + label=_(key), + initial=value, + ) + +class BaseOption(models.Model): + poll = models.ForeignKey('BasePoll') + + @property + def votes(self): + count = 0 + for vote in Vote.objects.filter(option=self): + count += vote.weight + return weight + + +class TextOption(BaseOption): + text = models.CharField(max_length=255) + + def __unicode__(self): + return self.text + + +class Vote(models.Model): + option = models.ForeignKey(BaseOption) + #profile = models.ForeignKey(Profile) # TODO: we need a person+ here + weight = models.IntegerField(default=1) + value = models.CharField(max_length=255, null=True) + + +class BasePoll(models.Model): description = models.TextField(null=True, blank=True, verbose_name = _("Description")) votescast = models.IntegerField(null=True, blank=True, verbose_name = _("Votes cast")) votesinvalid = models.IntegerField(null=True, blank=True, verbose_name = _("Votes invalid")) - published = models.BooleanField(default=False) - def add_option(self, option): - self.save() - optionc = Option() - optionc.poll = self - if isinstance(option, Application): - optionc.application = option - elif isinstance(option, Profile): - optionc.user = option - else: - optionc.text = str(option) - optionc.save() - return optionc + option_class = TextOption + vote_values = [_('votes')] - @property - def votescastf(self): - if self.votescast == -2: - return _('undocumented') - elif self.votescast: - return self.votescast - return '0' - - @property - def votesinvalidf(self): - if self.votesinvalid == -2: - return _('undocumented') - elif self.votesinvalid: - if self.votescast > 0: - percentage = round(float(self.votesinvalid) / float(self.votescast) * 100,1) - invalid = "%s (%s %%)" % (str(self.votesinvalid), str(percentage)) - return invalid - return self.votesinvalid - return '0' - - def has_vote(self): - for option in self.options: - if option.voteyes or option.voteno or option.voteundesided: - return True - return False + def set_options(self, options_data): + for option_data in options_data: + option = self.option_class(**option_data) + option.poll = self + option.save() def get_options(self): - return self.option_set.all() + return self.get_option_class().objects.filter(poll=self) - @property - def options(self): - return self.option_set.all() + def get_option_class(self): + return self.option_class - @property - def options_values(self): - return [option.value for option in self.options] + def get_vote_values(self): + return self.vote_values - def set_published(self, published=True): - """ - Changes the published-status of the poll. - """ - self.published = published - self.save() - - @property - def count_ballots(self): - if self.application: - return Poll.objects.filter(application=self.application).count() - if self.assignment: - return Poll.objects.filter(assignment=self.assignment).count() - return None - - @property - def ballot(self): - if self.application: - counter = 0 - for poll in Poll.objects.filter(application=self.application): - counter = counter + 1 - if self == poll: - return counter - if self.assignment: - counter = 0 - for poll in Poll.objects.filter(assignment=self.assignment): - counter = counter + 1 - if self == poll: - return counter - return None - - @models.permalink - def get_absolute_url(self, link='view'): - if self.application: - if link == 'view': - return ('application_poll_view', [str(self.id), 0]) - if link == 'delete': - return ('application_poll_delete', [str(self.id)]) - if self.assignment: - if link == 'view': - return ('assignment_poll_view', [str(self.id), 0]) - if link == 'delete': - return ('assignment_poll_delete', [str(self.id)]) - if link == 'view': - return ('poll_view', [str(self.id)]) - if link == 'delete': - return ('poll_delete', [str(self.id)]) - - def __unicode__(self): - if self.application: - return self.application.title - if self.assignment: - return self.assignment.name + def set_form_values(self, option, data): + for value in self.get_vote_values(): + try: + vote = Vote.objects.filter(option=option).get(value=value) + except Vote.DoesNotExist: + vote = Vote(option=option, value=value) + vote.weight = data[value] + vote.save() -class Option(models.Model): - text = models.CharField(max_length=100, null=True, blank=True, verbose_name = _("Text")) - user = models.ForeignKey(Profile, null=True, blank=True, verbose_name = _("Participant")) - application = models.ForeignKey(Application, null=True, blank=True, verbose_name = _("Application")) - poll = models.ForeignKey(Poll, verbose_name = _("Poll")) - voteyes = models.IntegerField(null=True, blank=True) - voteno = models.IntegerField(null=True, blank=True) - voteundesided = models.IntegerField(null=True, blank=True) + def get_form_values(self, option_id): + values = [] + for value in self.get_vote_values(): + try: + vote = Vote.objects.filter(option=option_id).get(value=value) + weight = vote.weight + except Vote.DoesNotExist: + weight = None + values.append((value, weight)) + return values - @property - def yes(self): - if self.voteyes == -1: - return _('majority') - if self.voteyes == -2: - return _('undocumented') - if self.voteyes: - if self.poll.votescast > 0 and self.voteyes > 0: - percentage = round(float(self.voteyes) / float(self.poll.votescast) * 100,1) - return "%s (%s %%)" % (str(self.voteyes), str(percentage)) - return self.voteyes - return '0' + def get_vote_form(self, **kwargs): + return OptionForm(extra=self.get_form_values(kwargs['formid']), **kwargs) - @property - def no(self): - if self.voteno == -1: - return _('majority') - if self.voteno == -2: - return _('undocumented') - if self.voteno: - if self.poll.votescast > 0 and self.voteno > 0: - percentage = round(float(self.voteno) / float(self.poll.votescast) * 100,1) - return "%s (%s %%)" % (str(self.voteno), str(percentage)) - return self.voteno - return '0' + def get_vote_forms(self, **kwargs): + forms = [] + for option in self.get_options(): + form = self.get_vote_form(formid=option.id, **kwargs) + form.option = option + forms.append(form) - @property - def undesided(self): - if self.voteundesided == -1: - return _('majority') - if self.voteundesided == -2: - return _('undocumented') - if self.voteundesided: - if self.poll.votescast > 0 and self.voteundesided > 0: - percentage = round(float(self.voteundesided) / float(self.poll.votescast) * 100,1) - return "%s (%s %%)" % (str(self.voteundesided), str(percentage)) - return self.voteundesided - return '0' + return forms - @property - def value(self): - if self.text != "" and self.text is not None: - return self.text - if self.user is not None: - return self.user - if self.application is not None: - return self.application - return None - def __unicode__(self): - if self.value: - return unicode(self.value) - return _("No options") +class PollFormView(TemplateView): + template_name = 'poll/poll.html' + poll_argument = 'poll_id' + + def set_poll(self, poll_id): + poll_id = poll_id + self.poll = self.poll_class.objects.get(pk=poll_id) + self.poll.vote_values = self.vote_values + + def get_context_data(self, **kwargs): + context = super(PollFormView, self).get_context_data(**kwargs) + self.set_poll(self.kwargs['poll_id']) + context['poll'] = self.poll + if not 'forms' in context: + context['forms'] = context['poll'].get_vote_forms() + return context + + def get_success_url(self): + return self.success_url + + def post(self, request, *args, **kwargs): + context = self.get_context_data(**kwargs) + forms = self.poll.get_vote_forms(data=self.request.POST) + error = False + for form in forms: + if not form.is_valid(): + error = True + if error: + return self.render_to_response(self.get_context_data(forms=forms)) + + for form in forms: + data = {} + for value in self.poll.vote_values: + data[value] = form.cleaned_data[value] + print data + self.poll.set_form_values(form.option, data) + return HttpResponseRedirect(self.get_success_url()) + diff --git a/openslides/utils/pdf.py b/openslides/utils/pdf.py index 4a1c1b6cd..86e093a5e 100755 --- a/openslides/utils/pdf.py +++ b/openslides/utils/pdf.py @@ -36,7 +36,7 @@ from openslides.agenda.models import Item from openslides.agenda.api import children_list from openslides.application.models import Application from openslides.assignment.models import Assignment -from openslides.poll.models import Poll, Option +#from openslides.poll.models import Poll, Option from openslides.participant.models import Profile from openslides.system.api import config_get from openslides.settings import SITE_ROOT @@ -343,7 +343,7 @@ def get_application(application, story): else: story.append(Paragraph(_("Application No."), stylesheet['Heading1'])) - + # submitter cell1a = [] cell1a.append(Spacer(0,0.2*cm)) @@ -357,7 +357,7 @@ def get_application(application, story): cell1b.append(Spacer(0,0.2*cm)) else: cell1b.append(Paragraph(unicode(application.submitter.profile), stylesheet['Normal'])) - + # supporters cell2a = [] cell2a.append(Paragraph("%s:" % _("Supporters"), stylesheet['Heading4'])) @@ -368,7 +368,7 @@ def get_application(application, story): for x in range(0,application.missing_supporters): cell2b.append(Paragraph(".  __________________________________________",stylesheet['Signaturefield'])) cell2b.append(Spacer(0,0.2*cm)) - + # status note = "" for n in application.notes: @@ -389,7 +389,7 @@ def get_application(application, story): data.append([cell1a,cell1b]) data.append([cell2a,cell2b]) data.append([cell3a,cell3b]) - + # voting results if len(application.results) > 0: cell4a = [] @@ -412,7 +412,7 @@ def get_application(application, story): ])) story.append(t) story.append(Spacer(0,1*cm)) - + # title story.append(Paragraph(application.title, stylesheet['Heading3'])) # text @@ -431,7 +431,7 @@ def print_application(request, application_id=None): doc = SimpleDocTemplate(response) doc.title = None story = [] - + if application_id is None: #print all applications title = config_get("application_pdf_title") story.append(Paragraph(title, stylesheet['Heading1'])) @@ -614,7 +614,7 @@ def get_assignment(assignment, story): ('LINEBELOW',(0,-1),(-1,-1),2,colors.black), ('ROWBACKGROUNDS', (0, 1), (-1, -1), (colors.white, (.9, .9, .9))), ])) - + # table data = [] data.append([cell1a,cell1b]) @@ -635,7 +635,7 @@ def get_assignment(assignment, story): # text story.append(Paragraph("%s" % assignment.description.replace('\r\n','
'), stylesheet['Paragraph'])) return story - + @permission_required('application.can_see_application') def print_assignment(request, assignment_id=None): response = HttpResponse(mimetype='application/pdf') @@ -644,7 +644,7 @@ def print_assignment(request, assignment_id=None): doc = SimpleDocTemplate(response) doc.title = None story = [] - + if assignment_id is None: #print all applications title = config_get("assignment_pdf_title") story.append(Paragraph(title, stylesheet['Heading1'])) @@ -667,7 +667,7 @@ def print_assignment(request, assignment_id=None): doc.build(story, onFirstPage=firstPage, onLaterPages=firstPage) return response - + @permission_required('application.can_manage_application') def print_assignment_poll(request, poll_id=None): poll = Poll.objects.get(id=poll_id) @@ -714,7 +714,7 @@ def print_assignment_poll(request, poll_id=None): rest = number % 2 if rest: data.append([cell,'']) - + if len(options) <= 2: t=Table(data, 10.5*cm, 7.42*cm) elif len(options) <= 5: @@ -742,7 +742,7 @@ def print_assignment_poll(request, poll_id=None): rest = number % 2 if rest: data.append([cell,'']) - + if len(options) <= 4: t=Table(data, 10.5*cm, 7.42*cm) elif len(options) <= 8: