# -*- 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 # ############################################################################## """Génération du PV de jury en PDF (celui en format paysage avec l'ensemble des décisions) """ import io import reportlab from reportlab.lib.units import cm, mm from reportlab.lib.enums import TA_JUSTIFY, TA_LEFT, TA_RIGHT from reportlab.platypus import Paragraph, Spacer, PageBreak, Table, TableStyle from reportlab.platypus.doctemplate import BaseDocTemplate from reportlab.lib.pagesizes import A4, landscape from reportlab.lib import styles from reportlab.lib.colors import Color from app.models import FormSemestre from app.scodoc import codes_cursus from app.scodoc.sco_exceptions import ScoPDFFormatError from app.scodoc import sco_pv_dict from app.scodoc import sco_pdf from app.scodoc import sco_preferences from app.scodoc.sco_pdf import SU from app.scodoc.sco_pv_templates import PVTemplate, jury_titres import sco_version # ---------------------------------------------- def pvjury_pdf( formsemestre: FormSemestre, etudids: list[int], date_commission=None, date_jury=None, numero_arrete=None, code_vdi=None, show_title=False, pv_title=None, pv_subtitle=None, pv_title_session=None, with_paragraph_nom=False, anonymous=False, ) -> bytes: """Doc PDF récapitulant les décisions de jury (tableau en format paysage) """ objects, a_diplome = _pvjury_pdf_type( formsemestre, etudids, only_diplome=False, date_commission=date_commission, numero_arrete=numero_arrete, code_vdi=code_vdi, date_jury=date_jury, show_title=show_title, pv_title_session=pv_title_session, pv_title=pv_title, pv_subtitle=pv_subtitle, with_paragraph_nom=with_paragraph_nom, anonymous=anonymous, ) if not objects: return b"" jury_de_diplome = formsemestre.est_terminal() # Si Jury de passage et qu'un étudiant valide le parcours # (car il a validé antérieurement le dernier semestre) # alors on génère aussi un PV de diplome (à la suite dans le même doc PDF) if not jury_de_diplome and a_diplome: # au moins un etudiant a validé son diplome: objects.append(PageBreak()) objects += _pvjury_pdf_type( formsemestre, etudids, only_diplome=True, date_commission=date_commission, date_jury=date_jury, numero_arrete=numero_arrete, code_vdi=code_vdi, show_title=show_title, pv_title_session=pv_title_session, pv_title=pv_title, with_paragraph_nom=with_paragraph_nom, anonymous=anonymous, )[0] # ----- Build PDF report = io.BytesIO() # in-memory document, no disk file document = BaseDocTemplate(report) document.pagesize = landscape(A4) document.addPageTemplates( PVTemplate( document, author=f"{sco_version.SCONAME} {sco_version.SCOVERSION} (E. Viennet)", title=SU(f"PV du jury de {formsemestre.titre_num()}"), subject="PV jury", preferences=sco_preferences.SemPreferences(formsemestre.id), ) ) try: document.build(objects) except (ValueError, KeyError, reportlab.platypus.doctemplate.LayoutError) as exc: raise ScoPDFFormatError(str(exc)) from exc data = report.getvalue() return data def _make_pv_styles(formsemestre: FormSemestre): style = reportlab.lib.styles.ParagraphStyle({}) style.fontSize = 12 style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre.id) style.leading = 18 style.alignment = TA_JUSTIFY indent = 1 * cm style_bullet = reportlab.lib.styles.ParagraphStyle({}) style_bullet.fontSize = 12 style_bullet.fontName = sco_preferences.get_preference( "PV_FONTNAME", formsemestre.id ) style_bullet.leading = 12 style_bullet.alignment = TA_JUSTIFY style_bullet.firstLineIndent = 0 style_bullet.leftIndent = indent style_bullet.bulletIndent = indent style_bullet.bulletFontName = "Times-Roman" style_bullet.bulletFontSize = 11 style_bullet.spaceBefore = 5 * mm style_bullet.spaceAfter = 5 * mm return style, style_bullet def _pvjury_pdf_type( formsemestre: FormSemestre, etudids: list[int], only_diplome=False, date_commission=None, date_jury=None, numero_arrete=None, code_vdi=None, show_title=False, pv_title=None, pv_subtitle=None, pv_title_session=None, anonymous=False, with_paragraph_nom=False, ) -> tuple[list, bool]: """Objets platypus PDF récapitulant les décisions de jury pour un type de jury (passage ou delivrance). Ramene: liste d'obj platypus, et un booléen indiquant si au moins un étudiant est diplômé. """ from app.scodoc import sco_pv_forms from app.but import jury_but_pv a_diplome = False # Jury de diplome si sem. terminal OU que l'on demande seulement les diplomés diplome = formsemestre.est_terminal() or only_diplome titre_jury, _ = jury_titres(formsemestre, diplome) titre_diplome = pv_title or formsemestre.formation.titre_officiel objects = [] style, style_bullet = _make_pv_styles(formsemestre) objects += [Spacer(0, 5 * mm)] objects += sco_pdf.make_paras( f""" Procès-verbal de {titre_jury} du département { sco_preferences.get_preference("DeptName", formsemestre.id) or "(sans nom)" } - {pv_title_session} { formsemestre.annee_scolaire()} - { formsemestre.annee_scolaire()+1} """, style, ) objects += sco_pdf.make_paras( f"""{titre_diplome}""", style, ) if pv_subtitle: objects += sco_pdf.make_paras( f"""{pv_subtitle}""", style, ) if show_title: objects += sco_pdf.make_paras( f"""Semestre: {formsemestre.titre}""", style, ) if sco_preferences.get_preference("PV_TITLE_WITH_VDI", formsemestre.id): objects += sco_pdf.make_paras( f"""Code: {code_vdi or ""}""", style, ) if date_jury: objects += sco_pdf.make_paras( f"""Jury tenu le {date_jury}""", style ) try: objects += sco_pdf.make_paras( "" + (sco_preferences.get_preference("PV_INTRO", formsemestre.id) or "") % { "Decnum": numero_arrete, "VDICode": code_vdi, "UnivName": sco_preferences.get_preference("UnivName", formsemestre.id), "Type": titre_jury, "Date": date_commission, # deprecated "date_commission": date_commission, } + "", style_bullet, ) except KeyError as exc: raise ScoPDFFormatError(msg=f"balise inconnue: {exc.args[0]}") from exc objects += sco_pdf.make_paras( """Le jury propose les décisions suivantes :""", style ) objects += [Spacer(0, 4 * mm)] if formsemestre.formation.is_apc(): rows, titles = jury_but_pv.pvjury_table_but( formsemestre, etudids=etudids, line_sep="
", only_diplome=only_diplome, anonymous=anonymous, with_paragraph_nom=with_paragraph_nom, ) columns_ids = list(titles.keys()) a_diplome = codes_cursus.ADM in [row.get("diplome") for row in rows] else: dpv = sco_pv_dict.dict_pvjury(formsemestre.id, etudids=etudids, with_prev=True) if not dpv: return [], False rows, titles, columns_ids = sco_pv_forms.pvjury_table( dpv, only_diplome=only_diplome, anonymous=anonymous, with_paragraph_nom=with_paragraph_nom, ) a_diplome = True in (x["validation_parcours"] for x in dpv["decisions"]) # convert to lists of tuples: columns_ids = ["etudid"] + columns_ids rows = [[line.get(x, "") for x in columns_ids] for line in rows] titles = [titles.get(x, "") for x in columns_ids] # Make a new cell style and put all cells in paragraphs cell_style = styles.ParagraphStyle({}) cell_style.fontSize = sco_preferences.get_preference( "SCOLAR_FONT_SIZE", formsemestre.id ) cell_style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre.id) cell_style.leading = 1.0 * sco_preferences.get_preference( "SCOLAR_FONT_SIZE", formsemestre.id ) # vertical space LINEWIDTH = 0.5 table_style = [ ( "FONTNAME", (0, 0), (-1, 0), sco_preferences.get_preference("PV_FONTNAME", formsemestre.id), ), ("LINEBELOW", (0, 0), (-1, 0), LINEWIDTH, Color(0, 0, 0)), ("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ("VALIGN", (0, 0), (-1, -1), "TOP"), ] titles = [f"{x}" for x in titles] def _format_pv_cell(x): """convert string to paragraph""" if isinstance(x, str): return Paragraph(SU(x), cell_style) else: return x widths_by_id = { "nom": 5 * cm, "cursus": 2.8 * cm, "ects": 1.4 * cm, "devenir": 1.8 * cm, "decision_but": 1.8 * cm, } table_cells = [[_format_pv_cell(x) for x in line[1:]] for line in ([titles] + rows)] widths = [widths_by_id.get(col_id) for col_id in columns_ids[1:]] objects.append( Table(table_cells, repeatRows=1, colWidths=widths, style=table_style) ) # Table sous la table avec signatures # Create paragraphs left_para = sco_pdf.make_paras( f"""{ sco_preferences.get_preference("PV_SIGNATURE_LEFT", formsemestre.id) or "" }""", style, ) # Signature du directeur right_para = sco_pdf.make_paras( f"""{ sco_preferences.get_preference("DirectorName", formsemestre.id) or "" }, { sco_preferences.get_preference("DirectorTitle", formsemestre.id) or "" }""", style, ) # Create a table with two cells data = [[left_para, right_para]] # Create the table table_signatures = Table(data) # Apply table style to remove borders table_signatures.setStyle( TableStyle( [ ("BOX", (0, 0), (-1, -1), 0, "white"), # No border for the table ("INNERGRID", (0, 0), (-1, -1), 0, "white"), # No inner grid lines ("VALIGN", (0, 0), (-1, -1), "TOP"), # Align content to the top ] ) ) objects.append(Spacer(0, 6 * mm)) objects.append(table_signatures) # Légende des codes codes = list(codes_cursus.CODES_EXPL.keys()) codes.sort() objects += sco_pdf.make_paras( """ Codes utilisés :""", style, ) L = [] for code in codes: L.append((code, codes_cursus.CODES_EXPL[code])) TableStyle2 = [ ( "FONTNAME", (0, 0), (-1, 0), sco_preferences.get_preference("PV_FONTNAME", formsemestre.id), ), ("LINEBELOW", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ("LINEABOVE", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ("LINEBEFORE", (0, 0), (0, -1), LINEWIDTH, Color(0, 0, 0)), ("LINEAFTER", (-1, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ] objects.append( Table( [[Paragraph(SU(x), cell_style) for x in line] for line in L], colWidths=(2 * cm, None), style=TableStyle2, ) ) return objects, a_diplome