# -*- mode: python -*- # -*- coding: utf-8 -*- ############################################################################## # # Gestion scolarite IUT # # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # # 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 # ############################################################################## """Edition des PV de jury """ import io import re from PIL import Image as PILImage from PIL import UnidentifiedImageError import reportlab from reportlab.lib.units import cm, mm from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_JUSTIFY from reportlab.platypus import Paragraph, Spacer, Frame, PageBreak from reportlab.platypus import Table, TableStyle, Image from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate from reportlab.lib.pagesizes import A4, landscape from reportlab.lib import styles from reportlab.lib.colors import Color from flask import g from app.models import FormSemestre import app.scodoc.sco_utils as scu from app.scodoc import sco_pdf from app.scodoc.sco_logos import find_logo from app.scodoc.sco_pdf import SU LOGO_FOOTER_ASPECT = scu.CONFIG.LOGO_FOOTER_ASPECT # XXX A AUTOMATISER LOGO_FOOTER_HEIGHT = scu.CONFIG.LOGO_FOOTER_HEIGHT * mm LOGO_FOOTER_WIDTH = LOGO_FOOTER_HEIGHT * scu.CONFIG.LOGO_FOOTER_ASPECT LOGO_HEADER_ASPECT = scu.CONFIG.LOGO_HEADER_ASPECT # XXX logo IUTV (A AUTOMATISER) LOGO_HEADER_HEIGHT = scu.CONFIG.LOGO_HEADER_HEIGHT * mm LOGO_HEADER_WIDTH = LOGO_HEADER_HEIGHT * scu.CONFIG.LOGO_HEADER_ASPECT def page_footer(canvas, doc, logo, preferences, with_page_numbers=True): "Add footer on page" width = doc.pagesize[0] # - doc.pageTemplate.left_p - doc.pageTemplate.right_p foot = Frame( 0.1 * mm, 0.2 * cm, width - 1 * mm, 2 * cm, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, id="monfooter", showBoundary=0, ) left_foot_style = reportlab.lib.styles.ParagraphStyle({}) left_foot_style.fontName = preferences["SCOLAR_FONT"] left_foot_style.fontSize = preferences["SCOLAR_FONT_SIZE_FOOT"] left_foot_style.leftIndent = 0 left_foot_style.firstLineIndent = 0 left_foot_style.alignment = TA_RIGHT right_foot_style = reportlab.lib.styles.ParagraphStyle({}) right_foot_style.fontName = preferences["SCOLAR_FONT"] right_foot_style.fontSize = preferences["SCOLAR_FONT_SIZE_FOOT"] right_foot_style.alignment = TA_RIGHT p = sco_pdf.make_paras( f"""{preferences["INSTITUTION_NAME"]}{ preferences["INSTITUTION_ADDRESS"]}""", left_foot_style, ) np = Paragraph(f'{doc.page}', right_foot_style) tabstyle = TableStyle( [ ("LEFTPADDING", (0, 0), (-1, -1), 0), ("RIGHTPADDING", (0, 0), (-1, -1), 0), ("ALIGN", (0, 0), (-1, -1), "RIGHT"), # ('INNERGRID', (0,0), (-1,-1), 0.25, black),#debug # ('LINEABOVE', (0,0), (-1,0), 0.5, black), ("VALIGN", (1, 0), (1, 0), "MIDDLE"), ("RIGHTPADDING", (-1, 0), (-1, 0), 1 * cm), ] ) elems = [p] if logo: elems.append(logo) col_widths = [None, LOGO_FOOTER_WIDTH + 2 * mm] if with_page_numbers: elems.append(np) col_widths.append(2 * cm) else: elems.append("") col_widths.append(8 * mm) # force marge droite tab = Table([elems], style=tabstyle, colWidths=col_widths) canvas.saveState() # is it necessary ? foot.addFromList([tab], canvas) canvas.restoreState() def page_header(canvas, doc, logo, preferences, only_on_first_page=False): "Ajoute au canvas le frame avec le logo" if only_on_first_page and int(doc.page) > 1: return height = doc.pagesize[1] head = Frame( -22 * mm, height - 13 * mm - LOGO_HEADER_HEIGHT, 10 * cm, LOGO_HEADER_HEIGHT + 2 * mm, leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0, 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 par étudiant)""" def __init__( self, document, pagesbookmarks=None, author=None, title=None, subject=None, margins=(0, 0, 0, 0), # additional margins in mm (left,top,right, bottom) preferences=None, # dictionnary with preferences, required force_header=False, force_footer=False, # always add a footer (whatever the preferences, use for PV) template_name="CourrierJuryTemplate", ): """Initialise our page template.""" self.pagesbookmarks = pagesbookmarks or {} self.pdfmeta_author = author self.pdfmeta_title = title self.pdfmeta_subject = subject 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 left, top, right, bottom = margins # marge additionnelle en mm # marges du Frame principal self.bot_p = 2 * cm self.left_p = 2.5 * cm self.right_p = 2.5 * cm self.top_p = 0 * cm # log("margins=%s" % str(margins)) content = Frame( self.left_p + left * mm, self.bot_p + bottom * mm, document.pagesize[0] - self.right_p - self.left_p - left * mm - right * mm, document.pagesize[1] - self.top_p - self.bot_p - top * mm - bottom * mm, ) PageTemplate.__init__(self, template_name, [content]) self.background_image_filename = None self.logo_footer = None self.logo_header = None # Search logos in dept specific dir, then in global scu.CONFIG dir if template_name == "PVJuryTemplate": background = find_logo( logoname="pvjury_background", dept_id=g.scodoc_dept_id, ) or find_logo( logoname="pvjury_background", dept_id=g.scodoc_dept_id, prefix="", ) else: background = find_logo( logoname="letter_background", dept_id=g.scodoc_dept_id, ) or find_logo( logoname="letter_background", dept_id=g.scodoc_dept_id, prefix="", ) if not self.background_image_filename and background is not None: self.background_image_filename = background.filepath footer = find_logo(logoname="footer", dept_id=g.scodoc_dept_id) if footer is not None: self.logo_footer = Image( footer.filepath, height=LOGO_FOOTER_HEIGHT, width=LOGO_FOOTER_WIDTH, ) header = find_logo(logoname="header", dept_id=g.scodoc_dept_id) if header is not None: self.logo_header = Image( header.filepath, height=LOGO_HEADER_HEIGHT, width=LOGO_HEADER_WIDTH, ) def beforeDrawPage(self, canv, doc): """Draws a logo and an contribution message on each page.""" # ---- Add some meta data and bookmarks if self.pdfmeta_author: canv.setAuthor(SU(self.pdfmeta_author)) if self.pdfmeta_title: canv.setTitle(SU(self.pdfmeta_title)) if self.pdfmeta_subject: canv.setSubject(SU(self.pdfmeta_subject)) bm = self.pagesbookmarks.get(doc.page, None) if bm is not None: key = bm txt = SU(bm) canv.bookmarkPage(key) canv.addOutlineEntry(txt, bm) # ---- Background image if self.background_image_filename and self.with_page_background: canv.drawImage( self.background_image_filename, 0, 0, doc.pagesize[0], doc.pagesize[1] ) # ---- Header/Footer if self.with_header: page_header( canv, doc, self.logo_header, self.preferences, self.header_only_on_first_page, ) if self.with_footer: page_footer( canv, doc, self.logo_footer, self.preferences, with_page_numbers=self.with_page_numbers, ) class PVTemplate(CourrierIndividuelTemplate): """Template pour les pages des PV de jury""" def __init__( self, document, author=None, title=None, subject=None, margins=None, # additional margins in mm (left,top,right, bottom) 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"], ) super().__init__( document, author=author, title=title, subject=subject, margins=margins, preferences=preferences, force_header=True, force_footer=True, template_name="PVJuryTemplate", ) 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, canv, doc): # """Called after all flowables have been drawn on a page""" # pass # def beforeDrawPage(self, canv, doc): # """Called before any flowables are drawn on a page""" # # If the page number is even, force a page break # super().beforeDrawPage(canv, doc) # # Note: on cherche un moyen de generer un saut de page double # # (redémarrer sur page impaire, nouvelle feuille en recto/verso). Pas trouvé en Platypus. # # # # if self.__pageNum % 2 == 0: # # canvas.showPage() # # # Increment pageNum again since we've added a blank page # # self.__pageNum += 1 def jury_titres(formsemestre: FormSemestre, diplome: bool) -> tuple[str, str]: """Titres du PV ou lettre de jury""" if not diplome: if formsemestre.formation.is_apc(): t = f"""BUT{(formsemestre.semestre_id+1)//2}""" s = t else: t = f"""passage de Semestre {formsemestre.semestre_id} en Semestre {formsemestre.semestre_id + 1}""" s = "passage de semestre" else: t = "délivrance du diplôme" s = t return t, s # titre long, titre court