Save the submitters to the motion
This commit is contained in:
parent
1ae140c11e
commit
1f87749742
@ -19,6 +19,9 @@ from .models import Motion
|
|||||||
|
|
||||||
|
|
||||||
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
||||||
|
"""
|
||||||
|
Form to automaticly save the version data for a motion.
|
||||||
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Motion
|
model = Motion
|
||||||
fields = ()
|
fields = ()
|
||||||
@ -38,12 +41,20 @@ class BaseMotionForm(forms.ModelForm, CssClassMixin):
|
|||||||
widget=forms.Textarea(), required=False, label=_("Reason"))
|
widget=forms.Textarea(), required=False, label=_("Reason"))
|
||||||
|
|
||||||
|
|
||||||
class MotionCreateForm(BaseMotionForm):
|
class MotionSubmitterMixin(forms.ModelForm):
|
||||||
pass
|
submitter = MultiplePersonFormField(label=_("Submitter"))
|
||||||
|
|
||||||
|
|
||||||
class MotionUpdateForm(BaseMotionForm):
|
class MotionSupporterMixin(forms.ModelForm):
|
||||||
pass
|
supporter = MultiplePersonFormField(required=False, label=_("Supporters"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MotionTrivialChangesMixin(object):
|
||||||
|
trivial_change = forms.BooleanField(
|
||||||
|
required=False, label=_("Trivial change"),
|
||||||
|
help_text=_("Trivial changes don't create a new version."))
|
||||||
|
|
||||||
|
|
||||||
class ConfigForm(forms.Form, CssClassMixin):
|
class ConfigForm(forms.Form, CssClassMixin):
|
||||||
|
@ -31,49 +31,21 @@ from openslides.projector.models import SlideMixin
|
|||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
|
|
||||||
|
|
||||||
RELATION = (
|
# TODO: Save submitter and supporter in the same table
|
||||||
(1, _('Submitter')),
|
class MotionSubmitter(models.Model):
|
||||||
(2, _('Supporter')))
|
|
||||||
|
|
||||||
|
|
||||||
class RelatedPersonsManager(models.Manager):
|
|
||||||
"""
|
|
||||||
Manager for MotionRelatedPersons.
|
|
||||||
|
|
||||||
Returns a custom manager with gives a specific queryset for one type of
|
|
||||||
persons.
|
|
||||||
"""
|
|
||||||
def __init__(self, relation, *args, **kwargs):
|
|
||||||
super(RelatedPersonsManager, self).__init__(*args, **kwargs)
|
|
||||||
for key, value in RELATION:
|
|
||||||
if key == relation:
|
|
||||||
self.relation = key
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError('Unknown relation with id %d' % relation)
|
|
||||||
|
|
||||||
def get_query_set(self):
|
|
||||||
return (super(RelatedPersonsManager, self).get_query_set()
|
|
||||||
.filter(relation=self.relation))
|
|
||||||
|
|
||||||
|
|
||||||
class MotionRelatedPersons(models.Model):
|
|
||||||
"""
|
|
||||||
Saves the all persons related to a motion.
|
|
||||||
|
|
||||||
relation = 1: submitter
|
|
||||||
relation = 2: supporter
|
|
||||||
|
|
||||||
The custom manager submitter and supporter return a queryset with the
|
|
||||||
specific persons.
|
|
||||||
"""
|
|
||||||
submitter = RelatedPersonsManager(relation=1)
|
|
||||||
supporter = RelatedPersonsManager(relation=2)
|
|
||||||
objects = models.Manager()
|
|
||||||
|
|
||||||
person = PersonField()
|
person = PersonField()
|
||||||
relation = models.IntegerField(default=1, choices=RELATION)
|
motion = models.ForeignKey('Motion', related_name="submitter")
|
||||||
motion = models.ForeignKey('Motion', related_name="persons")
|
|
||||||
|
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):
|
||||||
@ -198,20 +170,6 @@ class Motion(SlideMixin, models.Model):
|
|||||||
|
|
||||||
reason = property(get_reason, set_reason)
|
reason = property(get_reason, set_reason)
|
||||||
|
|
||||||
@property
|
|
||||||
def submitter(self):
|
|
||||||
"""
|
|
||||||
Return a queryset with all submitter of this motion.
|
|
||||||
"""
|
|
||||||
return MotionRelatedPersons.submitter.filter(motion=self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supporter(self):
|
|
||||||
"""
|
|
||||||
Returns a queryset with all supporter of this motion.
|
|
||||||
"""
|
|
||||||
return MotionRelatedPersons.supporter.filter(motion=self)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def new_version(self):
|
def new_version(self):
|
||||||
"""
|
"""
|
||||||
@ -292,9 +250,15 @@ class MotionVersion(models.Model):
|
|||||||
note = models.TextField(null=True, blank=True)
|
note = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return "%s Version %s" % (self.motion, self.get_version_number())
|
return "%s Version %s" % (self.motion, self.version_number)
|
||||||
|
|
||||||
def get_version_number(self):
|
def get_absolute_url(self, link='detail'):
|
||||||
|
if link == 'view' or link == 'detail':
|
||||||
|
return reverse('motion_version_detail', args=[str(self.motion.id),
|
||||||
|
str(self.version_number)])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version_number(self):
|
||||||
if self.pk is None:
|
if self.pk is None:
|
||||||
return 'new'
|
return 'new'
|
||||||
return (MotionVersion.objects.filter(motion=self.motion)
|
return (MotionVersion.objects.filter(motion=self.motion)
|
||||||
|
@ -4,9 +4,21 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block title %}{{ block.super }} – {% trans "Motion" %} "{{ version.title }}"{% endblock %}
|
{% block title %}{{ block.super }} – {% trans "Motion" %} "{{ motion.title }}"{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<p>Titel: {{ object.title }} </p>
|
<p>Titel: {{ motion.title }} </p>
|
||||||
<p>Text: {{ object.text }}</p>
|
<p>Text: {{ motion.text }}</p>
|
||||||
|
<p>Reason: {{ motion.reason }}</p>
|
||||||
|
<p>Submitter: {% for submitter in motion.submitter.all %}{{ submitter.person }} {% endfor %}</p>
|
||||||
|
|
||||||
|
<ol>
|
||||||
|
{% for motion_version in motion.versions.all %}
|
||||||
|
{% if motion_version.id == motion.version.id %}
|
||||||
|
<li><strong><a href="{% model_url motion_version %}">{{ motion_version }}</a></strong></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a href="{% model_url motion_version %}">{{ motion_version }}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{% trans "Motions" %}</h1>
|
<h1>{% trans "Motions" %}</h1>
|
||||||
<ul>
|
<ol>
|
||||||
{% for motion in motion_list %}
|
{% for motion in motion_list %}
|
||||||
<li>{{ motion }}</li>
|
<li><a href="{% model_url motion %}">{{ motion }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ol>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -17,6 +17,7 @@ from django.db import transaction
|
|||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
from django.http import Http404
|
||||||
|
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
@ -26,8 +27,10 @@ from openslides.utils.template import Tab
|
|||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
from openslides.projector.projector import Widget, SLIDE
|
from openslides.projector.projector import Widget, SLIDE
|
||||||
from .models import Motion
|
from openslides.config.models import config
|
||||||
from .forms import MotionCreateForm, MotionUpdateForm
|
from .models import Motion, MotionSubmitter
|
||||||
|
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
||||||
|
MotionTrivialChangesMixin)
|
||||||
|
|
||||||
|
|
||||||
from django.views.generic.edit import ModelFormMixin
|
from django.views.generic.edit import ModelFormMixin
|
||||||
@ -55,7 +58,10 @@ class MotionDetailView(DetailView):
|
|||||||
object = super(MotionDetailView, self).get_object()
|
object = super(MotionDetailView, self).get_object()
|
||||||
version_id = self.kwargs.get('version_id', None)
|
version_id = self.kwargs.get('version_id', None)
|
||||||
if version_id is not None:
|
if version_id is not None:
|
||||||
|
try:
|
||||||
object.version = int(version_id) -1
|
object.version = int(version_id) -1
|
||||||
|
except IndexError:
|
||||||
|
raise Http404
|
||||||
return object
|
return object
|
||||||
|
|
||||||
motion_detail = MotionDetailView.as_view()
|
motion_detail = MotionDetailView.as_view()
|
||||||
@ -66,9 +72,28 @@ class MotionMixin(object):
|
|||||||
Mixin to add save the version-data to the motion-object
|
Mixin to add save the version-data to the motion-object
|
||||||
"""
|
"""
|
||||||
def manipulate_object(self, form):
|
def manipulate_object(self, 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])
|
||||||
|
|
||||||
|
def post_save(self, form):
|
||||||
|
super(MotionMixin, self).post_save(form)
|
||||||
|
# TODO: only delete and save neccessary submitters
|
||||||
|
self.object.submitter.all().delete()
|
||||||
|
MotionSubmitter.objects.bulk_create(
|
||||||
|
[MotionSubmitter(motion=self.object, person=person)
|
||||||
|
for person in form.cleaned_data['submitter']])
|
||||||
|
|
||||||
|
def get_form_class(self):
|
||||||
|
form_classes = [BaseMotionForm]
|
||||||
|
if config['motion_allow_trivial_change']:
|
||||||
|
form_classes.append(MotionTrivialChangesMixin)
|
||||||
|
if self.request.user.has_perm('motion.can_manage_motion'):
|
||||||
|
form_classes.append(MotionSubmitterMixin)
|
||||||
|
if config['motion_min_supporters'] > 0:
|
||||||
|
form_classes.append(MotionSupporterMixin)
|
||||||
|
return type('MotionForm', tuple(form_classes), {})
|
||||||
|
|
||||||
|
|
||||||
class MotionCreateView(MotionMixin, CreateView):
|
class MotionCreateView(MotionMixin, CreateView):
|
||||||
"""
|
"""
|
||||||
@ -76,7 +101,6 @@ class MotionCreateView(MotionMixin, CreateView):
|
|||||||
"""
|
"""
|
||||||
permission_required = 'motion.can_create_motion'
|
permission_required = 'motion.can_create_motion'
|
||||||
model = Motion
|
model = Motion
|
||||||
form_class = MotionCreateForm
|
|
||||||
|
|
||||||
motion_create = MotionCreateView.as_view()
|
motion_create = MotionCreateView.as_view()
|
||||||
|
|
||||||
@ -85,7 +109,21 @@ class MotionUpdateView(MotionMixin, UpdateView):
|
|||||||
"""
|
"""
|
||||||
Update a motion.
|
Update a motion.
|
||||||
"""
|
"""
|
||||||
|
# TODO: set permissions
|
||||||
model = Motion
|
model = Motion
|
||||||
form_class = MotionUpdateForm
|
apply_url = ''
|
||||||
|
|
||||||
motion_edit = MotionUpdateView.as_view()
|
motion_edit = MotionUpdateView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
def register_tab(request):
|
||||||
|
"""
|
||||||
|
Register the projector tab.
|
||||||
|
"""
|
||||||
|
selected = request.path.startswith('/motion/')
|
||||||
|
return Tab(
|
||||||
|
title=_('Motions'),
|
||||||
|
url=reverse('motion_list'),
|
||||||
|
permission=request.user.has_perm('motion.can_see_motion'),
|
||||||
|
selected=selected,
|
||||||
|
)
|
||||||
|
@ -35,6 +35,7 @@ def active(request, pattern):
|
|||||||
|
|
||||||
@register.simple_tag
|
@register.simple_tag
|
||||||
def model_url(object, link='view'):
|
def model_url(object, link='view'):
|
||||||
|
# TODO: Rename to object_url
|
||||||
return object.get_absolute_url(link)
|
return object.get_absolute_url(link)
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class SuccessUrlMixin(object):
|
|||||||
if 'apply' in self.request.POST:
|
if 'apply' in self.request.POST:
|
||||||
return reverse(self.get_apply_url(), args=[self.object.id])
|
return reverse(self.get_apply_url(), args=[self.object.id])
|
||||||
if self.success_url:
|
if self.success_url:
|
||||||
url = reverse(success_url)
|
url = reverse(self.success_url)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
url = self.object.get_absolute_url()
|
url = self.object.get_absolute_url()
|
||||||
@ -230,12 +230,15 @@ class ModelFormMixin(object):
|
|||||||
self.object = form.save(commit=False)
|
self.object = form.save(commit=False)
|
||||||
self.manipulate_object(form)
|
self.manipulate_object(form)
|
||||||
self.object.save()
|
self.object.save()
|
||||||
form.save_m2m()
|
self.post_save(form)
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
def manipulate_object(self, form):
|
def manipulate_object(self, form):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def post_save(self, form):
|
||||||
|
form.save_m2m()
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(PermissionMixin, SuccessUrlMixin, ExtraContextMixin,
|
class UpdateView(PermissionMixin, SuccessUrlMixin, ExtraContextMixin,
|
||||||
ModelFormMixin, _UpdateView):
|
ModelFormMixin, _UpdateView):
|
||||||
|
Loading…
Reference in New Issue
Block a user