Added a poll description field for each assignment poll

fixed signals in MainMenuTests
This commit is contained in:
Oskar Hahn 2014-01-11 17:07:47 +01:00
parent 03c59303d7
commit 6409f828d7
9 changed files with 56 additions and 40 deletions

View File

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

View File

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

View File

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

View File

@ -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,10 +72,12 @@
</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' %}
</a> </a>
</p> </p>
<!-- Control buttons --> <!-- Control buttons -->
<div class="control-group"> <div class="control-group">

View File

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

View File

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

View File

@ -30,19 +30,19 @@ 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:
data = {}
for value in self.poll.get_vote_values():
data[value] = form.cleaned_data[value]
self.poll.set_vote_objects_with_values(form.option, data)
for form in option_forms: pollform.save()
data = {} response = HttpResponseRedirect(self.get_success_url())
for value in self.poll.get_vote_values(): return response
data[value] = form.cleaned_data[value]
self.poll.set_vote_objects_with_values(form.option, data)
pollform.save()
return HttpResponseRedirect(self.get_success_url())
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)

View File

@ -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;
} }
@ -379,7 +382,7 @@ legend + .control-group {
margin: 0 5px 0 45px; margin: 0 5px 0 45px;
width: auto; width: auto;
} }
/* hide optional column */ /* hide optional column */
.optional { .optional {
display: none; display: none;
} }

View File

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