mirror of
https://github.com/Kunsi/pretalx-plugin-broadcast-tools
synced 2024-12-22 18:49:11 +00:00
add feature "export pdf page for each talk happening"
This commit is contained in:
parent
c4ccb146ef
commit
25fe8b804e
2 changed files with 243 additions and 0 deletions
236
pretalx_broadcast_tools/exporter.py
Normal file
236
pretalx_broadcast_tools/exporter.py
Normal file
|
@ -0,0 +1,236 @@
|
|||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.utils.timezone import now
|
||||
from pretalx.schedule.exporters import ScheduleData
|
||||
from pretalx.submission.models import SubmissionStates
|
||||
from reportlab.graphics import renderPDF
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.enums import TA_CENTER
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.styles import ParagraphStyle, StyleSheet1
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.platypus import (
|
||||
Flowable,
|
||||
PageBreak,
|
||||
PageTemplate,
|
||||
Paragraph,
|
||||
SimpleDocTemplate,
|
||||
Spacer,
|
||||
Table,
|
||||
TableStyle,
|
||||
)
|
||||
|
||||
A4_WIDTH, A4_HEIGHT = A4
|
||||
PAGE_PADDING = 10 * mm
|
||||
|
||||
|
||||
class PDFInfoPage(Flowable):
|
||||
def __init__(self, event, fahrplan_day, room_details, talk, style):
|
||||
super().__init__()
|
||||
self.event = event
|
||||
self.talk = talk
|
||||
self.day = fahrplan_day
|
||||
self.room = room_details
|
||||
self.style = style
|
||||
self.y_position = PAGE_PADDING
|
||||
|
||||
def _add(self, item, gap=2):
|
||||
_, height = item.wrapOn(
|
||||
self.canv, A4_WIDTH - 2 * PAGE_PADDING, A4_HEIGHT - 2 * PAGE_PADDING
|
||||
)
|
||||
self.y_position += height + gap * mm
|
||||
item.drawOn(self.canv, PAGE_PADDING, -self.y_position)
|
||||
|
||||
def _checkbox_text(self, text, **kwargs):
|
||||
item = Paragraph(text, **kwargs)
|
||||
_, height = item.wrapOn(
|
||||
self.canv, A4_WIDTH - 2 * PAGE_PADDING, A4_HEIGHT - 2 * PAGE_PADDING
|
||||
)
|
||||
self.y_position += height + 2 * mm
|
||||
item.drawOn(self.canv, PAGE_PADDING + 1.3 * height, -self.y_position)
|
||||
self.canv.rect(PAGE_PADDING, -self.y_position, height * 0.8, height * 0.8)
|
||||
|
||||
def _space(self):
|
||||
self._add(Spacer(1, PAGE_PADDING / 2))
|
||||
|
||||
def draw(self):
|
||||
# add Submission code, type, title and "do not record"
|
||||
# horizontally to the side of the page.
|
||||
self.canv.saveState()
|
||||
self.canv.rotate(90)
|
||||
self.canv.setFont("Helvetica", 12)
|
||||
self.canv.drawString(
|
||||
-(A4_HEIGHT - (PAGE_PADDING / 3)),
|
||||
-(PAGE_PADDING / 3),
|
||||
f"{self.talk.submission.code} | {self.talk.submission.submission_type.name} | {self.event.name} | {self.talk.local_start.isoformat()} | Day {self.day['index']} | {self.room['name']}",
|
||||
)
|
||||
self.canv.restoreState()
|
||||
|
||||
if self.talk.submission.do_not_record:
|
||||
self._add(
|
||||
Paragraph("DO NOT RECORD. DO NOT STREAM", style=self.style["Warning"]),
|
||||
gap=0,
|
||||
)
|
||||
self._space()
|
||||
|
||||
self._add(
|
||||
Paragraph(
|
||||
f"{self.event.name} | {self.room['name']} | {self.talk.local_start.strftime('%F %T')} {self.event.timezone}",
|
||||
style=self.style["Meta"],
|
||||
)
|
||||
)
|
||||
self._add(
|
||||
Paragraph(self.talk.submission.title, style=self.style["Title"]), gap=0
|
||||
)
|
||||
self._space()
|
||||
|
||||
for spk in self.talk.submission.speakers.all():
|
||||
self._checkbox_text(
|
||||
spk.get_display_name(),
|
||||
style=self.style["Speaker"],
|
||||
)
|
||||
self._space()
|
||||
|
||||
self._add(
|
||||
Table(
|
||||
[
|
||||
(
|
||||
"Duration",
|
||||
"Language",
|
||||
"Type",
|
||||
"Code",
|
||||
),
|
||||
(
|
||||
self.talk.export_duration,
|
||||
self.talk.submission.content_locale,
|
||||
self.talk.submission.submission_type.name,
|
||||
self.talk.submission.code,
|
||||
),
|
||||
],
|
||||
colWidths=30 * mm,
|
||||
style=TableStyle(
|
||||
[
|
||||
("ALIGN", (0, 0), (-1, -1), "CENTER"),
|
||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
|
||||
("BOX", (0, 0), (-1, -1), 0.25, colors.black),
|
||||
]
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
if self.talk.submission.abstract:
|
||||
self._space()
|
||||
self._add(
|
||||
Paragraph(
|
||||
self.talk.submission.abstract,
|
||||
style=self.style["Abstract"],
|
||||
)
|
||||
)
|
||||
|
||||
if self.talk.submission.notes:
|
||||
self._space()
|
||||
for line in self.talk.submission.notes.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
self._add(
|
||||
Paragraph(
|
||||
line,
|
||||
style=self.style["Notes"],
|
||||
)
|
||||
)
|
||||
|
||||
if self.talk.submission.internal_notes:
|
||||
self._space()
|
||||
for line in self.talk.submission.internal_notes.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
self._add(
|
||||
Paragraph(
|
||||
line,
|
||||
style=self.style["Notes"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class PDFExporter(ScheduleData):
|
||||
identifier = "broadcast_pdf"
|
||||
verbose_name = "Broadcast Tools PDF (with internal notes)"
|
||||
public = False
|
||||
show_qrcode = False
|
||||
icon = "fa-file-pdf"
|
||||
|
||||
def _add_pages(self, doc):
|
||||
style = self._style()
|
||||
pages = []
|
||||
for fahrplan_day in self.data:
|
||||
for room_details in fahrplan_day["rooms"]:
|
||||
for talk in room_details["talks"]:
|
||||
pages.append(
|
||||
PDFInfoPage(self.event, fahrplan_day, room_details, talk, style)
|
||||
)
|
||||
pages.append(PageBreak())
|
||||
return pages
|
||||
|
||||
def _style(self):
|
||||
stylesheet = StyleSheet1()
|
||||
stylesheet.add(
|
||||
ParagraphStyle(name="Normal", fontName="Helvetica", fontSize=12, leading=14)
|
||||
)
|
||||
stylesheet.add(
|
||||
ParagraphStyle(
|
||||
name="Title", fontName="Helvetica-Bold", fontSize=20, leading=24
|
||||
)
|
||||
)
|
||||
stylesheet.add(
|
||||
ParagraphStyle(
|
||||
name="Speaker", fontName="Helvetica-Oblique", fontSize=12, leading=14
|
||||
)
|
||||
)
|
||||
stylesheet.add(
|
||||
ParagraphStyle(name="Meta", fontName="Helvetica", fontSize=10, leading=12)
|
||||
)
|
||||
stylesheet.add(
|
||||
ParagraphStyle(
|
||||
name="Abstract", fontName="Helvetica", fontSize=14, leading=16
|
||||
)
|
||||
)
|
||||
stylesheet.add(
|
||||
ParagraphStyle(
|
||||
name="Notes", fontName="Helvetica-Oblique", fontSize=12, leading=14
|
||||
)
|
||||
)
|
||||
stylesheet.add(
|
||||
ParagraphStyle(
|
||||
name="Warning",
|
||||
fontName="Helvetica-Bold",
|
||||
fontSize=20,
|
||||
leading=24,
|
||||
alignment=TA_CENTER,
|
||||
textColor=colors.red,
|
||||
)
|
||||
)
|
||||
return stylesheet
|
||||
|
||||
def render(self, *args, **kwargs):
|
||||
with NamedTemporaryFile(suffix=".pdf") as f:
|
||||
doc = SimpleDocTemplate(
|
||||
f.name,
|
||||
pagesize=A4,
|
||||
rightMargin=0,
|
||||
leftMargin=0,
|
||||
topMargin=0,
|
||||
bottomMargin=0,
|
||||
)
|
||||
doc.build(self._add_pages(doc))
|
||||
f.seek(0)
|
||||
timestamp = now().strftime("%Y-%m-%d-%H%M")
|
||||
|
||||
return (
|
||||
f"{self.event.slug}_broadcast_tools_{timestamp}.pdf",
|
||||
"application/pdf",
|
||||
f.read(),
|
||||
)
|
|
@ -4,6 +4,7 @@ from django.utils.translation import gettext_noop
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from pretalx.common.models.settings import hierarkey
|
||||
from pretalx.common.signals import register_data_exporters
|
||||
from pretalx.orga.signals import nav_event_settings
|
||||
|
||||
hierarkey.add_default(
|
||||
|
@ -34,3 +35,9 @@ def navbar_info(sender, request, **kwargs):
|
|||
and url.url_name == "orga",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@receiver(register_data_exporters, dispatch_uid="exporter_myexporter")
|
||||
def register_data_exporter(sender, **kwargs):
|
||||
from .exporter import PDFExporter
|
||||
return PDFExporter
|
||||
|
|
Loading…
Reference in a new issue