Backend 'FormSemestre': début de modernisation + fix regressions

This commit is contained in:
Emmanuel Viennet 2024-10-19 23:52:35 +02:00
parent 7473334387
commit 24e144f372
10 changed files with 253 additions and 285 deletions

View File

@ -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})")

View File

@ -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

View File

@ -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)

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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:

View File

@ -3,7 +3,7 @@
"Infos sur version ScoDoc"
SCOVERSION = "9.7.29"
SCOVERSION = "9.7.30"
SCONAME = "ScoDoc"

View File

@ -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(