diff --git a/CHANGELOG b/CHANGELOG index 516f101ff..90eacdeb0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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) diff --git a/openslides/assignment/models.py b/openslides/assignment/models.py index 67f75c93e..27ca2d221 100644 --- a/openslides/assignment/models.py +++ b/openslides/assignment/models.py @@ -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() diff --git a/openslides/motion/models.py b/openslides/motion/models.py index cdb7c4d02..87ff18230 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -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 diff --git a/openslides/poll/models.py b/openslides/poll/models.py index 021cccc6e..d4450e781 100644 --- a/openslides/poll/models.py +++ b/openslides/poll/models.py @@ -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 diff --git a/openslides/poll/views.py b/openslides/poll/views.py index 32fa89822..13a2f5fab 100644 --- a/openslides/poll/views.py +++ b/openslides/poll/views.py @@ -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'])