eds/cms/pdf.py
2018-05-23 21:16:25 +02:00

341 lines
15 KiB
Python

import os
import tempfile
from django.conf import settings
from django.contrib.staticfiles.finders import find
from reportlab.lib import colors
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import cm
from reportlab.pdfgen import canvas
from reportlab.platypus import (Frame, FrameBreak, Flowable, NextPageTemplate,
Paragraph, PageTemplate, Preformatted, Spacer,
SimpleDocTemplate, Table, TableStyle
)
style_normal = ParagraphStyle(name='CORPS', fontName='Helvetica', fontSize=9, alignment=TA_LEFT)
style_bold = ParagraphStyle(name='CORPS', fontName='Helvetica-Bold', fontSize=10, alignment=TA_LEFT)
style_footer = ParagraphStyle(name='CORPS', fontName='Helvetica', fontSize=7, alignment=TA_CENTER)
LOGO_EPC = find('img/logo_EPC.png')
LOGO_ESNE = find('img/logo_ESNE.png')
LOGO_EPC_LONG = find('img/header.gif')
class HorLine(Flowable):
"""Horizontal Line flowable --- draws a line in a flowable"""
def __init__(self, width):
Flowable.__init__(self)
self.width = width
def __repr__(self):
return "Line(w=%s)" % self.width
def draw(self):
self.canv.line(0, 0, self.width, 0)
class EpcBaseDocTemplate(SimpleDocTemplate):
points = '.' * 93
def __init__(self, filename, section='', subject='', orientation=A4):
path = os.path.join(tempfile.gettempdir(), filename)
super().__init__(
path,
pagesize=orientation,
lefMargin=2.5 * cm, bottomMargin=1 * cm, topMargin=1 * cm, rightMargin=2.5 * cm
)
self.page_frame = Frame(
self.leftMargin, self.bottomMargin, self.width - 2.5, self.height - 4 * cm,
id='first_table', showBoundary=0, leftPadding=0 * cm
)
self.story = []
self.section = section
self.subject = subject
def header(self, canvas, doc):
# Logos
canvas.saveState()
canvas.drawImage(
LOGO_EPC, doc.leftMargin, doc.height - 1.5 * cm, 5 * cm, 3 * cm, preserveAspectRatio=True
)
canvas.drawImage(
LOGO_ESNE, doc.width - 2.5 * cm, doc.height - 1.2 * cm, 5 * cm, 3 * cm, preserveAspectRatio=True
)
# Section and subject
x = doc.leftMargin
y = doc.height - 2.5 * cm
canvas.setFont('Helvetica-Bold', 10)
canvas.line(x, y, x + doc.width, y)
y -= 0.4 * cm
canvas.drawString(x, y, self.section)
canvas.drawRightString(x + doc.width, y, self.subject)
y -= 0.2 * cm
canvas.line(x, y, x + doc.width, y)
# Footer
canvas.line(doc.leftMargin, 1 * cm, doc.width + doc.leftMargin, 1 * cm)
footer = Paragraph(settings.PDF_FOOTER_TEXT, style_footer)
w, h = footer.wrap(doc.width, doc.bottomMargin)
footer.drawOn(canvas, doc.leftMargin, h)
canvas.restoreState()
def later_header(self, canvas, doc):
# Footer
canvas.saveState()
canvas.line(doc.leftMargin, 1 * cm, doc.width + doc.leftMargin, 1 * cm)
footer = Paragraph(settings.PDF_FOOTER_TEXT, style_footer)
w, h = footer.wrap(doc.width, doc.bottomMargin)
footer.drawOn(canvas, doc.leftMargin, h)
canvas.restoreState()
def formating(self, text, len=25):
return Preformatted(text, style_normal, maxLineLength=len)
def normal_template_page(self):
first_page_table_frame = Frame(self.leftMargin, self.bottomMargin, self.width + 1 * cm, self.height - 5 * cm,
id='first_table', showBoundary=0, leftPadding=0 * cm)
later_pages_table_frame = Frame(self.leftMargin, self.bottomMargin, self.width + 1 * cm, self.height - 5 * cm,
id='later_table', showBoundary=0, leftPadding=0 * cm)
# Page template
first_page = PageTemplate(id='FirstPage', frames=[first_page_table_frame], onPage=self.header)
later_pages = PageTemplate(id='LaterPages', frames=[later_pages_table_frame], onPage=self.header)
self.addPageTemplates([first_page, later_pages])
self.story = [NextPageTemplate(['*', 'LaterPages'])]
def six_semester_template_page(self):
frame_title = Frame(self.leftMargin, 24*cm, self.width, 1*cm, showBoundary=0, leftPadding=0)
w, h = (7.5 * cm, 6.5 * cm,)
x = [self.leftMargin, 11 * cm] * 3
y = [17 * cm, 17 * cm, 10 * cm, 10 * cm, 3 * cm, 3 * cm]
frames = [Frame(x[f], y[f], width=w, height=h, showBoundary=0, leftPadding=0) for f in range(6)]
# Frame for total periods
frames.append(Frame(self.leftMargin, self.bottomMargin, self.width, 1.5 * cm, leftPadding=0))
frames.insert(0, frame_title)
# Page template
frame_page = PageTemplate(id='FirstPage', frames=frames, onPage=self.header)
self.addPageTemplates(frame_page)
class NumberedCanvas(canvas.Canvas):
"""
Page number and pages counter
"""
def __init__(self, *args, **kwargs):
canvas.Canvas.__init__(self, *args, **kwargs)
self._saved_page_states = []
def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
def save(self):
"""add page info to each page (page x of y)"""
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.draw_page_number(num_pages)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_number(self, page_count):
self.setFont("Helvetica", 7)
self.drawString(self._pagesize[0] / 2, 1 * cm, "Page {0} de {1}".format(self._pageNumber, page_count))
class ModuleDescriptionPdf(EpcBaseDocTemplate):
"""
PDF for module description
"""
def __init__(self, filename):
super().__init__(filename, 'Filière EDS', 'Module de formation')
self.normal_template_page()
def produce(self, module):
str_competence = ' \n'.join(['- {0} ({1})'.format(c.nom, c.code) for c in module.competence_set.all()])
str_sous_competence = ''
for c in module.competence_set.all():
for sc in c.souscompetence_set.all():
str_sous_competence += '- {0} (voir {1})\n'.format(sc.nom, c.code)
str_res = ' \n'.join(['- {0}'.format(c.nom) for c in module.ressource_set.all()]) # for future use
str_objectif = ' \n'.join(['- {0}'.format(c.nom) for c in module.objectif_set.all()])
self.story.append(Paragraph(module.__str__(), style_bold))
self.story.append(Spacer(0, 0.5 * cm))
data = [
['Domaine', module.processus.domaine.__str__()],
['Processus', module.processus.__str__()],
['Situation emblématique', module.situation],
['Compétences visées', str_competence],
['Plus-value sur le CFC ASE', str_sous_competence],
['Objectifs', str_objectif],
['Didactique', module.didactique],
['Evaluation', module.evaluation],
['Type', '{0}, obligatoire'.format(module.type)],
['Semestre', 'Sem. {0}'.format(module.semestre)],
['Présentiel', '{0} heures'.format(module.total_presentiel)],
['Travail personnel', '{0} heures'.format(module.travail_perso)],
['Responsable', module.processus.domaine.responsable.descr_pdf()]
]
formated_data = []
for foo in data:
formated_data.append(
[
Preformatted(foo[0], style_normal, maxLineLength=15),
Preformatted(foo[1], style_normal, maxLineLength=85),
]
)
t = Table(data=formated_data, colWidths=[4 * cm, 13 * cm])
t.hAlign = TA_LEFT
t.setStyle(tblstyle=TableStyle(
[
('SIZE', (0, 0), (-1, -1), 7),
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('LEFTPADDING', (0, 0), (-1, -1), 0),
]
)
)
self.story.append(t)
self.build(self.story)
class FormationPlanPdf(EpcBaseDocTemplate):
"""
PDF for formation plan
"""
def __init__(self, filename):
super().__init__(filename, 'Filière EDS', 'Plan de formation', landscape(A4))
self.normal_template_page()
def formating(self, el1='', length=40):
el1 = '' if el1 == '' else el1.__str__()
return Preformatted(el1, style_normal, maxLineLength=length)
def produce(self, domain, process):
data = [
['Domaines', 'Processus', 'Sem1', 'Sem2', 'Sem3', 'Sem4', 'Sem5', 'Sem6'],
[self.formating(domain[0]), self.formating(process[0], 60), 'M01', '', '', '', '', ''],
[self.formating(''), self.formating('', 60), 'M02', '', '', '', '', ''],
[self.formating(''), self.formating(process[1], 60), '', '', '', 'M03', '', ''],
[self.formating(''), self.formating('', 60), '', 'M04', '', '', '', ''],
[self.formating(domain[1]), self.formating(process[2], 60), 'M05', '', 'M06', '', '', ''],
[self.formating(''), self.formating(process[3], 60), '', '', '', '', 'M07', 'M09'],
[self.formating(''), self.formating('', 60), '', '', '', '', 'M08', ''],
[self.formating(domain[2]), self.formating(process[4], 60), '', '', 'M10', '', 'M12'],
[self.formating(''), self.formating(process[5], 60), '', '', 'M11', '', ''],
[self.formating(domain[3]), self.formating(process[6], 60), '', '', 'M13', '', 'M14', ''],
[self.formating(domain[4]), self.formating(process[7], 60), 'M15', '', '', '', '', ''],
[self.formating(domain[5]), self.formating(process[8], 60), 'M16_1', '', 'M16_2', '', 'M16_3', ''],
[self.formating(domain[6]), self.formating(process[9], 60), 'M17_1', '', 'M17_2', '', 'M17_3', ''],
[self.formating(domain[7]), self.formating(process[10], 60), 'Macc', '', '', '', '', ''],
]
t = Table(
data=data, colWidths=[7 * cm, 9 * cm, 1.5 * cm, 1.5 * cm, 1.5 * cm, 1.5 * cm, 1.5 * cm, 1.5 * cm],
spaceBefore=0.5 * cm, spaceAfter=1 * cm
)
t.setStyle(tblstyle=TableStyle(
[
('SIZE', (0, 0), (-1, -1), 8),
('FONT', (0, 0), (-1, 0), 'Helvetica-Bold'),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('ALIGN', (2, 0), (-1, -1), 'CENTER'),
('GRID', (0, 0), (-1, -1), 0.25, colors.black),
# Domaine 1
('SPAN', (0, 1), (0, 4)),
('SPAN', (1, 1), (1, 2)),
('SPAN', (1, 3), (1, 4)),
('BACKGROUND', (0, 1), (1, 4), settings.DOMAINE_COULEURS['D1']),
('BACKGROUND', (2, 1), (2, 2), settings.DOMAINE_COULEURS['D1']),
('BACKGROUND', (5, 3), (5, 3), settings.DOMAINE_COULEURS['D1']),
('BACKGROUND', (3, 4), (3, 4), settings.DOMAINE_COULEURS['D1']),
# Domaine 2
('SPAN', (0, 5), (0, 7)),
('BACKGROUND', (0, 5), (1, 7), settings.DOMAINE_COULEURS['D2']),
('BACKGROUND', (2, 5), (2, 5), settings.DOMAINE_COULEURS['D2']),
('BACKGROUND', (4, 5), (4, 5), settings.DOMAINE_COULEURS['D2']),
('BACKGROUND', (6, 6), (6, 6), settings.DOMAINE_COULEURS['D2']),
('BACKGROUND', (7, 6), (7, 6), settings.DOMAINE_COULEURS['D2']),
('BACKGROUND', (6, 7), (6, 7), settings.DOMAINE_COULEURS['D2']),
# Domaine 3
('SPAN', (0, 8), (0, 9)),
('SPAN', (1, 6), (1, 7)),
('SPAN', (4, 8), (5, 8)),
('SPAN', (4, 9), (5, 9)),
('BACKGROUND', (0, 8), (1, 9), settings.DOMAINE_COULEURS['D3']),
('BACKGROUND', (4, 8), (6, 8), settings.DOMAINE_COULEURS['D3']),
('BACKGROUND', (4, 9), (5, 9), settings.DOMAINE_COULEURS['D3']),
# Domaine 4
('BACKGROUND', (0, 10), (1, 10), settings.DOMAINE_COULEURS['D4']),
('BACKGROUND', (4, 10), (4, 10), settings.DOMAINE_COULEURS['D4']),
('BACKGROUND', (6, 10), (6, 10), settings.DOMAINE_COULEURS['D4']),
# Domaine 5
('SPAN', (2, 11), (-1, 11)),
('BACKGROUND', (0, 11), (-1, 11), settings.DOMAINE_COULEURS['D5']),
# Domaine 6
('SPAN', (2, 12), (3, 12)),
('SPAN', (4, 12), (5, 12)),
('SPAN', (6, 12), (7, 12)),
('BACKGROUND', (0, 12), (-1, 12), settings.DOMAINE_COULEURS['D6']),
# Domaine 7
('SPAN', (2, 13), (3, 13)),
('SPAN', (4, 13), (5, 13)),
('SPAN', (6, 13), (7, 13)),
('BACKGROUND', (0, 13), (-1, 13), settings.DOMAINE_COULEURS['D7']),
# Domaine 8
('SPAN', (2, 14), (-1, 14)),
('BACKGROUND', (0, 14), (-1, 14), settings.DOMAINE_COULEURS['D8']),
]
)
)
t.hAlign = TA_LEFT
self.story.append(t)
self.build(self.story)
class PeriodeSemestrePdf(EpcBaseDocTemplate):
"""
PDF for periods during semesters
"""
def __init__(self, filename):
super().__init__(filename, 'Filière EDS', 'Périodes de formation')
self.six_semester_template_page()
def produce(self, context):
for sem in range(1, 7):
modules = [m for m in context['modules'] if getattr(m, 'sem{0}'.format(sem))]
total = context['tot{0}'.format(str(sem))]
data = [['Semestre {0}'.format(sem), '{0} h.'.format(total)]]
for line in modules:
value = getattr(line, 'sem{0}'.format(sem))
data.append([line.nom, '{0} h.'.format(value)])
t = Table(data=data, colWidths=[6 * cm, 1 * cm], spaceBefore=0 * cm, spaceAfter=0.5 * cm, hAlign=TA_LEFT,
style=[
('ALIGN', (0, 0), (0, -1), 'LEFT'),
('ALIGN', (1, 0), (1, -1), 'RIGHT'),
('LINEBELOW', (0, 0), (1, 0), 1, colors.black),
('SIZE', (0, 0), (-1, -1), 8),
('FONT', (0, 0), (-1, 0), 'Helvetica-Bold'),
]
)
self.story.append(t)
self.story.append(FrameBreak())
self.story.append(Paragraph('Total de la formation: {0} heures'.format(context['tot']), style_bold))
self.build(self.story)