diff --git a/app/__init__.py b/app/__init__.py index 76f9471b..905cfce1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -201,7 +201,7 @@ def create_app(config_class=DevConfig): app.register_blueprint(auth_bp, url_prefix="/auth") from app.entreprises import bp as entreprises_bp - + app.register_blueprint(entreprises_bp, url_prefix="/ScoDoc/entreprises") from app.views import scodoc_bp @@ -295,10 +295,12 @@ def create_app(config_class=DevConfig): from app.scodoc.sco_bulletins_legacy import BulletinGeneratorLegacy from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard + from app.but.bulletin_but_pdf import BulletinGeneratorStandardBUT from app.scodoc.sco_bulletins_ucac import BulletinGeneratorUCAC - # l'ordre est important, le premeir sera le "défaut" pour les nouveaux départements. + # l'ordre est important, le premier sera le "défaut" pour les nouveaux départements. sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandard) + sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandardBUT) sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy) sco_bulletins_generator.register_bulletin_class(BulletinGeneratorUCAC) if app.testing or app.debug: diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index 771746bf..87ec62a2 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -11,8 +11,8 @@ import datetime from flask import url_for, g from app.comp.res_but import ResultatsSemestreBUT -from app.models import FormSemestre, Identite -from app.scodoc import sco_utils as scu +from app.models import FormSemestre, Identite, formsemestre +from app.scodoc import sco_bulletins, sco_utils as scu from app.scodoc import sco_bulletins_json from app.scodoc import sco_bulletins_pdf from app.scodoc import sco_preferences @@ -217,7 +217,7 @@ class BulletinBUT: return f"Bonus de {fmt_note(bonus_vect.iloc[0])}" def bulletin_etud( - self, etud: Identite, formsemestre, force_publishing=False + self, etud: Identite, formsemestre: FormSemestre, force_publishing=False ) -> dict: """Le bulletin de l'étudiant dans ce semestre: dict pour la version JSON / HTML. - Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai @@ -317,11 +317,29 @@ class BulletinBUT: return d - def bulletin_etud_complet(self, etud) -> dict: + def bulletin_etud_complet(self, etud: Identite) -> dict: """Bulletin dict complet avec toutes les infos pour les bulletins pdf""" - d = self.bulletin_etud(force_publishing=True) + d = self.bulletin_etud(etud, self.res.formsemestre, force_publishing=True) + d["etudid"] = etud.id + d["etud"] = d["etudiant"] + d["etud"]["nomprenom"] = etud.nomprenom + d.update(self.res.sem) d["filigranne"] = sco_bulletins_pdf.get_filigranne( - self.res.get_etud_etat(etud.id), self.prefs + self.res.get_etud_etat(etud.id), + self.prefs, + decision_sem=d["semestre"].get("decision_sem"), ) - # XXX TODO A COMPLETER - raise NotImplementedError() + # --- Absences + d["nbabs"], d["nbabsjust"] = self.res.formsemestre.get_abs_count(etud.id) + # --- Rangs + d[ + "rang_nt" + ] = f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}" + d["rang_txt"] = "Rang " + d["rang_nt"] + + # --- Appréciations + d.update( + sco_bulletins.get_appreciations_list(self.res.formsemestre.id, etud.id) + ) + # XXX TODO A COMPLETER ? + return d diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py new file mode 100644 index 00000000..5003e216 --- /dev/null +++ b/app/but/bulletin_but_pdf.py @@ -0,0 +1,116 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + +"""Génération bulletin BUT au format PDF standard +""" + +import datetime +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 + + +class BulletinGeneratorStandardBUT(BulletinGeneratorStandard): + """Génération du bulletin de BUT au format PDF. + + self.infos est le dict issu de BulletinBUT.bulletin_etud_complet() + """ + + list_in_menu = False # spécialisation du BulletinGeneratorStandard, ne pas présenter à l'utilisateur + + 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). + """ + 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 + + # XXX à modifier pour générer plusieurs tables: + return table_synthese.gen(format=format) + + def but_table_synthese(self): + """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, + } + P = [] # elems pour générer table avec gen_table (liste de dicts) + col_keys = ["titre", "moyenne"] # noms des colonnes à afficher + for ue_acronym, ue in self.infos["ues"].items(): + # 1er ligne titre UE + moy_ue = ue.get("moyenne") + t = { + "titre": f"{ue_acronym} - {ue['titre']}", + "moyenne": moy_ue.get("value", "-") if moy_ue is not None else "-", + "_css_row_class": "note_bold", + "_pdf_row_markup": ["b"], + "_pdf_style": [], + } + P.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"], + "_pdf_style": [ + ( + "LINEBELOW", + (0, 0), + (-1, 0), + self.PDF_LINEWIDTH, + self.PDF_LINECOLOR, + ) + ], + } + P.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 diff --git a/app/comp/moy_mat.py b/app/comp/moy_mat.py new file mode 100644 index 00000000..e5ba903c --- /dev/null +++ b/app/comp/moy_mat.py @@ -0,0 +1,52 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + +"""Calcul des moyennes de matières +""" + +# C'est un recalcul (optionnel) effectué _après_ le calcul standard. + +import numpy as np +import pandas as pd +from app.comp import moy_ue +from app.models.formsemestre import FormSemestre + +from app.scodoc.sco_codes_parcours import UE_SPORT +from app.scodoc.sco_utils import ModuleType + + +def compute_mat_moys_classic( + formsemestre: FormSemestre, + sem_matrix: np.array, + ues: list, + modimpl_inscr_df: pd.DataFrame, + modimpl_coefs: np.array, +) -> dict: + """Calcul des moyennes par matières. + Result: dict, { matiere_id : Series, index etudid } + """ + modimpls_std = [ + m + for m in formsemestre.modimpls_sorted + if (m.module.module_type == ModuleType.STANDARD) + and (m.module.ue.type != UE_SPORT) + ] + matiere_ids = {m.module.matiere.id for m in modimpls_std} + matiere_moy = {} # { matiere_id : moy pd.Series, index etudid } + for matiere_id in matiere_ids: + modimpl_mask = np.array( + [m.module.matiere.id == matiere_id for m in formsemestre.modimpls_sorted] + ) + etud_moy_gen, _, _ = moy_ue.compute_ue_moys_classic( + formsemestre, + sem_matrix=sem_matrix, + ues=ues, + modimpl_inscr_df=modimpl_inscr_df, + modimpl_coefs=modimpl_coefs, + modimpl_mask=modimpl_mask, + ) + matiere_moy[matiere_id] = etud_moy_gen + return matiere_moy diff --git a/app/comp/moy_ue.py b/app/comp/moy_ue.py index 8b98d2ef..563fb3b1 100644 --- a/app/comp/moy_ue.py +++ b/app/comp/moy_ue.py @@ -27,7 +27,6 @@ """Fonctions de calcul des moyennes d'UE (classiques ou BUT) """ -from re import X import numpy as np import pandas as pd diff --git a/app/comp/res_classic.py b/app/comp/res_classic.py index 1dfcfb4d..ecc1e500 100644 --- a/app/comp/res_classic.py +++ b/app/comp/res_classic.py @@ -15,7 +15,7 @@ from flask import g, url_for from app import db from app import log -from app.comp import moy_mod, moy_ue, inscr_mod +from app.comp import moy_mat, moy_mod, moy_ue, inscr_mod from app.comp.res_common import NotesTableCompat from app.comp.bonus_spo import BonusSport from app.models import ScoDocSiteConfig @@ -24,6 +24,7 @@ from app.models.formsemestre import FormSemestre from app.models.ues import UniteEns from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc import sco_preferences from app.scodoc.sco_utils import ModuleType @@ -133,6 +134,10 @@ class ResultatsSemestreClassic(NotesTableCompat): # --- Classements: self.compute_rangs() + # --- En option, moyennes par matières + if sco_preferences.get_preference("bul_show_matieres", self.formsemestre.id): + self.compute_moyennes_matieres() + def get_etud_mod_moy(self, moduleimpl_id: int, etudid: int) -> float: """La moyenne de l'étudiant dans le moduleimpl Result: valeur float (peut être NaN) ou chaîne "NI" (non inscrit ou DEM) @@ -158,6 +163,16 @@ class ResultatsSemestreClassic(NotesTableCompat): ), } + def compute_moyennes_matieres(self): + """Calcul les moyennes par matière. Doit être appelée au besoin, en fin de compute.""" + self.moyennes_matieres = moy_mat.compute_mat_moys_classic( + self.formsemestre, + self.sem_matrix, + self.ues, + self.modimpl_inscr_df, + self.modimpl_coefs, + ) + def compute_etud_ue_coef(self, etudid: int, ue: UniteEns) -> float: """Détermine le coefficient de l'UE pour cet étudiant. N'est utilisé que pour l'injection des UE capitalisées dans la diff --git a/app/comp/res_common.py b/app/comp/res_common.py index b019c977..5f652ec5 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -39,6 +39,7 @@ class ResultatsSemestre(ResultatsCache): "modimpl_inscr_df", "modimpls_results", "etud_coef_ue_df", + "moyennes_matieres", ) def __init__(self, formsemestre: FormSemestre): @@ -57,6 +58,8 @@ class ResultatsSemestre(ResultatsCache): self.etud_coef_ue_df = None """coefs d'UE effectifs pour chaque étudiant (pour form. classiques)""" self.validations = None + self.moyennes_matieres = {} + """Moyennes de matières, si calculées. { matiere_id : Series, index etudid }""" def compute(self): "Charge les notes et inscriptions et calcule toutes les moyennes" @@ -165,7 +168,6 @@ class ResultatsSemestre(ResultatsCache): """ # Supposant qu'il y a peu d'UE capitalisées, # on va soustraire la moyenne d'UE et ajouter celle de l'UE capitalisée. - # return # XXX XXX XXX if not self.validations: self.validations = res_sem.load_formsemestre_validations(self.formsemestre) ue_capitalisees = self.validations.ue_capitalisees @@ -184,7 +186,9 @@ class ResultatsSemestre(ResultatsCache): sum_coefs_ue = 0.0 for ue in self.formsemestre.query_ues(): ue_cap = self.get_etud_ue_status(etudid, ue.id) - if ue_cap and ue_cap["is_capitalized"]: + if ue_cap is None: + continue + if ue_cap["is_capitalized"]: recompute_mg = True coef = ue_cap["coef_ue"] if not np.isnan(ue_cap["moy"]): @@ -195,6 +199,12 @@ class ResultatsSemestre(ResultatsCache): # On doit prendre en compte une ou plusieurs UE capitalisées # et donc recalculer la moyenne générale self.etud_moy_gen[etudid] = sum_notes_ue / sum_coefs_ue + # Ajoute le bonus sport + if self.bonus is not None and self.bonus[etudid]: + self.etud_moy_gen[etudid] += self.bonus[etudid] + self.etud_moy_gen[etudid] = max( + 0.0, min(self.etud_moy_gen[etudid], 20.0) + ) def _get_etud_ue_cap(self, etudid, ue): """""" @@ -510,8 +520,9 @@ class NotesTableCompat(ResultatsSemestre): def get_etud_mat_moy(self, matiere_id, etudid): """moyenne d'un étudiant dans une matière (ou NA si pas de notes)""" - # non supporté en 9.2 - return "na" + if not self.moyennes_matieres: + return "nd" + return self.moyennes_matieres[matiere_id][etudid] def get_etud_mod_moy(self, moduleimpl_id: int, etudid: int) -> float: """La moyenne de l'étudiant dans le moduleimpl diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 18a0852d..1ae0a5a8 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -361,7 +361,7 @@ class FormSemestre(db.Model): def get_abs_count(self, etudid): """Les comptes d'absences de cet étudiant dans ce semestre: - tuple (nb abs non justifiées, nb abs justifiées) + tuple (nb abs, nb abs justifiées) Utilise un cache. """ from app.scodoc import sco_abs diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 73345296..071cbe8e 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -1037,7 +1037,7 @@ def get_abs_count(etudid, sem): def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso): """Les comptes d'absences de cet étudiant entre ces deux dates, incluses: - tuple (nb abs non justifiées, nb abs justifiées) + tuple (nb abs, nb abs justifiées) Utilise un cache. """ key = str(etudid) + "_" + date_debut_iso + "_" + date_fin_iso diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index e3c5d4ba..ba1c1b44 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -65,7 +65,7 @@ from app.scodoc import sco_preferences from app.scodoc import sco_pvjury from app.scodoc import sco_users import app.scodoc.sco_utils as scu -from app.scodoc.sco_utils import ModuleType +from app.scodoc.sco_utils import ModuleType, fmt_note import app.scodoc.notesdb as ndb # ----- CLASSES DE BULLETINS DE NOTES @@ -189,7 +189,9 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"): formsemestre.etuds_inscriptions[etudid].etat ) I["etud_etat"] = nt.get_etud_etat(etudid) - I["filigranne"] = sco_bulletins_pdf.get_filigranne(I["etud_etat"], prefs) + I["filigranne"] = sco_bulletins_pdf.get_filigranne( + I["etud_etat"], prefs, decision_dem=I["decision_sem"] + ) I["demission"] = "" if I["etud_etat"] == scu.DEMISSION: I["demission"] = "(Démission)" @@ -197,15 +199,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"): I["demission"] = "(Défaillant)" # --- Appreciations - cnx = ndb.GetDBConnexion() - apprecs = sco_etud.appreciations_list( - cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id} - ) - I["appreciations_list"] = apprecs - I["appreciations_txt"] = [x["date"] + ": " + x["comment"] for x in apprecs] - I["appreciations"] = I[ - "appreciations_txt" - ] # deprecated / keep it for backward compat in templates + I.update(get_appreciations_list(formsemestre_id, etudid)) # --- Notes ues = nt.get_ues_stat_dict() @@ -297,7 +291,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"): else: u["cur_moy_ue_txt"] = "bonus appliqué sur les UEs" else: - u["cur_moy_ue_txt"] = "bonus de %.3g points" % x + u["cur_moy_ue_txt"] = f"bonus de {fmt_note(x)} points" if nt.bonus_ues is not None: u["cur_moy_ue_txt"] += " (+ues)" u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"]) @@ -397,6 +391,21 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"): return C +def get_appreciations_list(formsemestre_id: int, etudid: int) -> dict: + """Appréciations pour cet étudiant dans ce semestre""" + cnx = ndb.GetDBConnexion() + apprecs = sco_etud.appreciations_list( + cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id} + ) + d = { + "appreciations_list": apprecs, + "appreciations_txt": [x["date"] + ": " + x["comment"] for x in apprecs], + } + # deprecated / keep it for backward compat in templates: + d["appreciations"] = d["appreciations_txt"] + return d + + def _get_etud_etat_html(etat: str) -> str: """chaine html représentant l'état (backward compat sco7)""" if etat == scu.INSCRIT: # "I" @@ -923,7 +932,7 @@ def do_formsemestre_bulletinetud( if formsemestre.formation.is_apc(): etud = Identite.query.get(etudid) r = bulletin_but.BulletinBUT(formsemestre) - I = r.bulletin_etud_complet(etud, formsemestre) + I = r.bulletin_etud_complet(etud) else: I = formsemestre_bulletinetud_dict(formsemestre.id, etudid) etud = I["etud"] diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index aafdc09f..ceeb0aac 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -63,41 +63,6 @@ from app.scodoc import sco_pdf from app.scodoc.sco_pdf import PDFLOCK import sco_version -# Liste des types des classes de générateurs de bulletins PDF: -BULLETIN_CLASSES = collections.OrderedDict() - - -def register_bulletin_class(klass): - BULLETIN_CLASSES[klass.__name__] = klass - - -def bulletin_class_descriptions(): - return [x.description for x in BULLETIN_CLASSES.values()] - - -def bulletin_class_names(): - return list(BULLETIN_CLASSES.keys()) - - -def bulletin_default_class_name(): - return bulletin_class_names()[0] - - -def bulletin_get_class(class_name): - return BULLETIN_CLASSES[class_name] - - -def bulletin_get_class_name_displayed(formsemestre_id): - """Le nom du générateur utilisé, en clair""" - from app.scodoc import sco_preferences - - bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id) - try: - gen_class = bulletin_get_class(bul_class_name) - return gen_class.description - except: - return "invalide ! (voir paramètres)" - class BulletinGenerator: "Virtual superclass for PDF bulletin generators" "" @@ -105,6 +70,7 @@ class BulletinGenerator: # see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods 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 ? def __init__( self, @@ -270,9 +236,14 @@ def make_formsemestre_bulletinetud( formsemestre_id = infos["formsemestre_id"] bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id) - try: + + gen_class = None + if infos.get("type") == "BUT" and format.startswith("pdf"): + gen_class = bulletin_get_class(bul_class_name + "BUT") + if gen_class is None: gen_class = bulletin_get_class(bul_class_name) - except: + + if gen_class is None: raise ValueError( "Type de bulletin PDF invalide (paramètre: %s)" % bul_class_name ) @@ -313,3 +284,48 @@ def make_formsemestre_bulletinetud( filename = bul_generator.get_filename() return data, filename + + +#### + +# Liste des types des classes de générateurs de bulletins PDF: +BULLETIN_CLASSES = collections.OrderedDict() + + +def register_bulletin_class(klass): + BULLETIN_CLASSES[klass.__name__] = klass + + +def bulletin_class_descriptions(): + return [x.description for x in BULLETIN_CLASSES.values()] + + +def bulletin_class_names() -> list[str]: + "Liste les noms des classes de bulletins à présenter à l'utilisateur" + return [ + class_name + for class_name in BULLETIN_CLASSES + if BULLETIN_CLASSES[class_name].list_in_menu + ] + + +def bulletin_default_class_name(): + return bulletin_class_names()[0] + + +def bulletin_get_class(class_name: str) -> BulletinGenerator: + """La class de génération de bulletin de ce nom, + ou None si pas trouvée + """ + return BULLETIN_CLASSES.get(class_name) + + +def bulletin_get_class_name_displayed(formsemestre_id): + """Le nom du générateur utilisé, en clair""" + from app.scodoc import sco_preferences + + bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id) + gen_class = bulletin_get_class(bul_class_name) + if gen_class is None: + return "invalide ! (voir paramètres)" + return gen_class.description diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py index 748fd5a0..1df2ca66 100644 --- a/app/scodoc/sco_bulletins_pdf.py +++ b/app/scodoc/sco_bulletins_pdf.py @@ -276,13 +276,13 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"): return pdfdoc, filename -def get_filigranne(etud_etat: str, prefs) -> str: +def get_filigranne(etud_etat: str, prefs, decision_sem=None) -> str: """Texte à placer en "filigranne" sur le bulletin pdf""" if etud_etat == scu.DEMISSION: return "Démission" elif etud_etat == sco_codes_parcours.DEF: return "Défaillant" - elif (prefs["bul_show_temporary"] and not I["decision_sem"]) or prefs[ + elif (prefs["bul_show_temporary"] and not decision_sem) or prefs[ "bul_show_temporary_forced" ]: return prefs["bul_temporary_txt"] diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py index 60c6f2a0..0485a756 100644 --- a/app/scodoc/sco_bulletins_standard.py +++ b/app/scodoc/sco_bulletins_standard.py @@ -66,7 +66,8 @@ from app.scodoc import sco_groups from app.scodoc import sco_evaluations from app.scodoc import gen_tables -# Important: Le nom de la classe ne doit pas changer (bien le choisir), car il sera stocké en base de données (dans les préférences) +# Important: Le nom de la classe ne doit pas changer (bien le choisir), +# car il sera stocké en base de données (dans les préférences) class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): description = "standard ScoDoc (version 2011)" # la description doit être courte: elle apparait dans le menu de paramètrage ScoDoc supported_formats = ["html", "pdf"] @@ -264,11 +265,11 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): def build_bulletin_table(self): """Génère la table centrale du bulletin de notes - Renvoie: colkeys, P, pdf_style, colWidths - - colkeys: nom des colonnes de la table (clés) - - table (liste de dicts de chaines de caracteres) - - style (commandes table Platypus) - - largeurs de colonnes pour PDF + Renvoie: col_keys, P, pdf_style, col_widths + - col_keys: nom des colonnes de la table (clés) + - table: liste de dicts de chaines de caractères + - pdf_style: commandes table Platypus + - col_widths: largeurs de colonnes pour PDF """ I = self.infos P = [] # elems pour générer table avec gen_table (liste de dicts) @@ -287,25 +288,25 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): with_col_coef = prefs["bul_show_coef"] with_col_ects = prefs["bul_show_ects"] - colkeys = ["titre", "module"] # noms des colonnes à afficher + col_keys = ["titre", "module"] # noms des colonnes à afficher if with_col_rang: - colkeys += ["rang"] + col_keys += ["rang"] if with_col_minmax: - colkeys += ["min"] + col_keys += ["min"] if with_col_moypromo: - colkeys += ["moy"] + col_keys += ["moy"] if with_col_minmax: - colkeys += ["max"] - colkeys += ["note"] + col_keys += ["max"] + col_keys += ["note"] if with_col_coef: - colkeys += ["coef"] + col_keys += ["coef"] if with_col_ects: - colkeys += ["ects"] + col_keys += ["ects"] if with_col_abs: - colkeys += ["abs"] + col_keys += ["abs"] colidx = {} # { nom_colonne : indice à partir de 0 } (pour styles platypus) i = 0 - for k in colkeys: + for k in col_keys: colidx[k] = i i += 1 @@ -313,7 +314,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): bul_pdf_mod_colwidth = float(prefs["bul_pdf_mod_colwidth"]) * cm else: bul_pdf_mod_colwidth = None - colWidths = { + col_widths = { "titre": None, "module": bul_pdf_mod_colwidth, "min": 1.5 * cm, @@ -541,7 +542,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator): ("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu: ] # - return colkeys, P, pdf_style, colWidths + return col_keys, P, pdf_style, col_widths def _list_modules( self, diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index d8be56f2..2c846515 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -595,11 +595,12 @@ def formsemestre_description_table(formsemestre_id, with_evals=False): """Description du semestre sous forme de table exportable Liste des modules et de leurs coefficients """ - sem = sco_formsemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id) - F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] + F = sco_formations.formation_list(args={"formation_id": formsemestre.formation_id})[ + 0 + ] parcours = sco_codes_parcours.get_parcours_from_code(F["type_parcours"]) Mlist = sco_moduleimpl.moduleimpl_withmodule_list( formsemestre_id=formsemestre_id, sort_by_ue=True @@ -709,7 +710,7 @@ def formsemestre_description_table(formsemestre_id, with_evals=False): titles["coefficient"] = "Coef. éval." titles["evalcomplete_str"] = "Complète" titles["publish_incomplete_str"] = "Toujours Utilisée" - title = "%s %s" % (parcours.SESSION_NAME.capitalize(), sem["titremois"]) + title = "%s %s" % (parcours.SESSION_NAME.capitalize(), formsemestre.titre_mois()) return GenTable( columns_ids=columns_ids, diff --git a/sco_version.py b/sco_version.py index 70dbc47d..4e4f558d 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.2a-61" +SCOVERSION = "9.2a-62" SCONAME = "ScoDoc"