############################################################################## # ScoDoc # Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. # See LICENSE ############################################################################## """Génération bulletin BUT au format PDF standard """ from reportlab.lib.colors import blue from reportlab.lib.units import cm, mm from reportlab.platypus import Paragraph, Spacer from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard from app.scodoc import gen_tables from app.scodoc.sco_codes_parcours import UE_SPORT class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): """Génération du bulletin de BUT au format PDF. self.infos est le dict issu de BulletinBUT.bulletin_etud_complet() """ # spécialisation du BulletinGeneratorStandard, ne pas présenter à l'utilisateur: list_in_menu = False scale_table_in_page = False # pas de mise à l'échelle pleine page auto multi_pages = True # plusieurs pages par bulletins small_fontsize = "8" def bul_table(self, format="html"): """Génère la table centrale du bulletin de notes Renvoie: - en HTML: une chaine - en PDF: une liste d'objets PLATYPUS (eg instance de Table). """ tables_infos = [ # ---- TABLE SYNTHESE UES self.but_table_synthese_ues(), ] if self.version != "short": tables_infos += [ # ---- TABLE RESSOURCES self.but_table_ressources(), # ---- TABLE SAE self.but_table_saes(), ] objects = [] for i, (col_keys, rows, pdf_style, col_widths) in enumerate(tables_infos): table = gen_tables.GenTable( rows=rows, columns_ids=col_keys, pdf_table_style=pdf_style, pdf_col_widths=[col_widths[k] for k in col_keys], preferences=self.preferences, html_class="notes_bulletin", html_class_ignore_default=True, html_with_td_classes=True, ) table_objects = table.gen(format=format) objects += table_objects # objects += [KeepInFrame(0, 0, table_objects, mode="shrink")] if i != 2: objects.append(Spacer(1, 6 * mm)) return objects def but_table_synthese_ues(self, title_bg=(182, 235, 255)): """La table de synthèse; pour chaque UE, liste des ressources et SAÉs avec leurs notes et leurs coefs. Renvoie: colkeys, P, pdf_style, colWidths - colkeys: nom des colonnes de la table (clés) - P : table (liste de dicts de chaines de caracteres) - pdf_style : commandes table Platypus - largeurs de colonnes pour PDF """ col_widths = { "titre": None, "moyenne": 2 * cm, "coef": 2 * cm, } title_bg = tuple(x / 255.0 for x in title_bg) nota_bene = """La moyenne des ressources et SAÉs dans une UE dépend des poids donnés aux évaluations.""" # elems pour générer table avec gen_table (liste de dicts) rows = [ # Ligne de titres { "titre": "Unités d'enseignement", "moyenne": Paragraph("<para align=right><b>Note/20</b></para>"), "coef": "Coef.", "_coef_pdf": Paragraph("<para align=right><b><i>Coef.</i></b></para>"), "_css_row_class": "note_bold", "_pdf_row_markup": ["b"], "_pdf_style": [ ("BACKGROUND", (0, 0), (-1, 0), title_bg), # ("BOTTOMPADDING", (0, 0), (-1, 0), 7), ], }, { "titre": nota_bene, "_titre_pdf": Paragraph( f"<para fontSize={self.small_fontsize}><i>{nota_bene}</i></para>" ), "_titre_colspan": 3, "_pdf_style": [ ("BACKGROUND", (0, 0), (-1, 0), title_bg), ("BOTTOMPADDING", (0, 0), (-1, 0), 7), ( "LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, blue, ), ], }, ] col_keys = ["titre", "coef", "moyenne"] # noms des colonnes à afficher for ue_acronym, ue in self.infos["ues"].items(): self.ue_rows(rows, ue_acronym, ue, title_bg) # Global pdf style commands: pdf_style = [ ("VALIGN", (0, 0), (-1, -1), "TOP"), ("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu: ] return col_keys, rows, pdf_style, col_widths def ue_rows(self, rows: list, ue_acronym: str, ue: dict, title_bg: tuple): "Décrit une UE dans la table synthèse: titre, sous-titre et liste modules" if (ue["type"] == UE_SPORT) and len(ue.get("modules", [])) == 0: # ne mentionne l'UE que s'il y a des modules return # 1er ligne titre UE moy_ue = ue.get("moyenne") t = { "titre": f"{ue_acronym} - {ue['titre']}", "moyenne": Paragraph( f"""<para align=right><b>{moy_ue.get("value", "-") if moy_ue is not None else "-" }</b></para>""" ), "_css_row_class": "note_bold", "_pdf_row_markup": ["b"], "_pdf_style": [ ( "LINEABOVE", (0, 0), (-1, 0), self.PDF_LINEWIDTH, self.PDF_LINECOLOR, ), ("BACKGROUND", (0, 0), (-1, 0), title_bg), ("BOTTOMPADDING", (0, 0), (-1, 0), 7), ], } rows.append(t) if ue["type"] == UE_SPORT: self.ue_sport_rows(rows, ue, title_bg) else: self.ue_std_rows(rows, ue, title_bg) def ue_std_rows(self, rows: list, ue: dict, title_bg: tuple): "Lignes décrivant une UE standard dans la table de synthèse" # 2eme ligne titre UE (bonus/malus/ects) if "ECTS" in ue: ects_txt = f'ECTS: {ue["ECTS"]["acquis"]:.3g} / {ue["ECTS"]["total"]:.3g}' else: ects_txt = "" t = { "titre": f"""Bonus: {ue['bonus']} - Malus: { ue["malus"]}""", "coef": ects_txt, "_coef_pdf": Paragraph(f"""<para align=left>{ects_txt}</para>"""), "_coef_colspan": 2, "_pdf_style": [ ("BACKGROUND", (0, 0), (-1, 0), title_bg), ("LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, self.PDF_LINECOLOR), # cadre autour du bonus/malus, gris clair ("BOX", (0, 0), (0, 0), self.PDF_LINEWIDTH, (0.7, 0.7, 0.7)), ], } rows.append(t) # Liste chaque ressource puis chaque SAE for mod_type in ("ressources", "saes"): for mod_code, mod in ue[mod_type].items(): t = { "titre": f"{mod_code} {self.infos[mod_type][mod_code]['titre']}", "moyenne": Paragraph(f'<para align=right>{mod["moyenne"]}</para>'), "coef": mod["coef"], "_coef_pdf": Paragraph( f"<para align=right><i>{mod['coef']}</i></para>" ), "_pdf_style": [ ( "LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, (0.7, 0.7, 0.7), # gris clair ) ], } rows.append(t) def ue_sport_rows(self, rows: list, ue: dict, title_bg: tuple): "Lignes décrivant l'UE bonus dans la table de synthèse" # UE BONUS for mod_code, mod in ue["modules"].items(): rows.append( { "titre": f"{mod_code or ''} {mod['titre'] or ''}", } ) self.evaluations_rows(rows, mod["evaluations"]) def but_table_ressources(self): """La table de synthèse; pour chaque ressources, note et liste d'évaluations Renvoie: colkeys, P, pdf_style, colWidths """ return self.bul_table_modules( mod_type="ressources", title="Ressources", title_bg=(248, 200, 68) ) def but_table_saes(self): "table des SAEs" return self.bul_table_modules( mod_type="saes", title="Situations d'apprentissage et d'évaluation", title_bg=(198, 255, 171), ) def bul_table_modules(self, mod_type=None, title="", title_bg=(248, 200, 68)): """Table ressources ou SAEs - colkeys: nom des colonnes de la table (clés) - P : table (liste de dicts de chaines de caracteres) - pdf_style : commandes table Platypus - largeurs de colonnes pour PDF """ # UE à utiliser pour les poids (# colonne/UE) ue_infos = self.infos["ues"] ue_acros = list( [k for k in ue_infos if ue_infos[k]["type"] != UE_SPORT] ) # ['RT1.1', 'RT2.1', 'RT3.1'] # Colonnes à afficher: col_keys = ["titre"] + ue_acros + ["coef", "moyenne"] # Largeurs des colonnes: col_widths = { "titre": None, # "poids": None, "moyenne": 2 * cm, "coef": 2 * cm, } for ue_acro in ue_acros: col_widths[ue_acro] = 12 * mm # largeur col. poids title_bg = tuple(x / 255.0 for x in title_bg) # elems pour générer table avec gen_table (liste de dicts) # Ligne de titres t = { "titre": title, # "_titre_colspan": 1 + len(ue_acros), "moyenne": "Note/20", "coef": "Coef.", "_coef_pdf": Paragraph("<para align=right><i>Coef.</i></para>"), "_css_row_class": "note_bold", "_pdf_row_markup": ["b"], "_pdf_style": [ ("BACKGROUND", (0, 0), (-1, 0), title_bg), ("BOTTOMPADDING", (0, 0), (-1, 0), 7), ( "LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, blue, ), ], } for ue_acro in ue_acros: t[ue_acro] = Paragraph( f"<para align=right fontSize={self.small_fontsize}><i>{ue_acro}</i></para>" ) rows = [t] for mod_code, mod in self.infos[mod_type].items(): # 1er ligne titre module t = { "titre": f"{mod_code} - {mod['titre']}", "_titre_colspan": 2 + len(ue_acros), "_css_row_class": "note_bold", "_pdf_row_markup": ["b"], "_pdf_style": [ ( "LINEABOVE", (0, 0), (-1, 0), self.PDF_LINEWIDTH, self.PDF_LINECOLOR, ), ("BACKGROUND", (0, 0), (-1, 0), title_bg), ("BOTTOMPADDING", (0, 0), (-1, 0), 7), ], } rows.append(t) # Evaluations: self.evaluations_rows(rows, mod["evaluations"], ue_acros) # Global pdf style commands: pdf_style = [ ("VALIGN", (0, 0), (-1, -1), "TOP"), ("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu: ] return col_keys, rows, pdf_style, col_widths def evaluations_rows(self, rows, evaluations, ue_acros=()): "lignes des évaluations" for e in evaluations: t = { "titre": f"{e['description'] or ''}", "moyenne": e["note"]["value"], "_moyenne_pdf": Paragraph( f"""<para align=right>{e["note"]["value"]}</para>""" ), "coef": e["coef"], "_coef_pdf": Paragraph( f"<para align=right fontSize={self.small_fontsize}><i>{e['coef']}</i></para>" ), "_pdf_style": [ ( "LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, (0.7, 0.7, 0.7), # gris clair ) ], } col_idx = 1 # 1ere col. poids for ue_acro in ue_acros: t[ue_acro] = Paragraph( f"""<para align=right fontSize={self.small_fontsize}><i>{ e["poids"].get(ue_acro, "") or ""}</i></para>""" ) t["_pdf_style"].append( ( "BOX", (col_idx, 0), (col_idx, 0), self.PDF_LINEWIDTH, (0.7, 0.7, 0.7), # gris clair ), ) col_idx += 1 rows.append(t)