forked from ScoDoc/ScoDoc
Edition UEs: assouplie verrouillage en BUT. + début modernisation code.
This commit is contained in:
parent
abd8bf484d
commit
3b984ea823
@ -45,7 +45,6 @@ from app.models import (
|
|||||||
FormSemestreUECoef,
|
FormSemestreUECoef,
|
||||||
Matiere,
|
Matiere,
|
||||||
Module,
|
Module,
|
||||||
ModuleImpl,
|
|
||||||
UniteEns,
|
UniteEns,
|
||||||
)
|
)
|
||||||
from app.models import ApcValidationRCUE, ScolarFormSemestreValidation, ScolarEvent
|
from app.models import ApcValidationRCUE, ScolarFormSemestreValidation, ScolarEvent
|
||||||
@ -546,6 +545,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
formation_id, int(tf[2]["semestre_idx"])
|
formation_id, int(tf[2]["semestre_idx"])
|
||||||
)
|
)
|
||||||
ue_id = do_ue_create(tf[2])
|
ue_id = do_ue_create(tf[2])
|
||||||
|
matiere_id = None
|
||||||
if is_apc or cursus.UE_IS_MODULE or tf[2]["create_matiere"]:
|
if is_apc or cursus.UE_IS_MODULE or tf[2]["create_matiere"]:
|
||||||
# rappel: en APC, toutes les UE ont une matière, créée ici
|
# rappel: en APC, toutes les UE ont une matière, créée ici
|
||||||
# (inutilisée mais à laquelle les modules sont rattachés)
|
# (inutilisée mais à laquelle les modules sont rattachés)
|
||||||
@ -597,46 +597,21 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _add_ue_semestre_id(ues: list[dict], is_apc):
|
def next_ue_numero(formation_id, semestre_id=None) -> int:
|
||||||
"""ajoute semestre_id dans les ue, en regardant
|
|
||||||
semestre_idx ou à défaut, pour les formations non APC, le premier module
|
|
||||||
de chacune.
|
|
||||||
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
|
|
||||||
qui les place à la fin de la liste.
|
|
||||||
"""
|
|
||||||
for ue in ues:
|
|
||||||
if ue["semestre_idx"] is not None:
|
|
||||||
ue["semestre_id"] = ue["semestre_idx"]
|
|
||||||
elif is_apc:
|
|
||||||
ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
|
|
||||||
else:
|
|
||||||
# était le comportement ScoDoc7
|
|
||||||
ue = UniteEns.get_ue(ue["ue_id"])
|
|
||||||
module = ue.modules.first()
|
|
||||||
if module:
|
|
||||||
ue["semestre_id"] = module.semestre_id
|
|
||||||
else:
|
|
||||||
ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
|
|
||||||
|
|
||||||
|
|
||||||
def next_ue_numero(formation_id, semestre_id=None):
|
|
||||||
"""Numero d'une nouvelle UE dans cette formation.
|
"""Numero d'une nouvelle UE dans cette formation.
|
||||||
Si le semestre est specifie, cherche les UE ayant des modules de ce semestre
|
Si le semestre est specifie, cherche les UE ayant des modules de ce semestre
|
||||||
"""
|
"""
|
||||||
formation = db.session.get(Formation, formation_id)
|
formation = db.session.get(Formation, formation_id)
|
||||||
ues = ue_list(args={"formation_id": formation_id})
|
ues = formation.ues.all()
|
||||||
if not ues:
|
if not ues:
|
||||||
return 0
|
return 0
|
||||||
if semestre_id is None:
|
if semestre_id is None:
|
||||||
return ues[-1]["numero"] + 1000
|
return ues[-1].numero + 1000
|
||||||
else:
|
|
||||||
# Avec semestre: (prend le semestre du 1er module de l'UE)
|
# Avec semestre: (prend le semestre du 1er module de l'UE)
|
||||||
_add_ue_semestre_id(ues, formation.get_cursus().APC_SAE)
|
ue_list_semestre = [ue for ue in ues if ue.get_semestre_id() == semestre_id]
|
||||||
ue_list_semestre = [ue for ue in ues if ue["semestre_id"] == semestre_id]
|
|
||||||
if ue_list_semestre:
|
if ue_list_semestre:
|
||||||
return ue_list_semestre[-1]["numero"] + 10
|
return ue_list_semestre[-1].numero + 10
|
||||||
else:
|
return ues[-1].numero + 1000
|
||||||
return ues[-1]["numero"] + 1000
|
|
||||||
|
|
||||||
|
|
||||||
def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
||||||
@ -698,20 +673,22 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""):
|
|||||||
show_tags = scu.to_bool(request.args.get("show_tags", 0))
|
show_tags = scu.to_bool(request.args.get("show_tags", 0))
|
||||||
locked = formation.has_locked_sems(semestre_idx)
|
locked = formation.has_locked_sems(semestre_idx)
|
||||||
semestre_ids = range(1, parcours.NB_SEM + 1)
|
semestre_ids = range(1, parcours.NB_SEM + 1)
|
||||||
# transition: on requete ici via l'ORM mais on utilise les fonctions ScoDoc7
|
|
||||||
# basées sur des dicts
|
ues = (
|
||||||
ues_obj = UniteEns.query.filter_by(
|
formation.ues.filter_by(is_external=False)
|
||||||
formation_id=formation_id, is_external=False
|
.order_by(UniteEns.semestre_idx, UniteEns.numero)
|
||||||
).order_by(UniteEns.semestre_idx, UniteEns.numero)
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# safety check: renumérote les ue s'il en manque ou s'il y a des ex-aequo.
|
# safety check: renumérote les ue s'il en manque ou s'il y a des ex-aequo.
|
||||||
# cela facilite le travail de la passerelle !
|
# cela facilite le travail de la passerelle !
|
||||||
numeros = {ue.numero for ue in ues_obj}
|
numeros = {ue.numero for ue in ues}
|
||||||
if (None in numeros) or len(numeros) < ues_obj.count():
|
if (None in numeros) or len(numeros) < len(ues):
|
||||||
scu.objects_renumber(db, ues_obj)
|
scu.objects_renumber(db, ues)
|
||||||
|
|
||||||
ues_externes_obj = UniteEns.query.filter_by(
|
ues_externes = UniteEns.query.filter_by(
|
||||||
formation_id=formation_id, is_external=True
|
formation_id=formation_id, is_external=True
|
||||||
)
|
).all()
|
||||||
# liste ordonnée des formsemestres de cette formation:
|
# liste ordonnée des formsemestres de cette formation:
|
||||||
formsemestres = sorted(
|
formsemestres = sorted(
|
||||||
FormSemestre.query.filter_by(formation_id=formation_id).all(),
|
FormSemestre.query.filter_by(formation_id=formation_id).all(),
|
||||||
@ -721,29 +698,25 @@ def ue_table(formation_id=None, semestre_idx=1, msg=""):
|
|||||||
|
|
||||||
if is_apc:
|
if is_apc:
|
||||||
# Pour faciliter la transition des anciens programmes non APC
|
# Pour faciliter la transition des anciens programmes non APC
|
||||||
for ue in ues_obj:
|
for ue in ues:
|
||||||
ue.guess_semestre_idx()
|
ue.guess_semestre_idx()
|
||||||
# vérifie qu'on a bien au moins une matière dans chaque UE
|
# vérifie qu'on a bien au moins une matière dans chaque UE
|
||||||
if ue.matieres.count() < 1:
|
if ue.matieres.count() < 1:
|
||||||
mat = Matiere(ue_id=ue.id)
|
mat = Matiere(ue_id=ue.id)
|
||||||
db.session.add(mat)
|
db.session.add(mat)
|
||||||
# donne des couleurs aux UEs crées avant
|
# donne des couleurs aux UEs crées avant
|
||||||
colorie_anciennes_ues(ues_obj)
|
colorie_anciennes_ues(ues)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
ues = [ue.to_dict() for ue in ues_obj]
|
|
||||||
ues_externes = [ue.to_dict() for ue in ues_externes_obj]
|
|
||||||
|
|
||||||
# tri par semestre et numero:
|
# tri par semestre et numero:
|
||||||
_add_ue_semestre_id(ues, is_apc)
|
ues.sort(key=lambda u: (u.get_semestre_id(), u.numero))
|
||||||
_add_ue_semestre_id(ues_externes, is_apc)
|
ues_externes.sort(key=lambda u: (u.get_semestre_id(), u.numero))
|
||||||
ues.sort(key=lambda u: (u["semestre_id"], u["numero"]))
|
|
||||||
ues_externes.sort(key=lambda u: (u["semestre_id"], u["numero"]))
|
|
||||||
# Codes dupliqués (pour aider l'utilisateur)
|
# Codes dupliqués (pour aider l'utilisateur)
|
||||||
seen = set()
|
seen = set()
|
||||||
duplicated_codes = {
|
duplicated_codes = {
|
||||||
ue["ue_code"] for ue in ues if ue["ue_code"] in seen or seen.add(ue["ue_code"])
|
ue.ue_code for ue in ues if ue.ue_code in seen or seen.add(ue.ue_code)
|
||||||
}
|
}
|
||||||
ues_with_duplicated_code = [ue for ue in ues if ue["ue_code"] in duplicated_codes]
|
ues_with_duplicated_code = [ue for ue in ues if ue.ue_code in duplicated_codes]
|
||||||
|
|
||||||
has_perm_change = current_user.has_permission(Permission.EditFormation)
|
has_perm_change = current_user.has_permission(Permission.EditFormation)
|
||||||
# editable = (not locked) and has_perm_change
|
# editable = (not locked) and has_perm_change
|
||||||
@ -799,8 +772,8 @@ du programme" (menu "Semestre") si vous avez un semestre en cours);
|
|||||||
formation ont le même code : <tt>{
|
formation ont le même code : <tt>{
|
||||||
', '.join([
|
', '.join([
|
||||||
'<a class="stdlink" href="' + url_for( "notes.ue_edit",
|
'<a class="stdlink" href="' + url_for( "notes.ue_edit",
|
||||||
scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"] )
|
scodoc_dept=g.scodoc_dept, ue_id=ue.id )
|
||||||
+ '">' + ue["acronyme"] + " (code " + ue["ue_code"] + ")</a>"
|
+ '">' + ue.acronyme + " (code " + ue.ue_code + ")</a>"
|
||||||
for ue in ues_with_duplicated_code ])
|
for ue in ues_with_duplicated_code ])
|
||||||
}</tt>.
|
}</tt>.
|
||||||
Il faut corriger cela, sinon les capitalisations et ECTS seront
|
Il faut corriger cela, sinon les capitalisations et ECTS seront
|
||||||
@ -1115,7 +1088,7 @@ def _html_select_semestre_idx(formation_id, semestre_ids, semestre_idx):
|
|||||||
|
|
||||||
def _ue_table_ues(
|
def _ue_table_ues(
|
||||||
parcours,
|
parcours,
|
||||||
ues: list[dict],
|
ues: list[UniteEns],
|
||||||
editable,
|
editable,
|
||||||
tag_editable,
|
tag_editable,
|
||||||
has_perm_change,
|
has_perm_change,
|
||||||
@ -1132,33 +1105,25 @@ def _ue_table_ues(
|
|||||||
cur_ue_semestre_id = None
|
cur_ue_semestre_id = None
|
||||||
iue = 0
|
iue = 0
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
if ue["ects"] is None:
|
ects_str = "" if ue.ects is None else f", {ue.ects:g} ECTS"
|
||||||
ue["ects_str"] = ""
|
klass = "span_apo_edit" if editable else ""
|
||||||
else:
|
|
||||||
ue["ects_str"] = ", %g ECTS" % ue["ects"]
|
|
||||||
if editable:
|
|
||||||
klass = "span_apo_edit"
|
|
||||||
else:
|
|
||||||
klass = ""
|
|
||||||
edit_url = url_for(
|
edit_url = url_for(
|
||||||
"apiweb.ue_set_code_apogee",
|
"apiweb.ue_set_code_apogee",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
ue_id=ue["ue_id"],
|
ue_id=ue.id,
|
||||||
)
|
)
|
||||||
ue[
|
code_apogee_str = f""", Apo: <span
|
||||||
"code_apogee_str"
|
class="{klass}" data-url="{edit_url}" id="{ue.id}"
|
||||||
] = f""", Apo: <span
|
|
||||||
class="{klass}" data-url="{edit_url}" id="{ue['ue_id']}"
|
|
||||||
data-placeholder="{scu.APO_MISSING_CODE_STR}">{
|
data-placeholder="{scu.APO_MISSING_CODE_STR}">{
|
||||||
ue["code_apogee"] or ""
|
ue.code_apogee or ""
|
||||||
}</span>"""
|
}</span>"""
|
||||||
|
|
||||||
if cur_ue_semestre_id != ue["semestre_id"]:
|
if cur_ue_semestre_id != ue.semestre_id:
|
||||||
cur_ue_semestre_id = ue["semestre_id"]
|
cur_ue_semestre_id = ue.semestre_id
|
||||||
if ue["semestre_id"] == codes_cursus.UE_SEM_DEFAULT:
|
if ue.semestre_id == codes_cursus.UE_SEM_DEFAULT:
|
||||||
lab = "Pas d'indication de semestre:"
|
lab = "Pas d'indication de semestre:"
|
||||||
else:
|
else:
|
||||||
lab = f"""Semestre {ue["semestre_id"]}:"""
|
lab = f"""Semestre {ue.semestre_id}:"""
|
||||||
H.append(
|
H.append(
|
||||||
f'<div class="ue_list_div"><div class="ue_list_tit_sem">{lab}</div>'
|
f'<div class="ue_list_div"><div class="ue_list_tit_sem">{lab}</div>'
|
||||||
)
|
)
|
||||||
@ -1166,52 +1131,55 @@ def _ue_table_ues(
|
|||||||
H.append('<li class="notes_ue_list">')
|
H.append('<li class="notes_ue_list">')
|
||||||
if iue != 0 and editable:
|
if iue != 0 and editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<a href="ue_move?ue_id=%s&after=0" class="aud">%s</a>'
|
f"""<a href="{
|
||||||
% (ue["ue_id"], arrow_up)
|
url_for( 'notes.ue_move',
|
||||||
|
scodoc_dept=g.scodoc_dept, ue_id=ue.id, after=0)}"
|
||||||
|
class="aud">{arrow_up}</a>"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append(arrow_none)
|
H.append(arrow_none)
|
||||||
if iue < len(ues) - 1 and editable:
|
if iue < len(ues) - 1 and editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<a href="ue_move?ue_id=%s&after=1" class="aud">%s</a>'
|
f"""<a href="{
|
||||||
% (ue["ue_id"], arrow_down)
|
url_for( 'notes.ue_move',
|
||||||
|
scodoc_dept=g.scodoc_dept, ue_id=ue.id, after=1)}"
|
||||||
|
class="aud">{arrow_down}</a>"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append(arrow_none)
|
H.append(arrow_none)
|
||||||
ue["acro_titre"] = str(ue["acronyme"])
|
acro_titre = ue.acronyme
|
||||||
if ue["titre"] != ue["acronyme"]:
|
if ue.titre != ue.acronyme:
|
||||||
ue["acro_titre"] += " " + str(ue["titre"])
|
acro_titre += " " + (ue.titre or "")
|
||||||
H.append(
|
H.append(
|
||||||
"""%(acro_titre)s <span class="ue_code">(code %(ue_code)s%(ects_str)s, coef. %(coefficient)3.2f%(code_apogee_str)s)</span>
|
f"""acro_titre <span class="ue_code">(code {ue.ue_code}{ects_str}, coef. {
|
||||||
|
(ue.coefficient or 0):3.2f}{code_apogee_str})</span>
|
||||||
<span class="ue_coef"></span>
|
<span class="ue_coef"></span>
|
||||||
"""
|
"""
|
||||||
% ue
|
|
||||||
)
|
)
|
||||||
if ue["type"] != codes_cursus.UE_STANDARD:
|
if ue.type != codes_cursus.UE_STANDARD:
|
||||||
H.append(
|
H.append(
|
||||||
'<span class="ue_type">%s</span>'
|
f"""<span class="ue_type">{codes_cursus.UE_TYPE_NAME[ue.type]}</span>"""
|
||||||
% codes_cursus.UE_TYPE_NAME[ue["type"]]
|
|
||||||
)
|
)
|
||||||
if ue["is_external"]:
|
if ue.is_external:
|
||||||
# Cas spécial: si l'UE externe a plus d'un module, c'est peut être une UE
|
# Cas spécial: si l'UE externe a plus d'un module, c'est peut être une UE
|
||||||
# qui a été déclarée externe par erreur (ou suite à un bug d'import/export xml)
|
# qui a été déclarée externe par erreur (ou suite à un bug d'import/export xml)
|
||||||
# Dans ce cas, propose de changer le type (même si verrouillée)
|
# Dans ce cas, propose de changer le type (même si verrouillée)
|
||||||
if len(sco_moduleimpl.moduleimpls_in_external_ue(ue["ue_id"])) > 1:
|
if len(sco_moduleimpl.moduleimpls_in_external_ue(ue.id)) > 1:
|
||||||
H.append('<span class="ue_is_external">')
|
H.append('<span class="ue_is_external">')
|
||||||
if has_perm_change:
|
if has_perm_change:
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a class="stdlink" href="{
|
f"""<a class="stdlink" href="{
|
||||||
url_for("notes.ue_set_internal",
|
url_for("notes.ue_set_internal",
|
||||||
scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"])
|
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
}">transformer en UE ordinaire</a> """
|
}">transformer en UE ordinaire</a> """
|
||||||
)
|
)
|
||||||
H.append("</span>")
|
H.append("</span>")
|
||||||
ue_locked, ue_locked_reason = ue_is_locked(ue["ue_id"])
|
ue_locked, ue_locked_reason = ue.is_locked()
|
||||||
ue_editable = editable and not ue_locked
|
ue_editable = editable and not ue_locked
|
||||||
if ue_editable:
|
if ue_editable:
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a class="stdlink" href="{
|
f"""<a class="stdlink" href="{
|
||||||
url_for("notes.ue_edit", scodoc_dept=g.scodoc_dept, ue_id=ue["ue_id"])
|
url_for("notes.ue_edit", scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
}">modifier</a>"""
|
}">modifier</a>"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -1231,11 +1199,14 @@ def _ue_table_ues(
|
|||||||
delete_disabled_icon,
|
delete_disabled_icon,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (iue >= len(ues) - 1) or ue["semestre_id"] != ues[iue + 1]["semestre_id"]:
|
if (iue >= len(ues) - 1) or (
|
||||||
|
ue.get_semestre_id() != ues[iue + 1].get_semestre_id()
|
||||||
|
):
|
||||||
H.append(
|
H.append(
|
||||||
f"""</ul><ul><li><a href="{url_for('notes.ue_create', scodoc_dept=g.scodoc_dept,
|
f"""</ul><ul><li><a href="{
|
||||||
formation_id=ue['formation_id'], semestre_idx=ue['semestre_id'])
|
url_for('notes.ue_create', scodoc_dept=g.scodoc_dept,
|
||||||
}">Ajouter une UE dans le semestre {ue['semestre_id'] or ''}</a></li></ul>
|
formation_id=ue.formation_id, semestre_idx=ue.semestre_id)
|
||||||
|
}">Ajouter une UE dans le semestre {ue.semestre_id or ''}</a></li></ul>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
@ -1246,7 +1217,7 @@ def _ue_table_ues(
|
|||||||
|
|
||||||
def _ue_table_matieres(
|
def _ue_table_matieres(
|
||||||
parcours,
|
parcours,
|
||||||
ue_dict: dict,
|
ue: UniteEns,
|
||||||
editable,
|
editable,
|
||||||
tag_editable,
|
tag_editable,
|
||||||
arrow_up,
|
arrow_up,
|
||||||
@ -1256,7 +1227,6 @@ def _ue_table_matieres(
|
|||||||
delete_disabled_icon,
|
delete_disabled_icon,
|
||||||
):
|
):
|
||||||
"""Édition de programme: liste des matières (et leurs modules) d'une UE."""
|
"""Édition de programme: liste des matières (et leurs modules) d'une UE."""
|
||||||
ue = UniteEns.get_ue(ue_dict["ue_id"])
|
|
||||||
H = []
|
H = []
|
||||||
if not parcours.UE_IS_MODULE:
|
if not parcours.UE_IS_MODULE:
|
||||||
H.append('<ul class="notes_matiere_list">')
|
H.append('<ul class="notes_matiere_list">')
|
||||||
@ -1503,16 +1473,18 @@ def do_ue_edit(args, bypass_lock=False, dont_invalidate_cache=False):
|
|||||||
"edit an UE"
|
"edit an UE"
|
||||||
# check
|
# check
|
||||||
ue_id = args["ue_id"]
|
ue_id = args["ue_id"]
|
||||||
ue = ue_list({"ue_id": ue_id})[0]
|
ue = UniteEns.get_ue(ue_id)
|
||||||
if not bypass_lock:
|
if not bypass_lock:
|
||||||
ue_locked, ue_locked_reason = ue_is_locked(ue["ue_id"])
|
ue_locked, ue_locked_reason = ue.is_locked()
|
||||||
if ue_locked:
|
if ue_locked:
|
||||||
raise ScoLockedFormError(msg=f"UE verrouillée: {ue_locked_reason}")
|
raise ScoLockedFormError(msg=f"UE verrouillée: {ue_locked_reason}")
|
||||||
# check: acronyme unique dans cette formation
|
# check: acronyme unique dans cette formation
|
||||||
if "acronyme" in args:
|
if "acronyme" in args:
|
||||||
new_acro = args["acronyme"]
|
new_acro = args["acronyme"]
|
||||||
ues = ue_list({"formation_id": ue["formation_id"], "acronyme": new_acro})
|
ues = UniteEns.query.filter_by(
|
||||||
if ues and ues[0]["ue_id"] != ue_id:
|
formation_id=ue.formation_id, acronyme=new_acro
|
||||||
|
).all()
|
||||||
|
if ues and ues[0].id != ue_id:
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé !
|
f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé !
|
||||||
(chaque UE doit avoir un acronyme unique dans la formation.)"""
|
(chaque UE doit avoir un acronyme unique dans la formation.)"""
|
||||||
@ -1521,48 +1493,12 @@ def do_ue_edit(args, bypass_lock=False, dont_invalidate_cache=False):
|
|||||||
if "ue_code" in args and not args["ue_code"]:
|
if "ue_code" in args and not args["ue_code"]:
|
||||||
del args["ue_code"]
|
del args["ue_code"]
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
ue.from_dict(args)
|
||||||
_ueEditor.edit(cnx, args)
|
db.session.commit()
|
||||||
|
|
||||||
formation = db.session.get(Formation, ue["formation_id"])
|
|
||||||
if not dont_invalidate_cache:
|
if not dont_invalidate_cache:
|
||||||
# Invalide les semestres utilisant cette formation
|
# Invalide les semestres utilisant cette formation
|
||||||
# ainsi que les poids et coefs
|
# ainsi que les poids et coefs
|
||||||
formation.invalidate_module_coefs()
|
ue.formation.invalidate_module_coefs()
|
||||||
|
|
||||||
|
|
||||||
def ue_is_locked(ue_id: int) -> tuple[bool, str]:
|
|
||||||
"""True if UE should not be modified:
|
|
||||||
utilisée dans un formsemestre verrouillé ou validations de jury de cette UE.
|
|
||||||
Renvoie aussi une explication.
|
|
||||||
"""
|
|
||||||
# before 9.7.23: contains modules used in a locked formsemestre
|
|
||||||
# starting from 9.7.23: + existence de validations de jury de cette UE
|
|
||||||
ue = UniteEns.query.get(ue_id)
|
|
||||||
if not ue:
|
|
||||||
return True, "inexistante"
|
|
||||||
if ue.formation.is_apc():
|
|
||||||
# en APC, interdit toute modification d'UE si utilisée dans un semestre verrouillé
|
|
||||||
if False in [formsemestre.etat for formsemestre in ue.formation.formsemestres]:
|
|
||||||
return True, "utilisée dans un semestre verrouillé"
|
|
||||||
else:
|
|
||||||
# en classique: interdit si contient des modules utilisés dans des semestres verrouillés
|
|
||||||
# en effet, dans certaines (très anciennes) formations, une UE peut avoir des modules de
|
|
||||||
# différents semestre
|
|
||||||
if (
|
|
||||||
Module.query.filter(Module.ue_id == ue_id)
|
|
||||||
.join(Module.modimpls)
|
|
||||||
.join(ModuleImpl.formsemestre)
|
|
||||||
.filter_by(etat=False)
|
|
||||||
.count()
|
|
||||||
):
|
|
||||||
return True, "avec modules utilisés dans des semestres verrouillés"
|
|
||||||
|
|
||||||
nb_validations = ScolarFormSemestreValidation.query.filter_by(ue_id=ue_id).count()
|
|
||||||
if nb_validations > 0:
|
|
||||||
return True, f"avec {nb_validations} validations de jury"
|
|
||||||
|
|
||||||
return False, ""
|
|
||||||
|
|
||||||
|
|
||||||
UE_PALETTE = [
|
UE_PALETTE = [
|
||||||
|
@ -10,6 +10,7 @@ from app.models import APO_CODE_STR_LEN
|
|||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models.but_refcomp import ApcNiveau, ApcParcours
|
from app.models.but_refcomp import ApcNiveau, ApcParcours
|
||||||
from app.models.modules import Module
|
from app.models.modules import Module
|
||||||
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
@ -185,10 +186,45 @@ class UniteEns(models.ScoDocModel):
|
|||||||
return 1 if self.semestre_idx is None else (self.semestre_idx - 1) // 2 + 1
|
return 1 if self.semestre_idx is None else (self.semestre_idx - 1) // 2 + 1
|
||||||
|
|
||||||
def is_locked(self) -> tuple[bool, str]:
|
def is_locked(self) -> tuple[bool, str]:
|
||||||
"""True if UE should not be modified"""
|
"""True if UE should not be modified:
|
||||||
from app.formations import edit_ue
|
utilisée dans un formsemestre verrouillé ou validations de jury de cette UE.
|
||||||
|
Renvoie aussi une explication.
|
||||||
|
"""
|
||||||
|
from app.models import FormSemestre, ModuleImpl, ScolarFormSemestreValidation
|
||||||
|
|
||||||
return edit_ue.ue_is_locked(self.id)
|
# before 9.7.23: contains modules used in a locked formsemestre
|
||||||
|
# starting from 9.7.23: + existence de validations de jury de cette UE
|
||||||
|
if self.formation.is_apc():
|
||||||
|
# en APC, interdit toute modification d'UE si il y a un formsemestre verrouillé
|
||||||
|
# de cette formation ayant le semestre de cette UE.
|
||||||
|
# (ne détaille pas les parcours, donc si un semestre Sn d'un parcours est verrouillé
|
||||||
|
# cela va verrouiller toutes les UE d'indice Sn, même si pas de ce parcours)
|
||||||
|
# modifié en 9.7.28
|
||||||
|
locked_sems = self.formation.formsemestres.filter_by(
|
||||||
|
etat=False, semestre_id=self.semestre_idx
|
||||||
|
)
|
||||||
|
if locked_sems.count():
|
||||||
|
return True, "utilisée dans un semestre verrouillé"
|
||||||
|
else:
|
||||||
|
# en classique: interdit si contient des modules utilisés dans des semestres verrouillés
|
||||||
|
# en effet, dans certaines (très anciennes) formations, une UE peut avoir des modules de
|
||||||
|
# différents semestre
|
||||||
|
if (
|
||||||
|
Module.query.filter(Module.ue_id == self.id)
|
||||||
|
.join(Module.modimpls)
|
||||||
|
.join(ModuleImpl.formsemestre)
|
||||||
|
.filter_by(etat=False)
|
||||||
|
.count()
|
||||||
|
):
|
||||||
|
return True, "avec modules utilisés dans des semestres verrouillés"
|
||||||
|
|
||||||
|
nb_validations = ScolarFormSemestreValidation.query.filter_by(
|
||||||
|
ue_id=self.id
|
||||||
|
).count()
|
||||||
|
if nb_validations > 0:
|
||||||
|
return True, f"avec {nb_validations} validations de jury"
|
||||||
|
|
||||||
|
return False, ""
|
||||||
|
|
||||||
def can_be_deleted(self) -> bool:
|
def can_be_deleted(self) -> bool:
|
||||||
"""True si l'UE n'a pas de moduleimpl rattachés
|
"""True si l'UE n'a pas de moduleimpl rattachés
|
||||||
@ -214,6 +250,24 @@ class UniteEns(models.ScoDocModel):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
return self.semestre_idx
|
return self.semestre_idx
|
||||||
|
|
||||||
|
def get_semestre_id(self) -> int:
|
||||||
|
"""L'indice du semestre de l'UE.
|
||||||
|
Regarde semestre_idx ou, pour les formations non APC,
|
||||||
|
le premier module de chacune.
|
||||||
|
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT (1000000),
|
||||||
|
qui les place à la fin de la liste.
|
||||||
|
Contrairement à guess_semestre_idx, ne modifie pas l'UE.
|
||||||
|
"""
|
||||||
|
if self.semestre_idx is not None:
|
||||||
|
return self.semestre_idx
|
||||||
|
if self.formation.is_apc():
|
||||||
|
return codes_cursus.UE_SEM_DEFAULT
|
||||||
|
# était le comportement ScoDoc7
|
||||||
|
module = self.modules.first()
|
||||||
|
if module:
|
||||||
|
return module.semestre_id
|
||||||
|
return codes_cursus.UE_SEM_DEFAULT
|
||||||
|
|
||||||
def get_ects(self, parcour: ApcParcours = None, only_parcours=False) -> float:
|
def get_ects(self, parcour: ApcParcours = None, only_parcours=False) -> float:
|
||||||
"""Crédits ECTS associés à cette UE.
|
"""Crédits ECTS associés à cette UE.
|
||||||
En BUT, cela peut quelquefois dépendre du parcours.
|
En BUT, cela peut quelquefois dépendre du parcours.
|
||||||
|
Loading…
Reference in New Issue
Block a user