2014-04-10 20:18:22 +02:00
|
|
|
import locale
|
2018-08-22 17:34:16 +02:00
|
|
|
from decimal import Decimal
|
2017-08-23 20:51:06 +02:00
|
|
|
from typing import Type # noqa
|
2014-04-10 20:18:22 +02:00
|
|
|
|
2012-07-13 11:16:06 +02:00
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2018-08-22 17:34:16 +02:00
|
|
|
from django.core.validators import MinValueValidator
|
2011-07-31 10:46:29 +02:00
|
|
|
from django.db import models
|
2013-12-02 22:29:11 +01:00
|
|
|
from django.utils.translation import ugettext as _
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2012-07-03 00:05:48 +02:00
|
|
|
|
2012-02-14 16:31:21 +01:00
|
|
|
class BaseOption(models.Model):
|
2012-07-13 11:16:06 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Base option class for a poll.
|
2012-07-13 11:16:06 +02:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
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.
|
2012-07-13 11:16:06 +02:00
|
|
|
"""
|
2017-08-23 20:51:06 +02:00
|
|
|
vote_class = None # type: Type[BaseVote]
|
2013-12-02 22:29:11 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
abstract = True
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2012-02-15 13:44:55 +01:00
|
|
|
def get_votes(self):
|
2012-07-13 11:16:06 +02:00
|
|
|
return self.get_vote_class().objects.filter(option=self)
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
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
|
|
|
|
|
2012-07-13 11:16:06 +02:00
|
|
|
def __getitem__(self, name):
|
|
|
|
try:
|
|
|
|
return self.get_votes().get(value=name)
|
|
|
|
except self.get_vote_class().DoesNotExist:
|
2013-10-17 11:34:54 +02:00
|
|
|
raise KeyError
|
2011-09-08 12:30:34 +02:00
|
|
|
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2012-07-13 11:16:06 +02:00
|
|
|
class BaseVote(models.Model):
|
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Base vote class for an option.
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
Subclasses have to define an option field. This must be a ForeignKeyField
|
|
|
|
to a subclass of BasePoll.
|
2012-07-13 11:16:06 +02:00
|
|
|
"""
|
2018-08-22 17:34:16 +02:00
|
|
|
weight = models.DecimalField(default=Decimal('1'), null=True, validators=[
|
|
|
|
MinValueValidator(Decimal('-2'))], max_digits=15, decimal_places=6)
|
2012-02-14 16:31:21 +01:00
|
|
|
value = models.CharField(max_length=255, null=True)
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
2014-08-16 09:25:18 +02:00
|
|
|
def __str__(self):
|
2013-12-02 22:29:11 +01:00
|
|
|
return self.print_weight()
|
|
|
|
|
|
|
|
def get_value(self):
|
|
|
|
return _(self.value)
|
|
|
|
|
2012-07-13 11:16:06 +02:00
|
|
|
def print_weight(self, raw=False):
|
2012-05-19 14:19:32 +02:00
|
|
|
if raw:
|
|
|
|
return self.weight
|
2012-07-13 11:16:06 +02:00
|
|
|
try:
|
2014-04-10 20:18:22 +02:00
|
|
|
percent_base = self.option.poll.get_percent_base()
|
2012-07-13 11:16:06 +02:00
|
|
|
except AttributeError:
|
2013-12-02 22:29:11 +01:00
|
|
|
# The poll class is no child of CollectVotesCast
|
2012-07-13 11:16:06 +02:00
|
|
|
percent_base = 0
|
|
|
|
return print_value(self.weight, percent_base)
|
2012-02-19 19:27:00 +01:00
|
|
|
|
2012-02-19 17:31:17 +01:00
|
|
|
|
2014-04-10 20:18:22 +02:00
|
|
|
class CollectDefaultVotesMixin(models.Model):
|
2013-12-02 22:29:11 +01:00
|
|
|
"""
|
2014-04-10 20:18:22 +02:00
|
|
|
Mixin for a poll to collect the default vote values for valid votes,
|
|
|
|
invalid votes and votes cast.
|
2013-12-02 22:29:11 +01:00
|
|
|
"""
|
2018-08-22 17:34:16 +02:00
|
|
|
votesvalid = models.DecimalField(null=True, blank=True, validators=[
|
|
|
|
MinValueValidator(Decimal('-2'))], max_digits=15, decimal_places=6)
|
|
|
|
votesinvalid = models.DecimalField(null=True, blank=True, validators=[
|
|
|
|
MinValueValidator(Decimal('-2'))], max_digits=15, decimal_places=6)
|
|
|
|
votescast = models.DecimalField(null=True, blank=True, validators=[
|
|
|
|
MinValueValidator(Decimal('-2'))], max_digits=15, decimal_places=6)
|
2013-12-02 22:29:11 +01:00
|
|
|
|
|
|
|
class Meta:
|
|
|
|
abstract = True
|
2012-02-19 17:31:17 +01:00
|
|
|
|
2014-04-10 20:18:22 +02:00
|
|
|
def get_percent_base_choice(self):
|
|
|
|
"""
|
2016-08-26 13:46:57 +02:00
|
|
|
Returns one of the strings of the percent base.
|
2014-04-10 20:18:22 +02:00
|
|
|
"""
|
|
|
|
raise NotImplementedError('You have to provide a get_percent_base_choice() method.')
|
|
|
|
|
2012-02-19 17:31:17 +01:00
|
|
|
|
2012-03-03 11:16:10 +01:00
|
|
|
class PublishPollMixin(models.Model):
|
2013-12-02 22:29:11 +01:00
|
|
|
"""
|
|
|
|
Mixin for a poll to add a flag whether the poll is published or not.
|
|
|
|
"""
|
2012-03-03 11:16:10 +01:00
|
|
|
published = models.BooleanField(default=False)
|
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
2012-03-03 11:16:10 +01:00
|
|
|
def set_published(self, published):
|
|
|
|
self.published = published
|
|
|
|
self.save()
|
|
|
|
|
|
|
|
|
2012-07-10 11:48:03 +02:00
|
|
|
class BasePoll(models.Model):
|
2012-07-13 11:16:06 +02:00
|
|
|
"""
|
|
|
|
Base poll class.
|
|
|
|
"""
|
2015-12-07 12:40:30 +01:00
|
|
|
vote_values = ['Votes']
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
class Meta:
|
|
|
|
abstract = True
|
|
|
|
|
2012-02-15 13:44:55 +01:00
|
|
|
def has_votes(self):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Returns True if there are votes in the poll.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-07-13 11:16:06 +02:00
|
|
|
if self.get_votes().exists():
|
2012-02-15 13:44:55 +01:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2017-03-24 08:14:08 +01:00
|
|
|
def set_options(self, options_data=[], skip_autoupdate=False):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Adds new option objects to the poll.
|
2012-04-17 17:35:50 +02:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
option_data: A list of arguments for the option.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-02-14 16:31:21 +01:00
|
|
|
for option_data in options_data:
|
2012-04-17 17:35:50 +02:00
|
|
|
option = self.get_option_class()(**option_data)
|
2012-02-14 16:31:21 +01:00
|
|
|
option.poll = self
|
2017-03-24 08:14:08 +01:00
|
|
|
option.save(skip_autoupdate=skip_autoupdate)
|
2012-02-14 16:31:21 +01:00
|
|
|
|
|
|
|
def get_options(self):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Returns the option objects for the poll.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-02-14 16:31:21 +01:00
|
|
|
return self.get_option_class().objects.filter(poll=self)
|
|
|
|
|
|
|
|
def get_option_class(self):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Returns the option class for the poll. Default is self.option_class.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-02-14 16:31:21 +01:00
|
|
|
return self.option_class
|
|
|
|
|
|
|
|
def get_vote_values(self):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Returns the possible values for the poll. Default is as list.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-02-14 16:31:21 +01:00
|
|
|
return self.vote_values
|
|
|
|
|
2012-07-13 11:16:06 +02:00
|
|
|
def get_vote_class(self):
|
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Returns the related vote class.
|
2012-07-13 11:16:06 +02:00
|
|
|
"""
|
|
|
|
return self.get_option_class().vote_class
|
|
|
|
|
|
|
|
def get_votes(self):
|
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Return a QuerySet with all vote objects related to this poll.
|
2012-07-13 11:16:06 +02:00
|
|
|
"""
|
2012-07-16 14:29:30 +02:00
|
|
|
return self.get_vote_class().objects.filter(option__poll__id=self.id)
|
2012-07-13 11:16:06 +02:00
|
|
|
|
2016-10-12 11:23:53 +02:00
|
|
|
def set_vote_objects_with_values(self, option, data, skip_autoupdate=False):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Creates or updates the vote objects for the poll.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-02-14 16:31:21 +01:00
|
|
|
for value in self.get_vote_values():
|
|
|
|
try:
|
2012-07-13 11:16:06 +02:00
|
|
|
vote = self.get_votes().filter(option=option).get(value=value)
|
|
|
|
except ObjectDoesNotExist:
|
|
|
|
vote = self.get_vote_class()(option=option, value=value)
|
2012-02-14 16:31:21 +01:00
|
|
|
vote.weight = data[value]
|
2016-10-12 11:23:53 +02:00
|
|
|
vote.save(skip_autoupdate=skip_autoupdate)
|
2012-02-14 16:31:21 +01:00
|
|
|
|
2013-12-02 22:29:11 +01:00
|
|
|
def get_vote_objects_with_values(self, option_id):
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2013-12-02 22:29:11 +01:00
|
|
|
Returns the vote values and their weight as a list with two elements.
|
2012-04-17 17:35:50 +02:00
|
|
|
"""
|
2012-02-14 16:31:21 +01:00
|
|
|
values = []
|
|
|
|
for value in self.get_vote_values():
|
|
|
|
try:
|
2013-12-02 22:29:11 +01:00
|
|
|
vote = self.get_votes().filter(option=option_id).get(value=value)
|
2012-07-13 11:16:06 +02:00
|
|
|
except ObjectDoesNotExist:
|
|
|
|
values.append(self.get_vote_class()(value=value, weight=''))
|
2013-12-02 22:29:11 +01:00
|
|
|
else:
|
|
|
|
values.append(vote)
|
2012-02-14 16:31:21 +01:00
|
|
|
return values
|
|
|
|
|
2012-07-13 11:16:06 +02:00
|
|
|
|
|
|
|
def print_value(value, percent_base=0):
|
2013-12-02 22:29:11 +01:00
|
|
|
"""
|
|
|
|
Returns a human readable string for the vote value. It is 'majority',
|
|
|
|
'undocumented' or the vote value with percent value if so.
|
|
|
|
"""
|
2012-02-19 19:27:00 +01:00
|
|
|
if value == -1:
|
2013-12-02 22:29:11 +01:00
|
|
|
verbose_value = _('majority')
|
2012-02-19 19:27:00 +01:00
|
|
|
elif value == -2:
|
2013-12-02 22:29:11 +01:00
|
|
|
verbose_value = _('undocumented')
|
2012-07-03 00:05:48 +02:00
|
|
|
elif value is None:
|
2013-12-02 22:29:11 +01:00
|
|
|
verbose_value = _('undocumented')
|
|
|
|
else:
|
|
|
|
if percent_base:
|
2014-04-10 20:18:22 +02:00
|
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
|
|
verbose_value = u'%d (%s %%)' % (value, locale.format('%.1f', value * percent_base))
|
2013-12-02 22:29:11 +01:00
|
|
|
else:
|
|
|
|
verbose_value = u'%s' % value
|
|
|
|
return verbose_value
|