Add workflows for motions
This commit is contained in:
parent
3f02a28002
commit
fb82a1787b
@ -26,9 +26,12 @@ LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
SESSION_COOKIE_NAME = 'OpenSlidesSessionID'
|
||||
|
||||
|
||||
ugettext = lambda s: s
|
||||
|
||||
MOTION_WORKFLOW = (
|
||||
('default', ugettext('default'), 'openslides.motion.workflow.default_workflow'),
|
||||
)
|
||||
|
||||
LANGUAGES = (
|
||||
('de', ugettext('German')),
|
||||
('en', ugettext('English')),
|
||||
|
@ -16,6 +16,7 @@ from django.utils.translation import ugettext as _
|
||||
from openslides.utils.forms import CssClassMixin
|
||||
from openslides.utils.person import PersonFormField, MultiplePersonFormField
|
||||
from .models import Motion
|
||||
from .workflow import motion_workflow_choices
|
||||
|
||||
|
||||
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
||||
@ -117,3 +118,9 @@ class ConfigForm(forms.Form, CssClassMixin):
|
||||
('NEVER_CREATE_NEW_VERSION', _('create never 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())
|
||||
|
@ -30,6 +30,8 @@ from openslides.projector.api import register_slidemodel
|
||||
from openslides.projector.models import SlideMixin
|
||||
from openslides.agenda.models import Item
|
||||
|
||||
from .workflow import motion_workflow_choices, get_state, State
|
||||
|
||||
|
||||
# TODO: Save submitter and supporter in the same table
|
||||
class MotionSubmitter(models.Model):
|
||||
@ -58,8 +60,7 @@ class Motion(SlideMixin, models.Model):
|
||||
# is deactivated. Maybe it has to be renamed.
|
||||
permitted_version = models.ForeignKey(
|
||||
'MotionVersion', null=True, blank=True, related_name="permitted")
|
||||
# TODO: Define status
|
||||
status = models.CharField(max_length=3)
|
||||
state_id = models.CharField(max_length=3)
|
||||
# Log (Translatable)
|
||||
identifier = models.CharField(max_length=255, null=True, blank=True,
|
||||
unique=True)
|
||||
@ -86,6 +87,9 @@ class Motion(SlideMixin, models.Model):
|
||||
"""
|
||||
Saves the motion. Create or update a motion_version object
|
||||
"""
|
||||
if not self.state_id:
|
||||
self.state = 'default'
|
||||
|
||||
super(Motion, self).save(*args, **kwargs)
|
||||
for attr in ['title', 'text', 'reason']:
|
||||
if getattr(self, attr) != getattr(self.last_version, attr):
|
||||
@ -268,12 +272,33 @@ class Motion(SlideMixin, models.Model):
|
||||
#self.writelog(_("Supporter: -%s") % (person))
|
||||
|
||||
def create_poll(self):
|
||||
"""
|
||||
Create a new poll for this motion
|
||||
"""
|
||||
# TODO: auto increment the poll_number in the Database
|
||||
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.set_options()
|
||||
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):
|
||||
title = models.CharField(max_length=255, verbose_name=ugettext_lazy("Title"))
|
||||
|
@ -25,4 +25,4 @@ def default_config(sender, key, **kwargs):
|
||||
'motion_pdf_title': _('Motions'),
|
||||
'motion_pdf_preamble': '',
|
||||
'motion_create_new_version': 'ALLWASY_CREATE_NEW_VERSION',
|
||||
}.get(key)
|
||||
'motion_workflow': 'default'}.get(key)
|
||||
|
@ -12,6 +12,7 @@
|
||||
<p>Reason: {{ motion.reason }}</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>State: {{ motion.state }}</p>
|
||||
|
||||
<ol>
|
||||
{% for motion_version in motion.versions.all %}
|
||||
|
@ -248,6 +248,7 @@ class Config(FormView):
|
||||
'motion_pdf_title': config['motion_pdf_title'],
|
||||
'motion_pdf_preamble': config['motion_pdf_preamble'],
|
||||
'motion_create_new_version': config['motion_create_new_version'],
|
||||
'motion_workflow': config['motion_workflow'],
|
||||
}
|
||||
|
||||
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_preamble'] = form.cleaned_data['motion_pdf_preamble']
|
||||
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.'))
|
||||
return super(Config, self).form_valid(form)
|
||||
|
||||
|
74
openslides/motion/workflow.py
Normal file
74
openslides/motion/workflow.py
Normal 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)'))])
|
Loading…
Reference in New Issue
Block a user