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):
|
def __str__(self):
|
||||||
return self.user_name
|
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):
|
def set_password(self, password):
|
||||||
"Set password"
|
"Set password"
|
||||||
current_app.logger.info(f"set_password({self})")
|
current_app.logger.info(f"set_password({self})")
|
||||||
|
@ -126,7 +126,7 @@ class ScoDocModel(db.Model):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_instance(cls, oid: int, accept_none=False):
|
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.
|
cherche uniquement dans le département courant.
|
||||||
|
|
||||||
Ne fonctionne que si le modèle a un attribut dept_id
|
Ne fonctionne que si le modèle a un attribut dept_id
|
||||||
|
@ -139,7 +139,7 @@ class FormSemestre(models.ScoDocModel):
|
|||||||
|
|
||||||
# Relations:
|
# Relations:
|
||||||
etapes = db.relationship(
|
etapes = db.relationship(
|
||||||
"FormSemestreEtape", cascade="all,delete", backref="formsemestre"
|
"FormSemestreEtape", cascade="all,delete-orphan", backref="formsemestre"
|
||||||
)
|
)
|
||||||
modimpls = db.relationship(
|
modimpls = db.relationship(
|
||||||
"ModuleImpl",
|
"ModuleImpl",
|
||||||
@ -242,11 +242,9 @@ class FormSemestre(models.ScoDocModel):
|
|||||||
args["dept_id"] = g.scodoc_dept_id
|
args["dept_id"] = g.scodoc_dept_id
|
||||||
formsemestre: "FormSemestre" = cls.create_from_dict(args)
|
formsemestre: "FormSemestre" = cls.create_from_dict(args)
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
for etape in args["etapes"]:
|
for etape in args.get("etapes") or []:
|
||||||
formsemestre.add_etape(etape)
|
formsemestre.add_etape(etape)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
for u in args["responsables"]:
|
|
||||||
formsemestre.responsables.append(u)
|
|
||||||
# create default partition
|
# create default partition
|
||||||
partition = Partition(
|
partition = Partition(
|
||||||
formsemestre=formsemestre, partition_name=None, numero=1000000
|
formsemestre=formsemestre, partition_name=None, numero=1000000
|
||||||
@ -281,11 +279,26 @@ class FormSemestre(models.ScoDocModel):
|
|||||||
if "date_fin" in args:
|
if "date_fin" in args:
|
||||||
args["date_fin"] = scu.convert_fr_date(args["date_fin"])
|
args["date_fin"] = scu.convert_fr_date(args["date_fin"])
|
||||||
if "etat" in args:
|
if "etat" in args:
|
||||||
|
if args["etat"] is None:
|
||||||
|
del args["etat"]
|
||||||
|
else:
|
||||||
args["etat"] = bool(args["etat"])
|
args["etat"] = bool(args["etat"])
|
||||||
if "bul_bgcolor" in args:
|
if "bul_bgcolor" in args:
|
||||||
args["bul_bgcolor"] = args.get("bul_bgcolor") or "white"
|
args["bul_bgcolor"] = args.get("bul_bgcolor") or "white"
|
||||||
if "titre" in args:
|
if "titre" in args:
|
||||||
args["titre"] = args.get("titre") or "sans titre"
|
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
|
return args
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -1346,6 +1359,7 @@ class FormSemestre(models.ScoDocModel):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
sco_cache.invalidate_formsemestre(formsemestre_id=self.id)
|
||||||
|
|
||||||
def etud_validations_description_html(self, etudid: int) -> str:
|
def etud_validations_description_html(self, etudid: int) -> str:
|
||||||
"""Description textuelle des validations de jury de cet étudiant dans ce semestre"""
|
"""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 aurait du etre not null, mais oublié
|
||||||
etape_apo = db.Column(db.String(APO_CODE_STR_LEN), index=True)
|
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):
|
def __bool__(self):
|
||||||
"Etape False if code empty"
|
"Etape False if code empty"
|
||||||
return self.etape_apo is not None and (len(self.etape_apo) > 0)
|
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 import log
|
||||||
from app.models import Departement
|
from app.models import Departement
|
||||||
from app.models import Formation, FormSemestre
|
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.gen_tables import GenTable
|
||||||
from app.scodoc.codes_cursus import NO_SEMESTRE_ID
|
from app.scodoc.codes_cursus import NO_SEMESTRE_ID
|
||||||
from app.scodoc.sco_exceptions import ScoInvalidIdType, ScoValueError
|
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])
|
return ", ".join([str(x) for x in etapes])
|
||||||
|
|
||||||
|
|
||||||
def do_formsemestre_create( # DEPRECATED, use FormSemestre.create_formsemestre()
|
def read_formsemestre_responsables(formsemestre_id: int) -> list[int]: # OBSOLETE
|
||||||
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
|
|
||||||
"""recupere liste des responsables de ce semestre
|
"""recupere liste des responsables de ce semestre
|
||||||
:returns: liste d'id
|
: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]
|
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
|
# ---------------------- Coefs des UE
|
||||||
|
|
||||||
_formsemestre_uecoef_editor = ndb.EditableTable(
|
_formsemestre_uecoef_editor = ndb.EditableTable(
|
||||||
|
@ -50,7 +50,7 @@ from app.models import (
|
|||||||
UniteEns,
|
UniteEns,
|
||||||
)
|
)
|
||||||
from app.models.formations import Formation
|
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
|
from app.models.but_refcomp import ApcParcours
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
@ -143,7 +143,7 @@ def can_edit_sem(formsemestre_id: int = None, sem=None):
|
|||||||
return sem
|
return sem
|
||||||
|
|
||||||
|
|
||||||
resp_fields = [
|
RESP_FIELDS = [
|
||||||
"responsable_id",
|
"responsable_id",
|
||||||
"responsable_id2",
|
"responsable_id2",
|
||||||
"responsable_id3",
|
"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} !",
|
f"inconnu numéro {modimpl.responsable_id} resp. de {modimpl.id} !",
|
||||||
)
|
)
|
||||||
for index, resp in enumerate(formsemestre.responsables):
|
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()
|
group_tous = formsemestre.get_default_group()
|
||||||
if group_tous:
|
if group_tous:
|
||||||
initvalues["edt_promo_id"] = group_tous.edt_id or ""
|
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",
|
"titre",
|
||||||
@ -755,7 +755,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
"input_type": "text_suggest",
|
"input_type": "text_suggest",
|
||||||
"size": 50,
|
"size": 50,
|
||||||
"withcheckbox": True,
|
"withcheckbox": True,
|
||||||
"title": "%s %s" % (mod.code or "", mod.titre or ""),
|
"title": f"""{mod.code or ""} {mod.titre or ""}""",
|
||||||
"allowed_values": allowed_user_names,
|
"allowed_values": allowed_user_names,
|
||||||
"template": itemtemplate,
|
"template": itemtemplate,
|
||||||
"text_suggest_options": {
|
"text_suggest_options": {
|
||||||
@ -875,67 +875,31 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
formsemestre_id=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
return redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
|
return redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
|
||||||
else:
|
# Edition ou modification du semestre
|
||||||
if tf[2]["gestion_compensation_lst"]:
|
tf[2]["gestion_compensation"] = bool(tf[2]["gestion_compensation_lst"])
|
||||||
tf[2]["gestion_compensation"] = True
|
tf[2]["gestion_semestrielle"] = bool(tf[2]["gestion_semestrielle_lst"])
|
||||||
else:
|
tf[2]["bul_hide_xml"] = not bool(tf[2]["bul_publish_xml_lst"])
|
||||||
tf[2]["gestion_compensation"] = False
|
_remap_resp_modimpls(tf[2])
|
||||||
if tf[2]["gestion_semestrielle_lst"]:
|
if "parcours" in tf[2]:
|
||||||
tf[2]["gestion_semestrielle"] = True
|
tf[2]["parcours"] = [
|
||||||
else:
|
db.session.get(ApcParcours, int(parcour_id_str))
|
||||||
tf[2]["gestion_semestrielle"] = False
|
for parcour_id_str in tf[2]["parcours"]
|
||||||
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:
|
# Modules sélectionnés:
|
||||||
# (retire le "MI" du début du nom de champs)
|
# (retire le "MI" du début du nom de champs)
|
||||||
module_ids_checked = [int(x[2:]) for x in tf[2]["tf-checked"]]
|
module_ids_checked = [int(x[2:]) for x in tf[2]["tf-checked"]]
|
||||||
_formsemestre_check_ue_bonus_unicity(module_ids_checked)
|
_formsemestre_check_ue_bonus_unicity(module_ids_checked)
|
||||||
if not edit:
|
if not edit:
|
||||||
if is_apc:
|
if is_apc:
|
||||||
_formsemestre_check_module_list(
|
_formsemestre_check_module_list(module_ids_checked, tf[2]["semestre_id"])
|
||||||
module_ids_checked, tf[2]["semestre_id"]
|
|
||||||
)
|
|
||||||
# création du semestre
|
# création du semestre
|
||||||
formsemestre_id = sco_formsemestre.do_formsemestre_create(tf[2])
|
formsemestre = FormSemestre.create_formsemestre(tf[2])
|
||||||
# création des modules
|
# création des modules
|
||||||
for module_id in module_ids_checked:
|
for module_id in module_ids_checked:
|
||||||
modargs = {
|
modargs = {
|
||||||
"module_id": module_id,
|
"module_id": module_id,
|
||||||
"formsemestre_id": formsemestre_id,
|
"formsemestre_id": formsemestre.id,
|
||||||
"responsable_id": tf[2][f"MI{module_id}"],
|
"responsable_id": tf[2][f"MI{module_id}"],
|
||||||
}
|
}
|
||||||
_ = ModuleImpl.create_from_dict(modargs)
|
_ = ModuleImpl.create_from_dict(modargs)
|
||||||
@ -949,19 +913,16 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
x for x in module_ids_checked if not x in module_ids_existing
|
x for x in module_ids_checked if not x in module_ids_existing
|
||||||
]
|
]
|
||||||
if is_apc:
|
if is_apc:
|
||||||
_formsemestre_check_module_list(
|
_formsemestre_check_module_list(module_ids_tocreate, tf[2]["semestre_id"])
|
||||||
module_ids_tocreate, tf[2]["semestre_id"]
|
|
||||||
)
|
|
||||||
# modules existants à modifier
|
# modules existants à modifier
|
||||||
module_ids_toedit = [
|
module_ids_toedit = [x for x in module_ids_checked if x in module_ids_existing]
|
||||||
x for x in module_ids_checked if x in module_ids_existing
|
|
||||||
]
|
|
||||||
# modules à détruire
|
# modules à détruire
|
||||||
module_ids_todelete = [
|
module_ids_todelete = [
|
||||||
x for x in module_ids_existing if not x in module_ids_checked
|
x for x in module_ids_existing if not x in module_ids_checked
|
||||||
]
|
]
|
||||||
#
|
#
|
||||||
sco_formsemestre.do_formsemestre_edit(tf[2])
|
formsemestre.from_dict(tf[2])
|
||||||
|
sco_cache.invalidate_formsemestre(formsemestre.id)
|
||||||
#
|
#
|
||||||
msg = []
|
msg = []
|
||||||
for module_id in module_ids_tocreate:
|
for module_id in module_ids_tocreate:
|
||||||
@ -978,12 +939,10 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
group_id = tf[2][f"{module_id}!group_id"]
|
group_id = tf[2][f"{module_id}!group_id"]
|
||||||
log(f"""inscription module: {module_id}!group_id = '{group_id}'""")
|
log(f"""inscription module: {module_id}!group_id = '{group_id}'""")
|
||||||
if group_id:
|
if group_id:
|
||||||
etudids = [
|
etudids = [x["etudid"] for x in sco_groups.get_group_members(group_id)]
|
||||||
x["etudid"] for x in sco_groups.get_group_members(group_id)
|
|
||||||
]
|
|
||||||
log(
|
log(
|
||||||
"inscription module:module_id=%s,moduleimpl_id=%s: %s"
|
f"""inscription module:module_id={module_id},moduleimpl_id={
|
||||||
% (module_id, modimpl.id, etudids)
|
modimpl.id}: {etudids}"""
|
||||||
)
|
)
|
||||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||||
modimpl.id,
|
modimpl.id,
|
||||||
@ -991,18 +950,16 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
etudids,
|
etudids,
|
||||||
)
|
)
|
||||||
msg += [
|
msg += [
|
||||||
"inscription de %d étudiants au module %s"
|
f"""inscription de {len(etudids)} étudiants au module {
|
||||||
% (len(etudids), mod["code"] or "(module sans code)")
|
mod.code or "(module sans code)"}"""
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
log(
|
log(
|
||||||
"inscription module:module_id=%s,moduleimpl_id=%s: aucun etudiant inscrit"
|
f"""inscription module:module_id={module_id},moduleimpl_id={
|
||||||
% (module_id, modimpl.id)
|
modimpl.id}: aucun etudiant inscrit"""
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
ok, diag = formsemestre_delete_moduleimpls(
|
ok, diag = formsemestre_delete_moduleimpls(formsemestre.id, module_ids_todelete)
|
||||||
formsemestre.id, module_ids_todelete
|
|
||||||
)
|
|
||||||
msg += diag
|
msg += diag
|
||||||
for module_id in module_ids_toedit:
|
for module_id in module_ids_toedit:
|
||||||
moduleimpl_id = sco_moduleimpl.moduleimpl_list(
|
moduleimpl_id = sco_moduleimpl.moduleimpl_list(
|
||||||
@ -1014,18 +971,10 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
"formsemestre_id": formsemestre.id,
|
"formsemestre_id": formsemestre.id,
|
||||||
"responsable_id": tf[2]["MI" + str(module_id)],
|
"responsable_id": tf[2]["MI" + str(module_id)],
|
||||||
}
|
}
|
||||||
sco_moduleimpl.do_moduleimpl_edit(
|
sco_moduleimpl.do_moduleimpl_edit(modargs, formsemestre_id=formsemestre.id)
|
||||||
modargs, formsemestre_id=formsemestre.id
|
# --- Etapes
|
||||||
)
|
_set_apo_etapes(formsemestre, tf[2], etapes)
|
||||||
# --- Association des parcours
|
# --- id edt du groupe par défault
|
||||||
if formsemestre is None:
|
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
|
||||||
if "parcours" in tf[2]:
|
|
||||||
formsemestre.parcours = [
|
|
||||||
db.session.get(ApcParcours, int(parcour_id_str))
|
|
||||||
for parcour_id_str in tf[2]["parcours"]
|
|
||||||
]
|
|
||||||
# --- Id edt du groupe par défault
|
|
||||||
if "edt_promo_id" in tf[2]:
|
if "edt_promo_id" in tf[2]:
|
||||||
group_tous = formsemestre.get_default_group()
|
group_tous = formsemestre.get_default_group()
|
||||||
if group_tous:
|
if group_tous:
|
||||||
@ -1041,6 +990,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
formsemestre.update_inscriptions_parcours_from_groups()
|
formsemestre.update_inscriptions_parcours_from_groups()
|
||||||
# --- Fin
|
# --- Fin
|
||||||
if edit:
|
if edit:
|
||||||
|
log(f"""formsemestre_edit: {formsemestre}""")
|
||||||
if msg:
|
if msg:
|
||||||
return f"""
|
return f"""
|
||||||
<div class="ue_warning"><span>Attention !<ul>
|
<div class="ue_warning"><span>Attention !<ul>
|
||||||
@ -1057,7 +1007,6 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
||||||
}">retour au tableau de bord</a>
|
}">retour au tableau de bord</a>
|
||||||
"""
|
"""
|
||||||
else:
|
|
||||||
flash("Semestre modifié")
|
flash("Semestre modifié")
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
@ -1067,7 +1016,6 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
check_parcours=0,
|
check_parcours=0,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
flash("Nouveau semestre créé")
|
flash("Nouveau semestre créé")
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
@ -1078,6 +1026,51 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
def _formsemestre_check_module_list(module_ids, semestre_idx):
|
||||||
"""En APC: Vérifie que tous les modules de la liste
|
"""En APC: Vérifie que tous les modules de la liste
|
||||||
sont dans le semestre indiqué.
|
sont dans le semestre indiqué.
|
||||||
@ -1711,7 +1704,8 @@ def formsemestre_edit_options(formsemestre_id):
|
|||||||
"""dialog to change formsemestre options
|
"""dialog to change formsemestre options
|
||||||
(accessible par EditFormSemestre ou dir. etudes)
|
(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:
|
if not ok:
|
||||||
return err
|
return err
|
||||||
return sco_preferences.SemPreferences(formsemestre_id).edit(
|
return sco_preferences.SemPreferences(formsemestre_id).edit(
|
||||||
@ -1719,46 +1713,46 @@ def formsemestre_edit_options(formsemestre_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def formsemestre_change_publication_bul(
|
def formsemestre_change_publication_bul(formsemestre_id, dialog_confirmed=False):
|
||||||
formsemestre_id, dialog_confirmed=False, redirect=True
|
"""Change état publication bulletins sur portail"""
|
||||||
):
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
"""Change etat publication bulletins sur portail"""
|
ok, err = sco_permissions_check.check_access_diretud(formsemestre)
|
||||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
|
||||||
if not ok:
|
if not ok:
|
||||||
return err
|
return err
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
etat = not formsemestre.bul_hide_xml
|
||||||
etat = not sem["bul_hide_xml"]
|
|
||||||
|
|
||||||
|
status_url = url_for(
|
||||||
|
"notes.formsemestre_status",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
)
|
||||||
if not dialog_confirmed:
|
if not dialog_confirmed:
|
||||||
if etat:
|
msg = "non" if etat else ""
|
||||||
msg = "non"
|
|
||||||
else:
|
|
||||||
msg = ""
|
|
||||||
return scu.confirm_dialog(
|
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,
|
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.
|
par exemple pendant la tenue d'un jury ou avant harmonisation des notes.
|
||||||
<br>
|
<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="",
|
dest_url="",
|
||||||
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
|
cancel_url=status_url,
|
||||||
parameters={"bul_hide_xml": etat, "formsemestre_id": formsemestre_id},
|
parameters={"bul_hide_xml": etat, "formsemestre_id": formsemestre_id},
|
||||||
)
|
)
|
||||||
|
|
||||||
args = {"formsemestre_id": formsemestre_id, "bul_hide_xml": etat}
|
formsemestre.bul_hide_xml = etat
|
||||||
sco_formsemestre.do_formsemestre_edit(args)
|
db.session.add(formsemestre)
|
||||||
if redirect:
|
db.session.commit()
|
||||||
return flask.redirect(
|
log(f"formsemestre_change_publication_bul: {formsemestre} -> {etat}")
|
||||||
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
|
||||||
)
|
return flask.redirect(status_url)
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||||
"""Changement manuel des coefficients des UE capitalisées."""
|
"""Changement manuel des coefficients des UE capitalisées."""
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
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:
|
if not ok:
|
||||||
return err
|
return err
|
||||||
|
|
||||||
|
@ -23,7 +23,11 @@ _SCO_PERMISSIONS = (
|
|||||||
(1 << 9, "EditFormationTags", "Tagguer les formations"),
|
(1 << 9, "EditFormationTags", "Tagguer les formations"),
|
||||||
(1 << 10, "EditAllNotes", "Modifier toutes les notes"),
|
(1 << 10, "EditAllNotes", "Modifier toutes les notes"),
|
||||||
(1 << 11, "EditAllEvals", "Modifier toutes les évaluations"),
|
(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 << 13, "AbsChange", "Saisir des absences ou justificatifs"),
|
||||||
(1 << 14, "AbsAddBillet", "Saisir des billets d'absences"),
|
(1 << 14, "AbsAddBillet", "Saisir des billets d'absences"),
|
||||||
# changer adresse/photo ou pour envoyer bulletins par mail ou pour debouche
|
# 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)
|
return current_user.has_permission(Permission.EtudChangeAdr)
|
||||||
|
|
||||||
|
|
||||||
def check_access_diretud(
|
def check_access_diretud(formsemestre: FormSemestre):
|
||||||
formsemestre_id, required_permission=Permission.EditFormSemestre
|
|
||||||
):
|
|
||||||
"""Check if access granted: responsable or EditFormSemestre
|
"""Check if access granted: responsable or EditFormSemestre
|
||||||
Return True|False, HTML_error_page
|
Return True|False, HTML_error_page
|
||||||
"""
|
"""
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
if not formsemestre.can_be_edited_by(current_user):
|
||||||
if (not current_user.has_permission(required_permission)) and (
|
|
||||||
current_user.id not in (u.id for u in formsemestre.responsables)
|
|
||||||
):
|
|
||||||
return (
|
return (
|
||||||
False,
|
False,
|
||||||
render_template(
|
render_template(
|
||||||
|
@ -2568,8 +2568,7 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
|||||||
"""Debug.
|
"""Debug.
|
||||||
Check that ue and module formations are consistents
|
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)
|
modimpls = sco_moduleimpl.moduleimpl_list(formsemestre_id=formsemestre_id)
|
||||||
bad_ue = []
|
bad_ue = []
|
||||||
bad_sem = []
|
bad_sem = []
|
||||||
@ -2584,12 +2583,12 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
|||||||
modimpl["mod"] = mod.to_dict()
|
modimpl["mod"] = mod.to_dict()
|
||||||
modimpl["ue"] = ue_dict
|
modimpl["ue"] = ue_dict
|
||||||
bad_ue.append(modimpl)
|
bad_ue.append(modimpl)
|
||||||
if sem["formation_id"] != mod.formation_id:
|
if formsemestre.formation_id != mod.formation_id:
|
||||||
bad_sem.append(modimpl)
|
bad_sem.append(modimpl)
|
||||||
modimpl["mod"] = mod.to_dict()
|
modimpl["mod"] = mod.to_dict()
|
||||||
|
|
||||||
H = [
|
H = [
|
||||||
f"""<p>formation_id={sem["formation_id"]}""",
|
f"""<p>formation_id={formsemestre.formation_id}""",
|
||||||
]
|
]
|
||||||
if bad_ue:
|
if bad_ue:
|
||||||
H += [
|
H += [
|
||||||
@ -2605,22 +2604,24 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
|||||||
H.append("<p>Aucun problème à signaler !</p>")
|
H.append("<p>Aucun problème à signaler !</p>")
|
||||||
else:
|
else:
|
||||||
log(f"check_sem_integrity: problem detected: formations_set={formations_set}")
|
log(f"check_sem_integrity: problem detected: formations_set={formations_set}")
|
||||||
if sem["formation_id"] in formations_set:
|
if formsemestre.formation_id in formations_set:
|
||||||
formations_set.remove(sem["formation_id"])
|
formations_set.remove(formsemestre.formation_id)
|
||||||
if len(formations_set) == 1:
|
if len(formations_set) == 1:
|
||||||
if fix:
|
if fix:
|
||||||
log(f"check_sem_integrity: trying to fix {formsemestre_id}")
|
log(f"check_sem_integrity: trying to fix {formsemestre_id}")
|
||||||
formation_id = formations_set.pop()
|
formation_id = formations_set.pop()
|
||||||
if sem["formation_id"] != formation_id:
|
if formsemestre.formation_id != formation_id:
|
||||||
sem["formation_id"] = formation_id
|
formsemestre.formation_id = formation_id
|
||||||
sco_formsemestre.do_formsemestre_edit(sem)
|
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>""")
|
H.append("""<p class="alert">Problème réparé: vérifiez</p>""")
|
||||||
else:
|
else:
|
||||||
H.append(
|
H.append(
|
||||||
f"""
|
f"""
|
||||||
<p class="alert">Problème détecté réparable:
|
<p class="alert">Problème détecté réparable:
|
||||||
<a href="check_sem_integrity?formsemestre_id={
|
<a href="{url_for( "notes.check_sem_integrity", scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id}&fix=1">réparer maintenant</a></p>
|
formsemestre_id=formsemestre_id, fix=1)}">réparer maintenant</a></p>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
"Infos sur version ScoDoc"
|
"Infos sur version ScoDoc"
|
||||||
|
|
||||||
SCOVERSION = "9.7.29"
|
SCOVERSION = "9.7.30"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
@ -22,13 +22,13 @@ from app.models import (
|
|||||||
Evaluation,
|
Evaluation,
|
||||||
Formation,
|
Formation,
|
||||||
FormationModalite,
|
FormationModalite,
|
||||||
|
FormSemestre,
|
||||||
Identite,
|
Identite,
|
||||||
Matiere,
|
Matiere,
|
||||||
Module,
|
Module,
|
||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
)
|
)
|
||||||
from app.scodoc import codes_cursus
|
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_inscriptions
|
||||||
from app.scodoc import sco_formsemestre_validation
|
from app.scodoc import sco_formsemestre_validation
|
||||||
from app.scodoc import sco_saisie_notes
|
from app.scodoc import sco_saisie_notes
|
||||||
@ -255,11 +255,8 @@ class ScoFake(object):
|
|||||||
if responsables is None:
|
if responsables is None:
|
||||||
responsables = (self.default_user.id,)
|
responsables = (self.default_user.id,)
|
||||||
titre = titre or "sans titre"
|
titre = titre or "sans titre"
|
||||||
oid = sco_formsemestre.do_formsemestre_create(locals())
|
formsemestre = FormSemestre.create_formsemestre(locals())
|
||||||
oids = sco_formsemestre.do_formsemestre_list(args={"formsemestre_id": oid})
|
return formsemestre.id
|
||||||
if not oids:
|
|
||||||
raise ScoValueError("formsemestre not created !")
|
|
||||||
return oid
|
|
||||||
|
|
||||||
@logging_meth
|
@logging_meth
|
||||||
def create_moduleimpl(
|
def create_moduleimpl(
|
||||||
|
Loading…
Reference in New Issue
Block a user