From 88124fa388d283202b5b351d381d01cc2337f6be Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Fri, 15 Dec 2023 03:37:55 +0100 Subject: [PATCH] =?UTF-8?q?formsemestre=5Fenseignants=5Flist:=20r=C3=A9-?= =?UTF-8?q?=C3=A9criture,=20fix=20#756?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/auth/models.py | 8 ++++ app/auth/routes.py | 2 - app/models/modules.py | 1 + app/scodoc/sco_users.py | 2 +- app/views/notes.py | 99 +++++++++++++++++++---------------------- sco_version.py | 2 +- 6 files changed, 58 insertions(+), 56 deletions(-) diff --git a/app/auth/models.py b/app/auth/models.py index dc9e32fc7..0d59736de 100644 --- a/app/auth/models.py +++ b/app/auth/models.py @@ -234,6 +234,14 @@ class User(UserMixin, db.Model, ScoDocModel): return None return db.session.get(User, user_id) + def sort_key(self) -> tuple: + "sort key" + return ( + (self.nom or "").upper(), + (self.prenom or "").upper(), + (self.user_name or "").upper(), + ) + def to_dict(self, include_email=True): """l'utilisateur comme un dict, avec des champs supplémentaires""" data = { diff --git a/app/auth/routes.py b/app/auth/routes.py index 7818e60b1..13ee17501 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -208,5 +208,3 @@ def cas_users_import_config(): title=_("Importation configuration CAS utilisateurs"), form=form, ) - - return diff --git a/app/models/modules.py b/app/models/modules.py index 891a476dc..31c561925 100644 --- a/app/models/modules.py +++ b/app/models/modules.py @@ -22,6 +22,7 @@ class Module(db.Model): abbrev = db.Column(db.Text()) # nom court # certains départements ont des codes infiniment longs: donc Text ! code = db.Column(db.Text(), nullable=False) + "code module, chaine non nullable" heures_cours = db.Column(db.Float) heures_td = db.Column(db.Float) heures_tp = db.Column(db.Float) diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index 158378cec..76f3f3a87 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -268,7 +268,7 @@ def get_user_list( q = q.filter_by(active=True) if having_role: q = q.join(UserRole).filter_by(role_id=having_role.id) - return q.order_by(User.nom, User.user_name).all() + return q.order_by(User.nom, User.prenom, User.user_name).all() @cache.memoize(timeout=50) # seconds diff --git a/app/views/notes.py b/app/views/notes.py index b7fbbd915..10887e037 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -30,7 +30,6 @@ Module notes: issu de ScoDoc7 / ZNotes.py Emmanuel Viennet, 2021 """ - from operator import itemgetter import time @@ -59,6 +58,7 @@ from app.comp import jury, res_sem from app.comp.res_compat import NotesTableCompat from app.models import ( ApcNiveau, + Assiduite, BulAppreciations, DispenseUE, Evaluation, @@ -1266,66 +1266,60 @@ def formsemestre_enseignants_list(formsemestre_id, fmt="html"): """Liste les enseignants intervenants dans le semestre (resp. modules et chargés de TD) et indique les absences saisies par chacun. """ - sem = sco_formsemestre.get_formsemestre(formsemestre_id) - # resp. de modules: - mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id) - sem_ens = {} - for mod in mods: - if not mod["responsable_id"] in sem_ens: - sem_ens[mod["responsable_id"]] = {"mods": [mod]} + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + # resp. de modules et charges de TD + sem_ens: dict[ + int, list[ModuleImpl] + ] = {} # uid : { "mods" : liste des modimpls, ... } + modimpls = formsemestre.modimpls_sorted + for modimpl in modimpls: + if not modimpl.responsable_id in sem_ens: + sem_ens[modimpl.responsable_id] = {"mods": [modimpl]} else: - sem_ens[mod["responsable_id"]]["mods"].append(mod) - # charges de TD: - for mod in mods: - for ensd in mod["ens"]: - if not ensd["ens_id"] in sem_ens: - sem_ens[ensd["ens_id"]] = {"mods": [mod]} + sem_ens[modimpl.responsable_id]["mods"].append(modimpl) + + for enseignant in modimpl.enseignants: + if not enseignant.id in sem_ens: + sem_ens[enseignant.id] = {"mods": [modimpl]} else: - sem_ens[ensd["ens_id"]]["mods"].append(mod) + sem_ens[enseignant.id]["mods"].append(modimpl) # compte les absences ajoutées par chacun dans tout le semestre - cnx = ndb.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - for ens in sem_ens: - u = User.query.filter_by(id=ens).first() - if not u: - continue - cursor.execute( - """SELECT * FROM scolog L, notes_formsemestre_inscription I - WHERE method = 'AddAbsence' - and authenticated_user = %(authenticated_user)s - and L.etudid = I.etudid - and I.formsemestre_id = %(formsemestre_id)s - and date > %(date_debut)s - and date < %(date_fin)s - """, - { - "authenticated_user": u.user_name, - "formsemestre_id": formsemestre_id, - "date_debut": ndb.DateDMYtoISO(sem["date_debut"]), - "date_fin": ndb.DateDMYtoISO(sem["date_fin"]), - }, + for uid, info in sem_ens.items(): + # Note : avant 9.6, on utilisait Scolog pour compter les opérations AddAbsence + # ici on compte directement les assiduités + info["nbabsadded"] = ( + Assiduite.query.filter_by(user_id=uid, etat=scu.EtatAssiduite.ABSENT) + .filter( + Assiduite.date_debut >= formsemestre.date_debut, + Assiduite.date_debut <= formsemestre.date_fin, + ) + .join(Identite) + .join(FormSemestreInscription) + .filter_by(formsemestre_id=formsemestre.id) + .count() ) - - events = cursor.dictfetchall() - sem_ens[ens]["nbabsadded"] = len(events) - # description textuelle des modules - for ens in sem_ens: - sem_ens[ens]["descr_mods"] = ", ".join( - [x["module"]["code"] or "?" for x in sem_ens[ens]["mods"]] + for uid, info in sem_ens.items(): + info["descr_mods"] = ", ".join( + [modimpl.module.code for modimpl in sem_ens[uid]["mods"]] ) # ajoute infos sur enseignant: - for ens in sem_ens: - sem_ens[ens].update(sco_users.user_info(ens)) - if sem_ens[ens]["email"]: - sem_ens[ens]["_email_target"] = "mailto:%s" % sem_ens[ens]["email"] + for uid, info in sem_ens.items(): + user: User = db.session.get(User, uid) + if user: + if user.email: + info["email"] = user.email + info["_email_target"] = f"mailto:{user.email}" + info["nom_fmt"] = user.get_nom_fmt() + info["prenom_fmt"] = user.get_prenom_fmt() + info["sort_key"] = user.sort_key() sem_ens_list = list(sem_ens.values()) - sem_ens_list.sort(key=itemgetter("nomprenom")) + sem_ens_list.sort(key=itemgetter("sort_key")) # --- Generate page with table - title = "Enseignants de " + sem["titremois"] + title = f"Enseignants de {formsemestre.titre_mois()}" T = GenTable( columns_ids=["nom_fmt", "prenom_fmt", "descr_mods", "nbabsadded", "email"], titles={ @@ -1338,12 +1332,13 @@ def formsemestre_enseignants_list(formsemestre_id, fmt="html"): rows=sem_ens_list, html_sortable=True, html_class="table_leftalign", - filename=scu.make_filename("Enseignants-" + sem["titreannee"]), + filename=scu.make_filename(f"Enseignants-{formsemestre.titre_annee()}"), html_title=html_sco_header.html_sem_header( "Enseignants du semestre", with_page_header=False ), - base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id), - caption="Tous les enseignants (responsables ou associés aux modules de ce semestre) apparaissent. Le nombre de saisies d'absences est le nombre d'opérations d'ajout effectuées sur ce semestre, sans tenir compte des annulations ou double saisies.", + base_url=f"{request.base_url}?formsemestre_id={formsemestre_id}", + caption="""Tous les enseignants (responsables ou associés aux modules de + ce semestre) apparaissent. Le nombre de saisies d'absences est indicatif.""", preferences=sco_preferences.SemPreferences(formsemestre_id), ) return T.make_page(page_title=title, title=title, fmt=fmt) diff --git a/sco_version.py b/sco_version.py index 5c3eb33ed..92c357e09 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.70" +SCOVERSION = "9.6.71" SCONAME = "ScoDoc"