Merge pull request #1170 from ostcar/poll_description
Added a poll description field for each assignment poll
This commit is contained in:
commit
e10d9e5e86
@ -22,6 +22,7 @@ Other:
|
|||||||
- Changed api for main menu entries.
|
- Changed api for main menu entries.
|
||||||
- Enhanced http error pages.
|
- Enhanced http error pages.
|
||||||
- Moved dashboard and select widgets view from projector to core app.
|
- Moved dashboard and select widgets view from projector to core app.
|
||||||
|
- Created a poll description field for each assignment-poll.
|
||||||
|
|
||||||
|
|
||||||
Version 1.5.1 (unreleased)
|
Version 1.5.1 (unreleased)
|
||||||
|
@ -9,7 +9,7 @@ from openslides.utils.person import PersonFormField
|
|||||||
from .models import Assignment
|
from .models import Assignment
|
||||||
|
|
||||||
|
|
||||||
class AssignmentForm(forms.ModelForm, CssClassMixin):
|
class AssignmentForm(CssClassMixin, forms.ModelForm):
|
||||||
posts = forms.IntegerField(
|
posts = forms.IntegerField(
|
||||||
min_value=1, initial=1, label=ugettext_lazy("Number of available posts"))
|
min_value=1, initial=1, label=ugettext_lazy("Number of available posts"))
|
||||||
|
|
||||||
@ -18,8 +18,7 @@ class AssignmentForm(forms.ModelForm, CssClassMixin):
|
|||||||
exclude = ('status', 'elected')
|
exclude = ('status', 'elected')
|
||||||
|
|
||||||
|
|
||||||
class AssignmentRunForm(forms.Form, CssClassMixin):
|
class AssignmentRunForm(CssClassMixin, forms.Form):
|
||||||
candidate = PersonFormField(
|
candidate = PersonFormField(
|
||||||
widget=forms.Select(attrs={'class': 'medium-input'}),
|
widget=forms.Select(attrs={'class': 'medium-input'}),
|
||||||
label=ugettext_lazy("Nominate a participant"),
|
label=ugettext_lazy("Nominate a participant"))
|
||||||
)
|
|
||||||
|
@ -50,9 +50,9 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
name = models.CharField(max_length=100, verbose_name=ugettext_lazy("Name"))
|
name = models.CharField(max_length=100, verbose_name=ugettext_lazy("Name"))
|
||||||
description = models.TextField(null=True, blank=True, verbose_name=ugettext_lazy("Description"))
|
description = models.TextField(null=True, blank=True, verbose_name=ugettext_lazy("Description"))
|
||||||
posts = models.PositiveSmallIntegerField(verbose_name=ugettext_lazy("Number of available posts"))
|
posts = models.PositiveSmallIntegerField(verbose_name=ugettext_lazy("Number of available posts"))
|
||||||
polldescription = models.CharField(
|
poll_description_default = models.CharField(
|
||||||
max_length=100, null=True, blank=True,
|
max_length=79, null=True, blank=True,
|
||||||
verbose_name=ugettext_lazy("Comment on the ballot paper"))
|
verbose_name=ugettext_lazy("Default comment on the ballot paper"))
|
||||||
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
status = models.CharField(max_length=3, choices=STATUS, default='sea')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -192,8 +192,8 @@ class Assignment(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
return person in self.elected
|
return person in self.elected
|
||||||
|
|
||||||
def gen_poll(self):
|
def gen_poll(self):
|
||||||
poll = AssignmentPoll(assignment=self)
|
poll = AssignmentPoll.objects.create(
|
||||||
poll.save()
|
assignment=self, description=self.poll_description_default)
|
||||||
poll.set_options([{'candidate': person} for person in self.candidates])
|
poll.set_options([{'candidate': person} for person in self.candidates])
|
||||||
return poll
|
return poll
|
||||||
|
|
||||||
@ -255,9 +255,11 @@ class AssignmentOption(BaseOption):
|
|||||||
class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
||||||
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
PublishPollMixin, AbsoluteUrlMixin, BasePoll):
|
||||||
option_class = AssignmentOption
|
option_class = AssignmentOption
|
||||||
|
|
||||||
assignment = models.ForeignKey(Assignment, related_name='poll_set')
|
assignment = models.ForeignKey(Assignment, related_name='poll_set')
|
||||||
yesnoabstain = models.NullBooleanField()
|
yesnoabstain = models.NullBooleanField()
|
||||||
|
description = models.CharField(
|
||||||
|
max_length=79, null=True, blank=True,
|
||||||
|
verbose_name=ugettext_lazy("Comment on the ballot paper"))
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return _("Ballot %d") % self.get_ballot()
|
return _("Ballot %d") % self.get_ballot()
|
||||||
@ -297,3 +299,6 @@ class AssignmentPoll(RelatedModelMixin, CollectInvalid, CollectVotesCast,
|
|||||||
|
|
||||||
def get_ballot(self):
|
def get_ballot(self):
|
||||||
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
return self.assignment.poll_set.filter(id__lte=self.id).count()
|
||||||
|
|
||||||
|
def append_pollform_fields(self, fields):
|
||||||
|
fields.append('description')
|
||||||
|
@ -25,14 +25,13 @@
|
|||||||
</small>
|
</small>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{% if assignment.polldescription %}
|
|
||||||
<p><b>{% trans "Short description (for ballot paper)" %}:</b> {{ assignment.polldescription }}</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
{% trans "Special values" %}: <span class="badge badge-success">-1</span> = {% trans 'majority' %} | <span class="badge">-2</span> = {% trans 'undocumented' %}
|
|
||||||
</p>
|
|
||||||
<form action="" method="post" class="small-form">{% csrf_token %}
|
<form action="" method="post" class="small-form">{% csrf_token %}
|
||||||
|
<p>
|
||||||
|
{% trans "Special values" %}:
|
||||||
|
<span class="badge badge-success">-1</span> = {% trans 'majority' %}|
|
||||||
|
<span class="badge">-2</span> = {% trans 'undocumented' %}
|
||||||
|
</p>
|
||||||
|
|
||||||
<table class="table table-striped table-bordered" style="width: auto;">
|
<table class="table table-striped table-bordered" style="width: auto;">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Candidates" %}</th>
|
<th>{% trans "Candidates" %}</th>
|
||||||
@ -73,6 +72,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<p><strong>{% trans "Short description (for ballot paper)" %}:</strong></p>
|
||||||
|
<p class="normal-form">{{ pollform.description }}</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'assignment_poll_pdf' poll.id %}" class="btn" target="_blank">
|
<a href="{% url 'assignment_poll_pdf' poll.id %}" class="btn" target="_blank">
|
||||||
<i class="icon-print"></i> {% trans 'Ballot paper as PDF' %}
|
<i class="icon-print"></i> {% trans 'Ballot paper as PDF' %}
|
||||||
|
@ -197,10 +197,11 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
|||||||
class PollCreateView(SingleObjectMixin, RedirectView):
|
class PollCreateView(SingleObjectMixin, RedirectView):
|
||||||
model = Assignment
|
model = Assignment
|
||||||
permission_required = 'assignment.can_manage_assignment'
|
permission_required = 'assignment.can_manage_assignment'
|
||||||
url_name = 'assignment_poll_view'
|
url_name = 'assignment_detail'
|
||||||
|
|
||||||
def pre_redirect(self, *args, **kwargs):
|
def pre_redirect(self, *args, **kwargs):
|
||||||
self.object = self.get_object().gen_poll()
|
self.object = self.get_object()
|
||||||
|
self.object.gen_poll()
|
||||||
messages.success(self.request, _("New ballot was successfully created."))
|
messages.success(self.request, _("New ballot was successfully created."))
|
||||||
|
|
||||||
|
|
||||||
@ -213,7 +214,7 @@ class PollUpdateView(PollFormView):
|
|||||||
self.assignment = self.poll.get_assignment()
|
self.assignment = self.poll.get_assignment()
|
||||||
context['assignment'] = self.assignment
|
context['assignment'] = self.assignment
|
||||||
context['poll'] = self.poll
|
context['poll'] = self.poll
|
||||||
context['polls'] = self.assignment.poll_set.filter(assignment=self.assignment)
|
context['polls'] = self.assignment.poll_set.all()
|
||||||
context['ballotnumber'] = self.poll.get_ballot()
|
context['ballotnumber'] = self.poll.get_ballot()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@ -513,7 +514,7 @@ class AssignmentPollPDF(PDFView):
|
|||||||
_("Election") + ": " + self.poll.assignment.name,
|
_("Election") + ": " + self.poll.assignment.name,
|
||||||
stylesheet['Ballot_title']))
|
stylesheet['Ballot_title']))
|
||||||
cell.append(Paragraph(
|
cell.append(Paragraph(
|
||||||
self.poll.assignment.polldescription,
|
self.poll.description or '',
|
||||||
stylesheet['Ballot_subtitle']))
|
stylesheet['Ballot_subtitle']))
|
||||||
options = self.poll.get_options()
|
options = self.poll.get_options()
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from django import forms
|
|||||||
from openslides.utils.forms import CssClassMixin
|
from openslides.utils.forms import CssClassMixin
|
||||||
|
|
||||||
|
|
||||||
class OptionForm(forms.Form, CssClassMixin):
|
class OptionForm(CssClassMixin, forms.Form):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
extra = kwargs.pop('extra')
|
extra = kwargs.pop('extra')
|
||||||
formid = kwargs.pop('formid')
|
formid = kwargs.pop('formid')
|
||||||
@ -20,5 +20,4 @@ class OptionForm(forms.Form, CssClassMixin):
|
|||||||
label=value,
|
label=value,
|
||||||
initial=weight,
|
initial=weight,
|
||||||
min_value=-2,
|
min_value=-2,
|
||||||
required=False,
|
required=False)
|
||||||
)
|
|
||||||
|
@ -30,11 +30,10 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
error = True
|
error = True
|
||||||
|
|
||||||
if error:
|
if error:
|
||||||
return self.render_to_response(self.get_context_data(
|
response = self.render_to_response(self.get_context_data(
|
||||||
forms=option_forms,
|
forms=option_forms,
|
||||||
pollform=pollform,
|
pollform=pollform))
|
||||||
))
|
else:
|
||||||
|
|
||||||
for form in option_forms:
|
for form in option_forms:
|
||||||
data = {}
|
data = {}
|
||||||
for value in self.poll.get_vote_values():
|
for value in self.poll.get_vote_values():
|
||||||
@ -42,7 +41,8 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
self.poll.set_vote_objects_with_values(form.option, data)
|
self.poll.set_vote_objects_with_values(form.option, data)
|
||||||
|
|
||||||
pollform.save()
|
pollform.save()
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
response = HttpResponseRedirect(self.get_success_url())
|
||||||
|
return response
|
||||||
|
|
||||||
def get_poll_class(self):
|
def get_poll_class(self):
|
||||||
if self.poll_class is not None:
|
if self.poll_class is not None:
|
||||||
@ -71,4 +71,4 @@ class PollFormView(FormMixin, TemplateView):
|
|||||||
def get_modelform_class(self):
|
def get_modelform_class(self):
|
||||||
fields = []
|
fields = []
|
||||||
self.poll.append_pollform_fields(fields)
|
self.poll.append_pollform_fields(fields)
|
||||||
return modelform_factory(self.poll.__class__, fields=fields)
|
return modelform_factory(type(self.poll), fields=fields)
|
||||||
|
@ -142,6 +142,9 @@ input, textarea {
|
|||||||
.small-form input {
|
.small-form input {
|
||||||
width: 55px;
|
width: 55px;
|
||||||
}
|
}
|
||||||
|
.normal-form input {
|
||||||
|
width: 320px;
|
||||||
|
}
|
||||||
textarea {
|
textarea {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,13 @@ from openslides.utils.main_menu import MainMenuEntry
|
|||||||
class MainMenuEntryObject(TestCase):
|
class MainMenuEntryObject(TestCase):
|
||||||
request_factory = RequestFactory()
|
request_factory = RequestFactory()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""
|
||||||
|
Remove all receivers of the MainMenuEntry-signal, so it is not called in
|
||||||
|
other tests.
|
||||||
|
"""
|
||||||
|
MainMenuEntry.signal.receivers = []
|
||||||
|
|
||||||
def get_entry(self, cls):
|
def get_entry(self, cls):
|
||||||
request = self.request_factory.get('/')
|
request = self.request_factory.get('/')
|
||||||
request.user = AnonymousUser()
|
request.user = AnonymousUser()
|
||||||
|
Loading…
Reference in New Issue
Block a user