# -*- 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 from reportlab.platypus import ( Paragraph, Spacer, PageBreak, Table, ) 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_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, 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_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'onj platypus, et un boolen 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 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"""VDI et 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) ) # Signature du directeur objects += sco_pdf.make_paras( f"""{ sco_preferences.get_preference("DirectorName", formsemestre.id) or "" }, { sco_preferences.get_preference("DirectorTitle", formsemestre.id) or "" }""", style, ) # 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