Bulletins et PV: champs décisions jury BUT

This commit is contained in:
Emmanuel Viennet 2022-07-08 18:09:45 +02:00
parent 8e463e5971
commit bf27fcbdc6
10 changed files with 97 additions and 54 deletions

View File

@ -326,10 +326,7 @@ class BulletinBUT:
semestre_infos["ECTS"] = {"acquis": ects_acquis, "total": ects_tot}
if sco_preferences.get_preference("bul_show_decision", formsemestre.id):
semestre_infos.update(
sco_bulletins_json.dict_decision_jury(etud.id, formsemestre.id)
)
semestre_infos.update(
but_validations.dict_decision_jury(etud, formsemestre)
sco_bulletins_json.dict_decision_jury(etud, formsemestre)
)
if etat_inscription == scu.INSCRIT:
# moyenne des moyennes générales du semestre

View File

@ -67,7 +67,7 @@ class ApcValidationRCUE(db.Model):
return self.ue2.niveau_competence
def to_dict_bul(self) -> dict:
"Export dict pour bulletins"
"Export dict pour bulletins: le code et le niveau de compétence"
return {"code": self.code, "niveau": self.niveau().to_dict_bul()}
@ -309,7 +309,9 @@ class ApcValidationAnnee(db.Model):
def dict_decision_jury(etud: Identite, formsemestre: FormSemestre) -> dict:
"""
Un dict avec les décisions de jury BUT enregistrées.
Un dict avec les décisions de jury BUT enregistrées:
- decision_rcue : list[dict]
- decision_annee : dict
Ne reprend pas les décisions d'UE, non spécifiques au BUT.
"""
decisions = {}

View File

@ -434,7 +434,7 @@ def _get_etud_etat_html(etat: str) -> str:
elif etat == scu.DEF: # "DEF"
return ' <font color="red">(DEFAILLANT)</font> '
else:
return ' <font color="red">(%s)</font> ' % etat
return f' <font color="red">({etat})</font> '
def _sort_mod_by_matiere(modlist, nt, etudid):
@ -707,6 +707,7 @@ def etud_descr_situation_semestre(
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
"""
# Fonction utilisée par tous les bulletins (APC ou classiques)
cnx = ndb.GetDBConnexion()
infos = scu.DictDefault(defaultvalue="")
@ -728,8 +729,7 @@ def etud_descr_situation_semestre(
# il y a eu une erreur qui a laissé un event 'inscription'
# on l'efface:
log(
"etud_descr_situation_semestre: removing duplicate INSCRIPTION event for etudid=%s !"
% etudid
f"etud_descr_situation_semestre: removing duplicate INSCRIPTION event for etudid={etudid} !"
)
sco_etud.scolar_events_delete(cnx, event["event_id"])
else:
@ -738,8 +738,7 @@ def etud_descr_situation_semestre(
# assert date_dem == None, 'plusieurs démissions !'
if date_dem: # cela ne peut pas arriver sauf bug (signale a Evry 2013?)
log(
"etud_descr_situation_semestre: removing duplicate DEMISSION event for etudid=%s !"
% etudid
f"etud_descr_situation_semestre: removing duplicate DEMISSION event for etudid={etudid} !"
)
sco_etud.scolar_events_delete(cnx, event["event_id"])
else:
@ -747,8 +746,7 @@ def etud_descr_situation_semestre(
elif event_type == "DEFAILLANCE":
if date_def:
log(
"etud_descr_situation_semestre: removing duplicate DEFAILLANCE event for etudid=%s !"
% etudid
f"etud_descr_situation_semestre: removing duplicate DEFAILLANCE event for etudid={etudid} !"
)
sco_etud.scolar_events_delete(cnx, event["event_id"])
else:
@ -756,10 +754,10 @@ def etud_descr_situation_semestre(
if show_date_inscr:
if not date_inscr:
infos["date_inscription"] = ""
infos["descr_inscription"] = "Pas inscrit%s." % ne
infos["descr_inscription"] = f"Pas inscrit{ne}."
else:
infos["date_inscription"] = date_inscr
infos["descr_inscription"] = "Inscrit%s le %s." % (ne, date_inscr)
infos["descr_inscription"] = f"Inscrit{ne} le {date_inscr}."
else:
infos["date_inscription"] = ""
infos["descr_inscription"] = ""
@ -767,15 +765,15 @@ def etud_descr_situation_semestre(
infos["situation"] = infos["descr_inscription"]
if date_dem:
infos["descr_demission"] = "Démission le %s." % date_dem
infos["descr_demission"] = f"Démission le {date_dem}."
infos["date_demission"] = date_dem
infos["descr_decision_jury"] = "Démission"
infos["situation"] += " " + infos["descr_demission"]
return infos, None # ne donne pas les dec. de jury pour les demissionnaires
if date_def:
infos["descr_defaillance"] = "Défaillant%s" % ne
infos["descr_defaillance"] = f"Défaillant{ne}"
infos["date_defaillance"] = date_def
infos["descr_decision_jury"] = "Défaillant%s" % ne
infos["descr_decision_jury"] = f"Défaillant{ne}"
infos["situation"] += " " + infos["descr_defaillance"]
dpv = sco_pvjury.dict_pvjury(formsemestre_id, etudids=[etudid])
@ -798,6 +796,7 @@ def etud_descr_situation_semestre(
dec = infos["descr_decision_jury"]
else:
infos["descr_decision_jury"] = ""
infos["decision_jury"] = ""
if pv["decisions_ue_descr"] and show_uevalid:
infos["decisions_ue"] = pv["decisions_ue_descr"]
@ -809,14 +808,31 @@ def etud_descr_situation_semestre(
infos["mention"] = pv["mention"]
if pv["mention"] and show_mention:
dec += "Mention " + pv["mention"] + ". "
dec += f"Mention {pv['mention']}."
# Décisions APC / BUT
if pv.get("decision_annee", {}):
infos["descr_decision_annee"] = "Décision année: " + pv.get(
"decision_annee", {}
).get("code", "")
else:
infos["descr_decision_annee"] = ""
if pv.get("decision_rcue", []):
infos["descr_decisions_rcue"] = "Niveaux de compétences: " + ", ".join(
[
f"""{dec_rcue["niveau"]["competence"]["titre"]} {dec_rcue["niveau"]["ordre"]}: {dec_rcue["code"]}"""
for dec_rcue in pv.get("decision_rcue", [])
]
)
else:
infos["descr_decisions_rcue"] = ""
infos["situation"] += " " + dec
if not pv["validation_parcours"]: # parcours non terminé
if pv["autorisations_descr"]:
infos["situation"] += (
" Autorisé à s'inscrire en %s." % pv["autorisations_descr"]
)
infos[
"situation"
] += f" Autorisé à s'inscrire en {pv['autorisations_descr']}."
else:
infos["situation"] += " Diplôme obtenu."
return infos, dpv

View File

@ -25,7 +25,7 @@
#
##############################################################################
"""Génération du bulletin en format JSON (beta, non completement testé)
"""Génération du bulletin en format JSON
"""
import datetime
@ -33,8 +33,9 @@ import json
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models.formsemestre import FormSemestre
from app.models import but_validations
from app.models.etudiants import Identite
from app.models.formsemestre import FormSemestre
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -139,7 +140,7 @@ def formsemestre_bulletinetud_published_dict(
etat_inscription = etud.inscription_etat(formsemestre.id)
if etat_inscription != scu.INSCRIT:
d.update(dict_decision_jury(etudid, formsemestre_id, with_decisions=True))
d.update(dict_decision_jury(etud, formsemestre, with_decisions=True))
return d
# Groupes:
@ -343,9 +344,7 @@ def formsemestre_bulletinetud_published_dict(
d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)
# --- Decision Jury
d.update(
dict_decision_jury(etudid, formsemestre_id, with_decisions=xml_with_decisions)
)
d.update(dict_decision_jury(etud, formsemestre, with_decisions=xml_with_decisions))
# --- Appreciations
cnx = ndb.GetDBConnexion()
apprecs = sco_etud.appreciations_list(
@ -364,7 +363,9 @@ def formsemestre_bulletinetud_published_dict(
return d
def dict_decision_jury(etudid, formsemestre_id, with_decisions=False) -> dict:
def dict_decision_jury(
etud: Identite, formsemestre: FormSemestre, with_decisions: bool = False
) -> dict:
"""dict avec decision pour bulletins json
- decision : décision semestre
- decision_ue : list des décisions UE
@ -372,6 +373,8 @@ def dict_decision_jury(etudid, formsemestre_id, with_decisions=False) -> dict:
with_decision donne les décision même si bul_show_decision est faux.
Si formation APC, indique aussi validations année et RCUEs
Exemple:
{
'autorisation_inscription': [{'semestre_id': 4}],
@ -397,14 +400,14 @@ def dict_decision_jury(etudid, formsemestre_id, with_decisions=False) -> dict:
d = {}
if (
sco_preferences.get_preference("bul_show_decision", formsemestre_id)
sco_preferences.get_preference("bul_show_decision", formsemestre.id)
or with_decisions
):
infos, dpv = sco_bulletins.etud_descr_situation_semestre(
etudid,
formsemestre_id,
etud.id,
formsemestre.id,
show_uevalid=sco_preferences.get_preference(
"bul_show_uevalid", formsemestre_id
"bul_show_uevalid", formsemestre.id
),
)
d["situation"] = infos["situation"]
@ -456,4 +459,7 @@ def dict_decision_jury(etudid, formsemestre_id, with_decisions=False) -> dict:
)
else:
d["decision"] = dict(code="", etat="DEM")
# Ajout jury BUT:
if formsemestre.formation.is_apc():
d.update(but_validations.dict_decision_jury(etud, formsemestre))
return d

View File

@ -140,12 +140,15 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
text = (field or "") % scu.WrapDict(
cdict
) # note that None values are mapped to empty strings
except:
except: # pylint: disable=bare-except
log(
f"""process_field: invalid format. field={field!r}
values={pprint.pformat(cdict)}
"""
)
# ne sera pas visible si lien vers pdf:
scu.flash_once(f"Attention: format PDF invalide (champs {field}")
raise ValueError
text = (
"<para><i>format invalide !</i></para><para>"
+ traceback.format_exc()

View File

@ -55,6 +55,7 @@ from reportlab.lib import styles
from flask import g
from app.scodoc import sco_utils as scu
from app.scodoc.sco_utils import CONFIG
from app import log
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
@ -67,7 +68,7 @@ PAGE_WIDTH = defaultPageSize[0]
DEFAULT_PDF_FOOTER_TEMPLATE = CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE
def SU(s):
def SU(s: str) -> str:
"convert s from string to string suitable for ReportLab"
if not s:
return ""
@ -145,9 +146,9 @@ def makeParas(txt, style, suppress_empty=False):
) from e
else:
raise e
except Exception as e:
except Exception as exc:
log(traceback.format_exc())
log("Invalid pdf para format: %s" % txt)
log(f"Invalid pdf para format: {txt}")
try:
result = [
Paragraph(
@ -155,13 +156,14 @@ def makeParas(txt, style, suppress_empty=False):
style,
)
]
except ValueError as e: # probleme font ? essaye sans style
except ValueError as exc2: # probleme font ? essaye sans style
# recupere font en cause ?
m = re.match(r".*family/bold/italic for (.*)", e.args[0], re.DOTALL)
if m:
message = f"police non disponible: {m[1]}"
else:
message = "format invalide"
scu.flash_once(f"problème génération PDF: {message}")
return [
Paragraph(
SU(f'<font color="red"><b>Erreur: {message}</b></font>'),

View File

@ -57,7 +57,13 @@ from flask import g, request
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre, UniteEns, ScolarAutorisationInscription
from app.models import (
FormSemestre,
UniteEns,
ScolarAutorisationInscription,
but_validations,
)
from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -81,8 +87,8 @@ from app.scodoc.sco_pdf import PDFLOCK
from app.scodoc.TrivialFormulator import TrivialFormulator
def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem):
"""Liste des UE validées dans ce semestre"""
def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem) -> list[dict]:
"""Liste des UE validées dans ce semestre (incluant les UE capitalisées)"""
if not decisions_ue:
return []
uelist = []
@ -93,14 +99,17 @@ def _descr_decisions_ues(nt, etudid, decisions_ue, decision_sem):
decisions_ue[ue_id]["code"] == sco_codes_parcours.ADM
or (
# XXX ceci devrait dépendre du parcours et non pas être une option ! #sco8
scu.CONFIG.CAPITALIZE_ALL_UES
decision_sem
and scu.CONFIG.CAPITALIZE_ALL_UES
and sco_codes_parcours.code_semestre_validant(decision_sem["code"])
)
):
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
uelist.append(ue)
except:
log("descr_decisions_ues: ue_id=%s decisions_ue=%s" % (ue_id, decisions_ue))
log(
f"Exception in descr_decisions_ues: ue_id={ue_id} decisions_ue={decisions_ue}"
)
# Les UE capitalisées dans d'autres semestres:
if etudid in nt.validations.ue_capitalisees.index:
for ue_id in nt.validations.ue_capitalisees.loc[[etudid]]["ue_id"]:
@ -230,8 +239,11 @@ def dict_pvjury(
L = []
D = {} # même chose que L, mais { etudid : dec }
for etudid in etudids:
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
Se = sco_cursus.get_situation_etud_cursus(etud, formsemestre_id)
# etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
etud: Identite = Identite.query.get(etudid)
Se = sco_cursus.get_situation_etud_cursus(
etud.to_dict_scodoc7(), formsemestre_id
)
semestre_non_terminal = semestre_non_terminal or Se.semestre_non_terminal
d = {}
d["identite"] = nt.identdict[etudid]
@ -240,6 +252,8 @@ def dict_pvjury(
) # I|D|DEF (inscription ou démission ou défaillant)
d["decision_sem"] = nt.get_etud_decision_sem(etudid)
d["decisions_ue"] = nt.get_etud_decision_ues(etudid)
if formsemestre.formation.is_apc():
d.update(but_validations.dict_decision_jury(etud, formsemestre))
d["last_formsemestre_id"] = Se.get_semestres()[
-1
] # id du dernier semestre (chronologiquement) dans lequel il a été inscrit
@ -305,12 +319,7 @@ def dict_pvjury(
d["decision_sem"]["compense_formsemestre_id"]
)
obs.append(
"%s compense %s (%s)"
% (
sem["sem_id_txt"],
compensed["sem_id_txt"],
compensed["anneescolaire"],
)
f"""{sem["sem_id_txt"]} compense {compensed["sem_id_txt"]} ({compensed["anneescolaire"]})"""
)
d["observation"] = ", ".join(obs)

View File

@ -664,6 +664,15 @@ def flash_errors(form):
# see https://getbootstrap.com/docs/4.0/components/alerts/
def flash_once(message: str):
"""Flash the message, but only once per request"""
if not hasattr(g, "sco_flashed_once"):
g.sco_flashed_once = set()
if not message in g.sco_flashed_once:
flash(message)
g.sco_flashed_once.add(message)
def sendCSVFile(data, filename): # DEPRECATED utiliser send_file
"""publication fichier CSV."""
return send_file(data, filename=filename, mime=CSV_MIMETYPE, attached=True)

View File

@ -2552,9 +2552,8 @@ def formsemestre_validation_suppress_etud(
)
if not dialog_confirmed:
d = sco_bulletins_json.dict_decision_jury(
etudid, formsemestre_id, with_decisions=True
etud, formsemestre, with_decisions=True
)
d.update(but_validations.dict_decision_jury(etud, formsemestre))
descr_ues = [f"{u['acronyme']}: {u['code']}" for u in d.get("decision_ue", [])]
dec_annee = d.get("decision_annee")

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.3.16"
SCOVERSION = "9.3.17"
SCONAME = "ScoDoc"