From fe6790738fbf2d686aa73fe430f09d4054b6d1b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 9 Mar 2022 18:03:18 +0100 Subject: [PATCH 1/5] =?UTF-8?q?Base=20test:=20compl=C3=A8te=20=C3=A9tudian?= =?UTF-8?q?ts=20et=20groupe=20par=20defaut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/groups.py | 6 +++ scodoc.py | 4 +- .../fakedatabase/create_test_api_database.py | 45 ++++++++++++------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/models/groups.py b/app/models/groups.py index 976d465be5..f6452cf7ca 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -63,6 +63,12 @@ class GroupDescr(db.Model): # "A", "C2", ... (NULL for 'all'): group_name = db.Column(db.String(GROUPNAME_STR_LEN)) + etuds = db.relationship( + "Identite", + secondary="group_membership", + lazy="dynamic", + ) + def __repr__(self): return ( f"""<{self.__class__.__name__} {self.id} "{self.group_name or '(tous)'}">""" diff --git a/scodoc.py b/scodoc.py index 9eea0f1c96..c728bed98f 100755 --- a/scodoc.py +++ b/scodoc.py @@ -14,6 +14,7 @@ import click import flask from flask.cli import with_appcontext from flask.templating import render_template +from flask_login import login_user, logout_user, current_user import psycopg2 import sqlalchemy @@ -45,7 +46,6 @@ cli.register(app) def make_shell_context(): from app.scodoc import notesdb as ndb from app.scodoc import sco_utils as scu - from flask_login import login_user, logout_user, current_user import app as mapp # le package app import numpy as np import pandas as pd @@ -504,6 +504,8 @@ def init_test_database(): ctx = app.test_request_context() ctx.push() + admin = User.query.filter_by(user_name="admin").first() + login_user(admin) create_test_api_database.init_test_database() diff --git a/tools/fakedatabase/create_test_api_database.py b/tools/fakedatabase/create_test_api_database.py index bfd346fa11..cd5a7e7e35 100644 --- a/tools/fakedatabase/create_test_api_database.py +++ b/tools/fakedatabase/create_test_api_database.py @@ -30,7 +30,12 @@ from flask_login import login_user from app import auth from app import models from app import db -from app.scodoc import sco_formations +from app.scodoc import ( + sco_formations, + sco_formsemestre, + sco_formsemestre_inscriptions, + sco_groups, +) from tools.fakeportal.gen_nomprenoms import nomprenom # La formation à utiliser: @@ -69,19 +74,26 @@ def create_user(dept): return user -def create_fake_etud(): +def create_fake_etud(dept): """Créé un faux étudiant et l'insère dans la base""" civilite = random.choice(("M", "F", "X")) nom, prenom = nomprenom(civilite) - etud = models.Identite(civilite=civilite, nom=nom, prenom=prenom) + etud = models.Identite(civilite=civilite, nom=nom, prenom=prenom, dept_id=dept.id) db.session.add(etud) db.session.commit() + adresse = models.Adresse( + etudid=etud.id, email=f"{etud.prenom}.{etud.nom}@example.com" + ) + db.session.add(adresse) + admission = models.Admission(etudid=etud.id) + db.session.add(admission) + db.session.commit() return etud -def create_etuds(nb=16): +def create_etuds(dept, nb=16): "create nb etuds" - return [create_fake_etud() for _ in range(nb)] + return [create_fake_etud(dept) for _ in range(nb)] def create_formsemestre(formation, user, semestre_idx=1): @@ -104,30 +116,29 @@ def create_formsemestre(formation, user, semestre_idx=1): ) db.session.add(modimpl) db.session.commit() + partition_id = sco_groups.partition_create( + formsemestre.id, default=True, redirect=False + ) + _group_id = sco_groups.create_group(partition_id, default=True) return formsemestre def inscrit_etudiants(etuds, formsemestre): """Inscrit les etudiants aux semestres et à tous ses modules""" for etud in etuds: - ins = models.FormSemestreInscription( - etudid=etud.id, formsemestre_id=formsemestre.id, etat="I" + sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( + formsemestre.id, + etud.id, + group_ids=[], + etat="I", + method="init db test", ) - db.session.add(ins) - for modimpl in formsemestre.modimpls: - insmod = models.ModuleImplInscription( - etudid=etud.id, moduleimpl_id=modimpl.id - ) - db.session.add(insmod) - db.session.commit() def init_test_database(): dept = init_departement("TAPI") user = create_user(dept) - login_user(user) - - etuds = create_etuds() + etuds = create_etuds(dept) formation = import_formation() formsemestre = create_formsemestre(formation, user) inscrit_etudiants(etuds, formsemestre) From a83d49187449bffd87b1d0eb15736408f3b82476 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 10 Mar 2022 00:50:36 +0100 Subject: [PATCH 2/5] Bulletins PDV BUT, v0 --- app/but/bulletin_but.py | 2 +- app/but/bulletin_but_pdf.py | 242 +++++++++++++++++++++----- app/scodoc/sco_bulletins_generator.py | 9 +- 3 files changed, 204 insertions(+), 49 deletions(-) diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index f64769fc91..6a3f6e2392 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -177,7 +177,7 @@ class BulletinBUT: "date": e.jour.isoformat() if e.jour else None, "heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None, "heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None, - "coef": e.coefficient, + "coef": fmt_note(e.coefficient), "poids": {p.ue.acronyme: p.poids for p in e.ue_poids}, "note": { "value": fmt_note( diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py index 5003e216e6..bd8791ccd8 100644 --- a/app/but/bulletin_but_pdf.py +++ b/app/but/bulletin_but_pdf.py @@ -7,19 +7,12 @@ """Génération bulletin BUT au format PDF standard """ -import datetime +from reportlab.platypus import KeepInFrame, Paragraph, Spacer + from app.scodoc.sco_pdf import blue, cm, mm -from flask import url_for, g -from app.models.formsemestre import FormSemestre - from app.scodoc import gen_tables -from app.scodoc import sco_utils as scu -from app.scodoc import sco_bulletins_json -from app.scodoc import sco_preferences -from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_utils import fmt_note -from app.comp.res_but import ResultatsSemestreBUT from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard @@ -31,6 +24,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): """ list_in_menu = False # spécialisation du BulletinGeneratorStandard, ne pas présenter à l'utilisateur + scale_table_in_page = False def bul_table(self, format="html"): """Génère la table centrale du bulletin de notes @@ -38,31 +32,35 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): - en HTML: une chaine - en PDF: une liste d'objets PLATYPUS (eg instance de Table). """ - formsemestre_id = self.infos["formsemestre_id"] - ( - synth_col_keys, - synth_P, - synth_pdf_style, - synth_col_widths, - ) = self.but_table_synthese() - # - table_synthese = gen_tables.GenTable( - rows=synth_P, - columns_ids=synth_col_keys, - pdf_table_style=synth_pdf_style, - pdf_col_widths=[synth_col_widths[k] for k in synth_col_keys], - preferences=self.preferences, - html_class="notes_bulletin", - html_class_ignore_default=True, - html_with_td_classes=True, - ) - # Ici on ajoutera table des ressources, tables des UE - # TODO + tables_infos = [ + # ---- TABLE SYNTHESE UES + self.but_table_synthese_ues(), + # ---- 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)) - # XXX à modifier pour générer plusieurs tables: - return table_synthese.gen(format=format) + return objects - def but_table_synthese(self): + 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 @@ -76,8 +74,30 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): "moyenne": 2 * cm, "coef": 2 * cm, } - P = [] # elems pour générer table avec gen_table (liste de dicts) - col_keys = ["titre", "moyenne"] # noms des colonnes à afficher + title_bg = tuple(x / 255.0 for x in title_bg) + # elems pour générer table avec gen_table (liste de dicts) + rows = [ + # Ligne de titres + { + "titre": "Unités d'enseignement", + "moyenne": "Note/20", + "coef": "Coef.", + "_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, + ), + ], + } + ] + col_keys = ["titre", "moyenne", "coef"] # noms des colonnes à afficher for ue_acronym, ue in self.infos["ues"].items(): # 1er ligne titre UE moy_ue = ue.get("moyenne") @@ -86,31 +106,163 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): "moyenne": moy_ue.get("value", "-") if moy_ue is not None else "-", "_css_row_class": "note_bold", "_pdf_row_markup": ["b"], - "_pdf_style": [], + "_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), + ], } - P.append(t) + rows.append(t) # 2eme ligne titre UE (bonus/malus/ects) t = { - "titre": "", - "moyenne": f"""Bonus: {ue['bonus']} - Malus: { - ue["malus"]} - ECTS: {ue["ECTS"]["acquis"]} / {ue["ECTS"]["total"]}""", - "_css_row_class": "note_bold", - "_pdf_row_markup": ["b"], + "titre": f"""Bonus: {ue['bonus']} - Malus: { + ue["malus"]}""", + "moyenne": f"""ECTS: {ue["ECTS"]["acquis"]} / {ue["ECTS"]["total"]}""", + "_moyenne_colspan": 2, + # "_css_row_class": "", + # "_pdf_row_markup": [""], "_pdf_style": [ + ("ALIGN", (0, 0), (1, 0), "RIGHT"), + ("TEXTCOLOR", (0, 0), (-1, 0), blue), + ("BACKGROUND", (0, 0), (-1, 0), title_bg), ( "LINEBELOW", (0, 0), (-1, 0), self.PDF_LINEWIDTH, self.PDF_LINECOLOR, - ) + ), ], } - P.append(t) - + rows.append(t) + # Liste chaque ressource + for mod_code, mod in ue["ressources"].items(): + t = { + "titre": f"{mod_code} {self.infos['ressources'][mod_code]['titre']}", + "moyenne": mod["moyenne"], + "coef": mod["coef"], + "_coef_pdf": Paragraph( + f"{mod['coef']}" + ), + "_pdf_style": [ + ( + "LINEBELOW", + (0, 0), + (-1, 0), + self.PDF_LINEWIDTH, + (0.7, 0.7, 0.7), # gris clair + ) + ], + } + rows.append(t) # 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, P, pdf_style, col_widths + return col_keys, rows, pdf_style, col_widths + + 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 + """ + col_widths = { + "titre": None, + "moyenne": 2 * cm, + "coef": 2 * cm, + } + title_bg = tuple(x / 255.0 for x in title_bg) + # elems pour générer table avec gen_table (liste de dicts) + rows = [ + # Ligne de titres + { + "titre": title, + "moyenne": "Note/20", + "coef": "Coef.", + "_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, + ), + ], + } + ] + col_keys = ["titre", "moyenne", "coef"] # noms des colonnes à afficher + for mod_code, mod in self.infos[mod_type].items(): + # 1er ligne titre module + t = { + "titre": f"{mod_code} - {mod['titre']}", + "moyenne": "", # pas de moyenne + "_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: + for e in mod["evaluations"]: + t = { + "titre": f"{e['description']}", + "moyenne": e["note"]["value"], + "coef": e["coef"], + "_coef_pdf": Paragraph( + f"{e['coef']}" + ), + "_pdf_style": [ + ( + "LINEBELOW", + (0, 0), + (-1, 0), + self.PDF_LINEWIDTH, + (0.7, 0.7, 0.7), # gris clair + ) + ], + } + rows.append(t) + # 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 diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index 2aeb792fb4..5fa07aa8f6 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -71,6 +71,7 @@ class BulletinGenerator: supported_formats = [] # should list supported formats, eg [ 'html', 'pdf' ] description = "superclass for bulletins" # description for user interface list_in_menu = True # la classe doit-elle est montrée dans le menu de config ? + scale_table_in_page = True # rescale la table sur 1 page def __init__( self, @@ -163,8 +164,9 @@ class BulletinGenerator: # signatures objects += self.bul_signatures_pdf() # pylint: disable=no-member - # Réduit sur une page - objects = [KeepInFrame(0, 0, objects, mode="shrink")] + if self.scale_table_in_page: + # Réduit sur une page + objects = [KeepInFrame(0, 0, objects, mode="shrink")] # if not stand_alone: objects.append(PageBreak()) # insert page break at end @@ -219,7 +221,7 @@ class BulletinGenerator: # --------------------------------------------------------------------------- def make_formsemestre_bulletinetud( infos, - version="long", # short, long, selectedevals + version=None, # short, long, selectedevals format="pdf", # html, pdf stand_alone=True, ): @@ -231,6 +233,7 @@ def make_formsemestre_bulletinetud( """ from app.scodoc import sco_preferences + version = version or "long" if not version in scu.BULLETINS_VERSIONS: raise ValueError("invalid version code !") From 793eca017a7e3651bdd46954fb5609a6c210eb6f Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 10 Mar 2022 00:51:13 +0100 Subject: [PATCH 3/5] =?UTF-8?q?Bonus=20Tarbes=20(=C3=A0=20tester)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/comp/bonus_spo.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/app/comp/bonus_spo.py b/app/comp/bonus_spo.py index 2feb05776e..b7c506a080 100644 --- a/app/comp/bonus_spo.py +++ b/app/comp/bonus_spo.py @@ -862,6 +862,51 @@ class BonusStDenis(BonusSportAdditif): bonus_max = 0.5 +class BonusTarbes(BonusSportAdditif): + """Calcul bonus optionnels (sport, culture), règle IUT de Tarbes. + +
    +
  • Les étudiants opeuvent suivre un ou plusieurs activités optionnelles notées. + La meilleure des notes obtenue est prise en compte, si elle est supérieure à 10/20. +
  • +
  • Le trentième des points au dessus de 10 est ajouté à la moyenne des UE. +
  • +
  • Exemple: un étudiant ayant 16/20 bénéficiera d'un bonus de (16-10)/30 = 0,2 points + sur chaque UE. +
  • +
+ """ + + name = "bonus_tarbes" + displayed_name = "IUT de Tazrbes" + seuil_moy_gen = 10.0 + proportion_point = 1 / 30.0 + classic_use_bonus_ues = True + + def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan): + """calcul du bonus""" + # Prend la note de chaque modimpl, sans considération d'UE + if len(sem_modimpl_moys_inscrits.shape) > 2: # apc + sem_modimpl_moys_inscrits = sem_modimpl_moys_inscrits[:, :, 0] + # ici sem_modimpl_moys_inscrits est nb_etuds x nb_mods_bonus, en APC et en classic + note_bonus_max = np.max(sem_modimpl_moys_inscrits, axis=1) # 1d, nb_etuds + ues = self.formsemestre.query_ues(with_sport=False).all() + ues_idx = [ue.id for ue in ues] + + if self.formsemestre.formation.is_apc(): # --- BUT + bonus_moy_arr = np.where( + note_bonus_max > self.seuil_moy_gen, + (note_bonus_max - self.seuil_moy_gen) * self.proportion_point, + 0.0, + ) + self.bonus_ues = pd.DataFrame( + np.stack([bonus_moy_arr] * len(ues)).T, + index=self.etuds_idx, + columns=ues_idx, + dtype=float, + ) + + class BonusTours(BonusDirect): """Calcul bonus sport & culture IUT Tours. From 9587159692070309d8311e09d6449b26b606816f Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 10 Mar 2022 01:24:37 +0100 Subject: [PATCH 4/5] format malus --- app/but/bulletin_but.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index 6a3f6e2392..a533fb801b 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -80,7 +80,7 @@ class BulletinBUT: "bonus": fmt_note(res.bonus_ues[ue.id][etud.id]) if res.bonus_ues is not None and ue.id in res.bonus_ues else fmt_note(0.0), - "malus": res.malus[ue.id][etud.id], + "malus": fmt_note(res.malus[ue.id][etud.id]), "capitalise": None, # "AAAA-MM-JJ" TODO #sco92 "ressources": self.etud_ue_mod_results(etud, ue, res.ressources), "saes": self.etud_ue_mod_results(etud, ue, res.saes), From 5efebb133679306080123c80eb95134715c4f897 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Thu, 10 Mar 2022 09:18:19 +0100 Subject: [PATCH 5/5] =?UTF-8?q?Bul.=20BUT:=20SAE=20dans=20synth=C3=A8se,?= =?UTF-8?q?=20pied=20de=20bul.=20sur=20meme=20page.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/but/bulletin_but_pdf.py | 43 ++++++++++++++-------------- app/scodoc/sco_bulletins_standard.py | 7 +++-- app/scodoc/sco_groups.py | 2 +- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py index bd8791ccd8..6a31768479 100644 --- a/app/but/bulletin_but_pdf.py +++ b/app/but/bulletin_but_pdf.py @@ -6,7 +6,7 @@ """Génération bulletin BUT au format PDF standard """ - +import itertools from reportlab.platypus import KeepInFrame, Paragraph, Spacer from app.scodoc.sco_pdf import blue, cm, mm @@ -141,26 +141,27 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): ], } rows.append(t) - # Liste chaque ressource - for mod_code, mod in ue["ressources"].items(): - t = { - "titre": f"{mod_code} {self.infos['ressources'][mod_code]['titre']}", - "moyenne": mod["moyenne"], - "coef": mod["coef"], - "_coef_pdf": Paragraph( - f"{mod['coef']}" - ), - "_pdf_style": [ - ( - "LINEBELOW", - (0, 0), - (-1, 0), - self.PDF_LINEWIDTH, - (0.7, 0.7, 0.7), # gris clair - ) - ], - } - rows.append(t) + # Liste chaque ressource puis 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": mod["moyenne"], + "coef": mod["coef"], + "_coef_pdf": Paragraph( + f"{mod['coef']}" + ), + "_pdf_style": [ + ( + "LINEBELOW", + (0, 0), + (-1, 0), + self.PDF_LINEWIDTH, + (0.7, 0.7, 0.7), # gris clair + ) + ], + } + rows.append(t) # Global pdf style commands: pdf_style = [ ("VALIGN", (0, 0), (-1, -1), "TOP"), diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py index fd84e7d940..95f6f172dc 100644 --- a/app/scodoc/sco_bulletins_standard.py +++ b/app/scodoc/sco_bulletins_standard.py @@ -46,10 +46,11 @@ de la forme %(XXX)s sont remplacées par la valeur de XXX, pour XXX dans: Balises img: actuellement interdites. """ +from reportlab.platypus import KeepTogether, Paragraph, Spacer, Table +from reportlab.lib.units import cm, mm +from reportlab.lib.colors import Color, blue import app.scodoc.sco_utils as scu -from app.scodoc.sco_pdf import Color, Paragraph, Spacer, Table -from app.scodoc.sco_pdf import blue, cm, mm from app.scodoc.sco_pdf import SU from app.scodoc import sco_preferences from app.scodoc.sco_permissions import Permission @@ -195,7 +196,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): # ----- if format == "pdf": - return Op + return [KeepTogether(Op)] elif format == "html": return "\n".join(H) diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 2a27b498d0..48a1dbba34 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -124,7 +124,7 @@ def get_partition(partition_id): {"partition_id": partition_id}, ) if not r: - raise ValueError("invalid partition_id (%s)" % partition_id) + raise ScoValueError(f"Partition inconnue (déjà supprimée ?) ({partition_id})") return r[0]