From 1309e77bfab61f2b38e93499c2ce0cffc62e58e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 10 Feb 2023 22:04:09 +0100 Subject: [PATCH] Calcul des etuds d'un modimpl avec notes en ATT. Affichage sur tableau bord. Fix tri liste etuds (#595). --- app/comp/moy_mod.py | 20 ++-- app/models/moduleimpls.py | 25 ++++- app/scodoc/sco_exceptions.py | 8 ++ app/scodoc/sco_moduleimpl.py | 2 +- app/scodoc/sco_moduleimpl_inscriptions.py | 23 ++-- app/scodoc/sco_moduleimpl_status.py | 124 +++++++++++++--------- app/static/css/gt_table.css | 5 + app/tables/list_etuds.py | 117 ++++++++++++++++++++ app/tables/recap.py | 2 - app/tables/table_builder.py | 42 +++++--- app/views/notes.py | 29 ++--- sco_version.py | 2 +- 12 files changed, 299 insertions(+), 100 deletions(-) create mode 100644 app/tables/list_etuds.py diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py index 1fccbda158..5856184fcf 100644 --- a/app/comp/moy_mod.py +++ b/app/comp/moy_mod.py @@ -85,6 +85,8 @@ class ModuleImplResults: "{ evaluation.id : bool } indique si à prendre en compte ou non." self.evaluations_etat = {} "{ evaluation_id: EvaluationEtat }" + self.etudids_attente = set() + "etudids avec au moins une note ATT dans ce module" self.en_attente = False "Vrai si au moins une évaluation a une note en attente" # @@ -145,7 +147,6 @@ class ModuleImplResults: evals_notes = pd.DataFrame(index=self.etudids, dtype=float) self.evaluations_completes = [] self.evaluations_completes_dict = {} - self.en_attente = False for evaluation in moduleimpl.evaluations: eval_df = self._load_evaluation_notes(evaluation) # is_complete ssi tous les inscrits (non dem) au semestre ont une note @@ -172,15 +173,20 @@ class ModuleImplResults: eval_df, how="left", left_index=True, right_index=True ) # Notes en attente: (ne prend en compte que les inscrits, non démissionnaires) - nb_att = sum( - evals_notes[str(evaluation.id)][list(inscrits_module)] - == scu.NOTES_ATTENTE + eval_notes_inscr = evals_notes[str(evaluation.id)][list(inscrits_module)] + eval_etudids_attente = set( + eval_notes_inscr.iloc[ + (eval_notes_inscr == scu.NOTES_ATTENTE).to_numpy() + ].index ) + self.etudids_attente |= eval_etudids_attente self.evaluations_etat[evaluation.id] = EvaluationEtat( - evaluation_id=evaluation.id, nb_attente=nb_att, is_complete=is_complete + evaluation_id=evaluation.id, + nb_attente=len(eval_etudids_attente), + is_complete=is_complete, ) - if nb_att > 0: - self.en_attente = True + # au moins une note en ATT dans ce modimpl: + self.en_attente = bool(self.etudids_attente) # Force columns names to integers (evaluation ids) evals_notes.columns = pd.Index([int(x) for x in evals_notes.columns], dtype=int) diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index 930909d341..42da823f83 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -5,10 +5,12 @@ import pandas as pd import flask_sqlalchemy from app import db +from app.auth.models import User from app.comp import df_cache from app.models.etudiants import Identite from app.models.modules import Module - +from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError +from app.scodoc.sco_permissions import Permission from app.scodoc import sco_utils as scu @@ -99,6 +101,27 @@ class ModuleImpl(db.Model): d.pop("module", None) return d + def can_change_ens_by(self, user: User, raise_exc=False) -> bool: + """Check if user can modify module resp. + If raise_exc, raises exception (AccessDenied or ScoLockedSemError) if not. + = Admin, et dir des etud. (si option l'y autorise) + """ + if not self.formsemestre.etat: + if raise_exc: + raise ScoLockedSemError("Modification impossible: semestre verrouille") + return False + # -- check access + # admin ou resp. semestre avec flag resp_can_change_resp + if user.has_permission(Permission.ScoImplement): + return True + if ( + user.id in [resp.id for resp in self.formsemestre.responsables] + ) and self.formsemestre.resp_can_change_ens: + return True + if raise_exc: + raise AccessDenied(f"Modification impossible pour {user}") + return False + # Enseignants (chargés de TD ou TP) d'un moduleimpl notes_modules_enseignants = db.Table( diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py index 7896336f23..5019647083 100644 --- a/app/scodoc/sco_exceptions.py +++ b/app/scodoc/sco_exceptions.py @@ -122,6 +122,14 @@ class ScoLockedFormError(ScoValueError): super().__init__(msg=msg, dest_url=dest_url) +class ScoLockedSemError(ScoValueError): + "Modification d'un formsemestre verrouillé" + + def __init__(self, msg="", dest_url=None): + msg = "Ce semestre est verrouillé ! " + str(msg) + super().__init__(msg=msg, dest_url=dest_url) + + class ScoNonEmptyFormationObject(ScoValueError): """On ne peut pas supprimer un module/matiere ou UE si des formsemestre s'y réfèrent""" diff --git a/app/scodoc/sco_moduleimpl.py b/app/scodoc/sco_moduleimpl.py index 0e1edbee3b..052512b0ec 100644 --- a/app/scodoc/sco_moduleimpl.py +++ b/app/scodoc/sco_moduleimpl.py @@ -377,7 +377,7 @@ def can_change_module_resp(moduleimpl_id): if not current_user.has_permission(Permission.ScoImplement) and ( (current_user.id not in sem["responsables"]) or (not sem["resp_can_change_ens"]) ): - raise AccessDenied("Modification impossible pour %s" % current_user) + raise AccessDenied(f"Modification impossible pour {current_user}") return M, sem diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index bf330adb34..39e97d0d2c 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -38,6 +38,7 @@ from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre, Identite, ScolarFormSemestreValidation, UniteEns from app import log +from app.tables import list_etuds from app.scodoc.scolog import logdb from app.scodoc import html_sco_header from app.scodoc import htmlutils @@ -520,14 +521,15 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) -> H.append(f"""{ue.acronyme}""") H.append("""""") - for etudid, ues_etud in table_inscr.items(): - etud: Identite = Identite.query.get(etudid) + etuds = list_etuds.etuds_sorted_from_ids(table_inscr.keys()) + for etud in etuds: + ues_etud = table_inscr[etud.id] H.append( - f"""{etud.nomprenom}""" ) @@ -539,7 +541,7 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) -> else: # Validations d'UE déjà enregistrées dans d'autres semestres validations_ue = ( - ScolarFormSemestreValidation.query.filter_by(etudid=etudid) + ScolarFormSemestreValidation.query.filter_by(etudid=etud.id) .filter( ScolarFormSemestreValidation.formsemestre_id != res.formsemestre.id, @@ -556,7 +558,8 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) -> ) validation = validations_ue[-1] if validations_ue else None expl_validation = ( - f"""Validée ({validation.code}) le {validation.event_date.strftime("%d/%m/%Y")}""" + f"""Validée ({validation.code}) le { + validation.event_date.strftime("%d/%m/%Y")}""" if validation else "" ) @@ -567,13 +570,13 @@ def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) -> title="{etud.nomprenom} {'inscrit' if est_inscr else 'non inscrit'} à l'UE {ue.acronyme}. {expl_validation}", onchange="change_ue_inscr(this);" data-url_inscr={ - url_for("notes.etud_inscrit_ue", - scodoc_dept=g.scodoc_dept, etudid=etudid, + url_for("notes.etud_inscrit_ue", + scodoc_dept=g.scodoc_dept, etudid=etud.id, formsemestre_id=res.formsemestre.id, ue_id=ue.id) } data-url_desinscr={ - url_for("notes.etud_desinscrit_ue", - scodoc_dept=g.scodoc_dept, etudid=etudid, + url_for("notes.etud_desinscrit_ue", + scodoc_dept=g.scodoc_dept, etudid=etud.id, formsemestre_id=res.formsemestre.id, ue_id=ue.id) } /> diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py index 5bf2a7f831..1127a968e9 100644 --- a/app/scodoc/sco_moduleimpl_status.py +++ b/app/scodoc/sco_moduleimpl_status.py @@ -36,6 +36,7 @@ from flask_login import current_user from app import db from app.auth.models import User from app.comp import res_sem +from app.comp.res_common import ResultatsSemestre from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre, ModuleImpl from app.models.evaluations import Evaluation @@ -59,9 +60,7 @@ from app.scodoc import sco_formsemestre_status from app.scodoc import sco_groups from app.scodoc import sco_moduleimpl from app.scodoc import sco_permissions_check -from app.scodoc import sco_users - -# ported from old DTML code in oct 2009 +from app.tables import list_etuds # menu evaluation dans moduleimpl def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0) -> str: @@ -196,23 +195,20 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): if not isinstance(moduleimpl_id, int): raise ScoInvalidIdType("moduleimpl_id must be an integer !") modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id) - M = modimpl.to_dict() + mi_dict = modimpl.to_dict() formsemestre_id = modimpl.formsemestre_id formsemestre: FormSemestre = modimpl.formsemestre - Mod = sco_edit_module.module_list(args={"module_id": modimpl.module_id})[0] + mod_dict = sco_edit_module.module_list(args={"module_id": modimpl.module_id})[0] sem = sco_formsemestre.get_formsemestre(formsemestre_id) - F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0] + formation_dict = sco_formations.formation_list( + args={"formation_id": sem["formation_id"]} + )[0] mod_inscrits = sco_moduleimpl.do_moduleimpl_inscription_list( moduleimpl_id=moduleimpl_id ) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - - # mod_evals = sco_evaluation_db.do_evaluation_list({"moduleimpl_id": moduleimpl_id}) - # mod_evals.sort( - # key=lambda x: (x["numero"], x["jour"], x["heure_debut"]), reverse=True - # ) - # la plus RECENTE en tête + # Evaluations, la plus RECENTE en tête evaluations = modimpl.evaluations.order_by( Evaluation.numero.desc(), Evaluation.jour.desc(), @@ -240,18 +236,23 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): ) arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() # - module_resp = User.query.get(M["responsable_id"]) - mod_type_name = scu.MODULE_TYPE_NAMES[Mod["module_type"]] + module_resp = User.query.get(modimpl.responsable_id) + mod_type_name = scu.MODULE_TYPE_NAMES[mod_dict["module_type"]] H = [ html_sco_header.sco_header( - page_title=f"{mod_type_name} {Mod['code']} {Mod['titre']}" + page_title=f"{mod_type_name} {mod_dict['code']} {mod_dict['titre']}", + javascripts=["js/etud_info.js"], + init_qtip=True, ), f"""

{mod_type_name} - {Mod['code']} {Mod['titre']} - {"dans l'UE " + modimpl.module.ue.acronyme if modimpl.module.module_type == scu.ModuleType.MALUS else ""} + {mod_dict['code']} {mod_dict['titre']} + {"dans l'UE " + modimpl.module.ue.acronyme + if modimpl.module.module_type == scu.ModuleType.MALUS + else "" + }

+ scu.ModuleType(mod_dict['module_type']).name.lower()}"> """) # 3ieme ligne: Formation H.append( - """""" % F + """""" + % formation_dict ) # Ligne: Inscrits H.append( @@ -312,15 +310,18 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): if current_user.has_permission(Permission.ScoEtudInscrit): H.append( """modifier""" - % M["moduleimpl_id"] + % mi_dict["moduleimpl_id"] ) H.append("") # Ligne: règle de calcul - has_expression = sco_compute_moy.moduleimpl_has_expression(M) + has_expression = sco_compute_moy.moduleimpl_has_expression(mi_dict) if has_expression: H.append( - ' +
Responsable: @@ -259,18 +260,14 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): ({module_resp.user_name}) """, ] - try: - sco_moduleimpl.can_change_module_resp(moduleimpl_id) + if modimpl.can_change_ens_by(current_user): H.append( - """modifier""" - % moduleimpl_id + f"""modifier""" ) - except: - pass H.append("""""") - H.append( - ", ".join([sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]]) - ) + H.append(", ".join([u.get_nomprenom() for u in modimpl.enseignants])) H.append("""""") try: sco_moduleimpl.can_change_ens(moduleimpl_id) @@ -302,7 +299,8 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): H.append("""
Formation: %(titre)s
Formation: %(titre)s
Règle de calcul: moyenne=%s' - % M["computation_expr"] + f"""
Règle de calcul: + moyenne={mi_dict["computation_expr"]} + """ ) H.append("""inutilisée dans cette version de ScoDoc""") if sco_moduleimpl.can_change_ens(moduleimpl_id, raise_exc=False): @@ -380,20 +381,24 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): # if formsemestre_has_decisions(formsemestre_id): H.append( - """
  • Décisions de jury saisies: seul le responsable du semestre peut saisir des notes (il devra modifier les décisions de jury).
""" + """
    +
  • Décisions de jury saisies: seul le responsable du + semestre peut saisir des notes (il devra modifier les décisions de jury). +
  • +
""" ) # H.append( - """

%d évaluations : + f"""

+{nb_evaluations} évaluations : -""" - % (nb_evaluations, moduleimpl_id) +""" ) # # Liste les noms de partitions partitions = sco_groups.get_partitions_list(sem["formsemestre_id"]) H.append( - """Afficher les groupes + """Afficher les groupes de  @@ -420,20 +424,21 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):

""" - % M + % mi_dict ) # -------- Tableau des evaluations top_table_links = "" if can_edit_evals: top_table_links = f"""Créer nouvelle évaluation """ if nb_evaluations > 0: top_table_links += f""" Trier par date """ @@ -477,31 +482,35 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None): f"""
+
+ {_html_modimpl_etuds_attente(nt, modimpl)} +
+

Légende

-

Rappel : seules les notes des évaluations complètement saisies +

Rappel : seules les notes des évaluations complètement saisies (affichées en vert) apparaissent dans les bulletins.

""" @@ -844,3 +853,22 @@ def _evaluation_poids_html(evaluation: Evaluation, max_poids: float = 0.0) -> st + "" ) return H + + +def _html_modimpl_etuds_attente(res: ResultatsSemestre, modimpl: ModuleImpl) -> str: + """Affiche la liste des étudiants ayant au moins une note en attente dans ce modimpl""" + m_res = res.modimpls_results.get(modimpl.id) + if m_res: + if not m_res.etudids_attente: + return "
Aucun étudiant n'a de notes en attente.
" + elif len(m_res.etudids_attente) < 10: + return f""" +

Étudiants avec une note en attente :

+ {list_etuds.html_table_etuds(m_res.etudids_attente)} + """ + else: + return f"""
{ + len(m_res.etudids_attente) + } étudiants ont des notes en attente.
""" + + return "" diff --git a/app/static/css/gt_table.css b/app/static/css/gt_table.css index 450ff68f3a..d35ca909fc 100644 --- a/app/static/css/gt_table.css +++ b/app/static/css/gt_table.css @@ -641,4 +641,9 @@ table.dataTable.order-column.stripe.hover tbody tr.even:hover td.sorting_1 { table.dataTable.gt_table { width: auto; padding-right: 5px; +} + +/* Tables non centrées */ +table.dataTable.gt_table.gt_left { + margin-left: 16px; } \ No newline at end of file diff --git a/app/tables/list_etuds.py b/app/tables/list_etuds.py new file mode 100644 index 0000000000..175cf0726b --- /dev/null +++ b/app/tables/list_etuds.py @@ -0,0 +1,117 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + +"""Liste simple d'étudiants +""" + +from flask import g, url_for +from app.models import Identite +from app.tables import table_builder as tb + + +class TableEtud(tb.Table): + """Table listant des étudiants + Peut-être sous-classée pour ajouter des colonnes. + L'id de la ligne est etuid, et le row stocke etud. + """ + + def __init__( + self, + etuds: list[Identite] = None, + classes: list[str] = None, + row_class=None, + with_foot_titles=False, + **kwargs, + ): + self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows + classes = classes or ["gt_table", "gt_left"] + super().__init__( + row_class=row_class or RowEtud, + classes=classes, + with_foot_titles=with_foot_titles, + **kwargs, + ) + self.add_etuds(etuds) + + def add_etuds(self, etuds: list[Identite]): + "Ajoute des étudiants à la table" + for etud in etuds: + row = self.row_class(self, etud) + row.add_etud_cols() + self.add_row(row) + + +class RowEtud(tb.Row): + "Ligne de la table d'étudiants" + # pour le moment très simple, extensible (codes, liens bulletins, ...) + def __init__(self, table: TableEtud, etud: Identite, *args, **kwargs): + super().__init__(table, etud.id, *args, **kwargs) + self.etud = etud + + def add_etud_cols(self): + """Ajoute colonnes étudiant: codes, noms""" + etud = self.etud + self.table.group_titles.update( + { + "etud_codes": "Codes", + "identite_detail": "", + "identite_court": "", + } + ) + # --- Codes (seront cachés, mais exportés en excel) + # self.add_cell("etudid", "etudid", etud.id, "etud_codes") + # self.add_cell( + # "code_nip", + # "code_nip", + # etud.code_nip or "", + # "etud_codes", + # ) + + # --- Identité étudiant + # url_bulletin = url_for( + # "notes.formsemestre_bulletinetud", + # scodoc_dept=g.scodoc_dept, + # formsemestre_id=res.formsemestre.id, + # etudid=etud.id, + # ) + url_bulletin = None # pour extension future + self.add_cell("civilite_str", "Civ.", etud.civilite_str, "identite_detail") + self.add_cell( + "nom_disp", + "Nom", + etud.nom_disp(), + "identite_detail", + data={"order": etud.sort_key}, + target=url_bulletin, + target_attrs={"class": "etudinfo discretelink", "id": str(etud.id)}, + ) + self.add_cell("prenom", "Prénom", etud.prenom, "identite_detail") + # self.add_cell( + # "nom_short", + # "Nom", + # etud.nom_short, + # "identite_court", + # data={ + # "order": etud.sort_key, + # "etudid": etud.id, + # "nomprenom": etud.nomprenom, + # }, + # target=url_bulletin, + # target_attrs={"class": "etudinfo", "id": str(etud.id)}, + # ) + + +def etuds_sorted_from_ids(etudids) -> list[Identite]: + "Liste triée d'etuds à partir d'une collections d'etudids" + etuds = [Identite.query.get_or_404(etudid) for etudid in etudids] + return sorted(etuds, key=lambda etud: etud.sort_key) + + +def html_table_etuds(etudids) -> str: + """Table HTML simple des étudiants indiqués""" + etuds = etuds_sorted_from_ids(etudids) + table = TableEtud(etuds) + return table.html() diff --git a/app/tables/recap.py b/app/tables/recap.py index 12f0fcc747..c48acced79 100644 --- a/app/tables/recap.py +++ b/app/tables/recap.py @@ -38,8 +38,6 @@ class TableRecap(tb.Table): moy_sae__, ... les moyennes de SAE dans l'UE On ajoute aussi des classes: - - pour les lignes: - selected_row pour l'étudiant sélectionné - les colonnes: - la moyenne générale a la classe col_moy_gen - les colonnes SAE ont la classe col_sae diff --git a/app/tables/table_builder.py b/app/tables/table_builder.py index 50a61f559e..829f3642a5 100644 --- a/app/tables/table_builder.py +++ b/app/tables/table_builder.py @@ -68,6 +68,7 @@ class Table(Element): classes: list[str] = None, attrs: dict[str, str] = None, data: dict = None, + with_foot_titles=True, row_class=None, xls_sheet_name="feuille", xls_before_table=[], # liste de cellules a placer avant la table @@ -100,8 +101,10 @@ class Table(Element): self.head_title_row: "Row" = Row( self, "title_head", cell_elt="th", classes=["titles"] ) - self.foot_title_row: "Row" = Row( - self, "title_foot", cell_elt="th", classes=["titles"] + self.foot_title_row: "Row" = ( + Row(self, "title_foot", cell_elt="th", classes=["titles"]) + if with_foot_titles + else None ) self.empty_cell = Cell.empty() # Excel (xls) spécifique: @@ -119,8 +122,10 @@ class Table(Element): """ self.sort_columns() # Titres - self.add_head_row(self.head_title_row) - self.add_foot_row(self.foot_title_row) + if self.head_title_row: + self.add_head_row(self.head_title_row) + if self.foot_title_row: + self.add_foot_row(self.foot_title_row) def get_row_by_id(self, row_id) -> "Row": "return the row, or None" @@ -261,18 +266,23 @@ class Table(Element): title = title or "" 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, - 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 - ) - - return self.head_title_row.cells.get(col_id), self.foot_title_row.cells[col_id] + if self.head_title_row: + self.head_title_row.cells[col_id] = self.head_title_row.add_cell( + col_id, + None, + title, + classes=classes, + group=self.column_group.get(col_id), + ) + if self.foot_title_row: + self.foot_title_row.cells[col_id] = self.foot_title_row.add_cell( + col_id, None, title, classes=classes + ) + head_cell = ( + self.head_title_row.cells.get(col_id) if self.head_title_row else None + ) + foot_cell = self.foot_title_row.cells[col_id] if self.foot_title_row else None + return head_cell, foot_cell def excel(self, wb: Workbook = None): """Simple Excel representation of the table.""" diff --git a/app/views/notes.py b/app/views/notes.py index 75f02ab6de..7ac2834a9d 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -1043,15 +1043,18 @@ def edit_enseignants_form(moduleimpl_id): @scodoc @permission_required(Permission.ScoView) @scodoc7func -def edit_moduleimpl_resp(moduleimpl_id): +def edit_moduleimpl_resp(moduleimpl_id: int): """Changement d'un enseignant responsable de module Accessible par Admin et dir des etud si flag resp_can_change_ens """ - M, sem = sco_moduleimpl.can_change_module_resp(moduleimpl_id) + modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id) + modimpl.can_change_ens_by(current_user, raise_exc=True) # access control H = [ html_sco_header.html_sem_header( - 'Modification du responsable du module %s' - % (moduleimpl_id, M["module"]["titre"]), + f"""Modification du responsable du module {modimpl.module.titre or ""}""", javascripts=["libjs/AutoSuggest.js"], cssstyles=["css/autosuggest_inquisitor.css"], bodyOnLoad="init_tf_form('')", @@ -1065,9 +1068,9 @@ def edit_moduleimpl_resp(moduleimpl_id): uid2display[u["id"]] = u["nomplogin"] allowed_user_names = list(uid2display.values()) - initvalues = M + initvalues = modimpl.to_dict(with_module=False) initvalues["responsable_id"] = uid2display.get( - M["responsable_id"], M["responsable_id"] + modimpl.responsable_id, modimpl.responsable_id ) form = [ ("moduleimpl_id", {"input_type": "hidden"}), @@ -1112,9 +1115,8 @@ def edit_moduleimpl_resp(moduleimpl_id): ) else: responsable_id = User.get_user_id_from_nomplogin(tf[2]["responsable_id"]) - if ( - not responsable_id - ): # presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps) + if not responsable_id: + # presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps) return flask.redirect( url_for( "notes.moduleimpl_status", @@ -1123,16 +1125,15 @@ def edit_moduleimpl_resp(moduleimpl_id): ) ) - sco_moduleimpl.do_moduleimpl_edit( - {"moduleimpl_id": moduleimpl_id, "responsable_id": responsable_id}, - formsemestre_id=sem["formsemestre_id"], - ) + modimpl.responsable_id = responsable_id + db.session.add(modimpl) + db.session.commit() + flash("Responsable modifié") return flask.redirect( url_for( "notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id, - head_message="responsable modifié", ) ) diff --git a/sco_version.py b/sco_version.py index b4ee5fb499..6561608c52 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.4.39" +SCOVERSION = "9.4.40" SCONAME = "ScoDoc"