637 lines
25 KiB
Python
637 lines
25 KiB
Python
|
from io import BytesIO
|
|||
|
|
|||
|
import extract_msg
|
|||
|
import nh3
|
|||
|
|
|||
|
from bs4 import BeautifulSoup
|
|||
|
from pypdf import PdfWriter
|
|||
|
|
|||
|
from functools import partial
|
|||
|
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
|
|||
|
from reportlab.lib.pagesizes import A4
|
|||
|
from reportlab.lib import colors
|
|||
|
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
|
|||
|
from reportlab.lib.units import cm
|
|||
|
from reportlab.pdfgen import canvas
|
|||
|
from reportlab.platypus import (
|
|||
|
PageBreak, Paragraph, Preformatted, SimpleDocTemplate, Spacer, Table, TableStyle
|
|||
|
)
|
|||
|
|
|||
|
from django.contrib.staticfiles.finders import find
|
|||
|
from django.utils.text import slugify
|
|||
|
|
|||
|
from .utils import format_d_m_Y, format_duree, format_Ymd
|
|||
|
|
|||
|
|
|||
|
def format_booleen(val):
|
|||
|
return '?' if val is None else ('oui' if val else 'non')
|
|||
|
|
|||
|
|
|||
|
class PageNumCanvas(canvas.Canvas):
|
|||
|
"""A special canvas to be able to draw the total page number in the footer."""
|
|||
|
def __init__(self, *args, **kwargs):
|
|||
|
super().__init__(*args, **kwargs)
|
|||
|
self._pages = []
|
|||
|
|
|||
|
def showPage(self):
|
|||
|
self._pages.append(dict(self.__dict__))
|
|||
|
self._startPage()
|
|||
|
|
|||
|
def save(self):
|
|||
|
page_count = len(self._pages)
|
|||
|
for page in self._pages:
|
|||
|
self.__dict__.update(page)
|
|||
|
self.draw_page_number(page_count)
|
|||
|
canvas.Canvas.showPage(self)
|
|||
|
super().save()
|
|||
|
|
|||
|
def draw_page_number(self, page_count):
|
|||
|
self.setFont("Helvetica", 9)
|
|||
|
self.drawRightString(self._pagesize[0] - 1.6*cm, 2.3*cm, "p. %s/%s" % (self._pageNumber, page_count))
|
|||
|
|
|||
|
|
|||
|
class CleanParagraph(Paragraph):
|
|||
|
"""If the (HTML) text cannot be parsed, try to clean it."""
|
|||
|
def __init__(self, text, *args, **kwargs):
|
|||
|
if text:
|
|||
|
text = text.replace('</p>', '</p><br/>')
|
|||
|
if '<ul>' in text:
|
|||
|
text = text.replace('<li>', ' • ').replace('</ul>','<br>').replace('</li>', '<br>')
|
|||
|
try:
|
|||
|
super().__init__(text, *args, **kwargs)
|
|||
|
except ValueError:
|
|||
|
text = nh3.clean(
|
|||
|
text, tags={'p', 'br', 'b', 'strong', 'u', 'i', 'em', 'ul', 'li'}
|
|||
|
).replace('<br>', '<br/>')
|
|||
|
super().__init__(text, *args, **kwargs)
|
|||
|
|
|||
|
|
|||
|
class RawParagraph(Paragraph):
|
|||
|
"""Raw text, replace new lines by <br/>."""
|
|||
|
def __init__(self, text='', *args, **kwargs):
|
|||
|
if text:
|
|||
|
text = text.replace('\r\n', '\n').replace('\n', '\n<br/>')
|
|||
|
super().__init__(text, *args, **kwargs)
|
|||
|
|
|||
|
|
|||
|
class StyleMixin:
|
|||
|
FONTSIZE = 9
|
|||
|
MAXLINELENGTH = 120
|
|||
|
|
|||
|
def __init__(self, base_font='Helvetica', **kwargs):
|
|||
|
self.styles = getSampleStyleSheet()
|
|||
|
self.style_title = ParagraphStyle(
|
|||
|
name='title', fontName='Helvetica-Bold', fontSize=self.FONTSIZE + 4,
|
|||
|
leading=self.FONTSIZE + 5, alignment=TA_CENTER
|
|||
|
)
|
|||
|
self.style_normal = ParagraphStyle(
|
|||
|
name='normal', fontName='Helvetica', fontSize=self.FONTSIZE, alignment=TA_LEFT,
|
|||
|
leading=self.FONTSIZE + 1, spaceAfter=0
|
|||
|
)
|
|||
|
self.style_justifie = ParagraphStyle(
|
|||
|
name='justifie', parent=self.style_normal, alignment=TA_JUSTIFY, spaceAfter=0.2 * cm
|
|||
|
)
|
|||
|
self.style_sub_title = ParagraphStyle(
|
|||
|
name='sous_titre', fontName='Helvetica-Bold', fontSize=self.FONTSIZE + 2,
|
|||
|
alignment=TA_LEFT, spaceBefore=0.5 * cm, spaceAfter=0.1 * cm
|
|||
|
)
|
|||
|
self.style_inter_title = ParagraphStyle(
|
|||
|
name='inter_titre', fontName='Helvetica-Bold', fontSize=self.FONTSIZE + 1,
|
|||
|
alignment=TA_LEFT, spaceBefore=0.3 * cm, spaceAfter=0
|
|||
|
)
|
|||
|
self.style_bold = ParagraphStyle(
|
|||
|
name='bold', fontName='Helvetica-Bold', fontSize=self.FONTSIZE, leading=self.FONTSIZE + 1
|
|||
|
)
|
|||
|
self.style_italic = ParagraphStyle(
|
|||
|
name='italic', fontName='Helvetica-Oblique', fontSize=self.FONTSIZE - 1, leading=self.FONTSIZE
|
|||
|
)
|
|||
|
self.style_indent = ParagraphStyle(
|
|||
|
name='indent', fontName='Helvetica', fontSize=self.FONTSIZE, alignment=TA_LEFT,
|
|||
|
leftIndent=1 * cm
|
|||
|
)
|
|||
|
super().__init__(**kwargs)
|
|||
|
|
|||
|
|
|||
|
class HeaderFooterMixin:
|
|||
|
LOGO = find('img/logo-cr.png')
|
|||
|
EDUQUA = find('img/eduqua.png')
|
|||
|
DON = find('img/logo-zewo.png')
|
|||
|
|
|||
|
def draw_header(self, canvas, doc):
|
|||
|
canvas.saveState()
|
|||
|
canvas.drawImage(
|
|||
|
self.LOGO, doc.leftMargin + 316, doc.height+60, 7 * cm, 1.6 * cm, preserveAspectRatio=True, mask='auto'
|
|||
|
)
|
|||
|
canvas.restoreState()
|
|||
|
|
|||
|
def draw_footer(self, canvas, doc):
|
|||
|
canvas.saveState()
|
|||
|
canvas.drawImage(
|
|||
|
self.EDUQUA, doc.leftMargin, doc.height - 670, 1.8 * cm, 0.8 * cm, preserveAspectRatio=True
|
|||
|
)
|
|||
|
canvas.drawImage(
|
|||
|
self.DON, doc.leftMargin + 60, doc.height - 670, 2.5 * cm, 0.8 * cm, preserveAspectRatio=True, mask='auto'
|
|||
|
)
|
|||
|
tab = [220, 365]
|
|||
|
line = [658, 667, 676, 685]
|
|||
|
|
|||
|
canvas.setFont("Helvetica", 8)
|
|||
|
canvas.drawRightString(doc.leftMargin + tab[0], doc.height - line[2], "CCP ?")
|
|||
|
canvas.drawRightString(doc.leftMargin + tab[0], doc.height - line[3], "IBAN ?")
|
|||
|
canvas.setLineWidth(0.5)
|
|||
|
canvas.line(doc.leftMargin + 230, 2.2 * cm, doc.leftMargin + 230, 1.0 * cm)
|
|||
|
|
|||
|
canvas.drawRightString(doc.leftMargin + tab[1], doc.height - line[0], "Rte d’Englisberg 3")
|
|||
|
canvas.setFont("Helvetica-Bold", 8)
|
|||
|
canvas.drawRightString(doc.leftMargin + tab[1], doc.height - line[1], "1763 Granges-Paccot")
|
|||
|
canvas.setFont("Helvetica", 8)
|
|||
|
canvas.line(doc.leftMargin + 375, 2.2 * cm, doc.leftMargin + 375, 1.0 * cm)
|
|||
|
canvas.drawRightString(doc.leftMargin + doc.width, doc.height - line[0], "+41 26 407 70 44")
|
|||
|
canvas.drawRightString(doc.leftMargin + doc.width, doc.height - line[1], "secretariat@fondation-transit.ch")
|
|||
|
canvas.drawRightString(doc.leftMargin + doc.width, doc.height - line[2], "fondation-transit.ch/aemo")
|
|||
|
canvas.restoreState()
|
|||
|
|
|||
|
|
|||
|
class BasePDF(HeaderFooterMixin, StyleMixin):
|
|||
|
|
|||
|
def __init__(self, tampon, instance, **kwargs):
|
|||
|
self.instance = instance
|
|||
|
self.kwargs = kwargs
|
|||
|
self.doc = SimpleDocTemplate(
|
|||
|
tampon, title=self.title, pagesize=A4,
|
|||
|
leftMargin=1.5 * cm, rightMargin=1.5 * cm, topMargin=2 * cm, bottomMargin=2.5 * cm
|
|||
|
)
|
|||
|
self.story = []
|
|||
|
super().__init__(**kwargs)
|
|||
|
|
|||
|
def draw_header_footer(self, canvas, doc):
|
|||
|
self.draw_header(canvas, doc)
|
|||
|
self.draw_footer(canvas, doc)
|
|||
|
|
|||
|
def produce(self):
|
|||
|
# subclass should call self.doc.build(self.story, onFirstPage=self.draw_header_footer)
|
|||
|
raise NotImplementedError
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
raise NotImplementedError
|
|||
|
|
|||
|
@staticmethod
|
|||
|
def format_note(note, length=StyleMixin.MAXLINELENGTH):
|
|||
|
return Preformatted(note.replace('\r\n', '\n'), maxLineLength=length)
|
|||
|
|
|||
|
def set_title(self, title=''):
|
|||
|
self.story.append(Spacer(0, 1 * cm))
|
|||
|
self.story.append(Paragraph(title, self.style_title))
|
|||
|
self.story.append(Spacer(0, 1.2 * cm))
|
|||
|
|
|||
|
def parent_data(self, person):
|
|||
|
"""Return parent data ready to be used in a 2-column table."""
|
|||
|
parents = person.parents()
|
|||
|
par1 = parents[0] if len(parents) > 0 else None
|
|||
|
par2 = parents[1] if len(parents) > 1 else None
|
|||
|
data = [
|
|||
|
[('Parent 1 (%s)' % par1.role.nom) if par1 else '-',
|
|||
|
('Parent 2 (%s)' % par2.role.nom) if par2 else '-'],
|
|||
|
[par1.contact.nom_prenom if par1 else '', par2.contact.nom_prenom if par2 else ''],
|
|||
|
[par1.contact.adresse if par1 else '', par2.contact.adresse if par2 else ''],
|
|||
|
[par1.contact.contact if par1 else '', par2.contact.contact if par2 else ''],
|
|||
|
|
|||
|
['Autorité parentale: {}'.format(format_booleen(par1.contact.autorite_parentale)) if par1 else '',
|
|||
|
'Autorité parentale: {}'.format(format_booleen(par2.contact.autorite_parentale)) if par2 else ''],
|
|||
|
]
|
|||
|
return data
|
|||
|
|
|||
|
def formate_persons(self, parents_list):
|
|||
|
labels = (
|
|||
|
("Nom", "nom"), ("Prénom", "prenom"), ("Adresse", "rue"),
|
|||
|
("Localité", 'localite_display'), ("Profession", 'profession'), ("Tél.", 'telephone'),
|
|||
|
("Courriel", 'email'), ('Remarque', 'remarque'),
|
|||
|
)
|
|||
|
P = partial(Paragraph, style=self.style_normal)
|
|||
|
Pbold = partial(Paragraph, style=self.style_bold)
|
|||
|
data = []
|
|||
|
parents = [parent for parent in parents_list if parent is not None]
|
|||
|
if len(parents) == 0:
|
|||
|
pass
|
|||
|
elif len(parents) == 1:
|
|||
|
data.append([Pbold('Rôle:'), Pbold(parents[0].role.nom), '', ''])
|
|||
|
for label in labels:
|
|||
|
data.append([
|
|||
|
label[0], P(getattr(parents[0], label[1])), '', ''
|
|||
|
])
|
|||
|
elif len(parents) == 2:
|
|||
|
data.append([
|
|||
|
Pbold('Rôle:'), Pbold(parents[0].role.nom),
|
|||
|
Pbold('Rôle:'), Pbold(parents[1].role.nom)
|
|||
|
])
|
|||
|
for label in labels:
|
|||
|
data.append([
|
|||
|
label[0], P(getattr(parents[0], label[1]) if parents[0] else ''),
|
|||
|
label[0], P(getattr(parents[1], label[1]) if parents[1] else '')
|
|||
|
])
|
|||
|
return data
|
|||
|
|
|||
|
def get_table(self, data, columns, before=0.0, after=0.0, inter=None):
|
|||
|
"""Prepare a Table instance with data and columns, with a common style."""
|
|||
|
if inter:
|
|||
|
inter = inter * cm
|
|||
|
cols = [c * cm for c in columns]
|
|||
|
|
|||
|
t = Table(
|
|||
|
data=data, colWidths=cols, hAlign=TA_LEFT,
|
|||
|
spaceBefore=before * cm, spaceAfter=after * cm
|
|||
|
)
|
|||
|
t.hAlign = 0
|
|||
|
t.setStyle(tblstyle=TableStyle([
|
|||
|
('ALIGN', (0, 0), (-1, -1), 'LEFT'),
|
|||
|
('FONTSIZE', (0, 0), (-1, -1), self.FONTSIZE),
|
|||
|
('VALIGN', (0, 0), (-1, -1), "TOP"),
|
|||
|
('LEFTPADDING', (0, 0), (0, -1), 1),
|
|||
|
('LEADING', (0, 0), (-1, -1), 7),
|
|||
|
]))
|
|||
|
return t
|
|||
|
|
|||
|
def _add_subtitle(self, data):
|
|||
|
t = Table(
|
|||
|
data=[data], colWidths=[18 * cm / len(data)] * len(data),
|
|||
|
hAlign=TA_LEFT,
|
|||
|
spaceBefore=0.2 * cm, spaceAfter=0.5 * cm
|
|||
|
)
|
|||
|
t.hAlign = 0
|
|||
|
t.setStyle(tblstyle=TableStyle([
|
|||
|
('FONT', (0, 0), (-1, -1), "Helvetica-Bold"),
|
|||
|
('FONTSIZE', (0, 0), (-1, -1), self.FONTSIZE + 2),
|
|||
|
('LINEBELOW', (0, -1), (-1, -1), 0.25, colors.black),
|
|||
|
('ALIGN', (-1, -1), (-1, -1), 'RIGHT'),
|
|||
|
]))
|
|||
|
self.story.append(t)
|
|||
|
|
|||
|
def write_paragraph(self, title, text, html=False, justifie=False):
|
|||
|
if title:
|
|||
|
self.story.append(Paragraph(title, self.style_sub_title))
|
|||
|
style = self.style_justifie if justifie else self.style_normal
|
|||
|
if html:
|
|||
|
if text.startswith('<p>') and text.endswith('</p>'):
|
|||
|
soup = BeautifulSoup(text, features="html5lib")
|
|||
|
for tag in soup.find_all(['p', 'ul']):
|
|||
|
self.story.append(CleanParagraph(str(tag), style))
|
|||
|
else:
|
|||
|
self.story.append(CleanParagraph(text, style))
|
|||
|
else:
|
|||
|
self.story.append(RawParagraph(text, style))
|
|||
|
|
|||
|
def enfant_data(self, enfant):
|
|||
|
labels = [('Tél.', enfant.telephone)]
|
|||
|
if hasattr(enfant, 'formation'):
|
|||
|
labels.extend([
|
|||
|
('Statut scol', enfant.formation.get_statut_display()),
|
|||
|
('Centre', enfant.formation.cercle_scolaire),
|
|||
|
('Collège', enfant.formation.college),
|
|||
|
('Classe', enfant.formation.classe),
|
|||
|
('Struct. extra-fam.', enfant.formation.creche),
|
|||
|
('Ens.', enfant.formation.enseignant),
|
|||
|
])
|
|||
|
labels.extend([
|
|||
|
('Permis de séjour', enfant.permis),
|
|||
|
('Validité', enfant.validite),
|
|||
|
])
|
|||
|
row = [f"{enfant.nom_prenom} (*{format_d_m_Y(enfant.date_naissance)})"]
|
|||
|
for label in labels:
|
|||
|
if label[1]:
|
|||
|
row.append(f"{label[0]}: {label[1]}")
|
|||
|
return '; '.join(row)
|
|||
|
|
|||
|
|
|||
|
class DemandeAccompagnement(BasePDF):
|
|||
|
title = None
|
|||
|
|
|||
|
def produce(self):
|
|||
|
famille = self.instance
|
|||
|
suivi = famille.suivi
|
|||
|
self.set_title("Famille {} - {}".format(famille.nom, self.title))
|
|||
|
|
|||
|
self.story.append(Paragraph("<strong>Motif(s) de la demande:</strong> {}".format(
|
|||
|
suivi.get_motif_demande_display()), self.style_normal
|
|||
|
))
|
|||
|
self.story.append(Paragraph('_' * 90, self.style_normal))
|
|||
|
|
|||
|
self.write_paragraph("Dates", suivi.dates_demande)
|
|||
|
self.write_paragraph(
|
|||
|
"Difficultés",
|
|||
|
"{}<br/>{}<br/><br/>{}".format(
|
|||
|
"<em>Quelles sont les difficultés éducatives que vous rencontrez et depuis combien de "
|
|||
|
"temps ?",
|
|||
|
"Fonctionnement familial: règles, coucher, lever, repas, jeux, relations "
|
|||
|
"parent-enfants, rapport au sein de la fratrie, … (exemple)</em>",
|
|||
|
suivi.difficultes
|
|||
|
),
|
|||
|
html=True
|
|||
|
)
|
|||
|
self.write_paragraph(
|
|||
|
"Autres services",
|
|||
|
"<em>{}</em><br/>{}".format(
|
|||
|
"Avez-vous fait appel à d'autres services ? Si oui, avec quels vécus ?",
|
|||
|
suivi.autres_contacts
|
|||
|
),
|
|||
|
html=True
|
|||
|
)
|
|||
|
self.write_paragraph("Aides souhaitées", suivi.aides, html=True)
|
|||
|
self.write_paragraph("Ressources/Compétences", suivi.competences, html=True)
|
|||
|
self.write_paragraph("Disponibilités", suivi.disponibilites, html=True)
|
|||
|
self.write_paragraph("Remarques", suivi.remarque)
|
|||
|
|
|||
|
self.doc.build(self.story, onFirstPage=self.draw_header_footer)
|
|||
|
|
|||
|
|
|||
|
class JournalPdf(BasePDF):
|
|||
|
title = "Journal de bord"
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
return '{}_journal.pdf'.format(slugify(self.instance.nom))
|
|||
|
|
|||
|
def get_title(self):
|
|||
|
return 'Famille {} - {}'.format(self.instance.nom, self.title)
|
|||
|
|
|||
|
def produce(self):
|
|||
|
famille = self.instance
|
|||
|
self.set_title(self.get_title())
|
|||
|
|
|||
|
self.style_bold.spaceAfter = 0.2*cm
|
|||
|
self.style_italic.spaceBefore = 0.2 * cm
|
|||
|
self.style_italic.spaceAfter = 0.7 * cm
|
|||
|
for prest in famille.prestations.all().prefetch_related('intervenants'):
|
|||
|
self.story.append(CleanParagraph(prest.texte, self.style_normal))
|
|||
|
self.story.append(
|
|||
|
Paragraph('{} - {} ({})'.format(
|
|||
|
'/'.join(interv.sigle for interv in prest.intervenants.all()),
|
|||
|
format_d_m_Y(prest.date_prestation),
|
|||
|
format_duree(prest.duree)
|
|||
|
), self.style_italic)
|
|||
|
)
|
|||
|
|
|||
|
self.doc.build(
|
|||
|
self.story,
|
|||
|
onFirstPage=self.draw_header_footer, onLaterPages=self.draw_footer,
|
|||
|
canvasmaker=PageNumCanvas
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
class RapportPdf(BasePDF):
|
|||
|
title = None
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
return "{}_resume_{}.pdf".format(
|
|||
|
slugify(self.instance.famille.nom),
|
|||
|
format_Ymd(self.instance.date)
|
|||
|
)
|
|||
|
|
|||
|
def produce(self):
|
|||
|
rapport = self.instance
|
|||
|
self.style_normal.fontSize += 2
|
|||
|
self.style_normal.leading += 2
|
|||
|
self.style_justifie.fontSize += 2
|
|||
|
self.style_justifie.leading += 2
|
|||
|
self.doc.title = 'Résumé "AEMO"'
|
|||
|
self.set_title('Famille {} - {}'.format(rapport.famille.nom, self.doc.title))
|
|||
|
self._add_subtitle([f"Date: {format_d_m_Y(rapport.date)}"])
|
|||
|
|
|||
|
data = [
|
|||
|
("Enfant(s)", '<br/>'.join(
|
|||
|
[f"{enfant.nom_prenom} (*{format_d_m_Y(enfant.date_naissance)})"
|
|||
|
for enfant in rapport.famille.membres_suivis()]), False),
|
|||
|
("Intervenant-e-s", ', '.join([i.nom_prenom for i in rapport.intervenants()]), False),
|
|||
|
("Début du suivi", format_d_m_Y(rapport.famille.suivi.date_debut_suivi), False),
|
|||
|
("Situation / contexte familial", rapport.situation, True),
|
|||
|
]
|
|||
|
data.append(("Observations", rapport.observations, True))
|
|||
|
data.append(("Perspectives d'avenir", rapport.projet, True))
|
|||
|
|
|||
|
for title, text, html in data:
|
|||
|
self.write_paragraph(title, text, html=html, justifie=True)
|
|||
|
|
|||
|
if hasattr(rapport, 'sig_interv'):
|
|||
|
self.story.append(Spacer(0, 0.5 * cm))
|
|||
|
|
|||
|
for idx, interv in enumerate(rapport.sig_interv.all()):
|
|||
|
if idx == 0:
|
|||
|
self.write_paragraph("Signature des intervenant-e-s :", '')
|
|||
|
self.story.append(Spacer(0, 0.2 * cm))
|
|||
|
self.write_paragraph('', interv.nom_prenom + (f', {interv.profession}' if interv.profession else ''))
|
|||
|
self.story.append(Spacer(0, 1 * cm))
|
|||
|
|
|||
|
secret_style = ParagraphStyle(
|
|||
|
name='italic', fontName='Helvetica-Oblique', fontSize=self.FONTSIZE - 1, leading=self.FONTSIZE + 1,
|
|||
|
backColor=colors.Color(0.96, 0.96, 0.96, 1), borderRadius=12,
|
|||
|
)
|
|||
|
self.story.append(Paragraph(
|
|||
|
"Le présent résumé comporte des éléments <b>couverts par le secret professionnel au sens "
|
|||
|
"de la LPSy et du Code pénal</b>. Seuls les propriétaires des données, à savoir les membres "
|
|||
|
"de la famille faisant l’objet du résumé, peuvent <b>ensemble</b> lever ce secret ou "
|
|||
|
"accepter la divulgation des données. Si cette autorisation n’est pas donnée, l’autorité "
|
|||
|
"compétente en matière de levée du secret professionnel doit impérativement être saisie.",
|
|||
|
secret_style
|
|||
|
))
|
|||
|
|
|||
|
self.doc.build(self.story, onFirstPage=self.draw_header_footer, onLaterPages=self.draw_footer)
|
|||
|
|
|||
|
|
|||
|
class MessagePdf(BasePDF):
|
|||
|
title = 'Message'
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
return '{}_message.pdf'.format(slugify(self.instance.subject))
|
|||
|
|
|||
|
def produce(self):
|
|||
|
doc = self.instance
|
|||
|
self.set_title('{} - Famille {}'.format(self.title, doc.famille.nom))
|
|||
|
with extract_msg.Message(doc.fichier.path) as msg:
|
|||
|
P = partial(Paragraph, style=self.style_normal)
|
|||
|
Pbold = partial(Paragraph, style=self.style_bold)
|
|||
|
|
|||
|
msg_headers = [
|
|||
|
[Pbold('De:'), P(msg.sender)],
|
|||
|
[Pbold('À:'), P(msg.to)],
|
|||
|
]
|
|||
|
if msg.cc:
|
|||
|
msg_headers.append([Pbold('CC:'), P(msg.cc)])
|
|||
|
if msg.date:
|
|||
|
msg_headers.append([Pbold('Date:'), P(msg.date)])
|
|||
|
msg_headers.append([Pbold('Sujet:'), P(msg.subject)])
|
|||
|
self.story.append(self.get_table(msg_headers, [3, 15]))
|
|||
|
self.story.append(Pbold('Message:'))
|
|||
|
self.story.append(RawParagraph(msg.body, style=self.style_normal))
|
|||
|
return self.doc.build(
|
|||
|
self.story,
|
|||
|
onFirstPage=self.draw_header_footer, onLaterPages=self.draw_footer,
|
|||
|
canvasmaker=PageNumCanvas
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
class EvaluationPdf:
|
|||
|
def __init__(self, tampon, famille):
|
|||
|
self.tampon = tampon
|
|||
|
self.famille = famille
|
|||
|
self.merger = PdfWriter()
|
|||
|
|
|||
|
def append_pdf(self, PDFClass):
|
|||
|
tampon = BytesIO()
|
|||
|
pdf = PDFClass(tampon, self.famille)
|
|||
|
pdf.produce()
|
|||
|
self.merger.append(tampon)
|
|||
|
|
|||
|
def produce(self):
|
|||
|
|
|||
|
self.append_pdf(CoordonneesPagePdf)
|
|||
|
self.append_pdf(DemandeAccompagnementPagePdf)
|
|||
|
self.merger.write(self.tampon)
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
return '{}_aemo_evaluation.pdf'.format(slugify(self.famille.nom))
|
|||
|
|
|||
|
|
|||
|
class CoordonneesFamillePdf(BasePDF):
|
|||
|
title = "Informations"
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
return '{}_coordonnees.pdf'.format(slugify(self.instance.nom))
|
|||
|
|
|||
|
def produce(self):
|
|||
|
famille = self.instance
|
|||
|
suivi = famille.suivi
|
|||
|
self.set_title('Famille {} - {}'.format(famille.nom, self.title))
|
|||
|
|
|||
|
# Parents
|
|||
|
self.story.append(Paragraph('Parents', self.style_sub_title))
|
|||
|
data = self.formate_persons(famille.parents())
|
|||
|
if data:
|
|||
|
self.story.append(self.get_table(data, columns=[2, 7, 2, 7], before=0, after=0))
|
|||
|
else:
|
|||
|
self.story.append(Paragraph('-', self.style_normal))
|
|||
|
|
|||
|
# Situation matrimoniale
|
|||
|
data = [
|
|||
|
['Situation matrimoniale: {}'.format(famille.get_statut_marital_display()),
|
|||
|
'Autorité parentale: {}'.format(famille.get_autorite_parentale_display())],
|
|||
|
]
|
|||
|
self.story.append(self.get_table(data, columns=[9, 9], before=0, after=0))
|
|||
|
|
|||
|
# Personnes significatives
|
|||
|
autres_parents = list(famille.autres_parents())
|
|||
|
if autres_parents:
|
|||
|
self.story.append(Paragraph('Personne-s significative-s', self.style_sub_title))
|
|||
|
data = self.formate_persons(autres_parents)
|
|||
|
self.story.append(self.get_table(data, columns=[2, 7, 3, 6], before=0, after=0))
|
|||
|
if len(autres_parents) > 2:
|
|||
|
self.story.append(PageBreak())
|
|||
|
|
|||
|
# Enfants suivis
|
|||
|
self.write_paragraph(
|
|||
|
"Enfant(s)",
|
|||
|
'<br/>'.join(self.enfant_data(enfant) for enfant in famille.membres_suivis())
|
|||
|
)
|
|||
|
# Réseau
|
|||
|
self.story.append(Paragraph("Réseau", self.style_sub_title))
|
|||
|
data = [
|
|||
|
['AS OPE', '{} - (Mandat: {})'.format(
|
|||
|
', '.join(ope.nom_prenom for ope in suivi.ope_referents),
|
|||
|
suivi.get_mandat_ope_display()
|
|||
|
)],
|
|||
|
['Interv. CRNE', '{}'.format(
|
|||
|
', '.join('{}'.format(i.nom_prenom) for i in suivi.intervenants.all().distinct())
|
|||
|
)]
|
|||
|
]
|
|||
|
for enfant in famille.membres_suivis():
|
|||
|
for contact in enfant.reseaux.all():
|
|||
|
data.append([
|
|||
|
enfant.prenom,
|
|||
|
'{} ({})'.format(contact, contact.contact)
|
|||
|
])
|
|||
|
self.story.append(self.get_table(data, columns=[2, 16], before=0, after=0))
|
|||
|
|
|||
|
self.write_paragraph("Motif de la demande", famille.suivi.motif_detail)
|
|||
|
self.write_paragraph("Collaborations", famille.suivi.collaboration)
|
|||
|
|
|||
|
# Historique
|
|||
|
self.story.append(Paragraph('Historique', self.style_sub_title))
|
|||
|
P = partial(Paragraph, style=self.style_normal)
|
|||
|
Pbold = partial(Paragraph, style=self.style_bold)
|
|||
|
fields = ['date_demande', 'date_debut_evaluation', 'date_fin_evaluation',
|
|||
|
'date_debut_suivi', 'date_fin_suivi']
|
|||
|
data = []
|
|||
|
for field_name in fields:
|
|||
|
field = famille.suivi._meta.get_field(field_name)
|
|||
|
if getattr(famille.suivi, field_name):
|
|||
|
data.append(
|
|||
|
[Pbold(f"{field.verbose_name} :"), P(format_d_m_Y(getattr(famille.suivi, field_name)))]
|
|||
|
)
|
|||
|
if famille.suivi.motif_fin_suivi:
|
|||
|
data.append([Pbold("Motif de fin de suivi :"), famille.suivi.get_motif_fin_suivi_display()])
|
|||
|
if famille.destination:
|
|||
|
data.append([Pbold("Destination :"), famille.get_destination_display()])
|
|||
|
if famille.archived_at:
|
|||
|
data.append([Pbold("Date d'archivage :"), format_d_m_Y(famille.archived_at)])
|
|||
|
|
|||
|
self.story.append(self.get_table(data, [4, 5]))
|
|||
|
self.doc.build(self.story, onFirstPage=self.draw_header_footer)
|
|||
|
|
|||
|
|
|||
|
class DemandeAccompagnementPagePdf(DemandeAccompagnement):
|
|||
|
title = "Évaluation AEMO"
|
|||
|
|
|||
|
|
|||
|
class BilanPdf(BasePDF):
|
|||
|
title = "Bilan AEMO"
|
|||
|
|
|||
|
def get_filename(self):
|
|||
|
return "{}_bilan_{}.pdf".format(
|
|||
|
slugify(self.instance.famille.nom),
|
|||
|
format_Ymd(self.instance.date)
|
|||
|
)
|
|||
|
|
|||
|
def produce(self):
|
|||
|
bilan = self.instance
|
|||
|
self.style_normal.fontSize += 2
|
|||
|
self.style_normal.leading += 2
|
|||
|
self.style_justifie.fontSize += 2
|
|||
|
self.style_justifie.leading += 2
|
|||
|
self.set_title('Famille {} - {}'.format(bilan.famille.nom, self.title))
|
|||
|
self._add_subtitle([f"Date: {format_d_m_Y(bilan.date)}"])
|
|||
|
|
|||
|
for title, text, html in (
|
|||
|
("Enfant(s)", '<br/>'.join(
|
|||
|
[f"{enfant.nom_prenom} (*{format_d_m_Y(enfant.date_naissance)})"
|
|||
|
for enfant in bilan.famille.membres_suivis()]), False),
|
|||
|
("Intervenant-e-s", ', '.join(
|
|||
|
[i.intervenant.nom_prenom for i in bilan.famille.interventions_actives(bilan.date)]
|
|||
|
), False),
|
|||
|
("Début du suivi", format_d_m_Y(bilan.famille.suivi.date_debut_suivi), False),
|
|||
|
("Besoins et objectifs", bilan.objectifs, True),
|
|||
|
("Rythme et fréquence", bilan.rythme, True),
|
|||
|
):
|
|||
|
self.write_paragraph(title, text, html=html, justifie=True)
|
|||
|
|
|||
|
self.story.append(Spacer(0, 0.5 * cm))
|
|||
|
|
|||
|
for idx, interv in enumerate(bilan.sig_interv.all()):
|
|||
|
if idx == 0:
|
|||
|
self.write_paragraph("Signature des intervenant-e-s AEMO :", '')
|
|||
|
self.story.append(Spacer(0, 0.2 * cm))
|
|||
|
self.write_paragraph('', interv.nom_prenom + (f', {interv.profession}' if interv.profession else ''))
|
|||
|
self.story.append(Spacer(0, 1 * cm))
|
|||
|
|
|||
|
if bilan.sig_famille:
|
|||
|
self.write_paragraph("Signature de la famille :", '')
|
|||
|
|
|||
|
self.doc.build(self.story, onFirstPage=self.draw_header_footer, onLaterPages=self.draw_footer)
|
|||
|
|
|||
|
|
|||
|
class CoordonneesPagePdf(CoordonneesFamillePdf):
|
|||
|
title = "Informations"
|
|||
|
|
|||
|
def get_filename(self, famille):
|
|||
|
return '{}_aemo_evaluation.pdf'.format(slugify(famille.nom))
|
|||
|
|
|||
|
|
|||
|
def str_or_empty(value):
|
|||
|
return '' if not value else str(value)
|