OpenSlides/openslides/poll/models.py
Norman Jäckel c5fbe2e9ee Refactored config API.
Removed form_field attributes.
Added extra fields for HTML rendering like label and help text.
Added fields for sorting and grouping. Removed old collection system.
Added config groups to config view via OPTIONS requests.
Regrouped all variables.
Added validation. Changed internal handling.
2015-06-18 21:13:30 +02:00

277 lines
8.6 KiB
Python

import locale
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
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.
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:
raise KeyError
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 __str__(self):
return self.print_weight()
def get_value(self):
return _(self.value)
def print_weight(self, raw=False):
if raw:
return self.weight
try:
percent_base = self.option.poll.get_percent_base()
except AttributeError:
# The poll class is no child of CollectVotesCast
percent_base = 0
return print_value(self.weight, percent_base)
PERCENT_BASE_CHOICES = (
{'value': 'WITHOUT_INVALID', 'display_name': ugettext_lazy('Only all valid votes')},
{'value': 'WITH_INVALID', 'display_name': ugettext_lazy('All votes cast (including invalid votes)')},
{'value': 'DISABLED', 'display_name': ugettext_lazy('Disabled (no percents)')})
class CollectDefaultVotesMixin(models.Model):
"""
Mixin for a poll to collect the default vote values for valid votes,
invalid votes and votes cast.
"""
votesvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=ugettext_lazy('Valid votes'))
votesinvalid = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=ugettext_lazy('Invalid votes'))
votescast = MinMaxIntegerField(null=True, blank=True, min_value=-2,
verbose_name=ugettext_lazy('Votes cast'))
class Meta:
abstract = True
def append_pollform_fields(self, fields):
fields.append('votesvalid')
fields.append('votesinvalid')
fields.append('votescast')
super(CollectDefaultVotesMixin, self).append_pollform_fields(fields)
def get_percent_base_choice(self):
"""
Returns one of the three strings in PERCENT_BASE_CHOICES.
"""
raise NotImplementedError('You have to provide a get_percent_base_choice() method.')
def print_votesvalid(self):
if self.get_percent_base_choice() == 'DISABLED':
value = print_value(self.votesvalid, None)
else:
value = print_value(self.votesvalid, self.get_percent_base())
return value
def print_votesinvalid(self):
if self.get_percent_base_choice() == 'WITH_INVALID':
value = print_value(self.votesinvalid, self.get_percent_base())
else:
value = print_value(self.votesinvalid, None)
return value
def print_votescast(self):
if self.get_percent_base_choice() == 'WITH_INVALID':
value = print_value(self.votescast, self.get_percent_base())
else:
value = print_value(self.votescast, None)
return value
def get_percent_base(self):
if self.get_percent_base_choice() == "WITHOUT_INVALID" and self.votesvalid > 0:
base = 100 / float(self.votesvalid)
elif self.get_percent_base_choice() == "WITH_INVALID" and self.votescast > 0:
base = 100 / float(self.votescast)
else:
base = None
return base
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 BasePoll(models.Model):
"""
Base poll class.
"""
vote_values = [ugettext_noop('votes')]
class Meta:
abstract = True
def has_votes(self):
"""
Returns True if there are votes in the poll.
"""
if self.get_votes().exists():
return True
return False
def set_options(self, options_data=[]):
"""
Adds new option objects to the poll.
option_data: A list of arguments for the option.
"""
for option_data in options_data:
option = self.get_option_class()(**option_data)
option.poll = self
option.save()
def get_options(self):
"""
Returns the option objects for the poll.
"""
return self.get_option_class().objects.filter(poll=self)
def get_option_class(self):
"""
Returns the option class for the poll. Default is self.option_class.
"""
return self.option_class
def get_vote_values(self):
"""
Returns the possible values for the poll. Default is as list.
"""
return self.vote_values
def get_vote_class(self):
"""
Returns the related vote class.
"""
return self.get_option_class().vote_class
def get_votes(self):
"""
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_vote_objects_with_values(self, option, data):
"""
Creates or updates the vote objects for the poll.
"""
for value in self.get_vote_values():
try:
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()
def get_vote_objects_with_values(self, option_id):
"""
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)
except ObjectDoesNotExist:
values.append(self.get_vote_class()(value=value, weight=''))
else:
values.append(vote)
return values
def get_vote_form(self, **kwargs):
"""
Returns the form for one option of the poll.
"""
from openslides.poll.forms import OptionForm
return OptionForm(extra=self.get_vote_objects_with_values(kwargs['formid']),
**kwargs)
def get_vote_forms(self, **kwargs):
"""
Returns a list of forms for the poll.
"""
forms = []
for option in self.get_options():
form = self.get_vote_form(formid=option.id, **kwargs)
form.option = option
forms.append(form)
return forms
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:
verbose_value = _('majority')
elif value == -2:
verbose_value = _('undocumented')
elif value is None:
verbose_value = _('undocumented')
else:
if percent_base:
locale.setlocale(locale.LC_ALL, '')
verbose_value = u'%d (%s %%)' % (value, locale.format('%.1f', value * percent_base))
else:
verbose_value = u'%s' % value
return verbose_value