Fixed #595
This commit is contained in:
parent
a4773ad17c
commit
10b51f6897
@ -36,17 +36,21 @@ from .exceptions import MotionError, WorkflowError
|
|||||||
|
|
||||||
|
|
||||||
class Motion(SlideMixin, models.Model):
|
class Motion(SlideMixin, models.Model):
|
||||||
"""The Motion Class.
|
"""
|
||||||
|
The Motion Class.
|
||||||
|
|
||||||
This class is the main entry point to all other classes related to a motion.
|
This class is the main entry point to all other classes related to a motion.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
prefix = 'motion'
|
prefix = 'motion'
|
||||||
"""Prefix for the slide system."""
|
"""
|
||||||
|
Prefix for the slide system.
|
||||||
|
"""
|
||||||
|
|
||||||
active_version = models.ForeignKey('MotionVersion', null=True,
|
active_version = models.ForeignKey('MotionVersion', null=True,
|
||||||
related_name="active_version")
|
related_name="active_version")
|
||||||
"""Points to a specific version.
|
"""
|
||||||
|
Points to a specific version.
|
||||||
|
|
||||||
Used be the permitted-version-system to deside which version is the active
|
Used be the permitted-version-system to deside which version is the active
|
||||||
version. Could also be used to only choose a specific version as a default
|
version. Could also be used to only choose a specific version as a default
|
||||||
@ -54,23 +58,29 @@ class Motion(SlideMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
state = models.ForeignKey('State', null=True) # TODO: Check whether null=True is necessary.
|
state = models.ForeignKey('State', null=True) # TODO: Check whether null=True is necessary.
|
||||||
"""The related state object.
|
"""
|
||||||
|
The related state object.
|
||||||
|
|
||||||
This attribute is to get the current state of the motion.
|
This attribute is to get 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)
|
||||||
"""A string as human readable identifier for the motion."""
|
"""
|
||||||
|
A string as human readable identifier for the motion.
|
||||||
|
"""
|
||||||
|
|
||||||
identifier_number = models.IntegerField(null=True)
|
identifier_number = models.IntegerField(null=True)
|
||||||
"""Counts the number of the motion in one category.
|
"""
|
||||||
|
Counts the number of the motion in one category.
|
||||||
|
|
||||||
Needed to find the next free motion-identifier.
|
Needed to find the next free motion-identifier.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
category = models.ForeignKey('Category', null=True, blank=True)
|
category = models.ForeignKey('Category', null=True, blank=True)
|
||||||
"""ForeignKey to one category of motions."""
|
"""
|
||||||
|
ForeignKey to one category of motions.
|
||||||
|
"""
|
||||||
|
|
||||||
# TODO: proposal
|
# TODO: proposal
|
||||||
#master = models.ForeignKey('self', null=True, blank=True)
|
#master = models.ForeignKey('self', null=True, blank=True)
|
||||||
@ -86,12 +96,15 @@ class Motion(SlideMixin, models.Model):
|
|||||||
# ordering = ('number',)
|
# ordering = ('number',)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
"""Return a human readable name of this motion."""
|
"""
|
||||||
|
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.
|
"""
|
||||||
|
Save the motion.
|
||||||
|
|
||||||
1. Set the state of a new motion to the default state.
|
1. Set the state of a new motion to the default state.
|
||||||
2. Save the motion object.
|
2. Save the motion object.
|
||||||
@ -113,6 +126,9 @@ class Motion(SlideMixin, models.Model):
|
|||||||
if not self.state:
|
if not self.state:
|
||||||
self.reset_state()
|
self.reset_state()
|
||||||
|
|
||||||
|
if not self.identifier and self.identifier is not None:
|
||||||
|
self.identifier = None
|
||||||
|
|
||||||
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
|
||||||
@ -163,7 +179,8 @@ 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.
|
"""
|
||||||
|
Return an URL for this version.
|
||||||
|
|
||||||
The keyword argument 'link' can be 'detail', 'view', 'edit' or 'delete'.
|
The keyword argument 'link' can be 'detail', 'view', 'edit' or 'delete'.
|
||||||
"""
|
"""
|
||||||
@ -202,7 +219,8 @@ class Motion(SlideMixin, models.Model):
|
|||||||
break
|
break
|
||||||
|
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
@ -212,7 +230,8 @@ 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 into the version object, wenn motion.save() is
|
The titel will me saved into the version object, wenn motion.save() is
|
||||||
called.
|
called.
|
||||||
@ -220,13 +239,15 @@ class Motion(SlideMixin, models.Model):
|
|||||||
self._title = title
|
self._title = title
|
||||||
|
|
||||||
title = property(get_title, set_title)
|
title = property(get_title, set_title)
|
||||||
"""The title of the motion.
|
"""
|
||||||
|
The title of the motion.
|
||||||
|
|
||||||
Is saved in a MotionVersion object.
|
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().
|
||||||
"""
|
"""
|
||||||
@ -236,20 +257,23 @@ 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.
|
"""
|
||||||
|
The text of a motin.
|
||||||
|
|
||||||
Is saved in a MotionVersion object.
|
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().
|
||||||
"""
|
"""
|
||||||
@ -259,21 +283,24 @@ 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.
|
"""
|
||||||
|
The reason for the motion.
|
||||||
|
|
||||||
Is saved in a MotionVersion object.
|
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.
|
"""
|
||||||
|
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.
|
||||||
@ -287,7 +314,8 @@ 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.
|
||||||
"""
|
"""
|
||||||
@ -297,7 +325,8 @@ 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.
|
||||||
|
|
||||||
The keyword argument 'version' can be a MotionVersion object or the
|
The keyword argument 'version' can be a MotionVersion object or the
|
||||||
version_number of a version object or None.
|
version_number of a version object or None.
|
||||||
@ -319,11 +348,15 @@ 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."""
|
"""
|
||||||
|
The active version of this motion.
|
||||||
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_version(self):
|
def last_version(self):
|
||||||
"""Return the newest version of the motion."""
|
"""
|
||||||
|
Return 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]
|
||||||
@ -348,11 +381,15 @@ class Motion(SlideMixin, models.Model):
|
|||||||
MotionSubmitter.objects.create(motion=self, person=person)
|
MotionSubmitter.objects.create(motion=self, person=person)
|
||||||
|
|
||||||
def is_supporter(self, person):
|
def is_supporter(self, person):
|
||||||
"""Return True, if person is a supporter of this motion. Else: False."""
|
"""
|
||||||
|
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 'person' as a supporter of this motion.
|
||||||
|
"""
|
||||||
if self.state.allow_support:
|
if self.state.allow_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()
|
||||||
@ -360,14 +397,17 @@ 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 'person' as supporter from this motion.
|
||||||
|
"""
|
||||||
if self.state.allow_support:
|
if self.state.allow_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.
|
Return the new poll object.
|
||||||
"""
|
"""
|
||||||
@ -381,7 +421,8 @@ 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 set_state(self, state):
|
def set_state(self, state):
|
||||||
"""Set the state of the motion.
|
"""
|
||||||
|
Set the state of the motion.
|
||||||
|
|
||||||
State can be the id of a state object or a state object.
|
State can be the id of a state object or a state object.
|
||||||
"""
|
"""
|
||||||
@ -393,7 +434,11 @@ class Motion(SlideMixin, models.Model):
|
|||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
def reset_state(self):
|
def reset_state(self):
|
||||||
"""Set the state to the default state. If the motion is new, it chooses the default workflow from config."""
|
"""
|
||||||
|
Set the state to the default state.
|
||||||
|
|
||||||
|
If the motion is new, it chooses the default workflow from config.
|
||||||
|
"""
|
||||||
if self.state:
|
if self.state:
|
||||||
self.state = self.state.workflow.first_state
|
self.state = self.state.workflow.first_state
|
||||||
else:
|
else:
|
||||||
@ -401,7 +446,9 @@ class Motion(SlideMixin, models.Model):
|
|||||||
Workflow.objects.get(pk=config['motion_workflow']).state_set.all()[0])
|
Workflow.objects.get(pk=config['motion_workflow']).state_set.all()[0])
|
||||||
|
|
||||||
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
|
||||||
@ -409,7 +456,9 @@ 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 a title for the Agenda.
|
||||||
|
"""
|
||||||
return self.last_version.title # TODO: nutze active_version
|
return self.last_version.title # TODO: nutze active_version
|
||||||
|
|
||||||
## def get_agenda_title_supplement(self):
|
## def get_agenda_title_supplement(self):
|
||||||
@ -417,7 +466,8 @@ 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.
|
"""
|
||||||
|
Return a dictonary with all allowed actions for a specific person.
|
||||||
|
|
||||||
The dictonary contains the following actions.
|
The dictonary contains the following actions.
|
||||||
|
|
||||||
@ -463,7 +513,8 @@ class Motion(SlideMixin, models.Model):
|
|||||||
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.
|
"""
|
||||||
|
Set the active state of a version to True.
|
||||||
|
|
||||||
'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.
|
||||||
"""
|
"""
|
||||||
@ -476,7 +527,8 @@ 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.
|
||||||
"""
|
"""
|
||||||
@ -492,7 +544,8 @@ class Motion(SlideMixin, models.Model):
|
|||||||
|
|
||||||
class MotionVersion(models.Model):
|
class MotionVersion(models.Model):
|
||||||
"""
|
"""
|
||||||
A MotionVersion object saves some date of the motion."""
|
A MotionVersion object saves some date of the motion.
|
||||||
|
"""
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion, related_name='versions')
|
motion = models.ForeignKey(Motion, related_name='versions')
|
||||||
"""The motion to which the version belongs."""
|
"""The motion to which the version belongs."""
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.identifier }}{% endblock %}
|
{% block title %}{{ block.super }} – {% trans "Motion" %} {{ motion.identifier|default:'' }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>
|
<h1>
|
||||||
{{ motion.title }} {{ motion.category }}
|
{{ motion.title }} {{ motion.category|default:'' }}
|
||||||
<br>
|
<br>
|
||||||
<small>
|
<small>
|
||||||
{% if motion.identifier %}
|
{% if motion.identifier %}
|
||||||
{% trans "Motion" %} {{ motion.identifier }},
|
{% trans "Motion" %} {{ motion.identifier|default:'' }},
|
||||||
{% else %}
|
{% else %}
|
||||||
<i>[{% trans "no number" %}]</i>,
|
<i>[{% trans "no number" %}]</i>,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% for motion in motion_list %}
|
{% for motion in motion_list %}
|
||||||
<tr class="{% if motion.active %}activeline{% endif %}">
|
<tr class="{% if motion.active %}activeline{% endif %}">
|
||||||
<td>{{ motion.identifier }}</td>
|
<td>{{ motion.identifier|default:'' }}</td>
|
||||||
<td><a href="{% model_url motion %}">{{ motion.title }}</a></td>
|
<td><a href="{% model_url motion %}">{{ motion.title }}</a></td>
|
||||||
{% if min_supporters > 0 %}
|
{% if min_supporters > 0 %}
|
||||||
<td class="optional">{# motion.count_supporters #}</td>
|
<td class="optional">{# motion.count_supporters #}</td>
|
||||||
|
@ -137,6 +137,10 @@ class ModelTest(TestCase):
|
|||||||
state_1.next_states.add(state_2)
|
state_1.next_states.add(state_2)
|
||||||
state_1.save()
|
state_1.save()
|
||||||
|
|
||||||
|
def test_two_empty_identifiers(self):
|
||||||
|
motion1 = Motion.objects.create(title='foo', text='bar', identifier='')
|
||||||
|
motion2 = Motion.objects.create(title='foo2', text='bar2', identifier='')
|
||||||
|
|
||||||
|
|
||||||
class ConfigTest(TestCase):
|
class ConfigTest(TestCase):
|
||||||
def test_stop_submitting(self):
|
def test_stop_submitting(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user