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:
parent
969816be37
commit
66b853a10a
@ -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 {
|
||||
|
@ -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 %}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user