PV Jury PDF: refactoring, optimisation, amélioration.

This commit is contained in:
Emmanuel Viennet 2023-02-19 02:54:29 +01:00 committed by iziram
parent 78fa0a88cf
commit c8a70670be
10 changed files with 472 additions and 415 deletions

View File

@ -92,17 +92,12 @@ def pvjury_page_but(formsemestre_id: int, fmt="html"):
def pvjury_table_but( def pvjury_table_but(
formsemestre: FormSemestre, formsemestre: FormSemestre, etudids: list[int] = None, line_sep: str = "\n"
etudids: list[int] = None,
line_sep: str = "\n",
only_diplome=False,
anonymous=False,
with_paragraph_nom=False,
) -> tuple[list[dict], dict]: ) -> tuple[list[dict], dict]:
"""Table avec résultats jury BUT pour PV. """Table avec résultats jury BUT pour PV.
Si etudids est None, prend tous les étudiants inscrits. Si etudids est None, prend tous les étudiants inscrits.
""" """
# remplace pour le BUT la fonction sco_pv_forms.pvjury_table # remplace pour le BUT la fonction sco_pvjury.pvjury_table
annee_but = (formsemestre.semestre_id + 1) // 2 annee_but = (formsemestre.semestre_id + 1) // 2
titles = { titles = {
"nom": "Code" if anonymous else "Nom", "nom": "Code" if anonymous else "Nom",

View File

@ -12,7 +12,7 @@ import numpy as np
from app.but import jury_but from app.but import jury_but
from app.models.etudiants import Identite from app.models.etudiants import Identite
from app.models.formsemestre import FormSemestre from app.models.formsemestre import FormSemestre
from app.scodoc import sco_pv_dict from app.scodoc import sco_dict_pv_jury
def get_jury_but_results(formsemestre: FormSemestre) -> list[dict]: def get_jury_but_results(formsemestre: FormSemestre) -> list[dict]:
@ -20,7 +20,7 @@ def get_jury_but_results(formsemestre: FormSemestre) -> list[dict]:
if formsemestre.formation.referentiel_competence is None: if formsemestre.formation.referentiel_competence is None:
# pas de ref. comp., donc pas de decisions de jury (ne lance pas d'exception) # pas de ref. comp., donc pas de decisions de jury (ne lance pas d'exception)
return [] return []
dpv = sco_pv_dict.dict_pvjury(formsemestre.id) dpv = sco_dict_pv_jury.dict_pvjury(formsemestre.id)
rows = [] rows = []
for etudid in formsemestre.etuds_inscriptions: for etudid in formsemestre.etuds_inscriptions:
rows.append(_get_jury_but_etud_result(formsemestre, dpv, etudid)) rows.append(_get_jury_but_etud_result(formsemestre, dpv, etudid))

View File

@ -71,14 +71,14 @@ from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre from app.models import FormSemestre
from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc.sco_exceptions import AccessDenied, ScoPermissionDenied from app.scodoc.sco_exceptions import ScoPermissionDenied
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_bulletins_pdf from app.scodoc import sco_bulletins_pdf
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_groups_view from app.scodoc import sco_groups_view
from app.scodoc import sco_pv_forms from app.scodoc import sco_pvjury
from app.scodoc import sco_pv_lettres_inviduelles from app.scodoc import sco_dict_pv_jury
from app.scodoc import sco_pv_pdf from app.scodoc import sco_pvpdf
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
@ -395,23 +395,27 @@ def do_formsemestre_archive(
signature=signature, signature=signature,
) )
if data: if data:
PVArchive.store(archive_id, f"CourriersDecisions{groups_filename}.pdf", data) PVArchive.store(archive_id, "CourriersDecisions%s.pdf" % groups_filename, data)
# PV de jury (PDF): # PV de jury (PDF): disponible seulement en classique
data = sco_pv_pdf.pvjury_pdf( # en BUT, le PV est sous forme excel (Decisions_Jury.xlsx ci-dessus)
formsemestre, if not formsemestre.formation.is_apc():
etudids=etudids, dpv = sco_dict_pv_jury.dict_pvjury(
date_commission=date_commission, formsemestre_id, etudids=etudids, with_prev=True
date_jury=date_jury, )
numero_arrete=numero_arrete, data = sco_pvpdf.pvjury_pdf(
code_vdi=code_vdi, dpv,
show_title=show_title, date_commission=date_commission,
pv_title=pv_title, date_jury=date_jury,
with_paragraph_nom=with_paragraph_nom, numero_arrete=numero_arrete,
anonymous=anonymous, code_vdi=code_vdi,
) show_title=show_title,
if data: pv_title=pv_title,
PVArchive.store(archive_id, f"PV_Jury{groups_filename}.pdf", data) with_paragraph_nom=with_paragraph_nom,
anonymous=anonymous,
)
if data:
PVArchive.store(archive_id, "PV_Jury%s.pdf" % groups_filename, data)
def formsemestre_archive(formsemestre_id, group_ids: list[int] = None): def formsemestre_archive(formsemestre_id, group_ids: list[int] = None):
@ -427,8 +431,6 @@ def formsemestre_archive(formsemestre_id, group_ids: list[int] = None):
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )
) )
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
if not group_ids: if not group_ids:
# tous les inscrits du semestre # tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(formsemestre_id)] group_ids = [sco_groups.get_default_group(formsemestre_id)]
@ -469,7 +471,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
), ),
("sep", {"input_type": "separator", "title": "Informations sur PV de jury"}), ("sep", {"input_type": "separator", "title": "Informations sur PV de jury"}),
] ]
descr += sco_pv_forms.descrform_pvjury(formsemestre) descr += sco_pvjury.descrform_pvjury(formsemestre)
descr += [ descr += [
( (
"signature", "signature",

View File

@ -59,7 +59,7 @@ from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pv_dict from app.scodoc import sco_dict_pv_jury
from app.scodoc import sco_users from app.scodoc import sco_users
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType, fmt_note from app.scodoc.sco_utils import ModuleType, fmt_note
@ -787,7 +787,7 @@ def etud_descr_situation_semestre(
infos["date_defaillance"] = date_def infos["date_defaillance"] = date_def
infos["descr_decision_jury"] = f"Défaillant{ne}" infos["descr_decision_jury"] = f"Défaillant{ne}"
dpv = sco_pv_dict.dict_pvjury(formsemestre_id, etudids=[etudid]) dpv = sco_dict_pv_jury.dict_pvjury(formsemestre_id, etudids=[etudid])
if dpv: if dpv:
infos["decision_sem"] = dpv["decisions"][0]["decision_sem"] infos["decision_sem"] = dpv["decisions"][0]["decision_sem"]

View File

@ -0,0 +1,324 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
"""Ancienne fonction de synthèse des information jury
(pour formations classiques)
"""
from operator import itemgetter
from app import log
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import (
Formation,
FormSemestre,
Identite,
ScolarAutorisationInscription,
UniteEns,
but_validations,
)
from app.scodoc import codes_cursus
from app.scodoc import sco_etud
from app.scodoc import sco_formsemestre
from app.scodoc import sco_cursus
from app.scodoc import sco_cursus_dut
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
def dict_pvjury(
formsemestre_id,
etudids=None,
with_prev=False,
with_parcours_decisions=False,
):
"""Données pour édition jury
etudids == None => tous les inscrits, sinon donne la liste des ids
Si with_prev: ajoute infos sur code jury semestre precedent
Si with_parcours_decisions: ajoute infos sur code decision jury de tous les semestre du parcours
Résultat:
{
'date' : date de la decision la plus recente,
'formsemestre' : sem,
'is_apc' : bool,
'formation' : { 'acronyme' :, 'titre': ... }
'decisions' : { [ { 'identite' : {'nom' :, 'prenom':, ...,},
'etat' : I ou D ou DEF
'decision_sem' : {'code':, 'code_prev': },
'decisions_ue' : { ue_id : { 'code' : ADM|CMP|AJ, 'event_date' :,
'acronyme', 'numero': } },
'autorisations' : [ { 'semestre_id' : { ... } } ],
'validation_parcours' : True si parcours validé (diplome obtenu)
'prev_code' : code (calculé slt si with_prev),
'mention' : mention (en fct moy gen),
'sum_ects' : total ECTS acquis dans ce semestre (incluant les UE capitalisées)
'sum_ects_capitalises' : somme des ECTS des UE capitalisees
}
]
},
'decisions_dict' : { etudid : decision (comme ci-dessus) },
}
"""
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
if etudids is None:
etudids = nt.get_etudids()
if not etudids:
return {}
cnx = ndb.GetDBConnexion()
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
max_date = "0000-01-01"
has_prev = False # vrai si au moins un etudiant a un code prev
semestre_non_terminal = False # True si au moins un etudiant a un devenir
decisions = []
D = {} # même chose que decisions, mais { etudid : dec }
for etudid in etudids:
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]
d["etat"] = nt.get_etud_etat(
etudid
) # 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
ects_capitalises_by_ue_code = _comp_ects_capitalises_by_ue_code(nt, etudid)
d["sum_ects_capitalises"] = sum(ects_capitalises_by_ue_code.values())
ects_by_ue_code = _comp_ects_by_ue_code(nt, d["decisions_ue"])
d["sum_ects"] = _sum_ects_dicts(ects_capitalises_by_ue_code, ects_by_ue_code)
if d["decision_sem"] and codes_cursus.code_semestre_validant(
d["decision_sem"]["code"]
):
d["mention"] = scu.get_mention(nt.get_etud_moy_gen(etudid))
else:
d["mention"] = ""
# Versions "en français": (avec les UE capitalisées d'ailleurs)
dec_ue_list = _descr_decisions_ues(
nt, etudid, d["decisions_ue"], d["decision_sem"]
)
d["decisions_ue_nb"] = len(
dec_ue_list
) # avec les UE capitalisées, donc des éventuels doublons
# Mais sur la description (eg sur les bulletins), on ne veut pas
# afficher ces doublons: on uniquifie sur ue_code
_codes = set()
ue_uniq = []
for ue in dec_ue_list:
if ue["ue_code"] not in _codes:
ue_uniq.append(ue)
_codes.add(ue["ue_code"])
d["decisions_ue_descr"] = ", ".join([ue["acronyme"] for ue in ue_uniq])
if nt.is_apc:
d["decision_sem_descr"] = "" # pas de validation de semestre en BUT
else:
d["decision_sem_descr"] = _descr_decision_sem(d["etat"], d["decision_sem"])
autorisations = ScolarAutorisationInscription.query.filter_by(
etudid=etudid, origin_formsemestre_id=formsemestre_id
).all()
d["autorisations"] = [a.to_dict() for a in autorisations]
d["autorisations_descr"] = _descr_autorisations(autorisations)
d["validation_parcours"] = Se.parcours_validated()
d["parcours"] = Se.get_cursus_descr(filter_futur=True)
if with_parcours_decisions:
d["parcours_decisions"] = Se.get_parcours_decisions()
# Observations sur les compensations:
compensators = sco_cursus_dut.scolar_formsemestre_validation_list(
cnx, args={"compense_formsemestre_id": formsemestre_id, "etudid": etudid}
)
obs = []
for compensator in compensators:
# nb: il ne devrait y en avoir qu'un !
csem = sco_formsemestre.get_formsemestre(compensator["formsemestre_id"])
obs.append(
"%s compensé par %s (%s)"
% (sem["sem_id_txt"], csem["sem_id_txt"], csem["anneescolaire"])
)
if d["decision_sem"] and d["decision_sem"]["compense_formsemestre_id"]:
compensed = sco_formsemestre.get_formsemestre(
d["decision_sem"]["compense_formsemestre_id"]
)
obs.append(
f"""{sem["sem_id_txt"]} compense {compensed["sem_id_txt"]} ({compensed["anneescolaire"]})"""
)
d["observation"] = ", ".join(obs)
# Cherche la date de decision (sem ou UE) la plus récente:
if d["decision_sem"]:
date = ndb.DateDMYtoISO(d["decision_sem"]["event_date"])
if date and date > max_date: # decision plus recente
max_date = date
if d["decisions_ue"]:
for dec_ue in d["decisions_ue"].values():
if dec_ue:
date = ndb.DateDMYtoISO(dec_ue["event_date"])
if date and date > max_date: # decision plus recente
max_date = date
# Code semestre precedent
if with_prev: # optionnel car un peu long...
info = sco_etud.get_etud_info(etudid=etudid, filled=True)
if not info:
continue # should not occur
etud = info[0]
if Se.prev and Se.prev_decision:
d["prev_decision_sem"] = Se.prev_decision
d["prev_code"] = Se.prev_decision["code"]
d["prev_code_descr"] = _descr_decision_sem(
scu.INSCRIT, Se.prev_decision
)
d["prev"] = Se.prev
has_prev = True
else:
d["prev_decision_sem"] = None
d["prev_code"] = ""
d["prev_code_descr"] = ""
d["Se"] = Se
decisions.append(d)
D[etudid] = d
return {
"date": ndb.DateISOtoDMY(max_date),
"formsemestre": sem,
"is_apc": nt.is_apc,
"has_prev": has_prev,
"semestre_non_terminal": semestre_non_terminal,
"formation": Formation.query.get_or_404(sem["formation_id"]).to_dict(),
"decisions": decisions,
"decisions_dict": D,
}
def _comp_ects_capitalises_by_ue_code(nt: NotesTableCompat, etudid: int):
"""Calcul somme des ECTS des UE capitalisees"""
ues = nt.get_ues_stat_dict()
ects_by_ue_code = {}
for ue in ues:
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
if ue_status and ue_status["is_capitalized"]:
ects_val = float(ue_status["ue"]["ects"] or 0.0)
ects_by_ue_code[ue["ue_code"]] = ects_val
return ects_by_ue_code
def _comp_ects_by_ue_code(nt, decision_ues):
"""Calcul somme des ECTS validés dans ce semestre (sans les UE capitalisées)
decision_ues est le resultat de nt.get_etud_decision_ues
Chaque resultat est un dict: { ue_code : ects }
"""
if not decision_ues:
return {}
ects_by_ue_code = {}
for ue_id in decision_ues:
d = decision_ues[ue_id]
ue = UniteEns.query.get(ue_id)
ects_by_ue_code[ue.ue_code] = d["ects"]
return ects_by_ue_code
def _descr_autorisations(autorisations: list[ScolarAutorisationInscription]) -> str:
"résumé textuel des autorisations d'inscription (-> 'S1, S3' )"
return ", ".join([f"S{a.semestre_id}" for a in autorisations])
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 = []
# Les UE validées dans ce semestre:
for ue_id in decisions_ue.keys():
try:
if decisions_ue[ue_id] and (
codes_cursus.code_ue_validant(decisions_ue[ue_id]["code"])
or (
(not nt.is_apc)
and (
# XXX ceci devrait dépendre du parcours et non pas être une option ! #sco8
decision_sem
and scu.CONFIG.CAPITALIZE_ALL_UES
and codes_cursus.code_semestre_validant(decision_sem["code"])
)
)
):
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
uelist.append(ue)
except:
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"]:
try:
uelist.append(nt.get_etud_ue_status(etudid, ue_id)["ue"])
except (KeyError, TypeError):
pass
uelist.sort(key=itemgetter("numero"))
return uelist
def _descr_decision_sem(etat, decision_sem):
"résumé textuel de la décision de semestre"
if etat == "D":
decision = "Démission"
else:
if decision_sem:
cod = decision_sem["code"]
decision = codes_cursus.CODES_EXPL.get(cod, "") # + ' (%s)' % cod
else:
decision = ""
return decision
def _sum_ects_dicts(s, t):
"""Somme deux dictionnaires { ue_code : ects },
quand une UE de même code apparait deux fois, prend celle avec le plus d'ECTS.
"""
sum_ects = sum(s.values()) + sum(t.values())
for ue_code in set(s).intersection(set(t)):
sum_ects -= min(s[ue_code], t[ue_code])
return sum_ects

View File

@ -39,10 +39,8 @@ from app.models import Formation
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_bac from app.scodoc import sco_bac
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_cache
from app.scodoc import sco_formations
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pv_dict from app.scodoc import sco_dict_pv_jury
from app.scodoc import sco_etud from app.scodoc import sco_etud
import sco_version import sco_version
from app.scodoc.gen_tables import GenTable from app.scodoc.gen_tables import GenTable
@ -59,7 +57,7 @@ def _build_results_table(start_date=None, end_date=None, types_parcours=[]):
# Décisions de jury de tous les semestres: # Décisions de jury de tous les semestres:
dpv_by_sem = {} dpv_by_sem = {}
for formsemestre_id in formsemestre_ids: for formsemestre_id in formsemestre_ids:
dpv_by_sem[formsemestre_id] = sco_pv_dict.dict_pvjury( dpv_by_sem[formsemestre_id] = sco_dict_pv_jury.dict_pvjury(
formsemestre_id, with_parcours_decisions=True formsemestre_id, with_parcours_decisions=True
) )
@ -352,7 +350,7 @@ end_date='2017-08-31'
formsemestre_ids = get_set_formsemestre_id_dates( start_date, end_date) formsemestre_ids = get_set_formsemestre_id_dates( start_date, end_date)
dpv_by_sem = {} dpv_by_sem = {}
for formsemestre_id in formsemestre_ids: for formsemestre_id in formsemestre_ids:
dpv_by_sem[formsemestre_id] = sco_pv_dict.dict_pvjury( formsemestre_id, with_parcours_decisions=True) dpv_by_sem[formsemestre_id] = sco_dict_pv_jury.dict_pvjury( formsemestre_id, with_parcours_decisions=True)
semlist = [ dpv['formsemestre'] for dpv in dpv_by_sem.values() ] semlist = [ dpv['formsemestre'] for dpv in dpv_by_sem.values() ]

View File

@ -64,7 +64,7 @@ from app.scodoc import sco_cursus_dut
from app.scodoc.sco_cursus_dut import etud_est_inscrit_ue from app.scodoc.sco_cursus_dut import etud_est_inscrit_ue
from app.scodoc import sco_photos from app.scodoc import sco_photos
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pv_dict from app.scodoc import sco_dict_pv_jury
# ------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------
def formsemestre_validation_etud_form( def formsemestre_validation_etud_form(
@ -562,7 +562,7 @@ def formsemestre_recap_parcours_table(
is_cur = Se.formsemestre_id == sem["formsemestre_id"] is_cur = Se.formsemestre_id == sem["formsemestre_id"]
num_sem += 1 num_sem += 1
dpv = sco_pv_dict.dict_pvjury(sem["formsemestre_id"], etudids=[etudid]) dpv = sco_dict_pv_jury.dict_pvjury(sem["formsemestre_id"], etudids=[etudid])
pv = dpv["decisions"][0] pv = dpv["decisions"][0]
decision_sem = pv["decision_sem"] decision_sem = pv["decision_sem"]
decisions_ue = pv["decisions_ue"] decisions_ue = pv["decisions_ue"]

View File

@ -47,7 +47,7 @@ from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pv_dict from app.scodoc import sco_dict_pv_jury
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
@ -137,7 +137,7 @@ def list_inscrits(formsemestre_id, with_dems=False):
def list_etuds_from_sem(src, dst) -> list[dict]: def list_etuds_from_sem(src, dst) -> list[dict]:
"""Liste des etudiants du semestre src qui sont autorisés à passer dans le semestre dst.""" """Liste des etudiants du semestre src qui sont autorisés à passer dans le semestre dst."""
target = dst["semestre_id"] target = dst["semestre_id"]
dpv = sco_pv_dict.dict_pvjury(src["formsemestre_id"]) dpv = sco_dict_pv_jury.dict_pvjury(src["formsemestre_id"])
if not dpv: if not dpv:
return [] return []
etuds = [ etuds = [

View File

@ -38,15 +38,18 @@ import flask
from flask import flash, redirect, url_for from flask import flash, redirect, url_for
from flask import g, request from flask import g, request
from app.models import FormSemestre, Identite from app.models import (
Formation,
FormSemestre,
ScolarAutorisationInscription,
)
from app.models.etudiants import Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_cursus from app.scodoc import sco_dict_pv_jury
from app.scodoc import sco_cursus_dut
from app.scodoc import sco_edit_ue
from app.scodoc import sco_etud from app.scodoc import sco_etud
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_groups_view from app.scodoc import sco_groups_view
@ -60,57 +63,6 @@ from app.scodoc.sco_pdf import PDFLOCK
from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.TrivialFormulator import TrivialFormulator
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 = []
# Les UE validées dans ce semestre:
for ue_id in decisions_ue.keys():
try:
if decisions_ue[ue_id] and (
codes_cursus.code_ue_validant(decisions_ue[ue_id]["code"])
or (
(not nt.is_apc)
and (
# XXX ceci devrait dépendre du parcours et non pas être une option ! #sco8
decision_sem
and scu.CONFIG.CAPITALIZE_ALL_UES
and codes_cursus.code_semestre_validant(decision_sem["code"])
)
)
):
ue = sco_edit_ue.ue_list(args={"ue_id": ue_id})[0]
uelist.append(ue)
except:
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"]:
try:
uelist.append(nt.get_etud_ue_status(etudid, ue_id)["ue"])
except (KeyError, TypeError):
pass
uelist.sort(key=itemgetter("numero"))
return uelist
def _descr_decision_sem(etat, decision_sem):
"résumé textuel de la décision de semestre"
if etat == "D":
decision = "Démission"
else:
if decision_sem:
cod = decision_sem["code"]
decision = codes_cursus.CODES_EXPL.get(cod, "") # + ' (%s)' % cod
else:
decision = ""
return decision
def _descr_decision_sem_abbrev(etat, decision_sem): def _descr_decision_sem_abbrev(etat, decision_sem):
"résumé textuel tres court (code) de la décision de semestre" "résumé textuel tres court (code) de la décision de semestre"
if etat == "D": if etat == "D":
@ -123,232 +75,6 @@ def _descr_decision_sem_abbrev(etat, decision_sem):
return decision return decision
def descr_autorisations(autorisations: list[ScolarAutorisationInscription]) -> str:
"résumé textuel des autorisations d'inscription (-> 'S1, S3' )"
return ", ".join([f"S{a.semestre_id}" for a in autorisations])
def _comp_ects_by_ue_code(nt, decision_ues):
"""Calcul somme des ECTS validés dans ce semestre (sans les UE capitalisées)
decision_ues est le resultat de nt.get_etud_decision_ues
Chaque resultat est un dict: { ue_code : ects }
"""
if not decision_ues:
return {}
ects_by_ue_code = {}
for ue_id in decision_ues:
d = decision_ues[ue_id]
ue = UniteEns.query.get(ue_id)
ects_by_ue_code[ue.ue_code] = d["ects"]
return ects_by_ue_code
def _comp_ects_capitalises_by_ue_code(nt: NotesTableCompat, etudid: int):
"""Calcul somme des ECTS des UE capitalisees"""
ues = nt.get_ues_stat_dict()
ects_by_ue_code = {}
for ue in ues:
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
if ue_status and ue_status["is_capitalized"]:
ects_val = float(ue_status["ue"]["ects"] or 0.0)
ects_by_ue_code[ue["ue_code"]] = ects_val
return ects_by_ue_code
def _sum_ects_dicts(s, t):
"""Somme deux dictionnaires { ue_code : ects },
quand une UE de même code apparait deux fois, prend celle avec le plus d'ECTS.
"""
sum_ects = sum(s.values()) + sum(t.values())
for ue_code in set(s).intersection(set(t)):
sum_ects -= min(s[ue_code], t[ue_code])
return sum_ects
def dict_pvjury(
formsemestre_id,
etudids=None,
with_prev=False,
with_parcours_decisions=False,
):
"""Données pour édition jury
etudids == None => tous les inscrits, sinon donne la liste des ids
Si with_prev: ajoute infos sur code jury semestre precedent
Si with_parcours_decisions: ajoute infos sur code decision jury de tous les semestre du parcours
Résultat:
{
'date' : date de la decision la plus recente,
'formsemestre' : sem,
'is_apc' : bool,
'formation' : { 'acronyme' :, 'titre': ... }
'decisions' : { [ { 'identite' : {'nom' :, 'prenom':, ...,},
'etat' : I ou D ou DEF
'decision_sem' : {'code':, 'code_prev': },
'decisions_ue' : { ue_id : { 'code' : ADM|CMP|AJ, 'event_date' :,
'acronyme', 'numero': } },
'autorisations' : [ { 'semestre_id' : { ... } } ],
'validation_parcours' : True si parcours validé (diplome obtenu)
'prev_code' : code (calculé slt si with_prev),
'mention' : mention (en fct moy gen),
'sum_ects' : total ECTS acquis dans ce semestre (incluant les UE capitalisées)
'sum_ects_capitalises' : somme des ECTS des UE capitalisees
}
]
},
'decisions_dict' : { etudid : decision (comme ci-dessus) },
}
"""
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
if etudids is None:
etudids = nt.get_etudids()
if not etudids:
return {}
cnx = ndb.GetDBConnexion()
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
max_date = "0000-01-01"
has_prev = False # vrai si au moins un etudiant a un code prev
semestre_non_terminal = False # True si au moins un etudiant a un devenir
decisions = []
D = {} # même chose que decisions, mais { etudid : dec }
for etudid in etudids:
# 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]
d["etat"] = nt.get_etud_etat(
etudid
) # 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
ects_capitalises_by_ue_code = _comp_ects_capitalises_by_ue_code(nt, etudid)
d["sum_ects_capitalises"] = sum(ects_capitalises_by_ue_code.values())
ects_by_ue_code = _comp_ects_by_ue_code(nt, d["decisions_ue"])
d["sum_ects"] = _sum_ects_dicts(ects_capitalises_by_ue_code, ects_by_ue_code)
if d["decision_sem"] and codes_cursus.code_semestre_validant(
d["decision_sem"]["code"]
):
d["mention"] = scu.get_mention(nt.get_etud_moy_gen(etudid))
else:
d["mention"] = ""
# Versions "en français": (avec les UE capitalisées d'ailleurs)
dec_ue_list = _descr_decisions_ues(
nt, etudid, d["decisions_ue"], d["decision_sem"]
)
d["decisions_ue_nb"] = len(
dec_ue_list
) # avec les UE capitalisées, donc des éventuels doublons
# Mais sur la description (eg sur les bulletins), on ne veut pas
# afficher ces doublons: on uniquifie sur ue_code
_codes = set()
ue_uniq = []
for ue in dec_ue_list:
if ue["ue_code"] not in _codes:
ue_uniq.append(ue)
_codes.add(ue["ue_code"])
d["decisions_ue_descr"] = ", ".join([ue["acronyme"] for ue in ue_uniq])
if nt.is_apc:
d["decision_sem_descr"] = "" # pas de validation de semestre en BUT
else:
d["decision_sem_descr"] = _descr_decision_sem(d["etat"], d["decision_sem"])
autorisations = ScolarAutorisationInscription.query.filter_by(
etudid=etudid, origin_formsemestre_id=formsemestre_id
).all()
d["autorisations"] = [a.to_dict() for a in autorisations]
d["autorisations_descr"] = descr_autorisations(autorisations)
d["validation_parcours"] = Se.parcours_validated()
d["parcours"] = Se.get_cursus_descr(filter_futur=True)
if with_parcours_decisions:
d["parcours_decisions"] = Se.get_parcours_decisions()
# Observations sur les compensations:
compensators = sco_cursus_dut.scolar_formsemestre_validation_list(
cnx, args={"compense_formsemestre_id": formsemestre_id, "etudid": etudid}
)
obs = []
for compensator in compensators:
# nb: il ne devrait y en avoir qu'un !
csem = sco_formsemestre.get_formsemestre(compensator["formsemestre_id"])
obs.append(
"%s compensé par %s (%s)"
% (sem["sem_id_txt"], csem["sem_id_txt"], csem["anneescolaire"])
)
if d["decision_sem"] and d["decision_sem"]["compense_formsemestre_id"]:
compensed = sco_formsemestre.get_formsemestre(
d["decision_sem"]["compense_formsemestre_id"]
)
obs.append(
f"""{sem["sem_id_txt"]} compense {compensed["sem_id_txt"]} ({compensed["anneescolaire"]})"""
)
d["observation"] = ", ".join(obs)
# Cherche la date de decision (sem ou UE) la plus récente:
if d["decision_sem"]:
date = ndb.DateDMYtoISO(d["decision_sem"]["event_date"])
if date and date > max_date: # decision plus recente
max_date = date
if d["decisions_ue"]:
for dec_ue in d["decisions_ue"].values():
if dec_ue:
date = ndb.DateDMYtoISO(dec_ue["event_date"])
if date and date > max_date: # decision plus recente
max_date = date
# Code semestre precedent
if with_prev: # optionnel car un peu long...
info = sco_etud.get_etud_info(etudid=etudid, filled=True)
if not info:
continue # should not occur
etud = info[0]
if Se.prev and Se.prev_decision:
d["prev_decision_sem"] = Se.prev_decision
d["prev_code"] = Se.prev_decision["code"]
d["prev_code_descr"] = _descr_decision_sem(
scu.INSCRIT, Se.prev_decision
)
d["prev"] = Se.prev
has_prev = True
else:
d["prev_decision_sem"] = None
d["prev_code"] = ""
d["prev_code_descr"] = ""
d["Se"] = Se
decisions.append(d)
D[etudid] = d
return {
"date": ndb.DateISOtoDMY(max_date),
"formsemestre": sem,
"is_apc": nt.is_apc,
"has_prev": has_prev,
"semestre_non_terminal": semestre_non_terminal,
"formation": sco_formations.formation_list(
args={"formation_id": sem["formation_id"]}
)[0],
"decisions": decisions,
"decisions_dict": D,
}
def pvjury_table( def pvjury_table(
dpv, dpv,
only_diplome=False, only_diplome=False,
@ -501,7 +227,7 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True):
footer = html_sco_header.sco_footer() footer = html_sco_header.sco_footer()
dpv = sco_pv_dict.dict_pvjury(formsemestre_id, with_prev=True) dpv = sco_dict_pv_jury.dict_pvjury(formsemestre_id, with_prev=True)
if not dpv: if not dpv:
if format == "html": if format == "html":
return ( return (
@ -683,7 +409,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids: list[int] = None, etudid
tf[2]["anonymous"] = bool(tf[2]["anonymous"]) tf[2]["anonymous"] = bool(tf[2]["anonymous"])
try: try:
PDFLOCK.acquire() PDFLOCK.acquire()
pdfdoc = sco_pv_pdf.pvjury_pdf( pdfdoc = sco_pvpdf.pvjury_pdf(
formsemestre, formsemestre,
etudids, etudids,
numero_arrete=tf[2]["numero_arrete"], numero_arrete=tf[2]["numero_arrete"],

View File

@ -49,6 +49,7 @@ from app.models import FormSemestre, Identite
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc import sco_bulletins_pdf from app.scodoc import sco_bulletins_pdf
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_dict_pv_jury
from app.scodoc import sco_etud from app.scodoc import sco_etud
from app.scodoc import sco_pdf from app.scodoc import sco_pdf
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
@ -384,9 +385,7 @@ def pdf_lettres_individuelles(
(tous ceux du semestre, ou la liste indiquée par etudids) (tous ceux du semestre, ou la liste indiquée par etudids)
Renvoie pdf data ou chaine vide si aucun etudiant avec décision de jury. Renvoie pdf data ou chaine vide si aucun etudiant avec décision de jury.
""" """
from app.scodoc import sco_pvjury dpv = sco_dict_pv_jury.dict_pvjury(formsemestre_id, etudids=etudids, with_prev=True)
dpv = sco_pvjury.dict_pvjury(formsemestre_id, etudids=etudids, with_prev=True)
if not dpv: if not dpv:
return "" return ""
# Ajoute infos sur etudiants # Ajoute infos sur etudiants
@ -656,59 +655,56 @@ def add_apc_infos(formsemestre: FormSemestre, params: dict, decision: dict):
# ---------------------------------------------- # ----------------------------------------------
def pvjury_pdf( def pvjury_pdf(
dpv, formsemestre: FormSemestre,
etudids: list[int],
date_commission=None, date_commission=None,
date_jury=None, date_jury=None,
numeroArrete=None, numero_arrete=None,
VDICode=None, code_vdi=None,
showTitle=False, show_title=False,
pv_title=None, pv_title=None,
with_paragraph_nom=False, with_paragraph_nom=False,
anonymous=False, anonymous=False,
): ) -> bytes:
"""Doc PDF récapitulant les décisions de jury """Doc PDF récapitulant les décisions de jury
(tableau en format paysage) (tableau en format paysage)
dpv: result of dict_pvjury
""" """
if not dpv: objects, a_diplome = _pvjury_pdf_type(
return {} formsemestre,
sem = dpv["formsemestre"] etudids,
formsemestre_id = sem["formsemestre_id"]
objects = _pvjury_pdf_type(
dpv,
only_diplome=False, only_diplome=False,
date_commission=date_commission, date_commission=date_commission,
numeroArrete=numeroArrete, numero_arrete=numero_arrete,
VDICode=VDICode, code_vdi=code_vdi,
date_jury=date_jury, date_jury=date_jury,
showTitle=showTitle, show_title=show_title,
pv_title=pv_title, pv_title=pv_title,
with_paragraph_nom=with_paragraph_nom, with_paragraph_nom=with_paragraph_nom,
anonymous=anonymous, anonymous=anonymous,
) )
if not objects:
return b""
jury_de_diplome = not dpv["semestre_non_terminal"] jury_de_diplome = formsemestre.est_terminal()
# Si Jury de passage et qu'un étudiant valide le parcours (car il a validé antérieurement le dernier semestre) # Si Jury de passage et qu'un étudiant valide le parcours (car il a validé antérieurement le dernier semestre)
# alors on génère aussi un PV de diplome (à la suite dans le même doc PDF) # alors on génère aussi un PV de diplome (à la suite dans le même doc PDF)
if not jury_de_diplome: if not jury_de_diplome and a_diplome:
validations_parcours = [x["validation_parcours"] for x in dpv["decisions"]] # au moins un etudiant a validé son diplome:
if True in validations_parcours: objects.append(PageBreak())
# au moins un etudiant a validé son diplome: objects += _pvjury_pdf_type(
objects.append(PageBreak()) formsemestre,
objects += _pvjury_pdf_type( etudids,
dpv, only_diplome=True,
only_diplome=True, date_commission=date_commission,
date_commission=date_commission, date_jury=date_jury,
date_jury=date_jury, numero_arrete=numero_arrete,
numeroArrete=numeroArrete, code_vdi=code_vdi,
VDICode=VDICode, show_title=show_title,
showTitle=showTitle, pv_title=pv_title,
pv_title=pv_title, with_paragraph_nom=with_paragraph_nom,
with_paragraph_nom=with_paragraph_nom, anonymous=anonymous,
anonymous=anonymous, )[0]
)
# ----- Build PDF # ----- Build PDF
report = io.BytesIO() # in-memory document, no disk file report = io.BytesIO() # in-memory document, no disk file
@ -717,10 +713,10 @@ def pvjury_pdf(
document.addPageTemplates( document.addPageTemplates(
PVTemplate( PVTemplate(
document, document,
author="%s %s (E. Viennet)" % (sco_version.SCONAME, sco_version.SCOVERSION), author=f"{sco_version.SCONAME} {sco_version.SCOVERSION} (E. Viennet)",
title=SU("PV du jury de %s" % sem["titre_num"]), title=SU(f"PV du jury de {formsemestre.titre_num()}"),
subject="PV jury", subject="PV jury",
preferences=sco_preferences.SemPreferences(formsemestre_id), preferences=sco_preferences.SemPreferences(formsemestre.id),
) )
) )
@ -730,7 +726,8 @@ def pvjury_pdf(
def _pvjury_pdf_type( def _pvjury_pdf_type(
dpv, formsemestre: FormSemestre,
etudids: list[int],
only_diplome=False, only_diplome=False,
date_commission=None, date_commission=None,
date_jury=None, date_jury=None,
@ -740,20 +737,18 @@ def _pvjury_pdf_type(
pv_title=None, pv_title=None,
anonymous=False, anonymous=False,
with_paragraph_nom=False, with_paragraph_nom=False,
): ) -> tuple[list, bool]:
"""Doc PDF récapitulant les décisions de jury pour un type de jury (passage ou delivrance) """Objets platypus PDF récapitulant les décisions de jury
dpv: result of dict_pvjury pour un type de jury (passage ou delivrance).
Ramene: liste d'onj platypus, et un boolen indiquant si au moins un étudiant est diplômé.
""" """
from app.scodoc import sco_pvjury from app.scodoc import sco_pvjury
# Jury de diplome si sem. terminal OU que l'on demande les diplomés d'un semestre antérieur a_diplome = False
diplome = (not dpv["semestre_non_terminal"]) or only_diplome # Jury de diplome si sem. terminal OU que l'on demande seulement les diplomés
diplome = formsemestre.est_terminal() or only_diplome
sem = dpv["formsemestre"]
formsemestre_id = sem["formsemestre_id"]
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
titre_jury, _ = _descr_jury(formsemestre, diplome) titre_jury, _ = _descr_jury(formsemestre, diplome)
titre_diplome = pv_title or dpv["formation"]["titre_officiel"] titre_diplome = pv_title or formsemestre.formation.titre_officiel
objects = [] objects = []
style = reportlab.lib.styles.ParagraphStyle({}) style = reportlab.lib.styles.ParagraphStyle({})
@ -780,14 +775,11 @@ def _pvjury_pdf_type(
objects += [Spacer(0, 5 * mm)] objects += [Spacer(0, 5 * mm)]
objects += sco_pdf.make_paras( objects += sco_pdf.make_paras(
""" f"""
<para align="center"><b>Procès-verbal de %s du département %s - Session unique %s</b></para> <para align="center"><b>Procès-verbal de {titre_jury} du département {
""" sco_preferences.get_preference("DeptName", formsemestre.id) or "(sans nom)"
% ( } - Session unique {formsemestre.annee_scolaire()}</b></para>
titre_jury, """,
sco_preferences.get_preference("DeptName", formsemestre_id) or "(sans nom)",
sem["anneescolaire"],
),
style, style,
) )
@ -803,7 +795,7 @@ def _pvjury_pdf_type(
objects += sco_pdf.make_paras( objects += sco_pdf.make_paras(
"""<para align="center"><b>Semestre: %s</b></para>""" % sem["titre"], style """<para align="center"><b>Semestre: %s</b></para>""" % sem["titre"], style
) )
if sco_preferences.get_preference("PV_TITLE_WITH_VDI", formsemestre_id): if sco_preferences.get_preference("PV_TITLE_WITH_VDI", formsemestre.id):
objects += sco_pdf.make_paras( objects += sco_pdf.make_paras(
"""<para align="center">VDI et Code: %s</para>""" % (VDICode or ""), style """<para align="center">VDI et Code: %s</para>""" % (VDICode or ""), style
) )
@ -815,11 +807,11 @@ def _pvjury_pdf_type(
objects += sco_pdf.make_paras( objects += sco_pdf.make_paras(
"<para>" "<para>"
+ (sco_preferences.get_preference("PV_INTRO", formsemestre_id) or "") + (sco_preferences.get_preference("PV_INTRO", formsemestre.id) or "")
% { % {
"Decnum": numeroArrete, "Decnum": numero_arrete,
"VDICode": VDICode, "VDICode": code_vdi,
"UnivName": sco_preferences.get_preference("UnivName", formsemestre_id), "UnivName": sco_preferences.get_preference("UnivName", formsemestre.id),
"Type": titre_jury, "Type": titre_jury,
"Date": date_commission, # deprecated "Date": date_commission, # deprecated
"date_commission": date_commission, "date_commission": date_commission,
@ -832,12 +824,26 @@ def _pvjury_pdf_type(
"""<para>Le jury propose les décisions suivantes :</para>""", style """<para>Le jury propose les décisions suivantes :</para>""", style
) )
objects += [Spacer(0, 4 * mm)] objects += [Spacer(0, 4 * mm)]
lines, titles, columns_ids = sco_pvjury.pvjury_table(
dpv, if formsemestre.formation.is_apc():
only_diplome=only_diplome, rows, titles = jury_but_pv.pvjury_table_but(
anonymous=anonymous, formsemestre, etudids=etudids, line_sep="<br/>"
with_paragraph_nom=with_paragraph_nom, )
) columns_ids = list(titles.keys())
a_diplome = codes_cursus.ADM in [row.get("diplome") for row in rows]
else:
dpv = sco_dict_pv_jury.dict_pvjury(
formsemestre.id, etudids=etudids, with_prev=True
)
if not dpv:
return [], False
rows, titles, columns_ids = sco_pvjury.pvjury_table(
dpv,
only_diplome=only_diplome,
anonymous=anonymous,
with_paragraph_nom=with_paragraph_nom,
)
a_diplome = True in (x["validation_parcours"] for x in dpv["decisions"])
# convert to lists of tuples: # convert to lists of tuples:
columns_ids = ["etudid"] + columns_ids columns_ids = ["etudid"] + columns_ids
lines = [[line.get(x, "") for x in columns_ids] for line in lines] lines = [[line.get(x, "") for x in columns_ids] for line in lines]
@ -845,11 +851,11 @@ def _pvjury_pdf_type(
# Make a new cell style and put all cells in paragraphs # Make a new cell style and put all cells in paragraphs
cell_style = styles.ParagraphStyle({}) cell_style = styles.ParagraphStyle({})
cell_style.fontSize = sco_preferences.get_preference( cell_style.fontSize = sco_preferences.get_preference(
"SCOLAR_FONT_SIZE", formsemestre_id "SCOLAR_FONT_SIZE", formsemestre.id
) )
cell_style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre_id) cell_style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre.id)
cell_style.leading = 1.0 * sco_preferences.get_preference( cell_style.leading = 1.0 * sco_preferences.get_preference(
"SCOLAR_FONT_SIZE", formsemestre_id "SCOLAR_FONT_SIZE", formsemestre.id
) # vertical space ) # vertical space
LINEWIDTH = 0.5 LINEWIDTH = 0.5
table_style = [ table_style = [
@ -857,7 +863,7 @@ def _pvjury_pdf_type(
"FONTNAME", "FONTNAME",
(0, 0), (0, 0),
(-1, 0), (-1, 0),
sco_preferences.get_preference("PV_FONTNAME", formsemestre_id), sco_preferences.get_preference("PV_FONTNAME", formsemestre.id),
), ),
("LINEBELOW", (0, 0), (-1, 0), LINEWIDTH, Color(0, 0, 0)), ("LINEBELOW", (0, 0), (-1, 0), LINEWIDTH, Color(0, 0, 0)),
("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
@ -872,22 +878,28 @@ def _pvjury_pdf_type(
else: else:
return x return x
Pt = [[_format_pv_cell(x) for x in line[1:]] for line in ([titles] + lines)] widths_by_id = {
widths = [6 * cm, 2.8 * cm, 2.8 * cm, None, None, None, None] "nom": 5 * cm,
if dpv["has_prev"]: "cursus": 2.8 * cm,
widths[2:2] = [2.8 * cm] "ects": 1.4 * cm,
if sco_preferences.get_preference("bul_show_mention", formsemestre_id): "devenir": 1.8 * cm,
widths += [None] "decision_but": 1.8 * cm,
objects.append(Table(Pt, repeatRows=1, colWidths=widths, style=table_style)) }
table_cells = [[_format_pv_cell(x) for x in line[1:]] for line in ([titles] + rows)]
widths = [widths_by_id.get(col_id) for col_id in columns_ids[1:]]
objects.append(
Table(table_cells, repeatRows=1, colWidths=widths, style=table_style)
)
# Signature du directeur # Signature du directeur
objects += sco_pdf.make_paras( objects += sco_pdf.make_paras(
"""<para spaceBefore="10mm" align="right"> f"""<para spaceBefore="10mm" align="right">{
%s, %s</para>""" sco_preferences.get_preference("DirectorName", formsemestre.id) or ""
% ( }, {
sco_preferences.get_preference("DirectorName", formsemestre_id) or "", sco_preferences.get_preference("DirectorTitle", formsemestre.id) or ""
sco_preferences.get_preference("DirectorTitle", formsemestre_id) or "", }</para>""",
),
style, style,
) )
@ -907,7 +919,7 @@ def _pvjury_pdf_type(
"FONTNAME", "FONTNAME",
(0, 0), (0, 0),
(-1, 0), (-1, 0),
sco_preferences.get_preference("PV_FONTNAME", formsemestre_id), sco_preferences.get_preference("PV_FONTNAME", formsemestre.id),
), ),
("LINEBELOW", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ("LINEBELOW", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
("LINEABOVE", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)), ("LINEABOVE", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
@ -922,4 +934,4 @@ def _pvjury_pdf_type(
) )
) )
return objects return objects, a_diplome