Merge branch 'dev92' of https://scodoc.org/git/ScoDoc/ScoDoc into api

This commit is contained in:
leonard_montalbano 2022-03-11 09:11:26 +01:00
commit 8e36201482
6 changed files with 96 additions and 138 deletions

View File

@ -11,7 +11,7 @@ import datetime
from flask import url_for, g from flask import url_for, g
from app.comp.res_but import ResultatsSemestreBUT from app.comp.res_but import ResultatsSemestreBUT
from app.models import FormSemestre, Identite, formsemestre from app.models import FormSemestre, Identite
from app.scodoc import sco_bulletins, sco_utils as scu from app.scodoc import sco_bulletins, sco_utils as scu
from app.scodoc import sco_bulletins_json from app.scodoc import sco_bulletins_json
from app.scodoc import sco_bulletins_pdf from app.scodoc import sco_bulletins_pdf
@ -171,6 +171,10 @@ class BulletinBUT:
# eval_notes est une pd.Series avec toutes les notes des étudiants inscrits # eval_notes est une pd.Series avec toutes les notes des étudiants inscrits
eval_notes = self.res.modimpls_results[e.moduleimpl_id].evals_notes[e.id] eval_notes = self.res.modimpls_results[e.moduleimpl_id].evals_notes[e.id]
notes_ok = eval_notes.where(eval_notes > scu.NOTES_ABSENCE).dropna() notes_ok = eval_notes.where(eval_notes > scu.NOTES_ABSENCE).dropna()
poids = {
ue.acronyme: self.res.modimpls_evals_poids[e.moduleimpl_id][ue.id][e.id]
for ue in self.res.ues
}
d = { d = {
"id": e.id, "id": e.id,
"description": e.description, "description": e.description,
@ -178,7 +182,7 @@ class BulletinBUT:
"heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None, "heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None,
"heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None, "heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
"coef": fmt_note(e.coefficient), "coef": fmt_note(e.coefficient),
"poids": {p.ue.acronyme: p.poids for p in e.ue_poids}, "poids": poids,
"note": { "note": {
"value": fmt_note( "value": fmt_note(
eval_notes[etud.id], eval_notes[etud.id],
@ -330,11 +334,13 @@ class BulletinBUT:
return d return d
def bulletin_etud_complet(self, etud: Identite) -> dict: def bulletin_etud_complet(self, etud: Identite, version="long") -> dict:
"""Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf """Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf
Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict
""" """
d = self.bulletin_etud(etud, self.res.formsemestre, force_publishing=True) d = self.bulletin_etud(
etud, self.res.formsemestre, version=version, force_publishing=True
)
d["etudid"] = etud.id d["etudid"] = etud.id
d["etud"] = d["etudiant"] d["etud"] = d["etudiant"]
d["etud"]["nomprenom"] = etud.nomprenom d["etud"]["nomprenom"] = etud.nomprenom

View File

@ -6,8 +6,7 @@
"""Génération bulletin BUT au format PDF standard """Génération bulletin BUT au format PDF standard
""" """
import itertools from reportlab.platypus import Paragraph, Spacer
from reportlab.platypus import KeepInFrame, Paragraph, Spacer
from app.scodoc.sco_pdf import blue, cm, mm from app.scodoc.sco_pdf import blue, cm, mm
@ -35,6 +34,9 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
tables_infos = [ tables_infos = [
# ---- TABLE SYNTHESE UES # ---- TABLE SYNTHESE UES
self.but_table_synthese_ues(), self.but_table_synthese_ues(),
]
if self.version != "short":
tables_infos += [
# ---- TABLE RESSOURCES # ---- TABLE RESSOURCES
self.but_table_ressources(), self.but_table_ressources(),
# ---- TABLE SAE # ---- TABLE SAE
@ -82,6 +84,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
"titre": "Unités d'enseignement", "titre": "Unités d'enseignement",
"moyenne": "Note/20", "moyenne": "Note/20",
"coef": "Coef.", "coef": "Coef.",
"_coef_pdf": Paragraph("<para align=right><b><i>Coef.</i></b></para>"),
"_css_row_class": "note_bold", "_css_row_class": "note_bold",
"_pdf_row_markup": ["b"], "_pdf_row_markup": ["b"],
"_pdf_style": [ "_pdf_style": [
@ -97,7 +100,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
], ],
} }
] ]
col_keys = ["titre", "moyenne", "coef"] # noms des colonnes à afficher col_keys = ["titre", "coef", "moyenne"] # noms des colonnes à afficher
for ue_acronym, ue in self.infos["ues"].items(): for ue_acronym, ue in self.infos["ues"].items():
# 1er ligne titre UE # 1er ligne titre UE
moy_ue = ue.get("moyenne") moy_ue = ue.get("moyenne")
@ -120,16 +123,16 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
} }
rows.append(t) rows.append(t)
# 2eme ligne titre UE (bonus/malus/ects) # 2eme ligne titre UE (bonus/malus/ects)
ects_txt = f'ECTS: {ue["ECTS"]["acquis"]:.3g} / {ue["ECTS"]["total"]:.3g}'
t = { t = {
"titre": f"""Bonus: {ue['bonus']} - Malus: { "titre": f"""Bonus: {ue['bonus']} - Malus: {
ue["malus"]}""", ue["malus"]}""",
"moyenne": f"""ECTS: {ue["ECTS"]["acquis"]} / {ue["ECTS"]["total"]}""", "coef": ects_txt,
"_moyenne_colspan": 2, "_coef_pdf": Paragraph(f"""<para align=right>{ects_txt}</para>"""),
"_coef_colspan": 2,
# "_css_row_class": "", # "_css_row_class": "",
# "_pdf_row_markup": [""], # "_pdf_row_markup": [""],
"_pdf_style": [ "_pdf_style": [
("ALIGN", (0, 0), (1, 0), "RIGHT"),
("TEXTCOLOR", (0, 0), (-1, 0), blue),
("BACKGROUND", (0, 0), (-1, 0), title_bg), ("BACKGROUND", (0, 0), (-1, 0), title_bg),
( (
"LINEBELOW", "LINEBELOW",
@ -138,6 +141,14 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
self.PDF_LINEWIDTH, self.PDF_LINEWIDTH,
self.PDF_LINECOLOR, self.PDF_LINECOLOR,
), ),
# cadre autour du bonus/malus
(
"BOX",
(0, 0),
(0, 0),
self.PDF_LINEWIDTH,
(0.7, 0.7, 0.7), # gris clair
),
], ],
} }
rows.append(t) rows.append(t)
@ -192,19 +203,30 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
- pdf_style : commandes table Platypus - pdf_style : commandes table Platypus
- largeurs de colonnes pour PDF - largeurs de colonnes pour PDF
""" """
poids_fontsize = "8"
# UE à utiliser pour les poids (# colonne/UE)
ue_acros = list(self.infos["ues"].keys()) # ['RT1.1', 'RT2.1', 'RT3.1']
# Colonnes à afficher:
col_keys = ["titre"] + ue_acros + ["coef", "moyenne"]
# Largeurs des colonnes:
col_widths = { col_widths = {
"titre": None, "titre": None,
# "poids": None,
"moyenne": 2 * cm, "moyenne": 2 * cm,
"coef": 2 * cm, "coef": 2 * cm,
} }
for ue_acro in ue_acros:
col_widths[ue_acro] = 12 * mm # largeur col. poids
title_bg = tuple(x / 255.0 for x in title_bg) title_bg = tuple(x / 255.0 for x in title_bg)
# elems pour générer table avec gen_table (liste de dicts) # elems pour générer table avec gen_table (liste de dicts)
rows = [
# Ligne de titres # Ligne de titres
{ t = {
"titre": title, "titre": title,
# "_titre_colspan": 1 + len(ue_acros),
"moyenne": "Note/20", "moyenne": "Note/20",
"coef": "Coef.", "coef": "Coef.",
"_coef_pdf": Paragraph("<para align=right><i>Coef.</i></para>"),
"_css_row_class": "note_bold", "_css_row_class": "note_bold",
"_pdf_row_markup": ["b"], "_pdf_row_markup": ["b"],
"_pdf_style": [ "_pdf_style": [
@ -219,13 +241,16 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
), ),
], ],
} }
] for ue_acro in ue_acros:
col_keys = ["titre", "moyenne", "coef"] # noms des colonnes à afficher t[ue_acro] = Paragraph(
f"<para align=right fontSize={poids_fontsize}><i>{ue_acro}</i></para>"
)
rows = [t]
for mod_code, mod in self.infos[mod_type].items(): for mod_code, mod in self.infos[mod_type].items():
# 1er ligne titre module # 1er ligne titre module
t = { t = {
"titre": f"{mod_code} - {mod['titre']}", "titre": f"{mod_code} - {mod['titre']}",
"moyenne": "", # pas de moyenne "_titre_colspan": 2 + len(ue_acros),
"_css_row_class": "note_bold", "_css_row_class": "note_bold",
"_pdf_row_markup": ["b"], "_pdf_row_markup": ["b"],
"_pdf_style": [ "_pdf_style": [
@ -248,7 +273,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
"moyenne": e["note"]["value"], "moyenne": e["note"]["value"],
"coef": e["coef"], "coef": e["coef"],
"_coef_pdf": Paragraph( "_coef_pdf": Paragraph(
f"<para align=right><i>{e['coef']}</i></para>" f"<para align=right fontSize={poids_fontsize}><i>{e['coef']}</i></para>"
), ),
"_pdf_style": [ "_pdf_style": [
( (
@ -260,6 +285,21 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
) )
], ],
} }
col_idx = 1 # 1ere col. poids
for ue_acro in ue_acros:
t[ue_acro] = Paragraph(
f"""<para align=right fontSize={poids_fontsize}><i>{e["poids"].get(ue_acro, "")}</i></para>"""
)
t["_pdf_style"].append(
(
"BOX",
(col_idx, 0),
(col_idx, 0),
self.PDF_LINEWIDTH,
(0.7, 0.7, 0.7), # gris clair
),
)
col_idx += 1
rows.append(t) rows.append(t)
# Global pdf style commands: # Global pdf style commands:
pdf_style = [ pdf_style = [

View File

@ -197,6 +197,7 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
evals_poids, _ = moy_mod.load_evaluations_poids(modimpl.id) evals_poids, _ = moy_mod.load_evaluations_poids(modimpl.id)
etuds_moy_module = mod_results.compute_module_moy(evals_poids) etuds_moy_module = mod_results.compute_module_moy(evals_poids)
modimpls_results[modimpl.id] = mod_results modimpls_results[modimpl.id] = mod_results
modimpls_evals_poids[modimpl.id] = evals_poids
modimpls_notes.append(etuds_moy_module) modimpls_notes.append(etuds_moy_module)
if len(modimpls_notes): if len(modimpls_notes):
cube = notes_sem_assemble_cube(modimpls_notes) cube = notes_sem_assemble_cube(modimpls_notes)

View File

@ -63,12 +63,15 @@ from app.scodoc.sco_pdf import SU
from app import log from app import log
def mark_paras(L, tags): def mark_paras(L, tags) -> list[str]:
"""Put each (string) element of L between <b>""" """Put each (string) element of L between <tag>...</tag>,
for each supplied tag.
Leave non string elements untouched.
"""
for tag in tags: for tag in tags:
b = "<" + tag + ">" start = "<" + tag + ">"
c = "</" + tag.split()[0] + ">" end = "</" + tag.split()[0] + ">"
L = [b + (x or "") + c for x in L] L = [(start + (x or "") + end) if isinstance(x, str) else x for x in L]
return L return L

View File

@ -885,7 +885,7 @@ def do_formsemestre_bulletinetud(
if formsemestre.formation.is_apc(): if formsemestre.formation.is_apc():
etud = Identite.query.get(etudid) etud = Identite.query.get(etudid)
r = bulletin_but.BulletinBUT(formsemestre) r = bulletin_but.BulletinBUT(formsemestre)
I = r.bulletin_etud_complet(etud) I = r.bulletin_etud_complet(etud, version=version)
else: else:
I = formsemestre_bulletinetud_dict(formsemestre.id, etudid) I = formsemestre_bulletinetud_dict(formsemestre.id, etudid)
etud = I["etud"] etud = I["etud"]
@ -980,7 +980,7 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
except KeyError as e: except KeyError as e:
raise ScoValueError( raise ScoValueError(
"format 'Message d'accompagnement' (bul_intro_mail) invalide, revoir les réglages dans les préférences" "format 'Message d'accompagnement' (bul_intro_mail) invalide, revoir les réglages dans les préférences"
) ) from e
else: else:
hea = "" hea = ""
@ -1011,97 +1011,6 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
) )
def _formsemestre_bulletinetud_header_html_old_XXX(
etud: Identite,
formsemestre: FormSemestre,
format=None,
version=None,
):
H = [
html_sco_header.sco_header(
page_title=f"Bulletin de {etud.nomprenom}",
javascripts=[
"js/bulletin.js",
"libjs/d3.v3.min.js",
"js/radar_bulletin.js",
],
cssstyles=["css/radar_bulletin.css"],
),
f"""<table class="bull_head"><tr><td>
<h2><a class="discretelink" href="{
url_for(
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id
)}">{etud.nomprenom}</a></h2>
<form name="f" method="GET" action="{request.base_url}">
Bulletin <span class="bull_liensemestre"><a href="{
url_for("notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre.id)}
">{formsemestre.titre_mois()}</a></span>
<br/>
<table><tr>
<td>établi le {time.strftime("%d/%m/%Y à %Hh%M")} (notes sur 20)</td>
<td><span class="rightjust">
<input type="hidden" name="formsemestre_id" value="{formsemestre.id}"></input>
<input type="hidden" name="etudid" value="{etud.id}"></input>
<input type="hidden" name="format" value="{format}"></input>
<select name="version" onchange="document.f.submit()" class="noprint">
""",
]
for (v, e) in (
("short", "Version courte"),
("selectedevals", "Version intermédiaire"),
("long", "Version complète"),
):
if v == version:
selected = " selected"
else:
selected = ""
H.append('<option value="%s"%s>%s</option>' % (v, selected, e))
H.append("""</select></td>""")
# Menu
endpoint = "notes.formsemestre_bulletinetud"
menu_autres_operations = make_menu_autres_operations(
formsemestre, etud, endpoint, version
)
H.append("""<td class="bulletin_menubar"><div class="bulletin_menubar">""")
H.append(menu_autres_operations)
H.append("""</div></td>""")
H.append(
'<td> <a href="%s">%s</a></td>'
% (
url_for(
"notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre.id,
etudid=etud.id,
format="pdf",
version=version,
),
scu.ICON_PDF,
)
)
H.append("""</tr></table>""")
#
H.append(
"""</form></span></td><td class="bull_photo"><a href="%s">%s</a>
"""
% (
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id),
sco_photos.etud_photo_html(etud, title="fiche de " + etud.nomprenom),
)
)
H.append(
"""</td></tr>
</table>
"""
)
return "".join(H)
def make_menu_autres_operations( def make_menu_autres_operations(
formsemestre: FormSemestre, etud: Identite, endpoint: str, version: str formsemestre: FormSemestre, etud: Identite, endpoint: str, version: str
) -> str: ) -> str:

View File

@ -1885,7 +1885,6 @@ def formsemestre_bulletins_choice(
formsemestre_id, title="", explanation="", choose_mail=False formsemestre_id, title="", explanation="", choose_mail=False
): ):
"""Choix d'une version de bulletin""" """Choix d'une version de bulletin"""
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [ H = [
html_sco_header.html_sem_header(title), html_sco_header.html_sem_header(title),
""" """