forked from ScoDoc/ScoDoc
Backend 'Matieres': utilise uniquement modèle.
This commit is contained in:
parent
056be2e152
commit
1dfba157c2
@ -25,16 +25,14 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""Ajout/Modification/Supression matieres
|
"""Ajout/Modification/Suppression matieres
|
||||||
(portage from DTML)
|
|
||||||
"""
|
"""
|
||||||
import flask
|
import flask
|
||||||
from flask import g, render_template, request, url_for
|
from flask import flash, g, render_template, request, url_for
|
||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.models import Formation, Matiere, UniteEns, ScolarNews
|
from app.models import Matiere, UniteEns
|
||||||
|
|
||||||
import app.scodoc.notesdb as ndb
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
|
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||||
@ -44,59 +42,9 @@ from app.scodoc.sco_exceptions import (
|
|||||||
ScoNonEmptyFormationObject,
|
ScoNonEmptyFormationObject,
|
||||||
)
|
)
|
||||||
|
|
||||||
_matiereEditor = ndb.EditableTable(
|
|
||||||
"notes_matieres",
|
|
||||||
"matiere_id",
|
|
||||||
("matiere_id", "ue_id", "numero", "titre"),
|
|
||||||
sortkey="numero",
|
|
||||||
output_formators={"numero": ndb.int_null_is_zero},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def matiere_list(*args, **kw):
|
|
||||||
"list matieres"
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
return _matiereEditor.list(cnx, *args, **kw)
|
|
||||||
|
|
||||||
|
|
||||||
def do_matiere_edit(*args, **kw):
|
|
||||||
"edit a matiere"
|
|
||||||
from app.formations import edit_ue
|
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
# check
|
|
||||||
mat = matiere_list({"matiere_id": args[0]["matiere_id"]})[0]
|
|
||||||
if matiere_is_locked(mat["matiere_id"]):
|
|
||||||
raise ScoLockedFormError()
|
|
||||||
# edit
|
|
||||||
_matiereEditor.edit(cnx, *args, **kw)
|
|
||||||
formation_id = edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]["formation_id"]
|
|
||||||
db.session.get(Formation, formation_id).invalidate_cached_sems()
|
|
||||||
|
|
||||||
|
|
||||||
def do_matiere_create(args):
|
|
||||||
"create a matiere"
|
|
||||||
from app.formations import edit_ue
|
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
# check
|
|
||||||
ue = edit_ue.ue_list({"ue_id": args["ue_id"]})[0]
|
|
||||||
# create matiere
|
|
||||||
r = _matiereEditor.create(cnx, args)
|
|
||||||
|
|
||||||
# news
|
|
||||||
formation = db.session.get(Formation, ue["formation_id"])
|
|
||||||
ScolarNews.add(
|
|
||||||
typ=ScolarNews.NEWS_FORM,
|
|
||||||
obj=ue["formation_id"],
|
|
||||||
text=f"Modification de la formation {formation.acronyme}",
|
|
||||||
)
|
|
||||||
formation.invalidate_cached_sems()
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def matiere_create(ue_id=None):
|
def matiere_create(ue_id=None):
|
||||||
"""Creation d'une matiere"""
|
"""Formulaire création d'une matiere"""
|
||||||
ue: UniteEns = UniteEns.query.get_or_404(ue_id)
|
ue: UniteEns = UniteEns.query.get_or_404(ue_id)
|
||||||
default_numero = max([mat.numero for mat in ue.matieres] or [9]) + 1
|
default_numero = max([mat.numero for mat in ue.matieres] or [9]) + 1
|
||||||
H = [
|
H = [
|
||||||
@ -153,8 +101,8 @@ associé.
|
|||||||
if tf[0] == -1:
|
if tf[0] == -1:
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
# check unicity
|
# check unicity
|
||||||
mats = matiere_list(args={"ue_id": ue_id, "titre": tf[2]["titre"]})
|
nb_mats = Matiere.query.filter_by(ue_id=ue_id, titre=tf[2]["titre"]).count()
|
||||||
if mats:
|
if nb_mats:
|
||||||
return render_template(
|
return render_template(
|
||||||
"sco_page.j2",
|
"sco_page.j2",
|
||||||
title="Création d'une matière",
|
title="Création d'une matière",
|
||||||
@ -164,56 +112,14 @@ associé.
|
|||||||
+ tf[1]
|
+ tf[1]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
_ = do_matiere_create(tf[2])
|
Matiere.create_from_dict(tf[2])
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
|
||||||
def can_delete_matiere(matiere: Matiere) -> tuple[bool, str]:
|
|
||||||
"True si la matiere n'est pas utilisée dans des formsemestre"
|
|
||||||
locked = matiere_is_locked(matiere.id)
|
|
||||||
if locked:
|
|
||||||
return False
|
|
||||||
if any(m.modimpls.all() for m in matiere.modules):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def do_matiere_delete(oid):
|
|
||||||
"delete matiere and attached modules"
|
|
||||||
from app.formations import edit_module, edit_ue
|
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
# check
|
|
||||||
matiere = Matiere.query.get_or_404(oid)
|
|
||||||
mat = matiere_list({"matiere_id": oid})[0] # compat sco7
|
|
||||||
ue = edit_ue.ue_list({"ue_id": mat["ue_id"]})[0]
|
|
||||||
if not can_delete_matiere(matiere):
|
|
||||||
# il y a au moins un modimpl dans un module de cette matière
|
|
||||||
raise ScoNonEmptyFormationObject("Matière", matiere.titre)
|
|
||||||
|
|
||||||
log(f"do_matiere_delete: matiere_id={matiere.id}")
|
|
||||||
# delete all modules in this matiere
|
|
||||||
mods = edit_module.module_list({"matiere_id": matiere.id})
|
|
||||||
for mod in mods:
|
|
||||||
edit_module.do_module_delete(mod["module_id"])
|
|
||||||
_matiereEditor.delete(cnx, oid)
|
|
||||||
|
|
||||||
# news
|
|
||||||
formation = db.session.get(Formation, ue["formation_id"])
|
|
||||||
ScolarNews.add(
|
|
||||||
typ=ScolarNews.NEWS_FORM,
|
|
||||||
obj=ue["formation_id"],
|
|
||||||
text=f"Modification de la formation {formation.acronyme}",
|
|
||||||
)
|
|
||||||
formation.invalidate_cached_sems()
|
|
||||||
|
|
||||||
|
|
||||||
def matiere_delete(matiere_id=None):
|
def matiere_delete(matiere_id=None):
|
||||||
"""Delete matière"""
|
"""Form delete matière"""
|
||||||
from app.formations import edit_ue
|
matiere = Matiere.get_instance(matiere_id)
|
||||||
|
if not matiere.can_be_deleted():
|
||||||
matiere = Matiere.query.get_or_404(matiere_id)
|
|
||||||
if not can_delete_matiere(matiere):
|
|
||||||
# il y a au moins un modimpl dans un module de cette matière
|
# il y a au moins un modimpl dans un module de cette matière
|
||||||
raise ScoNonEmptyFormationObject(
|
raise ScoNonEmptyFormationObject(
|
||||||
"Matière",
|
"Matière",
|
||||||
@ -226,22 +132,20 @@ def matiere_delete(matiere_id=None):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
mat = matiere_list(args={"matiere_id": matiere_id})[0]
|
|
||||||
ue_dict = edit_ue.ue_list(args={"ue_id": mat["ue_id"]})[0]
|
|
||||||
H = [
|
H = [
|
||||||
"<h2>Suppression de la matière %(titre)s" % mat,
|
f"""<h2>Suppression de la matière {matiere.titre}
|
||||||
" dans l'UE (%(acronyme)s))</h2>" % ue_dict,
|
dans l'UE {matiere.ue.acronyme}</h2>""",
|
||||||
]
|
]
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=str(ue_dict["formation_id"]),
|
formation_id=matiere.ue.formation_id,
|
||||||
)
|
)
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
(("matiere_id", {"input_type": "hidden"}),),
|
(("matiere_id", {"input_type": "hidden"}),),
|
||||||
initvalues=mat,
|
initvalues=matiere.to_dict(),
|
||||||
submitlabel="Confirmer la suppression",
|
submitlabel="Confirmer la suppression",
|
||||||
cancelbutton="Annuler",
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
@ -254,29 +158,23 @@ def matiere_delete(matiere_id=None):
|
|||||||
if tf[0] == -1:
|
if tf[0] == -1:
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
do_matiere_delete(matiere_id)
|
matiere.delete()
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
|
||||||
def matiere_edit(matiere_id=None):
|
def matiere_edit(matiere_id=None):
|
||||||
"""Edit matiere"""
|
"""Form edit matiere"""
|
||||||
from app.formations import edit_ue
|
matiere: Matiere = Matiere.get_instance(matiere_id)
|
||||||
|
if matiere.is_locked():
|
||||||
F = matiere_list(args={"matiere_id": matiere_id})
|
raise ScoLockedFormError()
|
||||||
if not F:
|
ue = matiere.ue
|
||||||
raise ScoValueError("Matière inexistante !")
|
formation = ue.formation
|
||||||
F = F[0]
|
ues = matiere.ue.formation.ues
|
||||||
ues = edit_ue.ue_list(args={"ue_id": F["ue_id"]})
|
ue_names = [f"{u.acronyme} ({u.titre or ''})" for u in ues]
|
||||||
if not ues:
|
ue_ids = [u.id for u in ues]
|
||||||
raise ScoValueError("UE inexistante !")
|
|
||||||
ue = ues[0]
|
|
||||||
formation: Formation = Formation.query.get_or_404(ue["formation_id"])
|
|
||||||
ues = edit_ue.ue_list(args={"formation_id": ue["formation_id"]})
|
|
||||||
ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues]
|
|
||||||
ue_ids = [u["ue_id"] for u in ues]
|
|
||||||
H = [
|
H = [
|
||||||
"""<h2>Modification de la matière %(titre)s""" % F,
|
f"""<h2>Modification de la matière {matiere.titre or 'sans titre'}
|
||||||
f"""(formation ({formation.acronyme}, version {formation.version})</h2>""",
|
(formation ({formation.acronyme}, version {formation.version})</h2>""",
|
||||||
]
|
]
|
||||||
help_msg = """<p class="help">Les matières sont des groupes de modules dans une UE
|
help_msg = """<p class="help">Les matières sont des groupes de modules dans une UE
|
||||||
d'une formation donnée. Les matières servent surtout pour la
|
d'une formation donnée. Les matières servent surtout pour la
|
||||||
@ -316,14 +214,14 @@ associé.
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
initvalues=F,
|
initvalues=matiere.to_dict(),
|
||||||
submitlabel="Modifier les valeurs",
|
submitlabel="Modifier les valeurs",
|
||||||
)
|
)
|
||||||
|
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=str(ue["formation_id"]),
|
formation_id=formation.id,
|
||||||
)
|
)
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -335,8 +233,8 @@ associé.
|
|||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
else:
|
else:
|
||||||
# check unicity
|
# check unicity
|
||||||
mats = matiere_list(args={"ue_id": tf[2]["ue_id"], "titre": tf[2]["titre"]})
|
mats = Matiere.query.filter_by(ue_id=tf[2]["ue_id"], titre=tf[2]["titre"]).all()
|
||||||
if len(mats) > 1 or (len(mats) == 1 and mats[0]["matiere_id"] != matiere_id):
|
if len(mats) > 1 or (len(mats) == 1 and mats[0].id != matiere_id):
|
||||||
return render_template(
|
return render_template(
|
||||||
"sco_page.j2",
|
"sco_page.j2",
|
||||||
title="Modification d'une matière",
|
title="Modification d'une matière",
|
||||||
@ -347,32 +245,18 @@ associé.
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
modif = False
|
||||||
# changement d'UE ?
|
# changement d'UE ?
|
||||||
if tf[2]["ue_id"] != F["ue_id"]:
|
if tf[2]["ue_id"] != ue.id:
|
||||||
log("attaching mat %s to new UE %s" % (matiere_id, tf[2]["ue_id"]))
|
log(f"attaching mat {matiere_id} to new UE id={tf[2]['ue_id']}")
|
||||||
ndb.SimpleQuery(
|
new_ue = UniteEns.get_ue(tf[2]["ue_id"])
|
||||||
"UPDATE notes_modules SET ue_id = %(ue_id)s WHERE matiere_id=%(matiere_id)s",
|
if new_ue.formation_id != formation.id:
|
||||||
{"ue_id": tf[2]["ue_id"], "matiere_id": matiere_id},
|
raise ScoValueError("UE does not belong to the same formation")
|
||||||
)
|
matiere.ue = new_ue
|
||||||
|
modif = True
|
||||||
do_matiere_edit(tf[2])
|
modif |= matiere.from_dict(tf[2])
|
||||||
|
if modif:
|
||||||
|
db.session.commit()
|
||||||
|
matiere.ue.formation.invalidate_cached_sems()
|
||||||
|
flash("Matière modifiée", "info")
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
|
||||||
def matiere_is_locked(matiere_id):
|
|
||||||
"""True if matiere should not be modified
|
|
||||||
(contains modules used in a locked formsemestre)
|
|
||||||
"""
|
|
||||||
r = ndb.SimpleDictFetch(
|
|
||||||
"""SELECT ma.id
|
|
||||||
FROM notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
|
|
||||||
WHERE ma.id = mod.matiere_id
|
|
||||||
AND mi.module_id = mod.id
|
|
||||||
AND mi.formsemestre_id = sem.id
|
|
||||||
AND ma.id = %(matiere_id)s
|
|
||||||
AND sem.etat = false
|
|
||||||
""",
|
|
||||||
{"matiere_id": matiere_id},
|
|
||||||
)
|
|
||||||
return len(r) > 0
|
|
||||||
|
@ -767,6 +767,7 @@ def module_edit(
|
|||||||
module_dict["semestre_id"] = 1
|
module_dict["semestre_id"] = 1
|
||||||
else:
|
else:
|
||||||
module_dict["semestre_id"] = module.ue.semestre_idx
|
module_dict["semestre_id"] = module.ue.semestre_idx
|
||||||
|
tags = module.tags if module else []
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
@ -774,7 +775,9 @@ def module_edit(
|
|||||||
html_foot_markup=(
|
html_foot_markup=(
|
||||||
f"""<div class="scobox sco_tag_module_edit"><span
|
f"""<div class="scobox sco_tag_module_edit"><span
|
||||||
class="sco_tag_edit"><textarea data-module_id="{module_id}" class="module_tag_editor"
|
class="sco_tag_edit"><textarea data-module_id="{module_id}" class="module_tag_editor"
|
||||||
>{','.join(sco_tag_module.module_tag_list(module_id))}</textarea></span></div>
|
>{
|
||||||
|
','.join(t.title for t in tags)
|
||||||
|
}</textarea></span></div>
|
||||||
"""
|
"""
|
||||||
if not create
|
if not create
|
||||||
else ""
|
else ""
|
||||||
@ -833,10 +836,14 @@ def module_edit(
|
|||||||
if matiere:
|
if matiere:
|
||||||
tf[2]["matiere_id"] = matiere.id
|
tf[2]["matiere_id"] = matiere.id
|
||||||
else:
|
else:
|
||||||
matiere_id = edit_matiere.do_matiere_create(
|
matiere = Matiere.create_from_dict(
|
||||||
{"ue_id": ue.id, "titre": ue.titre or "", "numero": 1},
|
{
|
||||||
|
"ue_id": ue.id,
|
||||||
|
"titre": ue.titre or "",
|
||||||
|
"numero": 1,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
tf[2]["matiere_id"] = matiere_id
|
tf[2]["matiere_id"] = matiere.id
|
||||||
|
|
||||||
tf[2]["semestre_id"] = ue.semestre_idx
|
tf[2]["semestre_id"] = ue.semestre_idx
|
||||||
module_id = do_module_create(tf[2])
|
module_id = do_module_create(tf[2])
|
||||||
@ -946,12 +953,6 @@ def module_is_locked(module_id):
|
|||||||
return len(r) > 0
|
return len(r) > 0
|
||||||
|
|
||||||
|
|
||||||
def module_count_moduleimpls(module_id):
|
|
||||||
"Number of moduleimpls using this module"
|
|
||||||
mods = sco_moduleimpl.moduleimpl_list(module_id=module_id)
|
|
||||||
return len(mods)
|
|
||||||
|
|
||||||
|
|
||||||
def formation_add_malus_modules(
|
def formation_add_malus_modules(
|
||||||
formation_id: int, semestre_id: int = None, titre=None, redirect=True
|
formation_id: int, semestre_id: int = None, titre=None, redirect=True
|
||||||
):
|
):
|
||||||
|
@ -547,9 +547,10 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
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)
|
||||||
matiere_id = edit_matiere.do_matiere_create(
|
matiere = Matiere.create_from_dict(
|
||||||
{"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1},
|
{"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1}
|
||||||
)
|
)
|
||||||
|
matiere_id = matiere.id
|
||||||
if cursus.UE_IS_MODULE:
|
if cursus.UE_IS_MODULE:
|
||||||
# dans ce mode, crée un (unique) module dans l'UE:
|
# dans ce mode, crée un (unique) module dans l'UE:
|
||||||
_ = edit_module.do_module_create(
|
_ = edit_module.do_module_create(
|
||||||
@ -676,9 +677,10 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False):
|
|||||||
return do_ue_delete(ue, delete_validations=delete_validations)
|
return do_ue_delete(ue, delete_validations=delete_validations)
|
||||||
|
|
||||||
|
|
||||||
def ue_table(formation_id=None, semestre_idx=1, msg=""): # was ue_list
|
def ue_table(formation_id=None, semestre_idx=1, msg=""):
|
||||||
"""Liste des matières et modules d'une formation, avec liens pour
|
"""Page affiochage ou édition d'une formation
|
||||||
éditer (si non verrouillée).
|
avec UEs, matières et module,
|
||||||
|
et liens pour éditer si non verrouillée et permission.
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_formsemestre_validation
|
from app.scodoc import sco_formsemestre_validation
|
||||||
|
|
||||||
@ -1240,7 +1242,7 @@ def _ue_table_ues(
|
|||||||
|
|
||||||
def _ue_table_matieres(
|
def _ue_table_matieres(
|
||||||
parcours,
|
parcours,
|
||||||
ue,
|
ue_dict: dict,
|
||||||
editable,
|
editable,
|
||||||
tag_editable,
|
tag_editable,
|
||||||
arrow_up,
|
arrow_up,
|
||||||
@ -1250,26 +1252,27 @@ 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">')
|
||||||
matieres = edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
matieres = ue.matieres.all()
|
||||||
for mat in matieres:
|
for mat in matieres:
|
||||||
if not parcours.UE_IS_MODULE:
|
if not parcours.UE_IS_MODULE:
|
||||||
H.append('<li class="notes_matiere_list">')
|
H.append('<li class="notes_matiere_list">')
|
||||||
if editable and not edit_matiere.matiere_is_locked(mat["matiere_id"]):
|
if editable and not mat.is_locked():
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a class="stdlink" href="{
|
f"""<a class="stdlink" href="{
|
||||||
url_for("notes.matiere_edit",
|
url_for("notes.matiere_edit",
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])
|
scodoc_dept=g.scodoc_dept, matiere_id=mat.id)
|
||||||
}">
|
}">
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
H.append("%(titre)s" % mat)
|
H.append(f"{mat.titre or 'sans titre'}")
|
||||||
if editable and not edit_matiere.matiere_is_locked(mat["matiere_id"]):
|
if editable and not mat.is_locked():
|
||||||
H.append("</a>")
|
H.append("</a>")
|
||||||
|
|
||||||
modules = edit_module.module_list(args={"matiere_id": mat["matiere_id"]})
|
modules = mat.modules.all()
|
||||||
H.append(
|
H.append(
|
||||||
_ue_table_modules(
|
_ue_table_modules(
|
||||||
parcours,
|
parcours,
|
||||||
@ -1291,14 +1294,17 @@ def _ue_table_matieres(
|
|||||||
H.append("<li>Aucune matière dans cette UE ! ")
|
H.append("<li>Aucune matière dans cette UE ! ")
|
||||||
if editable:
|
if editable:
|
||||||
H.append(
|
H.append(
|
||||||
"""<a class="stdlink" href="ue_delete?ue_id=%(ue_id)s">supprimer l'UE</a>"""
|
f"""<a class="stdlink" href="{
|
||||||
% ue
|
url_for('notes.ue_delete', scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
|
}">supprimer l'UE</a>"""
|
||||||
)
|
)
|
||||||
H.append("</li>")
|
H.append("</li>")
|
||||||
if editable and not parcours.UE_IS_MODULE:
|
if editable and not parcours.UE_IS_MODULE:
|
||||||
H.append(
|
H.append(
|
||||||
'<li><a class="stdlink" href="matiere_create?ue_id=%(ue_id)s">créer une matière</a> </li>'
|
f"""<li><a class="stdlink" href="{
|
||||||
% ue
|
url_for("notes.matiere_create", scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
|
}">créer une matière</a>
|
||||||
|
</li>"""
|
||||||
)
|
)
|
||||||
if not parcours.UE_IS_MODULE:
|
if not parcours.UE_IS_MODULE:
|
||||||
H.append("</ul>")
|
H.append("</ul>")
|
||||||
@ -1307,9 +1313,9 @@ def _ue_table_matieres(
|
|||||||
|
|
||||||
def _ue_table_modules(
|
def _ue_table_modules(
|
||||||
parcours,
|
parcours,
|
||||||
ue,
|
ue: UniteEns,
|
||||||
mat,
|
mat: Matiere,
|
||||||
modules,
|
modules: list[Module],
|
||||||
editable,
|
editable,
|
||||||
tag_editable,
|
tag_editable,
|
||||||
arrow_up,
|
arrow_up,
|
||||||
@ -1326,89 +1332,84 @@ def _ue_table_modules(
|
|||||||
H = ['<ul class="notes_module_list">']
|
H = ['<ul class="notes_module_list">']
|
||||||
im = 0
|
im = 0
|
||||||
for mod in modules:
|
for mod in modules:
|
||||||
mod["nb_moduleimpls"] = edit_module.module_count_moduleimpls(mod["module_id"])
|
nb_moduleimpls = mod.modimpls.count()
|
||||||
klass = "notes_module_list"
|
klass = "notes_module_list"
|
||||||
if mod["module_type"] == ModuleType.MALUS:
|
if mod.module_type == ModuleType.MALUS:
|
||||||
klass += " module_malus"
|
klass += " module_malus"
|
||||||
H.append('<li class="%s">' % klass)
|
H.append(f'<li class="{klass}">')
|
||||||
|
|
||||||
H.append('<span class="notes_module_list_buts">')
|
H.append('<span class="notes_module_list_buts">')
|
||||||
if im != 0 and editable:
|
if im != 0 and editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<a href="module_move?module_id=%s&after=0" class="aud">%s</a>'
|
f"""<a href="module_move?module_id={mod.id}&after=0" class="aud">{arrow_up}</a>"""
|
||||||
% (mod["module_id"], arrow_up)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append(arrow_none)
|
H.append(arrow_none)
|
||||||
if im < len(modules) - 1 and editable:
|
if im < len(modules) - 1 and editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<a href="module_move?module_id=%s&after=1" class="aud">%s</a>'
|
f"""<a href="module_move?module_id={mod.id}&after=1" class="aud">{arrow_down}</a>"""
|
||||||
% (mod["module_id"], arrow_down)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append(arrow_none)
|
H.append(arrow_none)
|
||||||
im += 1
|
im += 1
|
||||||
if mod["nb_moduleimpls"] == 0 and editable:
|
icon = delete_icon if nb_moduleimpls == 0 and editable else delete_disabled_icon
|
||||||
icon = delete_icon
|
|
||||||
else:
|
|
||||||
icon = delete_disabled_icon
|
|
||||||
H.append(
|
H.append(
|
||||||
'<a class="smallbutton" href="module_delete?module_id=%s">%s</a>'
|
f"""<a class="smallbutton" href="{
|
||||||
% (mod["module_id"], icon)
|
url_for("notes.module_delete", scodoc_dept=g.scodoc_dept, module_id=mod.id)
|
||||||
|
}">{icon}</a>"""
|
||||||
)
|
)
|
||||||
|
|
||||||
H.append("</span>")
|
H.append("</span>")
|
||||||
|
|
||||||
mod_editable = (
|
mod_editable = editable
|
||||||
editable # and not edit_module.module_is_locked( Mod['module_id'])
|
# and not edit_module.module_is_locked(Mod['module_id'])
|
||||||
)
|
|
||||||
if mod_editable:
|
if mod_editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<a class="discretelink" title="Modifier le module numéro %(numero)s, utilisé par %(nb_moduleimpls)d sessions" href="module_edit?module_id=%(module_id)s">'
|
f"""<a class="discretelink" title="Modifier le module numéro %(numero)s, utilisé par
|
||||||
% mod
|
{nb_moduleimpls} sessions" href="{
|
||||||
|
url_for("notes.module_edit", scodoc_dept=g.scodoc_dept, module_id=mod.id)
|
||||||
|
}">"""
|
||||||
)
|
)
|
||||||
if mod["module_type"] not in (scu.ModuleType.STANDARD, scu.ModuleType.MALUS):
|
if mod.module_type not in (scu.ModuleType.STANDARD, scu.ModuleType.MALUS):
|
||||||
H.append(
|
H.append(
|
||||||
f"""<span class="invalid-module-type">{scu.EMO_WARNING} type incompatible </span>"""
|
f"""<span class="invalid-module-type">{scu.EMO_WARNING} type incompatible </span>"""
|
||||||
)
|
)
|
||||||
H.append(
|
H.append(
|
||||||
'<span class="formation_module_tit">%s</span>'
|
f"""<span class="formation_module_tit">{scu.join_words(mod.code, mod.titre)}</span>"""
|
||||||
% scu.join_words(mod["code"], mod["titre"])
|
|
||||||
)
|
)
|
||||||
if mod_editable:
|
if mod_editable:
|
||||||
H.append("</a>")
|
H.append("</a>")
|
||||||
heurescoef = (
|
heures = (
|
||||||
"%(heures_cours)s/%(heures_td)s/%(heures_tp)s, coef. %(coefficient)s" % mod
|
f"""{mod.heures_cours or 0}/{mod.heures_td or 0}/{mod.heures_tp or 0}, """
|
||||||
|
if (mod.heures_cours or mod.heures_td or mod.heures_tp)
|
||||||
|
else ""
|
||||||
)
|
)
|
||||||
|
heurescoef = f"""{heures}coef. {mod.coefficient}"""
|
||||||
edit_url = url_for(
|
edit_url = url_for(
|
||||||
"apiweb.formation_module_set_code_apogee",
|
"apiweb.formation_module_set_code_apogee",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
module_id=mod["module_id"],
|
module_id=mod.id,
|
||||||
)
|
)
|
||||||
heurescoef += f""", Apo: <span
|
heurescoef += f""", Apo: <span
|
||||||
class="{'span_apo_edit' if editable else ''}"
|
class="{'span_apo_edit' if editable else ''}"
|
||||||
data-url="{edit_url}" id="{mod["module_id"]}"
|
data-url="{edit_url}" id="{mod.id}"
|
||||||
data-placeholder="{scu.APO_MISSING_CODE_STR}">{
|
data-placeholder="{scu.APO_MISSING_CODE_STR}">{
|
||||||
mod["code_apogee"] or ""
|
mod.code_apogee or ""
|
||||||
}</span>"""
|
}</span>"""
|
||||||
if tag_editable:
|
tag_cls = "module_tag_editor" if tag_editable else "module_tag_editor_ro"
|
||||||
tag_cls = "module_tag_editor"
|
tag_edit = f"""<span class="sco_tag_edit">
|
||||||
else:
|
<form>
|
||||||
tag_cls = "module_tag_editor_ro"
|
<textarea data-module_id="{mod.id}" class="{tag_cls}">{
|
||||||
tag_mk = """<span class="sco_tag_edit"><form><textarea data-module_id="{}" class="{}">{}</textarea></form></span>"""
|
",".join([ tag.title for tag in mod.tags ])
|
||||||
tag_edit = tag_mk.format(
|
}</textarea>
|
||||||
mod["module_id"],
|
</form>
|
||||||
tag_cls,
|
</span>"""
|
||||||
",".join(sco_tag_module.module_tag_list(mod["module_id"])),
|
if ue.semestre_idx is not None and mod.semestre_id != ue.semestre_idx:
|
||||||
)
|
|
||||||
if ue["semestre_idx"] is not None and mod["semestre_id"] != ue["semestre_idx"]:
|
|
||||||
warning_semestre = ' <span class="red">incohérent ?</span>'
|
warning_semestre = ' <span class="red">incohérent ?</span>'
|
||||||
else:
|
else:
|
||||||
warning_semestre = ""
|
warning_semestre = ""
|
||||||
H.append(
|
H.append(
|
||||||
" %s %s%s" % (parcours.SESSION_NAME, mod["semestre_id"], warning_semestre)
|
f""" {parcours.SESSION_NAME} {mod.semestre_id}{warning_semestre}
|
||||||
+ " (%s)" % heurescoef
|
{heurescoef}{tag_edit}"""
|
||||||
+ tag_edit
|
|
||||||
)
|
)
|
||||||
H.append("</li>")
|
H.append("</li>")
|
||||||
if not modules:
|
if not modules:
|
||||||
@ -1417,7 +1418,7 @@ def _ue_table_modules(
|
|||||||
H.append(
|
H.append(
|
||||||
f"""<a class="stdlink" href="{
|
f"""<a class="stdlink" href="{
|
||||||
url_for("notes.matiere_delete",
|
url_for("notes.matiere_delete",
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
scodoc_dept=g.scodoc_dept, matiere_id=mat.id)}"
|
||||||
>la supprimer</a>
|
>la supprimer</a>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
@ -1426,7 +1427,7 @@ def _ue_table_modules(
|
|||||||
H.append(
|
H.append(
|
||||||
f"""<li> <a class="stdlink" href="{
|
f"""<li> <a class="stdlink" href="{
|
||||||
url_for("notes.module_create",
|
url_for("notes.module_create",
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
scodoc_dept=g.scodoc_dept, matiere_id=mat.id)}"
|
||||||
>{create_element_msg}</a></li>
|
>{create_element_msg}</a></li>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -36,8 +36,8 @@ from flask_login import current_user
|
|||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app import db
|
from app import db
|
||||||
from app import log
|
from app import log
|
||||||
from app.formations import edit_matiere, edit_module, edit_ue
|
from app.formations import edit_module, edit_ue
|
||||||
from app.models import Formation, FormSemestre, Module, UniteEns
|
from app.models import Formation, FormSemestre, Matiere, Module, UniteEns
|
||||||
from app.models import ScolarNews
|
from app.models import ScolarNews
|
||||||
from app.models.but_refcomp import (
|
from app.models.but_refcomp import (
|
||||||
ApcAppCritique,
|
ApcAppCritique,
|
||||||
@ -113,35 +113,35 @@ def formation_export_dict(
|
|||||||
ue_dict.pop("code_apogee_rcue", None)
|
ue_dict.pop("code_apogee_rcue", None)
|
||||||
if ue_dict.get("ects") is None:
|
if ue_dict.get("ects") is None:
|
||||||
ue_dict.pop("ects", None)
|
ue_dict.pop("ects", None)
|
||||||
mats = edit_matiere.matiere_list({"ue_id": ue.id})
|
mats = ue.matieres.all()
|
||||||
mats.sort(key=lambda m: m["numero"] or 0)
|
mats.sort(key=lambda m: m.numero)
|
||||||
ue_dict["matiere"] = mats
|
mats_dict = [mat.to_dict() for mat in mats]
|
||||||
for mat in mats:
|
ue_dict["matiere"] = mats_dict
|
||||||
matiere_id = mat["matiere_id"]
|
for mat_d in mats_dict:
|
||||||
|
matiere_id = mat_d["matiere_id"]
|
||||||
if not export_ids:
|
if not export_ids:
|
||||||
del mat["id"]
|
del mat_d["id"]
|
||||||
del mat["matiere_id"]
|
del mat_d["matiere_id"]
|
||||||
del mat["ue_id"]
|
del mat_d["ue_id"]
|
||||||
mods = edit_module.module_list({"matiere_id": matiere_id})
|
mods = edit_module.module_list({"matiere_id": matiere_id})
|
||||||
mods.sort(key=lambda m: (m["numero"] or 0, m["code"]))
|
mods.sort(key=lambda m: (m["numero"] or 0, m["code"]))
|
||||||
mat["module"] = mods
|
mat_d["module"] = mods
|
||||||
for mod in mods:
|
for mod_d in mods:
|
||||||
module_id = mod["module_id"]
|
module: Module = db.session.get(Module, mod_d["module_id"])
|
||||||
if export_tags:
|
if export_tags:
|
||||||
tags = sco_tag_module.module_tag_list(module_id=mod["module_id"])
|
tags = [t.title for t in module.tags]
|
||||||
if tags:
|
if tags:
|
||||||
mod["tags"] = [{"name": x} for x in tags]
|
mod_d["tags"] = [{"name": x} for x in tags]
|
||||||
#
|
#
|
||||||
module: Module = db.session.get(Module, module_id)
|
|
||||||
if module.is_apc():
|
if module.is_apc():
|
||||||
# Exporte les coefficients
|
# Exporte les coefficients
|
||||||
if ue_reference_style == "id":
|
if ue_reference_style == "id":
|
||||||
mod["coefficients"] = [
|
mod_d["coefficients"] = [
|
||||||
{"ue_reference": str(ue_id), "coef": str(coef)}
|
{"ue_reference": str(ue_id), "coef": str(coef)}
|
||||||
for (ue_id, coef) in module.get_ue_coef_dict().items()
|
for (ue_id, coef) in module.get_ue_coef_dict().items()
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
mod["coefficients"] = [
|
mod_d["coefficients"] = [
|
||||||
{"ue_reference": ue_acronyme, "coef": str(coef)}
|
{"ue_reference": ue_acronyme, "coef": str(coef)}
|
||||||
for (
|
for (
|
||||||
ue_acronyme,
|
ue_acronyme,
|
||||||
@ -149,29 +149,29 @@ def formation_export_dict(
|
|||||||
) in module.get_ue_coef_dict_acronyme().items()
|
) in module.get_ue_coef_dict_acronyme().items()
|
||||||
]
|
]
|
||||||
# Et les parcours
|
# Et les parcours
|
||||||
mod["parcours"] = [
|
mod_d["parcours"] = [
|
||||||
p.to_dict(with_annees=False) for p in module.parcours
|
p.to_dict(with_annees=False) for p in module.parcours
|
||||||
]
|
]
|
||||||
# Et les AC
|
# Et les AC
|
||||||
if ac_as_list:
|
if ac_as_list:
|
||||||
# XML préfère une liste
|
# XML préfère une liste
|
||||||
mod["app_critiques"] = [
|
mod_d["app_critiques"] = [
|
||||||
x.to_dict(with_code=True) for x in module.app_critiques
|
x.to_dict(with_code=True) for x in module.app_critiques
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
mod["app_critiques"] = {
|
mod_d["app_critiques"] = {
|
||||||
x.code: x.to_dict() for x in module.app_critiques
|
x.code: x.to_dict() for x in module.app_critiques
|
||||||
}
|
}
|
||||||
if not export_ids:
|
if not export_ids:
|
||||||
del mod["id"]
|
del mod_d["id"]
|
||||||
del mod["ue_id"]
|
del mod_d["ue_id"]
|
||||||
del mod["matiere_id"]
|
del mod_d["matiere_id"]
|
||||||
del mod["module_id"]
|
del mod_d["module_id"]
|
||||||
del mod["formation_id"]
|
del mod_d["formation_id"]
|
||||||
if not export_codes_apo:
|
if not export_codes_apo:
|
||||||
del mod["code_apogee"]
|
del mod_d["code_apogee"]
|
||||||
if mod["ects"] is None:
|
if mod_d["ects"] is None:
|
||||||
del mod["ects"]
|
del mod_d["ects"]
|
||||||
return f_dict
|
return f_dict
|
||||||
|
|
||||||
|
|
||||||
@ -399,7 +399,8 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
|
|||||||
|
|
||||||
assert mat_info[0] == "matiere"
|
assert mat_info[0] == "matiere"
|
||||||
mat_info[1]["ue_id"] = ue_id
|
mat_info[1]["ue_id"] = ue_id
|
||||||
mat_id = edit_matiere.do_matiere_create(mat_info[1])
|
mat = Matiere.create_from_dict(mat_info[1])
|
||||||
|
mat_id = mat.id
|
||||||
# -- create modules
|
# -- create modules
|
||||||
for mod_info in mat_info[2]:
|
for mod_info in mat_info[2]:
|
||||||
assert mod_info[0] == "module"
|
assert mod_info[0] == "module"
|
||||||
@ -460,7 +461,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
|
|||||||
f"Warning: parcours {code_parcours} inexistant !"
|
f"Warning: parcours {code_parcours} inexistant !"
|
||||||
)
|
)
|
||||||
if import_tags and tag_names:
|
if import_tags and tag_names:
|
||||||
sco_tag_module.module_tag_set(mod_id, tag_names)
|
module.set_tags(tag_names)
|
||||||
if module.is_apc() and ue_coef_dict:
|
if module.is_apc() and ue_coef_dict:
|
||||||
modules_a_coefficienter.append((module, ue_coef_dict))
|
modules_a_coefficienter.append((module, ue_coef_dict))
|
||||||
# Fixe les coefs APC (à la fin pour que les UE soient créées)
|
# Fixe les coefs APC (à la fin pour que les UE soient créées)
|
||||||
|
@ -5,7 +5,7 @@ from flask import abort, g
|
|||||||
from flask_sqlalchemy.query import Query
|
from flask_sqlalchemy.query import Query
|
||||||
|
|
||||||
import app
|
import app
|
||||||
from app import db
|
from app import db, log
|
||||||
from app.comp import df_cache
|
from app.comp import df_cache
|
||||||
from app.models import ScoDocModel, SHORT_STR_LEN
|
from app.models import ScoDocModel, SHORT_STR_LEN
|
||||||
from app.models.but_refcomp import (
|
from app.models.but_refcomp import (
|
||||||
@ -14,6 +14,7 @@ from app.models.but_refcomp import (
|
|||||||
ApcParcours,
|
ApcParcours,
|
||||||
ApcParcoursNiveauCompetence,
|
ApcParcoursNiveauCompetence,
|
||||||
)
|
)
|
||||||
|
from app.models.events import ScolarNews
|
||||||
from app.models.modules import Module
|
from app.models.modules import Module
|
||||||
from app.models.moduleimpls import ModuleImpl
|
from app.models.moduleimpls import ModuleImpl
|
||||||
from app.models.ues import UniteEns, UEParcours
|
from app.models.ues import UniteEns, UEParcours
|
||||||
@ -21,6 +22,7 @@ from app.scodoc import sco_cache
|
|||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.codes_cursus import UE_STANDARD
|
from app.scodoc.codes_cursus import UE_STANDARD
|
||||||
|
from app.scodoc.sco_exceptions import ScoNonEmptyFormationObject, ScoValueError
|
||||||
|
|
||||||
|
|
||||||
class Formation(ScoDocModel):
|
class Formation(ScoDocModel):
|
||||||
@ -166,6 +168,7 @@ class Formation(ScoDocModel):
|
|||||||
sco_cache.invalidate_formsemestre()
|
sco_cache.invalidate_formsemestre()
|
||||||
|
|
||||||
def invalidate_cached_sems(self):
|
def invalidate_cached_sems(self):
|
||||||
|
"Invalide caches de tous les formssemestres de la formation"
|
||||||
for sem in self.formsemestres:
|
for sem in self.formsemestres:
|
||||||
sco_cache.invalidate_formsemestre(formsemestre_id=sem.id)
|
sco_cache.invalidate_formsemestre(formsemestre_id=sem.id)
|
||||||
|
|
||||||
@ -312,7 +315,9 @@ class Matiere(ScoDocModel):
|
|||||||
titre = db.Column(db.Text())
|
titre = db.Column(db.Text())
|
||||||
numero = db.Column(db.Integer, nullable=False, default=0) # ordre de présentation
|
numero = db.Column(db.Integer, nullable=False, default=0) # ordre de présentation
|
||||||
|
|
||||||
modules = db.relationship("Module", lazy="dynamic", backref="matiere")
|
modules = db.relationship(
|
||||||
|
"Module", lazy="dynamic", backref="matiere", cascade="all, delete-orphan"
|
||||||
|
)
|
||||||
_sco_dept_relations = ("UniteEns", "Formation") # accès au dept_id
|
_sco_dept_relations = ("UniteEns", "Formation") # accès au dept_id
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -325,5 +330,75 @@ class Matiere(ScoDocModel):
|
|||||||
e.pop("_sa_instance_state", None)
|
e.pop("_sa_instance_state", None)
|
||||||
# ScoDoc7 output_formators
|
# ScoDoc7 output_formators
|
||||||
e["numero"] = e["numero"] if e["numero"] else 0
|
e["numero"] = e["numero"] if e["numero"] else 0
|
||||||
e["ue_id"] = self.id
|
e["matiere_id"] = self.id
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
def is_locked(self) -> bool:
|
||||||
|
"""True if matiere cannot be be modified
|
||||||
|
because it contains modules used in a locked formsemestre.
|
||||||
|
"""
|
||||||
|
from app.models.formsemestre import FormSemestre
|
||||||
|
|
||||||
|
mat = (
|
||||||
|
db.session.query(Matiere)
|
||||||
|
.filter_by(id=self.id)
|
||||||
|
.join(Module)
|
||||||
|
.join(ModuleImpl)
|
||||||
|
.join(FormSemestre)
|
||||||
|
.filter_by(etat=False)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return bool(mat)
|
||||||
|
|
||||||
|
def can_be_deleted(self) -> bool:
|
||||||
|
"True si la matiere n'est pas utilisée dans des formsemestres"
|
||||||
|
locked = self.is_locked()
|
||||||
|
if locked:
|
||||||
|
return False
|
||||||
|
if any(m.modimpls.all() for m in self.modules):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"Delete matière. News, inval cache."
|
||||||
|
from app.models import ScolarNews
|
||||||
|
|
||||||
|
formation = self.ue.formation
|
||||||
|
log(f"matiere.delete: matiere_id={self.id}")
|
||||||
|
if not self.can_be_deleted():
|
||||||
|
# il y a au moins un modimpl dans un module de cette matière
|
||||||
|
raise ScoNonEmptyFormationObject("Matière", self.titre)
|
||||||
|
db.session.delete(self)
|
||||||
|
db.session.commit()
|
||||||
|
# news
|
||||||
|
ScolarNews.add(
|
||||||
|
typ=ScolarNews.NEWS_FORM,
|
||||||
|
obj=formation.id,
|
||||||
|
text=f"Modification de la formation {formation.acronyme}",
|
||||||
|
)
|
||||||
|
# cache
|
||||||
|
formation.invalidate_cached_sems()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_from_dict(cls, data: dict) -> "Matiere":
|
||||||
|
"""Create matière from dict. Log, news, cache.
|
||||||
|
data must include ue_id, a valid UE id.
|
||||||
|
Commit session.
|
||||||
|
"""
|
||||||
|
# check ue
|
||||||
|
if data.get("ue_id") is None:
|
||||||
|
raise ScoValueError("UE id missing")
|
||||||
|
_ = UniteEns.get_ue(data["ue_id"])
|
||||||
|
|
||||||
|
mat = super().create_from_dict(data)
|
||||||
|
db.session.commit()
|
||||||
|
db.session.refresh(mat)
|
||||||
|
# news
|
||||||
|
formation = mat.ue.formation
|
||||||
|
ScolarNews.add(
|
||||||
|
typ=ScolarNews.NEWS_FORM,
|
||||||
|
obj=formation.id,
|
||||||
|
text=f"Modification de la formation {formation.acronyme}",
|
||||||
|
)
|
||||||
|
formation.invalidate_cached_sems()
|
||||||
|
return mat
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
"""ScoDoc 9 models : Modules
|
"""ScoDoc 9 models : Modules
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import http
|
||||||
from flask import current_app, g
|
from flask import current_app, g
|
||||||
|
|
||||||
from app import db
|
from app import db, log
|
||||||
from app import models
|
from app import models
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models.but_refcomp import (
|
from app.models.but_refcomp import (
|
||||||
@ -75,11 +76,11 @@ class Module(models.ScoDocModel):
|
|||||||
backref=db.backref("modules", lazy=True),
|
backref=db.backref("modules", lazy=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
_sco_dept_relations = "Formation" # accès au dept_id
|
_sco_dept_relations = ("Formation",) # accès au dept_id
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.ue_coefs = []
|
self.ue_coefs = []
|
||||||
super(Module, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"""<Module{ModuleType(self.module_type or ModuleType.STANDARD).name
|
return f"""<Module{ModuleType(self.module_type or ModuleType.STANDARD).name
|
||||||
@ -449,6 +450,43 @@ class Module(models.ScoDocModel):
|
|||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
|
|
||||||
|
def set_tags(self, taglist: str | list[str] | None = None):
|
||||||
|
"""taglist may either be:
|
||||||
|
a string with tag names separated by commas ("un,deux")
|
||||||
|
or a list of strings (["un", "deux"])
|
||||||
|
Remplace les tags existants
|
||||||
|
"""
|
||||||
|
# TODO refactoring ScoTag
|
||||||
|
# TODO code à moderniser (+ revoir classe ScoTag, utiliser modèle)
|
||||||
|
# TODO Voir ItemSuiviTag et api etud_suivi
|
||||||
|
from app.scodoc.sco_tag_module import ScoTag, ModuleTag
|
||||||
|
|
||||||
|
taglist = taglist or []
|
||||||
|
if isinstance(taglist, str):
|
||||||
|
taglist = taglist.split(",")
|
||||||
|
taglist = [t.strip() for t in taglist]
|
||||||
|
taglist = [t for t in taglist if t]
|
||||||
|
log(f"module.set_tags: module_id={self.id} taglist={taglist}")
|
||||||
|
# Check tags syntax
|
||||||
|
for tag in taglist:
|
||||||
|
if not ScoTag.check_tag_title(tag):
|
||||||
|
log(f"module.set_tags({self.id}): invalid tag title")
|
||||||
|
return scu.json_error(404, "invalid tag")
|
||||||
|
|
||||||
|
newtags = set(taglist)
|
||||||
|
oldtags = set(t.title for t in self.tags)
|
||||||
|
to_del = oldtags - newtags
|
||||||
|
to_add = newtags - oldtags
|
||||||
|
|
||||||
|
# should be atomic, but it's not.
|
||||||
|
for tagname in to_add:
|
||||||
|
t = ModuleTag(tagname, object_id=self.id)
|
||||||
|
for tagname in to_del:
|
||||||
|
t = ModuleTag(tagname)
|
||||||
|
t.remove_tag_from_object(self.id)
|
||||||
|
|
||||||
|
return "", http.HTTPStatus.NO_CONTENT
|
||||||
|
|
||||||
|
|
||||||
class ModuleUECoef(db.Model):
|
class ModuleUECoef(db.Model):
|
||||||
"""Coefficients des modules vers les UE (APC, BUT)
|
"""Coefficients des modules vers les UE (APC, BUT)
|
||||||
@ -499,7 +537,7 @@ class ModuleUECoef(db.Model):
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class NotesTag(db.Model):
|
class NotesTag(models.ScoDocModel):
|
||||||
"""Tag sur un module"""
|
"""Tag sur un module"""
|
||||||
|
|
||||||
__tablename__ = "notes_tags"
|
__tablename__ = "notes_tags"
|
||||||
@ -511,6 +549,9 @@ class NotesTag(db.Model):
|
|||||||
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
|
dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True)
|
||||||
title = db.Column(db.Text(), nullable=False)
|
title = db.Column(db.Text(), nullable=False)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Tag {self.id} {self.title!r}>"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_or_create(cls, title: str, dept_id: int | None = None) -> "NotesTag":
|
def get_or_create(cls, title: str, dept_id: int | None = None) -> "NotesTag":
|
||||||
"""Get tag, or create it if it doesn't yet exists.
|
"""Get tag, or create it if it doesn't yet exists.
|
||||||
|
@ -274,7 +274,8 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
pole=None,
|
pole=None,
|
||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
"""Calcule la moyenne par UE des étudiants pour un tag donné,
|
"""Calcule la moyenne par UE des étudiants pour un tag donné,
|
||||||
en ayant connaissance des informations sur le tag et des inscriptions des étudiants aux différentes UEs.
|
en ayant connaissance des informations sur le tag et des inscriptions
|
||||||
|
des étudiants aux différentes UEs.
|
||||||
|
|
||||||
info_tag détermine les modules pris en compte :
|
info_tag détermine les modules pris en compte :
|
||||||
* si non `None`, seuls les modules rattachés au tag sont pris en compte
|
* si non `None`, seuls les modules rattachés au tag sont pris en compte
|
||||||
@ -342,7 +343,8 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
colonnes = [ue.id for ue in self.ues_standards]
|
colonnes = [ue.id for ue in self.ues_standards]
|
||||||
moyennes_ues_tag = moyennes_ues_tag[colonnes]
|
moyennes_ues_tag = moyennes_ues_tag[colonnes]
|
||||||
|
|
||||||
# Applique le masque d'inscription aux UE pour ne conserver que les UE dans lequel l'étudiant est inscrit
|
# Applique le masque d'inscription aux UE pour ne conserver
|
||||||
|
# que les UE dans lequel l'étudiant est inscrit
|
||||||
moyennes_ues_tag = moyennes_ues_tag[colonnes] * ues_inscr_parcours_df[colonnes]
|
moyennes_ues_tag = moyennes_ues_tag[colonnes] * ues_inscr_parcours_df[colonnes]
|
||||||
|
|
||||||
# Transforme les UEs en acronyme
|
# Transforme les UEs en acronyme
|
||||||
@ -405,7 +407,7 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
"""Vérifie l'unicité des tags"""
|
"""Vérifie l'unicité des tags"""
|
||||||
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
noms_tags_perso = sorted(list(set(dict_tags["personnalises"].keys())))
|
||||||
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
noms_tags_auto = sorted(list(set(dict_tags["auto"].keys()))) # + noms_tags_comp
|
||||||
noms_tags = noms_tags_perso + noms_tags_auto
|
# noms_tags = noms_tags_perso + noms_tags_auto
|
||||||
|
|
||||||
intersection = list(set(noms_tags_perso) & set(noms_tags_auto))
|
intersection = list(set(noms_tags_perso) & set(noms_tags_auto))
|
||||||
|
|
||||||
@ -455,7 +457,7 @@ def get_synthese_tags_personnalises_semestre(formsemestre: FormSemestre):
|
|||||||
modimpl_id = modimpl.id
|
modimpl_id = modimpl.id
|
||||||
|
|
||||||
# Liste des tags pour le module concerné
|
# Liste des tags pour le module concerné
|
||||||
tags = sco_tag_module.module_tag_list(modimpl.module.id)
|
tags = [t.title for t in modimpl.module.tags]
|
||||||
|
|
||||||
# Traitement des tags recensés, chacun pouvant étant de la forme
|
# Traitement des tags recensés, chacun pouvant étant de la forme
|
||||||
# "mathématiques", "théorie", "pe:0", "maths:2"
|
# "mathématiques", "théorie", "pe:0", "maths:2"
|
||||||
|
@ -30,16 +30,15 @@
|
|||||||
Implementation expérimentale (Jul. 2016) pour grouper les modules sur
|
Implementation expérimentale (Jul. 2016) pour grouper les modules sur
|
||||||
les avis de poursuites d'études.
|
les avis de poursuites d'études.
|
||||||
|
|
||||||
|
TODO: réécrire avec SQLAlchemy.
|
||||||
|
|
||||||
Pour l'UI, voir https://goodies.pixabay.com/jquery/tag-editor/demo.html
|
Pour l'UI, voir https://goodies.pixabay.com/jquery/tag-editor/demo.html
|
||||||
"""
|
"""
|
||||||
import http
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from flask import g
|
from flask import g
|
||||||
|
|
||||||
from app import db, log
|
from app import db
|
||||||
from app.formations import edit_module
|
|
||||||
from app.models import Formation, NotesTag
|
from app.models import Formation, NotesTag
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
@ -60,7 +59,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
|||||||
|
|
||||||
|
|
||||||
# NOTA: ancien code, n'utile pas de modèles SQLAlchemy
|
# NOTA: ancien code, n'utile pas de modèles SQLAlchemy
|
||||||
class ScoTag(object):
|
class ScoTag:
|
||||||
"""Generic tags for ScoDoc"""
|
"""Generic tags for ScoDoc"""
|
||||||
|
|
||||||
# must be overloaded:
|
# must be overloaded:
|
||||||
@ -208,8 +207,6 @@ class ModuleTag(ScoTag):
|
|||||||
|
|
||||||
|
|
||||||
# API
|
# API
|
||||||
|
|
||||||
|
|
||||||
# TODO placer dans la vraie API et ne plus utiliser sco_publish
|
# TODO placer dans la vraie API et ne plus utiliser sco_publish
|
||||||
def module_tag_search(term: str | int):
|
def module_tag_search(term: str | int):
|
||||||
"""List all used tag names (for auto-completion)"""
|
"""List all used tag names (for auto-completion)"""
|
||||||
@ -230,60 +227,6 @@ def module_tag_search(term: str | int):
|
|||||||
return scu.sendJSON(data)
|
return scu.sendJSON(data)
|
||||||
|
|
||||||
|
|
||||||
def module_tag_list(module_id="") -> list[str]:
|
|
||||||
"""les noms de tags associés à ce module"""
|
|
||||||
r = ndb.SimpleDictFetch(
|
|
||||||
"""SELECT t.title
|
|
||||||
FROM notes_modules_tags mt, notes_tags t
|
|
||||||
WHERE mt.tag_id = t.id
|
|
||||||
AND mt.module_id = %(module_id)s
|
|
||||||
""",
|
|
||||||
{"module_id": module_id},
|
|
||||||
)
|
|
||||||
return [x["title"] for x in r]
|
|
||||||
|
|
||||||
|
|
||||||
def module_tag_set(module_id="", taglist=None):
|
|
||||||
"""taglist may either be:
|
|
||||||
a string with tag names separated by commas ("un,deux")
|
|
||||||
or a list of strings (["un", "deux"])
|
|
||||||
Remplace les tags existants
|
|
||||||
"""
|
|
||||||
if not taglist:
|
|
||||||
taglist = []
|
|
||||||
elif isinstance(taglist, str):
|
|
||||||
taglist = taglist.split(",")
|
|
||||||
taglist = [t.strip() for t in taglist]
|
|
||||||
log(f"module_tag_set: module_id={module_id} taglist={taglist}")
|
|
||||||
# Check tags syntax
|
|
||||||
for tag in taglist:
|
|
||||||
if not ScoTag.check_tag_title(tag):
|
|
||||||
log(f"module_tag_set({module_id}): invalid tag title")
|
|
||||||
return scu.json_error(404, "invalid tag")
|
|
||||||
|
|
||||||
# TODO code à moderniser (+ revoir classe ScoTag, utiliser modèle)
|
|
||||||
# TODO Voir ItemSuiviTag et api etud_suivi
|
|
||||||
|
|
||||||
# Sanity check:
|
|
||||||
mod_dict = edit_module.module_list(args={"module_id": module_id})
|
|
||||||
if not mod_dict:
|
|
||||||
raise ScoValueError("invalid module !")
|
|
||||||
|
|
||||||
newtags = set(taglist)
|
|
||||||
oldtags = set(module_tag_list(module_id))
|
|
||||||
to_del = oldtags - newtags
|
|
||||||
to_add = newtags - oldtags
|
|
||||||
|
|
||||||
# should be atomic, but it's not.
|
|
||||||
for tagname in to_add:
|
|
||||||
t = ModuleTag(tagname, object_id=module_id)
|
|
||||||
for tagname in to_del:
|
|
||||||
t = ModuleTag(tagname)
|
|
||||||
t.remove_tag_from_object(module_id)
|
|
||||||
|
|
||||||
return "", http.HTTPStatus.NO_CONTENT
|
|
||||||
|
|
||||||
|
|
||||||
def split_tagname_coeff(tag: str, separateur=":") -> tuple[str, float]:
|
def split_tagname_coeff(tag: str, separateur=":") -> tuple[str, float]:
|
||||||
"""Découpage d'un tag, tel que saisi par un utilisateur dans le programme,
|
"""Découpage d'un tag, tel que saisi par un utilisateur dans le programme,
|
||||||
pour en extraire :
|
pour en extraire :
|
||||||
|
@ -56,12 +56,12 @@ Solution proposée (nov 2014):
|
|||||||
import flask
|
import flask
|
||||||
from flask import flash, g, request, render_template, url_for
|
from flask import flash, g, request, render_template, url_for
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from app.formations import edit_matiere, edit_module, edit_ue
|
from app.formations import edit_module, edit_ue
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
|
|
||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.models import Evaluation, Identite, ModuleImpl, UniteEns
|
from app.models import Evaluation, Identite, Matiere, ModuleImpl, UniteEns
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_moduleimpl
|
from app.scodoc import sco_moduleimpl
|
||||||
from app.scodoc import sco_saisie_notes
|
from app.scodoc import sco_saisie_notes
|
||||||
@ -110,7 +110,7 @@ def external_ue_create(
|
|||||||
)
|
)
|
||||||
ue = db.session.get(UniteEns, ue_id)
|
ue = db.session.get(UniteEns, ue_id)
|
||||||
flash(f"UE créée (code {ue.ue_code})")
|
flash(f"UE créée (code {ue.ue_code})")
|
||||||
matiere_id = edit_matiere.do_matiere_create(
|
matiere = Matiere.create_from_dict(
|
||||||
{"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}
|
{"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ def external_ue_create(
|
|||||||
"code": acronyme,
|
"code": acronyme,
|
||||||
"coefficient": ects, # tous le coef. module est egal à la quantite d'ECTS
|
"coefficient": ects, # tous le coef. module est egal à la quantite d'ECTS
|
||||||
"ue_id": ue_id,
|
"ue_id": ue_id,
|
||||||
"matiere_id": matiere_id,
|
"matiere_id": matiere.id,
|
||||||
"formation_id": formation_id,
|
"formation_id": formation_id,
|
||||||
"semestre_id": formsemestre.semestre_id,
|
"semestre_id": formsemestre.semestre_id,
|
||||||
"module_type": scu.ModuleType.STANDARD,
|
"module_type": scu.ModuleType.STANDARD,
|
||||||
|
@ -612,9 +612,7 @@ def formation_tag_modules_by_type(formation_id: int, semestre_idx: int):
|
|||||||
"""Taggue tous les modules de la formation en fonction de leur type : 'res', 'sae', 'malus'
|
"""Taggue tous les modules de la formation en fonction de leur type : 'res', 'sae', 'malus'
|
||||||
Ne taggue pas les modules standards.
|
Ne taggue pas les modules standards.
|
||||||
"""
|
"""
|
||||||
formation = Formation.query.filter_by(
|
formation = Formation.get_formation(formation_id)
|
||||||
id=formation_id, dept_id=g.scodoc_dept_id
|
|
||||||
).first_or_404()
|
|
||||||
sco_tag_module.formation_tag_modules_by_type(formation)
|
sco_tag_module.formation_tag_modules_by_type(formation)
|
||||||
flash("Formation tagguée")
|
flash("Formation tagguée")
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
@ -631,11 +629,12 @@ def formation_tag_modules_by_type(formation_id: int, semestre_idx: int):
|
|||||||
@bp.route("/module_tag_set", methods=["POST"])
|
@bp.route("/module_tag_set", methods=["POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.EditFormationTags)
|
@permission_required(Permission.EditFormationTags)
|
||||||
def module_tag_set():
|
def module_tag_set(): # TODO passer dans l'API
|
||||||
"""Set tags on module"""
|
"""Set tags on module"""
|
||||||
module_id = int(request.form.get("module_id"))
|
module_id = request.form.get("module_id")
|
||||||
|
module: Module = Module.get_instance(module_id)
|
||||||
taglist = request.form.get("taglist")
|
taglist = request.form.get("taglist")
|
||||||
return sco_tag_module.module_tag_set(module_id, taglist)
|
return module.set_tags(taglist)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/module_clone", methods=["POST"])
|
@bp.route("/module_clone", methods=["POST"])
|
||||||
@ -643,8 +642,8 @@ def module_tag_set():
|
|||||||
@permission_required(Permission.EditFormation)
|
@permission_required(Permission.EditFormation)
|
||||||
def module_clone():
|
def module_clone():
|
||||||
"""Clone existing module"""
|
"""Clone existing module"""
|
||||||
module_id = int(request.form.get("module_id"))
|
module_id = request.form.get("module_id")
|
||||||
module = Module.query.get_or_404(module_id)
|
module: Module = Module.get_instance(module_id)
|
||||||
module2 = module.clone()
|
module2 = module.clone()
|
||||||
db.session.add(module2)
|
db.session.add(module2)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -205,11 +205,8 @@ class ScoFake(object):
|
|||||||
|
|
||||||
@logging_meth
|
@logging_meth
|
||||||
def create_matiere(self, ue_id=None, titre=None, numero=0) -> int:
|
def create_matiere(self, ue_id=None, titre=None, numero=0) -> int:
|
||||||
oid = edit_matiere.do_matiere_create(locals())
|
mat = Matiere.create_from_dict(locals())
|
||||||
oids = edit_matiere.matiere_list(args={"matiere_id": oid})
|
return mat.id
|
||||||
if not oids:
|
|
||||||
raise ScoValueError("matiere not created !")
|
|
||||||
return oid
|
|
||||||
|
|
||||||
@logging_meth
|
@logging_meth
|
||||||
def create_module(
|
def create_module(
|
||||||
|
@ -36,8 +36,6 @@
|
|||||||
# - do_formsemestre_delete
|
# - do_formsemestre_delete
|
||||||
# - module_list
|
# - module_list
|
||||||
# - do_module_delete
|
# - do_module_delete
|
||||||
# - matiere_list
|
|
||||||
# - do_matiere_delete
|
|
||||||
# - ue_list
|
# - ue_list
|
||||||
# - do_ue_delete
|
# - do_ue_delete
|
||||||
# - do_formation_delete
|
# - do_formation_delete
|
||||||
@ -50,12 +48,11 @@ import pytest
|
|||||||
from app import db
|
from app import db
|
||||||
from app.formations import (
|
from app.formations import (
|
||||||
edit_formation,
|
edit_formation,
|
||||||
edit_matiere,
|
|
||||||
edit_module,
|
edit_module,
|
||||||
edit_ue,
|
edit_ue,
|
||||||
formation_io,
|
formation_io,
|
||||||
)
|
)
|
||||||
from app.models import Formation, ModuleImpl
|
from app.models import Formation, Matiere, ModuleImpl
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
from app.scodoc import sco_exceptions
|
from app.scodoc import sco_exceptions
|
||||||
from app.scodoc import sco_formsemestre_edit
|
from app.scodoc import sco_formsemestre_edit
|
||||||
@ -287,10 +284,12 @@ def test_formations(test_client):
|
|||||||
|
|
||||||
assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup
|
assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup
|
||||||
|
|
||||||
li_mat = edit_matiere.matiere_list()
|
li_mat = Matiere.query.all()
|
||||||
assert len(li_mat) == 4
|
assert len(li_mat) == 4
|
||||||
edit_matiere.do_matiere_delete(oid=matiere_id3) # on supprime la matiere
|
assert matiere_id3 in [m.matiere_id for m in li_mat]
|
||||||
li_mat2 = edit_matiere.matiere_list()
|
matiere = db.session.get(Matiere, matiere_id3)
|
||||||
|
matiere.delete() # on supprime la matiere
|
||||||
|
li_mat2 = Matiere.query.all()
|
||||||
assert len(li_mat2) == 3 # verification de la suppression de la matiere
|
assert len(li_mat2) == 3 # verification de la suppression de la matiere
|
||||||
|
|
||||||
li_ue = edit_ue.ue_list()
|
li_ue = edit_ue.ue_list()
|
||||||
|
Loading…
Reference in New Issue
Block a user