Add workflows for motions

This commit is contained in:
Oskar Hahn 2013-02-01 16:33:45 +01:00
parent 3f02a28002
commit fb82a1787b
7 changed files with 116 additions and 4 deletions

View File

@ -26,9 +26,12 @@ LOGIN_REDIRECT_URL = '/'
SESSION_COOKIE_NAME = 'OpenSlidesSessionID' SESSION_COOKIE_NAME = 'OpenSlidesSessionID'
ugettext = lambda s: s ugettext = lambda s: s
MOTION_WORKFLOW = (
('default', ugettext('default'), 'openslides.motion.workflow.default_workflow'),
)
LANGUAGES = ( LANGUAGES = (
('de', ugettext('German')), ('de', ugettext('German')),
('en', ugettext('English')), ('en', ugettext('English')),

View File

@ -16,6 +16,7 @@ from django.utils.translation import ugettext as _
from openslides.utils.forms import CssClassMixin from openslides.utils.forms import CssClassMixin
from openslides.utils.person import PersonFormField, MultiplePersonFormField from openslides.utils.person import PersonFormField, MultiplePersonFormField
from .models import Motion from .models import Motion
from .workflow import motion_workflow_choices
class BaseMotionForm(forms.ModelForm, CssClassMixin): class BaseMotionForm(forms.ModelForm, CssClassMixin):
@ -117,3 +118,9 @@ class ConfigForm(forms.Form, CssClassMixin):
('NEVER_CREATE_NEW_VERSION', _('create never a new version')), ('NEVER_CREATE_NEW_VERSION', _('create never a new version')),
('ASK_USER', _('Let the user choose if he wants to create a new version'))) ('ASK_USER', _('Let the user choose if he wants to create a new version')))
) )
motion_workflow = forms.ChoiceField(
widget=forms.Select(),
label=_("Workflow for the motions"),
required=True,
choices=motion_workflow_choices())

View File

@ -30,6 +30,8 @@ from openslides.projector.api import register_slidemodel
from openslides.projector.models import SlideMixin from openslides.projector.models import SlideMixin
from openslides.agenda.models import Item from openslides.agenda.models import Item
from .workflow import motion_workflow_choices, get_state, State
# TODO: Save submitter and supporter in the same table # TODO: Save submitter and supporter in the same table
class MotionSubmitter(models.Model): class MotionSubmitter(models.Model):
@ -58,8 +60,7 @@ class Motion(SlideMixin, models.Model):
# is deactivated. Maybe it has to be renamed. # is deactivated. Maybe it has to be renamed.
permitted_version = models.ForeignKey( permitted_version = models.ForeignKey(
'MotionVersion', null=True, blank=True, related_name="permitted") 'MotionVersion', null=True, blank=True, related_name="permitted")
# TODO: Define status state_id = models.CharField(max_length=3)
status = models.CharField(max_length=3)
# Log (Translatable) # Log (Translatable)
identifier = models.CharField(max_length=255, null=True, blank=True, identifier = models.CharField(max_length=255, null=True, blank=True,
unique=True) unique=True)
@ -86,6 +87,9 @@ class Motion(SlideMixin, models.Model):
""" """
Saves the motion. Create or update a motion_version object Saves the motion. Create or update a motion_version object
""" """
if not self.state_id:
self.state = 'default'
super(Motion, self).save(*args, **kwargs) super(Motion, self).save(*args, **kwargs)
for attr in ['title', 'text', 'reason']: for attr in ['title', 'text', 'reason']:
if getattr(self, attr) != getattr(self.last_version, attr): if getattr(self, attr) != getattr(self.last_version, attr):
@ -268,12 +272,33 @@ class Motion(SlideMixin, models.Model):
#self.writelog(_("Supporter: -%s") % (person)) #self.writelog(_("Supporter: -%s") % (person))
def create_poll(self): def create_poll(self):
"""
Create a new poll for this motion
"""
# TODO: auto increment the poll_number in the Database # TODO: auto increment the poll_number in the Database
poll_number = self.polls.aggregate(Max('poll_number'))['poll_number__max'] or 0 poll_number = self.polls.aggregate(Max('poll_number'))['poll_number__max'] or 0
poll = MotionPoll.objects.create(motion=self, poll_number=poll_number + 1) poll = MotionPoll.objects.create(motion=self, poll_number=poll_number + 1)
poll.set_options() poll.set_options()
return poll return poll
def get_state(self):
"""
Get the state of this motion. Return a State object.
"""
return get_state(self.state_id)
def set_state(self, state):
"""
Set the state of this motion.
state has to be a valid state id or State object.
"""
if type(state) is not State:
state = get_state(state)
self.state_id = state.id
state = property(get_state, set_state)
class MotionVersion(models.Model): class MotionVersion(models.Model):
title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title")) title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title"))

View File

@ -25,4 +25,4 @@ def default_config(sender, key, **kwargs):
'motion_pdf_title': _('Motions'), 'motion_pdf_title': _('Motions'),
'motion_pdf_preamble': '', 'motion_pdf_preamble': '',
'motion_create_new_version': 'ALLWASY_CREATE_NEW_VERSION', 'motion_create_new_version': 'ALLWASY_CREATE_NEW_VERSION',
}.get(key) 'motion_workflow': 'default'}.get(key)

View File

@ -12,6 +12,7 @@
<p>Reason: {{ motion.reason }}</p> <p>Reason: {{ motion.reason }}</p>
<p>Submitter: {% for submitter in motion.submitter.all %}{{ submitter.person }} {% endfor %}</p> <p>Submitter: {% for submitter in motion.submitter.all %}{{ submitter.person }} {% endfor %}</p>
<p>Supporter: {% for supporter in motion.supporter.all %}{{ supporter.person }} {% endfor %}</p> <p>Supporter: {% for supporter in motion.supporter.all %}{{ supporter.person }} {% endfor %}</p>
<p>State: {{ motion.state }}</p>
<ol> <ol>
{% for motion_version in motion.versions.all %} {% for motion_version in motion.versions.all %}

View File

@ -248,6 +248,7 @@ class Config(FormView):
'motion_pdf_title': config['motion_pdf_title'], 'motion_pdf_title': config['motion_pdf_title'],
'motion_pdf_preamble': config['motion_pdf_preamble'], 'motion_pdf_preamble': config['motion_pdf_preamble'],
'motion_create_new_version': config['motion_create_new_version'], 'motion_create_new_version': config['motion_create_new_version'],
'motion_workflow': config['motion_workflow'],
} }
def form_valid(self, form): def form_valid(self, form):
@ -258,6 +259,7 @@ class Config(FormView):
config['motion_pdf_title'] = form.cleaned_data['motion_pdf_title'] config['motion_pdf_title'] = form.cleaned_data['motion_pdf_title']
config['motion_pdf_preamble'] = form.cleaned_data['motion_pdf_preamble'] config['motion_pdf_preamble'] = form.cleaned_data['motion_pdf_preamble']
config['motion_create_new_version'] = form.cleaned_data['motion_create_new_version'] config['motion_create_new_version'] = form.cleaned_data['motion_create_new_version']
config['motion_workflow'] = form.cleaned_data['motion_workflow']
messages.success(self.request, _('Motion settings successfully saved.')) messages.success(self.request, _('Motion settings successfully saved.'))
return super(Config, self).form_valid(form) return super(Config, self).form_valid(form)

View File

@ -0,0 +1,74 @@
from django.conf import settings
from django.core import exceptions
from django.utils.importlib import import_module
from openslides.config.models import config
ugettext = lambda s: s
_workflow = None
class State(object):
def __init__(self, id, name, next_states=[], poll=False, support=False):
self.id = id
self.name = name
self.next_states = next_states
self.poll = poll
self.support = support
def __unicode__(self):
return self.name
def motion_workflow_choices():
for workflow in settings.MOTION_WORKFLOW:
yield workflow[0], workflow[1]
def get_state(state='default'):
global _workflow
if _workflow is not None:
return _workflow[state]
_workflow = {}
for workflow in settings.MOTION_WORKFLOW:
if workflow[0] == config['motion_workflow']:
try:
wf_module, wf_default_state_name = workflow[2].rsplit('.', 1)
except ValueError:
raise exceptions.ImproperlyConfigured(
'%s isn\'t a workflow module' % workflow[2])
try:
mod = import_module(wf_module)
except ImportError as e:
raise exceptions.ImproperlyConfigured(
'Error importing workflow %s: "%s"' % (wf_module, e))
try:
default_state = getattr(mod, wf_default_state_name)
except AttributeError:
raise exceptions.ImproperlyConfigured(
'Workflow module "%s" does not define a "%s" State'
% (wf_module, wf_default_state_name))
_workflow['default'] = default_state
break
else:
raise ImproperlyConfigured('Unknown workflow %s' % conf['motion_workflow'])
populate_workflow(default_state, _workflow)
return get_state(state)
def populate_workflow(state, workflow):
workflow[state.id] = state
for s in state.next_states:
if s.id not in workflow:
populate_workflow(s, workflow)
default_workflow = State('pub', ugettext('Published'), support=True, next_states=[
State('per', ugettext('Permitted'), poll=True, next_states=[
State('acc', ugettext('Accepted')),
State('rej', ugettext('Rejected')),
State('wit', ugettext('Withdrawed')),
State('adj', ugettext('Adjourned')),
State('noc', ugettext('Not Concerned')),
State('com', ugettext('Commited a bill')),
State('rev', ugettext('Needs Review'))]),
State('nop', ugettext('Rejected (not authorized)'))])