redesign the poll api, so it don't creates poll-tables. Also, it is easier to get the poll for a vote object.

#288 show percent after the vote results
This commit is contained in:
Oskar Hahn 2012-07-13 11:16:06 +02:00
parent 969816be37
commit 66b853a10a
8 changed files with 121 additions and 57 deletions

View File

@ -12,21 +12,23 @@
from datetime import datetime
from django.db import models
from django.db.models import Max
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models import Max
from django.dispatch import receiver
from django.utils.translation import pgettext
from django.utils.translation import ugettext_lazy as _, ugettext_noop
from openslides.utils.utils import _propper_unicode
from openslides.config.models import config
from openslides.config.signals import default_config_value
from openslides.participant.models import Profile
from openslides.poll.models import (BaseOption, BasePoll, CountVotesCast,
CountInvalid, Vote)
CountInvalid, BaseVote)
from openslides.projector.api import register_slidemodel
from openslides.projector.models import SlideMixin
@ -537,14 +539,13 @@ class AVersion(models.Model):
register_slidemodel(Application)
class ApplicationVote(BaseVote):
option = models.ForeignKey('ApplicationOption')
class ApplicationOption(BaseOption):
def __getattr__(self, name):
if name in ['Yes', 'No', 'Abstain']:
try:
return self.get_votes().get(value=name)
except Vote.DoesNotExist:
return None
raise AttributeError(name)
poll = models.ForeignKey('ApplicationPoll')
vote_class = ApplicationVote
class ApplicationPoll(BasePoll, CountInvalid, CountVotesCast):
@ -572,10 +573,6 @@ class ApplicationPoll(BasePoll, CountInvalid, CountVotesCast):
return self.application.applicationpoll_set.filter(id__lte=self.id).count()
from django.dispatch import receiver
from openslides.config.signals import default_config_value
@receiver(default_config_value, dispatch_uid="application_default_config")
def default_config(sender, key, **kwargs):
return {

View File

@ -6,10 +6,10 @@
{% block title %}{{ block.super }} {% trans "Application No." %} {{ application.number }} {% trans "Vote" %}{% endblock %}
{% block content %}
<h1>{%trans "Application No." %} {{ application.number }} {%trans "Vote" %}</h1>
<h1>{% trans "Application No." %} {{ application.number }} {% trans "Vote" %}</h1>
<h3>{{ application.title }}</h3>
<p>{%trans "Results of" %} {{ ballot }}. {%trans "Vote" %}</p>
<p>{% trans "Results of" %} {{ ballot }}. {% trans "Vote" %}</p>
<i class="helptext">{% trans "Special values" %}: -1 = {% trans 'majority' %}; -2 = {% trans 'undocumented' %}</i>
<form action="" method="post" class="small-form">{% csrf_token %}
{{ pre_form }}

View File

@ -50,7 +50,9 @@
{% if perms.application.can_manage_application %}
{% if "genpoll" in actions %}
<a href='{% url application_gen_poll application.id %}'>
<span class="button"><span class="icon statistics">{%trans 'New vote' %}</span></span>
<span class="button">
<span class="icon statistics">{%trans 'New vote' %}</span>
</span>
</a>
{% else %}
-
@ -65,8 +67,12 @@
<li>
{% if perms.application.can_manage_application %}
<strong>{{ forloop.counter }}. {% trans "Vote" %} </strong>
<a class="icon edit" href="{% url application_poll_view poll.id %}" title="{% trans 'Edit Vote' %}"><span></span></a>
<a class="icon delete" href="{% url application_poll_delete poll.id %}" title="{% trans 'Delete Vote' %}"><span></span></a>
<a class="icon edit" href="{% url application_poll_view poll.id %}" title="{% trans 'Edit Vote' %}">
<span></span>
</a>
<a class="icon delete" href="{% url application_poll_delete poll.id %}" title="{% trans 'Delete Vote' %}">
<span></span>
</a>
{% elif poll.has_votes %}
<strong>{{ forloop.counter }}. {% trans "Vote" %}:</strong>
{% endif %}

View File

@ -496,6 +496,7 @@ class ApplicationDelete(DeleteView):
else:
self.gen_confirm_form(request, _('Do you really want to delete <b>%s</b>?') % self.object, self.object.get_absolute_url('delete'))
class ViewPoll(PollFormView):
permission_required = 'application.can_manage_application'
poll_class = ApplicationPoll

View File

@ -24,7 +24,7 @@ from openslides.projector.projector import SlideMixin
from openslides.participant.models import Profile
from openslides.poll.models import (BasePoll, CountInvalid, CountVotesCast,
BaseOption, PublishPollMixin)
BaseOption, PublishPollMixin, BaseVote)
from openslides.agenda.models import Item
@ -143,7 +143,7 @@ class Assignment(models.Model, SlideMixin):
# candidate related to this poll
poll_option = poll.get_options().get(candidate=candidate)
for vote in poll_option.get_votes():
votes[vote.value] = vote.get_weight()
votes[vote.value] = vote.print_weight()
except AssignmentOption.DoesNotExist:
# candidate not in related to this poll
votes = None
@ -196,8 +196,14 @@ class Assignment(models.Model, SlideMixin):
register_slidemodel(Assignment)
class AssignmentVote(BaseVote):
option = models.ForeignKey('AssignmentOption')
class AssignmentOption(BaseOption):
poll = models.ForeignKey('AssignmentPoll')
candidate = models.ForeignKey(Profile)
vote_class = AssignmentVote
def __unicode__(self):
return unicode(self.candidate)

View File

@ -14,7 +14,6 @@ from django import forms
from django.utils.translation import ugettext_lazy as _
from openslides.utils.forms import CssClassMixin
from openslides.poll.models import Vote
class OptionForm(forms.Form, CssClassMixin):
@ -27,7 +26,7 @@ class OptionForm(forms.Form, CssClassMixin):
for vote in extra:
key = vote.value
value = vote.get_value()
weight = vote.get_weight(raw=True)
weight = vote.print_weight(raw=True)
self.fields[key] = forms.IntegerField(
label=value,
initial=weight,

View File

@ -10,6 +10,7 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.translation import ugettext_lazy as _, ugettext_noop
@ -17,35 +18,57 @@ from openslides.utils.modelfields import MinMaxIntegerField
class BaseOption(models.Model):
poll = models.ForeignKey('BasePoll')
"""
Base option class for a Poll.
Subclasses have to define a poll-field, which are a subclass of BasePoll.
"""
def get_votes(self):
return Vote.objects.filter(option=self)
return self.get_vote_class().objects.filter(option=self)
def __getitem__(self, name):
try:
return self.get_votes().get(value=name)
except self.get_vote_class().DoesNotExist:
return None
def get_vote_class(self):
return self.vote_class
class Meta:
abstract = True
class TextOption(BaseOption):
text = models.CharField(max_length=255)
class BaseVote(models.Model):
"""
Base Vote class for an option.
def __unicode__(self):
return self.text
class Vote(models.Model):
option = models.ForeignKey(BaseOption)
#profile = models.ForeignKey(Profile) # TODO: we need a person+ here
Subclasses have to define a option-field, which are a subclass of
BaseOption.
"""
weight = models.IntegerField(default=1, null=True) # Use MinMaxIntegerField
value = models.CharField(max_length=255, null=True)
def get_weight(self, raw=False):
def print_weight(self, raw=False):
if raw:
return self.weight
return print_value(self.weight)
try:
percent_base = self.option.poll.percent_base()
except AttributeError:
# The poll class is no child of CountVotesCast
percent_base = 0
return print_value(self.weight, percent_base)
def get_value(self):
return unicode(_(self.value))
return _(self.value)
def __unicode__(self):
return self.get_weight()
return self.print_weight()
class Meta:
abstract = True
class CountVotesCast(models.Model):
@ -58,6 +81,9 @@ class CountVotesCast(models.Model):
def print_votescast(self):
return print_value(self.votescast)
def percent_base(self):
return 100 / float(self.votescast)
class Meta:
abstract = True
@ -70,7 +96,13 @@ class CountInvalid(models.Model):
fields.append('votesinvalid')
def print_votesinvalid(self):
return print_value(self.votesinvalid)
try:
percent_base = self.percent_base()
except AttributeError:
# The poll class is no child of CountVotesCast
percent_base = 0
return print_value(self.votesinvalid, percent_base)
class Meta:
abstract = True
@ -88,14 +120,16 @@ class PublishPollMixin(models.Model):
class BasePoll(models.Model):
option_class = TextOption
"""
Base poll class.
"""
vote_values = [ugettext_noop('votes')]
def has_votes(self):
"""
Return True, the there are votes in the poll.
"""
if self.get_options().filter(vote__isnull=False):
if self.get_votes().exists():
return True
return False
@ -128,6 +162,19 @@ class BasePoll(models.Model):
"""
return self.vote_values
def get_vote_class(self):
"""
Return the releatet vote class.
"""
return self.get_option_class().vote_class
def get_votes(self):
"""
Return a QuerySet with all vote objects, releatet to this poll.
"""
return self.get_vote_class().objects
def set_form_values(self, option, data):
# TODO: recall this function. It has nothing to do with a form
"""
@ -135,9 +182,9 @@ class BasePoll(models.Model):
"""
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 = self.get_votes().filter(option=option).get(value=value)
except ObjectDoesNotExist:
vote = self.get_vote_class()(option=option, value=value)
vote.weight = data[value]
vote.save()
@ -150,10 +197,11 @@ class BasePoll(models.Model):
values = []
for value in self.get_vote_values():
try:
vote = Vote.objects.filter(option=option_id).get(value=value)
vote = self.get_votes().filter(option=option_id) \
.get(value=value)
values.append(vote)
except Vote.DoesNotExist:
values.append(Vote(value=value, weight=''))
except ObjectDoesNotExist:
values.append(self.get_vote_class()(value=value, weight=''))
return values
def get_vote_form(self, **kwargs):
@ -175,12 +223,19 @@ class BasePoll(models.Model):
forms.append(form)
return forms
class Meta:
abstract = True
def print_value(value, percent_base=0):
def print_value(value):
if value == -1:
value = _('majority')
return unicode(_('majority'))
elif value == -2:
value = _('undocumented')
return unicode(_('undocumented'))
elif value is None:
value = ''
return unicode(value)
return u''
if not percent_base:
return u'%s' % value
return u'%d (%.2f %%)' % (value, value * percent_base)

View File

@ -48,14 +48,14 @@ class PollFormView(TemplateView):
def post(self, request, *args, **kwargs):
self.set_poll(self.kwargs['poll_id'])
forms = self.poll.get_vote_forms(data=self.request.POST)
option_forms = self.poll.get_vote_forms(data=self.request.POST)
FormClass = self.get_modelform_class()
pollform = FormClass(data=self.request.POST, instance=self.poll,
prefix='pollform')
error = False
for form in forms:
for form in option_forms:
if not form.is_valid():
error = True
@ -64,11 +64,11 @@ class PollFormView(TemplateView):
if error:
return self.render_to_response(self.get_context_data(
forms=forms,
forms=option_forms,
pollform=pollform,
))
for form in forms:
for form in option_forms:
data = {}
for value in self.poll.get_vote_values():
data[value] = form.cleaned_data[value]