Ajout image de fond sur PV jury et lettres individuelles de décision.

This commit is contained in:
viennet 2020-10-13 15:41:04 +02:00
parent 439571862b
commit 6160d48e9e
4 changed files with 187 additions and 62 deletions

View File

@ -69,7 +69,7 @@ def SU(s):
# Remplace caractères composés
# eg 'e\xcc\x81' COMBINING ACUTE ACCENT par '\xc3\xa9' LATIN SMALL LETTER E WITH ACUTE
# car les "combining accents" ne sont pas traités par ReportLab mais peuvent
# nous être envoyés par certains navigaters ou imports
# nous être envoyés par certains navigateurs ou imports
u = unicodedata.normalize("NFC", unicode(s, SCO_ENCODING, "replace"))
return u.encode("utf8")
@ -203,6 +203,7 @@ class ScolarsPageTemplate(PageTemplate):
"""
canvas.saveState()
# ---- Logo: a small image, positionned at top left of the page
if self.logo is not None:
# draws the logo if it exists
((width, height), image) = self.logo

View File

@ -786,6 +786,77 @@ vu la délibération de la commission %(Type)s en date du %(Date)s présidée pa
"category": "pvpdf",
},
),
(
"PV_WITH_BACKGROUND",
{
"initvalue": 0,
"title": "Mettre l'image de fond sur les PV de jury (paysage)",
"input_type": "boolcheckbox",
"labels": ["non", "oui"],
"category": "pvpdf",
},
),
(
"PV_WITH_HEADER",
{
"initvalue": 1, # legacy
"title": "Ajouter l'en-tête sur les PV (paysage)",
"input_type": "boolcheckbox",
"labels": ["non", "oui"],
"category": "pvpdf",
},
),
(
"PV_WITH_FOOTER",
{
"initvalue": 1, # legacy
"title": "Ajouter le pied de page sur les PV (paysage)",
"input_type": "boolcheckbox",
"labels": ["non", "oui"],
"category": "pvpdf",
},
),
# marges PV paysages (en millimètres)
(
"pv_left_margin",
{
"initvalue": 0,
"size": 10,
"title": "Marge gauche PV en mm",
"type": "float",
"category": "pvpdf",
},
),
(
"pv_top_margin",
{
"initvalue": 23,
"size": 10,
"title": "Marge haute PV",
"type": "float",
"category": "pvpdf",
},
),
(
"pv_right_margin",
{
"initvalue": 0,
"size": 10,
"title": "Marge droite PV",
"type": "float",
"category": "pvpdf",
},
),
(
"pv_bottom_margin",
{
"initvalue": 5,
"size": 10,
"title": "Marge basse PV",
"type": "float",
"category": "pvpdf",
},
),
(
"PV_LETTER_DIPLOMA_SIGNATURE",
{
@ -872,11 +943,21 @@ s'est réuni le %(date_jury)s.
"category": "pvpdf",
},
),
(
"PV_LETTER_WITH_BACKGROUND",
{
"initvalue": 0,
"title": "Mettre l'image de fond sur les lettres individuelles de décision",
"input_type": "boolcheckbox",
"labels": ["non", "oui"],
"category": "pvpdf",
},
),
(
"PV_LETTER_WITH_HEADER",
{
"initvalue": 0,
"title": "Imprimer le logo en tête des lettres individuelles de décision",
"title": "Ajouter l'en-tête sur les lettres individuelles de décision",
"input_type": "boolcheckbox",
"labels": ["non", "oui"],
"category": "pvpdf",
@ -886,7 +967,7 @@ s'est réuni le %(date_jury)s.
"PV_LETTER_WITH_FOOTER",
{
"initvalue": 0,
"title": "Imprimer le pied de page sur les lettres individuelles de décision",
"title": "Ajouter le pied de page sur les lettres individuelles de décision",
"input_type": "boolcheckbox",
"labels": ["non", "oui"],
"category": "pvpdf",
@ -1633,8 +1714,7 @@ class sco_base_preferences:
self.load()
def load(self):
"""Load all preferences from db
"""
"""Load all preferences from db"""
log("loading preferences")
try:
GSL.acquire()
@ -2069,6 +2149,8 @@ def doc_preferences(context):
L.append(["----", "----", "----"])
for pref_name, pref in PREFS:
if pref["category"] == cat:
L.append(['`'+pref_name+'`', pref["title"], pref.get("explanation", "")])
L.append(
["`" + pref_name + "`", pref["title"], pref.get("explanation", "")]
)
return "\n".join([" | ".join(x) for x in L])

View File

@ -94,7 +94,9 @@ def pageFooter(canvas, doc, logo, preferences, with_page_numbers=True):
("RIGHTPADDING", (-1, 0), (-1, 0), 1 * cm),
]
)
elems = [p, logo]
elems = [p]
if logo:
elems.append(logo)
colWidths = [None, LOGO_FOOTER_WIDTH + 2 * mm]
if with_page_numbers:
elems.append(np)
@ -124,14 +126,14 @@ def pageHeader(canvas, doc, logo, preferences, only_on_first_page=False):
id="monheader",
showBoundary=0,
)
if logo:
canvas.saveState() # is it necessary ?
head.addFromList([logo], canvas)
canvas.restoreState()
class CourrierIndividuelTemplate(PageTemplate):
"""Template pour courrier avisant des decisions de jury (1 page /etudiant)
"""
"""Template pour courrier avisant des decisions de jury (1 page /etudiant)"""
def __init__(
self,
@ -156,6 +158,13 @@ class CourrierIndividuelTemplate(PageTemplate):
self.preferences = preferences
self.force_header = force_header
self.force_footer = force_footer
self.with_footer = (
self.force_footer or self.preferences["PV_LETTER_WITH_HEADER"]
)
self.with_header = (
self.force_header or self.preferences["PV_LETTER_WITH_FOOTER"]
)
self.with_page_background = self.preferences["PV_LETTER_WITH_BACKGROUND"]
self.with_page_numbers = False
self.header_only_on_first_page = False
# Our doc is made of a single frame
@ -175,13 +184,29 @@ class CourrierIndividuelTemplate(PageTemplate):
PageTemplate.__init__(self, template_name, [content])
self.background_image_filename = None
self.logo_footer = None
self.logo_header = None
for suffix in LOGOS_IMAGES_ALLOWED_TYPES:
if template_name == "PVJuryTemplate":
fn = image_dir + "/pvjury_background" + "." + suffix
else:
fn = image_dir + "/letter_background" + "." + suffix
if os.path.exists(fn):
self.background_image_filename = fn
fn = image_dir + "/logo_footer" + "." + suffix
if os.path.exists(fn):
self.logo_footer = Image(
image_dir + "/logo_footer.jpg",
fn,
height=LOGO_FOOTER_HEIGHT,
width=LOGO_FOOTER_WIDTH,
)
fn = image_dir + "/logo_header" + "." + suffix
if os.path.exists(fn):
self.logo_header = Image(
image_dir + "/logo_header.jpg",
fn,
height=LOGO_HEADER_HEIGHT,
width=LOGO_HEADER_WIDTH,
)
@ -201,8 +226,15 @@ class CourrierIndividuelTemplate(PageTemplate):
txt = SU(bm)
canvas.bookmarkPage(key)
canvas.addOutlineEntry(txt, bm)
if self.force_footer or self.preferences["PV_LETTER_WITH_HEADER"]:
# --- Add header
# ---- Background image
if self.background_image_filename and self.with_page_background:
canvas.drawImage(
self.background_image_filename, 0, 0, doc.pagesize[0], doc.pagesize[1]
)
# ---- Header/Footer
if self.with_header:
pageHeader(
canvas,
doc,
@ -210,8 +242,7 @@ class CourrierIndividuelTemplate(PageTemplate):
self.preferences,
self.header_only_on_first_page,
)
if self.force_footer or self.preferences["PV_LETTER_WITH_FOOTER"]:
# --- Add footer
if self.with_footer:
pageFooter(
canvas,
doc,
@ -222,8 +253,7 @@ class CourrierIndividuelTemplate(PageTemplate):
class PVTemplate(CourrierIndividuelTemplate):
"""Template pour les pages des PV de jury
"""
"""Template pour les pages des PV de jury"""
def __init__(
self,
@ -231,10 +261,17 @@ class PVTemplate(CourrierIndividuelTemplate):
author=None,
title=None,
subject=None,
margins=(0, 23, 0, 5), # additional margins in mm (left,top,right, bottom)
margins=None, # additional margins in mm (left,top,right, bottom)
image_dir="",
preferences=None, # dictionnary with preferences, required
):
if margins is None:
margins = (
preferences["pv_left_margin"],
preferences["pv_top_margin"],
preferences["pv_right_margin"],
preferences["pv_bottom_margin"],
)
CourrierIndividuelTemplate.__init__(
self,
document,
@ -250,6 +287,9 @@ class PVTemplate(CourrierIndividuelTemplate):
)
self.with_page_numbers = True
self.header_only_on_first_page = True
self.with_header = self.preferences["PV_WITH_HEADER"]
self.with_footer = self.preferences["PV_WITH_FOOTER"]
self.with_page_background = self.preferences["PV_WITH_BACKGROUND"]
def afterDrawPage(self, canvas, doc):
"""Called after all flowables have been drawn on a page"""
@ -437,9 +477,13 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None, context
s = "s"
else:
s = ""
params["autorisations_txt"] = (
"""Vous êtes autorisé%s à continuer dans le%s semestre%s : <b>%s</b>"""
% (etud["ne"], s, s, decision["autorisations_descr"])
params[
"autorisations_txt"
] = """Vous êtes autorisé%s à continuer dans le%s semestre%s : <b>%s</b>""" % (
etud["ne"],
s,
s,
decision["autorisations_descr"],
)
else:
params["autorisations_txt"] = ""
@ -538,9 +582,6 @@ def _make_signature_image(signature, leftindent, formsemestre_id, context=None):
# ----------------------------------------------
# PV complet, tableau en format paysage
def pvjury_pdf(
context,
dpv,
@ -554,6 +595,7 @@ def pvjury_pdf(
anonymous=False,
):
"""Doc PDF récapitulant les décisions de jury
(tableau en format paysage)
dpv: result of dict_pvjury
"""
if not dpv:

View File

@ -156,8 +156,7 @@ def fmt_note(val, note_max=None, keep_numeric=False):
def fmt_coef(val):
"""Conversion valeur coefficient (float) en chaine
"""
"""Conversion valeur coefficient (float) en chaine"""
if val < 0.01:
return "%g" % val # unusually small value
return "%g" % round(val, 2)
@ -210,7 +209,6 @@ if "INSTANCE_HOME" in os.environ:
)
# ----- Lecture du fichier de configuration
SCO_SRCDIR = os.path.split(VERSION.__file__)[0]
if SCO_SRCDIR:
@ -231,7 +229,9 @@ except:
raise
if hasattr(CONFIG, "CODES_EXPL"):
CODES_EXPL.update(CONFIG.CODES_EXPL) # permet de customiser les explications de codes
CODES_EXPL.update(
CONFIG.CODES_EXPL
) # permet de customiser les explications de codes
if CONFIG.CUSTOM_HTML_HEADER:
@ -296,6 +296,8 @@ PDF_MIMETYPE = "application/pdf"
XML_MIMETYPE = "text/xml"
JSON_MIMETYPE = "application/json"
LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "png") # remind that PIL does not read pdf
class DictDefault(dict): # obsolete, use collections.defaultdict
"""A dictionnary with default value for all keys
@ -462,8 +464,7 @@ ALPHANUM_EXP = re.compile(r"^[\w-]+$", re.UNICODE)
def is_valid_code_nip(s):
"""True si s peut être un code NIP: au moins 6 chiffres décimaux
"""
"""True si s peut être un code NIP: au moins 6 chiffres décimaux"""
if not s:
return False
return re.match(r"^[0-9]{6,32}$", s)
@ -747,8 +748,7 @@ from sgmllib import SGMLParser
class html2txt_parser(SGMLParser):
"""html2txt()
"""
"""html2txt()"""
def reset(self):
"""reset() --> initialize the parser"""