forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -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))
|
||||||
|
@ -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:
|
||||||
|
@ -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).
|
||||||
|
@ -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
@ -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
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user