1
0
forked from ScoDoc/ScoDoc

refactoring et préparatifs pour lettres individuelles BUT

This commit is contained in:
Emmanuel Viennet 2022-07-07 23:34:14 +02:00
parent 206825c42c
commit 543c3759d9
6 changed files with 195 additions and 163 deletions

View File

@ -21,6 +21,8 @@ from flask import g, url_for
from app import db from app import db
from app import log from app import log
from app.comp.res_but import ResultatsSemestreBUT from app.comp.res_but import ResultatsSemestreBUT
from app.comp.res_compat import NotesTableCompat
from app.comp import res_sem from app.comp import res_sem
from app.models import formsemestre from app.models import formsemestre
@ -50,7 +52,16 @@ from app.scodoc.sco_exceptions import ScoException, ScoValueError
from app.scodoc import sco_cursus_dut from app.scodoc import sco_cursus_dut
class SituationEtudCursusBUT(sco_cursus_dut.SituationEtudCursus): class SituationEtudCursusBUT(sco_cursus_dut.SituationEtudCursusClassic):
def __init__(self, etud: dict, formsemestre_id: int, res: ResultatsSemestreBUT): def __init__(self, etud: dict, formsemestre_id: int, res: ResultatsSemestreBUT):
self.semestre_non_terminal = bool super().__init__(etud, formsemestre_id, res)
self.formation # Ajustements pour le BUT
self.can_compensate_with_prev = False # jamais de compensation à la mode DUT
def check_compensation_dut(self, semc: dict, ntc: NotesTableCompat):
"Jamais de compensation façon DUT"
return False
def parcours_validated(self):
"True si le parcours est validé"
return False # XXX TODO

View File

@ -341,9 +341,7 @@ class SituationEtudCursusClassic(SituationEtudCursus):
)[0]["formation_code"] )[0]["formation_code"]
# si sem peut servir à compenser le semestre courant, positionne # si sem peut servir à compenser le semestre courant, positionne
# can_compensate # can_compensate
sem["can_compensate"] = check_compensation( sem["can_compensate"] = self.check_compensation_dut(sem, nt)
self.etudid, self.sem, self.nt, sem, nt
)
self.ue_acros = list(ue_acros.keys()) self.ue_acros = list(ue_acros.keys())
self.ue_acros.sort() self.ue_acros.sort()
@ -655,6 +653,46 @@ class SituationEtudCursusClassic(SituationEtudCursus):
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
) # > modif decision jury ) # > modif decision jury
def check_compensation_dut(self, semc: dict, ntc: NotesTableCompat):
"""Compensations DUT
Vérifie si le semestre sem peut se compenser en utilisant semc
- semc non utilisé par un autre semestre
- decision du jury prise ADM ou ADJ ou ATT ou ADC
- barres UE (moy ue > 8) dans sem et semc
- moyenne des moy_gen > 10
Return boolean
"""
# -- deja utilise ?
decc = ntc.get_etud_decision_sem(self.etudid)
if (
decc
and decc["compense_formsemestre_id"]
and decc["compense_formsemestre_id"] != self.sem["formsemestre_id"]
):
return False
# -- semestres consecutifs ?
if abs(self.sem["semestre_id"] - semc["semestre_id"]) != 1:
return False
# -- decision jury:
if decc and not decc["code"] in (ADM, ADJ, ATT, ADC):
return False
# -- barres UE et moyenne des moyennes:
moy_gen = self.nt.get_etud_moy_gen(self.etudid)
moy_genc = ntc.get_etud_moy_gen(self.etudid)
try:
moy_moy = (moy_gen + moy_genc) / 2
except: # un des semestres sans aucune note !
return False
if (
self.nt.etud_check_conditions_ues(self.etudid)[0]
and ntc.etud_check_conditions_ues(self.etudid)[0]
and moy_moy >= NOTES_BARRE_GEN_COMPENSATION
):
return True
else:
return False
class SituationEtudCursusECTS(SituationEtudCursusClassic): class SituationEtudCursusECTS(SituationEtudCursusClassic):
"""Gestion parcours basés sur ECTS""" """Gestion parcours basés sur ECTS"""
@ -701,47 +739,6 @@ class SituationEtudCursusECTS(SituationEtudCursusClassic):
return choices return choices
#
def check_compensation(etudid, sem, nt, semc, ntc):
"""Verifie si le semestre sem peut se compenser en utilisant semc
- semc non utilisé par un autre semestre
- decision du jury prise ADM ou ADJ ou ATT ou ADC
- barres UE (moy ue > 8) dans sem et semc
- moyenne des moy_gen > 10
Return boolean
"""
# -- deja utilise ?
decc = ntc.get_etud_decision_sem(etudid)
if (
decc
and decc["compense_formsemestre_id"]
and decc["compense_formsemestre_id"] != sem["formsemestre_id"]
):
return False
# -- semestres consecutifs ?
if abs(sem["semestre_id"] - semc["semestre_id"]) != 1:
return False
# -- decision jury:
if decc and not decc["code"] in (ADM, ADJ, ATT, ADC):
return False
# -- barres UE et moyenne des moyennes:
moy_gen = nt.get_etud_moy_gen(etudid)
moy_genc = ntc.get_etud_moy_gen(etudid)
try:
moy_moy = (moy_gen + moy_genc) / 2
except: # un des semestres sans aucune note !
return False
if (
nt.etud_check_conditions_ues(etudid)[0]
and ntc.etud_check_conditions_ues(etudid)[0]
and moy_moy >= NOTES_BARRE_GEN_COMPENSATION
):
return True
else:
return False
# ------------------------------------------------------------------------------------------- # -------------------------------------------------------------------------------------------

View File

@ -101,7 +101,7 @@ def get_formsemestre(formsemestre_id, raise_soft_exc=False):
return g.stored_get_formsemestre[formsemestre_id] return g.stored_get_formsemestre[formsemestre_id]
if not isinstance(formsemestre_id, int): if not isinstance(formsemestre_id, int):
log(f"get_formsemestre: invalid id '{formsemestre_id}'") log(f"get_formsemestre: invalid id '{formsemestre_id}'")
raise ScoInvalidIdType("formsemestre_id must be an integer !") raise ScoInvalidIdType("get_formsemestre: formsemestre_id must be an integer !")
sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id}) sems = do_formsemestre_list(args={"formsemestre_id": formsemestre_id})
if not sems: if not sems:
log("get_formsemestre: invalid formsemestre_id (%s)" % formsemestre_id) log("get_formsemestre: invalid formsemestre_id (%s)" % formsemestre_id)

View File

@ -103,14 +103,14 @@ def feuille_preparation_jury(formsemestre_id):
moy[etud.id] = nt.get_etud_moy_gen(etud.id) moy[etud.id] = nt.get_etud_moy_gen(etud.id)
for ue in nt.get_ues_stat_dict(filter_sport=True): for ue in nt.get_ues_stat_dict(filter_sport=True):
ue_status = nt.get_etud_ue_status(etud.id, ue["ue_id"]) ue_status = nt.get_etud_ue_status(etud.id, ue["ue_id"])
ue_code_s = ue["ue_code"] + "_%s" % nt.sem["semestre_id"] ue_code_s = f'{ue["ue_code"]}_{nt.sem["semestre_id"]}'
moy_ue[ue_code_s][etud.id] = ue_status["moy"] if ue_status else "" moy_ue[ue_code_s][etud.id] = ue_status["moy"] if ue_status else ""
ue_acro[ue_code_s] = (ue["numero"], ue["acronyme"], ue["titre"]) ue_acro[ue_code_s] = (ue["numero"], ue["acronyme"], ue["titre"])
if Se.prev: if Se.prev:
try: try:
moy_inter[etud.id] = (moy[etud.id] + prev_moy[etud.id]) / 2.0 moy_inter[etud.id] = (moy[etud.id] + prev_moy[etud.id]) / 2.0
except: except (KeyError, TypeError):
pass pass
decision = nt.get_etud_decision_sem(etud.id) decision = nt.get_etud_decision_sem(etud.id)
@ -120,10 +120,12 @@ def feuille_preparation_jury(formsemestre_id):
code[etud.id] += "+" # indique qu'il a servi a compenser code[etud.id] += "+" # indique qu'il a servi a compenser
assidu[etud.id] = {False: "Non", True: "Oui"}.get(decision["assidu"], "") assidu[etud.id] = {False: "Non", True: "Oui"}.get(decision["assidu"], "")
autorisations = ScolarAutorisationInscription.query.filter_by( autorisations_etud = ScolarAutorisationInscription.query.filter_by(
etudid=etud.id, origin_formsemestre_id=formsemestre_id etudid=etud.id, origin_formsemestre_id=formsemestre_id
).all() ).all()
autorisations[etud.id] = ", ".join(["S{x.semestre_id}" for x in autorisations]) autorisations[etud.id] = ", ".join(
[f"S{x.semestre_id}" for x in autorisations_etud]
)
# parcours: # parcours:
parcours[etud.id] = Se.get_parcours_descr() parcours[etud.id] = Se.get_parcours_descr()
# groupe principal (td) # groupe principal (td)
@ -154,11 +156,11 @@ def feuille_preparation_jury(formsemestre_id):
sid = sem["semestre_id"] sid = sem["semestre_id"]
sn = sp = "" sn = sp = ""
if sid >= 0: if sid >= 0:
sn = "S%s" % sid sn = f"S{sid}"
if prev_moy: # si qq chose dans precedent if prev_moy: # si qq chose dans precedent
sp = "S%s" % (sid - 1) sp = f"S{sid - 1}"
ws = sco_excel.ScoExcelSheet(sheet_name="Prepa Jury %s" % sn) sheet = sco_excel.ScoExcelSheet(sheet_name=f"Prepa Jury {sn}")
# génération des styles # génération des styles
style_bold = sco_excel.excel_make_style(size=10, bold=True) style_bold = sco_excel.excel_make_style(size=10, bold=True)
style_center = sco_excel.excel_make_style(halign="center") style_center = sco_excel.excel_make_style(halign="center")
@ -174,10 +176,10 @@ def feuille_preparation_jury(formsemestre_id):
) )
# Première ligne # Première ligne
ws.append_single_cell_row( sheet.append_single_cell_row(
"Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold "Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold
) )
ws.append_blank_row() sheet.append_blank_row()
# Ligne de titre # Ligne de titre
titles = ["Rang"] titles = ["Rang"]
@ -199,25 +201,25 @@ def feuille_preparation_jury(formsemestre_id):
] ]
if prev_moy: # si qq chose dans precedent if prev_moy: # si qq chose dans precedent
titles += [prev_ue_acro[x][1] for x in ue_prev_codes] + [ titles += [prev_ue_acro[x][1] for x in ue_prev_codes] + [
"Moy %s" % sp, f"Moy {sp}",
"Décision %s" % sp, f"Décision {sp}",
] ]
titles += [ue_acro[x][1] for x in ue_codes] + ["Moy %s" % sn] titles += [ue_acro[x][1] for x in ue_codes] + [f"Moy {sn}"]
if moy_inter: if moy_inter:
titles += ["Moy %s-%s" % (sp, sn)] titles += [f"Moy {sp}-{sn}"]
titles += ["Abs", "Abs Injust."] titles += ["Abs", "Abs Injust."]
if code: if code:
titles.append("Proposit. %s" % sn) titles.append("Proposit. {sn}")
if autorisations: if autorisations:
titles.append("Autorisations") titles.append("Autorisations")
# titles.append('Assidu') # titles.append('Assidu')
ws.append_row(ws.make_row(titles, style_boldcenter)) sheet.append_row(sheet.make_row(titles, style_boldcenter))
if prev_moy: # if prev_moy:
tit_prev_moy = "Moy " + sp # tit_prev_moy = "Moy " + sp
col_prev_moy = titles.index(tit_prev_moy) # # col_prev_moy = titles.index(tit_prev_moy)
tit_moy = "Moy " + sn # tit_moy = "Moy " + sn
col_moy = titles.index(tit_moy) # col_moy = titles.index(tit_moy)
col_abs = titles.index("Abs") # col_abs = titles.index("Abs")
def fmt(x): def fmt(x):
"reduit les notes a deux chiffres" "reduit les notes a deux chiffres"
@ -230,13 +232,13 @@ def feuille_preparation_jury(formsemestre_id):
i = 1 # numero etudiant i = 1 # numero etudiant
for etud in etuds: for etud in etuds:
cells = [] cells = []
cells.append(ws.make_cell(str(i))) cells.append(sheet.make_cell(str(i)))
if sco_preferences.get_preference("prepa_jury_nip"): if sco_preferences.get_preference("prepa_jury_nip"):
cells.append(ws.make_cell(etud.code_nip)) cells.append(sheet.make_cell(etud.code_nip))
if sco_preferences.get_preference("prepa_jury_ine"): if sco_preferences.get_preference("prepa_jury_ine"):
cells.append(ws.make_cell(etud.code_ine)) cells.append(sheet.make_cell(etud.code_ine))
admission = etud.admission.first() admission = etud.admission.first()
cells += ws.make_row( cells += sheet.make_row(
[ [
etud.id, etud.id,
etud.civilite_str, etud.civilite_str,
@ -254,50 +256,52 @@ def feuille_preparation_jury(formsemestre_id):
if prev_moy: if prev_moy:
for ue_acro in ue_prev_codes: for ue_acro in ue_prev_codes:
cells.append( cells.append(
ws.make_cell( sheet.make_cell(
fmt(prev_moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note fmt(prev_moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note
) )
) )
co += 1 co += 1
cells.append( cells.append(
ws.make_cell(fmt(prev_moy.get(etud.id, "")), style_bold) sheet.make_cell(fmt(prev_moy.get(etud.id, "")), style_bold)
) # moy gen prev ) # moy gen prev
cells.append( cells.append(
ws.make_cell(fmt(prev_code.get(etud.id, "")), style_moy) sheet.make_cell(fmt(prev_code.get(etud.id, "")), style_moy)
) # decision prev ) # decision prev
co += 2 co += 2
for ue_acro in ue_codes: for ue_acro in ue_codes:
cells.append( cells.append(
ws.make_cell(fmt(moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note) sheet.make_cell(
fmt(moy_ue.get(ue_acro, {}).get(etud.id, "")), style_note
)
) )
co += 1 co += 1
cells.append( cells.append(
ws.make_cell(fmt(moy.get(etud.id, "")), style_note_bold) sheet.make_cell(fmt(moy.get(etud.id, "")), style_note_bold)
) # moy gen ) # moy gen
co += 1 co += 1
if moy_inter: if moy_inter:
cells.append(ws.make_cell(fmt(moy_inter.get(etud.id, "")), style_note)) cells.append(sheet.make_cell(fmt(moy_inter.get(etud.id, "")), style_note))
cells.append(ws.make_cell(str(nbabs.get(etud.id, "")), style_center)) cells.append(sheet.make_cell(str(nbabs.get(etud.id, "")), style_center))
cells.append(ws.make_cell(str(nbabsjust.get(etud.id, "")), style_center)) cells.append(sheet.make_cell(str(nbabsjust.get(etud.id, "")), style_center))
if code: if code:
cells.append(ws.make_cell(code.get(etud.id, ""), style_moy)) cells.append(sheet.make_cell(code.get(etud.id, ""), style_moy))
cells.append(ws.make_cell(autorisations.get(etud.id, ""), style_moy)) cells.append(sheet.make_cell(autorisations.get(etud.id, ""), style_moy))
# l.append(assidu.get(etud.id, '')) # l.append(assidu.get(etud.id, ''))
ws.append_row(cells) sheet.append_row(cells)
i += 1 i += 1
# #
ws.append_blank_row() sheet.append_blank_row()
# Explications des codes # Explications des codes
codes = list(sco_codes_parcours.CODES_EXPL.keys()) codes = list(sco_codes_parcours.CODES_EXPL.keys())
codes.sort() codes.sort()
ws.append_single_cell_row("Explication des codes") sheet.append_single_cell_row("Explication des codes")
for code in codes: for code in codes:
ws.append_row( sheet.append_row(
ws.make_row(["", "", "", code, sco_codes_parcours.CODES_EXPL[code]]) sheet.make_row(["", "", "", code, sco_codes_parcours.CODES_EXPL[code]])
) )
ws.append_row( sheet.append_row(
ws.make_row( sheet.make_row(
[ [
"", "",
"", "",
@ -308,16 +312,16 @@ def feuille_preparation_jury(formsemestre_id):
) )
) )
# UE : Correspondances acronyme et titre complet # UE : Correspondances acronyme et titre complet
ws.append_blank_row() sheet.append_blank_row()
ws.append_single_cell_row("Titre des UE") sheet.append_single_cell_row("Titre des UE")
if prev_moy: if prev_moy:
for ue in ntp.get_ues_stat_dict(filter_sport=True): for ue in ntp.get_ues_stat_dict(filter_sport=True):
ws.append_row(ws.make_row(["", "", "", ue["acronyme"], ue["titre"]])) sheet.append_row(sheet.make_row(["", "", "", ue["acronyme"], ue["titre"]]))
for ue in nt.get_ues_stat_dict(filter_sport=True): for ue in nt.get_ues_stat_dict(filter_sport=True):
ws.append_row(ws.make_row(["", "", "", ue["acronyme"], ue["titre"]])) sheet.append_row(sheet.make_row(["", "", "", ue["acronyme"], ue["titre"]]))
# #
ws.append_blank_row() sheet.append_blank_row()
ws.append_single_cell_row( sheet.append_single_cell_row(
"Préparé par %s le %s sur %s pour %s" "Préparé par %s le %s sur %s pour %s"
% ( % (
sco_version.SCONAME, sco_version.SCONAME,
@ -326,7 +330,7 @@ def feuille_preparation_jury(formsemestre_id):
current_user, current_user,
) )
) )
xls = ws.generate() xls = sheet.generate()
flash("Feuille préparation jury générée") flash("Feuille préparation jury générée")
return scu.send_file( return scu.send_file(
xls, xls,

View File

@ -30,6 +30,8 @@
import io import io
import re import re
from PIL import Image as PILImage
import reportlab import reportlab
from reportlab.lib.units import cm, mm from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_RIGHT, TA_JUSTIFY from reportlab.lib.enums import TA_RIGHT, TA_JUSTIFY
@ -41,6 +43,7 @@ from reportlab.lib import styles
from reportlab.lib.colors import Color from reportlab.lib.colors import Color
from flask import g from flask import g
from app.models.formsemestre import FormSemestre
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
@ -125,6 +128,7 @@ def page_footer(canvas, doc, logo, preferences, with_page_numbers=True):
def page_header(canvas, doc, logo, preferences, only_on_first_page=False): def page_header(canvas, doc, logo, preferences, only_on_first_page=False):
"Ajoute au canvas le frame avec le logo"
if only_on_first_page and int(doc.page) > 1: if only_on_first_page and int(doc.page) > 1:
return return
height = doc.pagesize[1] height = doc.pagesize[1]
@ -147,12 +151,12 @@ def page_header(canvas, doc, logo, preferences, only_on_first_page=False):
class CourrierIndividuelTemplate(PageTemplate): class CourrierIndividuelTemplate(PageTemplate):
"""Template pour courrier avisant des decisions de jury (1 page /etudiant)""" """Template pour courrier avisant des decisions de jury (1 page par étudiant)"""
def __init__( def __init__(
self, self,
document, document,
pagesbookmarks={}, pagesbookmarks=None,
author=None, author=None,
title=None, title=None,
subject=None, subject=None,
@ -163,7 +167,7 @@ class CourrierIndividuelTemplate(PageTemplate):
template_name="CourrierJuryTemplate", template_name="CourrierJuryTemplate",
): ):
"""Initialise our page template.""" """Initialise our page template."""
self.pagesbookmarks = pagesbookmarks self.pagesbookmarks = pagesbookmarks or {}
self.pdfmeta_author = author self.pdfmeta_author = author
self.pdfmeta_title = title self.pdfmeta_title = title
self.pdfmeta_subject = subject self.pdfmeta_subject = subject
@ -237,32 +241,32 @@ class CourrierIndividuelTemplate(PageTemplate):
width=LOGO_HEADER_WIDTH, width=LOGO_HEADER_WIDTH,
) )
def beforeDrawPage(self, canvas, doc): def beforeDrawPage(self, canv, doc):
"""Draws a logo and an contribution message on each page.""" """Draws a logo and an contribution message on each page."""
# ---- Add some meta data and bookmarks # ---- Add some meta data and bookmarks
if self.pdfmeta_author: if self.pdfmeta_author:
canvas.setAuthor(SU(self.pdfmeta_author)) canv.setAuthor(SU(self.pdfmeta_author))
if self.pdfmeta_title: if self.pdfmeta_title:
canvas.setTitle(SU(self.pdfmeta_title)) canv.setTitle(SU(self.pdfmeta_title))
if self.pdfmeta_subject: if self.pdfmeta_subject:
canvas.setSubject(SU(self.pdfmeta_subject)) canv.setSubject(SU(self.pdfmeta_subject))
bm = self.pagesbookmarks.get(doc.page, None) bm = self.pagesbookmarks.get(doc.page, None)
if bm != None: if bm != None:
key = bm key = bm
txt = SU(bm) txt = SU(bm)
canvas.bookmarkPage(key) canv.bookmarkPage(key)
canvas.addOutlineEntry(txt, bm) canv.addOutlineEntry(txt, bm)
# ---- Background image # ---- Background image
if self.background_image_filename and self.with_page_background: if self.background_image_filename and self.with_page_background:
canvas.drawImage( canv.drawImage(
self.background_image_filename, 0, 0, doc.pagesize[0], doc.pagesize[1] self.background_image_filename, 0, 0, doc.pagesize[0], doc.pagesize[1]
) )
# ---- Header/Footer # ---- Header/Footer
if self.with_header: if self.with_header:
page_header( page_header(
canvas, canv,
doc, doc,
self.logo_header, self.logo_header,
self.preferences, self.preferences,
@ -270,7 +274,7 @@ class CourrierIndividuelTemplate(PageTemplate):
) )
if self.with_footer: if self.with_footer:
page_footer( page_footer(
canvas, canv,
doc, doc,
self.logo_footer, self.logo_footer,
self.preferences, self.preferences,
@ -332,6 +336,39 @@ class PVTemplate(CourrierIndividuelTemplate):
# self.__pageNum += 1 # self.__pageNum += 1
def _simulate_br(paragraph_txt: str, para="<para>") -> str:
"""Reportlab bug turnaround (could be removed in a future version).
p is a string with Reportlab intra-paragraph XML tags.
Replaces <br/> (currently ignored by Reportlab) by </para><para>
"""
return ("</para>" + para).join(re.split(r"<.*?br.*?/>", paragraph_txt))
def _make_signature_image(signature, leftindent, formsemestre_id) -> Table:
"crée un paragraphe avec l'image signature"
# cree une image PIL pour avoir la taille (W,H)
f = io.BytesIO(signature)
img = PILImage.open(f)
width, height = img.size
pdfheight = (
1.0
* sco_preferences.get_preference("pv_sig_image_height", formsemestre_id)
* mm
)
f.seek(0, 0)
style = styles.ParagraphStyle({})
style.leading = 1.0 * sco_preferences.get_preference(
"SCOLAR_FONT_SIZE", formsemestre_id
) # vertical space
style.leftIndent = leftindent
return Table(
[("", Image(f, width=width * pdfheight / float(height), height=pdfheight))],
colWidths=(9 * cm, 7 * cm),
)
def pdf_lettres_individuelles( def pdf_lettres_individuelles(
formsemestre_id, formsemestre_id,
etudids=None, etudids=None,
@ -394,8 +431,8 @@ def pdf_lettres_individuelles(
document.addPageTemplates( document.addPageTemplates(
CourrierIndividuelTemplate( CourrierIndividuelTemplate(
document, document,
author="%s %s (E. Viennet)" % (sco_version.SCONAME, sco_version.SCOVERSION), author=f"{sco_version.SCONAME} {sco_version.SCOVERSION} (E. Viennet)",
title="Lettres décision %s" % sem["titreannee"], title=f"Lettres décision {sem['titreannee']}",
subject="Décision jury", subject="Décision jury",
margins=margins, margins=margins,
pagesbookmarks=bookmarks, pagesbookmarks=bookmarks,
@ -410,10 +447,7 @@ def pdf_lettres_individuelles(
def _descr_jury(sem, diplome): def _descr_jury(sem, diplome):
if not diplome: if not diplome:
t = "passage de Semestre %d en Semestre %d" % ( t = f"""passage de Semestre {sem["semestre_id"]} en Semestre {sem["semestre_id"] + 1}"""
sem["semestre_id"],
sem["semestre_id"] + 1,
)
s = "passage de semestre" s = "passage de semestre"
else: else:
t = "délivrance du diplôme" t = "délivrance du diplôme"
@ -428,6 +462,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None):
""" """
# #
formsemestre_id = sem["formsemestre_id"] formsemestre_id = sem["formsemestre_id"]
formsemestre = FormSemestre.query.get(formsemestre_id)
Se: SituationEtudCursus = decision["Se"] Se: SituationEtudCursus = decision["Se"]
t, s = _descr_jury(sem, Se.parcours_validated() or not Se.semestre_non_terminal) t, s = _descr_jury(sem, Se.parcours_validated() or not Se.semestre_non_terminal)
objects = [] objects = []
@ -437,7 +472,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None):
style.leading = 18 style.leading = 18
style.alignment = TA_JUSTIFY style.alignment = TA_JUSTIFY
params["semestre_id"] = sem["semestre_id"] params["semestre_id"] = formsemestre.semestre_id
params["decision_sem_descr"] = decision["decision_sem_descr"] params["decision_sem_descr"] = decision["decision_sem_descr"]
params["type_jury"] = t # type de jury (passage ou delivrance) params["type_jury"] = t # type de jury (passage ou delivrance)
params["type_jury_abbrv"] = s # idem, abbrégé params["type_jury_abbrv"] = s # idem, abbrégé
@ -450,28 +485,25 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None):
params["INSTITUTION_CITY"] = ( params["INSTITUTION_CITY"] = (
sco_preferences.get_preference("INSTITUTION_CITY", formsemestre_id) or "" sco_preferences.get_preference("INSTITUTION_CITY", formsemestre_id) or ""
) )
if decision["prev_decision_sem"]: if decision["prev_decision_sem"]:
params["prev_semestre_id"] = decision["prev"]["semestre_id"] params["prev_semestre_id"] = decision["prev"]["semestre_id"]
params["prev_code_descr"] = decision["prev_code_descr"]
params["prev_decision_sem_txt"] = ""
params["decision_orig"] = ""
if formsemestre.formation.is_apc():
# ajout champs spécifiques PV BUT
add_apc_infos(formsemestre, params, decision)
else:
# ajout champs spécifiques PV DUT
add_classic_infos(formsemestre, params, decision)
params.update(decision["identite"]) params.update(decision["identite"])
# fix domicile # fix domicile
if params["domicile"]: if params["domicile"]:
params["domicile"] = params["domicile"].replace("\\n", "<br/>") params["domicile"] = params["domicile"].replace("\\n", "<br/>")
# Décision semestre courant:
if sem["semestre_id"] >= 0:
params["decision_orig"] = "du semestre S%s" % sem["semestre_id"]
else:
params["decision_orig"] = ""
if decision["prev_decision_sem"]:
params["prev_decision_sem_txt"] = (
"""<b>Décision du semestre antérieur S%(prev_semestre_id)s :</b> %(prev_code_descr)s"""
% params
)
else:
params["prev_decision_sem_txt"] = ""
# UE capitalisées: # UE capitalisées:
if decision["decisions_ue"] and decision["decisions_ue_descr"]: if decision["decisions_ue"] and decision["decisions_ue_descr"]:
params["decision_ue_txt"] = ( params["decision_ue_txt"] = (
@ -567,39 +599,23 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None):
return objects return objects
def _simulate_br(p, para="<para>"): def add_classic_infos(formsemestre: FormSemestre, params: dict, decision: dict):
"""Reportlab bug turnaround (could be removed in a future version). """Ajoute les champs pour les formations classiques, donc avec codes semestres"""
p is a string with Reportlab intra-paragraph XML tags. if decision["prev_decision_sem"]:
Replaces <br/> (currently ignored by Reportlab) by </para><para> params["prev_code_descr"] = decision["prev_code_descr"]
""" params[
l = re.split(r"<.*?br.*?/>", p) "prev_decision_sem_txt"
return ("</para>" + para).join(l) ] = f"""<b>Décision du semestre antérieur S{params['prev_semestre_id']} :</b> {params['prev_code_descr']}"""
# Décision semestre courant:
if formsemestre.semestre_id >= 0:
params["decision_orig"] = f"du semestre S{formsemestre.semestre_id}"
else:
params["decision_orig"] = ""
def _make_signature_image(signature, leftindent, formsemestre_id): def add_apc_infos(formsemestre: FormSemestre, params: dict, decision: dict):
"cree un paragraphe avec l'image signature" """Ajoute les champs pour les formations APC (BUT), donc avec codes RCUE et année"""
# cree une image PIL pour avoir la taille (W,H) pass # TODO XXX
from PIL import Image as PILImage
f = io.BytesIO(signature)
im = PILImage.open(f)
width, height = im.size
pdfheight = (
1.0
* sco_preferences.get_preference("pv_sig_image_height", formsemestre_id)
* mm
)
f.seek(0, 0)
style = styles.ParagraphStyle({})
style.leading = 1.0 * sco_preferences.get_preference(
"SCOLAR_FONT_SIZE", formsemestre_id
) # vertical space
style.leftIndent = leftindent
return Table(
[("", Image(f, width=width * pdfheight / float(height), height=pdfheight))],
colWidths=(9 * cm, 7 * cm),
)
# ---------------------------------------------- # ----------------------------------------------

View File

@ -296,7 +296,9 @@ def formsemestre_bulletinetud(
format = format or "html" format = format or "html"
if not isinstance(formsemestre_id, int): if not isinstance(formsemestre_id, int):
raise ScoInvalidIdType("formsemestre_id must be an integer !") raise ScoInvalidIdType(
"formsemestre_bulletinetud: formsemestre_id must be an integer !"
)
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if etudid: if etudid:
etud = models.Identite.query.get_or_404(etudid) etud = models.Identite.query.get_or_404(etudid)
@ -826,7 +828,9 @@ def XMLgetFormsemestres(etape_apo=None, formsemestre_id=None):
if not formsemestre_id: if not formsemestre_id:
return flask.abort(404, "argument manquant: formsemestre_id") return flask.abort(404, "argument manquant: formsemestre_id")
if not isinstance(formsemestre_id, int): if not isinstance(formsemestre_id, int):
return flask.abort(404, "formsemestre_id must be an integer !") return flask.abort(
404, "XMLgetFormsemestres: formsemestre_id must be an integer !"
)
args = {} args = {}
if etape_apo: if etape_apo:
args["etape_apo"] = etape_apo args["etape_apo"] = etape_apo