Wrote docstrings to the motion app
This commit is contained in:
parent
a34731c00e
commit
a08cf84ab8
@ -1,2 +1,14 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.motion
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The OpenSlides motion app appends the functionality to OpenSlides, to
|
||||||
|
manage motions.
|
||||||
|
|
||||||
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
import openslides.motion.signals
|
import openslides.motion.signals
|
||||||
import openslides.motion.slides
|
import openslides.motion.slides
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.motion.forms
|
openslides.motion.forms
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Forms for the motion app.
|
Defines the DjangoForms for the motion app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -20,14 +20,30 @@ from .workflow import motion_workflow_choices
|
|||||||
|
|
||||||
|
|
||||||
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
||||||
|
"""Base FormClass for a Motion.
|
||||||
|
|
||||||
|
For it's own, it append the version data es fields.
|
||||||
|
|
||||||
|
The Class can be mixed with the following Mixins to add fields for the
|
||||||
|
submitter, supporters etc.
|
||||||
"""
|
"""
|
||||||
Form to automaticly save the version data for a motion.
|
|
||||||
"""
|
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
|
||||||
|
"""Title of the Motion. Will be saved in a MotionVersion object."""
|
||||||
|
|
||||||
|
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
|
||||||
|
"""Text of the Motion. Will be saved in a MotionVersion object."""
|
||||||
|
|
||||||
|
reason = forms.CharField(
|
||||||
|
widget=forms.Textarea(), required=False, label=_("Reason"))
|
||||||
|
"""Reason of the Motion. will be saved in a MotionVersion object."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Motion
|
model = Motion
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Fill the FormFields releated to the version data with initial data."""
|
||||||
self.motion = kwargs.get('instance', None)
|
self.motion = kwargs.get('instance', None)
|
||||||
self.initial = kwargs.setdefault('initial', {})
|
self.initial = kwargs.setdefault('initial', {})
|
||||||
if self.motion is not None:
|
if self.motion is not None:
|
||||||
@ -36,16 +52,15 @@ class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
|||||||
self.initial['reason'] = self.motion.reason
|
self.initial['reason'] = self.motion.reason
|
||||||
super(BaseMotionForm, self).__init__(*args, **kwargs)
|
super(BaseMotionForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
title = forms.CharField(widget=forms.TextInput(), label=_("Title"))
|
|
||||||
text = forms.CharField(widget=forms.Textarea(), label=_("Text"))
|
|
||||||
reason = forms.CharField(
|
|
||||||
widget=forms.Textarea(), required=False, label=_("Reason"))
|
|
||||||
|
|
||||||
|
|
||||||
class MotionSubmitterMixin(forms.ModelForm):
|
class MotionSubmitterMixin(forms.ModelForm):
|
||||||
|
"""Mixin to append the submitter field to a MotionForm."""
|
||||||
|
|
||||||
submitter = MultiplePersonFormField(label=_("Submitter"))
|
submitter = MultiplePersonFormField(label=_("Submitter"))
|
||||||
|
"""Submitter of the Motion. Can be one or more persons."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Fill in the submitter of the motion as default value."""
|
||||||
if self.motion is not None:
|
if self.motion is not None:
|
||||||
submitter = [submitter.person.person_id for submitter in self.motion.submitter.all()]
|
submitter = [submitter.person.person_id for submitter in self.motion.submitter.all()]
|
||||||
self.initial['submitter'] = submitter
|
self.initial['submitter'] = submitter
|
||||||
@ -53,9 +68,13 @@ class MotionSubmitterMixin(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class MotionSupporterMixin(forms.ModelForm):
|
class MotionSupporterMixin(forms.ModelForm):
|
||||||
|
"""Mixin to append the supporter field to a Motionform."""
|
||||||
|
|
||||||
supporter = MultiplePersonFormField(required=False, label=_("Supporters"))
|
supporter = MultiplePersonFormField(required=False, label=_("Supporters"))
|
||||||
|
"""Supporter of the Motion. Can be one or more persons."""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Fill in the supporter of the motions as default value."""
|
||||||
if self.motion is not None:
|
if self.motion is not None:
|
||||||
supporter = [supporter.person.person_id for supporter in self.motion.supporter.all()]
|
supporter = [supporter.person.person_id for supporter in self.motion.supporter.all()]
|
||||||
self.initial['supporter'] = supporter
|
self.initial['supporter'] = supporter
|
||||||
@ -63,18 +82,21 @@ class MotionSupporterMixin(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class MotionCreateNewVersionMixin(forms.ModelForm):
|
class MotionCreateNewVersionMixin(forms.ModelForm):
|
||||||
|
"""Mixin to add the option to the form, to choose, to create a new version."""
|
||||||
|
|
||||||
new_version = forms.BooleanField(
|
new_version = forms.BooleanField(
|
||||||
required=False, label=_("Create new version"), initial=True,
|
required=False, label=_("Create new version"), initial=True,
|
||||||
help_text=_("Trivial changes don't create a new version."))
|
help_text=_("Trivial changes don't create a new version."))
|
||||||
|
"""BooleanField to decide, if a new version will be created, or the
|
||||||
|
last_version will be used."""
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(forms.Form, CssClassMixin):
|
class ConfigForm(CssClassMixin, forms.Form):
|
||||||
|
"""Form for the configuration tab of OpenSlides."""
|
||||||
motion_min_supporters = forms.IntegerField(
|
motion_min_supporters = forms.IntegerField(
|
||||||
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
widget=forms.TextInput(attrs={'class': 'small-input'}),
|
||||||
label=_("Number of (minimum) required supporters for a motion"),
|
label=_("Number of (minimum) required supporters for a motion"),
|
||||||
initial=4,
|
initial=4, min_value=0, max_value=8,
|
||||||
min_value=0,
|
|
||||||
max_value=8,
|
|
||||||
help_text=_("Choose 0 to disable the supporting system"),
|
help_text=_("Choose 0 to disable the supporting system"),
|
||||||
)
|
)
|
||||||
motion_preamble = forms.CharField(
|
motion_preamble = forms.CharField(
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
Models for the motion app.
|
Models for the motion app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
To use a motion object, you only have to import the Motion class. Any
|
||||||
|
functionality can be reached from a motion object.
|
||||||
|
|
||||||
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -34,39 +37,38 @@ from .workflow import (motion_workflow_choices, get_state, State, WorkflowError,
|
|||||||
DUMMY_STATE)
|
DUMMY_STATE)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Save submitter and supporter in the same table
|
|
||||||
class MotionSubmitter(models.Model):
|
|
||||||
person = PersonField()
|
|
||||||
motion = models.ForeignKey('Motion', related_name="submitter")
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.person)
|
|
||||||
|
|
||||||
|
|
||||||
class MotionSupporter(models.Model):
|
|
||||||
person = PersonField()
|
|
||||||
motion = models.ForeignKey('Motion', related_name="supporter")
|
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return unicode(self.person)
|
|
||||||
|
|
||||||
|
|
||||||
class Motion(SlideMixin, models.Model):
|
class Motion(SlideMixin, models.Model):
|
||||||
"""
|
"""The Motion Class.
|
||||||
The Motion-Model.
|
|
||||||
"""
|
This class is the main entry point to all other classes related to a motion.
|
||||||
prefix = "motion"
|
"""
|
||||||
|
|
||||||
|
prefix = "motion"
|
||||||
|
"""Prefix for the slide system."""
|
||||||
|
|
||||||
|
active_version = models.ForeignKey('MotionVersion', null=True,
|
||||||
|
related_name="active_version")
|
||||||
|
"""Points to a specific version.
|
||||||
|
|
||||||
|
Used be the permitted-version-system to deside witch version is the active
|
||||||
|
Version. Could also be used to only choose a specific version as a default
|
||||||
|
version. Like the Sighted versions on Wikipedia.
|
||||||
|
"""
|
||||||
|
|
||||||
active_version = models.ForeignKey(
|
|
||||||
'MotionVersion', null=True, related_name="active_version")
|
|
||||||
state_id = models.CharField(max_length=3)
|
state_id = models.CharField(max_length=3)
|
||||||
# Log (Translatable)
|
"""The id of a state object.
|
||||||
|
|
||||||
|
This Attribute is used be motion.state to identify the current state of the
|
||||||
|
motion.
|
||||||
|
"""
|
||||||
|
|
||||||
identifier = models.CharField(max_length=255, null=True, blank=True,
|
identifier = models.CharField(max_length=255, null=True, blank=True,
|
||||||
unique=True)
|
unique=True)
|
||||||
category = models.ForeignKey('Category', null=True, blank=True)
|
"""A string as human readable identifier for the motion."""
|
||||||
# TODO proposal
|
|
||||||
# Maybe rename to master_copy
|
# category = models.ForeignKey('Category', null=True, blank=True)
|
||||||
master = models.ForeignKey('self', null=True, blank=True)
|
# TODO: proposal
|
||||||
|
#master = models.ForeignKey('self', null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
permissions = (
|
permissions = (
|
||||||
@ -79,17 +81,35 @@ class Motion(SlideMixin, models.Model):
|
|||||||
# ordering = ('number',)
|
# ordering = ('number',)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
"""Return a human readable name of this motion."""
|
||||||
return self.get_title()
|
return self.get_title()
|
||||||
|
|
||||||
# TODO: Use transaction
|
# TODO: Use transaction
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""
|
"""Save the motion.
|
||||||
Saves the motion. Create or update a motion_version object
|
|
||||||
|
1. Set the state of a new motion to the default motion.
|
||||||
|
2. Save the motion object.
|
||||||
|
3. Save the version Data.
|
||||||
|
4. Set the active version for the motion.
|
||||||
|
|
||||||
|
A new version will be saved if motion.new_version was called
|
||||||
|
between the creation of this object and the last call of motion.save()
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
If the motion has new version data (title, text, reason)
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
the config 'motion_create_new_version' is set to
|
||||||
|
'ALLWASY_CREATE_NEW_VERSION'.
|
||||||
"""
|
"""
|
||||||
if not self.state_id:
|
if not self.state_id:
|
||||||
self.reset_state()
|
self.reset_state()
|
||||||
|
|
||||||
super(Motion, self).save(*args, **kwargs)
|
super(Motion, self).save(*args, **kwargs)
|
||||||
|
|
||||||
# Find out if the version data has changed
|
# Find out if the version data has changed
|
||||||
for attr in ['title', 'text', 'reason']:
|
for attr in ['title', 'text', 'reason']:
|
||||||
if not self.versions.exists():
|
if not self.versions.exists():
|
||||||
@ -109,7 +129,7 @@ class Motion(SlideMixin, models.Model):
|
|||||||
elif new_data and not need_new_version:
|
elif new_data and not need_new_version:
|
||||||
version = self.last_version
|
version = self.last_version
|
||||||
else:
|
else:
|
||||||
# We do not need to save the motion version
|
# We do not need to save the motion version.
|
||||||
return
|
return
|
||||||
|
|
||||||
# Save title, text and reason in the version object
|
# Save title, text and reason in the version object
|
||||||
@ -137,6 +157,10 @@ class Motion(SlideMixin, models.Model):
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def get_absolute_url(self, link='detail'):
|
def get_absolute_url(self, link='detail'):
|
||||||
|
"""Return an URL for this version.
|
||||||
|
|
||||||
|
The keywordargument 'link' can be 'detail', 'view', 'edit' or 'delete'.
|
||||||
|
"""
|
||||||
if link == 'view' or link == 'detail':
|
if link == 'view' or link == 'detail':
|
||||||
return reverse('motion_detail', args=[str(self.id)])
|
return reverse('motion_detail', args=[str(self.id)])
|
||||||
if link == 'edit':
|
if link == 'edit':
|
||||||
@ -145,8 +169,9 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return reverse('motion_delete', args=[str(self.id)])
|
return reverse('motion_delete', args=[str(self.id)])
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
"""
|
"""Get the title of the motion.
|
||||||
Get the title of the motion. The titel is taken from motion.version
|
|
||||||
|
The titel is taken from motion.version.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._title
|
return self._title
|
||||||
@ -154,16 +179,23 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return self.version.title
|
return self.version.title
|
||||||
|
|
||||||
def set_title(self, title):
|
def set_title(self, title):
|
||||||
"""
|
"""Set the titel of the motion.
|
||||||
Set the titel of the motion. The titel will me saved in motion.save()
|
|
||||||
|
The titel will me saved into the version object, wenn motion.save() is
|
||||||
|
called.
|
||||||
"""
|
"""
|
||||||
self._title = title
|
self._title = title
|
||||||
|
|
||||||
title = property(get_title, set_title)
|
title = property(get_title, set_title)
|
||||||
|
"""The title of the motion.
|
||||||
|
|
||||||
|
Is saved in a MotionVersion object.
|
||||||
|
"""
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self):
|
||||||
"""
|
"""Get the text of the motion.
|
||||||
Get the text of the motion. Simular to get_title()
|
|
||||||
|
Simular to get_title().
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._text
|
return self._text
|
||||||
@ -171,16 +203,22 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return self.version.text
|
return self.version.text
|
||||||
|
|
||||||
def set_text(self, text):
|
def set_text(self, text):
|
||||||
"""
|
""" Set the text of the motion.
|
||||||
Set the text of the motion. Simular to set_title()
|
|
||||||
|
Simular to set_title().
|
||||||
"""
|
"""
|
||||||
self._text = text
|
self._text = text
|
||||||
|
|
||||||
text = property(get_text, set_text)
|
text = property(get_text, set_text)
|
||||||
|
"""The text of a motin.
|
||||||
|
|
||||||
|
Is saved in a MotionVersion object.
|
||||||
|
"""
|
||||||
|
|
||||||
def get_reason(self):
|
def get_reason(self):
|
||||||
"""
|
"""Get the reason of the motion.
|
||||||
Get the reason of the motion. Simular to get_title()
|
|
||||||
|
Simular to get_title().
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._reason
|
return self._reason
|
||||||
@ -188,20 +226,26 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return self.version.reason
|
return self.version.reason
|
||||||
|
|
||||||
def set_reason(self, reason):
|
def set_reason(self, reason):
|
||||||
"""
|
"""Set the reason of the motion.
|
||||||
Set the reason of the motion. Simular to set_title()
|
|
||||||
|
Simular to set_title().
|
||||||
"""
|
"""
|
||||||
self._reason = reason
|
self._reason = reason
|
||||||
|
|
||||||
reason = property(get_reason, set_reason)
|
reason = property(get_reason, set_reason)
|
||||||
|
"""The reason for the motion.
|
||||||
|
|
||||||
|
Is saved in a MotionVersion object.
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def new_version(self):
|
def new_version(self):
|
||||||
"""
|
"""Return a Version object, not saved in the database.
|
||||||
|
|
||||||
On the first call, it creates a new version. On any later call, it
|
On the first call, it creates a new version. On any later call, it
|
||||||
use the existing new version.
|
use the existing new version.
|
||||||
|
|
||||||
The new_version object will be deleted when it is saved into the db
|
The new_version object will be deleted when it is saved into the db.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._new_version
|
return self._new_version
|
||||||
@ -210,9 +254,9 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return self._new_version
|
return self._new_version
|
||||||
|
|
||||||
def get_version(self):
|
def get_version(self):
|
||||||
"""
|
"""Get the 'active' version object.
|
||||||
Get the "active" version object. This version will be used to get the
|
|
||||||
data for this motion.
|
This version will be used to get the data for this motion.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self._version
|
return self._version
|
||||||
@ -220,12 +264,12 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return self.last_version
|
return self.last_version
|
||||||
|
|
||||||
def set_version(self, version):
|
def set_version(self, version):
|
||||||
"""
|
"""Set the 'active' version object.
|
||||||
Set the "active" version object.
|
|
||||||
|
|
||||||
If version is None, the last_version will be used.
|
The keyargument 'version' can be a MotionVersion object or the
|
||||||
If version is a version object, this object will be used.
|
version_number of a VersionObject or None.
|
||||||
If version is Int, the N version of this motion will be used.
|
|
||||||
|
If the argument is None, the newest version will be used.
|
||||||
"""
|
"""
|
||||||
if version is None:
|
if version is None:
|
||||||
try:
|
try:
|
||||||
@ -242,12 +286,11 @@ class Motion(SlideMixin, models.Model):
|
|||||||
self._version = version
|
self._version = version
|
||||||
|
|
||||||
version = property(get_version, set_version)
|
version = property(get_version, set_version)
|
||||||
|
"""The active version of this motion."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_version(self):
|
def last_version(self):
|
||||||
"""
|
"""Return the newest version of the motion."""
|
||||||
Get the newest version of the motion
|
|
||||||
"""
|
|
||||||
# TODO: Fix the case, that the motion has no Version
|
# TODO: Fix the case, that the motion has no Version
|
||||||
try:
|
try:
|
||||||
return self.versions.order_by('-version_number')[0]
|
return self.versions.order_by('-version_number')[0]
|
||||||
@ -255,15 +298,15 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return self.new_version
|
return self.new_version
|
||||||
|
|
||||||
def is_submitter(self, person):
|
def is_submitter(self, person):
|
||||||
|
"""Return True, if person is a submitter of this motion. Else: False."""
|
||||||
self.submitter.filter(person=person).exists()
|
self.submitter.filter(person=person).exists()
|
||||||
|
|
||||||
def is_supporter(self, person):
|
def is_supporter(self, person):
|
||||||
|
"""Return True, if person is a supporter of this motion. Else: False."""
|
||||||
return self.supporter.filter(person=person).exists()
|
return self.supporter.filter(person=person).exists()
|
||||||
|
|
||||||
def support(self, person):
|
def support(self, person):
|
||||||
"""
|
"""Add 'person' as a supporter of this motion."""
|
||||||
Add a Supporter to the list of supporters of the motion.
|
|
||||||
"""
|
|
||||||
if self.state.support:
|
if self.state.support:
|
||||||
if not self.is_supporter(person):
|
if not self.is_supporter(person):
|
||||||
MotionSupporter(motion=self, person=person).save()
|
MotionSupporter(motion=self, person=person).save()
|
||||||
@ -271,17 +314,16 @@ class Motion(SlideMixin, models.Model):
|
|||||||
raise WorkflowError("You can not support a motion in state %s" % self.state.name)
|
raise WorkflowError("You can not support a motion in state %s" % self.state.name)
|
||||||
|
|
||||||
def unsupport(self, person):
|
def unsupport(self, person):
|
||||||
"""
|
"""Remove 'person' as supporter from this motion."""
|
||||||
Remove a supporter from the list of supporters of the motion
|
|
||||||
"""
|
|
||||||
if self.state.support:
|
if self.state.support:
|
||||||
self.supporter.filter(person=person).delete()
|
self.supporter.filter(person=person).delete()
|
||||||
else:
|
else:
|
||||||
raise WorkflowError("You can not unsupport a motion in state %s" % self.state.name)
|
raise WorkflowError("You can not unsupport a motion in state %s" % self.state.name)
|
||||||
|
|
||||||
def create_poll(self):
|
def create_poll(self):
|
||||||
"""
|
"""Create a new poll for this motion.
|
||||||
Create a new poll for this motion
|
|
||||||
|
Return the new poll object.
|
||||||
"""
|
"""
|
||||||
if self.state.create_poll:
|
if self.state.create_poll:
|
||||||
# TODO: auto increment the poll_number in the Database
|
# TODO: auto increment the poll_number in the Database
|
||||||
@ -293,8 +335,9 @@ class Motion(SlideMixin, models.Model):
|
|||||||
raise WorkflowError("You can not create a poll in state %s" % self.state.name)
|
raise WorkflowError("You can not create a poll in state %s" % self.state.name)
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
"""
|
"""Return the state of the motion.
|
||||||
Get the state of this motion. Return a State object.
|
|
||||||
|
State is a State object. See openslides.motion.workflow for more informations.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return get_state(self.state_id)
|
return get_state(self.state_id)
|
||||||
@ -302,10 +345,10 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return DUMMY_STATE
|
return DUMMY_STATE
|
||||||
|
|
||||||
def set_state(self, next_state):
|
def set_state(self, next_state):
|
||||||
"""
|
"""Set the state of this motion.
|
||||||
Set the state of this motion.
|
|
||||||
|
|
||||||
next_state has to be a valid state id or State object.
|
The keyargument 'next_state' has to be a State object or an id of a
|
||||||
|
State object.
|
||||||
"""
|
"""
|
||||||
if not isinstance(next_state, State):
|
if not isinstance(next_state, State):
|
||||||
next_state = get_state(next_state)
|
next_state = get_state(next_state)
|
||||||
@ -315,17 +358,14 @@ class Motion(SlideMixin, models.Model):
|
|||||||
raise WorkflowError('%s is not a valid next_state' % next_state)
|
raise WorkflowError('%s is not a valid next_state' % next_state)
|
||||||
|
|
||||||
state = property(get_state, set_state)
|
state = property(get_state, set_state)
|
||||||
|
"""The state of the motion as Ste object."""
|
||||||
|
|
||||||
def reset_state(self):
|
def reset_state(self):
|
||||||
"""
|
"""Set the state to the default state."""
|
||||||
Set the state to the default state.
|
|
||||||
"""
|
|
||||||
self.state_id = get_state('default').id
|
self.state_id = get_state('default').id
|
||||||
|
|
||||||
def slide(self):
|
def slide(self):
|
||||||
"""
|
"""Return the slide dict."""
|
||||||
return the slide dict
|
|
||||||
"""
|
|
||||||
data = super(Motion, self).slide()
|
data = super(Motion, self).slide()
|
||||||
data['motion'] = self
|
data['motion'] = self
|
||||||
data['title'] = self.title
|
data['title'] = self.title
|
||||||
@ -333,6 +373,7 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def get_agenda_title(self):
|
def get_agenda_title(self):
|
||||||
|
"""Return a title for the Agenda."""
|
||||||
return self.last_version.title
|
return self.last_version.title
|
||||||
|
|
||||||
## def get_agenda_title_supplement(self):
|
## def get_agenda_title_supplement(self):
|
||||||
@ -340,8 +381,7 @@ class Motion(SlideMixin, models.Model):
|
|||||||
## return '(%s %s)' % (ugettext('motion'), number)
|
## return '(%s %s)' % (ugettext('motion'), number)
|
||||||
|
|
||||||
def get_allowed_actions(self, person):
|
def get_allowed_actions(self, person):
|
||||||
"""
|
"""Return a dictonary with all allowed actions for a specific person.
|
||||||
Gets a dictonary with all allowed actions for a specific person.
|
|
||||||
|
|
||||||
The dictonary contains the following actions.
|
The dictonary contains the following actions.
|
||||||
|
|
||||||
@ -374,11 +414,16 @@ class Motion(SlideMixin, models.Model):
|
|||||||
return actions
|
return actions
|
||||||
|
|
||||||
def write_log(self, message, person=None):
|
def write_log(self, message, person=None):
|
||||||
|
"""Write a log message.
|
||||||
|
|
||||||
|
Message should be in english and translatable.
|
||||||
|
|
||||||
|
e.G: motion.write_log(ugettext_noob('Message Text'))
|
||||||
|
"""
|
||||||
MotionLog.objects.create(motion=self, message=message, person=person)
|
MotionLog.objects.create(motion=self, message=message, person=person)
|
||||||
|
|
||||||
def activate_version(self, version):
|
def activate_version(self, version):
|
||||||
"""
|
"""Set the active state of a version to True.
|
||||||
Activate a version of this motion.
|
|
||||||
|
|
||||||
'version' can be a version object, or the version_number of a version.
|
'version' can be a version object, or the version_number of a version.
|
||||||
"""
|
"""
|
||||||
@ -391,8 +436,7 @@ class Motion(SlideMixin, models.Model):
|
|||||||
version.save()
|
version.save()
|
||||||
|
|
||||||
def reject_version(self, version):
|
def reject_version(self, version):
|
||||||
"""
|
"""Reject a version of this motion.
|
||||||
Reject a version of this motion.
|
|
||||||
|
|
||||||
'version' can be a version object, or the version_number of a version.
|
'version' can be a version object, or the version_number of a version.
|
||||||
"""
|
"""
|
||||||
@ -407,58 +451,126 @@ class Motion(SlideMixin, models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class MotionVersion(models.Model):
|
class MotionVersion(models.Model):
|
||||||
version_number = models.PositiveIntegerField(default=1)
|
"""
|
||||||
title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title"))
|
A MotionVersion object saves some date of the motion."""
|
||||||
text = models.TextField(verbose_name=_("Text"))
|
|
||||||
reason = models.TextField(null=True, blank=True, verbose_name=ugettext_lazy("Reason"))
|
|
||||||
rejected = models.BooleanField(default=False)
|
|
||||||
creation_time = models.DateTimeField(auto_now=True)
|
|
||||||
motion = models.ForeignKey(Motion, related_name='versions')
|
motion = models.ForeignKey(Motion, related_name='versions')
|
||||||
identifier = models.CharField(max_length=255, verbose_name=ugettext_lazy("Version identifier"))
|
"""The Motion, to witch the version belongs."""
|
||||||
note = models.TextField(null=True, blank=True)
|
|
||||||
|
version_number = models.PositiveIntegerField(default=1)
|
||||||
|
"""An id for this version in realation to a motion.
|
||||||
|
|
||||||
|
Is unique for each motion.
|
||||||
|
"""
|
||||||
|
|
||||||
|
title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title"))
|
||||||
|
"""The Title of a motion."""
|
||||||
|
|
||||||
|
text = models.TextField(verbose_name=_("Text"))
|
||||||
|
"""The text of a motion."""
|
||||||
|
|
||||||
|
reason = models.TextField(null=True, blank=True, verbose_name=ugettext_lazy("Reason"))
|
||||||
|
"""The reason for a motion."""
|
||||||
|
|
||||||
|
rejected = models.BooleanField(default=False)
|
||||||
|
"""Saves, if the version is rejected."""
|
||||||
|
|
||||||
|
creation_time = models.DateTimeField(auto_now=True)
|
||||||
|
"""Time, when the version was saved."""
|
||||||
|
|
||||||
|
#identifier = models.CharField(max_length=255, verbose_name=ugettext_lazy("Version identifier"))
|
||||||
|
#note = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("motion", "version_number")
|
unique_together = ("motion", "version_number")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
"""Return a string, representing this object."""
|
||||||
counter = self.version_number or _('new')
|
counter = self.version_number or _('new')
|
||||||
return "%s Version %s" % (self.motion, counter)
|
return "%s Version %s" % (self.motion, counter)
|
||||||
|
|
||||||
def get_absolute_url(self, link='detail'):
|
def get_absolute_url(self, link='detail'):
|
||||||
|
"""Return the URL of this Version.
|
||||||
|
|
||||||
|
The keyargument link can be 'view' or 'detail'.
|
||||||
|
"""
|
||||||
if link == 'view' or link == 'detail':
|
if link == 'view' or link == 'detail':
|
||||||
return reverse('motion_version_detail', args=[str(self.motion.id),
|
return reverse('motion_version_detail', args=[str(self.motion.id),
|
||||||
str(self.version_number)])
|
str(self.version_number)])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active(self):
|
def active(self):
|
||||||
|
"""Return True, if the version is the active version of a motion. Else: False."""
|
||||||
return self.active_version.exists()
|
return self.active_version.exists()
|
||||||
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class MotionSubmitter(models.Model):
|
||||||
name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name"))
|
"""Save the submitter of a Motion."""
|
||||||
prefix = models.CharField(max_length=32, verbose_name=ugettext_lazy("Category prefix"))
|
|
||||||
|
motion = models.ForeignKey('Motion', related_name="submitter")
|
||||||
|
"""The motion to witch the object belongs."""
|
||||||
|
|
||||||
|
person = PersonField()
|
||||||
|
"""The person, who is the submitter."""
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
"""Return the name of the submitter as string."""
|
||||||
|
return unicode(self.person)
|
||||||
|
|
||||||
|
|
||||||
class Comment(models.Model):
|
class MotionSupporter(models.Model):
|
||||||
motion_version = models.ForeignKey(MotionVersion)
|
"""Save the submitter of a Motion."""
|
||||||
text = models.TextField()
|
|
||||||
author = PersonField()
|
motion = models.ForeignKey('Motion', related_name="supporter")
|
||||||
creation_time = models.DateTimeField(auto_now=True)
|
"""The motion to witch the object belongs."""
|
||||||
|
|
||||||
|
person = PersonField()
|
||||||
|
"""The person, who is the supporter."""
|
||||||
|
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
"""Return the name of the supporter as string."""
|
||||||
|
return unicode(self.person)
|
||||||
|
|
||||||
|
|
||||||
|
## class Category(models.Model):
|
||||||
|
## name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name"))
|
||||||
|
## prefix = models.CharField(max_length=32, verbose_name=ugettext_lazy("Category prefix"))
|
||||||
|
|
||||||
|
## def __unicode__(self):
|
||||||
|
## return self.name
|
||||||
|
|
||||||
|
|
||||||
|
## class Comment(models.Model):
|
||||||
|
## motion_version = models.ForeignKey(MotionVersion)
|
||||||
|
## text = models.TextField()
|
||||||
|
## author = PersonField()
|
||||||
|
## creation_time = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
|
||||||
class MotionLog(models.Model):
|
class MotionLog(models.Model):
|
||||||
|
"""Save a logmessage for a motion."""
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion, related_name='log_messages')
|
motion = models.ForeignKey(Motion, related_name='log_messages')
|
||||||
|
"""The motion to witch the object belongs."""
|
||||||
|
|
||||||
message = models.CharField(max_length=255)
|
message = models.CharField(max_length=255)
|
||||||
|
"""The log message.
|
||||||
|
|
||||||
|
Should be in english.
|
||||||
|
"""
|
||||||
|
|
||||||
person = PersonField(null=True)
|
person = PersonField(null=True)
|
||||||
|
"""A person object, who created the log message. Optional."""
|
||||||
|
|
||||||
time = models.DateTimeField(auto_now=True)
|
time = models.DateTimeField(auto_now=True)
|
||||||
|
"""The Time, when the loged action was performed."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-time']
|
ordering = ['-time']
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
"""Return a string, representing the log message."""
|
||||||
# TODO: write time in the local time format.
|
# TODO: write time in the local time format.
|
||||||
if self.person is None:
|
if self.person is None:
|
||||||
return "%s %s" % (self.time, _(self.message))
|
return "%s %s" % (self.time, _(self.message))
|
||||||
@ -467,33 +579,63 @@ class MotionLog(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class MotionError(Exception):
|
class MotionError(Exception):
|
||||||
|
"""Exception raised when errors in the motion accure."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MotionVote(BaseVote):
|
class MotionVote(BaseVote):
|
||||||
|
"""Saves the votes for a MotionPoll.
|
||||||
|
|
||||||
|
There should allways be three MotionVote objects for each poll,
|
||||||
|
one for 'yes', 'no', and 'abstain'."""
|
||||||
|
|
||||||
option = models.ForeignKey('MotionOption')
|
option = models.ForeignKey('MotionOption')
|
||||||
|
"""The option object, to witch the vote belongs."""
|
||||||
|
|
||||||
|
|
||||||
class MotionOption(BaseOption):
|
class MotionOption(BaseOption):
|
||||||
|
"""Links between the MotionPollClass and the MotionVoteClass.
|
||||||
|
|
||||||
|
There should be one MotionOption object for each poll."""
|
||||||
|
|
||||||
poll = models.ForeignKey('MotionPoll')
|
poll = models.ForeignKey('MotionPoll')
|
||||||
|
"""The poll object, to witch the object belongs."""
|
||||||
|
|
||||||
vote_class = MotionVote
|
vote_class = MotionVote
|
||||||
|
"""The VoteClass, to witch this Class links."""
|
||||||
|
|
||||||
|
|
||||||
class MotionPoll(CountInvalid, CountVotesCast, BasePoll):
|
class MotionPoll(CountInvalid, CountVotesCast, BasePoll):
|
||||||
option_class = MotionOption
|
"""The Class to saves the poll results for a motion poll."""
|
||||||
vote_values = [
|
|
||||||
ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
|
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion, related_name='polls')
|
motion = models.ForeignKey(Motion, related_name='polls')
|
||||||
|
"""The motion to witch the object belongs."""
|
||||||
|
|
||||||
|
option_class = MotionOption
|
||||||
|
"""The option class, witch links between this object the the votes."""
|
||||||
|
|
||||||
|
vote_values = [
|
||||||
|
ugettext_noop('Yes'), ugettext_noop('No'), ugettext_noop('Abstain')]
|
||||||
|
"""The possible anwers for the poll. 'Yes, 'No' and 'Abstain'."""
|
||||||
|
|
||||||
poll_number = models.PositiveIntegerField(default=1)
|
poll_number = models.PositiveIntegerField(default=1)
|
||||||
|
"""An id for this poll in realation to a motion.
|
||||||
|
|
||||||
|
Is unique for each motion.
|
||||||
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("motion", "poll_number")
|
unique_together = ("motion", "poll_number")
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
"""Return a string, representing the poll."""
|
||||||
return _('Ballot %d') % self.poll_number
|
return _('Ballot %d') % self.poll_number
|
||||||
|
|
||||||
def get_absolute_url(self, link='edit'):
|
def get_absolute_url(self, link='edit'):
|
||||||
|
"""Return an URL for the poll.
|
||||||
|
|
||||||
|
The keyargument 'link' can be 'edit' or 'delete'.
|
||||||
|
"""
|
||||||
if link == 'edit':
|
if link == 'edit':
|
||||||
return reverse('motion_poll_edit', args=[str(self.motion.pk),
|
return reverse('motion_poll_edit', args=[str(self.motion.pk),
|
||||||
str(self.poll_number)])
|
str(self.poll_number)])
|
||||||
@ -501,16 +643,13 @@ class MotionPoll(CountInvalid, CountVotesCast, BasePoll):
|
|||||||
return reverse('motion_poll_delete', args=[str(self.motion.pk),
|
return reverse('motion_poll_delete', args=[str(self.motion.pk),
|
||||||
str(self.poll_number)])
|
str(self.poll_number)])
|
||||||
|
|
||||||
def get_motion(self):
|
|
||||||
return self.motion
|
|
||||||
|
|
||||||
def set_options(self):
|
def set_options(self):
|
||||||
|
"""Create the option class for this poll."""
|
||||||
#TODO: maybe it is possible with .create() to call this without poll=self
|
#TODO: maybe it is possible with .create() to call this without poll=self
|
||||||
|
# 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):
|
def append_pollform_fields(self, fields):
|
||||||
|
"""Apend the fields for invalid and votecast to the ModelForm."""
|
||||||
CountInvalid.append_pollform_fields(self, fields)
|
CountInvalid.append_pollform_fields(self, fields)
|
||||||
CountVotesCast.append_pollform_fields(self, fields)
|
CountVotesCast.append_pollform_fields(self, fields)
|
||||||
|
|
||||||
def get_ballot(self):
|
|
||||||
return self.motion.motionpoll_set.filter(id__lte=self.id).count()
|
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.motion.pdf
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Functions to generate the PDFs for the motion app.
|
||||||
|
"""
|
||||||
|
|
||||||
from reportlab.lib import colors
|
from reportlab.lib import colors
|
||||||
from reportlab.lib.units import cm
|
from reportlab.lib.units import cm
|
||||||
from reportlab.platypus import (
|
from reportlab.platypus import (
|
||||||
@ -11,6 +20,8 @@ from .models import Motion
|
|||||||
|
|
||||||
|
|
||||||
def motions_to_pdf(pdf):
|
def motions_to_pdf(pdf):
|
||||||
|
"""Create a PDF with all motions."""
|
||||||
|
|
||||||
motions = Motion.objects.all()
|
motions = Motion.objects.all()
|
||||||
all_motion_cover(pdf, motions)
|
all_motion_cover(pdf, motions)
|
||||||
for motion in motions:
|
for motion in motions:
|
||||||
@ -19,6 +30,8 @@ def motions_to_pdf(pdf):
|
|||||||
|
|
||||||
|
|
||||||
def motion_to_pdf(pdf, motion):
|
def motion_to_pdf(pdf, motion):
|
||||||
|
"""Create a PDF for one motion."""
|
||||||
|
|
||||||
pdf.append(Paragraph(_("Motion: %s") % motion.title, stylesheet['Heading1']))
|
pdf.append(Paragraph(_("Motion: %s") % motion.title, stylesheet['Heading1']))
|
||||||
|
|
||||||
motion_data = []
|
motion_data = []
|
||||||
@ -141,6 +154,7 @@ def motion_to_pdf(pdf, motion):
|
|||||||
|
|
||||||
|
|
||||||
def all_motion_cover(pdf, motions):
|
def all_motion_cover(pdf, motions):
|
||||||
|
"""Create a coverpage for all motions."""
|
||||||
pdf.append(Paragraph(config["motion_pdf_title"], stylesheet['Heading1']))
|
pdf.append(Paragraph(config["motion_pdf_title"], stylesheet['Heading1']))
|
||||||
|
|
||||||
preamble = config["motion_pdf_preamble"]
|
preamble = config["motion_pdf_preamble"]
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Signals for the motion app.
|
Signals for the motion app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ from openslides.config.signals import default_config_value
|
|||||||
|
|
||||||
@receiver(default_config_value, dispatch_uid="motion_default_config")
|
@receiver(default_config_value, dispatch_uid="motion_default_config")
|
||||||
def default_config(sender, key, **kwargs):
|
def default_config(sender, key, **kwargs):
|
||||||
|
"""Return the default config values for the motion app."""
|
||||||
return {
|
return {
|
||||||
'motion_min_supporters': 0,
|
'motion_min_supporters': 0,
|
||||||
'motion_preamble': _('The assembly may decide,'),
|
'motion_preamble': _('The assembly may decide,'),
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.motion.slides
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Defines the Slides for the motion app.
|
||||||
|
|
||||||
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
from openslides.projector.api import register_slidemodel
|
from openslides.projector.api import register_slidemodel
|
||||||
from .models import Motion
|
from .models import Motion
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.motion.urls
|
openslides.motion.urls
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
URL list for the motion app.
|
Defines the URL patterns for the motion app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by OpenSlides team, see AUTHORS.
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
|
|
||||||
Views for the motion app.
|
Views for the motion app.
|
||||||
|
|
||||||
:copyright: 2011, 2012 by the OpenSlides team, see AUTHORS.
|
Will automaticly imported from openslides.motion.urls.py
|
||||||
|
|
||||||
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
from reportlab.platypus import Paragraph
|
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@ -39,9 +40,7 @@ from .pdf import motions_to_pdf, motion_to_pdf
|
|||||||
|
|
||||||
|
|
||||||
class MotionListView(ListView):
|
class MotionListView(ListView):
|
||||||
"""
|
"""View, to list all motions."""
|
||||||
List all motion.
|
|
||||||
"""
|
|
||||||
permission_required = 'motion.can_see_motion'
|
permission_required = 'motion.can_see_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
|
|
||||||
@ -49,7 +48,11 @@ motion_list = MotionListView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class GetVersionMixin(object):
|
class GetVersionMixin(object):
|
||||||
|
"""Mixin to set a specific version to a motion."""
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
"""Return a Motion object. The id is taken from the url and the version
|
||||||
|
is set to the version with the 'version_number' from the URL."""
|
||||||
object = super(GetVersionMixin, self).get_object()
|
object = super(GetVersionMixin, self).get_object()
|
||||||
version_number = self.kwargs.get('version_number', None)
|
version_number = self.kwargs.get('version_number', None)
|
||||||
if version_number is not None:
|
if version_number is not None:
|
||||||
@ -61,14 +64,15 @@ class GetVersionMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
class MotionDetailView(GetVersionMixin, DetailView):
|
class MotionDetailView(GetVersionMixin, DetailView):
|
||||||
"""
|
"""Show one motion."""
|
||||||
Show the details of one motion.
|
|
||||||
"""
|
|
||||||
permission_required = 'motion.can_see_motion'
|
permission_required = 'motion.can_see_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
template_name = 'motion/motion_detail.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
"""Return the template context.
|
||||||
|
|
||||||
|
Append the allowed actions for the motion to the context.
|
||||||
|
"""
|
||||||
context = super(MotionDetailView, self).get_context_data(**kwargs)
|
context = super(MotionDetailView, self).get_context_data(**kwargs)
|
||||||
context['allowed_actions'] = self.object.get_allowed_actions(self.request.user)
|
context['allowed_actions'] = self.object.get_allowed_actions(self.request.user)
|
||||||
return context
|
return context
|
||||||
@ -77,10 +81,12 @@ motion_detail = MotionDetailView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class MotionMixin(object):
|
class MotionMixin(object):
|
||||||
"""
|
"""Mixin for MotionViewsClasses, to save the version data."""
|
||||||
Mixin to add save the version-data to the motion-object
|
|
||||||
"""
|
|
||||||
def manipulate_object(self, form):
|
def manipulate_object(self, form):
|
||||||
|
"""Save the version data into the motion object before it is saved in
|
||||||
|
the Database."""
|
||||||
|
|
||||||
super(MotionMixin, self).manipulate_object(form)
|
super(MotionMixin, self).manipulate_object(form)
|
||||||
for attr in ['title', 'text', 'reason']:
|
for attr in ['title', 'text', 'reason']:
|
||||||
setattr(self.object, attr, form.cleaned_data[attr])
|
setattr(self.object, attr, form.cleaned_data[attr])
|
||||||
@ -92,6 +98,7 @@ class MotionMixin(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def post_save(self, form):
|
def post_save(self, form):
|
||||||
|
"""Save the submitter an the supporter so the motion."""
|
||||||
super(MotionMixin, self).post_save(form)
|
super(MotionMixin, self).post_save(form)
|
||||||
# TODO: only delete and save neccessary submitters and supporter
|
# TODO: only delete and save neccessary submitters and supporter
|
||||||
if 'submitter' in form.cleaned_data:
|
if 'submitter' in form.cleaned_data:
|
||||||
@ -106,6 +113,13 @@ class MotionMixin(object):
|
|||||||
for person in form.cleaned_data['supporter']])
|
for person in form.cleaned_data['supporter']])
|
||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
|
"""Return the FormClass to Create or Update the Motion.
|
||||||
|
|
||||||
|
forms.BaseMotionForm is the base for the Class, and some FormMixins
|
||||||
|
will be mixed in dependence of some config values. See motion.forms
|
||||||
|
for more information on the mixins.
|
||||||
|
"""
|
||||||
|
|
||||||
form_classes = [BaseMotionForm]
|
form_classes = [BaseMotionForm]
|
||||||
if self.request.user.has_perm('motion.can_manage_motion'):
|
if self.request.user.has_perm('motion.can_manage_motion'):
|
||||||
form_classes.append(MotionSubmitterMixin)
|
form_classes.append(MotionSubmitterMixin)
|
||||||
@ -117,13 +131,12 @@ class MotionMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
class MotionCreateView(MotionMixin, CreateView):
|
class MotionCreateView(MotionMixin, CreateView):
|
||||||
"""
|
"""View to create a motion."""
|
||||||
Create a motion.
|
|
||||||
"""
|
|
||||||
permission_required = 'motion.can_create_motion'
|
permission_required = 'motion.can_create_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
"""Write a log message, if the form is valid."""
|
||||||
value = super(MotionCreateView, self).form_valid(form)
|
value = super(MotionCreateView, self).form_valid(form)
|
||||||
self.object.write_log(ugettext_noop('Motion created'), self.request.user)
|
self.object.write_log(ugettext_noop('Motion created'), self.request.user)
|
||||||
return value
|
return value
|
||||||
@ -132,15 +145,15 @@ motion_create = MotionCreateView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class MotionUpdateView(MotionMixin, UpdateView):
|
class MotionUpdateView(MotionMixin, UpdateView):
|
||||||
"""
|
"""View to update a motion."""
|
||||||
Update a motion.
|
|
||||||
"""
|
|
||||||
model = Motion
|
model = Motion
|
||||||
|
|
||||||
def has_permission(self, request, *args, **kwargs):
|
def has_permission(self, request, *args, **kwargs):
|
||||||
|
"""Check, if the request.user has the permission to edit the motion."""
|
||||||
return self.get_object().get_allowed_actions(request.user)['edit']
|
return self.get_object().get_allowed_actions(request.user)['edit']
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
"""Write a log message, if the form is valid."""
|
||||||
value = super(MotionUpdateView, self).form_valid(form)
|
value = super(MotionUpdateView, self).form_valid(form)
|
||||||
self.object.write_log(ugettext_noop('Motion updated'), self.request.user)
|
self.object.write_log(ugettext_noop('Motion updated'), self.request.user)
|
||||||
return value
|
return value
|
||||||
@ -149,34 +162,39 @@ motion_edit = MotionUpdateView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class MotionDeleteView(DeleteView):
|
class MotionDeleteView(DeleteView):
|
||||||
"""
|
"""View to delete a motion."""
|
||||||
Delete one Motion.
|
|
||||||
"""
|
|
||||||
model = Motion
|
model = Motion
|
||||||
success_url_name = 'motion_list'
|
success_url_name = 'motion_list'
|
||||||
|
|
||||||
def has_permission(self, request, *args, **kwargs):
|
def has_permission(self, request, *args, **kwargs):
|
||||||
|
"""Check if the request.user has the permission to delete the motion."""
|
||||||
return self.get_object().get_allowed_actions(request.user)['delete']
|
return self.get_object().get_allowed_actions(request.user)['delete']
|
||||||
|
|
||||||
motion_delete = MotionDeleteView.as_view()
|
motion_delete = MotionDeleteView.as_view()
|
||||||
|
|
||||||
|
|
||||||
class VersionPermitView(GetVersionMixin, SingleObjectMixin, QuestionMixin, RedirectView):
|
class VersionPermitView(GetVersionMixin, SingleObjectMixin, QuestionMixin, RedirectView):
|
||||||
|
"""View to permit a version of a motion."""
|
||||||
|
|
||||||
model = Motion
|
model = Motion
|
||||||
question_url_name = 'motion_version_detail'
|
question_url_name = 'motion_version_detail'
|
||||||
success_url_name = 'motion_version_detail'
|
success_url_name = 'motion_version_detail'
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
|
"""Set self.object to a motion."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super(VersionPermitView, self).get(*args, **kwargs)
|
return super(VersionPermitView, self).get(*args, **kwargs)
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
|
"""Return a list with arguments to create the success- and question_url."""
|
||||||
return [self.object.pk, self.object.version.version_number]
|
return [self.object.pk, self.object.version.version_number]
|
||||||
|
|
||||||
def get_question(self):
|
def get_question(self):
|
||||||
|
"""Return a string, shown to the user as question to permit the version."""
|
||||||
return _('Are you sure you want permit Version %s?') % self.object.version.version_number
|
return _('Are you sure you want permit Version %s?') % self.object.version.version_number
|
||||||
|
|
||||||
def case_yes(self):
|
def case_yes(self):
|
||||||
|
"""Activate the version, if the user chooses 'yes'."""
|
||||||
self.object.activate_version(self.object.version)
|
self.object.activate_version(self.object.version)
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
@ -184,21 +202,25 @@ version_permit = VersionPermitView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, RedirectView):
|
class VersionRejectView(GetVersionMixin, SingleObjectMixin, QuestionMixin, RedirectView):
|
||||||
|
"""View to reject a version."""
|
||||||
model = Motion
|
model = Motion
|
||||||
question_url_name = 'motion_version_detail'
|
question_url_name = 'motion_version_detail'
|
||||||
success_url_name = 'motion_version_detail'
|
success_url_name = 'motion_version_detail'
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
|
"""Set self.object to a motion."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super(VersionRejectView, self).get(*args, **kwargs)
|
return super(VersionRejectView, self).get(*args, **kwargs)
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
|
"""Return a list with arguments to create the success- and question_url."""
|
||||||
return [self.object.pk, self.object.version.version_number]
|
return [self.object.pk, self.object.version.version_number]
|
||||||
|
|
||||||
def get_question(self):
|
def get_question(self):
|
||||||
return _('Are you sure you want reject Version %s?') % self.object.version.version_number
|
return _('Are you sure you want reject Version %s?') % self.object.version.version_number
|
||||||
|
|
||||||
def case_yes(self):
|
def case_yes(self):
|
||||||
|
"""Reject the version, if the user chooses 'yes'."""
|
||||||
self.object.reject_version(self.object.version)
|
self.object.reject_version(self.object.version)
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
@ -206,23 +228,25 @@ version_reject = VersionRejectView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
||||||
|
"""View to support or unsupport a motion.
|
||||||
|
|
||||||
|
If self.support is True, the view will append a request.user to the supporter list.
|
||||||
|
|
||||||
|
If self.support is False, the view will remove a request.user from the supporter list.
|
||||||
"""
|
"""
|
||||||
Classed based view to support or unsupport a motion. Use
|
|
||||||
support=True or support=False in urls.py
|
|
||||||
"""
|
|
||||||
permission_required = 'motion.can_support_motion'
|
permission_required = 'motion.can_support_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
support = True
|
support = True
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""Set self.object to a motion."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super(SupportView, self).get(request, *args, **kwargs)
|
return super(SupportView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def check_permission(self, request):
|
def check_permission(self, request):
|
||||||
"""
|
"""Return True if the user can support or unsupport the motion. Else: False."""
|
||||||
Checks whether request.user can support or unsupport the motion.
|
|
||||||
Returns True or False.
|
|
||||||
"""
|
|
||||||
allowed_actions = self.object.get_allowed_actions(request.user)
|
allowed_actions = self.object.get_allowed_actions(request.user)
|
||||||
if self.support and not allowed_actions['support']:
|
if self.support and not allowed_actions['support']:
|
||||||
messages.error(request, _('You can not support this motion.'))
|
messages.error(request, _('You can not support this motion.'))
|
||||||
@ -233,17 +257,19 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
|
||||||
if self.check_permission(request):
|
|
||||||
super(SupportView, self).pre_redirect(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def get_question(self):
|
def get_question(self):
|
||||||
|
"""Return the question string."""
|
||||||
if self.support:
|
if self.support:
|
||||||
return _('Do you really want to support this motion?')
|
return _('Do you really want to support this motion?')
|
||||||
else:
|
else:
|
||||||
return _('Do you really want to unsupport this motion?')
|
return _('Do you really want to unsupport this motion?')
|
||||||
|
|
||||||
def case_yes(self):
|
def case_yes(self):
|
||||||
|
"""Append or remove the request.user from the motion.
|
||||||
|
|
||||||
|
First the methode checks the permissions, and writes a log message after
|
||||||
|
appending or removing the user.
|
||||||
|
"""
|
||||||
if self.check_permission(self.request):
|
if self.check_permission(self.request):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
if self.support:
|
if self.support:
|
||||||
@ -254,12 +280,14 @@ class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
|
|||||||
self.object.write_log(ugettext_noop("Supporter: -%s") % user, user)
|
self.object.write_log(ugettext_noop("Supporter: -%s") % user, user)
|
||||||
|
|
||||||
def get_success_message(self):
|
def get_success_message(self):
|
||||||
|
"""Return the success message."""
|
||||||
if self.support:
|
if self.support:
|
||||||
return _("You have supported this motion successfully.")
|
return _("You have supported this motion successfully.")
|
||||||
else:
|
else:
|
||||||
return _("You have unsupported this motion successfully.")
|
return _("You have unsupported this motion successfully.")
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
|
"""Return the url, the view should redirect to."""
|
||||||
return self.object.get_absolute_url()
|
return self.object.get_absolute_url()
|
||||||
|
|
||||||
motion_support = SupportView.as_view(support=True)
|
motion_support = SupportView.as_view(support=True)
|
||||||
@ -267,48 +295,68 @@ motion_unsupport = SupportView.as_view(support=False)
|
|||||||
|
|
||||||
|
|
||||||
class PollCreateView(SingleObjectMixin, RedirectView):
|
class PollCreateView(SingleObjectMixin, RedirectView):
|
||||||
|
"""View to create a poll for a motion."""
|
||||||
permission_required = 'motion.can_manage_motion'
|
permission_required = 'motion.can_manage_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""Set self.object to a motion."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super(PollCreateView, self).get(request, *args, **kwargs)
|
return super(PollCreateView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
|
"""Create the poll for the motion."""
|
||||||
self.poll = self.object.create_poll()
|
self.poll = self.object.create_poll()
|
||||||
self.object.write_log(ugettext_noop("Poll created"), request.user)
|
self.object.write_log(ugettext_noop("Poll created"), request.user)
|
||||||
messages.success(request, _("New vote was successfully created."))
|
messages.success(request, _("New vote was successfully created."))
|
||||||
|
|
||||||
def get_redirect_url(self, **kwargs):
|
def get_redirect_url(self, **kwargs):
|
||||||
|
"""Return the URL to the EditView of the poll."""
|
||||||
return reverse('motion_poll_edit', args=[self.object.pk, self.poll.poll_number])
|
return reverse('motion_poll_edit', args=[self.object.pk, self.poll.poll_number])
|
||||||
|
|
||||||
poll_create = PollCreateView.as_view()
|
poll_create = PollCreateView.as_view()
|
||||||
|
|
||||||
|
|
||||||
class PollMixin(object):
|
class PollMixin(object):
|
||||||
|
"""Mixin for the PollUpdateView and the PollDeleteView."""
|
||||||
permission_required = 'motion.can_manage_motion'
|
permission_required = 'motion.can_manage_motion'
|
||||||
success_url_name = 'motion_detail'
|
success_url_name = 'motion_detail'
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
"""Return a MotionPoll object.
|
||||||
|
|
||||||
|
Use the motion id and the poll_number from the url kwargs to get the
|
||||||
|
object.
|
||||||
|
"""
|
||||||
return MotionPoll.objects.filter(
|
return MotionPoll.objects.filter(
|
||||||
motion=self.kwargs['pk'],
|
motion=self.kwargs['pk'],
|
||||||
poll_number=self.kwargs['poll_number']).get()
|
poll_number=self.kwargs['poll_number']).get()
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
|
"""Return the arguments to create the url to the success_url"""
|
||||||
return [self.object.motion.pk]
|
return [self.object.motion.pk]
|
||||||
|
|
||||||
|
|
||||||
class PollUpdateView(PollMixin, PollFormView):
|
class PollUpdateView(PollMixin, PollFormView):
|
||||||
|
"""View to update a MotionPoll."""
|
||||||
|
|
||||||
poll_class = MotionPoll
|
poll_class = MotionPoll
|
||||||
|
"""Poll Class to use for this view."""
|
||||||
|
|
||||||
template_name = 'motion/poll_form.html'
|
template_name = 'motion/poll_form.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
"""Return the template context.
|
||||||
|
|
||||||
|
Append the motion object to the context.
|
||||||
|
"""
|
||||||
context = super(PollUpdateView, self).get_context_data(**kwargs)
|
context = super(PollUpdateView, self).get_context_data(**kwargs)
|
||||||
context.update({
|
context.update({
|
||||||
'motion': self.poll.motion})
|
'motion': self.poll.motion})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
"""Write a log message, if the form is valid."""
|
||||||
value = super(PollUpdateView, self).form_valid(form)
|
value = super(PollUpdateView, self).form_valid(form)
|
||||||
self.object.write_log(ugettext_noop('Poll updated'), self.request.user)
|
self.object.write_log(ugettext_noop('Poll updated'), self.request.user)
|
||||||
return value
|
return value
|
||||||
@ -317,9 +365,11 @@ poll_edit = PollUpdateView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class PollDeleteView(PollMixin, DeleteView):
|
class PollDeleteView(PollMixin, DeleteView):
|
||||||
|
"""View to delete a MotionPoll."""
|
||||||
model = MotionPoll
|
model = MotionPoll
|
||||||
|
|
||||||
def case_yes(self):
|
def case_yes(self):
|
||||||
|
"""Write a log message, if the form is valid."""
|
||||||
super(PollDeleteView, self).case_yes()
|
super(PollDeleteView, self).case_yes()
|
||||||
self.object.write_log(ugettext_noop('Poll deleted'), self.request.user)
|
self.object.write_log(ugettext_noop('Poll deleted'), self.request.user)
|
||||||
|
|
||||||
@ -327,12 +377,19 @@ poll_delete = PollDeleteView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class MotionSetStateView(SingleObjectMixin, RedirectView):
|
class MotionSetStateView(SingleObjectMixin, RedirectView):
|
||||||
|
"""View to set the state of a motion.
|
||||||
|
|
||||||
|
If self.reset is False, the new state is taken from url.
|
||||||
|
|
||||||
|
If self.reset is True, the default state is taken.
|
||||||
|
"""
|
||||||
permission_required = 'motion.can_manage_motion'
|
permission_required = 'motion.can_manage_motion'
|
||||||
url_name = 'motion_detail'
|
url_name = 'motion_detail'
|
||||||
model = Motion
|
model = Motion
|
||||||
reset = False
|
reset = False
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
|
"""Save the new state and write a log message."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
try:
|
try:
|
||||||
if self.reset:
|
if self.reset:
|
||||||
@ -350,6 +407,7 @@ class MotionSetStateView(SingleObjectMixin, RedirectView):
|
|||||||
% html_strong(self.object.state)))
|
% html_strong(self.object.state)))
|
||||||
|
|
||||||
def get_url_name_args(self):
|
def get_url_name_args(self):
|
||||||
|
"""Return the arguments to generate the redirect_url."""
|
||||||
return [self.object.pk]
|
return [self.object.pk]
|
||||||
|
|
||||||
set_state = MotionSetStateView.as_view()
|
set_state = MotionSetStateView.as_view()
|
||||||
@ -357,15 +415,18 @@ reset_state = MotionSetStateView.as_view(reset=True)
|
|||||||
|
|
||||||
|
|
||||||
class CreateAgendaItemView(SingleObjectMixin, RedirectView):
|
class CreateAgendaItemView(SingleObjectMixin, RedirectView):
|
||||||
|
"""View to create and agenda item for a motion."""
|
||||||
permission_required = 'agenda.can_manage_agenda'
|
permission_required = 'agenda.can_manage_agenda'
|
||||||
url_name = 'item_overview'
|
url_name = 'item_overview'
|
||||||
model = Motion
|
model = Motion
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""Set self.object to a motion."""
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super(CreateAgendaItemView, self).get(request, *args, **kwargs)
|
return super(CreateAgendaItemView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def pre_redirect(self, request, *args, **kwargs):
|
def pre_redirect(self, request, *args, **kwargs):
|
||||||
|
"""Create the agenda item."""
|
||||||
self.item = Item.objects.create(related_sid=self.object.sid)
|
self.item = Item.objects.create(related_sid=self.object.sid)
|
||||||
self.object.write_log(ugettext_noop('Created Agenda Item'), self.request.user)
|
self.object.write_log(ugettext_noop('Created Agenda Item'), self.request.user)
|
||||||
|
|
||||||
@ -373,23 +434,32 @@ create_agenda_item = CreateAgendaItemView.as_view()
|
|||||||
|
|
||||||
|
|
||||||
class MotionPDFView(SingleObjectMixin, PDFView):
|
class MotionPDFView(SingleObjectMixin, PDFView):
|
||||||
|
"""Create the PDF for one, or all motions.
|
||||||
|
|
||||||
|
If self.print_all_motions is True, the view returns a PDF with all motions.
|
||||||
|
|
||||||
|
If self.print_all_motions is False, the view returns a PDF with only one
|
||||||
|
motion."""
|
||||||
permission_required = 'motion.can_manage_motion'
|
permission_required = 'motion.can_manage_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
top_space = 0
|
top_space = 0
|
||||||
print_all_motions = False
|
print_all_motions = False
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
"""Set self.object to a motion."""
|
||||||
if not self.print_all_motions:
|
if not self.print_all_motions:
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super(MotionPDFView, self).get(request, *args, **kwargs)
|
return super(MotionPDFView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_filename(self):
|
def get_filename(self):
|
||||||
|
"""Return the filename for the PDF."""
|
||||||
if self.print_all_motions:
|
if self.print_all_motions:
|
||||||
return _("Motions")
|
return _("Motions")
|
||||||
else:
|
else:
|
||||||
return _("Motion: %s") % unicode(self.object)
|
return _("Motion: %s") % unicode(self.object)
|
||||||
|
|
||||||
def append_to_pdf(self, pdf):
|
def append_to_pdf(self, pdf):
|
||||||
|
"""Append PDF objects."""
|
||||||
if self.print_all_motions:
|
if self.print_all_motions:
|
||||||
motions_to_pdf(pdf)
|
motions_to_pdf(pdf)
|
||||||
else:
|
else:
|
||||||
@ -400,6 +470,7 @@ motion_detail_pdf = MotionPDFView.as_view(print_all_motions=False)
|
|||||||
|
|
||||||
|
|
||||||
class Config(FormView):
|
class Config(FormView):
|
||||||
|
"""The View for the config tab."""
|
||||||
permission_required = 'config.can_manage_config'
|
permission_required = 'config.can_manage_config'
|
||||||
form_class = ConfigForm
|
form_class = ConfigForm
|
||||||
template_name = 'motion/config.html'
|
template_name = 'motion/config.html'
|
||||||
@ -431,9 +502,8 @@ class Config(FormView):
|
|||||||
|
|
||||||
|
|
||||||
def register_tab(request):
|
def register_tab(request):
|
||||||
"""
|
"""Return the motion tab."""
|
||||||
Register the projector tab.
|
# TODO: Find a bether way to set the selected var.
|
||||||
"""
|
|
||||||
selected = request.path.startswith('/motion/')
|
selected = request.path.startswith('/motion/')
|
||||||
return Tab(
|
return Tab(
|
||||||
title=_('Motions'),
|
title=_('Motions'),
|
||||||
@ -445,6 +515,10 @@ def register_tab(request):
|
|||||||
|
|
||||||
|
|
||||||
def get_widgets(request):
|
def get_widgets(request):
|
||||||
|
"""Return the motion widgets for the dashboard.
|
||||||
|
|
||||||
|
There is only one widget. It shows all motions.
|
||||||
|
"""
|
||||||
return [Widget(
|
return [Widget(
|
||||||
name='motions',
|
name='motions',
|
||||||
display_name=_('Motions'),
|
display_name=_('Motions'),
|
||||||
|
@ -1,16 +1,44 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
openslides.utils.workflow
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Defines the States for motions. All States are linked together with there
|
||||||
|
'next_state' attributes. Together there are a workflow.
|
||||||
|
|
||||||
|
:copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS.
|
||||||
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.utils.importlib import import_module
|
from django.utils.importlib import import_module
|
||||||
|
from django.utils.translation import ugettext_noop,
|
||||||
|
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
ugettext = lambda s: s
|
|
||||||
|
|
||||||
_workflow = None
|
_workflow = None
|
||||||
|
|
||||||
|
|
||||||
class State(object):
|
class State(object):
|
||||||
|
"""Define a state for a motion."""
|
||||||
def __init__(self, id, name, next_states=[], create_poll=False, support=False,
|
def __init__(self, id, name, next_states=[], create_poll=False, support=False,
|
||||||
edit_as_submitter=False, version_permission=True):
|
edit_as_submitter=False, version_permission=True):
|
||||||
|
"""Set attributes for the state.
|
||||||
|
|
||||||
|
The Arguments are:
|
||||||
|
- 'id' a unique id for the state.
|
||||||
|
- 'name' a string representing the state.
|
||||||
|
- 'next_states' a list with all states, that can be choosen from this state.
|
||||||
|
|
||||||
|
All the other arguments are boolean values. If True, the specific action for
|
||||||
|
motions in this state.
|
||||||
|
- 'create_poll': polls can be created in this state.
|
||||||
|
- 'support': persons can support the motion in this state.
|
||||||
|
- 'edit_as_submitter': the submitter can edit the motion in this state.
|
||||||
|
- 'version_permission': new versions are not permitted.
|
||||||
|
"""
|
||||||
self.id = id
|
self.id = id
|
||||||
self.name = name
|
self.name = name
|
||||||
self.next_states = next_states
|
self.next_states = next_states
|
||||||
@ -20,19 +48,34 @@ class State(object):
|
|||||||
self.version_permission = version_permission
|
self.version_permission = version_permission
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
"""Return the name of the state."""
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class WorkflowError(Exception):
|
class WorkflowError(Exception):
|
||||||
|
"""Exception raised when errors in a state accure."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def motion_workflow_choices():
|
def motion_workflow_choices():
|
||||||
|
"""Return all possible workflows.
|
||||||
|
|
||||||
|
The possible workflows can be set in the settings with the setting
|
||||||
|
'MOTION_WORKFLOW'.
|
||||||
|
"""
|
||||||
for workflow in settings.MOTION_WORKFLOW:
|
for workflow in settings.MOTION_WORKFLOW:
|
||||||
yield workflow[0], workflow[1]
|
yield workflow[0], workflow[1]
|
||||||
|
|
||||||
|
|
||||||
def get_state(state='default'):
|
def get_state(state='default'):
|
||||||
|
"""Return a state object.
|
||||||
|
|
||||||
|
The argument 'state' has to be a state_id.
|
||||||
|
|
||||||
|
If the argument 'state' is 'default', the default state is returned.
|
||||||
|
|
||||||
|
The default state is the state object choosen in the config tab.
|
||||||
|
"""
|
||||||
global _workflow
|
global _workflow
|
||||||
if _workflow is not None:
|
if _workflow is not None:
|
||||||
try:
|
try:
|
||||||
@ -68,24 +111,34 @@ def get_state(state='default'):
|
|||||||
|
|
||||||
|
|
||||||
def populate_workflow(state, workflow):
|
def populate_workflow(state, workflow):
|
||||||
|
"""Append all 'next_states' from state to the workflow.
|
||||||
|
|
||||||
|
The argument state has to be a state object.
|
||||||
|
|
||||||
|
The argument workflow has to be a dictonary.
|
||||||
|
|
||||||
|
Calls this function recrusiv with all next_states from the next_states states.
|
||||||
|
"""
|
||||||
workflow[state.id] = state
|
workflow[state.id] = state
|
||||||
for s in state.next_states:
|
for s in state.next_states:
|
||||||
if s.id not in workflow:
|
if s.id not in workflow:
|
||||||
populate_workflow(s, workflow)
|
populate_workflow(s, workflow)
|
||||||
|
|
||||||
|
|
||||||
DUMMY_STATE = State('dummy', ugettext('Unknwon state'))
|
DUMMY_STATE = State('dummy', ugettext_noop('Unknwon state'))
|
||||||
|
"""A dummy state object. Returned, if the state_id is not known."""
|
||||||
|
|
||||||
default_workflow = State('pub', ugettext('Published'), support=True,
|
default_workflow = State('pub', ugettext_noop('Published'), support=True,
|
||||||
edit_as_submitter=True, version_permission=False)
|
edit_as_submitter=True, version_permission=False)
|
||||||
|
"""Default Workflow for OpenSlides."""
|
||||||
|
|
||||||
default_workflow.next_states = [
|
default_workflow.next_states = [
|
||||||
State('per', ugettext('Permitted'), create_poll=True, edit_as_submitter=True, next_states=[
|
State('per', ugettext_noop('Permitted'), create_poll=True, edit_as_submitter=True, next_states=[
|
||||||
State('acc', ugettext('Accepted')),
|
State('acc', ugettext_noop('Accepted')),
|
||||||
State('rej', ugettext('Rejected')),
|
State('rej', ugettext_noop('Rejected')),
|
||||||
State('wit', ugettext('Withdrawed')),
|
State('wit', ugettext_noop('Withdrawed')),
|
||||||
State('adj', ugettext('Adjourned')),
|
State('adj', ugettext_noop('Adjourned')),
|
||||||
State('noc', ugettext('Not Concerned')),
|
State('noc', ugettext_noop('Not Concerned')),
|
||||||
State('com', ugettext('Commited a bill')),
|
State('com', ugettext_noop('Commited a bill')),
|
||||||
State('rev', ugettext('Needs Review'))]),
|
State('rev', ugettext_noop('Needs Review'))]),
|
||||||
State('nop', ugettext('Rejected (not authorized)'))]
|
State('nop', ugettext_noop('Rejected (not authorized)'))]
|
||||||
|
Loading…
Reference in New Issue
Block a user