544 lines
20 KiB
Python
544 lines
20 KiB
Python
# -*- 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@gmail.com
|
|
#
|
|
##############################################################################
|
|
|
|
"""Generation bulletins de notes dans l'ancien format de ScoDoc (avant juillet 2011).
|
|
|
|
Code partiellement redondant, copié de l'ancien système de gestion des bulletins.
|
|
|
|
Voir sco_bulletins_standard pour une version plus récente.
|
|
|
|
CE FORMAT N'EVOLUERA PLUS ET EST CONSIDERE COMME OBSOLETE.
|
|
|
|
"""
|
|
from reportlab.lib.colors import Color, blue
|
|
from reportlab.lib.units import cm, mm
|
|
from reportlab.platypus import Paragraph, Spacer, Table
|
|
|
|
from app.scodoc import sco_bulletins_generator
|
|
from app.scodoc import sco_bulletins_pdf
|
|
from app.scodoc import sco_formsemestre
|
|
from app.scodoc.sco_permissions import Permission
|
|
from app.scodoc import sco_pdf
|
|
from app.scodoc.sco_pdf import SU
|
|
from app.scodoc import sco_preferences
|
|
import app.scodoc.sco_utils as scu
|
|
|
|
|
|
# Important: Le nom de la classe ne doit pas changer (bien le choisir), car il sera stocké en base de données (dans les préférences)
|
|
class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
|
|
description = "Ancien format ScoDoc" # la description doit être courte: elle apparait dans le menu de paramètrage ScoDoc
|
|
supported_formats = ["html", "pdf"]
|
|
|
|
def bul_title_pdf(self):
|
|
"""Génère la partie "titre" du bulletin de notes.
|
|
Renvoie une liste d'objets platypus
|
|
"""
|
|
objects = sco_bulletins_pdf.process_field(
|
|
self.preferences["bul_pdf_title"], self.infos, self.FieldStyle
|
|
)
|
|
objects.append(
|
|
Spacer(1, 5 * mm)
|
|
) # impose un espace vertical entre le titre et la table qui suit
|
|
return objects
|
|
|
|
def bul_table(self, format="html"):
|
|
"""Table bulletin"""
|
|
if format == "pdf":
|
|
return self.bul_table_pdf()
|
|
elif format == "html":
|
|
return self.bul_table_html()
|
|
else:
|
|
raise ValueError("invalid bulletin format (%s)" % format)
|
|
|
|
def bul_table_pdf(self):
|
|
"""Génère la table centrale du bulletin de notes
|
|
Renvoie une liste d'objets PLATYPUS (eg instance de Table).
|
|
"""
|
|
P, pdfTableStyle, colWidths = _bulletin_pdf_table_legacy(
|
|
self.infos, version=self.version
|
|
)
|
|
return [self.buildTableObject(P, pdfTableStyle, colWidths)]
|
|
|
|
def bul_table_html(self):
|
|
"""Génère la table centrale du bulletin de notes: chaine HTML"""
|
|
I = self.infos
|
|
formsemestre_id = self.infos["formsemestre_id"]
|
|
|
|
bul_show_abs_modules = sco_preferences.get_preference(
|
|
"bul_show_abs_modules", formsemestre_id
|
|
)
|
|
|
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
|
if sem["bul_bgcolor"]:
|
|
bgcolor = sem["bul_bgcolor"]
|
|
else:
|
|
bgcolor = "background-color: rgb(255,255,240)"
|
|
|
|
linktmpl = '<span onclick="toggle_vis_ue(this);" class="toggle_ue">%s</span>'
|
|
minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-")
|
|
pluslink = linktmpl % scu.icontag("plus_img", border="0", alt="+")
|
|
|
|
H = ['<table class="notes_bulletin" style="background-color: %s;">' % bgcolor]
|
|
|
|
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
|
|
minmax = (
|
|
'<span class="bul_minmax" title="[min, max] promo">[%s, %s]</span>'
|
|
% (I["moy_min"], I["moy_max"])
|
|
)
|
|
bargraph = ""
|
|
else:
|
|
minmax = ""
|
|
bargraph = I["moy_gen_bargraph_html"]
|
|
# 1ere ligne: titres
|
|
H.append(
|
|
'<tr><td class="note_bold">Moyenne</td><td class="note_bold cell_graph">%s%s%s%s</td>'
|
|
% (I["moy_gen"], I["etud_etat_html"], minmax, bargraph)
|
|
)
|
|
H.append('<td class="note_bold">%s</td>' % I["rang_txt"])
|
|
H.append('<td class="note_bold">Note/20</td><td class="note_bold">Coef</td>')
|
|
if bul_show_abs_modules:
|
|
H.append('<td class="note_bold">Abs (J. / N.J.)</td>')
|
|
H.append("</tr>")
|
|
|
|
def list_modules(ue_modules, rowstyle):
|
|
for mod in ue_modules:
|
|
if mod["mod_moy_txt"] == "NI":
|
|
continue # saute les modules où on n'est pas inscrit
|
|
H.append('<tr class="notes_bulletin_row_mod%s">' % rowstyle)
|
|
if sco_preferences.get_preference(
|
|
"bul_show_minmax_mod", formsemestre_id
|
|
):
|
|
rang_minmax = (
|
|
'%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>'
|
|
% (
|
|
mod["mod_rang_txt"],
|
|
scu.fmt_note(mod["stats"]["min"]),
|
|
scu.fmt_note(mod["stats"]["max"]),
|
|
)
|
|
)
|
|
else:
|
|
rang_minmax = mod["mod_rang_txt"] # vide si pas option rang
|
|
H.append(
|
|
'<td>%s</td><td>%s</td><td>%s</td><td class="note">%s</td><td>%s</td>'
|
|
% (
|
|
mod["code_html"],
|
|
mod["name_html"],
|
|
rang_minmax,
|
|
mod["mod_moy_txt"],
|
|
mod["mod_coef_txt"],
|
|
)
|
|
)
|
|
if bul_show_abs_modules:
|
|
H.append("<td>%s</td>" % mod["mod_abs_txt"])
|
|
H.append("</tr>")
|
|
|
|
if self.version != "short":
|
|
# --- notes de chaque eval:
|
|
for e in mod["evaluations"]:
|
|
if e["visibulletin"] or self.version == "long":
|
|
H.append(
|
|
'<tr class="notes_bulletin_row_eval%s">' % rowstyle
|
|
)
|
|
H.append(
|
|
'<td>%s</td><td>%s</td><td class="bull_nom_eval">%s</td><td class="note">%s</td><td class="bull_coef_eval">%s</td></tr>'
|
|
% (
|
|
"",
|
|
"",
|
|
e["name_html"],
|
|
e["note_html"],
|
|
e["coef_txt"],
|
|
)
|
|
)
|
|
|
|
# Contenu table: UE après UE
|
|
for ue in I["ues"]:
|
|
ue_descr = ue["ue_descr_html"]
|
|
coef_ue = ue["coef_ue_txt"]
|
|
rowstyle = ""
|
|
plusminus = minuslink #
|
|
if ue["ue_status"]["is_capitalized"]:
|
|
if sco_preferences.get_preference(
|
|
"bul_show_ue_cap_details", formsemestre_id
|
|
):
|
|
plusminus = minuslink
|
|
hide = ""
|
|
else:
|
|
plusminus = pluslink
|
|
hide = "sco_hide"
|
|
H.append('<tr class="notes_bulletin_row_ue">')
|
|
H.append(
|
|
'<td class="note_bold">%s%s</td><td class="note_bold">%s</td><td>%s</td><td>%s</td><td>%s</td>'
|
|
% (
|
|
plusminus,
|
|
ue["acronyme"],
|
|
ue["moy_ue_txt"],
|
|
ue_descr,
|
|
"",
|
|
coef_ue,
|
|
)
|
|
)
|
|
if bul_show_abs_modules:
|
|
H.append("<td></td>")
|
|
H.append("</tr>")
|
|
list_modules(ue["modules_capitalized"], " bul_row_ue_cap %s" % hide)
|
|
|
|
coef_ue = ""
|
|
ue_descr = "(en cours, non prise en compte)"
|
|
rowstyle = (
|
|
" bul_row_ue_cur" # style css pour indiquer UE non prise en compte
|
|
)
|
|
|
|
H.append('<tr class="notes_bulletin_row_ue">')
|
|
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
|
|
moy_txt = (
|
|
'%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>'
|
|
% (
|
|
scu.fmt_note(ue["cur_moy_ue_txt"]),
|
|
scu.fmt_note(ue["min"]),
|
|
scu.fmt_note(ue["max"]),
|
|
)
|
|
)
|
|
else:
|
|
moy_txt = ue["cur_moy_ue_txt"]
|
|
|
|
H.append(
|
|
'<td class="note_bold">%s%s</td><td class="note_bold">%s</td><td>%s</td><td>%s</td><td>%s</td>'
|
|
% (minuslink, ue["acronyme"], moy_txt, ue_descr, "", coef_ue)
|
|
)
|
|
if bul_show_abs_modules:
|
|
H.append("<td></td>")
|
|
H.append("</tr>")
|
|
list_modules(ue["modules"], rowstyle)
|
|
|
|
H.append("</table>")
|
|
|
|
# ---------------
|
|
return "\n".join(H)
|
|
|
|
def bul_part_below(self, format="html"):
|
|
"""Génère les informations placées sous la table de notes
|
|
(absences, appréciations, décisions de jury...)
|
|
"""
|
|
if format == "pdf":
|
|
return self.bul_part_below_pdf()
|
|
elif format == "html":
|
|
return self.bul_part_below_html()
|
|
else:
|
|
raise ValueError("invalid bulletin format (%s)" % format)
|
|
|
|
def bul_part_below_pdf(self):
|
|
"""
|
|
Renvoie une liste d'objets platypus
|
|
"""
|
|
objects = []
|
|
|
|
# ----- ABSENCES
|
|
if self.preferences["bul_show_abs"]:
|
|
nbabs = self.infos["nbabs"]
|
|
nbabsjust = self.infos["nbabsjust"]
|
|
objects.append(Spacer(1, 2 * mm))
|
|
if nbabs:
|
|
objects.append(
|
|
Paragraph(
|
|
SU(
|
|
"%d absences (1/2 journées), dont %d justifiées."
|
|
% (nbabs, nbabsjust)
|
|
),
|
|
self.CellStyle,
|
|
)
|
|
)
|
|
else:
|
|
objects.append(
|
|
Paragraph(SU("Pas d'absences signalées."), self.CellStyle)
|
|
)
|
|
|
|
# ----- APPRECIATIONS
|
|
if self.infos.get("appreciations_list", False):
|
|
objects.append(Spacer(1, 3 * mm))
|
|
objects.append(
|
|
Paragraph(
|
|
SU("Appréciation : " + "\n".join(self.infos["appreciations_txt"])),
|
|
self.CellStyle,
|
|
)
|
|
)
|
|
|
|
# ----- DECISION JURY
|
|
if self.preferences["bul_show_decision"]:
|
|
objects += sco_bulletins_pdf.process_field(
|
|
self.preferences["bul_pdf_caption"],
|
|
self.infos,
|
|
self.FieldStyle,
|
|
)
|
|
|
|
return objects
|
|
|
|
def bul_part_below_html(self):
|
|
"""
|
|
Renvoie chaine HTML
|
|
"""
|
|
I = self.infos
|
|
authuser = self.authuser
|
|
H = []
|
|
# --- Absences
|
|
# XXX TODO-ASSIDUITE
|
|
# au passage, utiliser url_for...
|
|
H.append(
|
|
"""<p>
|
|
XXX <a href="../Absences/CalAbs?etudid=%(etudid)s" class="bull_link">
|
|
<b>Absences :</b> %(nbabs)s demi-journées, dont %(nbabsjust)s justifiées
|
|
(pendant ce semestre).
|
|
</a></p>
|
|
"""
|
|
% I
|
|
)
|
|
# --- Decision Jury
|
|
if I["situation"]:
|
|
H.append("""<p class="bull_situation">%(situation)s</p>""" % I)
|
|
# --- Appreciations
|
|
# le dir. des etud peut ajouter des appreciations,
|
|
# mais aussi le chef (perm. ScoEtudInscrit)
|
|
can_edit_app = (authuser.id in self.infos["responsables"]) or (
|
|
authuser.has_permission(Permission.ScoEtudInscrit)
|
|
)
|
|
H.append('<div class="bull_appreciations">')
|
|
if I["appreciations_list"]:
|
|
H.append("<p><b>Appréciations</b></p>")
|
|
for app in I["appreciations_list"]:
|
|
if can_edit_app:
|
|
mlink = (
|
|
'<a class="stdlink" href="appreciation_add_form?id=%s">modifier</a> <a class="stdlink" href="appreciation_add_form?id=%s&suppress=1">supprimer</a>'
|
|
% (app["id"], app["id"])
|
|
)
|
|
else:
|
|
mlink = ""
|
|
H.append(
|
|
'<p><span class="bull_appreciations_date">%s</span>%s<span class="bull_appreciations_link">%s</span></p>'
|
|
% (app["date"], app["comment"], mlink)
|
|
)
|
|
if can_edit_app:
|
|
H.append(
|
|
'<p><a class="stdlink" href="appreciation_add_form?etudid=%(etudid)s&formsemestre_id=%(formsemestre_id)s">Ajouter une appréciation</a></p>'
|
|
% self.infos
|
|
)
|
|
H.append("</div>")
|
|
# ---------------
|
|
return "\n".join(H)
|
|
|
|
def bul_signatures_pdf(self):
|
|
"""Génère les signatures placées en bas du bulletin
|
|
Renvoie une liste d'objets platypus
|
|
"""
|
|
show_left = self.preferences["bul_show_sig_left"]
|
|
show_right = self.preferences["bul_show_sig_right"]
|
|
if show_left or show_right:
|
|
if show_left:
|
|
L = [
|
|
[
|
|
sco_bulletins_pdf.process_field(
|
|
self.preferences["bul_pdf_sig_left"],
|
|
self.infos,
|
|
self.FieldStyle,
|
|
)
|
|
]
|
|
]
|
|
else:
|
|
L = [[""]]
|
|
if show_right:
|
|
L[0].append(
|
|
sco_bulletins_pdf.process_field(
|
|
self.preferences["bul_pdf_sig_right"],
|
|
self.infos,
|
|
self.FieldStyle,
|
|
)
|
|
)
|
|
else:
|
|
L[0].append("")
|
|
t = Table(L)
|
|
t._argW[0] = 10 * cm # fixe largeur colonne gauche
|
|
|
|
return [Spacer(1, 1.5 * cm), t] # espace vertical avant signatures
|
|
else:
|
|
return []
|
|
|
|
|
|
# sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy)
|
|
|
|
|
|
class BulTableStyle(object):
|
|
"""Construction du style de tables reportlab platypus pour les bulletins "classiques" """
|
|
|
|
LINEWIDTH = 0.5
|
|
LINECOLOR = Color(0, 0, 0)
|
|
UEBGCOLOR = Color(
|
|
170 / 255.0, 187 / 255.0, 204 / 255.0
|
|
) # couleur fond lignes titres UE
|
|
MODSEPCOLOR = Color(
|
|
170 / 255.0, 170 / 255.0, 170 / 255.0
|
|
) # lignes séparant les modules
|
|
|
|
def __init__(self):
|
|
self.pdfTableStyle = [
|
|
("LINEBELOW", (0, 0), (-1, 0), self.LINEWIDTH, self.LINECOLOR)
|
|
]
|
|
self.tabline = 0
|
|
|
|
def get_style(self):
|
|
"get resulting style (a list of platypus table commands)"
|
|
# ajoute cadre extérieur bleu:
|
|
self.pdfTableStyle.append(("BOX", (0, 0), (-1, -1), 0.4, blue))
|
|
|
|
return self.pdfTableStyle
|
|
|
|
def newline(self, ue_type=None):
|
|
self.tabline += 1
|
|
if ue_type == "cur": # UE courante non prise en compte (car capitalisee)
|
|
self.pdfTableStyle.append(
|
|
(
|
|
"BACKGROUND",
|
|
(0, self.tabline),
|
|
(-1, self.tabline),
|
|
Color(210 / 255.0, 210 / 255.0, 210 / 255.0),
|
|
)
|
|
)
|
|
|
|
def ueline(self): # met la ligne courante du tableau pdf en style 'UE'
|
|
self.newline()
|
|
i = self.tabline
|
|
self.pdfTableStyle.append(("BACKGROUND", (0, i), (-1, i), self.UEBGCOLOR))
|
|
|
|
def modline(
|
|
self, ue_type=None
|
|
): # met la ligne courante du tableau pdf en style 'Module'
|
|
self.newline(ue_type=ue_type)
|
|
i = self.tabline
|
|
self.pdfTableStyle.append(("LINEABOVE", (0, i), (-1, i), 1, self.MODSEPCOLOR))
|
|
|
|
|
|
def _bulletin_pdf_table_legacy(I, version="long"):
|
|
"""Génère la table centrale du bulletin de notes
|
|
Renvoie un triplet:
|
|
- table (liste de listes de chaines de caracteres)
|
|
- style (commandes table Platypus)
|
|
- largeurs de colonnes
|
|
"""
|
|
S = BulTableStyle()
|
|
P = [] # elems pour gen. pdf
|
|
formsemestre_id = I["formsemestre_id"]
|
|
bul_show_abs_modules = sco_preferences.get_preference(
|
|
"bul_show_abs_modules", formsemestre_id
|
|
)
|
|
|
|
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
|
|
minmax = ' <font size="8">[%s, %s]</font>' % (I["moy_min"], I["moy_max"])
|
|
else:
|
|
minmax = ""
|
|
|
|
t = [
|
|
"Moyenne",
|
|
"%s%s%s" % (I["moy_gen"], I["etud_etat_html"], minmax),
|
|
I["rang_txt"],
|
|
"Note/20",
|
|
"Coef",
|
|
]
|
|
if bul_show_abs_modules:
|
|
t.append("Abs (J. / N.J.)")
|
|
P.append(sco_pdf.bold_paras(t))
|
|
|
|
def list_modules(ue_modules, ue_type=None):
|
|
"ajoute les lignes decrivant les modules d'une UE, avec eventuellement les évaluations de chacun"
|
|
for mod in ue_modules:
|
|
if mod["mod_moy_txt"] == "NI":
|
|
continue # saute les modules où on n'est pas inscrit
|
|
S.modline(ue_type=ue_type)
|
|
if sco_preferences.get_preference("bul_show_minmax_mod", formsemestre_id):
|
|
rang_minmax = '%s <font size="8">[%s, %s]</font>' % (
|
|
mod["mod_rang_txt"],
|
|
scu.fmt_note(mod["stats"]["min"]),
|
|
scu.fmt_note(mod["stats"]["max"]),
|
|
)
|
|
else:
|
|
rang_minmax = mod["mod_rang_txt"] # vide si pas option rang
|
|
t = [
|
|
mod["code"] or "",
|
|
mod["name"] or "",
|
|
rang_minmax,
|
|
mod["mod_moy_txt"],
|
|
mod["mod_coef_txt"],
|
|
]
|
|
if bul_show_abs_modules:
|
|
t.append(mod["mod_abs_txt"])
|
|
P.append(t)
|
|
if version != "short":
|
|
# --- notes de chaque eval:
|
|
for e in mod["evaluations"]:
|
|
if e["visibulletin"] or version == "long":
|
|
S.newline(ue_type=ue_type)
|
|
t = ["", "", e["name"], e["note_txt"], e["coef_txt"]]
|
|
if bul_show_abs_modules:
|
|
t.append("")
|
|
P.append(t)
|
|
|
|
for ue in I["ues"]:
|
|
ue_descr = ue["ue_descr_txt"]
|
|
coef_ue = ue["coef_ue_txt"]
|
|
ue_type = None
|
|
if ue["ue_status"]["is_capitalized"]:
|
|
t = [ue["acronyme"], ue["moy_ue_txt"], ue_descr, "", coef_ue]
|
|
if bul_show_abs_modules:
|
|
t.append("")
|
|
P.append(sco_pdf.bold_paras(t))
|
|
coef_ue = ""
|
|
ue_descr = "(en cours, non prise en compte)"
|
|
S.ueline()
|
|
if sco_preferences.get_preference(
|
|
"bul_show_ue_cap_details", formsemestre_id
|
|
):
|
|
list_modules(ue["modules_capitalized"])
|
|
ue_type = "cur"
|
|
|
|
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
|
|
moy_txt = '%s <font size="8">[%s, %s]</font>' % (
|
|
ue["cur_moy_ue_txt"],
|
|
ue["min"],
|
|
ue["max"],
|
|
)
|
|
else:
|
|
moy_txt = ue["cur_moy_ue_txt"]
|
|
t = [ue["acronyme"], moy_txt, ue_descr, "", coef_ue]
|
|
if bul_show_abs_modules:
|
|
t.append("")
|
|
P.append(sco_pdf.bold_paras(t))
|
|
S.ueline()
|
|
list_modules(ue["modules"], ue_type=ue_type)
|
|
|
|
# Largeur colonnes:
|
|
colWidths = [None, 5 * cm, 6 * cm, 2 * cm, 1.2 * cm]
|
|
if len(P[0]) > 5:
|
|
colWidths.append(1.5 * cm) # absences/modules
|
|
|
|
return P, S.get_style(), colWidths
|