OpenSlides/openslides/poll/models.py

244 lines
6.6 KiB
Python
Raw Normal View History

import locale
from decimal import Decimal
2018-08-22 22:00:08 +02:00
from typing import Optional, Type
from django.core.exceptions import ObjectDoesNotExist
from django.core.validators import MinValueValidator
2011-07-31 10:46:29 +02:00
from django.db import models
2012-07-03 00:05:48 +02:00
2012-02-14 16:31:21 +01:00
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.
"""
2019-01-06 16:22:33 +01:00
vote_class: Optional[Type["BaseVote"]] = None
class Meta:
abstract = True
2011-07-31 10:46:29 +02:00
def get_votes(self):
return self.get_vote_class().objects.filter(option=self)
2011-07-31 10:46:29 +02:00
def get_vote_class(self):
if self.vote_class is None:
2019-01-06 16:22:33 +01:00
raise NotImplementedError(
2019-01-12 23:01:42 +01:00
f"The option class {self} has to have an attribute vote_class."
2019-01-06 16:22:33 +01:00
)
return self.vote_class
def __getitem__(self, name):
try:
return self.get_votes().get(value=name)
except self.get_vote_class().DoesNotExist:
raise KeyError
2011-07-31 10:46:29 +02:00
class BaseVote(models.Model):
"""
Base vote class for an option.
2011-07-31 10:46:29 +02:00
Subclasses have to define an option field. This must be a ForeignKeyField
to a subclass of BasePoll.
"""
2019-01-06 16:22:33 +01: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
class Meta:
abstract = True
def __str__(self):
return self.print_weight()
def get_value(self):
2019-01-12 23:01:42 +01:00
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)
2012-02-19 19:27:00 +01:00
class CollectDefaultVotesMixin(models.Model):
"""
Mixin for a poll to collect the default vote values for valid votes,
invalid votes and votes cast.
"""
2019-01-06 16:22:33 +01: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,
)
class Meta:
abstract = True
def get_percent_base_choice(self):
"""
Returns one of the strings of the percent base.
"""
2019-01-06 16:22:33 +01:00
raise NotImplementedError(
"You have to provide a get_percent_base_choice() method."
)
2012-03-03 11:16:10 +01:00
class PublishPollMixin(models.Model):
"""
Mixin for a poll to add a flag whether the poll is published or not.
"""
2019-01-06 16:22:33 +01:00
2012-03-03 11:16:10 +01:00
published = models.BooleanField(default=False)
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):
"""
Base poll class.
"""
2019-01-06 16:22:33 +01:00
vote_values = ["Votes"]
2011-07-31 10:46:29 +02:00
class Meta:
abstract = True
def has_votes(self):
2012-04-17 17:35:50 +02:00
"""
Returns True if there are votes in the poll.
2012-04-17 17:35:50 +02:00
"""
if self.get_votes().exists():
return True
return False
2019-01-12 23:01:42 +01:00
def set_options(self, options_data=None, skip_autoupdate=False):
2012-04-17 17:35:50 +02:00
"""
Adds new option objects to the poll.
2012-04-17 17:35:50 +02:00
option_data: A list of arguments for the option.
2012-04-17 17:35:50 +02:00
"""
2019-01-12 23:01:42 +01:00
if options_data is None:
options_data = []
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
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
"""
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
"""
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
"""
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
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, skip_autoupdate=False):
2012-04-17 17:35:50 +02: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:
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]
vote.save(skip_autoupdate=skip_autoupdate)
2012-02-14 16:31:21 +01:00
def get_vote_objects_with_values(self, option_id):
2012-04-17 17:35:50 +02: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:
vote = self.get_votes().filter(option=option_id).get(value=value)
except ObjectDoesNotExist:
2019-01-06 16:22:33 +01:00
values.append(self.get_vote_class()(value=value, weight=""))
else:
values.append(vote)
2012-02-14 16:31:21 +01:00
return values
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.
"""
2012-02-19 19:27:00 +01:00
if value == -1:
2019-01-12 23:01:42 +01:00
verbose_value = "majority"
2012-02-19 19:27:00 +01:00
elif value == -2:
2019-01-12 23:01:42 +01:00
verbose_value = "undocumented"
2012-07-03 00:05:48 +02:00
elif value is None:
2019-01-12 23:01:42 +01:00
verbose_value = "undocumented"
else:
if percent_base:
2019-01-06 16:22:33 +01:00
locale.setlocale(locale.LC_ALL, "")
verbose_value = "%d (%s %%)" % (
value,
locale.format("%.1f", value * percent_base),
)
else:
2019-01-12 23:01:42 +01:00
verbose_value = value
return verbose_value