ScoDocMM/app/scodoc/sco_bulletins_pdf.py

276 lines
9.0 KiB
Python
Raw Permalink Normal View History

2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2021-01-01 17:51:08 +01:00
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""Génération des bulletins de notes en format PDF
On peut installer plusieurs classes générant des bulletins de formats différents.
La préférence (par semestre) 'bul_pdf_class_name' conserve le nom de la classe Python
utilisée pour générer les bulletins en PDF. Elle doit être une sous-classe de PDFBulletinGenerator
et définir les méthodes fabriquant les éléments PDF:
gen_part_title
gen_table
gen_part_below
gen_signatures
Les éléments PDF sont des objets PLATYPUS de la bibliothèque Reportlab.
Voir la documentation (Reportlab's User Guide), chapitre 5 et suivants.
Pour définir un nouveau type de bulletin:
- créer un fichier source sco_bulletins_pdf_xxxx.py xxxx est le nom (court) de votre type;
- dans ce fichier, sous-classer PDFBulletinGenerator ou PDFBulletinGeneratorDefault
(s'inspirer de sco_bulletins_pdf_default);
- en fin du fichier sco_bulletins_pdf.py, ajouter la ligne
import sco_bulletins_pdf_xxxx
- votre type sera alors (après redémarrage de ScoDoc) proposé dans le formulaire de paramètrage ScoDoc.
Chaque semestre peut si nécessaire utiliser un type de bulletin différent.
"""
2021-02-01 16:23:11 +01:00
import time
import traceback
import re
2021-06-17 00:08:37 +02:00
import os
2021-07-11 23:02:35 +02:00
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
2021-02-01 16:23:11 +01:00
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
2020-09-26 16:19:37 +02:00
from app.scodoc import VERSION
import app.scodoc.sco_utils as scu
from app.scodoc.notes_log import log
2021-07-19 19:53:01 +02:00
from app.scodoc import sco_cache
from app.scodoc import sco_formsemestre
from app.scodoc import sco_pdf
from app.scodoc import sco_preferences
from app.scodoc import sco_etud
2020-09-26 16:19:37 +02:00
def pdfassemblebulletins(
formsemestre_id,
objects,
bul_title,
infos,
pagesbookmarks,
filigranne=None,
server_name="",
context=None,
):
"generate PDF document from a list of PLATYPUS objects"
if not objects:
return ""
# Paramètres de mise en page
margins = (
sco_preferences.get_preference("left_margin", formsemestre_id),
sco_preferences.get_preference("top_margin", formsemestre_id),
sco_preferences.get_preference("right_margin", formsemestre_id),
sco_preferences.get_preference("bottom_margin", formsemestre_id),
2020-09-26 16:19:37 +02:00
)
2021-07-11 23:02:35 +02:00
report = StringIO.StringIO() # in-memory document, no disk file
2020-09-26 16:19:37 +02:00
document = BaseDocTemplate(report)
document.addPageTemplates(
2021-02-01 16:23:11 +01:00
sco_pdf.ScolarsPageTemplate(
2020-09-26 16:19:37 +02:00
document,
2021-01-04 11:31:18 +01:00
context=context,
2021-02-01 16:23:11 +01:00
author="%s %s (E. Viennet)" % (VERSION.SCONAME, VERSION.SCOVERSION),
2020-09-26 16:19:37 +02:00
title="Bulletin %s" % bul_title,
subject="Bulletin de note",
server_name=server_name,
margins=margins,
pagesbookmarks=pagesbookmarks,
filigranne=filigranne,
preferences=sco_preferences.SemPreferences(formsemestre_id),
2020-09-26 16:19:37 +02:00
)
)
document.build(objects)
data = report.getvalue()
return data
def process_field(
context, field, cdict, style, suppress_empty_pars=False, format="pdf"
):
"""Process a field given in preferences, returns
- if format = 'pdf': a list of Platypus objects
- if format = 'html' : a string
2020-10-21 22:56:25 +02:00
Substitutes all %()s markup
2020-09-26 16:19:37 +02:00
Remove potentialy harmful <img> tags
Replaces <logo name="header" width="xxx" height="xxx">
by <img src=".../logos/logo_header" width="xxx" height="xxx">
2020-10-21 22:56:25 +02:00
If format = 'html', replaces <para> by <p>. HTML does not allow logos.
2020-09-26 16:19:37 +02:00
"""
try:
2021-02-01 16:23:11 +01:00
text = (field or "") % scu.WrapDict(
2020-09-26 16:19:37 +02:00
cdict
) # note that None values are mapped to empty strings
except:
log("process_field: invalid format=%s" % field)
text = (
"<para><i>format invalide !<i></para><para>"
+ traceback.format_exc()
+ "</para>"
)
# remove unhandled or dangerous tags:
text = re.sub(r"<\s*img", "", text)
if format == "html":
# convert <para>
text = re.sub(r"<\s*para(\s*)(.*?)>", r"<p>", text)
return text
# --- PDF format:
# handle logos:
2021-06-15 12:34:33 +02:00
image_dir = scu.SCODOC_LOGOS_DIR + "/logos_" + scu.get_dept_id() + "/"
2020-09-26 16:19:37 +02:00
if not os.path.exists(image_dir):
2021-02-01 16:23:11 +01:00
image_dir = scu.SCODOC_LOGOS_DIR + "/" # use global logos
2020-09-26 16:19:37 +02:00
text = re.sub(
r"<(\s*)logo(.*?)src\s*=\s*(.*?)>", r"<\1logo\2\3>", text
) # remove forbidden src attribute
text = re.sub(
r'<\s*logo(.*?)name\s*=\s*"(\w*?)"(.*?)/?>',
r'<img\1src="%s/logo_\2.jpg"\3/>' % image_dir,
text,
)
# nota: le match sur \w*? donne le nom du logo et interdit les .. et autres
# tentatives d'acceder à d'autres fichiers !
# log('field: %s' % (text))
2021-02-01 16:23:11 +01:00
return sco_pdf.makeParas(text, style, suppress_empty=suppress_empty_pars)
2020-09-26 16:19:37 +02:00
def get_formsemestre_bulletins_pdf(
context, formsemestre_id, REQUEST, version="selectedevals"
):
"document pdf et filename"
from app.scodoc import sco_bulletins
2021-07-19 19:53:01 +02:00
cached = sco_cache.SemBulletinsPDFCache.get(formsemestre_id + "_" + version)
2020-09-26 16:19:37 +02:00
if cached:
return cached[1], cached[0]
fragments = []
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
# Make each bulletin
2021-07-19 19:53:01 +02:00
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > get_etudids, get_sexnom
2020-09-26 16:19:37 +02:00
bookmarks = {}
filigrannes = {}
i = 1
for etudid in nt.get_etudids():
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
context,
formsemestre_id,
etudid,
format="pdfpart",
version=version,
REQUEST=REQUEST,
)
fragments += frag
filigrannes[i] = filigranne
2021-02-01 16:23:11 +01:00
bookmarks[i] = scu.suppress_accents(nt.get_sexnom(etudid))
2020-09-26 16:19:37 +02:00
i = i + 1
#
infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)}
2020-09-26 16:19:37 +02:00
if REQUEST:
server_name = REQUEST.BASE0
else:
server_name = ""
try:
2021-02-01 16:23:11 +01:00
sco_pdf.PDFLOCK.acquire()
2020-09-26 16:19:37 +02:00
pdfdoc = pdfassemblebulletins(
formsemestre_id,
fragments,
sem["titremois"],
infos,
bookmarks,
filigranne=filigrannes,
server_name=server_name,
context=context,
)
finally:
2021-02-01 16:23:11 +01:00
sco_pdf.PDFLOCK.release()
2020-09-26 16:19:37 +02:00
#
dt = time.strftime("%Y-%m-%d")
filename = "bul-%s-%s.pdf" % (sem["titre_num"], dt)
2021-02-01 16:23:11 +01:00
filename = scu.unescape_html(filename).replace(" ", "_").replace("&", "")
2020-09-26 16:19:37 +02:00
# fill cache
2021-07-19 19:53:01 +02:00
sco_cache.SemBulletinsPDFCache.set(
formsemestre_id + "_" + version, (filename, pdfdoc)
)
2020-09-26 16:19:37 +02:00
return pdfdoc, filename
def get_etud_bulletins_pdf(context, etudid, REQUEST, version="selectedevals"):
"Bulletins pdf de tous les semestres de l'étudiant, et filename"
from app.scodoc import sco_bulletins
etud = sco_etud.get_etud_info(etudid=etudid, filled=1)[0]
2020-09-26 16:19:37 +02:00
fragments = []
bookmarks = {}
filigrannes = {}
i = 1
for sem in etud["sems"]:
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
context,
sem["formsemestre_id"],
etudid,
format="pdfpart",
version=version,
REQUEST=REQUEST,
)
fragments += frag
filigrannes[i] = filigranne
bookmarks[i] = sem["session_id"] # eg RT-DUT-FI-S1-2015
i = i + 1
infos = {"DeptName": sco_preferences.get_preference("DeptName")}
2020-09-26 16:19:37 +02:00
if REQUEST:
server_name = REQUEST.BASE0
else:
server_name = ""
try:
2021-02-01 16:23:11 +01:00
sco_pdf.PDFLOCK.acquire()
2020-09-26 16:19:37 +02:00
pdfdoc = pdfassemblebulletins(
None,
fragments,
etud["nomprenom"],
infos,
bookmarks,
filigranne=filigrannes,
server_name=server_name,
context=context,
)
finally:
2021-02-01 16:23:11 +01:00
sco_pdf.PDFLOCK.release()
2020-09-26 16:19:37 +02:00
#
filename = "bul-%s" % (etud["nomprenom"])
filename = (
2021-02-01 16:23:11 +01:00
scu.unescape_html(filename).replace(" ", "_").replace("&", "").replace(".", "")
2020-09-26 16:19:37 +02:00
+ ".pdf"
)
return pdfdoc, filename