From 60a77b8ba79da02f86943ea88417d0762189bfc4 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 14 Feb 2022 23:21:42 +0100 Subject: [PATCH] =?UTF-8?q?WIP:=20r=C3=A9organisation=20code=20bulletins?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- app/but/bulletin_but.py | 33 ++++++---- app/models/formsemestre.py | 1 + app/scodoc/sco_bulletins.py | 86 +++++++++++++-------------- app/scodoc/sco_bulletins_generator.py | 2 +- app/scodoc/sco_bulletins_pdf.py | 22 +++++-- app/scodoc/sco_preferences.py | 5 +- app/views/notes.py | 2 +- sco_version.py | 2 +- 9 files changed, 89 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 209a2a0176..f571216abc 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes). ### État actuel (26 jan 22) - - 9.1 (master) reproduit l'ensemble des fonctions de ScoDoc 7 (donc pas de BUT), sauf: + - 9.1.5x (master) reproduit l'ensemble des fonctions de ScoDoc 7 (donc pas de BUT), sauf: - ancien module "Entreprises" (obsolète) et ajoute la gestion du BUT. - - 9.2 (branche refactor_nt) est la version de développement. + - 9.2 (branche dev92) est la version de développement. ### Lignes de commandes diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index 5d4fd74f34..771746bf4a 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -9,14 +9,15 @@ import datetime from flask import url_for, g -from app.models.formsemestre import FormSemestre +from app.comp.res_but import ResultatsSemestreBUT +from app.models import FormSemestre, Identite from app.scodoc import sco_utils as scu from app.scodoc import sco_bulletins_json +from app.scodoc import sco_bulletins_pdf 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 class BulletinBUT: @@ -28,6 +29,7 @@ class BulletinBUT: def __init__(self, formsemestre: FormSemestre): """ """ self.res = ResultatsSemestreBUT(formsemestre) + self.prefs = sco_preferences.SemPreferences(formsemestre.id) def etud_ue_mod_results(self, etud, ue, modimpls) -> dict: "dict synthèse résultats dans l'UE pour les modules indiqués" @@ -84,7 +86,7 @@ class BulletinBUT: "saes": self.etud_ue_mod_results(etud, ue, res.saes), } if ue.type != UE_SPORT: - if sco_preferences.get_preference("bul_show_ue_rangs", res.formsemestre.id): + if self.prefs["bul_show_ue_rangs"]: rangs, effectif = res.ue_rangs[ue.id] rang = rangs[etud.id] else: @@ -155,9 +157,7 @@ class BulletinBUT: if e.visibulletin and ( modimpl_results.evaluations_etat[e.id].is_complete - or sco_preferences.get_preference( - "bul_show_all_evals", res.formsemestre.id - ) + or self.prefs["bul_show_all_evals"] ) ], } @@ -216,9 +216,11 @@ class BulletinBUT: else: return f"Bonus de {fmt_note(bonus_vect.iloc[0])}" - def bulletin_etud(self, etud, formsemestre, force_publishing=False) -> dict: - """Le bulletin de l'étudiant dans ce semestre. - Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai + def bulletin_etud( + self, etud: Identite, 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 (bulletins non publiés). """ res = self.res @@ -239,7 +241,9 @@ class BulletinBUT: }, "formsemestre_id": formsemestre.id, "etat_inscription": etat_inscription, - "options": sco_preferences.bulletin_option_affichage(formsemestre.id), + "options": sco_preferences.bulletin_option_affichage( + formsemestre.id, self.prefs + ), } if not published: return d @@ -312,3 +316,12 @@ class BulletinBUT: ) return d + + def bulletin_etud_complet(self, etud) -> dict: + """Bulletin dict complet avec toutes les infos pour les bulletins pdf""" + d = self.bulletin_etud(force_publishing=True) + d["filigranne"] = sco_bulletins_pdf.get_filigranne( + self.res.get_etud_etat(etud.id), self.prefs + ) + # XXX TODO A COMPLETER + raise NotImplementedError() diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 2451424843..2d491d7edc 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -117,6 +117,7 @@ class FormSemestre(db.Model): return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>" def to_dict(self): + "dict (compatible ScoDoc7)" d = dict(self.__dict__) d.pop("_sa_instance_state", None) # ScoDoc7 output_formators: (backward compat) diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index 93a926b64f..642841058a 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -28,30 +28,21 @@ """Génération des bulletins de notes """ -from app.models import formsemestre -import time -import pprint import email -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText -from email.mime.base import MIMEBase -from email.header import Header -from reportlab.lib.colors import Color -import urllib +import pprint +import time from flask import g, request from flask import url_for from flask_login import current_user from flask_mail import Message -from app.models.moduleimpls import ModuleImplInscription -import app.scodoc.sco_utils as scu -from app.scodoc.sco_utils import ModuleType -import app.scodoc.notesdb as ndb +from app import email from app import log +from app.but import bulletin_but from app.comp import res_sem from app.comp.res_common import NotesTableCompat -from app.models import FormSemestre +from app.models import FormSemestre, Identite, ModuleImplInscription from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc import html_sco_header @@ -60,9 +51,9 @@ from app.scodoc import sco_abs from app.scodoc import sco_abs_views from app.scodoc import sco_bulletins_generator from app.scodoc import sco_bulletins_json +from app.scodoc import sco_bulletins_pdf from app.scodoc import sco_bulletins_xml from app.scodoc import sco_codes_parcours -from app.scodoc import sco_cache from app.scodoc import sco_etud from app.scodoc import sco_evaluation_db from app.scodoc import sco_formations @@ -73,7 +64,9 @@ from app.scodoc import sco_photos from app.scodoc import sco_preferences from app.scodoc import sco_pvjury from app.scodoc import sco_users -from app import email +import app.scodoc.sco_utils as scu +from app.scodoc.sco_utils import ModuleType +import app.scodoc.notesdb as ndb # ----- CLASSES DE BULLETINS DE NOTES from app.scodoc import sco_bulletins_standard @@ -190,28 +183,18 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"): show_mention=prefs["bul_show_mention"], ) - if dpv: - I["decision_sem"] = dpv["decisions"][0]["decision_sem"] - else: - I["decision_sem"] = "" I.update(infos) I["etud_etat_html"] = _get_etud_etat_html( formsemestre.etuds_inscriptions[etudid].etat ) I["etud_etat"] = nt.get_etud_etat(etudid) - I["filigranne"] = "" + I["filigranne"] = sco_bulletins_pdf.get_filigranne(I["etud_etat"], prefs) I["demission"] = "" - if I["etud_etat"] == "D": + if I["etud_etat"] == scu.DEMISSION: I["demission"] = "(Démission)" - I["filigranne"] = "Démission" elif I["etud_etat"] == sco_codes_parcours.DEF: I["demission"] = "(Défaillant)" - I["filigranne"] = "Défaillant" - elif (prefs["bul_show_temporary"] and not I["decision_sem"]) or prefs[ - "bul_show_temporary_forced" - ]: - I["filigranne"] = prefs["bul_temporary_txt"] # --- Appreciations cnx = ndb.GetDBConnexion() @@ -687,6 +670,7 @@ def etud_descr_situation_semestre( descr_defaillance : "Défaillant" ou vide si non défaillant. decision_jury : "Validé", "Ajourné", ... (code semestre) descr_decision_jury : "Décision jury: Validé" (une phrase) + decision_sem : decisions_ue : noms (acronymes) des UE validées, séparées par des virgules. descr_decisions_ue : ' UE acquises: UE1, UE2', ou vide si pas de dec. ou si pas show_uevalid descr_mention : 'Mention Bien', ou vide si pas de mention ou si pas show_mention @@ -696,7 +680,7 @@ def etud_descr_situation_semestre( # --- Situation et décisions jury - # demission/inscription ? + # démission/inscription ? events = sco_etud.scolar_events_list( cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id} ) @@ -763,11 +747,15 @@ def etud_descr_situation_semestre( infos["situation"] += " " + infos["descr_defaillance"] dpv = sco_pvjury.dict_pvjury(formsemestre_id, etudids=[etudid]) + if dpv: + infos["decision_sem"] = dpv["decisions"][0]["decision_sem"] + else: + infos["decision_sem"] = "" if not show_decisions: return infos, dpv - # Decisions de jury: + # Décisions de jury: pv = dpv["decisions"][0] dec = "" if pv["decision_sem_descr"]: @@ -819,11 +807,15 @@ def formsemestre_bulletinetud( except: sco_etud.log_unknown_etud() raise ScoValueError("étudiant inconnu") - # API, donc erreurs admises en ScoValueError - sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True) + + formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id) + if not formsemestre: + # API, donc erreurs admises + raise ScoValueError(f"semestre {formsemestre_id} inconnu !") + sem = formsemestre.to_dict() bulletin = do_formsemestre_bulletinetud( - formsemestre_id, + formsemestre, etudid, format=format, version=version, @@ -835,7 +827,6 @@ def formsemestre_bulletinetud( filename = scu.bul_filename(sem, etud, format) return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0]) - sem = sco_formsemestre.get_formsemestre(formsemestre_id) H = [ _formsemestre_bulletinetud_header_html( etud, etudid, sem, formsemestre_id, format, version @@ -892,14 +883,14 @@ def can_send_bulletin_by_mail(formsemestre_id): def do_formsemestre_bulletinetud( - formsemestre_id, - etudid, + formsemestre: FormSemestre, + etudid: int, version="long", # short, long, selectedevals format="html", nohtml=False, - xml_with_decisions=False, # force decisions dans XML - force_publishing=False, # force publication meme si semestre non publie sur "portail" - prefer_mail_perso=False, # mails envoyes sur adresse perso si non vide + xml_with_decisions=False, # force décisions dans XML + force_publishing=False, # force publication meme si semestre non publié sur "portail" + prefer_mail_perso=False, # mails envoyés sur adresse perso si non vide ): """Génère le bulletin au format demandé. Retourne: (bul, filigranne) @@ -908,7 +899,7 @@ def do_formsemestre_bulletinetud( """ if format == "xml": bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud( - formsemestre_id, + formsemestre.id, etudid, xml_with_decisions=xml_with_decisions, force_publishing=force_publishing, @@ -919,7 +910,7 @@ def do_formsemestre_bulletinetud( elif format == "json": bul = sco_bulletins_json.make_json_formsemestre_bulletinetud( - formsemestre_id, + formsemestre.id, etudid, xml_with_decisions=xml_with_decisions, force_publishing=force_publishing, @@ -927,8 +918,13 @@ def do_formsemestre_bulletinetud( ) return bul, "" - I = formsemestre_bulletinetud_dict(formsemestre_id, etudid) - etud = I["etud"] + if formsemestre.formation.is_apc(): + etud = Identite.query.get(etudid) + r = bulletin_but.BulletinBUT(formsemestre) + I = r.bulletin_etud_complet(etud, formsemestre) + else: + I = formsemestre_bulletinetud_dict(formsemestre.id, etudid) + etud = I["etud"] if format == "html": htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud( @@ -954,7 +950,7 @@ def do_formsemestre_bulletinetud( elif format == "pdfmail": # format pdfmail: envoie le pdf par mail a l'etud, et affiche le html # check permission - if not can_send_bulletin_by_mail(formsemestre_id): + if not can_send_bulletin_by_mail(formsemestre.id): raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") if nohtml: @@ -983,7 +979,7 @@ def do_formsemestre_bulletinetud( ) + htm return h, I["filigranne"] # - mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr) + mail_bulletin(formsemestre.id, I, pdfdata, filename, recipient_addr) emaillink = '%s' % ( recipient_addr, recipient_addr, diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index 04a9efaee4..aafdc09f69 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -99,7 +99,7 @@ def bulletin_get_class_name_displayed(formsemestre_id): return "invalide ! (voir paramètres)" -class BulletinGenerator(object): +class BulletinGenerator: "Virtual superclass for PDF bulletin generators" "" # Here some helper methods # see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods diff --git a/app/scodoc/sco_bulletins_pdf.py b/app/scodoc/sco_bulletins_pdf.py index 94cfcf6bc2..748fd5a0b7 100644 --- a/app/scodoc/sco_bulletins_pdf.py +++ b/app/scodoc/sco_bulletins_pdf.py @@ -61,12 +61,10 @@ from reportlab.platypus.doctemplate import BaseDocTemplate from flask import g, request from app import log, ScoValueError -from app.comp import res_sem -from app.comp.res_common import NotesTableCompat from app.models import FormSemestre from app.scodoc import sco_cache -from app.scodoc import sco_formsemestre +from app.scodoc import sco_codes_parcours from app.scodoc import sco_pdf from app.scodoc import sco_preferences from app.scodoc import sco_etud @@ -190,7 +188,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): i = 1 for etud in formsemestre.get_inscrits(include_demdef=True, order=True): frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud( - formsemestre_id, + formsemestre, etud.id, format="pdfpart", version=version, @@ -239,8 +237,9 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"): filigrannes = {} i = 1 for sem in etud["sems"]: + formsemestre = FormSemestre.query.get(sem["formsemestre_id"]) frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud( - sem["formsemestre_id"], + formsemestre, etudid, format="pdfpart", version=version, @@ -275,3 +274,16 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"): ) return pdfdoc, filename + + +def get_filigranne(etud_etat: str, prefs) -> 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[ + "bul_show_temporary_forced" + ]: + return prefs["bul_temporary_txt"] + return "" diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index b639d5ee40..e0fcc53788 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -2114,7 +2114,7 @@ class BasePreferences(object): return form -class SemPreferences(object): +class SemPreferences: """Preferences for a formsemestre""" def __init__(self, formsemestre_id=None): @@ -2270,9 +2270,8 @@ def doc_preferences(): return "\n".join([" | ".join(x) for x in L]) -def bulletin_option_affichage(formsemestre_id: int) -> dict: +def bulletin_option_affichage(formsemestre_id: int, prefs: SemPreferences) -> dict: "dict avec les options d'affichages (préférences) pour ce semestre" - prefs = SemPreferences(formsemestre_id) fields = ( "bul_show_abs", "bul_show_abs_modules", diff --git a/app/views/notes.py b/app/views/notes.py index efad2808b8..b0b812a16f 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -1925,7 +1925,7 @@ def formsemestre_bulletins_mailetuds( nb_send = 0 for etudid in etudids: h, _ = sco_bulletins.do_formsemestre_bulletinetud( - formsemestre_id, + formsemestre, etudid, version=version, prefer_mail_perso=prefer_mail_perso, diff --git a/sco_version.py b/sco_version.py index 23fba00697..035ab07f29 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.1.56" +SCOVERSION = "9.2a-57" SCONAME = "ScoDoc"