forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -44,9 +44,13 @@ from app.views import notes_bp as bp
|
|||||||
from app.views import ScoData
|
from app.views import ScoData
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/bulletin_but/<int:formsemestre_id>/<int:etudid>")
|
|
||||||
@bp.route(
|
@bp.route(
|
||||||
"/bulletin_but/<int:formsemestre_id>/<int:etudid>/pdf", defaults={"fmt": "pdf"}
|
"/bulletin_but/<int:formsemestre_id>/<int:etudid>", endpoint="bulletin_but_html"
|
||||||
|
)
|
||||||
|
@bp.route(
|
||||||
|
"/bulletin_but/<int:formsemestre_id>/<int:etudid>/pdf",
|
||||||
|
defaults={"fmt": "pdf"},
|
||||||
|
endpoint="bulletin_but_pdf",
|
||||||
)
|
)
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
|
@ -20,12 +20,15 @@ from reportlab.lib.units import cm, mm
|
|||||||
from reportlab.platypus import Paragraph, Spacer, Table
|
from reportlab.platypus import Paragraph, Spacer, Table
|
||||||
|
|
||||||
from app.but import cursus_but
|
from app.but import cursus_but
|
||||||
from app.models import FormSemestre, Identite, ScolarFormSemestreValidation
|
from app.models import (
|
||||||
|
BulAppreciations,
|
||||||
|
FormSemestre,
|
||||||
|
Identite,
|
||||||
|
ScolarFormSemestreValidation,
|
||||||
|
)
|
||||||
|
|
||||||
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
||||||
from app.scodoc import sco_bulletins
|
|
||||||
from app.scodoc.sco_logos import Logo
|
from app.scodoc.sco_logos import Logo
|
||||||
from app.scodoc import sco_pdf, sco_preferences
|
|
||||||
from app.scodoc.sco_pdf import PDFLOCK, SU
|
from app.scodoc.sco_pdf import PDFLOCK, SU
|
||||||
|
|
||||||
|
|
||||||
@ -41,6 +44,7 @@ def make_bulletin_but_court_pdf(
|
|||||||
ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None,
|
ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None,
|
||||||
ues_acronyms: list[str] = None,
|
ues_acronyms: list[str] = None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
|
"génère le bulletin court BUT en pdf"
|
||||||
# A priori ce verrou n'est plus nécessaire avec Flask (multi-process)
|
# A priori ce verrou n'est plus nécessaire avec Flask (multi-process)
|
||||||
# mais...
|
# mais...
|
||||||
try:
|
try:
|
||||||
@ -64,6 +68,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
multi_pages = False # une page par bulletin
|
multi_pages = False # une page par bulletin
|
||||||
small_fontsize = "8"
|
small_fontsize = "8"
|
||||||
color_blue_bg = Color(0, 153 / 255, 204 / 255)
|
color_blue_bg = Color(0, 153 / 255, 204 / 255)
|
||||||
|
color_gray_bg = Color(0.86, 0.86, 0.86)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -94,13 +99,23 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
|
|
||||||
self.nb_ues = len(self.ues_acronyms)
|
self.nb_ues = len(self.ues_acronyms)
|
||||||
# Styles PDF
|
# Styles PDF
|
||||||
self.style_cell = styles.ParagraphStyle("style_cell")
|
self.style_base = styles.ParagraphStyle("style_base")
|
||||||
self.style_cell.fontName = "Helvetica"
|
self.style_base.fontName = "Helvetica"
|
||||||
|
self.style_base.fontSize = 9
|
||||||
|
|
||||||
|
self.style_nom = styles.ParagraphStyle("style_nom", self.style_base)
|
||||||
|
self.style_nom.fontSize = 11
|
||||||
|
self.style_nom.fontName = "Helvetica-Bold"
|
||||||
|
|
||||||
|
self.style_field = self.style_base # écrase style defaut buleltins
|
||||||
|
|
||||||
|
self.style_cell = styles.ParagraphStyle("style_cell", self.style_base)
|
||||||
self.style_cell.fontSize = 7
|
self.style_cell.fontSize = 7
|
||||||
self.style_cell.leading = 7
|
self.style_cell.leading = 7
|
||||||
self.style_bold = styles.ParagraphStyle("style_bold", self.style_cell)
|
self.style_cell_bold = styles.ParagraphStyle("style_cell_bold", self.style_cell)
|
||||||
self.style_bold.fontName = "Helvetica-Bold"
|
self.style_cell_bold.fontName = "Helvetica-Bold"
|
||||||
self.style_head = styles.ParagraphStyle("style_head", self.style_bold)
|
|
||||||
|
self.style_head = styles.ParagraphStyle("style_head", self.style_cell_bold)
|
||||||
self.style_head.fontSize = 9
|
self.style_head.fontSize = 9
|
||||||
|
|
||||||
self.style_niveaux = styles.ParagraphStyle("style_niveaux", self.style_cell)
|
self.style_niveaux = styles.ParagraphStyle("style_niveaux", self.style_cell)
|
||||||
@ -128,6 +143,25 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
"style_niveaux_code", self.style_niveaux
|
"style_niveaux_code", self.style_niveaux
|
||||||
)
|
)
|
||||||
self.style_niveaux_code.borderColor = black
|
self.style_niveaux_code.borderColor = black
|
||||||
|
#
|
||||||
|
self.style_jury = styles.ParagraphStyle("style_jury", self.style_base)
|
||||||
|
self.style_jury.fontSize = 9
|
||||||
|
self.style_jury.leading = self.style_jury.fontSize * 1.4 # espace les lignes
|
||||||
|
self.style_jury.backColor = self.color_gray_bg
|
||||||
|
self.style_jury.borderColor = black
|
||||||
|
self.style_jury.borderWidth = 1
|
||||||
|
self.style_jury.borderPadding = 2
|
||||||
|
self.style_jury.borderRadius = 2
|
||||||
|
|
||||||
|
self.style_appreciations = styles.ParagraphStyle(
|
||||||
|
"style_appreciations", self.style_base
|
||||||
|
)
|
||||||
|
self.style_appreciations.fontSize = 9
|
||||||
|
self.style_appreciations.leading = (
|
||||||
|
self.style_jury.fontSize * 1.4
|
||||||
|
) # espace les lignes
|
||||||
|
|
||||||
|
self.style_assiduite = self.style_cell
|
||||||
|
|
||||||
# Géométrie page
|
# Géométrie page
|
||||||
self.width_page_avail = 185 * mm # largeur utilisable
|
self.width_page_avail = 185 * mm # largeur utilisable
|
||||||
@ -138,7 +172,27 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
self.width_col_code = self.width_col_ue
|
self.width_col_code = self.width_col_ue
|
||||||
# Niveaux
|
# Niveaux
|
||||||
self.width_col_niveaux_titre = 24 * mm
|
self.width_col_niveaux_titre = 24 * mm
|
||||||
self.width_col_niveaux_code = 12 * mm
|
self.width_col_niveaux_code = 14 * mm
|
||||||
|
|
||||||
|
def bul_title_pdf(self, preference_field="bul_but_pdf_title") -> list:
|
||||||
|
"""Génère la partie "titre" du bulletin de notes.
|
||||||
|
Renvoie une liste d'objets platypus
|
||||||
|
"""
|
||||||
|
# comme les bulletins standard, mais avec notre préférence
|
||||||
|
return super().bul_title_pdf(preference_field=preference_field)
|
||||||
|
|
||||||
|
def bul_part_below(self, fmt="pdf") -> list:
|
||||||
|
"""Génère les informations placées sous la table
|
||||||
|
Dans le cas du bul. court BUT pdf, seulement les appréciations.
|
||||||
|
fmt est ignoré ici.
|
||||||
|
"""
|
||||||
|
appreciations = BulAppreciations.get_appreciations_list(
|
||||||
|
self.formsemestre.id, self.etud.id
|
||||||
|
)
|
||||||
|
return [
|
||||||
|
Spacer(1, 3 * mm),
|
||||||
|
self.bul_appreciations_pdf(appreciations, style=self.style_appreciations),
|
||||||
|
]
|
||||||
|
|
||||||
def bul_table(self, fmt=None) -> list:
|
def bul_table(self, fmt=None) -> list:
|
||||||
"""Génère la table centrale du bulletin de notes
|
"""Génère la table centrale du bulletin de notes
|
||||||
@ -148,7 +202,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
style_table_2cols = [
|
style_table_2cols = [
|
||||||
("ALIGN", (0, -1), (0, -1), "LEFT"),
|
("ALIGN", (0, -1), (0, -1), "LEFT"),
|
||||||
("ALIGN", (-1, -1), (-1, -1), "RIGHT"),
|
("ALIGN", (-1, -1), (-1, -1), "RIGHT"),
|
||||||
("VALIGN", (0, 0), (-1, 1), "TOP"),
|
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||||
("LEFTPADDING", (0, 0), (-1, -1), 0),
|
("LEFTPADDING", (0, 0), (-1, -1), 0),
|
||||||
("TOPPADDING", (0, 0), (-1, -1), 0),
|
("TOPPADDING", (0, 0), (-1, -1), 0),
|
||||||
("RIGHTPADDING", (0, 0), (-1, -1), 0),
|
("RIGHTPADDING", (0, 0), (-1, -1), 0),
|
||||||
@ -156,15 +210,24 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
]
|
]
|
||||||
# Ligne avec boite assiduité et table UEs
|
# Ligne avec boite assiduité et table UEs
|
||||||
table_abs_ues = Table(
|
table_abs_ues = Table(
|
||||||
[[self.box_assiduite(), self.table_ues()]],
|
[
|
||||||
colWidths=(3 * cm, self.width_page_avail - 3 * cm),
|
[
|
||||||
|
self.boite_identite() + [Spacer(1, 3 * mm), self.boite_assiduite()],
|
||||||
|
self.table_ues(),
|
||||||
|
],
|
||||||
|
],
|
||||||
style=style_table_2cols,
|
style=style_table_2cols,
|
||||||
)
|
)
|
||||||
table_abs_ues.hAlign = "RIGHT"
|
table_abs_ues.hAlign = "RIGHT"
|
||||||
# Ligne (en bas) avec table cursus et boite jury
|
# Ligne (en bas) avec table cursus et boite jury
|
||||||
table_cursus_jury = Table(
|
table_cursus_jury = Table(
|
||||||
[[self.table_cursus_but(), self.boite_decisions_jury()]],
|
[
|
||||||
colWidths=(self.width_page_avail - 45 * mm, 45 * mm),
|
[
|
||||||
|
self.table_cursus_but(),
|
||||||
|
[Spacer(1, 8 * mm), self.boite_decisions_jury()],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
colWidths=(self.width_page_avail - 84 * mm, 84 * mm),
|
||||||
style=style_table_2cols,
|
style=style_table_2cols,
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
@ -223,13 +286,15 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
col_widths = [self.width_col_ue_titres] + [self.width_col_ue] * self.nb_ues
|
col_widths = [self.width_col_ue_titres] + [self.width_col_ue] * self.nb_ues
|
||||||
|
|
||||||
rows_styled = [[Paragraph(SU(str(cell)), self.style_head) for cell in rows[0]]]
|
rows_styled = [[Paragraph(SU(str(cell)), self.style_head) for cell in rows[0]]]
|
||||||
rows_styled += [[Paragraph(SU(str(cell)), self.style_bold) for cell in rows[1]]]
|
rows_styled += [
|
||||||
|
[Paragraph(SU(str(cell)), self.style_cell_bold) for cell in rows[1]]
|
||||||
|
]
|
||||||
rows_styled += [
|
rows_styled += [
|
||||||
[Paragraph(SU(str(cell)), self.style_cell) for cell in row]
|
[Paragraph(SU(str(cell)), self.style_cell) for cell in row]
|
||||||
for row in rows[2:-1]
|
for row in rows[2:-1]
|
||||||
]
|
]
|
||||||
rows_styled += [
|
rows_styled += [
|
||||||
[Paragraph(SU(str(cell)), self.style_bold) for cell in rows[-1]]
|
[Paragraph(SU(str(cell)), self.style_cell_bold) for cell in rows[-1]]
|
||||||
]
|
]
|
||||||
table = Table(
|
table = Table(
|
||||||
rows_styled,
|
rows_styled,
|
||||||
@ -285,7 +350,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
] * self.nb_ues
|
] * self.nb_ues
|
||||||
|
|
||||||
rows_styled = [
|
rows_styled = [
|
||||||
[Paragraph(SU(str(cell)), self.style_bold) for cell in row]
|
[Paragraph(SU(str(cell)), self.style_cell_bold) for cell in row]
|
||||||
for row in rows[:2]
|
for row in rows[:2]
|
||||||
]
|
]
|
||||||
rows_styled += [
|
rows_styled += [
|
||||||
@ -310,7 +375,26 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
"saes", "Situations d'Apprentissage et d'Évaluation (SAÉ)"
|
"saes", "Situations d'Apprentissage et d'Évaluation (SAÉ)"
|
||||||
)
|
)
|
||||||
|
|
||||||
def box_assiduite(self) -> Table:
|
def boite_identite(self) -> list:
|
||||||
|
"Les informations sur l'identité et l'inscription de l'étudiant"
|
||||||
|
return [
|
||||||
|
Paragraph(
|
||||||
|
SU(f"""{self.etud.nomprenom}"""),
|
||||||
|
style=self.style_nom,
|
||||||
|
),
|
||||||
|
Paragraph(
|
||||||
|
SU(
|
||||||
|
f"""
|
||||||
|
<b>{self.bul["demission"]}</b><br/>
|
||||||
|
Formation: {self.formsemestre.titre_num()}<br/>
|
||||||
|
Année scolaire: {self.formsemestre.annee_scolaire_str()}<br/>
|
||||||
|
"""
|
||||||
|
),
|
||||||
|
style=self.style_base,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def boite_assiduite(self) -> Table:
|
||||||
"Les informations sur l'assiduité"
|
"Les informations sur l'assiduité"
|
||||||
if not self.bul["options"]["show_abs"]:
|
if not self.bul["options"]["show_abs"]:
|
||||||
return Paragraph("") # empty
|
return Paragraph("") # empty
|
||||||
@ -326,7 +410,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
for row in rows[:1]
|
for row in rows[:1]
|
||||||
]
|
]
|
||||||
rows_styled += [
|
rows_styled += [
|
||||||
[Paragraph(SU(str(cell)), self.style_cell) for cell in row]
|
[Paragraph(SU(str(cell)), self.style_assiduite) for cell in row]
|
||||||
for row in rows[1:]
|
for row in rows[1:]
|
||||||
]
|
]
|
||||||
table = Table(
|
table = Table(
|
||||||
@ -339,6 +423,7 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
("SPAN", (0, 1), (1, 1)),
|
("SPAN", (0, 1), (1, 1)),
|
||||||
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||||
],
|
],
|
||||||
|
colWidths=(25 * mm, 10 * mm),
|
||||||
)
|
)
|
||||||
table.hAlign = "LEFT"
|
table.hAlign = "LEFT"
|
||||||
return table
|
return table
|
||||||
@ -387,12 +472,14 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
style=[
|
style=[
|
||||||
("ALIGN", (0, 0), (-1, -1), "CENTER"),
|
("ALIGN", (0, 0), (-1, -1), "CENTER"),
|
||||||
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
|
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
|
||||||
("LEFTPADDING", (0, 0), (-1, -1), 2),
|
("LEFTPADDING", (0, 0), (-1, -1), 5),
|
||||||
("TOPPADDING", (0, 0), (-1, -1), 4),
|
("TOPPADDING", (0, 0), (-1, -1), 4),
|
||||||
("RIGHTPADDING", (0, 0), (-1, -1), 2),
|
("RIGHTPADDING", (0, 0), (-1, -1), 5),
|
||||||
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
|
("BOTTOMPADDING", (0, 0), (-1, -1), 4),
|
||||||
# sert de séparateur entre les lignes:
|
# sert de séparateur entre les lignes:
|
||||||
("LINEABOVE", (0, 1), (-1, -1), 3, white),
|
("LINEABOVE", (0, 1), (-1, -1), 3, white),
|
||||||
|
# séparateur colonne
|
||||||
|
("LINEBEFORE", (1, 1), (-1, -1), 5, white),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
table.hAlign = "LEFT"
|
table.hAlign = "LEFT"
|
||||||
@ -400,24 +487,24 @@ class BulletinGeneratorBUTCourt(BulletinGeneratorStandard):
|
|||||||
|
|
||||||
def boite_decisions_jury(self):
|
def boite_decisions_jury(self):
|
||||||
"""La boite en bas à droite avec jury"""
|
"""La boite en bas à droite avec jury"""
|
||||||
txt = f"""ECTS acquis : {self.ects_total}<br/>"""
|
txt = f"""ECTS acquis : {self.ects_total:g}<br/>"""
|
||||||
if self.bul["semestre"]["decision_annee"]:
|
if self.bul["semestre"]["decision_annee"]:
|
||||||
txt += f"""
|
txt += f"""
|
||||||
Jury tenu le {
|
Jury tenu le {
|
||||||
datetime.datetime.fromisoformat(self.bul["semestre"]["decision_annee"]["date"]).strftime("%d/%m/%Y à %H:%M")
|
datetime.datetime.fromisoformat(self.bul["semestre"]["decision_annee"]["date"]).strftime("%d/%m/%Y à %H:%M")
|
||||||
}, année BUT {self.bul["semestre"]["decision_annee"]["code"]}.
|
}, année BUT <b>{self.bul["semestre"]["decision_annee"]["code"]}</b>.
|
||||||
<br/>
|
<br/>
|
||||||
"""
|
"""
|
||||||
if self.bul["semestre"]["autorisation_inscription"]:
|
if self.bul["semestre"]["autorisation_inscription"]:
|
||||||
txt += (
|
txt += (
|
||||||
"Autorisé à s'inscrire en "
|
"Autorisé à s'inscrire en <b>"
|
||||||
+ ", ".join(
|
+ ", ".join(
|
||||||
[
|
[
|
||||||
f"S{aut['semestre_id']}"
|
f"S{aut['semestre_id']}"
|
||||||
for aut in self.bul["semestre"]["autorisation_inscription"]
|
for aut in self.bul["semestre"]["autorisation_inscription"]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
+ "."
|
+ "</b>."
|
||||||
)
|
)
|
||||||
|
|
||||||
return Paragraph(txt)
|
return Paragraph(txt, style=self.style_jury)
|
||||||
|
@ -113,10 +113,10 @@ class BulletinGenerator:
|
|||||||
self.diagnostic = None # error message if any problem
|
self.diagnostic = None # error message if any problem
|
||||||
# Common PDF styles:
|
# Common PDF styles:
|
||||||
# - Pour tous les champs du bulletin sauf les cellules de table:
|
# - Pour tous les champs du bulletin sauf les cellules de table:
|
||||||
self.FieldStyle = reportlab.lib.styles.ParagraphStyle({})
|
self.style_field = reportlab.lib.styles.ParagraphStyle({})
|
||||||
self.FieldStyle.fontName = self.preferences["SCOLAR_FONT_BUL_FIELDS"]
|
self.style_field.fontName = self.preferences["SCOLAR_FONT_BUL_FIELDS"]
|
||||||
self.FieldStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"]
|
self.style_field.fontSize = self.preferences["SCOLAR_FONT_SIZE"]
|
||||||
self.FieldStyle.firstLineIndent = 0
|
self.style_field.firstLineIndent = 0
|
||||||
# - Pour les cellules de table:
|
# - Pour les cellules de table:
|
||||||
self.CellStyle = reportlab.lib.styles.ParagraphStyle({})
|
self.CellStyle = reportlab.lib.styles.ParagraphStyle({})
|
||||||
self.CellStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"]
|
self.CellStyle.fontSize = self.preferences["SCOLAR_FONT_SIZE"]
|
||||||
|
@ -62,7 +62,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
|
|||||||
Renvoie une liste d'objets platypus
|
Renvoie une liste d'objets platypus
|
||||||
"""
|
"""
|
||||||
objects = sco_bulletins_pdf.process_field(
|
objects = sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_title"], self.infos, self.FieldStyle
|
self.preferences["bul_pdf_title"], self.infos, self.style_field
|
||||||
)
|
)
|
||||||
objects.append(
|
objects.append(
|
||||||
Spacer(1, 5 * mm)
|
Spacer(1, 5 * mm)
|
||||||
@ -301,7 +301,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
|
|||||||
objects += sco_bulletins_pdf.process_field(
|
objects += sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_caption"],
|
self.preferences["bul_pdf_caption"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
)
|
)
|
||||||
|
|
||||||
return objects
|
return objects
|
||||||
@ -387,7 +387,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
|
|||||||
sco_bulletins_pdf.process_field(
|
sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_sig_left"],
|
self.preferences["bul_pdf_sig_left"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -398,7 +398,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
|
|||||||
sco_bulletins_pdf.process_field(
|
sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_sig_right"],
|
self.preferences["bul_pdf_sig_right"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -142,7 +142,9 @@ class WrapDict(object):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"):
|
def process_field(
|
||||||
|
field, cdict, style, suppress_empty_pars=False, fmt="pdf", field_name=None
|
||||||
|
):
|
||||||
"""Process a field given in preferences, returns
|
"""Process a field given in preferences, returns
|
||||||
- if format = 'pdf': a list of Platypus objects
|
- if format = 'pdf': a list of Platypus objects
|
||||||
- if format = 'html' : a string
|
- if format = 'html' : a string
|
||||||
@ -178,7 +180,7 @@ def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"):
|
|||||||
# ne sera pas visible si lien vers pdf:
|
# ne sera pas visible si lien vers pdf:
|
||||||
scu.flash_once(f"Attention: format PDF invalide (champs {field})")
|
scu.flash_once(f"Attention: format PDF invalide (champs {field})")
|
||||||
text = (
|
text = (
|
||||||
"<para><i>format invalide !</i></para><para>"
|
"<para><i>format invalide ! (1)</i></para><para>"
|
||||||
+ traceback.format_exc()
|
+ traceback.format_exc()
|
||||||
+ "</para>"
|
+ "</para>"
|
||||||
)
|
)
|
||||||
@ -204,7 +206,9 @@ def process_field(field, cdict, style, suppress_empty_pars=False, fmt="pdf"):
|
|||||||
# secure_filename dans la classe Logo
|
# secure_filename dans la classe Logo
|
||||||
|
|
||||||
# log('field: %s' % (text))
|
# log('field: %s' % (text))
|
||||||
return sco_pdf.make_paras(text, style, suppress_empty=suppress_empty_pars)
|
return sco_pdf.make_paras(
|
||||||
|
text, style, suppress_empty=suppress_empty_pars, field_name=field_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
|
def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
|
||||||
|
@ -81,12 +81,15 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
description = "standard ScoDoc (version 2011)"
|
description = "standard ScoDoc (version 2011)"
|
||||||
supported_formats = ["html", "pdf"]
|
supported_formats = ["html", "pdf"]
|
||||||
|
|
||||||
def bul_title_pdf(self) -> list:
|
def bul_title_pdf(self, preference_field="bul_pdf_title") -> list:
|
||||||
"""Génère la partie "titre" du bulletin de notes.
|
"""Génère la partie "titre" du bulletin de notes.
|
||||||
Renvoie une liste d'objets platypus
|
Renvoie une liste d'objets platypus
|
||||||
"""
|
"""
|
||||||
objects = sco_bulletins_pdf.process_field(
|
objects = sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_title"], self.infos, self.FieldStyle
|
self.preferences[preference_field],
|
||||||
|
self.infos,
|
||||||
|
self.style_field,
|
||||||
|
field_name=preference_field,
|
||||||
)
|
)
|
||||||
objects.append(
|
objects.append(
|
||||||
Spacer(1, 5 * mm)
|
Spacer(1, 5 * mm)
|
||||||
@ -195,37 +198,26 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
% self.infos
|
% self.infos
|
||||||
)
|
)
|
||||||
H.append("</div>")
|
H.append("</div>")
|
||||||
# Appréciations sur PDF:
|
# ------ Appréciations sur PDF
|
||||||
if appreciations:
|
if appreciations:
|
||||||
story.append(Spacer(1, 3 * mm))
|
story.append(Spacer(1, 3 * mm))
|
||||||
try:
|
story.append(self.bul_appreciations_pdf(appreciations))
|
||||||
story.append(
|
|
||||||
Paragraph(
|
|
||||||
SU(
|
|
||||||
"Appréciation : "
|
|
||||||
+ "\n".join(BulAppreciations.summarize(appreciations))
|
|
||||||
),
|
|
||||||
self.CellStyle,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except AttributeError as exc:
|
|
||||||
raise ScoPDFFormatError(
|
|
||||||
"Appréciation invalide bloquant la génération du pdf"
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
# ----- DECISION JURY
|
# ----- DECISION JURY
|
||||||
if self.preferences["bul_show_decision"]:
|
if self.preferences["bul_show_decision"]:
|
||||||
story += sco_bulletins_pdf.process_field(
|
story += sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_caption"],
|
self.preferences["bul_pdf_caption"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
fmt="pdf",
|
fmt="pdf",
|
||||||
|
field_name="bul_pdf_caption",
|
||||||
)
|
)
|
||||||
field = sco_bulletins_pdf.process_field(
|
field = sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_caption"],
|
self.preferences["bul_pdf_caption"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
fmt="html",
|
fmt="html",
|
||||||
|
field_name="bul_pdf_caption",
|
||||||
)
|
)
|
||||||
H.append('<div class="bul_decision">' + field + "</div>")
|
H.append('<div class="bul_decision">' + field + "</div>")
|
||||||
|
|
||||||
@ -240,6 +232,24 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
elif fmt == "html":
|
elif fmt == "html":
|
||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
def bul_appreciations_pdf(
|
||||||
|
self, appreciations: list[BulAppreciations], style=None
|
||||||
|
) -> Paragraph:
|
||||||
|
"Liste d'objets platypus pour les appréciations sous le bulletin"
|
||||||
|
style = style or self.CellStyle
|
||||||
|
try:
|
||||||
|
return Paragraph(
|
||||||
|
SU(
|
||||||
|
"Appréciation du "
|
||||||
|
+ "\n".join(BulAppreciations.summarize(appreciations))
|
||||||
|
),
|
||||||
|
style,
|
||||||
|
)
|
||||||
|
except AttributeError as exc:
|
||||||
|
raise ScoPDFFormatError(
|
||||||
|
"Appréciation invalide bloquant la génération du pdf"
|
||||||
|
) from exc
|
||||||
|
|
||||||
def bul_signatures_pdf(self):
|
def bul_signatures_pdf(self):
|
||||||
"""Génère les signatures placées en bas du bulletin PDF
|
"""Génère les signatures placées en bas du bulletin PDF
|
||||||
Renvoie une liste d'objets platypus
|
Renvoie une liste d'objets platypus
|
||||||
@ -253,7 +263,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
sco_bulletins_pdf.process_field(
|
sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_sig_left"],
|
self.preferences["bul_pdf_sig_left"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
|
field_name="bul_pdf_sig_left",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -264,7 +275,8 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
sco_bulletins_pdf.process_field(
|
sco_bulletins_pdf.process_field(
|
||||||
self.preferences["bul_pdf_sig_right"],
|
self.preferences["bul_pdf_sig_right"],
|
||||||
self.infos,
|
self.infos,
|
||||||
self.FieldStyle,
|
self.style_field,
|
||||||
|
field_name="bul_pdf_sig_right",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -45,7 +45,7 @@ CONFIG.LOGO_HEADER_HEIGHT = 28
|
|||||||
# server_url: URL du serveur ScoDoc
|
# server_url: URL du serveur ScoDoc
|
||||||
# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir sco_version.py)
|
# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir sco_version.py)
|
||||||
CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = (
|
CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = (
|
||||||
"Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s"
|
"Édité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ from flask import g
|
|||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
from app.scodoc.sco_exceptions import ScoGenError, ScoPDFFormatError, ScoValueError
|
from app.scodoc.sco_exceptions import ScoGenError, ScoPDFFormatError, ScoValueError
|
||||||
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.sco_utils import CONFIG
|
from app.scodoc.sco_utils import CONFIG
|
||||||
import sco_version
|
import sco_version
|
||||||
@ -119,7 +120,9 @@ def _splitPara(txt):
|
|||||||
return L
|
return L
|
||||||
|
|
||||||
|
|
||||||
def make_paras(txt: str, style, suppress_empty=False) -> list[Paragraph]:
|
def make_paras(
|
||||||
|
txt: str, style, suppress_empty=False, field_name=None
|
||||||
|
) -> list[Paragraph]:
|
||||||
"""Returns a list of Paragraph instances from a text
|
"""Returns a list of Paragraph instances from a text
|
||||||
with one or more <para> ... </para>
|
with one or more <para> ... </para>
|
||||||
"""
|
"""
|
||||||
@ -157,9 +160,17 @@ def make_paras(txt: str, style, suppress_empty=False) -> list[Paragraph]:
|
|||||||
log(traceback.format_exc())
|
log(traceback.format_exc())
|
||||||
log(f"Invalid pdf para format: {txt}")
|
log(f"Invalid pdf para format: {txt}")
|
||||||
try:
|
try:
|
||||||
|
# récupère le nom de la préférence
|
||||||
|
if field_name:
|
||||||
|
p = sco_preferences.BasePreferences(g.scodoc_dept_id)
|
||||||
|
pref = p.prefs_dict.get(field_name)
|
||||||
|
if pref:
|
||||||
|
field_name = pref["title"]
|
||||||
result = [
|
result = [
|
||||||
Paragraph(
|
Paragraph(
|
||||||
SU('<font color="red"><i>Erreur: format invalide</i></font>'),
|
SU(
|
||||||
|
f"""<font color="red"><i>Erreur: format invalide (voir préférence {field_name or ""})</i></font>"""
|
||||||
|
),
|
||||||
style,
|
style,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -229,6 +229,16 @@ PREF_CATEGORIES = (
|
|||||||
"related": ("abs", "bul_margins", "bul_mail"),
|
"related": ("abs", "bul_margins", "bul_mail"),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"bul_but_pdf",
|
||||||
|
{
|
||||||
|
"title": "Réglages des bulletins BUT (pdf)",
|
||||||
|
"related": (
|
||||||
|
"bul",
|
||||||
|
"bul_margins",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
# sur page "Mise en page des bulletins"
|
# sur page "Mise en page des bulletins"
|
||||||
(
|
(
|
||||||
"bul_margins",
|
"bul_margins",
|
||||||
@ -257,7 +267,7 @@ PREF_CATEGORIES = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BasePreferences(object):
|
class BasePreferences:
|
||||||
"""Global preferences"""
|
"""Global preferences"""
|
||||||
|
|
||||||
_editor = ndb.EditableTable(
|
_editor = ndb.EditableTable(
|
||||||
@ -1681,6 +1691,30 @@ class BasePreferences(object):
|
|||||||
"category": "bul",
|
"category": "bul",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
# Bulletin court BUT
|
||||||
|
# avec peu de réglages afin conserver la mise en page compacte...
|
||||||
|
(
|
||||||
|
"bul_but_pdf_title",
|
||||||
|
{
|
||||||
|
"initvalue": """
|
||||||
|
<para fontSize="14" align="center">
|
||||||
|
<b>%(UnivName)s</b>
|
||||||
|
</para>
|
||||||
|
<para fontSize="14" align="center" spaceBefore="2mm">
|
||||||
|
<b>%(InstituteName)s</b>
|
||||||
|
</para>
|
||||||
|
<para fontSize="14" align="center" spaceBefore="4mm">
|
||||||
|
<b>Bachelor Universitaire de Technologie</b>
|
||||||
|
</para>
|
||||||
|
""",
|
||||||
|
"title": "Bulletins PDF BUT: paragraphe de titre",
|
||||||
|
"explanation": "(balises interprétées, voir documentation)",
|
||||||
|
"input_type": "textarea",
|
||||||
|
"rows": 10,
|
||||||
|
"cols": 64,
|
||||||
|
"category": "bul_but_pdf",
|
||||||
|
},
|
||||||
|
),
|
||||||
# XXX A COMPLETER, voir sco_formsemestre_edit.py XXX
|
# XXX A COMPLETER, voir sco_formsemestre_edit.py XXX
|
||||||
# bul_mail
|
# bul_mail
|
||||||
(
|
(
|
||||||
|
@ -42,6 +42,13 @@
|
|||||||
format='pdf',
|
format='pdf',
|
||||||
version=version,
|
version=version,
|
||||||
)}}">{{scu.ICON_PDF|safe}}</a>
|
)}}">{{scu.ICON_PDF|safe}}</a>
|
||||||
|
<a style="margin-left: 20px;"
|
||||||
|
href="{{url_for(
|
||||||
|
'notes.bulletin_but_html',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
etudid=etud.id
|
||||||
|
)}}">version courte spéciale BUT</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -39,6 +39,11 @@
|
|||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
|
<p><a href="{{url_for(
|
||||||
|
'notes.bulletin_but_pdf', scodoc_dept=g.scodoc_dept, etudid=etud.id,
|
||||||
|
formsemestre_id=formsemestre.id
|
||||||
|
)}}" class="stdlink">version pdf {{scu.ICON_PDF|safe}}</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="but_bul_court">
|
<div class="but_bul_court">
|
||||||
<div id="infos_etudiant">
|
<div id="infos_etudiant">
|
||||||
|
@ -11,6 +11,7 @@ SCONEWS = """
|
|||||||
|
|
||||||
<li>ScoDoc 9.6 (juillet 2023)</li>
|
<li>ScoDoc 9.6 (juillet 2023)</li>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Nouveaux bulletins BUT compacts</li>
|
||||||
<li>Nouvelle gestion des absences et assiduité</li>
|
<li>Nouvelle gestion des absences et assiduité</li>
|
||||||
<li>Mise à jour logiciels: Debian 12, Python 3.11, ...</li>
|
<li>Mise à jour logiciels: Debian 12, Python 3.11, ...</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -64,7 +64,7 @@ CONFIG.LOGO_HEADER_HEIGHT = 28 # taille verticale dans le document en millimetr
|
|||||||
# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir sco_version.py)
|
# scodoc_name: le nom du logiciel (ScoDoc actuellement, voir sco_version.py)
|
||||||
|
|
||||||
CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = (
|
CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE = (
|
||||||
"Edité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s"
|
"Édité par %(scodoc_name)s le %(day)s/%(month)s/%(year)s à %(hour)sh%(minute)s"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user