forked from ScoDoc/ScoDoc
Backend 'FormSemestre': début de modernisation + fix regressions
This commit is contained in:
parent
7473334387
commit
24e144f372
@ -157,6 +157,24 @@ class User(UserMixin, ScoDocModel):
|
||||
def __str__(self):
|
||||
return self.user_name
|
||||
|
||||
@classmethod
|
||||
def get_user(cls, user_id: int | str, accept_none=False):
|
||||
"""Get user by id, user_name or User instance, ou 404 (ou None si accept_none)
|
||||
If user_id == -1, returns None (without exception)
|
||||
"""
|
||||
query = None
|
||||
if isinstance(user_id, str):
|
||||
query = db.session.query(cls).filter_by(user_name=user_id)
|
||||
elif isinstance(user_id, int):
|
||||
if user_id == -1:
|
||||
return None
|
||||
query = db.session.query(cls).filter_by(id=user_id)
|
||||
elif isinstance(user_id, User):
|
||||
return user_id
|
||||
else:
|
||||
raise ValueError("invalid user_id")
|
||||
return query.first_or_404() if not accept_none else query.first()
|
||||
|
||||
def set_password(self, password):
|
||||
"Set password"
|
||||
current_app.logger.info(f"set_password({self})")
|
||||
|
@ -126,7 +126,7 @@ class ScoDocModel(db.Model):
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, oid: int, accept_none=False):
|
||||
"""Instance du modèle ou ou 404 (ou None si accept_none),
|
||||
"""Instance du modèle ou 404 (ou None si accept_none),
|
||||
cherche uniquement dans le département courant.
|
||||
|
||||
Ne fonctionne que si le modèle a un attribut dept_id
|
||||
|
@ -139,7 +139,7 @@ class FormSemestre(models.ScoDocModel):
|
||||
|
||||
# Relations:
|
||||
etapes = db.relationship(
|
||||
"FormSemestreEtape", cascade="all,delete", backref="formsemestre"
|
||||
"FormSemestreEtape", cascade="all,delete-orphan", backref="formsemestre"
|
||||
)
|
||||
modimpls = db.relationship(
|
||||
"ModuleImpl",
|
||||
@ -242,11 +242,9 @@ class FormSemestre(models.ScoDocModel):
|
||||
args["dept_id"] = g.scodoc_dept_id
|
||||
formsemestre: "FormSemestre" = cls.create_from_dict(args)
|
||||
db.session.flush()
|
||||
for etape in args["etapes"]:
|
||||
for etape in args.get("etapes") or []:
|
||||
formsemestre.add_etape(etape)
|
||||
db.session.commit()
|
||||
for u in args["responsables"]:
|
||||
formsemestre.responsables.append(u)
|
||||
# create default partition
|
||||
partition = Partition(
|
||||
formsemestre=formsemestre, partition_name=None, numero=1000000
|
||||
@ -281,11 +279,26 @@ class FormSemestre(models.ScoDocModel):
|
||||
if "date_fin" in args:
|
||||
args["date_fin"] = scu.convert_fr_date(args["date_fin"])
|
||||
if "etat" in args:
|
||||
args["etat"] = bool(args["etat"])
|
||||
if args["etat"] is None:
|
||||
del args["etat"]
|
||||
else:
|
||||
args["etat"] = bool(args["etat"])
|
||||
if "bul_bgcolor" in args:
|
||||
args["bul_bgcolor"] = args.get("bul_bgcolor") or "white"
|
||||
if "titre" in args:
|
||||
args["titre"] = args.get("titre") or "sans titre"
|
||||
if "capacite_accueil" in args: # peut être un nombre, "" ou None
|
||||
try:
|
||||
args["capacite_accueil"] = (
|
||||
int(args["capacite_accueil"])
|
||||
if args["capacite_accueil"] not in ("", None)
|
||||
else None
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("capacite_accueil invalide") from exc
|
||||
if "responsables" in args: # peut être liste d'uid ou de user_name ou de User
|
||||
resp_users = [User.get_user(u) for u in args["responsables"]]
|
||||
args["responsables"] = [u for u in resp_users if u is not None]
|
||||
return args
|
||||
|
||||
@classmethod
|
||||
@ -1346,6 +1359,7 @@ class FormSemestre(models.ScoDocModel):
|
||||
},
|
||||
)
|
||||
db.session.commit()
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id=self.id)
|
||||
|
||||
def etud_validations_description_html(self, etudid: int) -> str:
|
||||
"""Description textuelle des validations de jury de cet étudiant dans ce semestre"""
|
||||
@ -1461,6 +1475,15 @@ class FormSemestreEtape(models.ScoDocModel):
|
||||
# etape_apo aurait du etre not null, mais oublié
|
||||
etape_apo = db.Column(db.String(APO_CODE_STR_LEN), index=True)
|
||||
|
||||
@classmethod
|
||||
def create_from_apovdi(
|
||||
cls, formsemestre_id: int, apovdi: ApoEtapeVDI
|
||||
) -> "FormSemestreEtape":
|
||||
"Crée une instance à partir d'un objet ApoEtapeVDI. Ajoute à la session."
|
||||
etape = cls(formsemestre_id=formsemestre_id, etape_apo=str(apovdi))
|
||||
db.session.add(etape)
|
||||
return etape
|
||||
|
||||
def __bool__(self):
|
||||
"Etape False if code empty"
|
||||
return self.etape_apo is not None and (len(self.etape_apo) > 0)
|
||||
|
@ -39,7 +39,7 @@ import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
from app.models import Departement
|
||||
from app.models import Formation, FormSemestre
|
||||
from app.scodoc import sco_cache, codes_cursus, sco_preferences
|
||||
from app.scodoc import codes_cursus, sco_preferences
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc.codes_cursus import NO_SEMESTRE_ID
|
||||
from app.scodoc.sco_exceptions import ScoInvalidIdType, ScoValueError
|
||||
@ -231,63 +231,7 @@ def etapes_apo_str(etapes):
|
||||
return ", ".join([str(x) for x in etapes])
|
||||
|
||||
|
||||
def do_formsemestre_create( # DEPRECATED, use FormSemestre.create_formsemestre()
|
||||
args, silent=False
|
||||
):
|
||||
"create a formsemestre"
|
||||
from app.models import ScolarNews
|
||||
from app.scodoc import sco_groups
|
||||
|
||||
log("Warning: do_formsemestre_create is deprecated")
|
||||
cnx = ndb.GetDBConnexion()
|
||||
formsemestre_id = _formsemestreEditor.create(cnx, args)
|
||||
if args["etapes"]:
|
||||
args["formsemestre_id"] = formsemestre_id
|
||||
write_formsemestre_etapes(args)
|
||||
if args["responsables"]:
|
||||
args["formsemestre_id"] = formsemestre_id
|
||||
_write_formsemestre_responsables(args)
|
||||
|
||||
# create default partition
|
||||
partition_id = sco_groups.partition_create(
|
||||
formsemestre_id,
|
||||
default=True,
|
||||
redirect=0,
|
||||
numero=1000000, # à la fin
|
||||
)
|
||||
_ = sco_groups.create_group(partition_id, default=True)
|
||||
|
||||
# news
|
||||
if "titre" not in args:
|
||||
args["titre"] = "sans titre"
|
||||
args["formsemestre_id"] = formsemestre_id
|
||||
args["url"] = "Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % args
|
||||
if not silent:
|
||||
ScolarNews.add(
|
||||
typ=ScolarNews.NEWS_SEM,
|
||||
text='Création du semestre <a href="%(url)s">%(titre)s</a>' % args,
|
||||
url=args["url"],
|
||||
max_frequency=0,
|
||||
)
|
||||
return formsemestre_id
|
||||
|
||||
|
||||
def do_formsemestre_edit(sem, cnx=None, **kw):
|
||||
"""Apply modifications to formsemestre.
|
||||
Update etapes and resps. Invalidate cache."""
|
||||
if not cnx:
|
||||
cnx = ndb.GetDBConnexion()
|
||||
|
||||
_formsemestreEditor.edit(cnx, sem, **kw)
|
||||
write_formsemestre_etapes(sem)
|
||||
_write_formsemestre_responsables(sem)
|
||||
|
||||
sco_cache.invalidate_formsemestre(
|
||||
formsemestre_id=sem["formsemestre_id"]
|
||||
) # > modif formsemestre
|
||||
|
||||
|
||||
def read_formsemestre_responsables(formsemestre_id: int) -> list[int]: # py3.9+ syntax
|
||||
def read_formsemestre_responsables(formsemestre_id: int) -> list[int]: # OBSOLETE
|
||||
"""recupere liste des responsables de ce semestre
|
||||
:returns: liste d'id
|
||||
"""
|
||||
@ -301,14 +245,6 @@ def read_formsemestre_responsables(formsemestre_id: int) -> list[int]: # py3.9+
|
||||
return [x["responsable_id"] for x in r]
|
||||
|
||||
|
||||
def _write_formsemestre_responsables(sem): # TODO old, à ré-écrire avec models
|
||||
if sem and "responsables" in sem:
|
||||
sem["responsables"] = [
|
||||
uid for uid in sem["responsables"] if (uid is not None) and (uid != -1)
|
||||
]
|
||||
_write_formsemestre_aux(sem, "responsables", "responsable_id")
|
||||
|
||||
|
||||
# ---------------------- Coefs des UE
|
||||
|
||||
_formsemestre_uecoef_editor = ndb.EditableTable(
|
||||
|
@ -50,7 +50,7 @@ from app.models import (
|
||||
UniteEns,
|
||||
)
|
||||
from app.models.formations import Formation
|
||||
from app.models.formsemestre import FormSemestre
|
||||
from app.models.formsemestre import FormSemestre, FormSemestreEtape
|
||||
from app.models.but_refcomp import ApcParcours
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -143,7 +143,7 @@ def can_edit_sem(formsemestre_id: int = None, sem=None):
|
||||
return sem
|
||||
|
||||
|
||||
resp_fields = [
|
||||
RESP_FIELDS = [
|
||||
"responsable_id",
|
||||
"responsable_id2",
|
||||
"responsable_id3",
|
||||
@ -205,7 +205,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
f"inconnu numéro {modimpl.responsable_id} resp. de {modimpl.id} !",
|
||||
)
|
||||
for index, resp in enumerate(formsemestre.responsables):
|
||||
initvalues[resp_fields[index]] = uid2display.get(resp.id)
|
||||
initvalues[RESP_FIELDS[index]] = uid2display.get(resp.id)
|
||||
group_tous = formsemestre.get_default_group()
|
||||
if group_tous:
|
||||
initvalues["edt_promo_id"] = group_tous.edt_id or ""
|
||||
@ -317,7 +317,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
},
|
||||
},
|
||||
)
|
||||
for index, field in enumerate(resp_fields)
|
||||
for index, field in enumerate(RESP_FIELDS)
|
||||
],
|
||||
(
|
||||
"titre",
|
||||
@ -755,7 +755,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
"input_type": "text_suggest",
|
||||
"size": 50,
|
||||
"withcheckbox": True,
|
||||
"title": "%s %s" % (mod.code or "", mod.titre or ""),
|
||||
"title": f"""{mod.code or ""} {mod.titre or ""}""",
|
||||
"allowed_values": allowed_user_names,
|
||||
"template": itemtemplate,
|
||||
"text_suggest_options": {
|
||||
@ -875,157 +875,106 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
formsemestre_id=formsemestre.id,
|
||||
)
|
||||
)
|
||||
else:
|
||||
return redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
|
||||
else:
|
||||
if tf[2]["gestion_compensation_lst"]:
|
||||
tf[2]["gestion_compensation"] = True
|
||||
else:
|
||||
tf[2]["gestion_compensation"] = False
|
||||
if tf[2]["gestion_semestrielle_lst"]:
|
||||
tf[2]["gestion_semestrielle"] = True
|
||||
else:
|
||||
tf[2]["gestion_semestrielle"] = False
|
||||
if tf[2]["bul_publish_xml_lst"]:
|
||||
tf[2]["bul_hide_xml"] = False
|
||||
else:
|
||||
tf[2]["bul_hide_xml"] = True
|
||||
# remap les identifiants de responsables:
|
||||
for field in resp_fields:
|
||||
resp = User.get_user_from_nomplogin(tf[2][field])
|
||||
tf[2][field] = resp.id if resp else -1
|
||||
tf[2]["responsables"] = []
|
||||
for field in resp_fields:
|
||||
if tf[2][field]:
|
||||
tf[2]["responsables"].append(tf[2][field])
|
||||
for module_id in tf[2]["tf-checked"]:
|
||||
mod_resp = User.get_user_from_nomplogin(tf[2][module_id])
|
||||
if mod_resp is None:
|
||||
# Si un module n'a pas de responsable (ou inconnu),
|
||||
# l'affecte au 1er directeur des etudes:
|
||||
mod_resp_id = tf[2]["responsable_id"]
|
||||
else:
|
||||
mod_resp_id = mod_resp.id
|
||||
tf[2][module_id] = mod_resp_id
|
||||
|
||||
# etapes:
|
||||
tf[2]["etapes"] = []
|
||||
if etapes: # menus => case supplementaire pour saisie manuelle, indicée 0
|
||||
start_i = 0
|
||||
else:
|
||||
start_i = 1
|
||||
for n in range(start_i, scu.EDIT_NB_ETAPES + 1):
|
||||
tf[2]["etapes"].append(
|
||||
ApoEtapeVDI(
|
||||
etape=tf[2]["etape_apo" + str(n)], vdi=tf[2]["vdi_apo" + str(n)]
|
||||
)
|
||||
)
|
||||
# Modules sélectionnés:
|
||||
# (retire le "MI" du début du nom de champs)
|
||||
module_ids_checked = [int(x[2:]) for x in tf[2]["tf-checked"]]
|
||||
_formsemestre_check_ue_bonus_unicity(module_ids_checked)
|
||||
if not edit:
|
||||
if is_apc:
|
||||
_formsemestre_check_module_list(
|
||||
module_ids_checked, tf[2]["semestre_id"]
|
||||
)
|
||||
# création du semestre
|
||||
formsemestre_id = sco_formsemestre.do_formsemestre_create(tf[2])
|
||||
# création des modules
|
||||
for module_id in module_ids_checked:
|
||||
modargs = {
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"responsable_id": tf[2][f"MI{module_id}"],
|
||||
}
|
||||
_ = ModuleImpl.create_from_dict(modargs)
|
||||
else:
|
||||
# Modification du semestre:
|
||||
# on doit creer les modules nouvellement selectionnés
|
||||
# modifier ceux à modifier, et DETRUIRE ceux qui ne sont plus selectionnés.
|
||||
# Note: la destruction échouera s'il y a des objets dépendants
|
||||
# (eg des évaluations définies)
|
||||
module_ids_tocreate = [
|
||||
x for x in module_ids_checked if not x in module_ids_existing
|
||||
]
|
||||
if is_apc:
|
||||
_formsemestre_check_module_list(
|
||||
module_ids_tocreate, tf[2]["semestre_id"]
|
||||
)
|
||||
# modules existants à modifier
|
||||
module_ids_toedit = [
|
||||
x for x in module_ids_checked if x in module_ids_existing
|
||||
]
|
||||
# modules à détruire
|
||||
module_ids_todelete = [
|
||||
x for x in module_ids_existing if not x in module_ids_checked
|
||||
]
|
||||
#
|
||||
sco_formsemestre.do_formsemestre_edit(tf[2])
|
||||
#
|
||||
msg = []
|
||||
for module_id in module_ids_tocreate:
|
||||
modargs = {
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre.id,
|
||||
"responsable_id": tf[2]["MI" + str(module_id)],
|
||||
}
|
||||
modimpl = ModuleImpl.create_from_dict(modargs)
|
||||
assert modimpl.module_id == module_id
|
||||
mod = modimpl.module
|
||||
msg += [f"""création de {mod.code or "?"} ({mod.titre or "?"})"""]
|
||||
# INSCRIPTIONS DES ETUDIANTS
|
||||
group_id = tf[2][f"{module_id}!group_id"]
|
||||
log(f"""inscription module: {module_id}!group_id = '{group_id}'""")
|
||||
if group_id:
|
||||
etudids = [
|
||||
x["etudid"] for x in sco_groups.get_group_members(group_id)
|
||||
]
|
||||
log(
|
||||
"inscription module:module_id=%s,moduleimpl_id=%s: %s"
|
||||
% (module_id, modimpl.id, etudids)
|
||||
)
|
||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||
modimpl.id,
|
||||
formsemestre.id,
|
||||
etudids,
|
||||
)
|
||||
msg += [
|
||||
"inscription de %d étudiants au module %s"
|
||||
% (len(etudids), mod["code"] or "(module sans code)")
|
||||
]
|
||||
else:
|
||||
log(
|
||||
"inscription module:module_id=%s,moduleimpl_id=%s: aucun etudiant inscrit"
|
||||
% (module_id, modimpl.id)
|
||||
)
|
||||
#
|
||||
ok, diag = formsemestre_delete_moduleimpls(
|
||||
formsemestre.id, module_ids_todelete
|
||||
)
|
||||
msg += diag
|
||||
for module_id in module_ids_toedit:
|
||||
moduleimpl_id = sco_moduleimpl.moduleimpl_list(
|
||||
formsemestre_id=formsemestre.id, module_id=module_id
|
||||
)[0]["moduleimpl_id"]
|
||||
modargs = {
|
||||
"moduleimpl_id": moduleimpl_id,
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre.id,
|
||||
"responsable_id": tf[2]["MI" + str(module_id)],
|
||||
}
|
||||
sco_moduleimpl.do_moduleimpl_edit(
|
||||
modargs, formsemestre_id=formsemestre.id
|
||||
)
|
||||
# --- Association des parcours
|
||||
if formsemestre is None:
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
return redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
|
||||
# Edition ou modification du semestre
|
||||
tf[2]["gestion_compensation"] = bool(tf[2]["gestion_compensation_lst"])
|
||||
tf[2]["gestion_semestrielle"] = bool(tf[2]["gestion_semestrielle_lst"])
|
||||
tf[2]["bul_hide_xml"] = not bool(tf[2]["bul_publish_xml_lst"])
|
||||
_remap_resp_modimpls(tf[2])
|
||||
if "parcours" in tf[2]:
|
||||
formsemestre.parcours = [
|
||||
tf[2]["parcours"] = [
|
||||
db.session.get(ApcParcours, int(parcour_id_str))
|
||||
for parcour_id_str in tf[2]["parcours"]
|
||||
]
|
||||
# --- Id edt du groupe par défault
|
||||
# Modules sélectionnés:
|
||||
# (retire le "MI" du début du nom de champs)
|
||||
module_ids_checked = [int(x[2:]) for x in tf[2]["tf-checked"]]
|
||||
_formsemestre_check_ue_bonus_unicity(module_ids_checked)
|
||||
if not edit:
|
||||
if is_apc:
|
||||
_formsemestre_check_module_list(module_ids_checked, tf[2]["semestre_id"])
|
||||
# création du semestre
|
||||
formsemestre = FormSemestre.create_formsemestre(tf[2])
|
||||
# création des modules
|
||||
for module_id in module_ids_checked:
|
||||
modargs = {
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre.id,
|
||||
"responsable_id": tf[2][f"MI{module_id}"],
|
||||
}
|
||||
_ = ModuleImpl.create_from_dict(modargs)
|
||||
else:
|
||||
# Modification du semestre:
|
||||
# on doit creer les modules nouvellement selectionnés
|
||||
# modifier ceux à modifier, et DETRUIRE ceux qui ne sont plus selectionnés.
|
||||
# Note: la destruction échouera s'il y a des objets dépendants
|
||||
# (eg des évaluations définies)
|
||||
module_ids_tocreate = [
|
||||
x for x in module_ids_checked if not x in module_ids_existing
|
||||
]
|
||||
if is_apc:
|
||||
_formsemestre_check_module_list(module_ids_tocreate, tf[2]["semestre_id"])
|
||||
# modules existants à modifier
|
||||
module_ids_toedit = [x for x in module_ids_checked if x in module_ids_existing]
|
||||
# modules à détruire
|
||||
module_ids_todelete = [
|
||||
x for x in module_ids_existing if not x in module_ids_checked
|
||||
]
|
||||
#
|
||||
formsemestre.from_dict(tf[2])
|
||||
sco_cache.invalidate_formsemestre(formsemestre.id)
|
||||
#
|
||||
msg = []
|
||||
for module_id in module_ids_tocreate:
|
||||
modargs = {
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre.id,
|
||||
"responsable_id": tf[2]["MI" + str(module_id)],
|
||||
}
|
||||
modimpl = ModuleImpl.create_from_dict(modargs)
|
||||
assert modimpl.module_id == module_id
|
||||
mod = modimpl.module
|
||||
msg += [f"""création de {mod.code or "?"} ({mod.titre or "?"})"""]
|
||||
# INSCRIPTIONS DES ETUDIANTS
|
||||
group_id = tf[2][f"{module_id}!group_id"]
|
||||
log(f"""inscription module: {module_id}!group_id = '{group_id}'""")
|
||||
if group_id:
|
||||
etudids = [x["etudid"] for x in sco_groups.get_group_members(group_id)]
|
||||
log(
|
||||
f"""inscription module:module_id={module_id},moduleimpl_id={
|
||||
modimpl.id}: {etudids}"""
|
||||
)
|
||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||
modimpl.id,
|
||||
formsemestre.id,
|
||||
etudids,
|
||||
)
|
||||
msg += [
|
||||
f"""inscription de {len(etudids)} étudiants au module {
|
||||
mod.code or "(module sans code)"}"""
|
||||
]
|
||||
else:
|
||||
log(
|
||||
f"""inscription module:module_id={module_id},moduleimpl_id={
|
||||
modimpl.id}: aucun etudiant inscrit"""
|
||||
)
|
||||
#
|
||||
ok, diag = formsemestre_delete_moduleimpls(formsemestre.id, module_ids_todelete)
|
||||
msg += diag
|
||||
for module_id in module_ids_toedit:
|
||||
moduleimpl_id = sco_moduleimpl.moduleimpl_list(
|
||||
formsemestre_id=formsemestre.id, module_id=module_id
|
||||
)[0]["moduleimpl_id"]
|
||||
modargs = {
|
||||
"moduleimpl_id": moduleimpl_id,
|
||||
"module_id": module_id,
|
||||
"formsemestre_id": formsemestre.id,
|
||||
"responsable_id": tf[2]["MI" + str(module_id)],
|
||||
}
|
||||
sco_moduleimpl.do_moduleimpl_edit(modargs, formsemestre_id=formsemestre.id)
|
||||
# --- Etapes
|
||||
_set_apo_etapes(formsemestre, tf[2], etapes)
|
||||
# --- id edt du groupe par défault
|
||||
if "edt_promo_id" in tf[2]:
|
||||
group_tous = formsemestre.get_default_group()
|
||||
if group_tous:
|
||||
@ -1041,6 +990,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
formsemestre.update_inscriptions_parcours_from_groups()
|
||||
# --- Fin
|
||||
if edit:
|
||||
log(f"""formsemestre_edit: {formsemestre}""")
|
||||
if msg:
|
||||
return f"""
|
||||
<div class="ue_warning"><span>Attention !<ul>
|
||||
@ -1057,25 +1007,68 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
||||
}">retour au tableau de bord</a>
|
||||
"""
|
||||
else:
|
||||
flash("Semestre modifié")
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
check_parcours=0,
|
||||
)
|
||||
)
|
||||
else:
|
||||
flash("Nouveau semestre créé")
|
||||
flash("Semestre modifié")
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
check_parcours=0,
|
||||
)
|
||||
)
|
||||
flash("Nouveau semestre créé")
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _remap_resp_modimpls(args: dict):
|
||||
"remap les identifiants de responsables de modules"
|
||||
for field in RESP_FIELDS:
|
||||
resp = User.get_user_from_nomplogin(args[field])
|
||||
args[field] = resp.id if resp else -1
|
||||
args["responsables"] = []
|
||||
for field in RESP_FIELDS:
|
||||
if args[field]:
|
||||
args["responsables"].append(args[field])
|
||||
for module_id in args["tf-checked"]:
|
||||
mod_resp = User.get_user_from_nomplogin(args[module_id])
|
||||
if mod_resp is None:
|
||||
# Si un module n'a pas de responsable (ou inconnu),
|
||||
# l'affecte au 1er directeur des etudes:
|
||||
mod_resp_id = args["responsable_id"]
|
||||
else:
|
||||
mod_resp_id = mod_resp.id
|
||||
args[module_id] = mod_resp_id
|
||||
|
||||
|
||||
def _set_apo_etapes(formsemestre: FormSemestre, args: dict, etapes: list[str]):
|
||||
"""Affecte les étapes Apo du semestre,
|
||||
à partir de args["etape_apo<n>"] et args["vdi_apo<n>]
|
||||
"""
|
||||
# menus => case supplementaire pour saisie manuelle, indicée 0
|
||||
start_i = 0 if etapes else 1
|
||||
apo_etapes_vdi = []
|
||||
for n in range(start_i, scu.EDIT_NB_ETAPES + 1):
|
||||
apo_etapes_vdi.append(
|
||||
ApoEtapeVDI(etape=args["etape_apo" + str(n)], vdi=args["vdi_apo" + str(n)])
|
||||
)
|
||||
# uniques:
|
||||
apo_etapes_vdi_uniq = {str(e): e for e in apo_etapes_vdi if str(e)}.values()
|
||||
formsemestre.etapes = []
|
||||
db.session.add(formsemestre)
|
||||
db.session.flush()
|
||||
formsemestre.etapes = [
|
||||
FormSemestreEtape.create_from_apovdi(formsemestre.id, etape_vdi)
|
||||
for etape_vdi in apo_etapes_vdi_uniq
|
||||
]
|
||||
db.session.add(formsemestre)
|
||||
db.session.flush()
|
||||
log(f"setting etapes: {formsemestre}: {formsemestre.etapes}")
|
||||
|
||||
|
||||
def _formsemestre_check_module_list(module_ids, semestre_idx):
|
||||
@ -1711,7 +1704,8 @@ def formsemestre_edit_options(formsemestre_id):
|
||||
"""dialog to change formsemestre options
|
||||
(accessible par EditFormSemestre ou dir. etudes)
|
||||
"""
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre)
|
||||
if not ok:
|
||||
return err
|
||||
return sco_preferences.SemPreferences(formsemestre_id).edit(
|
||||
@ -1719,46 +1713,46 @@ def formsemestre_edit_options(formsemestre_id):
|
||||
)
|
||||
|
||||
|
||||
def formsemestre_change_publication_bul(
|
||||
formsemestre_id, dialog_confirmed=False, redirect=True
|
||||
):
|
||||
"""Change etat publication bulletins sur portail"""
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
||||
def formsemestre_change_publication_bul(formsemestre_id, dialog_confirmed=False):
|
||||
"""Change état publication bulletins sur portail"""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre)
|
||||
if not ok:
|
||||
return err
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
etat = not sem["bul_hide_xml"]
|
||||
etat = not formsemestre.bul_hide_xml
|
||||
|
||||
status_url = url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
)
|
||||
if not dialog_confirmed:
|
||||
if etat:
|
||||
msg = "non"
|
||||
else:
|
||||
msg = ""
|
||||
msg = "non" if etat else ""
|
||||
return scu.confirm_dialog(
|
||||
"<h2>Confirmer la %s publication des bulletins ?</h2>" % msg,
|
||||
f"<h2>Confirmer la {msg} publication des bulletins ?</h2>",
|
||||
help_msg="""Il est parfois utile de désactiver la diffusion des bulletins,
|
||||
par exemple pendant la tenue d'un jury ou avant harmonisation des notes.
|
||||
<br>
|
||||
Ce réglage n'a d'effet que si votre établissement a interfacé ScoDoc et un portail étudiant.
|
||||
Ce réglage n'a d'effet que si votre établissement a interfacé ScoDoc avec
|
||||
une passerelle étudiant.
|
||||
""",
|
||||
dest_url="",
|
||||
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
|
||||
cancel_url=status_url,
|
||||
parameters={"bul_hide_xml": etat, "formsemestre_id": formsemestre_id},
|
||||
)
|
||||
|
||||
args = {"formsemestre_id": formsemestre_id, "bul_hide_xml": etat}
|
||||
sco_formsemestre.do_formsemestre_edit(args)
|
||||
if redirect:
|
||||
return flask.redirect(
|
||||
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
||||
)
|
||||
return None
|
||||
formsemestre.bul_hide_xml = etat
|
||||
db.session.add(formsemestre)
|
||||
db.session.commit()
|
||||
log(f"formsemestre_change_publication_bul: {formsemestre} -> {etat}")
|
||||
|
||||
return flask.redirect(status_url)
|
||||
|
||||
|
||||
def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
"""Changement manuel des coefficients des UE capitalisées."""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre)
|
||||
if not ok:
|
||||
return err
|
||||
|
||||
|
@ -23,7 +23,11 @@ _SCO_PERMISSIONS = (
|
||||
(1 << 9, "EditFormationTags", "Tagguer les formations"),
|
||||
(1 << 10, "EditAllNotes", "Modifier toutes les notes"),
|
||||
(1 << 11, "EditAllEvals", "Modifier toutes les évaluations"),
|
||||
(1 << 12, "EditFormSemestre", "Mettre en place une formation (créer un semestre)"),
|
||||
(
|
||||
1 << 12,
|
||||
"EditFormSemestre",
|
||||
"Mettre en place ou modifier un semestre de formation",
|
||||
),
|
||||
(1 << 13, "AbsChange", "Saisir des absences ou justificatifs"),
|
||||
(1 << 14, "AbsAddBillet", "Saisir des billets d'absences"),
|
||||
# changer adresse/photo ou pour envoyer bulletins par mail ou pour debouche
|
||||
|
@ -35,16 +35,11 @@ def can_edit_suivi():
|
||||
return current_user.has_permission(Permission.EtudChangeAdr)
|
||||
|
||||
|
||||
def check_access_diretud(
|
||||
formsemestre_id, required_permission=Permission.EditFormSemestre
|
||||
):
|
||||
def check_access_diretud(formsemestre: FormSemestre):
|
||||
"""Check if access granted: responsable or EditFormSemestre
|
||||
Return True|False, HTML_error_page
|
||||
"""
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
if (not current_user.has_permission(required_permission)) and (
|
||||
current_user.id not in (u.id for u in formsemestre.responsables)
|
||||
):
|
||||
if not formsemestre.can_be_edited_by(current_user):
|
||||
return (
|
||||
False,
|
||||
render_template(
|
||||
|
@ -2568,8 +2568,7 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
"""Debug.
|
||||
Check that ue and module formations are consistents
|
||||
"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
|
||||
bad_ue = []
|
||||
bad_sem = []
|
||||
@ -2584,12 +2583,12 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
modimpl["mod"] = mod.to_dict()
|
||||
modimpl["ue"] = ue_dict
|
||||
bad_ue.append(modimpl)
|
||||
if sem["formation_id"] != mod.formation_id:
|
||||
if formsemestre.formation_id != mod.formation_id:
|
||||
bad_sem.append(modimpl)
|
||||
modimpl["mod"] = mod.to_dict()
|
||||
|
||||
H = [
|
||||
f"""<p>formation_id={sem["formation_id"]}""",
|
||||
f"""<p>formation_id={formsemestre.formation_id}""",
|
||||
]
|
||||
if bad_ue:
|
||||
H += [
|
||||
@ -2605,22 +2604,24 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
H.append("<p>Aucun problème à signaler !</p>")
|
||||
else:
|
||||
log(f"check_sem_integrity: problem detected: formations_set={formations_set}")
|
||||
if sem["formation_id"] in formations_set:
|
||||
formations_set.remove(sem["formation_id"])
|
||||
if formsemestre.formation_id in formations_set:
|
||||
formations_set.remove(formsemestre.formation_id)
|
||||
if len(formations_set) == 1:
|
||||
if fix:
|
||||
log(f"check_sem_integrity: trying to fix {formsemestre_id}")
|
||||
formation_id = formations_set.pop()
|
||||
if sem["formation_id"] != formation_id:
|
||||
sem["formation_id"] = formation_id
|
||||
sco_formsemestre.do_formsemestre_edit(sem)
|
||||
if formsemestre.formation_id != formation_id:
|
||||
formsemestre.formation_id = formation_id
|
||||
db.session.add(formsemestre)
|
||||
db.session.commit()
|
||||
sco_cache.invalidate_formsemestre(formsemestre.id)
|
||||
H.append("""<p class="alert">Problème réparé: vérifiez</p>""")
|
||||
else:
|
||||
H.append(
|
||||
f"""
|
||||
<p class="alert">Problème détecté réparable:
|
||||
<a href="check_sem_integrity?formsemestre_id={
|
||||
formsemestre_id}&fix=1">réparer maintenant</a></p>
|
||||
<a href="{url_for( "notes.check_sem_integrity", scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id, fix=1)}">réparer maintenant</a></p>
|
||||
"""
|
||||
)
|
||||
else:
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
"Infos sur version ScoDoc"
|
||||
|
||||
SCOVERSION = "9.7.29"
|
||||
SCOVERSION = "9.7.30"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -22,13 +22,13 @@ from app.models import (
|
||||
Evaluation,
|
||||
Formation,
|
||||
FormationModalite,
|
||||
FormSemestre,
|
||||
Identite,
|
||||
Matiere,
|
||||
Module,
|
||||
ModuleImpl,
|
||||
)
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_formsemestre_inscriptions
|
||||
from app.scodoc import sco_formsemestre_validation
|
||||
from app.scodoc import sco_saisie_notes
|
||||
@ -255,11 +255,8 @@ class ScoFake(object):
|
||||
if responsables is None:
|
||||
responsables = (self.default_user.id,)
|
||||
titre = titre or "sans titre"
|
||||
oid = sco_formsemestre.do_formsemestre_create(locals())
|
||||
oids = sco_formsemestre.do_formsemestre_list(args={"formsemestre_id": oid})
|
||||
if not oids:
|
||||
raise ScoValueError("formsemestre not created !")
|
||||
return oid
|
||||
formsemestre = FormSemestre.create_formsemestre(locals())
|
||||
return formsemestre.id
|
||||
|
||||
@logging_meth
|
||||
def create_moduleimpl(
|
||||
|
Loading…
Reference in New Issue
Block a user