Modernise code evaluations/enseignants

This commit is contained in:
Emmanuel Viennet 2024-02-04 23:08:08 +01:00
parent d5fdd5b8b8
commit 57d36927ac
9 changed files with 38 additions and 120 deletions

View File

@ -477,8 +477,8 @@ class User(UserMixin, ScoDocModel):
return f"{nom} {scu.format_prenom(self.prenom)} ({self.user_name})" return f"{nom} {scu.format_prenom(self.prenom)} ({self.user_name})"
@staticmethod @staticmethod
def get_user_id_from_nomplogin(nomplogin: str) -> Optional[int]: def get_user_from_nomplogin(nomplogin: str) -> Optional["User"]:
"""Returns id from the string "Dupont Pierre (dupont)" """Returns User instance from the string "Dupont Pierre (dupont)"
or None if user does not exist or None if user does not exist
""" """
match = re.match(r".*\((.*)\)", nomplogin.strip()) match = re.match(r".*\((.*)\)", nomplogin.strip())
@ -486,7 +486,7 @@ class User(UserMixin, ScoDocModel):
user_name = match.group(1) user_name = match.group(1)
u = User.query.filter_by(user_name=user_name).first() u = User.query.filter_by(user_name=user_name).first()
if u: if u:
return u.id return u
return None return None
def get_nom_fmt(self): def get_nom_fmt(self):

View File

@ -857,17 +857,20 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
tf[2]["bul_hide_xml"] = True tf[2]["bul_hide_xml"] = True
# remap les identifiants de responsables: # remap les identifiants de responsables:
for field in resp_fields: for field in resp_fields:
tf[2][field] = User.get_user_id_from_nomplogin(tf[2][field]) resp = User.get_user_from_nomplogin(tf[2][field])
tf[2][field] = resp.id if resp else -1
tf[2]["responsables"] = [] tf[2]["responsables"] = []
for field in resp_fields: for field in resp_fields:
if tf[2][field]: if tf[2][field]:
tf[2]["responsables"].append(tf[2][field]) tf[2]["responsables"].append(tf[2][field])
for module_id in tf[2]["tf-checked"]: for module_id in tf[2]["tf-checked"]:
mod_resp_id = User.get_user_id_from_nomplogin(tf[2][module_id]) mod_resp = User.get_user_from_nomplogin(tf[2][module_id])
if mod_resp_id is None: if mod_resp is None:
# Si un module n'a pas de responsable (ou inconnu), # Si un module n'a pas de responsable (ou inconnu),
# l'affecte au 1er directeur des etudes: # l'affecte au 1er directeur des etudes:
mod_resp_id = tf[2]["responsable_id"] mod_resp_id = tf[2]["responsable_id"]
else:
mod_resp_id = mod_resp.id
tf[2][module_id] = mod_resp_id tf[2][module_id] = mod_resp_id
# etapes: # etapes:
@ -1227,9 +1230,12 @@ def formsemestre_clone(formsemestre_id):
"formsemestre_status?formsemestre_id=%s" % formsemestre_id "formsemestre_status?formsemestre_id=%s" % formsemestre_id
) )
else: else:
resp = User.get_user_from_nomplogin(tf[2]["responsable_id"])
if not resp:
raise ScoValueError("id responsable invalide")
new_formsemestre_id = do_formsemestre_clone( new_formsemestre_id = do_formsemestre_clone(
formsemestre_id, formsemestre_id,
User.get_user_id_from_nomplogin(tf[2]["responsable_id"]), resp.id,
tf[2]["date_debut"], tf[2]["date_debut"],
tf[2]["date_fin"], tf[2]["date_fin"],
clone_evaluations=tf[2]["clone_evaluations"], clone_evaluations=tf[2]["clone_evaluations"],

View File

@ -25,21 +25,18 @@
# #
############################################################################## ##############################################################################
"""Fonctions sur les moduleimpl """Fonctions sur les moduleimpl (legacy: use models.moduleimpls instead)
""" """
from flask_login import current_user
import psycopg2 import psycopg2
from app import db from app import db
from app.models import Formation from app.models import Formation
from app.scodoc import scolog from app.scodoc import scolog
from app.scodoc import sco_formsemestre
from app.scodoc import sco_cache from app.scodoc import sco_cache
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_exceptions import ScoValueError, AccessDenied
# --- Gestion des "Implémentations de Modules" # --- Gestion des "Implémentations de Modules"
# Un "moduleimpl" correspond a la mise en oeuvre d'un module # Un "moduleimpl" correspond a la mise en oeuvre d'un module
@ -56,15 +53,6 @@ _moduleimplEditor = ndb.EditableTable(
), ),
) )
_modules_enseignantsEditor = ndb.EditableTable(
"notes_modules_enseignants",
None, # pas d'id dans cette Table d'association
(
"moduleimpl_id", # associe moduleimpl
"ens_id", # a l'id de l'enseignant (User.id)
),
)
def do_moduleimpl_create(args): def do_moduleimpl_create(args):
"create a moduleimpl" "create a moduleimpl"
@ -107,9 +95,6 @@ def moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None):
args = locals() args = locals()
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
modimpls = _moduleimplEditor.list(cnx, args) modimpls = _moduleimplEditor.list(cnx, args)
# Ajoute la liste des enseignants
for mo in modimpls:
mo["ens"] = do_ens_list(args={"moduleimpl_id": mo["moduleimpl_id"]})
return modimpls return modimpls
@ -341,23 +326,3 @@ def do_moduleimpl_inscrit_etuds(moduleimpl_id, formsemestre_id, etudids, reset=F
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
) # > moduleimpl_inscrit_etuds ) # > moduleimpl_inscrit_etuds
def do_ens_list(*args, **kw):
"liste les enseignants d'un moduleimpl (pas le responsable)"
cnx = ndb.GetDBConnexion()
ens = _modules_enseignantsEditor.list(cnx, *args, **kw)
return ens
def do_ens_edit(*args, **kw):
"edit ens"
cnx = ndb.GetDBConnexion()
_modules_enseignantsEditor.edit(cnx, *args, **kw)
def do_ens_create(args):
"create ens"
cnx = ndb.GetDBConnexion()
r = _modules_enseignantsEditor.create(cnx, args)
return r

View File

@ -209,15 +209,10 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
# #
sem_locked = not formsemestre.etat sem_locked = not formsemestre.etat
can_edit_evals = ( can_edit_evals = (
sco_permissions_check.can_edit_notes( modimpl.can_edit_notes(current_user, allow_ens=formsemestre.ens_can_edit_eval)
current_user, moduleimpl_id, allow_ens=formsemestre.ens_can_edit_eval
)
and not sem_locked
)
can_edit_notes = (
sco_permissions_check.can_edit_notes(current_user, moduleimpl_id)
and not sem_locked and not sem_locked
) )
can_edit_notes = modimpl.can_edit_notes(current_user) and not sem_locked
arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags() arrow_up, arrow_down, arrow_none = sco_groups.get_arrow_icons_tags()
# #
module_resp = db.session.get(User, modimpl.responsable_id) module_resp = db.session.get(User, modimpl.responsable_id)

View File

@ -8,50 +8,11 @@ from flask_login import current_user
from app import db from app import db
from app.auth.models import User from app.auth.models import User
from app.models import FormSemestre
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_etud from app.scodoc import sco_etud
from app.scodoc import sco_exceptions from app.scodoc import sco_exceptions
from app.scodoc import sco_moduleimpl
def can_edit_notes(authuser, moduleimpl_id, allow_ens=True):
"""True if authuser can enter or edit notes in this module.
If allow_ens, grant access to all ens in this module
Si des décisions de jury ont déjà été saisies dans ce semestre,
seul le directeur des études peut saisir des notes (et il ne devrait pas).
"""
from app.scodoc import sco_formsemestre
from app.scodoc import sco_cursus_dut
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]:
return False # semestre verrouillé
if sco_cursus_dut.formsemestre_has_decisions(sem["formsemestre_id"]):
# il y a des décisions de jury dans ce semestre !
return (
authuser.has_permission(Permission.EditAllNotes)
or authuser.id in sem["responsables"]
)
else:
if (
(not authuser.has_permission(Permission.EditAllNotes))
and authuser.id != M["responsable_id"]
and authuser.id not in sem["responsables"]
):
# enseignant (chargé de TD) ?
if allow_ens:
for ens in M["ens"]:
if ens["ens_id"] == authuser.id:
return True
return False
else:
return True
def can_suppress_annotation(annotation_id): def can_suppress_annotation(annotation_id):

View File

@ -48,6 +48,7 @@ from wtforms import (
HiddenField, HiddenField,
SelectMultipleField, SelectMultipleField,
) )
from app.models import ModuleImpl
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app import ScoValueError from app import ScoValueError
@ -58,7 +59,6 @@ from app.scodoc import sco_evaluation_db
from app.scodoc import sco_excel from app.scodoc import sco_excel
from app.scodoc.sco_excel import ScoExcelBook, COLORS from app.scodoc.sco_excel import ScoExcelBook, COLORS
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl from app.scodoc import sco_moduleimpl
from app.scodoc import sco_permissions_check from app.scodoc import sco_permissions_check
@ -247,6 +247,8 @@ class PlacementRunner:
# gr_title = sco_groups.listgroups_abbrev(d['groups']) # gr_title = sco_groups.listgroups_abbrev(d['groups'])
self.current_user = current_user self.current_user = current_user
self.moduleimpl_id = self.eval_data["moduleimpl_id"] self.moduleimpl_id = self.eval_data["moduleimpl_id"]
self.moduleimpl: ModuleImpl = ModuleImpl.query.get_or_404(self.moduleimpl_id)
# TODO: à revoir pour utiliser modèle ModuleImpl
self.moduleimpl_data = sco_moduleimpl.moduleimpl_list( self.moduleimpl_data = sco_moduleimpl.moduleimpl_list(
moduleimpl_id=self.moduleimpl_id moduleimpl_id=self.moduleimpl_id
)[0] )[0]
@ -280,9 +282,7 @@ class PlacementRunner:
def check_placement(self): def check_placement(self):
"""Vérifie que l'utilisateur courant a le droit d'édition sur les notes""" """Vérifie que l'utilisateur courant a le droit d'édition sur les notes"""
# Check access (admin, respformation, and responsable_id) # Check access (admin, respformation, and responsable_id)
return sco_permissions_check.can_edit_notes( return self.moduleimpl.can_edit_notes(self.current_user)
self.current_user, self.moduleimpl_id
)
def exec_placement(self): def exec_placement(self):
"""Excéute l'action liée au formulaire""" """Excéute l'action liée au formulaire"""

View File

@ -198,8 +198,8 @@ def do_evaluation_upload_xls():
evaluation_id = int(vals["evaluation_id"]) evaluation_id = int(vals["evaluation_id"])
comment = vals["comment"] comment = vals["comment"]
evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id) evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id)
# Check access (admin, respformation, and responsable_id) # Check access (admin, respformation, responsable_id, ens)
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id): if not evaluation.moduleimpl.can_edit_notes(current_user):
raise AccessDenied(f"Modification des notes impossible pour {current_user}") raise AccessDenied(f"Modification des notes impossible pour {current_user}")
# #
diag, lines = sco_excel.excel_file_to_list(vals["notefile"]) diag, lines = sco_excel.excel_file_to_list(vals["notefile"])
@ -315,7 +315,7 @@ def do_evaluation_set_etud_note(evaluation: Evaluation, etud: Identite, value) -
"""Enregistre la note d'un seul étudiant """Enregistre la note d'un seul étudiant
value: valeur externe (float ou str) value: valeur externe (float ou str)
""" """
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl.id): if not evaluation.moduleimpl.can_edit_notes(current_user):
raise AccessDenied(f"Modification des notes impossible pour {current_user}") raise AccessDenied(f"Modification des notes impossible pour {current_user}")
# Convert and check value # Convert and check value
L, invalids, _, _, _ = _check_notes([(etud.id, value)], evaluation) L, invalids, _, _, _ = _check_notes([(etud.id, value)], evaluation)
@ -336,7 +336,7 @@ def do_evaluation_set_missing(
modimpl = evaluation.moduleimpl modimpl = evaluation.moduleimpl
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(current_user, modimpl.id): if not modimpl.can_edit_notes(current_user):
raise AccessDenied(f"Modification des notes impossible pour {current_user}") raise AccessDenied(f"Modification des notes impossible pour {current_user}")
# #
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
@ -433,15 +433,11 @@ def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
"suppress all notes in this eval" "suppress all notes in this eval"
evaluation = Evaluation.query.get_or_404(evaluation_id) evaluation = Evaluation.query.get_or_404(evaluation_id)
if sco_permissions_check.can_edit_notes( if evaluation.moduleimpl.can_edit_notes(current_user, allow_ens=False):
current_user, evaluation.moduleimpl_id, allow_ens=False
):
# On a le droit de modifier toutes les notes # On a le droit de modifier toutes les notes
# recupere les etuds ayant une note # recupere les etuds ayant une note
notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id) notes_db = sco_evaluation_db.do_evaluation_get_all_notes(evaluation_id)
elif sco_permissions_check.can_edit_notes( elif evaluation.moduleimpl.can_edit_notes(current_user, allow_ens=True):
current_user, evaluation.moduleimpl_id, allow_ens=True
):
# Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi # Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi
notes_db = sco_evaluation_db.do_evaluation_get_all_notes( notes_db = sco_evaluation_db.do_evaluation_get_all_notes(
evaluation_id, by_uid=current_user.id evaluation_id, by_uid=current_user.id
@ -682,7 +678,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
evaluation = Evaluation.query.get_or_404(evaluation_id) evaluation = Evaluation.query.get_or_404(evaluation_id)
moduleimpl_id = evaluation.moduleimpl.id moduleimpl_id = evaluation.moduleimpl.id
formsemestre_id = evaluation.moduleimpl.formsemestre_id formsemestre_id = evaluation.moduleimpl.formsemestre_id
if not sco_permissions_check.can_edit_notes(current_user, moduleimpl_id): if not evaluation.moduleimpl.can_edit_notes(current_user):
return ( return (
html_sco_header.sco_header() html_sco_header.sco_header()
+ f""" + f"""
@ -813,9 +809,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=()):
# #
H.append("""</div><h3>Autres opérations</h3><ul>""") H.append("""</div><h3>Autres opérations</h3><ul>""")
if sco_permissions_check.can_edit_notes( if evaluation.moduleimpl.can_edit_notes(current_user, allow_ens=False):
current_user, moduleimpl_id, allow_ens=False
):
H.append( H.append(
f""" f"""
<li> <li>
@ -967,7 +961,7 @@ def saisie_notes(evaluation_id: int, group_ids: list = None):
) )
# Check access # Check access
# (admin, respformation, and responsable_id) # (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id): if not evaluation.moduleimpl.can_edit_notes(current_user):
return f""" return f"""
{html_sco_header.sco_header()} {html_sco_header.sco_header()}
<h2>Modification des notes impossible pour {current_user.user_name}</h2> <h2>Modification des notes impossible pour {current_user.user_name}</h2>
@ -1361,7 +1355,7 @@ def save_notes(
_external=True, _external=True,
) )
# Check access: admin, respformation, or responsable_id # Check access: admin, respformation, or responsable_id
if not sco_permissions_check.can_edit_notes(current_user, evaluation.moduleimpl_id): if not evaluation.moduleimpl.can_edit_notes(current_user):
return json_error(403, "modification notes non autorisee pour cet utilisateur") return json_error(403, "modification notes non autorisee pour cet utilisateur")
# #
valid_notes, _, _, _, _ = _check_notes(notes, evaluation) valid_notes, _, _, _, _ = _check_notes(notes, evaluation)

View File

@ -1053,24 +1053,22 @@ def edit_enseignants_form(moduleimpl_id):
) )
) )
else: else:
ens_id = User.get_user_id_from_nomplogin(tf[2]["ens_id"]) ens = User.get_user_from_nomplogin(tf[2]["ens_id"])
if not ens_id: if ens is None:
H.append( H.append(
'<p class="help">Pour ajouter un enseignant, choisissez un nom dans le menu</p>' '<p class="help">Pour ajouter un enseignant, choisissez un nom dans le menu</p>'
) )
else: else:
# et qu'il n'est pas deja: # et qu'il n'est pas deja:
if ( if (
ens_id in (x.id for x in modimpl.enseignants) ens.id in (x.id for x in modimpl.enseignants)
or ens_id == modimpl.responsable_id or ens.id == modimpl.responsable_id
): ):
H.append( H.append(
f"""<p class="help">Enseignant {ens_id} déjà dans la liste !</p>""" f"""<p class="help">Enseignant {ens.user_name} déjà dans la liste !</p>"""
) )
else: else:
sco_moduleimpl.do_ens_create( modimpl.enseignants.append(ens)
{"moduleimpl_id": moduleimpl_id, "ens_id": ens_id}
)
return flask.redirect( return flask.redirect(
url_for( url_for(
"notes.edit_enseignants_form", "notes.edit_enseignants_form",
@ -1156,7 +1154,7 @@ def edit_moduleimpl_resp(moduleimpl_id: int):
) )
) )
else: else:
responsable_id = User.get_user_id_from_nomplogin(tf[2]["responsable_id"]) responsable_id = User.get_user_from_nomplogin(tf[2]["responsable_id"])
if not responsable_id: if not responsable_id:
# presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps) # presque impossible: tf verifie les valeurs (mais qui peuvent changer entre temps)
return flask.redirect( return flask.redirect(

View File

@ -375,7 +375,6 @@ def test_import_formation(test_client, filename="formation-exemple-1.xml"):
formsemestre_id=formsemestre_ids[mod["semestre_id"] - 1], formsemestre_id=formsemestre_ids[mod["semestre_id"] - 1],
) )
mi = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0] mi = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
assert mi["ens"] == []
assert mi["module_id"] == mod["module_id"] assert mi["module_id"] == mod["module_id"]
# --- Export formation en XML # --- Export formation en XML