Motion poll PDF

This commit is contained in:
Oskar Hahn 2013-04-21 19:12:50 +02:00
parent 4f9041df65
commit 02d1025346
4 changed files with 125 additions and 21 deletions

View File

@ -11,6 +11,7 @@
""" """
import random import random
import os
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from reportlab.lib import colors from reportlab.lib import colors
@ -18,15 +19,21 @@ from reportlab.lib.units import cm
from reportlab.platypus import ( from reportlab.platypus import (
SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle) SimpleDocTemplate, PageBreak, Paragraph, Spacer, Table, TableStyle)
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings
from openslides.config.api import config from openslides.config.api import config
from openslides.utils.pdf import stylesheet from openslides.utils.pdf import stylesheet
from .models import Motion from .models import Motion
# Needed to count the delegates
# TODO: find another way to do this.
from openslides.participant.models import User
def motions_to_pdf(pdf): def motions_to_pdf(pdf):
"""Create a PDF with all motions.""" """
Create a PDF with all motions.
"""
motions = Motion.objects.all() motions = Motion.objects.all()
all_motion_cover(pdf, motions) all_motion_cover(pdf, motions)
for motion in motions: for motion in motions:
@ -35,8 +42,9 @@ def motions_to_pdf(pdf):
def motion_to_pdf(pdf, motion): def motion_to_pdf(pdf, motion):
"""Create a PDF for one motion.""" """
Create a PDF for one motion.
"""
pdf.append(Paragraph(_("Motion: %s") % motion.title, stylesheet['Heading1'])) pdf.append(Paragraph(_("Motion: %s") % motion.title, stylesheet['Heading1']))
motion_data = [] motion_data = []
@ -231,7 +239,9 @@ def convert_html_to_reportlab(pdf, text):
def all_motion_cover(pdf, motions): def all_motion_cover(pdf, motions):
"""Create a coverpage for all motions.""" """
Create a coverpage for all motions.
"""
pdf.append(Paragraph(config["motion_pdf_title"], stylesheet['Heading1'])) pdf.append(Paragraph(config["motion_pdf_title"], stylesheet['Heading1']))
preamble = config["motion_pdf_preamble"] preamble = config["motion_pdf_preamble"]
@ -245,3 +255,46 @@ def all_motion_cover(pdf, motions):
else: else:
for motion in motions: for motion in motions:
pdf.append(Paragraph(motion.title, stylesheet['Heading3'])) pdf.append(Paragraph(motion.title, stylesheet['Heading3']))
def motion_poll_to_pdf(pdf, poll):
imgpath = os.path.join(settings.SITE_ROOT, 'static/img/circle.png')
circle = "<img src='%s' width='15' height='15'/>&nbsp;&nbsp;" % imgpath
cell = []
cell.append(Spacer(0, 0.8 * cm))
cell.append(Paragraph(_("Motion No. %s") % poll.motion.identifier, stylesheet['Ballot_title']))
cell.append(Paragraph(poll.motion.title, stylesheet['Ballot_subtitle']))
cell.append(Paragraph(_("%d. Vote") % poll.poll_number, stylesheet['Ballot_description']))
cell.append(Spacer(0, 0.5 * cm))
cell.append(Paragraph(circle + unicode(_("Yes")), stylesheet['Ballot_option']))
cell.append(Paragraph(circle + unicode(_("No")), stylesheet['Ballot_option']))
cell.append(Paragraph(circle + unicode(_("Abstention")), stylesheet['Ballot_option']))
data = []
# get ballot papers config values
ballot_papers_selection = config["motion_pdf_ballot_papers_selection"]
ballot_papers_number = config["motion_pdf_ballot_papers_number"]
# set number of ballot papers
if ballot_papers_selection == "NUMBER_OF_DELEGATES":
# TODO: get this number from persons
number = User.objects.filter(type__iexact="delegate").count()
elif ballot_papers_selection == "NUMBER_OF_ALL_PARTICIPANTS":
# TODO: get the number from the persons
number = int(User.objects.count())
else: # ballot_papers_selection == "CUSTOM_NUMBER"
number = int(ballot_papers_number)
number = max(1, number)
# print ballot papers
if number > 0:
# TODO: try [cell, cell] * (number / 2)
for user in xrange(number / 2):
data.append([cell, cell])
rest = number % 2
if rest:
data.append([cell, ''])
t = Table(data, 10.5 * cm, 7.42 * cm)
t.setStyle(TableStyle(
[('GRID', (0, 0), (-1, -1), 0.25, colors.grey),
('VALIGN', (0, 0), (-1, -1), 'TOP')]))
pdf.append(t)

View File

@ -3,7 +3,7 @@
{% load i18n %} {% load i18n %}
{% block title %} {% block title %}
{{ block.super }} - {% trans "Motion" %} {{ motion.identifier }}, {{ ballot }}. {% trans "Vote" %} {{ block.super }} - {% trans "Motion" %} {{ motion.identifier }}, {{ poll }}. {% trans "Vote" %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -11,7 +11,7 @@
{{ motion }} {{ motion }}
<br> <br>
<small> <small>
{% trans "Motion" %} {{ motion.identifier }}, {{ ballot }}. {% trans "Vote" %} {% trans "Motion" %} {{ motion.identifier }}, {{ poll }}. {% trans "Vote" %}
</small> </small>
<small class="pull-right"> <small class="pull-right">
<div class="btn-toolbar"> <div class="btn-toolbar">
@ -57,8 +57,7 @@
<!-- ballot paper button --> <!-- ballot paper button -->
<p> <p>
{# TODO: create ballot paper instead of motion_detail_pdf ! #} <a href="{% url 'motion_poll_pdf' motion.id poll.poll_number %}" class="btn">
<a href="{% url 'motion_detail_pdf' motion.id %}" class="btn">
<i class="icon-print"></i> {% trans 'Ballot paper as PDF' %} <i class="icon-print"></i> {% trans 'Ballot paper as PDF' %}
</a> </a>
</p> </p>

View File

@ -90,6 +90,11 @@ urlpatterns = patterns('openslides.motion.views',
name='motion_poll_delete', name='motion_poll_delete',
), ),
url(r'^(?P<pk>\d+)/poll/(?P<poll_number>\d+)/pdf/$',
'poll_pdf',
name='motion_poll_pdf',
),
url(r'^(?P<pk>\d+)/set_state/(?P<state>\d+)/$', url(r'^(?P<pk>\d+)/set_state/(?P<state>\d+)/$',
'set_state', 'set_state',
name='motion_set_state', name='motion_set_state',

View File

@ -37,7 +37,7 @@ from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin, from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
MotionDisableVersioningMixin, MotionCategoryMixin, MotionDisableVersioningMixin, MotionCategoryMixin,
MotionIdentifierMixin) MotionIdentifierMixin)
from .pdf import motions_to_pdf, motion_to_pdf from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf
class MotionListView(ListView): class MotionListView(ListView):
@ -422,12 +422,16 @@ poll_create = PollCreateView.as_view()
class PollMixin(object): class PollMixin(object):
"""Mixin for the PollUpdateView and the PollDeleteView.""" """
Mixin for the PollUpdateView and the PollDeleteView.
"""
permission_required = 'motion.can_manage_motion' permission_required = 'motion.can_manage_motion'
success_url_name = 'motion_detail' success_url_name = 'motion_detail'
def get_object(self): def get_object(self):
"""Return a MotionPoll object. """
Return a MotionPoll object.
Use the motion id and the poll_number from the url kwargs to get the Use the motion id and the poll_number from the url kwargs to get the
object. object.
@ -437,30 +441,40 @@ class PollMixin(object):
poll_number=self.kwargs['poll_number']).get() poll_number=self.kwargs['poll_number']).get()
def get_url_name_args(self): def get_url_name_args(self):
"""Return the arguments to create the url to the success_url""" """
Return the arguments to create the url to the success_url.
"""
return [self.object.motion.pk] return [self.object.motion.pk]
class PollUpdateView(PollMixin, PollFormView): class PollUpdateView(PollMixin, PollFormView):
"""View to update a MotionPoll.""" """
View to update a MotionPoll.
"""
poll_class = MotionPoll poll_class = MotionPoll
"""Poll Class to use for this view.""" """
Poll Class to use for this view.
"""
template_name = 'motion/poll_form.html' template_name = 'motion/poll_form.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
"""Return the template context. """
Return the template context.
Append the motion object to the context. Append the motion object to the context.
""" """
context = super(PollUpdateView, self).get_context_data(**kwargs) context = super(PollUpdateView, self).get_context_data(**kwargs)
context.update({ context.update({
'motion': self.poll.motion}) 'motion': self.poll.motion,
'poll': self.poll})
return context return context
def form_valid(self, form): def form_valid(self, form):
"""Write a log message, if the form is valid.""" """
Write a log message, if the form is valid.
"""
value = super(PollUpdateView, self).form_valid(form) value = super(PollUpdateView, self).form_valid(form)
self.object.write_log(ugettext_noop('Poll updated'), self.request.user) self.object.write_log(ugettext_noop('Poll updated'), self.request.user)
return value return value
@ -469,21 +483,54 @@ poll_edit = PollUpdateView.as_view()
class PollDeleteView(PollMixin, DeleteView): class PollDeleteView(PollMixin, DeleteView):
"""View to delete a MotionPoll.""" """
View to delete a MotionPoll.
"""
model = MotionPoll model = MotionPoll
def case_yes(self): def case_yes(self):
"""Write a log message, if the form is valid.""" """
Write a log message, if the form is valid.
"""
super(PollDeleteView, self).case_yes() super(PollDeleteView, self).case_yes()
self.object.write_log(ugettext_noop('Poll deleted'), self.request.user) self.object.write_log(ugettext_noop('Poll deleted'), self.request.user)
def get_redirect_url(self, **kwargs): def get_redirect_url(self, **kwargs):
"""Return the URL to the DetailView of the motion.""" """
Return the URL to the DetailView of the motion.
"""
return reverse('motion_detail', args=[self.object.motion.pk]) return reverse('motion_detail', args=[self.object.motion.pk])
poll_delete = PollDeleteView.as_view() poll_delete = PollDeleteView.as_view()
class PollPDFView(PollMixin, PDFView):
"""
Generates a ballotpaper.
"""
permission_required = 'motion.can_manage_motion'
def get(self, *args, **kwargs):
self.object = self.get_object()
return super(PollPDFView, self).get(*args, **kwargs)
def get_filename(self):
"""
Return the filename for the PDF.
"""
return u'%s%s_%s' % (_("Motion"), str(self.object.poll_number), _("Poll"))
def append_to_pdf(self, pdf):
"""
Append PDF objects.
"""
motion_poll_to_pdf(pdf, self.object)
poll_pdf = PollPDFView.as_view()
class MotionSetStateView(SingleObjectMixin, RedirectView): class MotionSetStateView(SingleObjectMixin, RedirectView):
"""View to set the state of a motion. """View to set the state of a motion.