Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
12 changed files with 144 additions and 1307 deletions
Showing only changes of commit 4b2e88c678 - Show all commits

View File

@ -52,7 +52,7 @@ class ScoDocModel(db.Model):
def create_from_dict(cls, data: dict) -> "ScoDocModel": def create_from_dict(cls, data: dict) -> "ScoDocModel":
"""Create a new instance of the model with attributes given in dict. """Create a new instance of the model with attributes given in dict.
The instance is added to the session (but not flushed nor committed). The instance is added to the session (but not flushed nor committed).
Use only relevant arributes for the given model and ignore others. Use only relevant attributes for the given model and ignore others.
""" """
if data: if data:
args = cls.convert_dict_fields(cls.filter_model_attributes(data)) args = cls.convert_dict_fields(cls.filter_model_attributes(data))

View File

@ -742,6 +742,7 @@ def check_etud_duplicate_code(args, code_name, edit=True):
Raises ScoGenError si problème. Raises ScoGenError si problème.
""" """
etudid = args.get("etudid", None) etudid = args.get("etudid", None)
assert (not edit) or (etudid is not None) # si edit, etudid doit être spécifié
if not args.get(code_name, None): if not args.get(code_name, None):
return return
etuds = Identite.query.filter_by( etuds = Identite.query.filter_by(
@ -749,9 +750,7 @@ def check_etud_duplicate_code(args, code_name, edit=True):
).all() ).all()
duplicate = False duplicate = False
if edit: if edit:
duplicate = (len(etuds) > 1) or ( duplicate = (len(etuds) > 1) or ((len(etuds) == 1) and etuds[0].id != etudid)
(len(etuds) == 1) and etuds[0].id != args["etudid"]
)
else: else:
duplicate = len(etuds) > 0 duplicate = len(etuds) > 0
if duplicate: if duplicate:

View File

@ -3,12 +3,13 @@
""" """
import pandas as pd import pandas as pd
from flask import abort, g from flask import abort, g
from flask_login import current_user
from flask_sqlalchemy.query import Query from flask_sqlalchemy.query import Query
from app import db from app import db
from app.auth.models import User from app.auth.models import User
from app.comp import df_cache from app.comp import df_cache
from app.models import APO_CODE_STR_LEN from app.models import APO_CODE_STR_LEN, ScoDocModel
from app.models.etudiants import Identite from app.models.etudiants import Identite
from app.models.evaluations import Evaluation from app.models.evaluations import Evaluation
from app.models.modules import Module from app.models.modules import Module
@ -17,7 +18,7 @@ from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
class ModuleImpl(db.Model): class ModuleImpl(ScoDocModel):
"""Mise en oeuvre d'un module pour une annee/semestre""" """Mise en oeuvre d'un module pour une annee/semestre"""
__tablename__ = "notes_moduleimpl" __tablename__ = "notes_moduleimpl"
@ -52,7 +53,6 @@ class ModuleImpl(db.Model):
secondary="notes_modules_enseignants", secondary="notes_modules_enseignants",
lazy="dynamic", lazy="dynamic",
backref="moduleimpl", backref="moduleimpl",
viewonly=True,
) )
def __repr__(self): def __repr__(self):
@ -85,7 +85,7 @@ class ModuleImpl(db.Model):
@classmethod @classmethod
def get_modimpl(cls, moduleimpl_id: int | str, dept_id: int = None) -> "ModuleImpl": def get_modimpl(cls, moduleimpl_id: int | str, dept_id: int = None) -> "ModuleImpl":
"""FormSemestre ou 404, cherche uniquement dans le département spécifié ou le courant.""" """ModuleImpl ou 404, cherche uniquement dans le département spécifié ou le courant."""
from app.models.formsemestre import FormSemestre from app.models.formsemestre import FormSemestre
if not isinstance(moduleimpl_id, int): if not isinstance(moduleimpl_id, int):
@ -187,7 +187,7 @@ class ModuleImpl(db.Model):
return allow_ens and user.id in (ens.id for ens in self.enseignants) return allow_ens and user.id in (ens.id for ens in self.enseignants)
return True return True
def can_change_ens_by(self, user: User, raise_exc=False) -> bool: def can_change_responsable(self, user: User, raise_exc=False) -> bool:
"""Check if user can modify module resp. """Check if user can modify module resp.
If raise_exc, raises exception (AccessDenied or ScoLockedSemError) if not. If raise_exc, raises exception (AccessDenied or ScoLockedSemError) if not.
= Admin, et dir des etud. (si option l'y autorise) = Admin, et dir des etud. (si option l'y autorise)
@ -208,6 +208,27 @@ class ModuleImpl(db.Model):
raise AccessDenied(f"Modification impossible pour {user}") raise AccessDenied(f"Modification impossible pour {user}")
return False return False
def can_change_ens(self, user: User | None = None, raise_exc=True) -> bool:
"""check if user can modify ens list (raise exception if not)"
if user is None, current user.
"""
user = current_user if user is None else user
if not self.formsemestre.etat:
if raise_exc:
raise ScoLockedSemError("Modification impossible: semestre verrouille")
return False
# -- check access
# admin, resp. module ou resp. semestre
if (
user.id != self.responsable_id
and not user.has_permission(Permission.EditFormSemestre)
and user.id not in (u.id for u in self.formsemestre.responsables)
):
if raise_exc:
raise AccessDenied(f"Modification impossible pour {user}")
return False
return True
def est_inscrit(self, etud: Identite) -> bool: def est_inscrit(self, etud: Identite) -> bool:
""" """
Vérifie si l'étudiant est bien inscrit au moduleimpl (même si DEM ou DEF au semestre). Vérifie si l'étudiant est bien inscrit au moduleimpl (même si DEM ou DEF au semestre).

View File

@ -33,7 +33,7 @@ from flask import g, request
from flask_login import current_user from flask_login import current_user
from app import db from app import db
from app.models import Evaluation, GroupDescr, ModuleImpl, Partition from app.models import Evaluation, GroupDescr, Identite, ModuleImpl, Partition
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
@ -160,27 +160,32 @@ def sidebar(etudid: int = None):
etudid = request.form.get("etudid", None) etudid = request.form.get("etudid", None)
if etudid is not None: if etudid is not None:
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0] etud = Identite.get_etud(etudid)
params.update(etud)
params["fiche_url"] = url_for(
"scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid
)
# compte les absences du semestre en cours # compte les absences du semestre en cours
H.append( H.append(
"""<h2 id="insidebar-etud"><a href="%(fiche_url)s" class="sidebar"> f"""<h2 id="insidebar-etud"><a href="{
<font color="#FF0000">%(civilite_str)s %(nom_disp)s</font></a> url_for(
</h2> "scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid
<b>Absences</b>""" )
% params }" class="sidebar">
<font color="#FF0000">{etud.civilite_str} {etud.nom_disp()}</font></a>
</h2>
<b>Absences</b>"""
) )
if etud["cursem"]: inscription = etud.inscription_courante()
cur_sem = etud["cursem"] if inscription:
nbabs, nbabsjust = sco_assiduites.get_assiduites_count(etudid, cur_sem) formsemestre = inscription.formsemestre
nbabs, nbabsjust = sco_assiduites.formsemestre_get_assiduites_count(
etudid, formsemestre
)
nbabsnj = nbabs - nbabsjust nbabsnj = nbabs - nbabsjust
H.append( H.append(
f"""<span title="absences du { cur_sem["date_debut"] } au { f"""<span title="absences du {
cur_sem["date_fin"] }">({ formsemestre.date_debut.strftime("%d/%m/%Y")
sco_preferences.get_preference("assi_metrique", None)}) } au {
formsemestre.date_fin.strftime("%d/%m/%Y")
}">({
sco_preferences.get_preference("assi_metrique", None)})
<br>{ nbabsjust } J., { nbabsnj } N.J.</span>""" <br>{ nbabsjust } J., { nbabsnj } N.J.</span>"""
) )
H.append("<ul>") H.append("<ul>")
@ -189,21 +194,24 @@ def sidebar(etudid: int = None):
cur_formsemestre_id = retreive_formsemestre_from_request() cur_formsemestre_id = retreive_formsemestre_from_request()
H.append( H.append(
f""" f"""
<li><a href="{ url_for('assiduites.ajout_assiduite_etud', <li><a href="{
scodoc_dept=g.scodoc_dept, etudid=etudid) url_for('assiduites.ajout_assiduite_etud',
}">Ajouter</a></li> scodoc_dept=g.scodoc_dept, etudid=etudid)
<li><a href="{ url_for('assiduites.ajout_justificatif_etud', }">Ajouter</a></li>
scodoc_dept=g.scodoc_dept, etudid=etudid, <li><a href="{
formsemestre_id=cur_formsemestre_id, url_for('assiduites.ajout_justificatif_etud',
) scodoc_dept=g.scodoc_dept, etudid=etudid,
}">Justifier</a></li> formsemestre_id=cur_formsemestre_id,
)
}">Justifier</a></li>
""" """
) )
if sco_preferences.get_preference("handle_billets_abs"): if sco_preferences.get_preference("handle_billets_abs"):
H.append( H.append(
f"""<li><a href="{ url_for('absences.billets_etud', f"""<li><a href="{
scodoc_dept=g.scodoc_dept, etudid=etudid) url_for('absences.billets_etud',
}">Billets</a></li>""" scodoc_dept=g.scodoc_dept, etudid=etudid)
}">Billets</a></li>"""
) )
H.append( H.append(
f""" f"""

File diff suppressed because it is too large Load Diff

View File

@ -268,7 +268,7 @@ def abs_notification_message(
""" """
from app.scodoc import sco_bulletins from app.scodoc import sco_bulletins
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] etud = Identite.get_etud(etudid)
# Variables accessibles dans les balises du template: %(nom_variable)s : # Variables accessibles dans les balises du template: %(nom_variable)s :
values = sco_bulletins.make_context_dict(formsemestre, etud) values = sco_bulletins.make_context_dict(formsemestre, etud)
@ -287,7 +287,7 @@ def abs_notification_message(
log("abs_notification_message: empty template, not sending message") log("abs_notification_message: empty template, not sending message")
return None return None
subject = f"""[ScoDoc] Trop d'absences pour {etud["nomprenom"]}""" subject = f"""[ScoDoc] Trop d'absences pour {etud.nomprenom}"""
msg = Message(subject, sender=email.get_from_addr(formsemestre.departement.acronym)) msg = Message(subject, sender=email.get_from_addr(formsemestre.departement.acronym))
msg.body = txt msg.body = txt
return msg return msg

View File

@ -138,21 +138,18 @@ def etud_upload_file_form(etudid):
"""Page with a form to choose and upload a file, with a description.""" """Page with a form to choose and upload a file, with a description."""
# check permission # check permission
if not can_edit_etud_archive(current_user): if not can_edit_etud_archive(current_user):
raise AccessDenied("opération non autorisée pour %s" % current_user) raise AccessDenied(f"opération non autorisée pour {current_user}")
etuds = sco_etud.get_etud_info(filled=True) etud = Identite.get_etud(etudid)
if not etuds:
raise ScoValueError("étudiant inexistant")
etud = etuds[0]
H = [ H = [
html_sco_header.sco_header( html_sco_header.sco_header(
page_title="Chargement d'un document associé à %(nomprenom)s" % etud, page_title=f"Chargement d'un document associé à {etud.nomprenom}",
), ),
"""<h2>Chargement d'un document associé à %(nomprenom)s</h2> f"""<h2>Chargement d'un document associé à {etud.nomprenom}</h2>
"""
% etud, <p>Le fichier ne doit pas dépasser {
"""<p>Le fichier ne doit pas dépasser %sMo.</p> scu.CONFIG.ETUD_MAX_FILE_SIZE // (1024 * 1024)}Mo.</p>
""" """,
% (scu.CONFIG.ETUD_MAX_FILE_SIZE // (1024 * 1024)),
] ]
tf = TrivialFormulator( tf = TrivialFormulator(
request.base_url, request.base_url,
@ -176,20 +173,13 @@ def etud_upload_file_form(etudid):
if tf[0] == 0: if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer() return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1: elif tf[0] == -1:
return flask.redirect( return flask.redirect(etud.url_fiche())
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid) data = tf[2]["datafile"].read()
) descr = tf[2]["description"]
else: filename = tf[2]["datafile"].filename
data = tf[2]["datafile"].read() etud_archive_id = (etudid,)
descr = tf[2]["description"] _store_etud_file_to_new_archive(etud_archive_id, data, filename, description=descr)
filename = tf[2]["datafile"].filename return flask.redirect(etud.url_fiche())
etud_archive_id = etud["etudid"]
_store_etud_file_to_new_archive(
etud_archive_id, data, filename, description=descr
)
return flask.redirect(
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
def _store_etud_file_to_new_archive( def _store_etud_file_to_new_archive(
@ -209,23 +199,20 @@ def etud_delete_archive(etudid, archive_name, dialog_confirmed=False):
# check permission # check permission
if not can_edit_etud_archive(current_user): if not can_edit_etud_archive(current_user):
raise AccessDenied(f"opération non autorisée pour {current_user}") raise AccessDenied(f"opération non autorisée pour {current_user}")
etuds = sco_etud.get_etud_info(filled=True) etud = Identite.get_etud(etudid)
if not etuds: etud_archive_id = etudid
raise ScoValueError("étudiant inexistant")
etud = etuds[0]
etud_archive_id = etud["etudid"]
archive_id = ETUDS_ARCHIVER.get_id_from_name( archive_id = ETUDS_ARCHIVER.get_id_from_name(
etud_archive_id, archive_name, dept_id=etud["dept_id"] etud_archive_id, archive_name, dept_id=etud.dept_id
) )
if not dialog_confirmed: if not dialog_confirmed:
return scu.confirm_dialog( return scu.confirm_dialog(
"""<h2>Confirmer la suppression des fichiers ?</h2> f"""<h2>Confirmer la suppression des fichiers ?</h2>
<p>Fichier associé le %s à l'étudiant %s</p> <p>Fichier associé le {
<p>La suppression sera définitive.</p>""" ETUDS_ARCHIVER.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M")
% ( } à l'étudiant {etud.nomprenom}
ETUDS_ARCHIVER.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M"), </p>
etud["nomprenom"], <p>La suppression sera définitive.</p>
), """,
dest_url="", dest_url="",
cancel_url=url_for( cancel_url=url_for(
"scolar.fiche_etud", "scolar.fiche_etud",
@ -236,22 +223,17 @@ def etud_delete_archive(etudid, archive_name, dialog_confirmed=False):
parameters={"etudid": etudid, "archive_name": archive_name}, parameters={"etudid": etudid, "archive_name": archive_name},
) )
ETUDS_ARCHIVER.delete_archive(archive_id, dept_id=etud["dept_id"]) ETUDS_ARCHIVER.delete_archive(archive_id, dept_id=etud.dept_id)
flash("Archive supprimée") flash("Archive supprimée")
return flask.redirect( return flask.redirect(etud.url_fiche())
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
def etud_get_archived_file(etudid, archive_name, filename): def etud_get_archived_file(etudid, archive_name, filename):
"""Send file to client.""" """Send file to client."""
etuds = sco_etud.get_etud_info(etudid=etudid, filled=True) etud = Identite.get_etud(etudid)
if not etuds: etud_archive_id = etud.id
raise ScoValueError("étudiant inexistant")
etud = etuds[0]
etud_archive_id = etud["etudid"]
return ETUDS_ARCHIVER.get_archived_file( return ETUDS_ARCHIVER.get_archived_file(
etud_archive_id, archive_name, filename, dept_id=etud["dept_id"] etud_archive_id, archive_name, filename, dept_id=etud.dept_id
) )

View File

@ -1273,29 +1273,24 @@ def do_formsemestre_clone(
log(f"created formsemestre {formsemestre_id}") log(f"created formsemestre {formsemestre_id}")
formsemestre: FormSemestre = db.session.get(FormSemestre, formsemestre_id) formsemestre: FormSemestre = db.session.get(FormSemestre, formsemestre_id)
# 2- create moduleimpls # 2- create moduleimpls
mods_orig = sco_moduleimpl.moduleimpl_list(formsemestre_id=orig_formsemestre_id) modimpl_orig: ModuleImpl
for mod_orig in mods_orig: for modimpl_orig in formsemestre_orig.modimpls:
args = mod_orig.copy() args = modimpl_orig.to_dict(with_module=False)
args["formsemestre_id"] = formsemestre_id args["formsemestre_id"] = formsemestre_id
mid = sco_moduleimpl.do_moduleimpl_create(args) modimpl_new = ModuleImpl.create_from_dict(args)
# copy notes_modules_enseignants db.session.flush()
ens = sco_moduleimpl.do_ens_list( # copy enseignants
args={"moduleimpl_id": mod_orig["moduleimpl_id"]} for ens in modimpl_orig.enseignants:
) modimpl_new.enseignants.append(ens)
for e in ens: db.session.add(modimpl_new)
args = e.copy()
args["moduleimpl_id"] = mid
sco_moduleimpl.do_ens_create(args)
# optionally, copy evaluations # optionally, copy evaluations
if clone_evaluations: if clone_evaluations:
for e in Evaluation.query.filter_by( for e in Evaluation.query.filter_by(moduleimpl_id=modimpl_orig.id):
moduleimpl_id=mod_orig["moduleimpl_id"]
):
# copie en enlevant la date # copie en enlevant la date
new_eval = e.clone( new_eval = e.clone(
not_copying=("date_debut", "date_fin", "moduleimpl_id") not_copying=("date_debut", "date_fin", "moduleimpl_id")
) )
new_eval.moduleimpl_id = mid new_eval.moduleimpl_id = modimpl_new.id
# Copie les poids APC de l'évaluation # Copie les poids APC de l'évaluation
new_eval.set_ue_poids_dict(e.get_ue_poids_dict()) new_eval.set_ue_poids_dict(e.get_ue_poids_dict())
db.session.commit() db.session.commit()

View File

@ -37,7 +37,6 @@ 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_formsemestre
from app.scodoc import sco_cache from app.scodoc import sco_cache
import app.scodoc.sco_utils as scu
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.sco_exceptions import ScoValueError, AccessDenied from app.scodoc.sco_exceptions import ScoValueError, AccessDenied
@ -362,45 +361,3 @@ def do_ens_create(args):
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
r = _modules_enseignantsEditor.create(cnx, args) r = _modules_enseignantsEditor.create(cnx, args)
return r return r
def can_change_module_resp(moduleimpl_id):
"""Check if current user can modify module resp. (raise exception if not).
= Admin, et dir des etud. (si option l'y autorise)
"""
M = moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
# -- check lock
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]:
raise ScoValueError("Modification impossible: semestre verrouille")
# -- check access
# admin ou resp. semestre avec flag resp_can_change_resp
if not current_user.has_permission(Permission.EditFormSemestre) and (
(current_user.id not in sem["responsables"]) or (not sem["resp_can_change_ens"])
):
raise AccessDenied(f"Modification impossible pour {current_user}")
return M, sem
def can_change_ens(moduleimpl_id, raise_exc=True):
"check if current user can modify ens list (raise exception if not)"
M = moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0]
# -- check lock
sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"])
if not sem["etat"]:
if raise_exc:
raise ScoValueError("Modification impossible: semestre verrouille")
else:
return False
# -- check access
# admin, resp. module ou resp. semestre
if (
current_user.id != M["responsable_id"]
and not current_user.has_permission(Permission.EditFormSemestre)
and (current_user.id not in sem["responsables"])
):
if raise_exc:
raise AccessDenied("Modification impossible pour %s" % current_user)
else:
return False
return M, sem

View File

@ -244,7 +244,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
<span class="blacktt">({module_resp.user_name})</span> <span class="blacktt">({module_resp.user_name})</span>
""", """,
] ]
if modimpl.can_change_ens_by(current_user): if modimpl.can_change_responsable(current_user):
H.append( H.append(
f"""<a class="stdlink" href="{url_for("notes.edit_moduleimpl_resp", f"""<a class="stdlink" href="{url_for("notes.edit_moduleimpl_resp",
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id) scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id)
@ -253,14 +253,15 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
H.append("""</td><td>""") H.append("""</td><td>""")
H.append(", ".join([u.get_nomprenom() for u in modimpl.enseignants])) H.append(", ".join([u.get_nomprenom() for u in modimpl.enseignants]))
H.append("""</td><td>""") H.append("""</td><td>""")
try: if modimpl.can_change_ens(raise_exc=False):
sco_moduleimpl.can_change_ens(moduleimpl_id)
H.append( H.append(
"""<a class="stdlink" href="edit_enseignants_form?moduleimpl_id=%s">modifier les enseignants</a>""" f"""<a class="stdlink" href="{
% moduleimpl_id url_for("notes.edit_enseignants_form",
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id
)
}">modifier les enseignants</a>"""
) )
except:
pass
H.append("""</td></tr>""") H.append("""</td></tr>""")
# 2ieme ligne: Semestre, Coef # 2ieme ligne: Semestre, Coef

View File

@ -961,12 +961,15 @@ def formsemestre_custommenu_edit(formsemestre_id):
@scodoc7func @scodoc7func
def edit_enseignants_form(moduleimpl_id): def edit_enseignants_form(moduleimpl_id):
"modif liste enseignants/moduleimpl" "modif liste enseignants/moduleimpl"
M, sem = sco_moduleimpl.can_change_ens(moduleimpl_id) modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
modimpl.can_change_ens(raise_exc=True)
# -- # --
header = html_sco_header.html_sem_header( header = html_sco_header.html_sem_header(
'Enseignants du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>' f"""Enseignants du <a href="{
% (moduleimpl_id, M["module"]["titre"]), url_for("notes.moduleimpl_status",
page_title="Enseignants du module %s" % M["module"]["titre"], scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
}">module {modimpl.module.titre or modimpl.module.code}</a>""",
page_title=f"Enseignants du module {modimpl.module.titre or modimpl.module.code}",
javascripts=["libjs/AutoSuggest.js"], javascripts=["libjs/AutoSuggest.js"],
cssstyles=["css/autosuggest_inquisitor.css"], cssstyles=["css/autosuggest_inquisitor.css"],
bodyOnLoad="init_tf_form('')", bodyOnLoad="init_tf_form('')",
@ -981,21 +984,18 @@ def edit_enseignants_form(moduleimpl_id):
allowed_user_names = list(uid2display.values()) allowed_user_names = list(uid2display.values())
H = [ H = [
"<ul><li><b>%s</b> (responsable)</li>" f"""<ul><li><b>{
% uid2display.get(M["responsable_id"], M["responsable_id"]) uid2display.get(modimpl.responsable_id, modimpl.responsable_id)
}</b> (responsable)</li>"""
] ]
for ens in M["ens"]: u: User
u = db.session.get(User, ens["ens_id"]) for u in modimpl.enseignants:
if u:
nom = u.get_nomcomplet()
else:
nom = "? (compte inconnu)"
H.append( H.append(
f""" f"""
<li>{nom} (<a class="stdlink" href="{ <li>{u.get_nomcomplet()} (<a class="stdlink" href="{
url_for('notes.edit_enseignants_form_delete', url_for('notes.edit_enseignants_form_delete',
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id, scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id,
ens_id=ens["ens_id"]) ens_id=u.id)
}">supprimer</a>) }">supprimer</a>)
</li>""" </li>"""
) )
@ -1006,7 +1006,7 @@ def edit_enseignants_form(moduleimpl_id):
<p class="help">Pour changer le responsable du module, passez par la <p class="help">Pour changer le responsable du module, passez par la
page "<a class="stdlink" href="{ page "<a class="stdlink" href="{
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept, url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
formsemestre_id=M["formsemestre_id"]) formsemestre_id=modimpl.formsemestre_id)
}">Modification du semestre</a>", }">Modification du semestre</a>",
accessible uniquement au responsable de la formation (chef de département) accessible uniquement au responsable de la formation (chef de département)
</p> </p>
@ -1061,8 +1061,8 @@ def edit_enseignants_form(moduleimpl_id):
else: else:
# et qu'il n'est pas deja: # et qu'il n'est pas deja:
if ( if (
ens_id in [x["ens_id"] for x in M["ens"]] ens_id in (x.id for x in modimpl.enseignants)
or ens_id == M["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_id} déjà dans la liste !</p>"""
@ -1090,7 +1090,7 @@ def edit_moduleimpl_resp(moduleimpl_id: int):
Accessible par Admin et dir des etud si flag resp_can_change_ens Accessible par Admin et dir des etud si flag resp_can_change_ens
""" """
modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id) modimpl: ModuleImpl = ModuleImpl.query.get_or_404(moduleimpl_id)
modimpl.can_change_ens_by(current_user, raise_exc=True) # access control modimpl.can_change_responsable(current_user, raise_exc=True) # access control
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
f"""Modification du responsable du <a href="{ f"""Modification du responsable du <a href="{
@ -1372,22 +1372,17 @@ def edit_enseignants_form_delete(moduleimpl_id, ens_id: int):
ens_id: user.id ens_id: user.id
""" """
M, _ = sco_moduleimpl.can_change_ens(moduleimpl_id) modimpl = ModuleImpl.get_modimpl(moduleimpl_id)
modimpl.can_change_ens(raise_exc=True)
# search ens_id # search ens_id
ok = False ens: User | None = None
for ens in M["ens"]: for ens in modimpl.enseignants:
if ens["ens_id"] == ens_id: if ens.id == ens_id:
ok = True
break break
if not ok: if ens is None:
raise ScoValueError(f"invalid ens_id ({ens_id})") raise ScoValueError(f"invalid ens_id ({ens_id})")
ndb.SimpleQuery( modimpl.enseignants.remove(ens)
"""DELETE FROM notes_modules_enseignants db.session.commit()
WHERE moduleimpl_id = %(moduleimpl_id)s
AND ens_id = %(ens_id)s
""",
{"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",
@ -1399,18 +1394,6 @@ def edit_enseignants_form_delete(moduleimpl_id, ens_id: int):
# --- Gestion des inscriptions aux semestres # --- Gestion des inscriptions aux semestres
# Ancienne API, pas certain de la publier en ScoDoc8
# sco_publish(
# "/do_formsemestre_inscription_create",
# sco_formsemestre_inscriptions.do_formsemestre_inscription_create,
# Permission.EtudInscrit,
# )
# sco_publish(
# "/do_formsemestre_inscription_edit",
# sco_formsemestre_inscriptions.do_formsemestre_inscription_edit,
# Permission.EtudInscrit,
# )
sco_publish( sco_publish(
"/do_formsemestre_inscription_list", "/do_formsemestre_inscription_list",
sco_formsemestre_inscriptions.do_formsemestre_inscription_list, sco_formsemestre_inscriptions.do_formsemestre_inscription_list,

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.6.92" SCOVERSION = "9.6.93"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"