Merge pull request #1146 from normanjaeckel/ReworkPoll

Rename some classes of the poll api. Clean up the poll api.
This commit is contained in:
Norman Jäckel 2013-12-06 15:38:41 -08:00
commit 9bb7cd1388
5 changed files with 103 additions and 88 deletions

View File

@ -16,6 +16,7 @@ Other:
- Changed widget api. Used new metaclass.
- Changed api for plugins.
- Renamed config api classes.
- Renamed some classes of the poll api.
Version 1.5.1 (unreleased)

View File

@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import config
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
CountInvalid, CountVotesCast,
CollectInvalid, CollectVotesCast,
PublishPollMixin)
from openslides.projector.models import RelatedModelMixin, SlideMixin
from openslides.utils.person import PersonField
@ -249,7 +249,7 @@ class AssignmentOption(BaseOption):
return unicode(self.candidate)
class AssignmentPoll(RelatedModelMixin, CountInvalid, CountVotesCast,
class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
PublishPollMixin, BasePoll):
option_class = AssignmentOption
@ -290,9 +290,5 @@ class AssignmentPoll(RelatedModelMixin, CountInvalid, CountVotesCast,
else:
return [ugettext_noop('Votes')]
def append_pollform_fields(self, fields):
CountInvalid.append_pollform_fields(self, fields)
CountVotesCast.append_pollform_fields(self, fields)
def get_ballot(self):
return self.assignment.poll_set.filter(id__lte=self.id).count()

View File

@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.config.api import config
from openslides.mediafile.models import Mediafile
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
CountInvalid, CountVotesCast)
CollectInvalid, CollectVotesCast)
from openslides.projector.models import RelatedModelMixin, SlideMixin
from openslides.utils.jsonfield import JSONField
from openslides.utils.person import PersonField
@ -686,7 +686,7 @@ class MotionOption(BaseOption):
"""The VoteClass, to witch this Class links."""
class MotionPoll(RelatedModelMixin, CountInvalid, CountVotesCast, BasePoll):
class MotionPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast, BasePoll):
"""The Class to saves the poll results for a motion poll."""
motion = models.ForeignKey(Motion, related_name='polls')
@ -731,11 +731,6 @@ class MotionPoll(RelatedModelMixin, CountInvalid, CountVotesCast, BasePoll):
# or call this in save()
self.get_option_class()(poll=self).save()
def append_pollform_fields(self, fields):
"""Apend the fields for invalid and votecast to the ModelForm."""
CountInvalid.append_pollform_fields(self, fields)
CountVotesCast.append_pollform_fields(self, fields)
def get_related_model(self):
return self.motion

View File

@ -2,44 +2,59 @@
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_noop
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy, ugettext_noop
from openslides.utils.models import MinMaxIntegerField
class BaseOption(models.Model):
"""
Base option class for a Poll.
Base option class for a poll.
Subclasses have to define a poll-field, which are a subclass of BasePoll.
Subclasses have to define a poll field. This must be a ForeignKeyField
to a subclass of BasePoll. There must also be a vote_class attribute
which has to be a subclass of BaseVote. Otherwise you have to override the
get_vote_class method.
"""
vote_class = None
class Meta:
abstract = True
def get_votes(self):
return self.get_vote_class().objects.filter(option=self)
def get_vote_class(self):
if self.vote_class is None:
raise NotImplementedError('The option class %s has to have an attribute vote_class.' % self)
return self.vote_class
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 BaseVote(models.Model):
"""
Base vote class for an option.
Subclasses have to define an option field. This must be a ForeignKeyField
to a subclass of BasePoll.
"""
weight = models.IntegerField(default=1, null=True) # Use MinMaxIntegerField
value = models.CharField(max_length=255, null=True)
class Meta:
abstract = True
def __unicode__(self):
return self.print_weight()
class BaseVote(models.Model):
"""
Base Vote class for an option.
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_value(self):
return _(self.value)
def print_weight(self, raw=False):
if raw:
@ -47,27 +62,24 @@ class BaseVote(models.Model):
try:
percent_base = self.option.poll.percent_base()
except AttributeError:
# The poll class is no child of CountVotesCast
# The poll class is no child of CollectVotesCast
percent_base = 0
return print_value(self.weight, percent_base)
def get_value(self):
return _(self.value)
def __unicode__(self):
return self.print_weight()
class CollectVotesCast(models.Model):
"""
Mixin for a poll to collect the votes cast.
"""
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=ugettext_lazy('Votes cast'))
class Meta:
abstract = True
class CountVotesCast(models.Model):
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=_("Votes cast"))
def append_pollform_fields(self, fields):
fields.append('votescast')
super(CollectVotesCast, self).append_pollform_fields(fields)
def print_votescast(self):
return print_value(self.votescast, self.percent_base())
@ -77,40 +89,43 @@ class CountVotesCast(models.Model):
return 100 / float(self.votescast)
return 0
class CollectInvalid(models.Model):
"""
Mixin for a poll to collect invalid votes.
"""
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=ugettext_lazy('Votes invalid'))
class Meta:
abstract = True
class CountInvalid(models.Model):
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=_("Votes invalid"))
def append_pollform_fields(self, fields):
fields.append('votesinvalid')
super(CollectInvalid, self).append_pollform_fields(fields)
def print_votesinvalid(self):
try:
percent_base = self.percent_base()
except AttributeError:
# The poll class is no child of CountVotesCast
# The poll class is no child of CollectVotesCast
percent_base = 0
return print_value(self.votesinvalid, percent_base)
class Meta:
abstract = True
class PublishPollMixin(models.Model):
"""
Mixin for a poll to add a flag whether the poll is published or not.
"""
published = models.BooleanField(default=False)
class Meta:
abstract = True
def set_published(self, published):
self.published = published
self.save()
class Meta:
abstract = True
class BasePoll(models.Model):
"""
@ -118,9 +133,12 @@ class BasePoll(models.Model):
"""
vote_values = [ugettext_noop('votes')]
class Meta:
abstract = True
def has_votes(self):
"""
Return True, the there are votes in the poll.
Returns True if there are votes in the poll.
"""
if self.get_votes().exists():
return True
@ -128,9 +146,9 @@ class BasePoll(models.Model):
def set_options(self, options_data=[]):
"""
Add new Option pbjects to the poll.
Adds new option objects to the poll.
option_data: A List of arguments for the Option.
option_data: A list of arguments for the option.
"""
for option_data in options_data:
option = self.get_option_class()(**option_data)
@ -139,38 +157,37 @@ class BasePoll(models.Model):
def get_options(self):
"""
Return the option objects for the poll.
Returns the option objects for the poll.
"""
return self.get_option_class().objects.filter(poll=self)
def get_option_class(self):
"""
Return the option class for the poll. Default is self.option_class.
Returns the option class for the poll. Default is self.option_class.
"""
return self.option_class
def get_vote_values(self):
"""
Return the possible values for the poll as list.
Returns the possible values for the poll. Default is as list.
"""
return self.vote_values
def get_vote_class(self):
"""
Return the releatet vote class.
Returns the related 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 a QuerySet with all vote objects related to this poll.
"""
return self.get_vote_class().objects.filter(option__poll__id=self.id)
def set_form_values(self, option, data):
# TODO: recall this function. It has nothing to do with a form
def set_vote_objects_with_values(self, option, data):
"""
Create or update the vote objects for the poll.
Creates or updates the vote objects for the poll.
"""
for value in self.get_vote_values():
try:
@ -180,33 +197,31 @@ class BasePoll(models.Model):
vote.weight = data[value]
vote.save()
def get_form_values(self, option_id):
# TODO: recall this function. It has nothing to do with a form
def get_vote_objects_with_values(self, option_id):
"""
Return a the values and the weight of the values as a list with two
elements.
Returns the vote values and their weight as a list with two elements.
"""
values = []
for value in self.get_vote_values():
try:
vote = self.get_votes().filter(option=option_id) \
.get(value=value)
values.append(vote)
vote = self.get_votes().filter(option=option_id).get(value=value)
except ObjectDoesNotExist:
values.append(self.get_vote_class()(value=value, weight=''))
else:
values.append(vote)
return values
def get_vote_form(self, **kwargs):
"""
Return the form for one option of the poll.
Returns the form for one option of the poll.
"""
from openslides.poll.forms import OptionForm
return OptionForm(extra=self.get_form_values(kwargs['formid']),
return OptionForm(extra=self.get_vote_objects_with_values(kwargs['formid']),
**kwargs)
def get_vote_forms(self, **kwargs):
"""
Return a list of forms for the poll
Returns a list of forms for the poll.
"""
forms = []
for option in self.get_options():
@ -215,19 +230,28 @@ class BasePoll(models.Model):
forms.append(form)
return forms
class Meta:
abstract = True
def append_pollform_fields(self, fields):
"""
Appends additional field to a given list of fields. By default it
appends nothing.
"""
pass
def print_value(value, percent_base=0):
"""
Returns a human readable string for the vote value. It is 'majority',
'undocumented' or the vote value with percent value if so.
"""
if value == -1:
return unicode(_('majority'))
verbose_value = _('majority')
elif value == -2:
return unicode(_('undocumented'))
verbose_value = _('undocumented')
elif value is None:
return unicode(_('undocumented'))
if not percent_base:
return u'%s' % value
return u'%d (%.2f %%)' % (value, value * percent_base)
verbose_value = _('undocumented')
else:
if percent_base:
verbose_value = u'%d (%.2f %%)' % (value, value * percent_base)
else:
verbose_value = u'%s' % value
return verbose_value

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from django.core.exceptions import ImproperlyConfigured
from django.forms.models import modelform_factory
from django.http import HttpResponseRedirect
@ -40,7 +39,7 @@ class PollFormView(FormMixin, TemplateView):
data = {}
for value in self.poll.get_vote_values():
data[value] = form.cleaned_data[value]
self.poll.set_form_values(form.option, data)
self.poll.set_vote_objects_with_values(form.option, data)
pollform.save()
return HttpResponseRedirect(self.get_success_url())
@ -49,9 +48,9 @@ class PollFormView(FormMixin, TemplateView):
if self.poll_class is not None:
return self.poll_class
else:
raise ImproperlyConfigured(
"No poll class defined. Either provide a poll_class or define"
" a get_poll_class method.")
raise NotImplementedError(
'No poll class defined. Either provide a poll_class or define '
'a get_poll_class method.')
def get_object(self):
return self.get_poll_class().objects.get(pk=self.kwargs['poll_id'])