WIP: réorganisation code bulletins

This commit is contained in:
Emmanuel Viennet 2022-02-14 23:21:42 +01:00
parent c361ccb362
commit 60a77b8ba7
9 changed files with 89 additions and 68 deletions

View File

@ -20,10 +20,10 @@ Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes).
### État actuel (26 jan 22) ### É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. - 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 ### Lignes de commandes

View File

@ -9,14 +9,15 @@
import datetime import datetime
from flask import url_for, g 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_utils as scu
from app.scodoc import sco_bulletins_json from app.scodoc import sco_bulletins_json
from app.scodoc import sco_bulletins_pdf
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_codes_parcours import UE_SPORT
from app.scodoc.sco_utils import fmt_note from app.scodoc.sco_utils import fmt_note
from app.comp.res_but import ResultatsSemestreBUT
class BulletinBUT: class BulletinBUT:
@ -28,6 +29,7 @@ class BulletinBUT:
def __init__(self, formsemestre: FormSemestre): def __init__(self, formsemestre: FormSemestre):
""" """ """ """
self.res = ResultatsSemestreBUT(formsemestre) self.res = ResultatsSemestreBUT(formsemestre)
self.prefs = sco_preferences.SemPreferences(formsemestre.id)
def etud_ue_mod_results(self, etud, ue, modimpls) -> dict: def etud_ue_mod_results(self, etud, ue, modimpls) -> dict:
"dict synthèse résultats dans l'UE pour les modules indiqués" "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), "saes": self.etud_ue_mod_results(etud, ue, res.saes),
} }
if ue.type != UE_SPORT: 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] rangs, effectif = res.ue_rangs[ue.id]
rang = rangs[etud.id] rang = rangs[etud.id]
else: else:
@ -155,9 +157,7 @@ class BulletinBUT:
if e.visibulletin if e.visibulletin
and ( and (
modimpl_results.evaluations_etat[e.id].is_complete modimpl_results.evaluations_etat[e.id].is_complete
or sco_preferences.get_preference( or self.prefs["bul_show_all_evals"]
"bul_show_all_evals", res.formsemestre.id
)
) )
], ],
} }
@ -216,9 +216,11 @@ class BulletinBUT:
else: else:
return f"Bonus de {fmt_note(bonus_vect.iloc[0])}" return f"Bonus de {fmt_note(bonus_vect.iloc[0])}"
def bulletin_etud(self, etud, formsemestre, force_publishing=False) -> dict: def bulletin_etud(
"""Le bulletin de l'étudiant dans ce semestre. self, etud: Identite, formsemestre, force_publishing=False
Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai ) -> 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). (bulletins non publiés).
""" """
res = self.res res = self.res
@ -239,7 +241,9 @@ class BulletinBUT:
}, },
"formsemestre_id": formsemestre.id, "formsemestre_id": formsemestre.id,
"etat_inscription": etat_inscription, "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: if not published:
return d return d
@ -312,3 +316,12 @@ class BulletinBUT:
) )
return d 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()

View File

@ -117,6 +117,7 @@ class FormSemestre(db.Model):
return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>" return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>"
def to_dict(self): def to_dict(self):
"dict (compatible ScoDoc7)"
d = dict(self.__dict__) d = dict(self.__dict__)
d.pop("_sa_instance_state", None) d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat) # ScoDoc7 output_formators: (backward compat)

View File

@ -28,30 +28,21 @@
"""Génération des bulletins de notes """Génération des bulletins de notes
""" """
from app.models import formsemestre
import time
import pprint
import email import email
from email.mime.multipart import MIMEMultipart import pprint
from email.mime.text import MIMEText import time
from email.mime.base import MIMEBase
from email.header import Header
from reportlab.lib.colors import Color
import urllib
from flask import g, request from flask import g, request
from flask import url_for from flask import url_for
from flask_login import current_user from flask_login import current_user
from flask_mail import Message from flask_mail import Message
from app.models.moduleimpls import ModuleImplInscription
import app.scodoc.sco_utils as scu from app import email
from app.scodoc.sco_utils import ModuleType
import app.scodoc.notesdb as ndb
from app import log from app import log
from app.but import bulletin_but
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_common import NotesTableCompat 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_permissions import Permission
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc import html_sco_header 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_abs_views
from app.scodoc import sco_bulletins_generator from app.scodoc import sco_bulletins_generator
from app.scodoc import sco_bulletins_json 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_bulletins_xml
from app.scodoc import sco_codes_parcours from app.scodoc import sco_codes_parcours
from app.scodoc import sco_cache
from app.scodoc import sco_etud from app.scodoc import sco_etud
from app.scodoc import sco_evaluation_db from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formations 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_preferences
from app.scodoc import sco_pvjury from app.scodoc import sco_pvjury
from app.scodoc import sco_users 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 # ----- CLASSES DE BULLETINS DE NOTES
from app.scodoc import sco_bulletins_standard 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"], show_mention=prefs["bul_show_mention"],
) )
if dpv:
I["decision_sem"] = dpv["decisions"][0]["decision_sem"]
else:
I["decision_sem"] = ""
I.update(infos) I.update(infos)
I["etud_etat_html"] = _get_etud_etat_html( I["etud_etat_html"] = _get_etud_etat_html(
formsemestre.etuds_inscriptions[etudid].etat formsemestre.etuds_inscriptions[etudid].etat
) )
I["etud_etat"] = nt.get_etud_etat(etudid) I["etud_etat"] = nt.get_etud_etat(etudid)
I["filigranne"] = "" I["filigranne"] = sco_bulletins_pdf.get_filigranne(I["etud_etat"], prefs)
I["demission"] = "" I["demission"] = ""
if I["etud_etat"] == "D": if I["etud_etat"] == scu.DEMISSION:
I["demission"] = "(Démission)" I["demission"] = "(Démission)"
I["filigranne"] = "Démission"
elif I["etud_etat"] == sco_codes_parcours.DEF: elif I["etud_etat"] == sco_codes_parcours.DEF:
I["demission"] = "(Défaillant)" 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 # --- Appreciations
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
@ -687,6 +670,7 @@ def etud_descr_situation_semestre(
descr_defaillance : "Défaillant" ou vide si non défaillant. descr_defaillance : "Défaillant" ou vide si non défaillant.
decision_jury : "Validé", "Ajourné", ... (code semestre) decision_jury : "Validé", "Ajourné", ... (code semestre)
descr_decision_jury : "Décision jury: Validé" (une phrase) 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. 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_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 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 # --- Situation et décisions jury
# demission/inscription ? # démission/inscription ?
events = sco_etud.scolar_events_list( events = sco_etud.scolar_events_list(
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id} cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
) )
@ -763,11 +747,15 @@ def etud_descr_situation_semestre(
infos["situation"] += " " + infos["descr_defaillance"] infos["situation"] += " " + infos["descr_defaillance"]
dpv = sco_pvjury.dict_pvjury(formsemestre_id, etudids=[etudid]) 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: if not show_decisions:
return infos, dpv return infos, dpv
# Decisions de jury: # Décisions de jury:
pv = dpv["decisions"][0] pv = dpv["decisions"][0]
dec = "" dec = ""
if pv["decision_sem_descr"]: if pv["decision_sem_descr"]:
@ -819,11 +807,15 @@ def formsemestre_bulletinetud(
except: except:
sco_etud.log_unknown_etud() sco_etud.log_unknown_etud()
raise ScoValueError("étudiant inconnu") 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( bulletin = do_formsemestre_bulletinetud(
formsemestre_id, formsemestre,
etudid, etudid,
format=format, format=format,
version=version, version=version,
@ -835,7 +827,6 @@ def formsemestre_bulletinetud(
filename = scu.bul_filename(sem, etud, format) filename = scu.bul_filename(sem, etud, format)
return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0]) return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0])
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [ H = [
_formsemestre_bulletinetud_header_html( _formsemestre_bulletinetud_header_html(
etud, etudid, sem, formsemestre_id, format, version etud, etudid, sem, formsemestre_id, format, version
@ -892,14 +883,14 @@ def can_send_bulletin_by_mail(formsemestre_id):
def do_formsemestre_bulletinetud( def do_formsemestre_bulletinetud(
formsemestre_id, formsemestre: FormSemestre,
etudid, etudid: int,
version="long", # short, long, selectedevals version="long", # short, long, selectedevals
format="html", format="html",
nohtml=False, nohtml=False,
xml_with_decisions=False, # force decisions dans XML xml_with_decisions=False, # force décisions dans XML
force_publishing=False, # force publication meme si semestre non publie sur "portail" force_publishing=False, # force publication meme si semestre non publié sur "portail"
prefer_mail_perso=False, # mails envoyes sur adresse perso si non vide prefer_mail_perso=False, # mails envoyés sur adresse perso si non vide
): ):
"""Génère le bulletin au format demandé. """Génère le bulletin au format demandé.
Retourne: (bul, filigranne) Retourne: (bul, filigranne)
@ -908,7 +899,7 @@ def do_formsemestre_bulletinetud(
""" """
if format == "xml": if format == "xml":
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud( bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
formsemestre_id, formsemestre.id,
etudid, etudid,
xml_with_decisions=xml_with_decisions, xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing, force_publishing=force_publishing,
@ -919,7 +910,7 @@ def do_formsemestre_bulletinetud(
elif format == "json": elif format == "json":
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud( bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
formsemestre_id, formsemestre.id,
etudid, etudid,
xml_with_decisions=xml_with_decisions, xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing, force_publishing=force_publishing,
@ -927,7 +918,12 @@ def do_formsemestre_bulletinetud(
) )
return bul, "" return bul, ""
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid) 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"] etud = I["etud"]
if format == "html": if format == "html":
@ -954,7 +950,7 @@ def do_formsemestre_bulletinetud(
elif format == "pdfmail": elif format == "pdfmail":
# format pdfmail: envoie le pdf par mail a l'etud, et affiche le html # format pdfmail: envoie le pdf par mail a l'etud, et affiche le html
# check permission # 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 !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if nohtml: if nohtml:
@ -983,7 +979,7 @@ def do_formsemestre_bulletinetud(
) + htm ) + htm
return h, I["filigranne"] return h, I["filigranne"]
# #
mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr) mail_bulletin(formsemestre.id, I, pdfdata, filename, recipient_addr)
emaillink = '<a class="stdlink" href="mailto:%s">%s</a>' % ( emaillink = '<a class="stdlink" href="mailto:%s">%s</a>' % (
recipient_addr, recipient_addr,
recipient_addr, recipient_addr,

View File

@ -99,7 +99,7 @@ def bulletin_get_class_name_displayed(formsemestre_id):
return "invalide ! (voir paramètres)" return "invalide ! (voir paramètres)"
class BulletinGenerator(object): class BulletinGenerator:
"Virtual superclass for PDF bulletin generators" "" "Virtual superclass for PDF bulletin generators" ""
# Here some helper methods # Here some helper methods
# see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods # see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods

View File

@ -61,12 +61,10 @@ from reportlab.platypus.doctemplate import BaseDocTemplate
from flask import g, request from flask import g, request
from app import log, ScoValueError 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.models import FormSemestre
from app.scodoc import sco_cache 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_pdf
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_etud from app.scodoc import sco_etud
@ -190,7 +188,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
i = 1 i = 1
for etud in formsemestre.get_inscrits(include_demdef=True, order=True): for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud( frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre_id, formsemestre,
etud.id, etud.id,
format="pdfpart", format="pdfpart",
version=version, version=version,
@ -239,8 +237,9 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
filigrannes = {} filigrannes = {}
i = 1 i = 1
for sem in etud["sems"]: for sem in etud["sems"]:
formsemestre = FormSemestre.query.get(sem["formsemestre_id"])
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud( frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
sem["formsemestre_id"], formsemestre,
etudid, etudid,
format="pdfpart", format="pdfpart",
version=version, version=version,
@ -275,3 +274,16 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
) )
return pdfdoc, filename 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 ""

View File

@ -2114,7 +2114,7 @@ class BasePreferences(object):
return form return form
class SemPreferences(object): class SemPreferences:
"""Preferences for a formsemestre""" """Preferences for a formsemestre"""
def __init__(self, formsemestre_id=None): def __init__(self, formsemestre_id=None):
@ -2270,9 +2270,8 @@ def doc_preferences():
return "\n".join([" | ".join(x) for x in L]) 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" "dict avec les options d'affichages (préférences) pour ce semestre"
prefs = SemPreferences(formsemestre_id)
fields = ( fields = (
"bul_show_abs", "bul_show_abs",
"bul_show_abs_modules", "bul_show_abs_modules",

View File

@ -1925,7 +1925,7 @@ def formsemestre_bulletins_mailetuds(
nb_send = 0 nb_send = 0
for etudid in etudids: for etudid in etudids:
h, _ = sco_bulletins.do_formsemestre_bulletinetud( h, _ = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre_id, formsemestre,
etudid, etudid,
version=version, version=version,
prefer_mail_perso=prefer_mail_perso, prefer_mail_perso=prefer_mail_perso,

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.1.56" SCOVERSION = "9.2a-57"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"