This commit is contained in:
Oskar Hahn 2013-04-19 14:12:49 +02:00
parent a4773ad17c
commit 10b51f6897
4 changed files with 97 additions and 40 deletions

View File

@ -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."""

View File

@ -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 %}

View File

@ -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>

View File

@ -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):