diff --git a/app/comp/res_but.py b/app/comp/res_but.py index f3f0c97d..831c0104 100644 --- a/app/comp/res_but.py +++ b/app/comp/res_but.py @@ -17,8 +17,9 @@ from app.comp.bonus_spo import BonusSport from app.models import ScoDocSiteConfig from app.models.moduleimpls import ModuleImpl from app.models.ues import DispenseUE, UniteEns -from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc import sco_preferences +from app.scodoc.sco_codes_parcours import UE_SPORT +from app.scodoc.sco_utils import ModuleType class ResultatsSemestreBUT(NotesTableCompat): @@ -185,9 +186,15 @@ class ResultatsSemestreBUT(NotesTableCompat): modimpls = [ modimpl for modimpl in self.formsemestre.modimpls_sorted - if modimpl.module.ue.type != UE_SPORT - and (coefs[modimpl.id][ue.id] != 0) - and self.modimpl_inscr_df[modimpl.id][etudid] + if ( + modimpl.module.ue.type != UE_SPORT + and (coefs[modimpl.id][ue.id] != 0) + and self.modimpl_inscr_df[modimpl.id][etudid] + ) + or ( + modimpl.module.module_type == ModuleType.MALUS + and modimpl.module.ue_id == ue.id + ) ] if not with_bonus: return [ diff --git a/app/comp/res_common.py b/app/comp/res_common.py index e8b1c893..bbb14ca3 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -667,7 +667,7 @@ class ResultatsSemestre(ResultatsCache): "ues_validables", "UEs", ue_valid_txt_html, - "col_ues_validables", + group="col_ues_validables", classes=classes, raw_content=ue_valid_txt, data={"order": row.nb_ues_validables}, # tri @@ -719,7 +719,7 @@ class ResultatsSemestre(ResultatsCache): ue.acronyme, table.fmt_note(val), group=f"col_ue_{ue.id}", - classes=["col_ue", note_class], + classes=["col_ue", "col_moy_ue", note_class], ) row.table.foot_title_row.cells[col_id].target_attrs[ "title" @@ -751,7 +751,11 @@ class ResultatsSemestre(ResultatsCache): col_id = f"moy_{modimpl.module.type_abbrv()}_{modimpl.id}_{ue.id}" val_fmt = val_fmt_html = table.fmt_note(val) if modimpl.module.module_type == scu.ModuleType.MALUS: - val_fmt_html = (scu.EMO_RED_TRIANGLE_DOWN + val_fmt) if val else "" + val_fmt_html = ( + (scu.EMO_RED_TRIANGLE_DOWN + val_fmt) + if val and not np.isnan(val) + else "" + ) cell = row.add_cell( col_id, modimpl.module.code, @@ -992,22 +996,7 @@ class ResultatsSemestre(ResultatsCache): ) first_partition = True for partition in partitions: - col_classes = [] # la classe "partition" sera ajoutée par la table - if not first_partition: - col_classes.append("partition_aux") - first_partition = False cid = f"part_{partition['partition_id']}" - cell_head, cell_foot = table.add_title(cid, partition["partition_name"]) - cell_head.classes += col_classes - cell_foot.classes += col_classes - - if partition["bul_show_rank"]: - rg_cid = cid + "_rg" # rang dans la partition - cell_head, cell_foot = table.add_title( - cid, f"Rg {partition['partition_name']}" - ) - cell_head.classes.append("partition_rangs") - cell_foot.classes.append("partition_rangs") partition_etud_groups = partitions_etud_groups[partition["partition_id"]] for row in table.rows: @@ -1030,9 +1019,12 @@ class ResultatsSemestre(ResultatsCache): cid, partition["partition_name"], gr_name, - "partition", - classes=col_classes, + group="partition", + classes=[] if first_partition else ["partition_aux"], + # la classe "partition" est ajoutée par la Table car c'est le group + # la classe "partition_aux" est ajoutée à partir de la 2eme partition affichée ) + first_partition = False # Rangs dans groupe if ( @@ -1041,7 +1033,14 @@ class ResultatsSemestre(ResultatsCache): and (group["id"] in self.moy_gen_rangs_by_group) ): rang = self.moy_gen_rangs_by_group[group["id"]][0] - row.add_cell(rg_cid, None, rang.get(etudid, ""), "partition") + rg_cid = cid + "_rg" # rang dans la partition + row.add_cell( + rg_cid, + f"Rg {partition['partition_name']}", + rang.get(etudid, ""), + group="partition", + classes=["partition_aux"], + ) def _recap_add_evaluations(self, table: tb.Table): """Ajoute les colonnes avec les notes aux évaluations diff --git a/app/models/notes.py b/app/models/notes.py index 8d152a58..74bf2f18 100644 --- a/app/models/notes.py +++ b/app/models/notes.py @@ -4,8 +4,6 @@ """ from app import db - -import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu @@ -53,6 +51,13 @@ class NotesNotes(db.Model): d.pop("_sa_instance_state", None) return d + def __repr__(self): + "pour debug" + from app.models.evaluations import Evaluation + + return f"""<{self.__class__.__name__} {self.id} v={self.value} {self.date.isoformat() + } {Evaluation.query.get(self.evaluation_id) if self.evaluation_id else "X" }>""" + class NotesNotesLog(db.Model): """Historique des modifs sur notes (anciennes entrees de notes_notes)""" diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index a3959006..5902dbf7 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -1223,6 +1223,7 @@ def partition_move(partition_id, after=0, redirect=1): partition["numero"], neigh["numero"] = neigh["numero"], partition["numero"] partitionEditor.edit(cnx, partition) partitionEditor.edit(cnx, neigh) + sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id) # redirect to partition edit page: if redirect: @@ -1297,7 +1298,7 @@ def partition_set_name(partition_id, partition_name, redirect=1): ) if len(r) > 1 or (len(r) == 1 and r[0]["id"] != partition_id): raise ScoValueError( - "Partition %s déjà existante dans ce semestre !" % partition_name + f"Partition {partition_name} déjà existante dans ce semestre !" ) if not sco_permissions_check.can_change_groups(formsemestre_id): @@ -1307,6 +1308,7 @@ def partition_set_name(partition_id, partition_name, redirect=1): partitionEditor.edit( cnx, {"partition_id": partition_id, "partition_name": partition_name} ) + sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id) # redirect to partition edit page: if redirect: @@ -1339,6 +1341,7 @@ def group_set_name(group: GroupDescr, group_name: str, redirect=True): group.group_name = group_name db.session.add(group) db.session.commit() + sco_cache.invalidate_formsemestre(formsemestre_id=group.partition.formsemestre_id) # redirect to partition edit page: if redirect: @@ -1396,8 +1399,6 @@ def groups_auto_repartition(partition_id=None): """Reparti les etudiants dans des groupes dans une partition, en respectant le niveau et la mixité. """ - from app.scodoc import sco_formsemestre - partition = get_partition(partition_id) if not partition["groups_editable"]: raise AccessDenied("Partition non éditable") diff --git a/app/scodoc/table_builder.py b/app/scodoc/table_builder.py index 8ddd70c8..d7fc27e5 100644 --- a/app/scodoc/table_builder.py +++ b/app/scodoc/table_builder.py @@ -227,7 +227,11 @@ class Table(Element): if col_id not in self.titles: self.titles[col_id] = title self.head_title_row.cells[col_id] = self.head_title_row.add_cell( - col_id, None, title, classes=classes + col_id, + None, + title, + classes=classes, + group=self.column_group.get(col_id), ) self.foot_title_row.cells[col_id] = self.foot_title_row.add_cell( col_id, None, title, classes=classes @@ -276,9 +280,14 @@ class Row(Element): group: groupe de colonnes classes is a list of css class names """ + if (classes is None) or (group not in classes): + # ajoute le nom de groupe aux classes + classes = [group or ""] + (classes or []) + else: + classes = classes.copy() cell = Cell( content, - (classes or []) + [group or ""], # ajoute le nom de groupe aux classes + classes, elt=elt or self.cell_elt, attrs=attrs, data=data, @@ -294,7 +303,7 @@ class Row(Element): """Add a cell to the row. Si title est None, il doit avoir été ajouté avec table.add_title(). """ - cell.data["group"] = column_group + cell.data["group"] = column_group or "" self.cells[col_id] = cell if col_id not in self.table.column_ids: self.table.column_ids.append(col_id) @@ -375,7 +384,7 @@ class Cell(Element): if self.elt == "th": self.attrs["scope"] = "row" - self.data = data or {} + self.data = data.copy() if data else {} self.raw_content = raw_content or content self.target = target self.target_attrs = target_attrs or {} diff --git a/app/static/js/table_recap.js b/app/static/js/table_recap.js index 7431ae5a..62080b4b 100644 --- a/app/static/js/table_recap.js +++ b/app/static/js/table_recap.js @@ -1,7 +1,11 @@ // Tableau recap notes $(function () { $(function () { - let hidden_colums = ["etud_codes", "identite_detail", "partition_aux", "partition_rangs", "admission", "col_empty"]; + let hidden_colums = [ + "etud_codes", "identite_detail", + "partition_aux", "partition_rangs", "admission", + "col_empty" + ]; let mode_jury_but_bilan = $('table.table_recap').hasClass("table_jury_but_bilan"); if (mode_jury_but_bilan) { // table bilan décisions: cache les notes @@ -30,6 +34,7 @@ $(function () { // Les colonnes visibles étant mémorisé, il faut initialiser les titres des boutons function update_buttons_labels(dt) { + console.log("update_buttons_labels"); dt.buttons('toggle_ident:name').text(dt.columns(".identite_detail").visible()[0] ? "Nom seul" : "Civ/Nom/Prénom"); dt.buttons('toggle_partitions:name').text(dt.columns(".partition_aux").visible()[0] ? "Cacher les groupes" : "Montrer groupes"); if (!$('table.table_recap').hasClass("table_jury_but")) { @@ -106,7 +111,7 @@ $(function () { $('table.table_recap').hasClass("apc") ? { name: "toggle_res", - text: "Visilité ressources", + text: "Visibilité ressources", action: function (e, dt, node, config) { let visible = dt.columns(".col_res").visible()[0]; dt.columns(".col_res").visible(!visible); @@ -159,6 +164,14 @@ $(function () { } }); } + buttons.push({ + name: "reset_table_display", + text: "Rétablir affichage par défaut", + action: function (e, dt, node, config) { + localStorage.clear(); + location.reload(); + } + }); try { let table = $('table.table_recap').DataTable( { @@ -182,7 +195,7 @@ $(function () { }, { // Elimine les 0 à gauche pour les exports excel et les "copy" - targets: ["col_mod", "col_moy_gen", "col_ue", "col_res", "col_sae", "evaluation", "col_rcue"], + targets: ["col_mod", "col_moy_gen", "col_moy_ue", "col_res", "col_sae", "evaluation", "col_rcue"], render: function (data, type, row) { return type === 'export' ? data.replace(/0(\d\..*)/, '$1') : data; } @@ -194,6 +207,14 @@ $(function () { return type === 'export' ? data.replace(/.*(\d\d\.\d\d)/, '$1').replace(/0(\d\..*)/, '$1') : data; } }, + { + // Elimine emoji warning sur UEs + targets: ["col_ues_validables"], + render: function (data, type, row) { + return type === 'export' ? data.replace(/(\d+\/\d+).*/, '$1') : data; + } + } + ], dom: 'Bfrtip', buttons: [ @@ -235,6 +256,7 @@ $(function () { "order": order_info, } ); + update_buttons_labels(table); } catch (error) { // l'erreur peut etre causee par un ancien storage: localStorage.removeItem(etudids_key); @@ -242,7 +264,6 @@ $(function () { localStorage.removeItem(order_info_key); location.reload(); } - update_buttons_labels(table); }); $('table.table_recap tbody').on('click', 'tr', function () { if ($(this).hasClass('selected')) {