diff --git a/app/but/bulletin_but_court.py b/app/but/bulletin_but_court.py index 4ba1aee9..684b1682 100644 --- a/app/but/bulletin_but_court.py +++ b/app/but/bulletin_but_court.py @@ -44,9 +44,13 @@ from app.views import notes_bp as bp from app.views import ScoData -@bp.route("/bulletin_but//") @bp.route( - "/bulletin_but///pdf", defaults={"fmt": "pdf"} + "/bulletin_but//", endpoint="bulletin_but_html" +) +@bp.route( + "/bulletin_but///pdf", + defaults={"fmt": "pdf"}, + endpoint="bulletin_but_pdf", ) @scodoc @permission_required(Permission.ScoView) diff --git a/app/but/bulletin_but_court_pdf.py b/app/but/bulletin_but_court_pdf.py index 3f0a1fb6..40914b09 100644 --- a/app/but/bulletin_but_court_pdf.py +++ b/app/but/bulletin_but_court_pdf.py @@ -20,12 +20,15 @@ from reportlab.lib.units import cm, mm from reportlab.platypus import Paragraph, Spacer, Table from app.but import cursus_but -from app.models import FormSemestre, Identite, ScolarFormSemestreValidation +from app.models import ( + BulAppreciations, + FormSemestre, + Identite, + ScolarFormSemestreValidation, +) from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard -from app.scodoc import sco_bulletins from app.scodoc.sco_logos import Logo -from app.scodoc import sco_pdf, sco_preferences from app.scodoc.sco_pdf import PDFLOCK, SU @@ -41,6 +44,7 @@ def make_bulletin_but_court_pdf( ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None, ues_acronyms: list[str] = None, ) -> bytes: + "génère le bulletin court BUT en pdf" # A priori ce verrou n'est plus nécessaire avec Flask (multi-process) # mais... try: @@ -64,6 +68,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): multi_pages = False # une page par bulletin small_fontsize = "8" color_blue_bg = Color(0, 153 / 255, 204 / 255) + color_gray_bg = Color(0.86, 0.86, 0.86) def __init__( self, @@ -94,13 +99,23 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): self.nb_ues = len(self.ues_acronyms) # Styles PDF - self.style_cell = styles.ParagraphStyle("style_cell") - self.style_cell.fontName = "Helvetica" + self.style_base = styles.ParagraphStyle("style_base") + self.style_base.fontName = "Helvetica" + self.style_base.fontSize = 9 + + self.style_nom = styles.ParagraphStyle("style_nom", self.style_base) + self.style_nom.fontSize = 11 + self.style_nom.fontName = "Helvetica-Bold" + + self.style_field = self.style_base # écrase style defaut buleltins + + self.style_cell = styles.ParagraphStyle("style_cell", self.style_base) self.style_cell.fontSize = 7 self.style_cell.leading = 7 - self.style_bold = styles.ParagraphStyle("style_bold", self.style_cell) - self.style_bold.fontName = "Helvetica-Bold" - self.style_head = styles.ParagraphStyle("style_head", self.style_bold) + self.style_cell_bold = styles.ParagraphStyle("style_cell_bold", self.style_cell) + self.style_cell_bold.fontName = "Helvetica-Bold" + + self.style_head = styles.ParagraphStyle("style_head", self.style_cell_bold) self.style_head.fontSize = 9 self.style_niveaux = styles.ParagraphStyle("style_niveaux", self.style_cell) @@ -128,6 +143,25 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): "style_niveaux_code", self.style_niveaux ) self.style_niveaux_code.borderColor = black + # + self.style_jury = styles.ParagraphStyle("style_jury", self.style_base) + self.style_jury.fontSize = 9 + self.style_jury.leading = self.style_jury.fontSize * 1.4 # espace les lignes + self.style_jury.backColor = self.color_gray_bg + self.style_jury.borderColor = black + self.style_jury.borderWidth = 1 + self.style_jury.borderPadding = 2 + self.style_jury.borderRadius = 2 + + self.style_appreciations = styles.ParagraphStyle( + "style_appreciations", self.style_base + ) + self.style_appreciations.fontSize = 9 + self.style_appreciations.leading = ( + self.style_jury.fontSize * 1.4 + ) # espace les lignes + + self.style_assiduite = self.style_cell # Géométrie page self.width_page_avail = 185 * mm # largeur utilisable @@ -138,7 +172,27 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): self.width_col_code = self.width_col_ue # Niveaux self.width_col_niveaux_titre = 24 * mm - self.width_col_niveaux_code = 12 * mm + self.width_col_niveaux_code = 14 * mm + + def bul_title_pdf(self, preference_field="bul_but_pdf_title") -> list: + """Génère la partie "titre" du bulletin de notes. + Renvoie une liste d'objets platypus + """ + # comme les bulletins standard, mais avec notre préférence + return super().bul_title_pdf(preference_field=preference_field) + + def bul_part_below(self, fmt="pdf") -> list: + """Génère les informations placées sous la table + Dans le cas du bul. court BUT pdf, seulement les appréciations. + fmt est ignoré ici. + """ + appreciations = BulAppreciations.get_appreciations_list( + self.formsemestre.id, self.etud.id + ) + return [ + Spacer(1, 3 * mm), + self.bul_appreciations_pdf(appreciations, style=self.style_appreciations), + ] def bul_table(self, fmt=None) -> list: """Génère la table centrale du bulletin de notes @@ -148,7 +202,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): style_table_2cols = [ ("ALIGN", (0, -1), (0, -1), "LEFT"), ("ALIGN", (-1, -1), (-1, -1), "RIGHT"), - ("VALIGN", (0, 0), (-1, 1), "TOP"), + ("VALIGN", (0, 0), (-1, -1), "TOP"), ("LEFTPADDING", (0, 0), (-1, -1), 0), ("TOPPADDING", (0, 0), (-1, -1), 0), ("RIGHTPADDING", (0, 0), (-1, -1), 0), @@ -156,15 +210,24 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): ] # Ligne avec boite assiduité et table UEs table_abs_ues = Table( - [[self.box_assiduite(), self.table_ues()]], - colWidths=(3 * cm, self.width_page_avail - 3 * cm), + [ + [ + self.boite_identite() + [Spacer(1, 3 * mm), self.boite_assiduite()], + self.table_ues(), + ], + ], style=style_table_2cols, ) table_abs_ues.hAlign = "RIGHT" # Ligne (en bas) avec table cursus et boite jury table_cursus_jury = Table( - [[self.table_cursus_but(), self.boite_decisions_jury()]], - colWidths=(self.width_page_avail - 45 * mm, 45 * mm), + [ + [ + self.table_cursus_but(), + [Spacer(1, 8 * mm), self.boite_decisions_jury()], + ] + ], + colWidths=(self.width_page_avail - 84 * mm, 84 * mm), style=style_table_2cols, ) return [ @@ -223,13 +286,15 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): col_widths = [self.width_col_ue_titres] + [self.width_col_ue] * self.nb_ues rows_styled = [[Paragraph(SU(str(cell)), self.style_head) for cell in rows[0]]] - rows_styled += [[Paragraph(SU(str(cell)), self.style_bold) for cell in rows[1]]] + rows_styled += [ + [Paragraph(SU(str(cell)), self.style_cell_bold) for cell in rows[1]] + ] rows_styled += [ [Paragraph(SU(str(cell)), self.style_cell) for cell in row] for row in rows[2:-1] ] rows_styled += [ - [Paragraph(SU(str(cell)), self.style_bold) for cell in rows[-1]] + [Paragraph(SU(str(cell)), self.style_cell_bold) for cell in rows[-1]] ] table = Table( rows_styled, @@ -285,7 +350,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): ] * self.nb_ues rows_styled = [ - [Paragraph(SU(str(cell)), self.style_bold) for cell in row] + [Paragraph(SU(str(cell)), self.style_cell_bold) for cell in row] for row in rows[:2] ] rows_styled += [ @@ -310,7 +375,26 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): "saes", "Situations d'Apprentissage et d'Évaluation (SAÉ)" ) - def box_assiduite(self) -> Table: + def boite_identite(self) -> list: + "Les informations sur l'identité et l'inscription de l'étudiant" + return [ + Paragraph( + SU(f"""{self.etud.nomprenom}"""), + style=self.style_nom, + ), + Paragraph( + SU( + f""" + {self.bul["demission"]}
+ Formation: {self.formsemestre.titre_num()}
+ Année scolaire: {self.formsemestre.annee_scolaire_str()}
+ """ + ), + style=self.style_base, + ), + ] + + def boite_assiduite(self) -> Table: "Les informations sur l'assiduité" if not self.bul["options"]["show_abs"]: return Paragraph("") # empty @@ -326,7 +410,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): for row in rows[:1] ] rows_styled += [ - [Paragraph(SU(str(cell)), self.style_cell) for cell in row] + [Paragraph(SU(str(cell)), self.style_assiduite) for cell in row] for row in rows[1:] ] table = Table( @@ -339,6 +423,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): ("SPAN", (0, 1), (1, 1)), ("VALIGN", (0, 0), (-1, -1), "TOP"), ], + colWidths=(25 * mm, 10 * mm), ) table.hAlign = "LEFT" return table @@ -387,12 +472,14 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): style=[ ("ALIGN", (0, 0), (-1, -1), "CENTER"), ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), - ("LEFTPADDING", (0, 0), (-1, -1), 2), + ("LEFTPADDING", (0, 0), (-1, -1), 5), ("TOPPADDING", (0, 0), (-1, -1), 4), - ("RIGHTPADDING", (0, 0), (-1, -1), 2), + ("RIGHTPADDING", (0, 0), (-1, -1), 5), ("BOTTOMPADDING", (0, 0), (-1, -1), 4), # sert de séparateur entre les lignes: ("LINEABOVE", (0, 1), (-1, -1), 3, white), + # séparateur colonne + ("LINEBEFORE", (1, 1), (-1, -1), 5, white), ], ) table.hAlign = "LEFT" @@ -400,24 +487,24 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard): def boite_decisions_jury(self): """La boite en bas à droite avec jury""" - txt = f"""ECTS acquis : {self.ects_total}
""" + txt = f"""ECTS acquis : {self.ects_total:g}
""" if self.bul["semestre"]["decision_annee"]: txt += f""" Jury tenu le { datetime.datetime.fromisoformat(self.bul["semestre"]["decision_annee"]["date"]).strftime("%d/%m/%Y à %H:%M") - }, année BUT {self.bul["semestre"]["decision_annee"]["code"]}. + }, année BUT {self.bul["semestre"]["decision_annee"]["code"]}.
""" if self.bul["semestre"]["autorisation_inscription"]: txt += ( - "Autorisé à s'inscrire en " + "Autorisé à s'inscrire en " + ", ".join( [ f"S{aut['semestre_id']}" for aut in self.bul["semestre"]["autorisation_inscription"] ] ) - + "." + + "." ) - return Paragraph(txt) + return Paragraph(txt, style=self.style_jury) diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index 499833e1..f0c66db6 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -113,10 +113,10 @@ class BulletinGenerator: self.diagnostic = None # error message if any problem # Common PDF styles: # - Pour tous les champs du bulletin sauf les cellules de table: - self.FieldStyle = reportlab.lib.styles.ParagraphStyle({}) - self.FieldStyle.fontName = self.preferences["SCOLAR_FONT_BUL_FIELDS"] - self.FieldStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"] - self.FieldStyle.firstLineIndent = 0 + self.style_field = reportlab.lib.styles.ParagraphStyle({}) + self.style_field.fontName = self.preferences["SCOLAR_FONT_BUL_FIELDS"] + self.style_field.fontSize = self.preferences["SCOLAR_FONT_SIZE"] + self.style_field.firstLineIndent = 0 # - Pour les cellules de table: self.CellStyle = reportlab.lib.styles.ParagraphStyle({}) self.CellStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"] diff --git a/app/scodoc/sco_bulletins_legacy.py b/app/scodoc/sco_bulletins_legacy.py index 419a2437..95e3846a 100644 --- a/app/scodoc/sco_bulletins_legacy.py +++ b/app/scodoc/sco_bulletins_legacy.py @@ -62,7 +62,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): Renvoie une liste d'objets platypus """ objects = sco_bulletins_pdf.process_field( - self.preferences["bul_pdf_title"], self.infos, self.FieldStyle + self.preferences["bul_pdf_title"], self.infos, self.style_field ) objects.append( Spacer(1, 5 * mm) @@ -301,7 +301,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): objects += sco_bulletins_pdf.process_field( self.preferences["bul_pdf_caption"], self.infos, - self.FieldStyle, + self.style_field, ) return objects @@ -387,7 +387,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): sco_bulletins_pdf.process_field( self.preferences["bul_pdf_sig_left"], self.infos, - self.FieldStyle, + self.style_field, ) ] ] @@ -398,7 +398,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator): sco_bulletins_pdf.process_field( self.preferences["bul_pdf_sig_right"], self.infos, - self.FieldStyle, + self.style_field, ) ) else: diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py index 4413741d..01c39aef 100644 --- a/app/scodoc/sco_bulletins_pdf.py +++ b/app/scodoc/sco_bulletins_pdf.py @@ -142,7 +142,9 @@ class WrapDict(object): return value -def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"): +def process_field( + field, cdict, style, suppress_empty_pars=False, fmt="pdf", field_name=None +): """Process a field given in preferences, returns - if format = 'pdf': a list of Platypus objects - if format = 'html' : a string @@ -178,7 +180,7 @@ def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"): # ne sera pas visible si lien vers pdf: scu.flash_once(f"Attention: format PDF invalide (champs {field})") text = ( - "format invalide !" + "format invalide ! (1)" + traceback.format_exc() + "" ) @@ -204,7 +206,9 @@ def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"): # secure_filename dans la classe Logo # log('field: %s' % (text)) - return sco_pdf.make_paras(text, style, suppress_empty=suppress_empty_pars) + return sco_pdf.make_paras( + text, style, suppress_empty=suppress_empty_pars, field_name=field_name + ) def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py index dd3e8d7d..8bff41a4 100644 --- a/app/scodoc/sco_bulletins_standard.py +++ b/app/scodoc/sco_bulletins_standard.py @@ -81,12 +81,15 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): description = "standard ScoDoc (version 2011)" supported_formats = ["html", "pdf"] - def bul_title_pdf(self) -> list: + def bul_title_pdf(self, preference_field="bul_pdf_title") -> list: """Génère la partie "titre" du bulletin de notes. Renvoie une liste d'objets platypus """ objects = sco_bulletins_pdf.process_field( - self.preferences["bul_pdf_title"], self.infos, self.FieldStyle + self.preferences[preference_field], + self.infos, + self.style_field, + field_name=preference_field, ) objects.append( Spacer(1, 5 * mm) @@ -195,37 +198,26 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): % self.infos ) H.append("") - # Appréciations sur PDF: + # ------ Appréciations sur PDF if appreciations: story.append(Spacer(1, 3 * mm)) - try: - story.append( - Paragraph( - SU( - "Appréciation : " - + "\n".join(BulAppreciations.summarize(appreciations)) - ), - self.CellStyle, - ) - ) - except AttributeError as exc: - raise ScoPDFFormatError( - "Appréciation invalide bloquant la génération du pdf" - ) from exc + story.append(self.bul_appreciations_pdf(appreciations)) # ----- DECISION JURY if self.preferences["bul_show_decision"]: story += sco_bulletins_pdf.process_field( self.preferences["bul_pdf_caption"], self.infos, - self.FieldStyle, + self.style_field, fmt="pdf", + field_name="bul_pdf_caption", ) field = sco_bulletins_pdf.process_field( self.preferences["bul_pdf_caption"], self.infos, - self.FieldStyle, + self.style_field, fmt="html", + field_name="bul_pdf_caption", ) H.append('
' + field + "
") @@ -240,6 +232,24 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): elif fmt == "html": return "\n".join(H) + def bul_appreciations_pdf( + self, appreciations: list[BulAppreciations], style=None + ) -> Paragraph: + "Liste d'objets platypus pour les appréciations sous le bulletin" + style = style or self.CellStyle + try: + return Paragraph( + SU( + "Appréciation du " + + "\n".join(BulAppreciations.summarize(appreciations)) + ), + style, + ) + except AttributeError as exc: + raise ScoPDFFormatError( + "Appréciation invalide bloquant la génération du pdf" + ) from exc + def bul_signatures_pdf(self): """Génère les signatures placées en bas du bulletin PDF Renvoie une liste d'objets platypus @@ -253,7 +263,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): sco_bulletins_pdf.process_field( self.preferences["bul_pdf_sig_left"], self.infos, - self.FieldStyle, + self.style_field, + field_name="bul_pdf_sig_left", ) ] ] @@ -264,7 +275,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): sco_bulletins_pdf.process_field( self.preferences["bul_pdf_sig_right"], self.infos, - self.FieldStyle, + self.style_field, + field_name="bul_pdf_sig_right", ) ) else: diff --git a/app/scodoc/sco_config.py b/app/scodoc/sco_config.py index a7f38923..4c8a559f 100644 --- a/app/scodoc/sco_config.py +++ b/app/scodoc/sco_config.py @@ -45,7 +45,7 @@ CONFIG.LOGO_HEADER_HEIGHT = 28 # server_url: URL du serveur ScoDoc # scodoc_name: le nom du logiciel (ScoDoc actuellement, voir sco_version.py) CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = ( - "Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s" + "Édité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s" ) diff --git a/app/scodoc/sco_pdf.py b/app/scodoc/sco_pdf.py index 72e1f8df..b21f6267 100755 --- a/app/scodoc/sco_pdf.py +++ b/app/scodoc/sco_pdf.py @@ -57,6 +57,7 @@ from flask import g from app import log from app.scodoc.sco_exceptions import ScoGenError, ScoPDFFormatError, ScoValueError +from app.scodoc import sco_preferences from app.scodoc import sco_utils as scu from app.scodoc.sco_utils import CONFIG import sco_version @@ -119,7 +120,9 @@ def _splitPara(txt): return L -def make_paras(txt: str, style, suppress_empty=False) -> list[Paragraph]: +def make_paras( + txt: str, style, suppress_empty=False, field_name=None +) -> list[Paragraph]: """Returns a list of Paragraph instances from a text with one or more ... """ @@ -157,9 +160,17 @@ def make_paras(txt: str, style, suppress_empty=False) -> list[Paragraph]: log(traceback.format_exc()) log(f"Invalid pdf para format: {txt}") try: + # récupère le nom de la préférence + if field_name: + p = sco_preferences.BasePreferences(g.scodoc_dept_id) + pref = p.prefs_dict.get(field_name) + if pref: + field_name = pref["title"] result = [ Paragraph( - SU('Erreur: format invalide'), + SU( + f"""Erreur: format invalide (voir préférence {field_name or ""})""" + ), style, ) ] diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index 5a378453..1acfc5da 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -229,6 +229,16 @@ PREF_CATEGORIES = ( "related": ("abs", "bul_margins", "bul_mail"), }, ), + ( + "bul_but_pdf", + { + "title": "Réglages des bulletins BUT (pdf)", + "related": ( + "bul", + "bul_margins", + ), + }, + ), # sur page "Mise en page des bulletins" ( "bul_margins", @@ -257,7 +267,7 @@ PREF_CATEGORIES = ( ) -class BasePreferences(object): +class BasePreferences: """Global preferences""" _editor = ndb.EditableTable( @@ -1681,6 +1691,30 @@ class BasePreferences(object): "category": "bul", }, ), + # Bulletin court BUT + # avec peu de réglages afin conserver la mise en page compacte... + ( + "bul_but_pdf_title", + { + "initvalue": """ + +%(UnivName)s + + +%(InstituteName)s + + +Bachelor Universitaire de Technologie + +""", + "title": "Bulletins PDF BUT: paragraphe de titre", + "explanation": "(balises interprétées, voir documentation)", + "input_type": "textarea", + "rows": 10, + "cols": 64, + "category": "bul_but_pdf", + }, + ), # XXX A COMPLETER, voir sco_formsemestre_edit.py XXX # bul_mail ( diff --git a/app/templates/bul_head.j2 b/app/templates/bul_head.j2 index 691cb4fb..9a59ccd5 100644 --- a/app/templates/bul_head.j2 +++ b/app/templates/bul_head.j2 @@ -42,6 +42,13 @@ format='pdf', version=version, )}}">{{scu.ICON_PDF|safe}} + version courte spéciale BUT diff --git a/app/templates/but/bulletin_court_page.j2 b/app/templates/but/bulletin_court_page.j2 index 66da8bc3..f7249c16 100644 --- a/app/templates/but/bulletin_court_page.j2 +++ b/app/templates/but/bulletin_court_page.j2 @@ -39,6 +39,11 @@ {%- endmacro %} {% block app_content %} +

version pdf {{scu.ICON_PDF|safe}} +

diff --git a/sco_version.py b/sco_version.py index c72a2658..5cc41fa0 100644 --- a/sco_version.py +++ b/sco_version.py @@ -11,6 +11,7 @@ SCONEWS = """
  • ScoDoc 9.6 (juillet 2023)
    • +
    • Nouveaux bulletins BUT compacts
    • Nouvelle gestion des absences et assiduité
    • Mise à jour logiciels: Debian 12, Python 3.11, ...
    diff --git a/tools/scodoc_config.py b/tools/scodoc_config.py index 75a47e3e..205b4773 100644 --- a/tools/scodoc_config.py +++ b/tools/scodoc_config.py @@ -64,7 +64,7 @@ CONFIG.LOGO_HEADER_HEIGHT = 28 # taille verticale dans le document en millimetr # scodoc_name: le nom du logiciel (ScoDoc actuellement, voir sco_version.py) CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = ( - "Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s" + "Édité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s" )