From 2da359ae41b1d1068ff8af9ced5157ff0822eb8e Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 22 Mar 2024 17:39:48 +0100 Subject: [PATCH 01/17] Fix export excel table jury. Closes #868 --- app/models/etudiants.py | 7 +----- app/scodoc/sco_formsemestre_validation.py | 2 -- app/scodoc/sco_recapcomplet.py | 2 +- app/tables/jury_recap.py | 20 ++++++++-------- app/tables/recap.py | 7 +++++- app/tables/table_builder.py | 28 ++++++++++++++++++----- 6 files changed, 40 insertions(+), 26 deletions(-) diff --git a/app/models/etudiants.py b/app/models/etudiants.py index ef470f3e6..d818c8e9f 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -542,8 +542,6 @@ class Identite(models.ScoDocModel): def inscriptions(self) -> list["FormSemestreInscription"]: "Liste des inscriptions à des formsemestres, triée, la plus récente en tête" - from app.models.formsemestre import FormSemestre, FormSemestreInscription - return ( FormSemestreInscription.query.join(FormSemestreInscription.formsemestre) .filter( @@ -569,8 +567,6 @@ class Identite(models.ScoDocModel): (il est rare qu'il y en ai plus d'une, mais c'est possible). Triées par date de début de semestre décroissante (le plus récent en premier). """ - from app.models.formsemestre import FormSemestre, FormSemestreInscription - return ( FormSemestreInscription.query.join(FormSemestreInscription.formsemestre) .filter( @@ -1099,6 +1095,5 @@ class EtudAnnotation(db.Model): return e -from app.models.formsemestre import FormSemestre -from app.models.modules import Module +from app.models.formsemestre import FormSemestre, FormSemestreInscription from app.models.moduleimpls import ModuleImpl, ModuleImplInscription diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 4a5f34b4e..16e8d5299 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -57,14 +57,12 @@ from app.scodoc import html_sco_header from app.scodoc import sco_assiduites from app.scodoc import codes_cursus from app.scodoc import sco_cache -from app.scodoc import sco_edit_ue from app.scodoc import sco_etud from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_cursus from app.scodoc import sco_cursus_dut from app.scodoc.sco_cursus_dut import etud_est_inscrit_ue -from app.scodoc import sco_photos from app.scodoc import sco_preferences from app.scodoc import sco_pv_dict from app.scodoc.sco_permissions import Permission diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index b434c225a..d320f5a7e 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -110,7 +110,7 @@ def formsemestre_recapcomplet( force_publishing=force_publishing, ) - table_html, table, freq_codes_annuels = _formsemestre_recapcomplet_to_html( + table_html, _, freq_codes_annuels = _formsemestre_recapcomplet_to_html( formsemestre, filename=filename, mode_jury=mode_jury, diff --git a/app/tables/jury_recap.py b/app/tables/jury_recap.py index abe11e740..3b890db9e 100644 --- a/app/tables/jury_recap.py +++ b/app/tables/jury_recap.py @@ -8,21 +8,16 @@ """ import collections -import time import numpy as np from flask import g, url_for from app.but import cursus_but from app.but import jury_but -from app.but.jury_but import ( - DecisionsProposeesAnnee, - DecisionsProposeesRCUE, - DecisionsProposeesUE, -) +from app.but.jury_but import DecisionsProposeesRCUE + from app.comp.res_compat import NotesTableCompat from app.models import ApcNiveau, UniteEns from app.models.etudiants import Identite -from app.models.formsemestre import FormSemestre from app.scodoc.codes_cursus import ( BUT_BARRE_RCUE, BUT_RCUE_SUFFISANT, @@ -112,9 +107,11 @@ class TableJury(TableRecap): row.add_cell( "autorisations_inscription", "Passage", - ", ".join("S" + str(i) for i in sorted(autorisations[etud.id])) - if etud.id in autorisations - else "", + ( + ", ".join("S" + str(i) for i in sorted(autorisations[etud.id])) + if etud.id in autorisations + else "" + ), group="jury_code_sem", classes=["recorded_code"], ) @@ -136,6 +133,7 @@ class TableJury(TableRecap): if not self.read_only else "voir"} décisions""", group="col_jury_link", classes=["fontred"] if a_saisir else [], + no_excel=True, target=url_for( "notes.formsemestre_validation_etud_form", scodoc_dept=g.scodoc_dept, @@ -278,6 +276,7 @@ class RowJury(RowRecap): f"
{rcue.ue_1.acronyme}
{rcue.ue_2.acronyme}
", self.table.fmt_note(val), raw_content=val, + raw_title=f"{rcue.ue_1.acronyme}-{rcue.ue_2.acronyme}", group="rcue", classes=[note_class], column_classes={"col_rcue"}, @@ -293,6 +292,7 @@ class RowJury(RowRecap): "empty_code" if not dec_rcue.code_valide else "", ], column_classes={"col_rcue"}, + raw_title=f"{rcue.ue_1.acronyme}-{rcue.ue_2.acronyme}", ) # # --- Les ECTS validés diff --git a/app/tables/recap.py b/app/tables/recap.py index 283fc9bba..9de571581 100644 --- a/app/tables/recap.py +++ b/app/tables/recap.py @@ -613,6 +613,7 @@ class RowRecap(tb.Row): "etudid": etud.id, "nomprenom": etud.nomprenom, }, + no_excel=True, target=url_bulletin, target_attrs={"class": "etudinfo", "id": str(etud.id)}, ) @@ -623,7 +624,11 @@ class RowRecap(tb.Row): _, nbabsjust, nbabs = self.table.res.formsemestre.get_abs_count(self.etud.id) self.add_cell("nbabs", "Abs", f"{nbabs:1.0f}", "abs", raw_content=nbabs) self.add_cell( - "nbabsjust", "Just.", f"{nbabsjust:1.0f}", "abs", raw_content=nbabsjust + "nbabsjust", + "Just.", + f"{nbabsjust:1.0f}", + "abs", + raw_content=nbabsjust, ) def add_moyennes_cols( diff --git a/app/tables/table_builder.py b/app/tables/table_builder.py index d47847044..dc0ef6cd1 100644 --- a/app/tables/table_builder.py +++ b/app/tables/table_builder.py @@ -260,12 +260,18 @@ class Table(Element): self.titles.update(titles) def add_title( - self, col_id, title: str = None, classes: list[str] = None + self, + col_id, + title: str = None, + classes: list[str] = None, + raw_title: str = None, ) -> tuple["Cell", "Cell"]: """Record this title, and create cells for footer and header if they don't already exist. + If specified, raw_title will be used in excel exports. """ title = title or "" + if col_id not in self.titles: self.titles[col_id] = title if self.head_title_row: @@ -275,6 +281,7 @@ class Table(Element): title, classes=classes, group=self.column_group.get(col_id), + raw_content=raw_title or title, ) if self.foot_title_row: self.foot_title_row.cells[col_id] = self.foot_title_row.add_cell( @@ -359,6 +366,7 @@ class Row(Element): data: dict[str, str] = None, elt: str = None, raw_content=None, + raw_title: str | None = None, target_attrs: dict = None, target: str = None, column_classes: set[str] = None, @@ -384,16 +392,22 @@ class Row(Element): target_attrs=target_attrs, ) return self.add_cell_instance( - col_id, cell, column_group=group, title=title, no_excel=no_excel + col_id, + cell, + column_group=group, + title=title, + raw_title=raw_title, + no_excel=no_excel, ) def add_cell_instance( self, col_id: str, cell: "Cell", - column_group: str = None, - title: str = None, + column_group: str | None = None, + title: str | None = None, no_excel: bool = False, + raw_title: str | None = None, ) -> "Cell": """Add a cell to the row. Si title est None, il doit avoir été ajouté avec table.add_title(). @@ -410,7 +424,9 @@ class Row(Element): self.table.column_group[col_id] = column_group if title is not None: - self.table.add_title(col_id, title, classes=cell.classes) + self.table.add_title( + col_id, title, classes=cell.classes, raw_title=raw_title + ) return cell @@ -487,7 +503,7 @@ class Cell(Element): self.attrs["scope"] = "row" self.data = data.copy() if data else {} - self.raw_content = raw_content or content + self.raw_content = content if raw_content is None else raw_content self.target = target self.target_attrs = target_attrs or {} From 3c8b088d5eba9224b61bffeb1e51543d8709e697 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 22 Mar 2024 21:56:52 +0100 Subject: [PATCH 02/17] =?UTF-8?q?Jury=20BUT=20auto:=20avertissement=20si?= =?UTF-8?q?=20semestres=20pairs=20non=20bloqu=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/but/jury_but_validation_auto.py | 16 +++++++--- .../but/formsemestre_validation_auto_but.j2 | 16 +++++++++- app/views/notes.py | 31 +++++++++++++++++-- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/app/but/jury_but_validation_auto.py b/app/but/jury_but_validation_auto.py index 4cfffa581..0208282d9 100644 --- a/app/but/jury_but_validation_auto.py +++ b/app/but/jury_but_validation_auto.py @@ -16,8 +16,8 @@ from app.scodoc.sco_exceptions import ScoValueError def formsemestre_validation_auto_but( - formsemestre: FormSemestre, only_adm: bool = True -) -> int: + formsemestre: FormSemestre, only_adm: bool = True, dry_run=False +) -> tuple[int, list[jury_but.DecisionsProposeesAnnee]]: """Calcul automatique des décisions de jury sur une "année" BUT. - N'enregistre jamais de décisions de l'année scolaire précédente, même @@ -27,16 +27,22 @@ def formsemestre_validation_auto_but( En revanche, si only_adm est faux, on enregistre la première décision proposée par ScoDoc (mode à n'utiliser que pour les tests unitaires vérifiant la saisie des jurys) - Returns: nombre d'étudiants pour lesquels on a enregistré au moins un code. + Returns: + - En mode normal, (nombre d'étudiants pour lesquels on a enregistré au moins un code, []]) + - En mode dry_run, (0, list[DecisionsProposeesAnnee]) """ if not formsemestre.formation.is_apc(): raise ScoValueError("fonction réservée aux formations BUT") nb_etud_modif = 0 + decas = [] with sco_cache.DeferredSemCacheManager(): for etudid in formsemestre.etuds_inscriptions: etud = Identite.get_etud(etudid) deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre) - nb_etud_modif += deca.record_all(only_validantes=only_adm) + if not dry_run: + nb_etud_modif += deca.record_all(only_validantes=only_adm) + else: + decas.append(deca) db.session.commit() ScolarNews.add( @@ -49,4 +55,4 @@ def formsemestre_validation_auto_but( formsemestre_id=formsemestre.id, ), ) - return nb_etud_modif + return nb_etud_modif, decas diff --git a/app/templates/but/formsemestre_validation_auto_but.j2 b/app/templates/but/formsemestre_validation_auto_but.j2 index 72570ae53..5ebd21d72 100644 --- a/app/templates/but/formsemestre_validation_auto_but.j2 +++ b/app/templates/but/formsemestre_validation_auto_but.j2 @@ -12,7 +12,7 @@

Calcul automatique des décisions de jury du BUT