Merge pull request #1146 from normanjaeckel/ReworkPoll
Rename some classes of the poll api. Clean up the poll api.
This commit is contained in:
commit
9bb7cd1388
@ -16,6 +16,7 @@ Other:
|
|||||||
- Changed widget api. Used new metaclass.
|
- Changed widget api. Used new metaclass.
|
||||||
- Changed api for plugins.
|
- Changed api for plugins.
|
||||||
- Renamed config api classes.
|
- Renamed config api classes.
|
||||||
|
- Renamed some classes of the poll api.
|
||||||
|
|
||||||
|
|
||||||
Version 1.5.1 (unreleased)
|
Version 1.5.1 (unreleased)
|
||||||
|
@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
||||||
CountInvalid, CountVotesCast,
|
CollectInvalid, CollectVotesCast,
|
||||||
PublishPollMixin)
|
PublishPollMixin)
|
||||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
||||||
from openslides.utils.person import PersonField
|
from openslides.utils.person import PersonField
|
||||||
@ -249,7 +249,7 @@ class AssignmentOption(BaseOption):
|
|||||||
return unicode(self.candidate)
|
return unicode(self.candidate)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentPoll(RelatedModelMixin, CountInvalid, CountVotesCast,
|
class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
||||||
PublishPollMixin, BasePoll):
|
PublishPollMixin, BasePoll):
|
||||||
option_class = AssignmentOption
|
option_class = AssignmentOption
|
||||||
|
|
||||||
@ -290,9 +290,5 @@ class AssignmentPoll(RelatedModelMixin, CountInvalid, CountVotesCast,
|
|||||||
else:
|
else:
|
||||||
return [ugettext_noop('Votes')]
|
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):
|
def get_ballot(self):
|
||||||
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
||||||
|
@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.mediafile.models import Mediafile
|
from openslides.mediafile.models import Mediafile
|
||||||
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
from openslides.poll.models import (BaseOption, BasePoll, BaseVote,
|
||||||
CountInvalid, CountVotesCast)
|
CollectInvalid, CollectVotesCast)
|
||||||
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
from openslides.projector.models import RelatedModelMixin, SlideMixin
|
||||||
from openslides.utils.jsonfield import JSONField
|
from openslides.utils.jsonfield import JSONField
|
||||||
from openslides.utils.person import PersonField
|
from openslides.utils.person import PersonField
|
||||||
@ -686,7 +686,7 @@ class MotionOption(BaseOption):
|
|||||||
"""The VoteClass, to witch this Class links."""
|
"""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."""
|
"""The Class to saves the poll results for a motion poll."""
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion, related_name='polls')
|
motion = models.ForeignKey(Motion, related_name='polls')
|
||||||
@ -731,11 +731,6 @@ class MotionPoll(RelatedModelMixin, CountInvalid, CountVotesCast, BasePoll):
|
|||||||
# or call this in save()
|
# or call this in save()
|
||||||
self.get_option_class()(poll=self).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):
|
def get_related_model(self):
|
||||||
return self.motion
|
return self.motion
|
||||||
|
|
||||||
|
@ -2,44 +2,59 @@
|
|||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.translation import ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.utils.models import MinMaxIntegerField
|
from openslides.utils.models import MinMaxIntegerField
|
||||||
|
|
||||||
|
|
||||||
class BaseOption(models.Model):
|
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):
|
def get_votes(self):
|
||||||
return self.get_vote_class().objects.filter(option=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):
|
def __getitem__(self, name):
|
||||||
try:
|
try:
|
||||||
return self.get_votes().get(value=name)
|
return self.get_votes().get(value=name)
|
||||||
except self.get_vote_class().DoesNotExist:
|
except self.get_vote_class().DoesNotExist:
|
||||||
return None
|
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:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.print_weight()
|
||||||
|
|
||||||
class BaseVote(models.Model):
|
def get_value(self):
|
||||||
"""
|
return _(self.value)
|
||||||
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 print_weight(self, raw=False):
|
def print_weight(self, raw=False):
|
||||||
if raw:
|
if raw:
|
||||||
@ -47,27 +62,24 @@ class BaseVote(models.Model):
|
|||||||
try:
|
try:
|
||||||
percent_base = self.option.poll.percent_base()
|
percent_base = self.option.poll.percent_base()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# The poll class is no child of CountVotesCast
|
# The poll class is no child of CollectVotesCast
|
||||||
percent_base = 0
|
percent_base = 0
|
||||||
|
|
||||||
return print_value(self.weight, percent_base)
|
return print_value(self.weight, percent_base)
|
||||||
|
|
||||||
def get_value(self):
|
|
||||||
return _(self.value)
|
|
||||||
|
|
||||||
def __unicode__(self):
|
class CollectVotesCast(models.Model):
|
||||||
return self.print_weight()
|
"""
|
||||||
|
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:
|
class Meta:
|
||||||
abstract = True
|
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):
|
def append_pollform_fields(self, fields):
|
||||||
fields.append('votescast')
|
fields.append('votescast')
|
||||||
|
super(CollectVotesCast, self).append_pollform_fields(fields)
|
||||||
|
|
||||||
def print_votescast(self):
|
def print_votescast(self):
|
||||||
return print_value(self.votescast, self.percent_base())
|
return print_value(self.votescast, self.percent_base())
|
||||||
@ -77,40 +89,43 @@ class CountVotesCast(models.Model):
|
|||||||
return 100 / float(self.votescast)
|
return 100 / float(self.votescast)
|
||||||
return 0
|
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:
|
class Meta:
|
||||||
abstract = True
|
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):
|
def append_pollform_fields(self, fields):
|
||||||
fields.append('votesinvalid')
|
fields.append('votesinvalid')
|
||||||
|
super(CollectInvalid, self).append_pollform_fields(fields)
|
||||||
|
|
||||||
def print_votesinvalid(self):
|
def print_votesinvalid(self):
|
||||||
try:
|
try:
|
||||||
percent_base = self.percent_base()
|
percent_base = self.percent_base()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# The poll class is no child of CountVotesCast
|
# The poll class is no child of CollectVotesCast
|
||||||
percent_base = 0
|
percent_base = 0
|
||||||
|
|
||||||
return print_value(self.votesinvalid, percent_base)
|
return print_value(self.votesinvalid, percent_base)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class PublishPollMixin(models.Model):
|
class PublishPollMixin(models.Model):
|
||||||
|
"""
|
||||||
|
Mixin for a poll to add a flag whether the poll is published or not.
|
||||||
|
"""
|
||||||
published = models.BooleanField(default=False)
|
published = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def set_published(self, published):
|
def set_published(self, published):
|
||||||
self.published = published
|
self.published = published
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class BasePoll(models.Model):
|
class BasePoll(models.Model):
|
||||||
"""
|
"""
|
||||||
@ -118,9 +133,12 @@ class BasePoll(models.Model):
|
|||||||
"""
|
"""
|
||||||
vote_values = [ugettext_noop('votes')]
|
vote_values = [ugettext_noop('votes')]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def has_votes(self):
|
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():
|
if self.get_votes().exists():
|
||||||
return True
|
return True
|
||||||
@ -128,9 +146,9 @@ class BasePoll(models.Model):
|
|||||||
|
|
||||||
def set_options(self, options_data=[]):
|
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:
|
for option_data in options_data:
|
||||||
option = self.get_option_class()(**option_data)
|
option = self.get_option_class()(**option_data)
|
||||||
@ -139,38 +157,37 @@ class BasePoll(models.Model):
|
|||||||
|
|
||||||
def get_options(self):
|
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)
|
return self.get_option_class().objects.filter(poll=self)
|
||||||
|
|
||||||
def get_option_class(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
|
return self.option_class
|
||||||
|
|
||||||
def get_vote_values(self):
|
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
|
return self.vote_values
|
||||||
|
|
||||||
def get_vote_class(self):
|
def get_vote_class(self):
|
||||||
"""
|
"""
|
||||||
Return the releatet vote class.
|
Returns the related vote class.
|
||||||
"""
|
"""
|
||||||
return self.get_option_class().vote_class
|
return self.get_option_class().vote_class
|
||||||
|
|
||||||
def get_votes(self):
|
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)
|
return self.get_vote_class().objects.filter(option__poll__id=self.id)
|
||||||
|
|
||||||
def set_form_values(self, option, data):
|
def set_vote_objects_with_values(self, option, data):
|
||||||
# TODO: recall this function. It has nothing to do with a form
|
|
||||||
"""
|
"""
|
||||||
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():
|
for value in self.get_vote_values():
|
||||||
try:
|
try:
|
||||||
@ -180,33 +197,31 @@ class BasePoll(models.Model):
|
|||||||
vote.weight = data[value]
|
vote.weight = data[value]
|
||||||
vote.save()
|
vote.save()
|
||||||
|
|
||||||
def get_form_values(self, option_id):
|
def get_vote_objects_with_values(self, option_id):
|
||||||
# TODO: recall this function. It has nothing to do with a form
|
|
||||||
"""
|
"""
|
||||||
Return a the values and the weight of the values as a list with two
|
Returns the vote values and their weight as a list with two elements.
|
||||||
elements.
|
|
||||||
"""
|
"""
|
||||||
values = []
|
values = []
|
||||||
for value in self.get_vote_values():
|
for value in self.get_vote_values():
|
||||||
try:
|
try:
|
||||||
vote = self.get_votes().filter(option=option_id) \
|
vote = self.get_votes().filter(option=option_id).get(value=value)
|
||||||
.get(value=value)
|
|
||||||
values.append(vote)
|
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
values.append(self.get_vote_class()(value=value, weight=''))
|
values.append(self.get_vote_class()(value=value, weight=''))
|
||||||
|
else:
|
||||||
|
values.append(vote)
|
||||||
return values
|
return values
|
||||||
|
|
||||||
def get_vote_form(self, **kwargs):
|
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
|
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)
|
**kwargs)
|
||||||
|
|
||||||
def get_vote_forms(self, **kwargs):
|
def get_vote_forms(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return a list of forms for the poll
|
Returns a list of forms for the poll.
|
||||||
"""
|
"""
|
||||||
forms = []
|
forms = []
|
||||||
for option in self.get_options():
|
for option in self.get_options():
|
||||||
@ -215,19 +230,28 @@ class BasePoll(models.Model):
|
|||||||
forms.append(form)
|
forms.append(form)
|
||||||
return forms
|
return forms
|
||||||
|
|
||||||
class Meta:
|
def append_pollform_fields(self, fields):
|
||||||
abstract = True
|
"""
|
||||||
|
Appends additional field to a given list of fields. By default it
|
||||||
|
appends nothing.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def print_value(value, percent_base=0):
|
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:
|
if value == -1:
|
||||||
return unicode(_('majority'))
|
verbose_value = _('majority')
|
||||||
elif value == -2:
|
elif value == -2:
|
||||||
return unicode(_('undocumented'))
|
verbose_value = _('undocumented')
|
||||||
elif value is None:
|
elif value is None:
|
||||||
return unicode(_('undocumented'))
|
verbose_value = _('undocumented')
|
||||||
if not percent_base:
|
else:
|
||||||
return u'%s' % value
|
if percent_base:
|
||||||
|
verbose_value = u'%d (%.2f %%)' % (value, value * percent_base)
|
||||||
return u'%d (%.2f %%)' % (value, value * percent_base)
|
else:
|
||||||
|
verbose_value = u'%s' % value
|
||||||
|
return verbose_value
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
|
||||||
from django.forms.models import modelform_factory
|
from django.forms.models import modelform_factory
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
data = {}
|
data = {}
|
||||||
for value in self.poll.get_vote_values():
|
for value in self.poll.get_vote_values():
|
||||||
data[value] = form.cleaned_data[value]
|
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()
|
pollform.save()
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
@ -49,9 +48,9 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
if self.poll_class is not None:
|
if self.poll_class is not None:
|
||||||
return self.poll_class
|
return self.poll_class
|
||||||
else:
|
else:
|
||||||
raise ImproperlyConfigured(
|
raise NotImplementedError(
|
||||||
"No poll class defined. Either provide a poll_class or define"
|
'No poll class defined. Either provide a poll_class or define '
|
||||||
" a get_poll_class method.")
|
'a get_poll_class method.')
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return self.get_poll_class().objects.get(pk=self.kwargs['poll_id'])
|
return self.get_poll_class().objects.get(pk=self.kwargs['poll_id'])
|
||||||
|
Loading…
Reference in New Issue
Block a user