# -*- 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 = '%s'
minuslink = linktmpl % scu.icontag("minus_img", border="0", alt="-")
pluslink = linktmpl % scu.icontag("plus_img", border="0", alt="+")
H = ['
' % bgcolor]
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
minmax = (
'[%s, %s]'
% (I["moy_min"], I["moy_max"])
)
bargraph = ""
else:
minmax = ""
bargraph = I["moy_gen_bargraph_html"]
# 1ere ligne: titres
H.append(
'Moyenne | %s%s%s%s | '
% (I["moy_gen"], I["etud_etat_html"], minmax, bargraph)
)
H.append('%s | ' % I["rang_txt"])
H.append('Note/20 | Coef | ')
if bul_show_abs_modules:
H.append('Abs (J. / N.J.) | ')
H.append("
")
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('' % rowstyle)
if sco_preferences.get_preference(
"bul_show_minmax_mod", formsemestre_id
):
rang_minmax = '%s [%s, %s]' % (
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(
'%s | %s | %s | %s | %s | '
% (
mod["code_html"],
mod["name_html"],
rang_minmax,
mod["mod_moy_txt"],
mod["mod_coef_txt"],
)
)
if bul_show_abs_modules:
H.append("%s | " % mod["mod_abs_txt"])
H.append("
")
if self.version != "short":
# --- notes de chaque eval:
for e in mod["evaluations"]:
if e["visibulletin"] or self.version == "long":
H.append(
'' % rowstyle
)
H.append(
'%s | %s | %s | %s | %s |
'
% (
"",
"",
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('')
H.append(
'%s%s | %s | %s | %s | %s | '
% (
plusminus,
ue["acronyme"],
ue["moy_ue_txt"],
ue_descr,
"",
coef_ue,
)
)
if bul_show_abs_modules:
H.append(" | ")
H.append("
")
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('')
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
moy_txt = (
'%s [%s, %s]'
% (
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(
'%s%s | %s | %s | %s | %s | '
% (minuslink, ue["acronyme"], moy_txt, ue_descr, "", coef_ue)
)
if bul_show_abs_modules:
H.append(" | ")
H.append("
")
list_modules(ue["modules"], rowstyle)
H.append("
")
# ---------------
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
H.append(
"""
Absences : %(nbabs)s demi-journées, dont %(nbabsjust)s justifiées
(pendant ce semestre).
"""
% I
)
# --- Decision Jury
if I["situation"]:
H.append("""%(situation)s
""" % 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('')
if I["appreciations_list"]:
H.append("
Appréciations
")
for app in I["appreciations_list"]:
if can_edit_app:
mlink = (
'
modifier supprimer'
% (app["id"], app["id"])
)
else:
mlink = ""
H.append(
'
%s%s%s
'
% (app["date"], app["comment"], mlink)
)
if can_edit_app:
H.append(
'
Ajouter une appréciation
'
% self.infos
)
H.append("
")
# ---------------
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 = ' [%s, %s]' % (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 [%s, %s]' % (
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 [%s, %s]' % (
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