forked from ScoDoc/ScoDoc
Tableau recap: bonus et malus
This commit is contained in:
parent
0d726aa428
commit
795de44c0c
@ -426,47 +426,53 @@ class ResultatsSemestre(ResultatsCache):
|
||||
NO_NOTE = "-" # contenu des cellules sans notes
|
||||
rows = []
|
||||
# column_id : title
|
||||
titles = {
|
||||
"rang": "Rg",
|
||||
# ordre des colonnes de gauche à droite:
|
||||
"_civilite_str_col_order": 2,
|
||||
"_nom_disp_col_order": 3,
|
||||
"_prenom_col_order": 4,
|
||||
"_nom_short_col_order": 5,
|
||||
"_rang_col_order": 6,
|
||||
# les colonnes des groupes sont à la position 10
|
||||
"_ues_validables_col_order": 20,
|
||||
}
|
||||
titles = {}
|
||||
# les titres en footer: les mêmes, mais avec des bulles et liens:
|
||||
titles_bot = {}
|
||||
|
||||
def add_cell(
|
||||
row: dict, col_id: str, title: str, content: str, classes: str = ""
|
||||
row: dict,
|
||||
col_id: str,
|
||||
title: str,
|
||||
content: str,
|
||||
classes: str = "",
|
||||
idx: int = 100,
|
||||
):
|
||||
"Add a row to our table. classes is a list of css class names"
|
||||
row[col_id] = content
|
||||
if classes:
|
||||
row[f"_{col_id}_class"] = classes
|
||||
row[f"_{col_id}_class"] = classes + f" c{idx}"
|
||||
if not col_id in titles:
|
||||
titles[col_id] = title
|
||||
titles[f"_{col_id}_col_order"] = idx
|
||||
if classes:
|
||||
titles[f"_{col_id}_class"] = classes
|
||||
return idx + 1
|
||||
|
||||
etuds_inscriptions = self.formsemestre.etuds_inscriptions
|
||||
ues = self.formsemestre.query_ues(with_sport=True) # avec bonus
|
||||
ues_sans_bonus = [ue for ue in ues if ue.type != UE_SPORT]
|
||||
modimpl_ids = set() # modimpl effectivement présents dans la table
|
||||
for etudid in etuds_inscriptions:
|
||||
idx = 0 # index de la colonne
|
||||
etud = Identite.query.get(etudid)
|
||||
row = {"etudid": etudid}
|
||||
# --- Rang
|
||||
add_cell(row, "rang", "Rg", self.etud_moy_gen_ranks[etudid], "rang")
|
||||
idx = add_cell(
|
||||
row, "rang", "Rg", self.etud_moy_gen_ranks[etudid], "rang", idx
|
||||
)
|
||||
row["_rang_order"] = f"{self.etud_moy_gen_ranks_int[etudid]:05d}"
|
||||
# --- Identité étudiant
|
||||
add_cell(row, "civilite_str", "Civ.", etud.civilite_str, "identite_detail")
|
||||
add_cell(row, "nom_disp", "Nom", etud.nom_disp(), "identite_detail")
|
||||
add_cell(row, "prenom", "Prénom", etud.prenom, "identite_detail")
|
||||
add_cell(row, "nom_short", "Nom", etud.nom_short, "identite_court")
|
||||
idx = add_cell(
|
||||
row, "civilite_str", "Civ.", etud.civilite_str, "identite_detail", idx
|
||||
)
|
||||
idx = add_cell(
|
||||
row, "nom_disp", "Nom", etud.nom_disp(), "identite_detail", idx
|
||||
)
|
||||
idx = add_cell(row, "prenom", "Prénom", etud.prenom, "identite_detail", idx)
|
||||
idx = add_cell(
|
||||
row, "nom_short", "Nom", etud.nom_short, "identite_court", idx
|
||||
)
|
||||
row["_nom_short_target"] = url_for(
|
||||
"notes.formsemestre_bulletinetud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
@ -476,6 +482,8 @@ class ResultatsSemestre(ResultatsCache):
|
||||
row["_nom_short_target_attrs"] = f'class="etudinfo" id="{etudid}"'
|
||||
row["_nom_disp_target"] = row["_nom_short_target"]
|
||||
row["_nom_disp_target_attrs"] = row["_nom_short_target_attrs"]
|
||||
|
||||
idx = 30 # début des colonnes de notes
|
||||
# --- Moyenne générale
|
||||
moy_gen = self.etud_moy_gen.get(etudid, False)
|
||||
note_class = ""
|
||||
@ -483,12 +491,13 @@ class ResultatsSemestre(ResultatsCache):
|
||||
moy_gen = NO_NOTE
|
||||
elif isinstance(moy_gen, float) and moy_gen < barre_moy:
|
||||
note_class = " moy_ue_warning" # en rouge
|
||||
add_cell(
|
||||
idx = add_cell(
|
||||
row,
|
||||
"moy_gen",
|
||||
"Moy",
|
||||
fmt_note(moy_gen),
|
||||
"col_moy_gen" + note_class,
|
||||
idx,
|
||||
)
|
||||
titles_bot["_moy_gen_target_attrs"] = (
|
||||
'title="moyenne indicative"' if self.is_apc else ""
|
||||
@ -510,16 +519,32 @@ class ResultatsSemestre(ResultatsCache):
|
||||
if val < barre_warning_ue:
|
||||
note_class = " moy_ue_warning" # notes très basses
|
||||
nb_ues_warning += 1
|
||||
add_cell(
|
||||
idx = add_cell(
|
||||
row,
|
||||
col_id,
|
||||
ue.acronyme,
|
||||
fmt_note(val),
|
||||
"col_ue" + note_class,
|
||||
idx,
|
||||
)
|
||||
titles_bot[
|
||||
f"_{col_id}_target_attrs"
|
||||
] = f"""title="{ue.titre} S{ue.semestre_idx or '?'}" """
|
||||
# Bonus (sport) dans cette UE ?
|
||||
# Le bonus sport appliqué sur cette UE
|
||||
if (self.bonus_ues is not None) and (ue.id in self.bonus_ues):
|
||||
val = self.bonus_ues[ue.id][etud.id] or ""
|
||||
val_fmt = fmt_note(val)
|
||||
if val:
|
||||
val_fmt = f'<span class="green-arrow-up"></span><span class="sp2l">{val_fmt}</span>'
|
||||
idx = add_cell(
|
||||
row,
|
||||
f"bonus_ue_{ue.id}",
|
||||
f"Bonus {ue.acronyme}",
|
||||
val_fmt,
|
||||
"col_ue_bonus",
|
||||
idx,
|
||||
)
|
||||
# Les moyennes des modules (ou ressources et SAÉs) dans cette UE
|
||||
for modimpl in self.modimpls_in_ue(ue.id, etudid, with_bonus=False):
|
||||
if ue_status["is_capitalized"]:
|
||||
@ -546,13 +571,19 @@ class ResultatsSemestre(ResultatsCache):
|
||||
col_id = (
|
||||
f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}"
|
||||
)
|
||||
add_cell(
|
||||
val_fmt = fmt_note(val)
|
||||
if modimpl.module.module_type == scu.ModuleType.MALUS:
|
||||
val_fmt = (
|
||||
(scu.EMO_RED_TRIANGLE_DOWN + val_fmt) if val else ""
|
||||
)
|
||||
idx = add_cell(
|
||||
row,
|
||||
col_id,
|
||||
modimpl.module.code,
|
||||
fmt_note(val),
|
||||
val_fmt,
|
||||
# class col_res mod_ue_123
|
||||
f"col_{modimpl.module.type_abbrv()} mod_ue_{ue.id}",
|
||||
idx,
|
||||
)
|
||||
titles_bot[f"_{col_id}_target"] = url_for(
|
||||
"notes.moduleimpl_status",
|
||||
@ -571,9 +602,10 @@ class ResultatsSemestre(ResultatsCache):
|
||||
add_cell(
|
||||
row,
|
||||
"ues_validables",
|
||||
"Nb UE",
|
||||
"UEs",
|
||||
ue_valid_txt,
|
||||
"col_ue col_ues_validables",
|
||||
29, # juste avant moy. gen.
|
||||
)
|
||||
if nb_ues_warning:
|
||||
row["_ues_validables_class"] += " moy_ue_warning"
|
||||
@ -581,6 +613,7 @@ class ResultatsSemestre(ResultatsCache):
|
||||
row["_ues_validables_class"] += " moy_inf"
|
||||
row["_ues_validables_order"] = nb_ues_validables # pour tri
|
||||
rows.append(row)
|
||||
|
||||
self._recap_add_partitions(rows, titles)
|
||||
self._recap_add_admissions(rows, titles)
|
||||
# tri par rang croissant
|
||||
@ -589,6 +622,16 @@ class ResultatsSemestre(ResultatsCache):
|
||||
# INFOS POUR FOOTER
|
||||
bottom_infos = self._recap_bottom_infos(ues_sans_bonus, modimpl_ids, fmt_note)
|
||||
|
||||
# Ajoute style "col_empty" aux colonnes de modules vides
|
||||
for col_id in titles:
|
||||
c_class = f"_{col_id}_class"
|
||||
if "col_empty" in bottom_infos["moy"].get(c_class, ""):
|
||||
for row in rows:
|
||||
row[c_class] += " col_empty"
|
||||
titles[c_class] += " col_empty"
|
||||
for row in bottom_infos.values():
|
||||
row[c_class] = row.get(c_class, "") + " col_empty"
|
||||
|
||||
# --- TABLE FOOTER: ECTS, moyennes, min, max...
|
||||
footer_rows = []
|
||||
for (bottom_line, row) in bottom_infos.items():
|
||||
@ -604,7 +647,9 @@ class ResultatsSemestre(ResultatsCache):
|
||||
titles_bot.update(titles)
|
||||
footer_rows.append(titles_bot)
|
||||
column_ids = [title for title in titles if not title.startswith("_")]
|
||||
column_ids.sort(key=lambda col_id: titles.get("_" + col_id + "_col_order", 100))
|
||||
column_ids.sort(
|
||||
key=lambda col_id: titles.get("_" + col_id + "_col_order", 1000)
|
||||
)
|
||||
return (rows, footer_rows, titles, column_ids)
|
||||
|
||||
def _recap_bottom_infos(self, ues, modimpl_ids: set, fmt_note) -> dict:
|
||||
@ -651,7 +696,11 @@ class ResultatsSemestre(ResultatsCache):
|
||||
notes = self.modimpl_notes(modimpl.id, ue.id)
|
||||
row_min[col_id] = fmt_note(np.nanmin(notes))
|
||||
row_max[col_id] = fmt_note(np.nanmax(notes))
|
||||
row_moy[col_id] = fmt_note(np.nanmean(notes))
|
||||
moy = np.nanmean(notes)
|
||||
row_moy[col_id] = fmt_note(moy)
|
||||
if np.isnan(moy):
|
||||
# aucune note dans ce module
|
||||
row_moy[f"_{col_id}_class"] = "col_empty"
|
||||
|
||||
return { # { key : row } avec key = min, max, moy, coef
|
||||
"min": row_min,
|
||||
@ -696,6 +745,14 @@ class ResultatsSemestre(ResultatsCache):
|
||||
"type_admission": "Type Adm.",
|
||||
"classement": "Rg. Adm.",
|
||||
}
|
||||
first = True
|
||||
for i, cid in enumerate(fields):
|
||||
titles[f"_{cid}_col_order"] = 10000 + i # tout à droite
|
||||
if first:
|
||||
titles[f"_{cid}_class"] = "admission admission_first"
|
||||
first = False
|
||||
else:
|
||||
titles[f"_{cid}_class"] = "admission"
|
||||
titles.update(fields)
|
||||
for row in rows:
|
||||
etud = Identite.query.get(row["etudid"])
|
||||
@ -708,8 +765,6 @@ class ResultatsSemestre(ResultatsCache):
|
||||
first = False
|
||||
else:
|
||||
row[f"_{cid}_class"] = "admission"
|
||||
titles[f"_{cid}_class"] = row[f"_{cid}_class"]
|
||||
titles[f"_{cid}_col_order"] = 1000 # à la fin
|
||||
|
||||
def _recap_add_partitions(self, rows: list[dict], titles: dict):
|
||||
"""Ajoute les colonnes indiquant les groupes
|
||||
|
@ -643,7 +643,7 @@ def make_formsemestre_recapcomplet(
|
||||
"recap_row_nbeval",
|
||||
"recap_row_ects",
|
||||
)[ir - nblines + 6]
|
||||
cells = '<tr class="%s sortbottom">' % styl
|
||||
cells = f'<tr class="{styl} sortbottom">'
|
||||
else:
|
||||
el = etudlink % {
|
||||
"formsemestre_id": formsemestre_id,
|
||||
@ -651,14 +651,14 @@ def make_formsemestre_recapcomplet(
|
||||
"name": l[1],
|
||||
}
|
||||
if ir % 2 == 0:
|
||||
cells = '<tr class="recap_row_even" id="etudid%s">' % etudid
|
||||
cells = f'<tr class="recap_row_even" id="etudid{etudid}">'
|
||||
else:
|
||||
cells = '<tr class="recap_row_odd" id="etudid%s">' % etudid
|
||||
cells = f'<tr class="recap_row_odd" id="etudid{etudid}">'
|
||||
ir += 1
|
||||
# XXX nsn = [ x.replace('NA', '-') for x in l[:-2] ]
|
||||
# notes sans le NA:
|
||||
nsn = l[:-2] # copy
|
||||
for i in range(len(nsn)):
|
||||
for i, _ in enumerate(nsn):
|
||||
if nsn[i] == "NA":
|
||||
nsn[i] = "-"
|
||||
try:
|
||||
@ -1041,7 +1041,7 @@ def gen_formsemestre_recapcomplet_html(
|
||||
"",
|
||||
)
|
||||
H = [
|
||||
f"""<div class="table_recap"><table class="table_recap {'apc' if formsemestre.formation.is_apc() else ''}">"""
|
||||
f"""<div class="table_recap"><table class="table_recap {'apc' if formsemestre.formation.is_apc() else 'classic'}">"""
|
||||
]
|
||||
# header
|
||||
H.append(
|
||||
|
@ -933,6 +933,7 @@ ICON_XLS = icontag("xlsicon_img", title="Version tableur")
|
||||
|
||||
# HTML emojis
|
||||
EMO_WARNING = "⚠️" # warning /!\
|
||||
EMO_RED_TRIANGLE_DOWN = "🔻" # red triangle pointed down
|
||||
|
||||
|
||||
def sort_dates(L, reverse=False):
|
||||
|
@ -3568,6 +3568,11 @@ table.table_recap tbody td:hover {
|
||||
text-decoration: dashed underline;
|
||||
}
|
||||
|
||||
/* col moy gen en gras seulement pour les form. classiques */
|
||||
table.table_recap.classic td.col_moy_gen {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.table_recap .identite_court {
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
@ -3637,6 +3642,39 @@ table.table_recap td.col_ues_validables {
|
||||
font-style: normal !important;
|
||||
}
|
||||
|
||||
|
||||
.green-arrow-up {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-bottom: 8px solid rgb(48, 239, 0);
|
||||
}
|
||||
|
||||
table.table_recap td.col_ue_bonus,
|
||||
table.table_recap th.col_ue_bonus {
|
||||
font-size: 80%;
|
||||
font-weight: bold;
|
||||
color: rgb(0, 128, 11);
|
||||
}
|
||||
|
||||
table.table_recap td.col_ue_bonus>span.sp2l {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
table.table_recap td.col_ue_bonus {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.table_recap td.col_malus,
|
||||
table.table_recap th.col_malus {
|
||||
font-size: 80%;
|
||||
font-weight: bold;
|
||||
color: rgb(165, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
table.table_recap tr.ects td {
|
||||
color: rgb(160, 86, 3);
|
||||
font-weight: bold;
|
||||
|
@ -29,15 +29,19 @@ $(function () {
|
||||
action: function (e, dt, node, config) {
|
||||
let visible = dt.columns(".col_res").visible()[0];
|
||||
dt.columns(".col_res").visible(!visible);
|
||||
dt.columns(".col_ue_bonus").visible(!visible);
|
||||
dt.buttons('toggle_res:name').text(visible ? "Montrer les ressources" : "Cacher les ressources");
|
||||
}
|
||||
} : {
|
||||
name: "toggle_mod",
|
||||
text: "Cacher les modules",
|
||||
action: function (e, dt, node, config) {
|
||||
let visible = dt.columns(".col_mod").visible()[0];
|
||||
dt.columns(".col_mod").visible(!visible);
|
||||
let visible = dt.columns(".col_mod:not(.col_empty)").visible()[0];
|
||||
dt.columns(".col_mod:not(.col_empty)").visible(!visible);
|
||||
dt.columns(".col_ue_bonus").visible(!visible);
|
||||
dt.buttons('toggle_mod:name').text(visible ? "Montrer les modules" : "Cacher les modules");
|
||||
visible = dt.columns(".col_empty").visible()[0];
|
||||
dt.buttons('toggle_col_empty:name').text(visible ? "Cacher mod. vides" : "Montrer mod. vides");
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -62,6 +66,15 @@ $(function () {
|
||||
dt.buttons('toggle_admission:name').text(visible ? "Montrer infos admission" : "Cacher infos admission");
|
||||
}
|
||||
})
|
||||
buttons.push({
|
||||
name: "toggle_col_empty",
|
||||
text: "Montrer mod. vides",
|
||||
action: function (e, dt, node, config) {
|
||||
let visible = dt.columns(".col_empty").visible()[0];
|
||||
dt.columns(".col_empty").visible(!visible);
|
||||
dt.buttons('toggle_col_empty:name').text(visible ? "Montrer mod. vides" : "Cacher mod. vides");
|
||||
}
|
||||
})
|
||||
$('table.table_recap').DataTable(
|
||||
{
|
||||
paging: false,
|
||||
@ -77,8 +90,8 @@ $(function () {
|
||||
colReorder: true,
|
||||
"columnDefs": [
|
||||
{
|
||||
// cache le détail de l'identité et les colonnes admission
|
||||
"targets": ["identite_detail", "partition_aux", "admission"],
|
||||
// cache le détail de l'identité, les groupes, les colonnes admission et les vides
|
||||
"targets": ["identite_detail", "partition_aux", "admission", "col_empty"],
|
||||
"visible": false,
|
||||
},
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user