forked from ScoDoc/ScoDoc
Backend 'Modules': utilise uniquement modèles. + code modernization.
This commit is contained in:
parent
1dfba157c2
commit
29defb6f00
@ -33,168 +33,28 @@ from flask import flash, url_for, render_template
|
|||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from app import db, log
|
from app import db
|
||||||
from app import models
|
from app import models
|
||||||
from app.formations import edit_matiere
|
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import Formation, Matiere, Module, UniteEns
|
from app.models import Formation, Matiere, Module, UniteEns
|
||||||
from app.models import FormSemestre, ModuleImpl
|
from app.models import FormSemestre, ModuleImpl
|
||||||
from app.models import ScolarNews
|
|
||||||
from app.models.but_refcomp import ApcAppCritique, ApcParcours
|
from app.models.but_refcomp import ApcAppCritique, ApcParcours
|
||||||
|
|
||||||
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
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_exceptions import (
|
from app.scodoc.sco_exceptions import (
|
||||||
ScoValueError,
|
ScoValueError,
|
||||||
ScoLockedFormError,
|
|
||||||
ScoGenError,
|
|
||||||
ScoNonEmptyFormationObject,
|
ScoNonEmptyFormationObject,
|
||||||
)
|
)
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_moduleimpl
|
|
||||||
|
|
||||||
_moduleEditor = ndb.EditableTable(
|
|
||||||
"notes_modules",
|
|
||||||
"module_id",
|
|
||||||
(
|
|
||||||
"module_id",
|
|
||||||
"titre",
|
|
||||||
"code",
|
|
||||||
"abbrev",
|
|
||||||
"heures_cours",
|
|
||||||
"heures_td",
|
|
||||||
"heures_tp",
|
|
||||||
"coefficient",
|
|
||||||
"ue_id",
|
|
||||||
"matiere_id",
|
|
||||||
"formation_id",
|
|
||||||
"semestre_id",
|
|
||||||
"numero",
|
|
||||||
"code_apogee",
|
|
||||||
"module_type",
|
|
||||||
"edt_id",
|
|
||||||
#'ects'
|
|
||||||
),
|
|
||||||
sortkey="numero, code, titre",
|
|
||||||
output_formators={
|
|
||||||
"heures_cours": ndb.float_null_is_zero,
|
|
||||||
"heures_td": ndb.float_null_is_zero,
|
|
||||||
"heures_tp": ndb.float_null_is_zero,
|
|
||||||
"numero": ndb.int_null_is_zero,
|
|
||||||
"coefficient": ndb.float_null_is_zero,
|
|
||||||
"module_type": ndb.int_null_is_zero,
|
|
||||||
#'ects' : ndb.float_null_is_null
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def module_list(*args, **kw):
|
|
||||||
"list modules"
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
return _moduleEditor.list(cnx, *args, **kw)
|
|
||||||
|
|
||||||
|
|
||||||
def do_module_create(args) -> int:
|
|
||||||
"Create a module. Returns id of new object."
|
|
||||||
formation = db.session.get(Formation, args["formation_id"])
|
|
||||||
# refuse de créer un module APC avec semestres incohérents:
|
|
||||||
if formation.is_apc():
|
|
||||||
ue = db.session.get(UniteEns, args["ue_id"])
|
|
||||||
if int(args.get("semestre_id", 0)) != ue.semestre_idx:
|
|
||||||
raise ScoValueError("Formation incompatible: contacter le support ScoDoc")
|
|
||||||
# create
|
|
||||||
module = Module.create_from_dict(args)
|
|
||||||
db.session.commit()
|
|
||||||
log(f"do_module_create: created {module.id} with {args}")
|
|
||||||
|
|
||||||
# news
|
|
||||||
ScolarNews.add(
|
|
||||||
typ=ScolarNews.NEWS_FORM,
|
|
||||||
obj=formation.id,
|
|
||||||
text=f"Modification de la formation {formation.acronyme}",
|
|
||||||
)
|
|
||||||
formation.invalidate_cached_sems()
|
|
||||||
return module.id
|
|
||||||
|
|
||||||
|
|
||||||
def module_create(
|
|
||||||
matiere_id=None, module_type=None, semestre_id=None, formation_id=None
|
|
||||||
):
|
|
||||||
"""Formulaire de création d'un module
|
|
||||||
Si matiere_id est spécifié, le module sera créé dans cette matière (cas normal).
|
|
||||||
Sinon, donne le choix de l'UE de rattachement et utilise la première
|
|
||||||
matière de cette UE (si elle n'existe pas, la crée).
|
|
||||||
"""
|
|
||||||
return module_edit(
|
|
||||||
create=True,
|
|
||||||
matiere_id=matiere_id,
|
|
||||||
module_type=module_type,
|
|
||||||
semestre_id=semestre_id,
|
|
||||||
formation_id=formation_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def can_delete_module(module):
|
|
||||||
"True si le module n'est pas utilisée dans des formsemestre"
|
|
||||||
return len(module.modimpls.all()) == 0
|
|
||||||
|
|
||||||
|
|
||||||
def do_module_delete(oid):
|
|
||||||
"delete module"
|
|
||||||
module = Module.query.get_or_404(oid)
|
|
||||||
mod = module_list({"module_id": oid})[0] # sco7
|
|
||||||
if module_is_locked(module.id):
|
|
||||||
raise ScoLockedFormError()
|
|
||||||
if not can_delete_module(module):
|
|
||||||
raise ScoNonEmptyFormationObject(
|
|
||||||
"Module",
|
|
||||||
msg=module.titre,
|
|
||||||
dest_url=url_for(
|
|
||||||
"notes.ue_table",
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
formation_id=module.formation_id,
|
|
||||||
semestre_idx=module.ue.semestre_idx,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# S'il y a des moduleimpls, on ne peut pas detruire le module !
|
|
||||||
mods = sco_moduleimpl.moduleimpl_list(module_id=oid)
|
|
||||||
if mods:
|
|
||||||
err_page = f"""
|
|
||||||
<h3>Destruction du module impossible car il est utilisé dans des
|
|
||||||
semestres existants !</h3>
|
|
||||||
<p class="help">Il faut d'abord supprimer le semestre (ou en retirer
|
|
||||||
ce module).
|
|
||||||
Mais il est peut être préférable de laisser ce programme intact et
|
|
||||||
d'en créer une nouvelle version pour la modifier sans affecter
|
|
||||||
les semestres déjà en place.
|
|
||||||
</p>
|
|
||||||
<a href="{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
|
|
||||||
formation_id=mod["formation_id"])}">reprendre</a>
|
|
||||||
"""
|
|
||||||
raise ScoGenError(err_page)
|
|
||||||
# delete
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
_moduleEditor.delete(cnx, oid)
|
|
||||||
|
|
||||||
# news
|
|
||||||
formation = module.formation
|
|
||||||
ScolarNews.add(
|
|
||||||
typ=ScolarNews.NEWS_FORM,
|
|
||||||
obj=mod["formation_id"],
|
|
||||||
text=f"Modification de la formation {formation.acronyme}",
|
|
||||||
)
|
|
||||||
formation.invalidate_cached_sems()
|
|
||||||
|
|
||||||
|
|
||||||
def module_delete(module_id=None):
|
def module_delete(module_id=None):
|
||||||
"""Delete a module"""
|
"""Formulaire suppression d'un module"""
|
||||||
module = Module.query.get_or_404(module_id)
|
module = Module.query.get_or_404(module_id)
|
||||||
mod = module_list(args={"module_id": module_id})[0] # sco7
|
|
||||||
|
|
||||||
if not can_delete_module(module):
|
if not module.can_be_deleted():
|
||||||
raise ScoNonEmptyFormationObject(
|
raise ScoNonEmptyFormationObject(
|
||||||
"Module",
|
"Module",
|
||||||
msg=module.titre,
|
msg=module.titre,
|
||||||
@ -221,7 +81,7 @@ def module_delete(module_id=None):
|
|||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
(("module_id", {"input_type": "hidden"}),),
|
(("module_id", {"input_type": "hidden"}),),
|
||||||
initvalues=mod,
|
initvalues=module.to_dict(),
|
||||||
submitlabel="Confirmer la suppression",
|
submitlabel="Confirmer la suppression",
|
||||||
cancelbutton="Annuler",
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
@ -231,37 +91,38 @@ def module_delete(module_id=None):
|
|||||||
title="Suppression d'un module",
|
title="Suppression d'un module",
|
||||||
content="\n".join(H) + tf[1],
|
content="\n".join(H) + tf[1],
|
||||||
)
|
)
|
||||||
elif tf[0] == -1:
|
if tf[0] == -1: # cancel
|
||||||
return flask.redirect(dest_url)
|
|
||||||
else:
|
|
||||||
do_module_delete(module_id)
|
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
module.delete()
|
||||||
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
|
||||||
def do_module_edit(vals: dict) -> None:
|
def do_module_edit(vals: dict) -> None:
|
||||||
"edit a module"
|
"edit a module"
|
||||||
# check
|
# check
|
||||||
mod = module_list({"module_id": vals["module_id"]})[0]
|
module = Module.get_instance(vals["module_id"])
|
||||||
if module_is_locked(mod["module_id"]):
|
|
||||||
# formation verrouillée: empeche de modifier certains champs:
|
|
||||||
vals = vals.copy()
|
|
||||||
protected_fields = ("coefficient", "ue_id", "matiere_id", "semestre_id")
|
|
||||||
for f in protected_fields:
|
|
||||||
if f in vals:
|
|
||||||
del vals[f]
|
|
||||||
# edit
|
# edit
|
||||||
cnx = ndb.GetDBConnexion()
|
modif = module.from_dict(vals)
|
||||||
_moduleEditor.edit(cnx, vals)
|
if modif:
|
||||||
db.session.get(Formation, mod["formation_id"]).invalidate_cached_sems()
|
module.formation.invalidate_cached_sems()
|
||||||
|
|
||||||
|
|
||||||
def check_module_code_unicity(code, field, formation_id, module_id=None):
|
def module_create(
|
||||||
"true si code module unique dans la formation"
|
matiere_id=None, module_type=None, semestre_id=None, formation_id=None
|
||||||
modules = module_list(args={"code": code, "formation_id": formation_id})
|
):
|
||||||
if module_id: # edition: supprime le module en cours
|
"""Formulaire de création d'un module
|
||||||
modules = [m for m in modules if m["module_id"] != module_id]
|
Si matiere_id est spécifié, le module sera créé dans cette matière (cas normal).
|
||||||
|
Sinon, donne le choix de l'UE de rattachement et utilise la première
|
||||||
return len(modules) == 0
|
matière de cette UE (si elle n'existe pas, la crée).
|
||||||
|
"""
|
||||||
|
return module_edit(
|
||||||
|
create=True,
|
||||||
|
matiere_id=matiere_id,
|
||||||
|
module_type=module_type,
|
||||||
|
semestre_id=semestre_id,
|
||||||
|
formation_id=formation_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def module_edit(
|
def module_edit(
|
||||||
@ -278,14 +139,12 @@ def module_edit(
|
|||||||
Sinon, donne le choix de l'UE de rattachement et utilise la première matière
|
Sinon, donne le choix de l'UE de rattachement et utilise la première matière
|
||||||
de cette UE (si elle n'existe pas, la crée).
|
de cette UE (si elle n'existe pas, la crée).
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_tag_module
|
|
||||||
|
|
||||||
# --- Détermination de la formation
|
# --- Détermination de la formation
|
||||||
orig_semestre_idx = semestre_id
|
orig_semestre_idx = semestre_id
|
||||||
ue = None
|
ue = None
|
||||||
if create:
|
if create:
|
||||||
if matiere_id:
|
if matiere_id:
|
||||||
matiere = Matiere.query.get_or_404(matiere_id)
|
matiere = Matiere.get_instance(matiere_id)
|
||||||
ue = matiere.ue
|
ue = matiere.ue
|
||||||
formation = ue.formation
|
formation = ue.formation
|
||||||
orig_semestre_idx = ue.semestre_idx if semestre_id is None else semestre_id
|
orig_semestre_idx = ue.semestre_idx if semestre_id is None else semestre_id
|
||||||
@ -300,7 +159,7 @@ def module_edit(
|
|||||||
ue = module.ue
|
ue = module.ue
|
||||||
module_dict = module.to_dict()
|
module_dict = module.to_dict()
|
||||||
formation = module.formation
|
formation = module.formation
|
||||||
unlocked = not module_is_locked(module_id)
|
unlocked = not module.is_locked()
|
||||||
|
|
||||||
parcours = codes_cursus.get_cursus_from_code(formation.type_parcours)
|
parcours = codes_cursus.get_cursus_from_code(formation.type_parcours)
|
||||||
is_apc = parcours.APC_SAE # BUT
|
is_apc = parcours.APC_SAE # BUT
|
||||||
@ -326,17 +185,14 @@ def module_edit(
|
|||||||
if (module and module.matiere and (module.matiere.id == mat.id))
|
if (module and module.matiere and (module.matiere.id == mat.id))
|
||||||
or (mat.id == mat.ue.matieres.first().id)
|
or (mat.id == mat.ue.matieres.first().id)
|
||||||
]
|
]
|
||||||
mat_names = [
|
mat_names = [f"S{mat.ue.semestre_idx} / {mat.ue.acronyme}" for mat in matieres]
|
||||||
"S%s / %s" % (mat.ue.semestre_idx, mat.ue.acronyme) for mat in matieres
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
mat_names = ["%s / %s" % (mat.ue.acronyme, mat.titre or "") for mat in matieres]
|
mat_names = ["{mat.ue.acronyme} / {mat.titre or ''}" for mat in matieres]
|
||||||
|
|
||||||
if module: # edition
|
if module: # edition
|
||||||
ue_mat_ids = ["%s!%s" % (mat.ue.id, mat.id) for mat in matieres]
|
ue_mat_ids = [f"{mat.ue.id}!{mat.id}" for mat in matieres]
|
||||||
module_dict["ue_matiere_id"] = "%s!%s" % (
|
module_dict["ue_matiere_id"] = (
|
||||||
module_dict["ue_id"],
|
f"{module_dict['ue_id']}!{module_dict['matiere_id']}"
|
||||||
module_dict["matiere_id"],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||||
@ -433,8 +289,8 @@ def module_edit(
|
|||||||
"explanation": """code du module (issu du programme, exemple M1203,
|
"explanation": """code du module (issu du programme, exemple M1203,
|
||||||
R2.01, ou SAÉ 3.4. Doit être unique dans la formation)""",
|
R2.01, ou SAÉ 3.4. Doit être unique dans la formation)""",
|
||||||
"allow_null": False,
|
"allow_null": False,
|
||||||
"validator": lambda val, field, formation_id=formation.id: check_module_code_unicity(
|
"validator": lambda val, _, formation_id=formation.id: Module.check_module_code_unicity(
|
||||||
val, field, formation_id, module_id=module.id if module else None
|
val, formation_id, module_id=module.id if module else None
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -602,7 +458,8 @@ def module_edit(
|
|||||||
"title": "UE de rattachement",
|
"title": "UE de rattachement",
|
||||||
"explanation": "utilisée notamment pour les malus",
|
"explanation": "utilisée notamment pour les malus",
|
||||||
"labels": [
|
"labels": [
|
||||||
f"S{u.semestre_idx if u.semestre_idx is not None else '.'} / {u.acronyme} {u.titre}"
|
f"""S{u.semestre_idx if u.semestre_idx is not None else '.'
|
||||||
|
} / {u.acronyme} {u.titre}"""
|
||||||
for u in ues
|
for u in ues
|
||||||
],
|
],
|
||||||
"allowed_values": [u.id for u in ues],
|
"allowed_values": [u.id for u in ues],
|
||||||
@ -631,7 +488,8 @@ def module_edit(
|
|||||||
"input_type": "menu",
|
"input_type": "menu",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"title": parcours.SESSION_NAME.capitalize(),
|
"title": parcours.SESSION_NAME.capitalize(),
|
||||||
"explanation": f"{parcours.SESSION_NAME} de début du module dans la formation standard",
|
"explanation": f"""{parcours.SESSION_NAME
|
||||||
|
} de début du module dans la formation standard""",
|
||||||
"labels": [str(x) for x in semestres_indices],
|
"labels": [str(x) for x in semestres_indices],
|
||||||
"allowed_values": semestres_indices,
|
"allowed_values": semestres_indices,
|
||||||
"enabled": unlocked,
|
"enabled": unlocked,
|
||||||
@ -846,8 +704,7 @@ def module_edit(
|
|||||||
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 = Module.create_from_dict(tf[2], news=True)
|
||||||
module = db.session.get(Module, module_id)
|
|
||||||
else: # EDITION MODULE
|
else: # EDITION MODULE
|
||||||
# l'UE de rattachement peut changer
|
# l'UE de rattachement peut changer
|
||||||
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
||||||
@ -892,6 +749,7 @@ def module_edit(
|
|||||||
]
|
]
|
||||||
db.session.add(module)
|
db.session.add(module)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
module.formation.invalidate_cached_sems()
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
@ -904,28 +762,36 @@ def module_edit(
|
|||||||
|
|
||||||
def module_table(formation_id):
|
def module_table(formation_id):
|
||||||
"""Liste des modules de la formation
|
"""Liste des modules de la formation
|
||||||
(XXX inutile ou a revoir)
|
(affichage debug)
|
||||||
"""
|
"""
|
||||||
if not formation_id:
|
formation = Formation.get_formation(formation_id)
|
||||||
raise ScoValueError("invalid formation !")
|
editable = current_user.has_permission(Permission.EditFormation)
|
||||||
formation: Formation = Formation.query.get_or_404(formation_id)
|
|
||||||
H = [
|
H = [
|
||||||
f"""<h2>Listes des modules dans la formation {formation.titre} ({formation.acronyme}</h2>
|
f"""<h2>Listes des modules dans la formation
|
||||||
|
{formation.titre} ({formation.acronyme} (debug)
|
||||||
|
</h2>
|
||||||
<ul class="notes_module_list">
|
<ul class="notes_module_list">
|
||||||
""",
|
""",
|
||||||
]
|
]
|
||||||
editable = current_user.has_permission(Permission.EditFormation)
|
|
||||||
|
|
||||||
for module_dict in module_list(args={"formation_id": formation_id}):
|
for module in formation.modules:
|
||||||
H.append('<li class="notes_module_list">%s' % module_dict)
|
m_dict = module.to_dict()
|
||||||
|
m_dict["parcours"] = [p.code for p in module.parcours]
|
||||||
|
str_module = str(m_dict).replace(",", ",\n")
|
||||||
|
H.append(
|
||||||
|
f'<li class="notes_module_list"><pre style="margin-bottom: 1px;">{str_module}</pre>'
|
||||||
|
)
|
||||||
if editable:
|
if editable:
|
||||||
H.append(
|
H.append(
|
||||||
'<a href="module_edit?module_id=%(module_id)s">modifier</a>'
|
f"""
|
||||||
% module_dict
|
<a class="stdlink" href="{
|
||||||
)
|
url_for('notes.module_edit', scodoc_dept=g.scodoc_dept, module_id=module.id)
|
||||||
H.append(
|
}">modifier</a>
|
||||||
'<a href="module_delete?module_id=%(module_id)s">supprimer</a>'
|
<a class="stdlink" href="{
|
||||||
% module_dict
|
url_for('notes.module_delete', scodoc_dept=g.scodoc_dept, module_id=module.id)
|
||||||
|
}">supprimer</a>
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
H.append("</li>")
|
H.append("</li>")
|
||||||
H.append("</ul>")
|
H.append("</ul>")
|
||||||
@ -936,23 +802,6 @@ def module_table(formation_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def module_is_locked(module_id):
|
|
||||||
"""True if module should not be modified
|
|
||||||
(used in a locked formsemestre)
|
|
||||||
"""
|
|
||||||
r = ndb.SimpleDictFetch(
|
|
||||||
"""SELECT mi.id
|
|
||||||
FROM notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
|
|
||||||
WHERE mi.module_id = mod.id
|
|
||||||
AND mi.formsemestre_id = sem.id
|
|
||||||
AND mi.module_id = %(module_id)s
|
|
||||||
AND sem.etat = false
|
|
||||||
""",
|
|
||||||
{"module_id": module_id},
|
|
||||||
)
|
|
||||||
return len(r) > 0
|
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
):
|
):
|
||||||
@ -966,7 +815,7 @@ def formation_add_malus_modules(
|
|||||||
ues = ues.filter_by(semestre_idx=semestre_id)
|
ues = ues.filter_by(semestre_idx=semestre_id)
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
if ue.type == codes_cursus.UE_STANDARD:
|
if ue.type == codes_cursus.UE_STANDARD:
|
||||||
if ue_add_malus_module(ue, titre=titre) != None:
|
if ue_add_malus_module(ue, titre=titre) is not None:
|
||||||
nb += 1
|
nb += 1
|
||||||
|
|
||||||
flash(f"Modules de malus ajoutés dans {nb} UEs du S{semestre_id}")
|
flash(f"Modules de malus ajoutés dans {nb} UEs du S{semestre_id}")
|
||||||
@ -1002,7 +851,8 @@ def ue_add_malus_module(ue: UniteEns, titre=None, code=None) -> int:
|
|||||||
# c'est ennuyeux: dans ce cas, on pourrait demander à indiquer explicitement
|
# c'est ennuyeux: dans ce cas, on pourrait demander à indiquer explicitement
|
||||||
# le semestre ? ou affecter le malus au semestre 1 ???
|
# le semestre ? ou affecter le malus au semestre 1 ???
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"Impossible d'ajouter un malus si l'UE n'a pas de numéro de semestre et ne comporte pas d'autres modules"
|
"""Impossible d'ajouter un malus si l'UE n'a pas de numéro de semestre
|
||||||
|
et ne comporte pas d'autres modules"""
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
semestre_id = ue.semestre_idx
|
semestre_id = ue.semestre_idx
|
||||||
|
@ -37,7 +37,6 @@ from flask_login import current_user
|
|||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.but import apc_edit_ue
|
from app.but import apc_edit_ue
|
||||||
from app.formations import edit_matiere, edit_module
|
|
||||||
from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
|
from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Formation,
|
Formation,
|
||||||
@ -66,7 +65,6 @@ from app.scodoc import codes_cursus
|
|||||||
from app.scodoc import sco_edit_apc
|
from app.scodoc import sco_edit_apc
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_moduleimpl
|
from app.scodoc import sco_moduleimpl
|
||||||
from app.scodoc import sco_tag_module
|
|
||||||
|
|
||||||
_ueEditor = ndb.EditableTable(
|
_ueEditor = ndb.EditableTable(
|
||||||
"notes_ue",
|
"notes_ue",
|
||||||
@ -375,7 +373,8 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
"type": "float",
|
"type": "float",
|
||||||
"min_value": 0,
|
"min_value": 0,
|
||||||
"title": "Coef. RCUE",
|
"title": "Coef. RCUE",
|
||||||
"explanation": """pondération utilisée pour le calcul de la moyenne du RCUE. Laisser à 1, sauf si votre établissement a explicitement décidé de pondérations.
|
"explanation": """pondération utilisée pour le calcul de la moyenne du RCUE.
|
||||||
|
Laisser à 1, sauf si votre établissement a explicitement décidé de pondérations.
|
||||||
""",
|
""",
|
||||||
"defaut": 1.0,
|
"defaut": 1.0,
|
||||||
"allow_null": False,
|
"allow_null": False,
|
||||||
@ -421,7 +420,8 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
{
|
{
|
||||||
"title": "Code Apogée",
|
"title": "Code Apogée",
|
||||||
"size": 25,
|
"size": 25,
|
||||||
"explanation": "(optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules",
|
"explanation": """(optionnel) code élément pédagogique Apogée
|
||||||
|
ou liste de codes ELP séparés par des virgules""",
|
||||||
"max_length": APO_CODE_STR_LEN,
|
"max_length": APO_CODE_STR_LEN,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -445,7 +445,8 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
"input_type": "boolcheckbox",
|
"input_type": "boolcheckbox",
|
||||||
"title": "UE externe",
|
"title": "UE externe",
|
||||||
"readonly": not create, # ne permet pas de transformer une UE existante en externe
|
"readonly": not create, # ne permet pas de transformer une UE existante en externe
|
||||||
"explanation": "réservé pour les capitalisations d'UE effectuées à l'extérieur de l'établissement",
|
"explanation": """réservé pour les capitalisations d'UEs
|
||||||
|
effectuées à l'extérieur de l'établissement""",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@ -465,7 +466,8 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
"input_type": "boolcheckbox",
|
"input_type": "boolcheckbox",
|
||||||
"default": True,
|
"default": True,
|
||||||
"title": "Créer matière identique",
|
"title": "Créer matière identique",
|
||||||
"explanation": "créer immédiatement une matière dans cette UE (utile si on n'utilise pas de matières)",
|
"explanation": """créer immédiatement une matière dans cette UE
|
||||||
|
(utile si on n'utilise pas de matières)""",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -553,7 +555,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
matiere_id = matiere.id
|
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(
|
_ = Module.create_from_dict(
|
||||||
{
|
{
|
||||||
"titre": tf[2]["titre"],
|
"titre": tf[2]["titre"],
|
||||||
"code": tf[2]["acronyme"],
|
"code": tf[2]["acronyme"],
|
||||||
@ -565,6 +567,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
"semestre_id": tf[2]["semestre_idx"],
|
"semestre_id": tf[2]["semestre_idx"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
db.session.commit()
|
||||||
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})")
|
||||||
else:
|
else:
|
||||||
@ -608,9 +611,10 @@ def _add_ue_semestre_id(ues: list[dict], is_apc):
|
|||||||
ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
|
ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
|
||||||
else:
|
else:
|
||||||
# était le comportement ScoDoc7
|
# était le comportement ScoDoc7
|
||||||
modules = edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
ue = UniteEns.get_ue(ue["ue_id"])
|
||||||
if modules:
|
module = ue.modules.first()
|
||||||
ue["semestre_id"] = modules[0]["semestre_id"]
|
if module:
|
||||||
|
ue["semestre_id"] = module.semestre_id
|
||||||
else:
|
else:
|
||||||
ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
|
ue["semestre_id"] = codes_cursus.UE_SEM_DEFAULT
|
||||||
|
|
||||||
@ -1323,7 +1327,6 @@ def _ue_table_modules(
|
|||||||
arrow_none,
|
arrow_none,
|
||||||
delete_icon,
|
delete_icon,
|
||||||
delete_disabled_icon,
|
delete_disabled_icon,
|
||||||
unit_name="matière",
|
|
||||||
add_suppress_link=True, # lien "supprimer cette matière"
|
add_suppress_link=True, # lien "supprimer cette matière"
|
||||||
empty_list_msg="Aucun élément dans cette matière",
|
empty_list_msg="Aucun élément dans cette matière",
|
||||||
create_element_msg="créer un module",
|
create_element_msg="créer un module",
|
||||||
@ -1361,7 +1364,6 @@ def _ue_table_modules(
|
|||||||
H.append("</span>")
|
H.append("</span>")
|
||||||
|
|
||||||
mod_editable = editable
|
mod_editable = editable
|
||||||
# and not edit_module.module_is_locked(Mod['module_id'])
|
|
||||||
if mod_editable:
|
if mod_editable:
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a class="discretelink" title="Modifier le module numéro %(numero)s, utilisé par
|
f"""<a class="discretelink" title="Modifier le module numéro %(numero)s, utilisé par
|
||||||
|
@ -36,7 +36,7 @@ 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_module, edit_ue
|
from app.formations import edit_ue
|
||||||
from app.models import Formation, FormSemestre, Matiere, 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 (
|
||||||
@ -49,7 +49,6 @@ from app.models.but_refcomp import (
|
|||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_tag_module
|
|
||||||
from app.scodoc import sco_xml
|
from app.scodoc import sco_xml
|
||||||
from app.scodoc.gen_tables import GenTable
|
from app.scodoc.gen_tables import GenTable
|
||||||
from app.scodoc.sco_exceptions import ScoValueError, ScoFormatError
|
from app.scodoc.sco_exceptions import ScoValueError, ScoFormatError
|
||||||
@ -123,11 +122,11 @@ def formation_export_dict(
|
|||||||
del mat_d["id"]
|
del mat_d["id"]
|
||||||
del mat_d["matiere_id"]
|
del mat_d["matiere_id"]
|
||||||
del mat_d["ue_id"]
|
del mat_d["ue_id"]
|
||||||
mods = edit_module.module_list({"matiere_id": matiere_id})
|
mat = db.session.get(Matiere, matiere_id)
|
||||||
mods.sort(key=lambda m: (m["numero"] or 0, m["code"]))
|
mods = mat.modules.all()
|
||||||
mat_d["module"] = mods
|
mods.sort(key=lambda m: (m.numero, m.code))
|
||||||
for mod_d in mods:
|
mat_d["module"] = [mod.to_dict() for mod in mods]
|
||||||
module: Module = db.session.get(Module, mod_d["module_id"])
|
for module, mod_d in zip(mods, mat_d["module"]):
|
||||||
if export_tags:
|
if export_tags:
|
||||||
tags = [t.title for t in module.tags]
|
tags = [t.title for t in module.tags]
|
||||||
if tags:
|
if tags:
|
||||||
@ -200,7 +199,8 @@ def formation_export(
|
|||||||
if fmt is None:
|
if fmt is None:
|
||||||
return f_dict
|
return f_dict
|
||||||
|
|
||||||
filename = f"scodoc_formation_{formation.departement.acronym}_{formation.acronyme or ''}_v{formation.version}"
|
filename = f"""scodoc_formation_{formation.departement.acronym
|
||||||
|
}_{formation.acronyme or ''}_v{formation.version}"""
|
||||||
return scu.sendResult(
|
return scu.sendResult(
|
||||||
f_dict,
|
f_dict,
|
||||||
name="formation",
|
name="formation",
|
||||||
@ -290,14 +290,14 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
f = dom.getElementsByTagName("formation")[0] # or dom.documentElement
|
f = dom.getElementsByTagName("formation")[0] # or dom.documentElement
|
||||||
D = sco_xml.xml_to_dicts(f)
|
xml_dicts = sco_xml.xml_to_dicts(f)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise ScoFormatError(
|
raise ScoFormatError(
|
||||||
"""Ce document xml ne correspond pas à un programme exporté par ScoDoc.
|
"""Ce document xml ne correspond pas à un programme exporté par ScoDoc.
|
||||||
(élément 'formation' inexistant par exemple)."""
|
(élément 'formation' inexistant par exemple)."""
|
||||||
) from exc
|
) from exc
|
||||||
assert D[0] == "formation"
|
assert xml_dicts[0] == "formation"
|
||||||
f_dict = D[1]
|
f_dict = xml_dicts[1]
|
||||||
f_dict["dept_id"] = g.scodoc_dept_id
|
f_dict["dept_id"] = g.scodoc_dept_id
|
||||||
# Pour les clonages, on prend le refcomp_id donné:
|
# Pour les clonages, on prend le refcomp_id donné:
|
||||||
referentiel_competence_id = (
|
referentiel_competence_id = (
|
||||||
@ -334,7 +334,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
|
|||||||
modules_a_coefficienter = [] # Liste des modules avec coefs APC
|
modules_a_coefficienter = [] # Liste des modules avec coefs APC
|
||||||
with sco_cache.DeferredSemCacheManager():
|
with sco_cache.DeferredSemCacheManager():
|
||||||
# -- create UEs
|
# -- create UEs
|
||||||
for ue_info in D[2]:
|
for ue_info in xml_dicts[2]:
|
||||||
assert ue_info[0] == "ue"
|
assert ue_info[0] == "ue"
|
||||||
ue_info[1]["formation_id"] = formation.id
|
ue_info[1]["formation_id"] = formation.id
|
||||||
if "ue_id" in ue_info[1]:
|
if "ue_id" in ue_info[1]:
|
||||||
@ -414,11 +414,12 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
|
|||||||
mod_info[1]["ue_id"] = ue_id
|
mod_info[1]["ue_id"] = ue_id
|
||||||
if not "module_type" in mod_info[1]:
|
if not "module_type" in mod_info[1]:
|
||||||
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
|
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
|
||||||
mod_id = edit_module.do_module_create(mod_info[1])
|
module = Module.create_from_dict(
|
||||||
|
mod_info[1], news=True, inval_cache=True
|
||||||
|
)
|
||||||
if xml_module_id:
|
if xml_module_id:
|
||||||
modules_old2new[int(xml_module_id)] = mod_id
|
modules_old2new[int(xml_module_id)] = module.id
|
||||||
if len(mod_info) > 2:
|
if len(mod_info) > 2:
|
||||||
module: Module = db.session.get(Module, mod_id)
|
|
||||||
tag_names = []
|
tag_names = []
|
||||||
ue_coef_dict = {}
|
ue_coef_dict = {}
|
||||||
for child in mod_info[2]:
|
for child in mod_info[2]:
|
||||||
|
@ -66,7 +66,8 @@ class Formation(ScoDocModel):
|
|||||||
|
|
||||||
def html(self) -> str:
|
def html(self) -> str:
|
||||||
"titre complet pour affichage"
|
"titre complet pour affichage"
|
||||||
return f"""Formation {self.titre} ({self.acronyme}) version {self.version} code <tt>{self.formation_code}</tt>"""
|
return f"""Formation {self.titre} ({self.acronyme}) version {self.version
|
||||||
|
} code <tt>{self.formation_code}</tt>"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_formation(cls, formation_id: int | str, dept_id: int = None) -> "Formation":
|
def get_formation(cls, formation_id: int | str, dept_id: int = None) -> "Formation":
|
||||||
@ -334,7 +335,7 @@ class Matiere(ScoDocModel):
|
|||||||
return e
|
return e
|
||||||
|
|
||||||
def is_locked(self) -> bool:
|
def is_locked(self) -> bool:
|
||||||
"""True if matiere cannot be be modified
|
"""True if matiere can't modified
|
||||||
because it contains modules used in a locked formsemestre.
|
because it contains modules used in a locked formsemestre.
|
||||||
"""
|
"""
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
@ -361,8 +362,6 @@ class Matiere(ScoDocModel):
|
|||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
"Delete matière. News, inval cache."
|
"Delete matière. News, inval cache."
|
||||||
from app.models import ScolarNews
|
|
||||||
|
|
||||||
formation = self.ue.formation
|
formation = self.ue.formation
|
||||||
log(f"matiere.delete: matiere_id={self.id}")
|
log(f"matiere.delete: matiere_id={self.id}")
|
||||||
if not self.can_be_deleted():
|
if not self.can_be_deleted():
|
||||||
|
@ -7,14 +7,15 @@ from flask_login import current_user
|
|||||||
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.auth.models import User
|
from app.auth.models import User
|
||||||
from app.comp import df_cache
|
from app.comp import df_cache
|
||||||
from app.models import APO_CODE_STR_LEN, ScoDocModel
|
from app.models import APO_CODE_STR_LEN, ScoDocModel
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.evaluations import Evaluation
|
from app.models.evaluations import Evaluation
|
||||||
from app.models.modules import Module
|
from app.models.modules import Module
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError
|
from app.scodoc import sco_cache
|
||||||
|
from app.scodoc.sco_exceptions import AccessDenied, ScoLockedSemError, ScoValueError
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
@ -65,6 +66,30 @@ class ModuleImpl(ScoDocModel):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>"
|
return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_from_dict(cls, data: dict) -> "ModuleImpl":
|
||||||
|
"""Create modimpl from dict. Log, inval. cache.
|
||||||
|
data must include valid formsemestre_id, module_id and responsable_id
|
||||||
|
Commit session.
|
||||||
|
"""
|
||||||
|
from app.models import FormSemestre
|
||||||
|
|
||||||
|
# check required args
|
||||||
|
for required_arg in ("formsemestre_id", "module_id", "responsable_id"):
|
||||||
|
if required_arg not in data:
|
||||||
|
raise ScoValueError(f"missing argument: {required_arg}")
|
||||||
|
_ = FormSemestre.get_formsemestre(data["formsemestre_id"])
|
||||||
|
_ = Module.get_instance(data["module_id"])
|
||||||
|
if not db.session.get(User, data["responsable_id"]):
|
||||||
|
abort(404, "responsable_id invalide")
|
||||||
|
|
||||||
|
modimpl = super().create_from_dict(data)
|
||||||
|
db.session.commit()
|
||||||
|
db.session.refresh(modimpl)
|
||||||
|
log(f"ModuleImpl.create: created {modimpl.id} with {data}")
|
||||||
|
sco_cache.invalidate_formsemestre(formsemestre_id=modimpl.formsemestre_id)
|
||||||
|
return modimpl
|
||||||
|
|
||||||
def get_codes_apogee(self) -> set[str]:
|
def get_codes_apogee(self) -> set[str]:
|
||||||
"""Les codes Apogée (codés en base comme "VRT1,VRT2").
|
"""Les codes Apogée (codés en base comme "VRT1,VRT2").
|
||||||
(si non renseigné, ceux du module)
|
(si non renseigné, ceux du module)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import http
|
import http
|
||||||
from flask import current_app, g
|
from flask import current_app, g, url_for
|
||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app import models
|
from app import models
|
||||||
@ -13,9 +13,14 @@ from app.models.but_refcomp import (
|
|||||||
app_critiques_modules,
|
app_critiques_modules,
|
||||||
parcours_modules,
|
parcours_modules,
|
||||||
)
|
)
|
||||||
|
from app.models.events import ScolarNews
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.codes_cursus import UE_SPORT
|
from app.scodoc.codes_cursus import UE_SPORT
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import (
|
||||||
|
ScoValueError,
|
||||||
|
ScoLockedFormError,
|
||||||
|
ScoNonEmptyFormationObject,
|
||||||
|
)
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
@ -115,13 +120,39 @@ class Module(models.ScoDocModel):
|
|||||||
# on ne peut pas affecter directement parcours
|
# on ne peut pas affecter directement parcours
|
||||||
return super().filter_model_attributes(args, (excluded or set()) | {"parcours"})
|
return super().filter_model_attributes(args, (excluded or set()) | {"parcours"})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_module_code_unicity(cls, code, formation_id, module_id=None) -> bool:
|
||||||
|
"true si code module unique dans la formation"
|
||||||
|
from app.models import Formation
|
||||||
|
|
||||||
|
formation = Formation.get_formation(formation_id)
|
||||||
|
query = formation.modules.filter_by(code=code)
|
||||||
|
if module_id is not None: # edition: supprime le module en cours
|
||||||
|
query = query.filter(Module.id != module_id)
|
||||||
|
return query.count() == 0
|
||||||
|
|
||||||
def from_dict(self, args: dict, excluded: set[str] | None = None) -> bool:
|
def from_dict(self, args: dict, excluded: set[str] | None = None) -> bool:
|
||||||
"""Update object's fields given in dict. Add to session but don't commit.
|
"""Update object's fields given in dict. Add to session but don't commit.
|
||||||
True if modification.
|
True if modification.
|
||||||
- can't change ue nor formation
|
- can't change ue nor formation
|
||||||
- can change matiere_id, iff new matiere in same ue
|
- can change matiere_id, iff new matiere in same ue
|
||||||
- can change parcours: parcours list of ApcParcour id or instances.
|
- can change parcours: parcours list of ApcParcour id or instances.
|
||||||
|
Ne modifie pas les coefficients APC ue_coefs
|
||||||
"""
|
"""
|
||||||
|
args = args.copy()
|
||||||
|
if "ue_coefs" in args:
|
||||||
|
del args["ue_coefs"]
|
||||||
|
if self.is_locked():
|
||||||
|
# formation verrouillée: empeche de modifier coefficient, matiere, and semestre_id
|
||||||
|
protected_fields = ("coefficient", "matiere_id", "semestre_id")
|
||||||
|
for f in protected_fields:
|
||||||
|
if f in args:
|
||||||
|
del args[f]
|
||||||
|
# Unicité du code
|
||||||
|
if "code" in args and not Module.check_module_code_unicity(
|
||||||
|
args["code"], self.formation_id, self.id
|
||||||
|
):
|
||||||
|
raise ScoValueError("code module déjà utilisé")
|
||||||
# Vérifie les changements de matiere
|
# Vérifie les changements de matiere
|
||||||
new_matiere_id = args.get("matiere_id", self.matiere_id)
|
new_matiere_id = args.get("matiere_id", self.matiere_id)
|
||||||
if new_matiere_id != self.matiere_id:
|
if new_matiere_id != self.matiere_id:
|
||||||
@ -139,20 +170,103 @@ class Module(models.ScoDocModel):
|
|||||||
existing_parcours = {p.id for p in self.parcours}
|
existing_parcours = {p.id for p in self.parcours}
|
||||||
new_parcours = args.get("parcours", []) or []
|
new_parcours = args.get("parcours", []) or []
|
||||||
if existing_parcours != set(new_parcours):
|
if existing_parcours != set(new_parcours):
|
||||||
self._set_parcours_from_list(new_parcours)
|
self.set_parcours_from_list(new_parcours)
|
||||||
return True
|
return True
|
||||||
return modified
|
return modified
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_dict(cls, data: dict) -> "Module":
|
def create_from_dict(
|
||||||
|
cls,
|
||||||
|
data: dict,
|
||||||
|
inval_cache=False,
|
||||||
|
news=False,
|
||||||
|
) -> "Module":
|
||||||
"""Create from given dict, add parcours.
|
"""Create from given dict, add parcours.
|
||||||
Flush session."""
|
Flush session.
|
||||||
|
Si news, commit and log news.
|
||||||
|
"""
|
||||||
|
from app.models.formations import Formation
|
||||||
|
|
||||||
|
# check required arguments
|
||||||
|
for required_arg in ("code", "formation_id", "ue_id"):
|
||||||
|
if required_arg not in data:
|
||||||
|
raise ScoValueError(f"missing argument: {required_arg}")
|
||||||
|
if not data["code"]:
|
||||||
|
raise ScoValueError("module code must be non empty")
|
||||||
|
# Check formation
|
||||||
|
formation = Formation.get_formation(data["formation_id"])
|
||||||
|
ue = UniteEns.get_ue(data["ue_id"])
|
||||||
|
# refuse de créer un module APC avec semestres semestre du module != semestre de l'UE
|
||||||
|
if formation.is_apc():
|
||||||
|
if int(data.get("semestre_id", 1)) != ue.semestre_idx:
|
||||||
|
raise ScoValueError(
|
||||||
|
"Formation incompatible: indices UE et module différents"
|
||||||
|
)
|
||||||
module = super().create_from_dict(data)
|
module = super().create_from_dict(data)
|
||||||
db.session.flush()
|
db.session.flush()
|
||||||
module._set_parcours_from_list(data.get("parcours", []) or [])
|
module.set_parcours_from_list(data.get("parcours", []) or [])
|
||||||
|
log(f"module_create: created {module.id} with {data}")
|
||||||
|
if news:
|
||||||
|
db.session.commit()
|
||||||
|
db.session.refresh(module)
|
||||||
|
ScolarNews.add(
|
||||||
|
typ=ScolarNews.NEWS_FORM,
|
||||||
|
obj=formation.id,
|
||||||
|
text=f"Modification de la formation {formation.acronyme}",
|
||||||
|
)
|
||||||
|
if inval_cache:
|
||||||
|
formation.invalidate_cached_sems()
|
||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
||||||
def _set_parcours_from_list(self, parcours: list[ApcParcours | int]):
|
def is_locked(self) -> bool:
|
||||||
|
"""True if module cannot be modified
|
||||||
|
because it is used in a locked formsemestre.
|
||||||
|
"""
|
||||||
|
from app.models import FormSemestre, ModuleImpl
|
||||||
|
|
||||||
|
mods = (
|
||||||
|
db.session.query(Module)
|
||||||
|
.filter_by(id=self.id)
|
||||||
|
.join(ModuleImpl)
|
||||||
|
.join(FormSemestre)
|
||||||
|
.filter_by(etat=False)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return bool(mods)
|
||||||
|
|
||||||
|
def can_be_deleted(self) -> bool:
|
||||||
|
"""True if module can be deleted"""
|
||||||
|
return self.modimpls.count() == 0
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"Delete module. News, inval cache."
|
||||||
|
if self.is_locked():
|
||||||
|
raise ScoLockedFormError()
|
||||||
|
if not self.can_be_deleted():
|
||||||
|
raise ScoNonEmptyFormationObject(
|
||||||
|
"Module",
|
||||||
|
msg=self.titre or self.code,
|
||||||
|
dest_url=url_for(
|
||||||
|
"notes.ue_table",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formation_id=self.formation_id,
|
||||||
|
semestre_idx=self.ue.semestre_idx,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
formation = self.formation
|
||||||
|
db.session.delete(self)
|
||||||
|
log(f"Module.delete({self.id})")
|
||||||
|
db.session.commit()
|
||||||
|
# news
|
||||||
|
ScolarNews.add(
|
||||||
|
typ=ScolarNews.NEWS_FORM,
|
||||||
|
obj=formation.id,
|
||||||
|
text=f"Modification de la formation {formation.acronyme}",
|
||||||
|
)
|
||||||
|
formation.invalidate_cached_sems()
|
||||||
|
|
||||||
|
def set_parcours_from_list(self, parcours: list[ApcParcours | int]):
|
||||||
"""Ajoute ces parcours à la liste des parcours du module.
|
"""Ajoute ces parcours à la liste des parcours du module.
|
||||||
Chaque élément est soit un objet parcours soit un id.
|
Chaque élément est soit un objet parcours soit un id.
|
||||||
S'assure que chaque parcours est dans le référentiel de compétence
|
S'assure que chaque parcours est dans le référentiel de compétence
|
||||||
|
@ -41,8 +41,8 @@ from app import ScoValueError
|
|||||||
from app import comp
|
from app import comp
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
from app.models import FormSemestre, UniteEns
|
from app.models import FormSemestre, UniteEns
|
||||||
import app.pe.pe_affichage as pe_affichage
|
from app.pe import pe_affichage
|
||||||
import app.pe.pe_etudiant as pe_etudiant
|
from app.pe import pe_etudiant
|
||||||
from app.pe.moys import pe_tabletags, pe_moytag
|
from app.pe.moys import pe_tabletags, pe_moytag
|
||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
from app.scodoc import codes_cursus as sco_codes
|
from app.scodoc import codes_cursus as sco_codes
|
||||||
@ -59,13 +59,18 @@ class ResSemBUTTag(ResultatsSemestreBUT, pe_tabletags.TableTag):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
formsemestre: FormSemestre,
|
formsemestre: FormSemestre,
|
||||||
options={"moyennes_tags": True, "moyennes_ue_res_sae": False},
|
options: dict | None = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
formsemestre: le ``FormSemestre`` sur lequel il se base
|
formsemestre: le ``FormSemestre`` sur lequel il se base
|
||||||
options: Un dictionnaire d'options
|
options: Un dictionnaire d'options
|
||||||
"""
|
"""
|
||||||
|
options = (
|
||||||
|
{"moyennes_tags": True, "moyennes_ue_res_sae": False}
|
||||||
|
if options is None
|
||||||
|
else options
|
||||||
|
)
|
||||||
ResultatsSemestreBUT.__init__(self, formsemestre)
|
ResultatsSemestreBUT.__init__(self, formsemestre)
|
||||||
pe_tabletags.TableTag.__init__(self)
|
pe_tabletags.TableTag.__init__(self)
|
||||||
|
|
||||||
|
@ -491,9 +491,9 @@ def dict_decision_jury(
|
|||||||
]
|
]
|
||||||
|
|
||||||
d["decision_ue"] = []
|
d["decision_ue"] = []
|
||||||
if decision[
|
if decision["decisions_ue"]:
|
||||||
"decisions_ue"
|
# and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id):
|
||||||
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
|
# always publish (car utile pour export Apogee)
|
||||||
for ue_id in decision["decisions_ue"].keys():
|
for ue_id in decision["decisions_ue"].keys():
|
||||||
ue = edit_ue.ue_list({"ue_id": ue_id})[0]
|
ue = edit_ue.ue_list({"ue_id": ue_id})[0]
|
||||||
d["decision_ue"].append(
|
d["decision_ue"].append(
|
||||||
|
@ -34,7 +34,6 @@ import sqlalchemy as sa
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.formations import edit_module
|
|
||||||
from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
|
from app.models import APO_CODE_STR_LEN, SHORT_STR_LEN
|
||||||
from app.models import (
|
from app.models import (
|
||||||
ApcValidationAnnee,
|
ApcValidationAnnee,
|
||||||
@ -939,7 +938,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
"formsemestre_id": formsemestre_id,
|
"formsemestre_id": formsemestre_id,
|
||||||
"responsable_id": tf[2][f"MI{module_id}"],
|
"responsable_id": tf[2][f"MI{module_id}"],
|
||||||
}
|
}
|
||||||
_ = sco_moduleimpl.do_moduleimpl_create(modargs)
|
_ = ModuleImpl.create_from_dict(modargs)
|
||||||
else:
|
else:
|
||||||
# Modification du semestre:
|
# Modification du semestre:
|
||||||
# on doit creer les modules nouvellement selectionnés
|
# on doit creer les modules nouvellement selectionnés
|
||||||
@ -971,27 +970,23 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
"formsemestre_id": formsemestre.id,
|
"formsemestre_id": formsemestre.id,
|
||||||
"responsable_id": tf[2]["MI" + str(module_id)],
|
"responsable_id": tf[2]["MI" + str(module_id)],
|
||||||
}
|
}
|
||||||
moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(modargs)
|
modimpl = ModuleImpl.create_from_dict(modargs)
|
||||||
mod = edit_module.module_list({"module_id": module_id})[0]
|
assert modimpl.module_id == module_id
|
||||||
msg += [
|
mod = modimpl.module
|
||||||
"création de %s (%s)" % (mod["code"] or "?", mod["titre"] or "?")
|
msg += [f"""création de {mod.code or "?"} ({mod.titre or "?"})"""]
|
||||||
]
|
|
||||||
# INSCRIPTIONS DES ETUDIANTS
|
# INSCRIPTIONS DES ETUDIANTS
|
||||||
log(
|
group_id = tf[2][f"{module_id}!group_id"]
|
||||||
'inscription module: %s = "%s"'
|
log(f"""inscription module: {module_id}!group_id = '{group_id}'""")
|
||||||
% ("%s!group_id" % module_id, tf[2]["%s!group_id" % module_id])
|
|
||||||
)
|
|
||||||
group_id = tf[2]["%s!group_id" % module_id]
|
|
||||||
if group_id:
|
if group_id:
|
||||||
etudids = [
|
etudids = [
|
||||||
x["etudid"] for x in sco_groups.get_group_members(group_id)
|
x["etudid"] for x in sco_groups.get_group_members(group_id)
|
||||||
]
|
]
|
||||||
log(
|
log(
|
||||||
"inscription module:module_id=%s,moduleimpl_id=%s: %s"
|
"inscription module:module_id=%s,moduleimpl_id=%s: %s"
|
||||||
% (module_id, moduleimpl_id, etudids)
|
% (module_id, modimpl.id, etudids)
|
||||||
)
|
)
|
||||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||||
moduleimpl_id,
|
modimpl.id,
|
||||||
formsemestre.id,
|
formsemestre.id,
|
||||||
etudids,
|
etudids,
|
||||||
)
|
)
|
||||||
@ -1002,7 +997,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
else:
|
else:
|
||||||
log(
|
log(
|
||||||
"inscription module:module_id=%s,moduleimpl_id=%s: aucun etudiant inscrit"
|
"inscription module:module_id=%s,moduleimpl_id=%s: aucun etudiant inscrit"
|
||||||
% (module_id, moduleimpl_id)
|
% (module_id, modimpl.id)
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
ok, diag = formsemestre_delete_moduleimpls(
|
ok, diag = formsemestre_delete_moduleimpls(
|
||||||
@ -1022,7 +1017,6 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
sco_moduleimpl.do_moduleimpl_edit(
|
sco_moduleimpl.do_moduleimpl_edit(
|
||||||
modargs, formsemestre_id=formsemestre.id
|
modargs, formsemestre_id=formsemestre.id
|
||||||
)
|
)
|
||||||
mod = edit_module.module_list({"module_id": module_id})[0]
|
|
||||||
# --- Association des parcours
|
# --- Association des parcours
|
||||||
if formsemestre is None:
|
if formsemestre is None:
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
|
@ -40,7 +40,6 @@ from app.but.cursus_but import formsemestre_warning_apc_setup
|
|||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_common import ResultatsSemestre
|
from app.comp.res_common import ResultatsSemestre
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.formations import formation_io
|
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Evaluation,
|
Evaluation,
|
||||||
Formation,
|
Formation,
|
||||||
|
@ -51,18 +51,6 @@ _moduleimplEditor = ndb.EditableTable(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def do_moduleimpl_create(args):
|
|
||||||
"create a moduleimpl"
|
|
||||||
# TODO remplacer par une methode de ModuleImpl qui appelle
|
|
||||||
# super().create_from_dict() puis invalide le formsemestre
|
|
||||||
cnx = ndb.GetDBConnexion()
|
|
||||||
r = _moduleimplEditor.create(cnx, args)
|
|
||||||
sco_cache.invalidate_formsemestre(
|
|
||||||
formsemestre_id=args["formsemestre_id"]
|
|
||||||
) # > creation moduleimpl
|
|
||||||
return r
|
|
||||||
|
|
||||||
|
|
||||||
def do_moduleimpl_delete(oid, formsemestre_id=None):
|
def do_moduleimpl_delete(oid, formsemestre_id=None):
|
||||||
"delete moduleimpl (desinscrit tous les etudiants)"
|
"delete moduleimpl (desinscrit tous les etudiants)"
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
|
@ -48,8 +48,7 @@ from wtforms import (
|
|||||||
HiddenField,
|
HiddenField,
|
||||||
SelectMultipleField,
|
SelectMultipleField,
|
||||||
)
|
)
|
||||||
from app.formations import edit_module
|
from app.models import Evaluation, Module, ModuleImpl
|
||||||
from app.models import Evaluation, ModuleImpl
|
|
||||||
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
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
@ -243,9 +242,9 @@ class PlacementRunner:
|
|||||||
self.moduleimpl_data = sco_moduleimpl.moduleimpl_list(
|
self.moduleimpl_data = sco_moduleimpl.moduleimpl_list(
|
||||||
moduleimpl_id=self.moduleimpl_id
|
moduleimpl_id=self.moduleimpl_id
|
||||||
)[0]
|
)[0]
|
||||||
self.module_data = edit_module.module_list(
|
self.module_data = Module.get_module(
|
||||||
args={"module_id": self.moduleimpl_data["module_id"]}
|
self.moduleimpl_data["module_id"]
|
||||||
)[0]
|
).to_dict()
|
||||||
self.sem = sco_formsemestre.get_formsemestre(
|
self.sem = sco_formsemestre.get_formsemestre(
|
||||||
self.moduleimpl_data["formsemestre_id"]
|
self.moduleimpl_data["formsemestre_id"]
|
||||||
)
|
)
|
||||||
|
@ -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_module, edit_ue
|
from app.formations import 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, Matiere, ModuleImpl, UniteEns
|
from app.models import Evaluation, Identite, Matiere, Module, 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
|
||||||
@ -114,7 +114,7 @@ def external_ue_create(
|
|||||||
{"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}
|
{"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}
|
||||||
)
|
)
|
||||||
|
|
||||||
module_id = edit_module.do_module_create(
|
module = Module.create_from_dict(
|
||||||
{
|
{
|
||||||
"titre": "UE extérieure",
|
"titre": "UE extérieure",
|
||||||
"code": acronyme,
|
"code": acronyme,
|
||||||
@ -125,11 +125,13 @@ def external_ue_create(
|
|||||||
"semestre_id": formsemestre.semestre_id,
|
"semestre_id": formsemestre.semestre_id,
|
||||||
"module_type": scu.ModuleType.STANDARD,
|
"module_type": scu.ModuleType.STANDARD,
|
||||||
},
|
},
|
||||||
|
news=True,
|
||||||
|
inval_cache=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(
|
modimpl = ModuleImpl.create_from_dict(
|
||||||
{
|
{
|
||||||
"module_id": module_id,
|
"module_id": module.id,
|
||||||
"formsemestre_id": formsemestre_id,
|
"formsemestre_id": formsemestre_id,
|
||||||
# affecte le 1er responsable du semestre comme resp. du module
|
# affecte le 1er responsable du semestre comme resp. du module
|
||||||
"responsable_id": (
|
"responsable_id": (
|
||||||
@ -139,7 +141,6 @@ def external_ue_create(
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
modimpl = db.session.get(ModuleImpl, moduleimpl_id)
|
|
||||||
assert modimpl
|
assert modimpl
|
||||||
return modimpl
|
return modimpl
|
||||||
|
|
||||||
|
@ -2575,21 +2575,21 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
|||||||
bad_sem = []
|
bad_sem = []
|
||||||
formations_set = set() # les formations mentionnées dans les UE et modules
|
formations_set = set() # les formations mentionnées dans les UE et modules
|
||||||
for modimpl in modimpls:
|
for modimpl in modimpls:
|
||||||
mod = edit_module.module_list({"module_id": modimpl["module_id"]})[0]
|
mod = Module.get_instance(modimpl["module_id"])
|
||||||
formations_set.add(mod["formation_id"])
|
formations_set.add(mod.formation_id)
|
||||||
ue = UniteEns.query.get_or_404(mod["ue_id"])
|
ue = mod.ue
|
||||||
ue_dict = ue.to_dict()
|
ue_dict = ue.to_dict()
|
||||||
formations_set.add(ue_dict["formation_id"])
|
formations_set.add(ue_dict["formation_id"])
|
||||||
if ue_dict["formation_id"] != mod["formation_id"]:
|
if ue_dict["formation_id"] != mod.formation_id:
|
||||||
modimpl["mod"] = mod
|
modimpl["mod"] = mod.to_dict()
|
||||||
modimpl["ue"] = ue_dict
|
modimpl["ue"] = ue_dict
|
||||||
bad_ue.append(modimpl)
|
bad_ue.append(modimpl)
|
||||||
if sem["formation_id"] != mod["formation_id"]:
|
if sem["formation_id"] != mod.formation_id:
|
||||||
bad_sem.append(modimpl)
|
bad_sem.append(modimpl)
|
||||||
modimpl["mod"] = mod
|
modimpl["mod"] = mod.to_dict()
|
||||||
|
|
||||||
H = [
|
H = [
|
||||||
"<p>formation_id=%s" % sem["formation_id"],
|
f"""<p>formation_id={sem["formation_id"]}""",
|
||||||
]
|
]
|
||||||
if bad_ue:
|
if bad_ue:
|
||||||
H += [
|
H += [
|
||||||
|
@ -86,7 +86,6 @@ from app.scodoc import (
|
|||||||
sco_archives_etud,
|
sco_archives_etud,
|
||||||
sco_bug_report,
|
sco_bug_report,
|
||||||
sco_cache,
|
sco_cache,
|
||||||
sco_debouche,
|
|
||||||
sco_dept,
|
sco_dept,
|
||||||
sco_dump_db,
|
sco_dump_db,
|
||||||
sco_etud,
|
sco_etud,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"Infos sur version ScoDoc"
|
||||||
|
|
||||||
SCOVERSION = "9.7.28"
|
SCOVERSION = "9.7.28"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
@ -117,7 +117,7 @@ def GET(
|
|||||||
print("reply", reply.text)
|
print("reply", reply.text)
|
||||||
raise APIError(
|
raise APIError(
|
||||||
errmsg or f"""erreur get {url} !""",
|
errmsg or f"""erreur get {url} !""",
|
||||||
reply.json(),
|
reply if reply.status_code == 404 else reply.json(),
|
||||||
status_code=reply.status_code,
|
status_code=reply.status_code,
|
||||||
)
|
)
|
||||||
if raw:
|
if raw:
|
||||||
@ -220,7 +220,8 @@ def check_failure_get(path: str, headers: dict, err: str = None):
|
|||||||
# ^ Renvoi un 404
|
# ^ Renvoi un 404
|
||||||
except APIError as api_err:
|
except APIError as api_err:
|
||||||
if err is not None:
|
if err is not None:
|
||||||
assert api_err.payload["message"] == err
|
if "message" in api_err.payload:
|
||||||
|
assert api_err.payload["message"] == err
|
||||||
else:
|
else:
|
||||||
raise APIError("Le GET n'aurait pas du fonctionner")
|
raise APIError("Le GET n'aurait pas du fonctionner")
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ def test_formation_export(api_headers):
|
|||||||
assert isinstance(module["heures_td"], float)
|
assert isinstance(module["heures_td"], float)
|
||||||
assert isinstance(module["heures_tp"], float)
|
assert isinstance(module["heures_tp"], float)
|
||||||
assert isinstance(module["coefficient"], float)
|
assert isinstance(module["coefficient"], float)
|
||||||
assert isinstance(module["ects"], str)
|
assert isinstance(module["ects"], str) if "ects" in module else True
|
||||||
assert isinstance(module["semestre_id"], int)
|
assert isinstance(module["semestre_id"], int)
|
||||||
assert isinstance(module["numero"], int)
|
assert isinstance(module["numero"], int)
|
||||||
assert isinstance(module["code_apogee"], str)
|
assert isinstance(module["code_apogee"], str)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Utilitaires pour les tests de l'API
|
"""Utilitaires pour les tests de l'API
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
@ -37,11 +38,11 @@ def verify_occurences_ids_etuds(json_response) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
DEPARTEMENT_FIELDS = [
|
DEPARTEMENT_FIELDS = [
|
||||||
"id",
|
|
||||||
"acronym",
|
"acronym",
|
||||||
"description",
|
|
||||||
"visible",
|
|
||||||
"date_creation",
|
"date_creation",
|
||||||
|
"description",
|
||||||
|
"id",
|
||||||
|
"visible",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Champs "données personnelles"
|
# Champs "données personnelles"
|
||||||
@ -67,17 +68,17 @@ ETUD_FIELDS = {
|
|||||||
|
|
||||||
|
|
||||||
FORMATION_FIELDS = {
|
FORMATION_FIELDS = {
|
||||||
"dept_id",
|
|
||||||
"acronyme",
|
"acronyme",
|
||||||
"titre_officiel",
|
|
||||||
"formation_code",
|
|
||||||
"code_specialite",
|
"code_specialite",
|
||||||
"id",
|
"dept_id",
|
||||||
"titre",
|
"formation_code",
|
||||||
"version",
|
|
||||||
"type_parcours",
|
|
||||||
"referentiel_competence_id",
|
|
||||||
"formation_id",
|
"formation_id",
|
||||||
|
"id",
|
||||||
|
"referentiel_competence_id",
|
||||||
|
"titre_officiel",
|
||||||
|
"titre",
|
||||||
|
"type_parcours",
|
||||||
|
"version",
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMATION_EXPORT_FIELDS = {
|
FORMATION_EXPORT_FIELDS = {
|
||||||
@ -95,39 +96,42 @@ FORMATION_EXPORT_FIELDS = {
|
|||||||
|
|
||||||
FORMATION_EXPORT_UE_FIELDS = {
|
FORMATION_EXPORT_UE_FIELDS = {
|
||||||
"acronyme",
|
"acronyme",
|
||||||
|
"code_apogee",
|
||||||
|
"coefficient",
|
||||||
|
"color",
|
||||||
|
"ects",
|
||||||
|
"is_external",
|
||||||
|
"matiere",
|
||||||
"numero",
|
"numero",
|
||||||
|
"reference",
|
||||||
|
"semestre_idx",
|
||||||
"titre",
|
"titre",
|
||||||
"type",
|
"type",
|
||||||
"ue_code",
|
"ue_code",
|
||||||
"ects",
|
|
||||||
"is_external",
|
|
||||||
"code_apogee",
|
|
||||||
"coefficient",
|
|
||||||
"semestre_idx",
|
|
||||||
"color",
|
|
||||||
"reference",
|
|
||||||
"matiere",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMATION_EXPORT_UE_MATIERE_FIELDS = {
|
FORMATION_EXPORT_UE_MATIERE_FIELDS = {
|
||||||
"titre",
|
|
||||||
"numero",
|
|
||||||
"module",
|
"module",
|
||||||
|
"numero",
|
||||||
|
"titre",
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMATION_EXPORT_UE_MATIERE_MODULE_FIELDS = {
|
FORMATION_EXPORT_UE_MATIERE_MODULE_FIELDS = {
|
||||||
"titre",
|
|
||||||
"abbrev",
|
"abbrev",
|
||||||
|
"app_critiques",
|
||||||
|
"code_apogee",
|
||||||
"code",
|
"code",
|
||||||
|
"coefficient",
|
||||||
|
"coefficients",
|
||||||
|
"edt_id",
|
||||||
"heures_cours",
|
"heures_cours",
|
||||||
"heures_td",
|
"heures_td",
|
||||||
"coefficient",
|
"heures_tp",
|
||||||
"ects",
|
|
||||||
"semestre_id",
|
|
||||||
"numero",
|
|
||||||
"code_apogee",
|
|
||||||
"module_type",
|
"module_type",
|
||||||
"coefficients",
|
"numero",
|
||||||
|
"parcours",
|
||||||
|
"semestre_id",
|
||||||
|
"titre",
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMATION_EXPORT_UE_MATIERE_MODULE_COEF_FIELDS = {
|
FORMATION_EXPORT_UE_MATIERE_MODULE_COEF_FIELDS = {
|
||||||
@ -136,42 +140,42 @@ FORMATION_EXPORT_UE_MATIERE_MODULE_COEF_FIELDS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FORMSEMESTRE_FIELDS = [
|
FORMSEMESTRE_FIELDS = [
|
||||||
"titre",
|
"block_moyenne_generale",
|
||||||
"gestion_semestrielle",
|
"block_moyennes",
|
||||||
"scodoc7_id",
|
|
||||||
"date_debut",
|
|
||||||
"bul_bgcolor",
|
"bul_bgcolor",
|
||||||
|
"bul_hide_xml",
|
||||||
|
"date_debut_iso",
|
||||||
|
"date_debut",
|
||||||
|
"date_fin_iso",
|
||||||
"date_fin",
|
"date_fin",
|
||||||
"resp_can_edit",
|
"departement",
|
||||||
"dept_id",
|
"dept_id",
|
||||||
|
"elt_annee_apo",
|
||||||
|
"elt_sem_apo",
|
||||||
|
"ens_can_edit_eval",
|
||||||
|
"etape_apo",
|
||||||
"etat",
|
"etat",
|
||||||
"resp_can_change_ens",
|
"formation_id",
|
||||||
|
"formation",
|
||||||
|
"formsemestre_id",
|
||||||
|
"gestion_compensation",
|
||||||
|
"gestion_semestrielle",
|
||||||
"id",
|
"id",
|
||||||
"modalite",
|
"modalite",
|
||||||
"ens_can_edit_eval",
|
|
||||||
"formation_id",
|
|
||||||
"gestion_compensation",
|
|
||||||
"elt_sem_apo",
|
|
||||||
"semestre_id",
|
|
||||||
"bul_hide_xml",
|
|
||||||
"elt_annee_apo",
|
|
||||||
"block_moyenne_generale",
|
|
||||||
"formsemestre_id",
|
|
||||||
"titre_num",
|
|
||||||
"titre_formation",
|
|
||||||
"date_debut_iso",
|
|
||||||
"date_fin_iso",
|
|
||||||
"responsables",
|
|
||||||
"parcours",
|
"parcours",
|
||||||
"departement",
|
"resp_can_change_ens",
|
||||||
"formation",
|
"resp_can_edit",
|
||||||
"etape_apo",
|
"responsables",
|
||||||
"block_moyennes",
|
"scodoc7_id",
|
||||||
|
"semestre_id",
|
||||||
|
"titre_formation",
|
||||||
|
"titre_num",
|
||||||
|
"titre",
|
||||||
]
|
]
|
||||||
|
|
||||||
FSEM_FIELDS = {
|
FSEM_FIELDS = {
|
||||||
"block_moyennes",
|
|
||||||
"block_moyenne_generale",
|
"block_moyenne_generale",
|
||||||
|
"block_moyennes",
|
||||||
"bul_bgcolor",
|
"bul_bgcolor",
|
||||||
"bul_hide_xml",
|
"bul_hide_xml",
|
||||||
"date_debut_iso",
|
"date_debut_iso",
|
||||||
@ -198,123 +202,123 @@ FSEM_FIELDS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MODIMPL_FIELDS = {
|
MODIMPL_FIELDS = {
|
||||||
"id",
|
|
||||||
"formsemestre_id",
|
|
||||||
"computation_expr",
|
"computation_expr",
|
||||||
"module_id",
|
|
||||||
"responsable_id",
|
|
||||||
"moduleimpl_id",
|
|
||||||
"ens",
|
"ens",
|
||||||
|
"formsemestre_id",
|
||||||
|
"id",
|
||||||
|
"module_id",
|
||||||
"module",
|
"module",
|
||||||
|
"moduleimpl_id",
|
||||||
|
"responsable_id",
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_FIELDS = {
|
MODULE_FIELDS = {
|
||||||
"heures_tp",
|
|
||||||
"code_apogee",
|
|
||||||
"titre",
|
|
||||||
"coefficient",
|
|
||||||
"module_type",
|
|
||||||
"id",
|
|
||||||
"ects",
|
|
||||||
"abbrev",
|
"abbrev",
|
||||||
"ue_id",
|
"code_apogee",
|
||||||
"code",
|
"code",
|
||||||
|
"coefficient",
|
||||||
|
"ects",
|
||||||
"formation_id",
|
"formation_id",
|
||||||
"heures_cours",
|
"heures_cours",
|
||||||
"matiere_id",
|
|
||||||
"heures_td",
|
"heures_td",
|
||||||
"semestre_id",
|
"heures_tp",
|
||||||
"numero",
|
"id",
|
||||||
|
"matiere_id",
|
||||||
"module_id",
|
"module_id",
|
||||||
|
"module_type",
|
||||||
|
"numero",
|
||||||
|
"semestre_id",
|
||||||
|
"titre",
|
||||||
|
"ue_id",
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_FIELDS = {
|
UE_FIELDS = {
|
||||||
"semestre_idx",
|
|
||||||
"type",
|
|
||||||
"formation_id",
|
|
||||||
"ue_code",
|
|
||||||
"id",
|
|
||||||
"ects",
|
|
||||||
"acronyme",
|
"acronyme",
|
||||||
"is_external",
|
|
||||||
"numero",
|
|
||||||
"code_apogee",
|
"code_apogee",
|
||||||
"titre",
|
|
||||||
"coefficient",
|
"coefficient",
|
||||||
"color",
|
"color",
|
||||||
|
"ects",
|
||||||
|
"formation_id",
|
||||||
|
"id",
|
||||||
|
"is_external",
|
||||||
|
"numero",
|
||||||
|
"semestre_idx",
|
||||||
|
"titre",
|
||||||
|
"type",
|
||||||
|
"ue_code",
|
||||||
"ue_id",
|
"ue_id",
|
||||||
}
|
}
|
||||||
|
|
||||||
BULLETIN_FIELDS = {
|
BULLETIN_FIELDS = {
|
||||||
"version",
|
|
||||||
"type",
|
|
||||||
"date",
|
"date",
|
||||||
"publie",
|
"etat_inscription",
|
||||||
"etudiant",
|
"etudiant",
|
||||||
"formation",
|
"formation",
|
||||||
"formsemestre_id",
|
"formsemestre_id",
|
||||||
"etat_inscription",
|
|
||||||
"options",
|
"options",
|
||||||
|
"publie",
|
||||||
"ressources",
|
"ressources",
|
||||||
"saes",
|
"saes",
|
||||||
"ues",
|
|
||||||
"semestre",
|
"semestre",
|
||||||
|
"type",
|
||||||
|
"ues",
|
||||||
|
"version",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BULLETIN_ETUDIANT_FIELDS = {
|
BULLETIN_ETUDIANT_FIELDS = {
|
||||||
|
"boursier",
|
||||||
"civilite",
|
"civilite",
|
||||||
"code_ine",
|
"code_ine",
|
||||||
"code_nip",
|
"code_nip",
|
||||||
|
"codepostaldomicile",
|
||||||
"date_naissance",
|
"date_naissance",
|
||||||
"dept_id",
|
|
||||||
"dept_acronym",
|
"dept_acronym",
|
||||||
|
"dept_id",
|
||||||
|
"dept_naissance",
|
||||||
|
"description",
|
||||||
|
"domicile",
|
||||||
"email",
|
"email",
|
||||||
"emailperso",
|
"emailperso",
|
||||||
"etudid",
|
"etudid",
|
||||||
"nom",
|
|
||||||
"prenom",
|
|
||||||
"nomprenom",
|
|
||||||
"lieu_naissance",
|
|
||||||
"dept_naissance",
|
|
||||||
"nationalite",
|
|
||||||
"boursier",
|
|
||||||
"fiche_url",
|
|
||||||
"photo_url",
|
|
||||||
"id",
|
|
||||||
"domicile",
|
|
||||||
"villedomicile",
|
|
||||||
"telephone",
|
|
||||||
"fax",
|
"fax",
|
||||||
"description",
|
"fiche_url",
|
||||||
"codepostaldomicile",
|
"id",
|
||||||
|
"lieu_naissance",
|
||||||
|
"nationalite",
|
||||||
|
"nom",
|
||||||
|
"nomprenom",
|
||||||
"paysdomicile",
|
"paysdomicile",
|
||||||
|
"photo_url",
|
||||||
|
"prenom",
|
||||||
|
"telephone",
|
||||||
"telephonemobile",
|
"telephonemobile",
|
||||||
"typeadresse",
|
"typeadresse",
|
||||||
|
"villedomicile",
|
||||||
}
|
}
|
||||||
|
|
||||||
BULLETIN_FORMATION_FIELDS = {"id", "acronyme", "titre_officiel", "titre"}
|
BULLETIN_FORMATION_FIELDS = {"id", "acronyme", "titre_officiel", "titre"}
|
||||||
|
|
||||||
BULLETIN_OPTIONS_FIELDS = {
|
BULLETIN_OPTIONS_FIELDS = {
|
||||||
"show_abs",
|
|
||||||
"show_abs_modules",
|
"show_abs_modules",
|
||||||
"show_ects",
|
"show_abs",
|
||||||
"show_codemodules",
|
"show_codemodules",
|
||||||
|
"show_coef",
|
||||||
|
"show_date_inscr",
|
||||||
|
"show_ects",
|
||||||
"show_matieres",
|
"show_matieres",
|
||||||
"show_rangs",
|
"show_minmax_eval",
|
||||||
"show_ue_rangs",
|
"show_minmax_mod",
|
||||||
|
"show_minmax",
|
||||||
"show_mod_rangs",
|
"show_mod_rangs",
|
||||||
"show_moypromo",
|
"show_moypromo",
|
||||||
"show_minmax",
|
"show_rangs",
|
||||||
"show_minmax_mod",
|
|
||||||
"show_minmax_eval",
|
|
||||||
"show_coef",
|
|
||||||
"show_ue_cap_details",
|
|
||||||
"show_ue_cap_current",
|
|
||||||
"show_temporary",
|
"show_temporary",
|
||||||
"temporary_txt",
|
"show_ue_cap_current",
|
||||||
|
"show_ue_cap_details",
|
||||||
|
"show_ue_rangs",
|
||||||
"show_uevalid",
|
"show_uevalid",
|
||||||
"show_date_inscr",
|
"temporary_txt",
|
||||||
}
|
}
|
||||||
|
|
||||||
BULLETIN_RESSOURCES_FIELDS = {
|
BULLETIN_RESSOURCES_FIELDS = {
|
||||||
@ -346,23 +350,23 @@ BULLETIN_SAES_FIELDS = {
|
|||||||
|
|
||||||
########### RESSOURCES ET SAES ###########
|
########### RESSOURCES ET SAES ###########
|
||||||
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS = {
|
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_FIELDS = {
|
||||||
"id",
|
|
||||||
"titre",
|
|
||||||
"code_apogee",
|
"code_apogee",
|
||||||
"url",
|
|
||||||
"moyenne",
|
|
||||||
"evaluations",
|
"evaluations",
|
||||||
|
"id",
|
||||||
|
"moyenne",
|
||||||
|
"titre",
|
||||||
|
"url",
|
||||||
}
|
}
|
||||||
|
|
||||||
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS = {
|
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_FIELDS = {
|
||||||
"id",
|
"coef",
|
||||||
"description",
|
|
||||||
"date",
|
"date",
|
||||||
|
"description",
|
||||||
"heure_debut",
|
"heure_debut",
|
||||||
"heure_fin",
|
"heure_fin",
|
||||||
"coef",
|
"id",
|
||||||
"poids",
|
|
||||||
"note",
|
"note",
|
||||||
|
"poids",
|
||||||
"url",
|
"url",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,10 +377,10 @@ BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_POIDS_FIELDS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS = {
|
BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS = {
|
||||||
"value",
|
|
||||||
"min",
|
|
||||||
"max",
|
"max",
|
||||||
|
"min",
|
||||||
"moy",
|
"moy",
|
||||||
|
"value",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -384,19 +388,19 @@ BULLETIN_RESSOURCES_ET_SAES_RESSOURCE_ET_SAE_EVALUATION_NOTE_FIELDS = {
|
|||||||
BULLETIN_UES_FIELDS = {"RT1.1", "RT2.1", "RT3.1"}
|
BULLETIN_UES_FIELDS = {"RT1.1", "RT2.1", "RT3.1"}
|
||||||
|
|
||||||
BULLETIN_UES_UE_FIELDS = {
|
BULLETIN_UES_UE_FIELDS = {
|
||||||
"id",
|
"bonus",
|
||||||
"titre",
|
"capitalise",
|
||||||
"numero",
|
|
||||||
"type",
|
|
||||||
"color",
|
"color",
|
||||||
"competence",
|
"competence",
|
||||||
"moyenne",
|
"ECTS",
|
||||||
"bonus",
|
"id",
|
||||||
"malus",
|
"malus",
|
||||||
"capitalise",
|
"moyenne",
|
||||||
|
"numero",
|
||||||
"ressources",
|
"ressources",
|
||||||
"saes",
|
"saes",
|
||||||
"ECTS",
|
"titre",
|
||||||
|
"type",
|
||||||
}
|
}
|
||||||
|
|
||||||
BULLETIN_UES_UE_MOYENNE_FIELDS = {"value", "min", "max", "moy", "rang", "total"}
|
BULLETIN_UES_UE_MOYENNE_FIELDS = {"value", "min", "max", "moy", "rang", "total"}
|
||||||
@ -461,16 +465,16 @@ BULLETIN_UES_UE_ECTS_FIELDS = {"acquis", "total"}
|
|||||||
|
|
||||||
########### SEMESTRE ###########
|
########### SEMESTRE ###########
|
||||||
BULLETIN_SEMESTRE_FIELDS = {
|
BULLETIN_SEMESTRE_FIELDS = {
|
||||||
"etapes",
|
"absences",
|
||||||
|
"annee_universitaire",
|
||||||
"date_debut",
|
"date_debut",
|
||||||
"date_fin",
|
"date_fin",
|
||||||
"annee_universitaire",
|
|
||||||
"numero",
|
|
||||||
"inscription",
|
|
||||||
"groupes",
|
|
||||||
"absences",
|
|
||||||
"ECTS",
|
"ECTS",
|
||||||
|
"etapes",
|
||||||
|
"groupes",
|
||||||
|
"inscription",
|
||||||
"notes",
|
"notes",
|
||||||
|
"numero",
|
||||||
"rang",
|
"rang",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,78 +488,78 @@ BULLETIN_SEMESTRE_RANG_FIELDS = {"value", "total"}
|
|||||||
|
|
||||||
|
|
||||||
EVAL_FIELDS = {
|
EVAL_FIELDS = {
|
||||||
"id",
|
"coefficient",
|
||||||
"description",
|
|
||||||
"date_debut",
|
"date_debut",
|
||||||
"date_fin",
|
"date_fin",
|
||||||
"coefficient",
|
"description",
|
||||||
|
"etat",
|
||||||
"evaluation_type",
|
"evaluation_type",
|
||||||
|
"id",
|
||||||
"moduleimpl_id",
|
"moduleimpl_id",
|
||||||
|
"nb_inscrits",
|
||||||
|
"nb_notes_abs",
|
||||||
|
"nb_notes_att",
|
||||||
|
"nb_notes_exc",
|
||||||
|
"nb_notes_manquantes",
|
||||||
"note_max",
|
"note_max",
|
||||||
"numero",
|
"numero",
|
||||||
"poids",
|
"poids",
|
||||||
"publish_incomplete",
|
"publish_incomplete",
|
||||||
"visibulletin",
|
|
||||||
"etat",
|
|
||||||
"nb_inscrits",
|
|
||||||
"nb_notes_manquantes",
|
|
||||||
"nb_notes_abs",
|
|
||||||
"nb_notes_att",
|
|
||||||
"nb_notes_exc",
|
|
||||||
"saisie_notes",
|
"saisie_notes",
|
||||||
|
"visibulletin",
|
||||||
}
|
}
|
||||||
|
|
||||||
SAISIE_NOTES_FIELDS = {"datetime_debut", "datetime_fin", "datetime_mediane"}
|
SAISIE_NOTES_FIELDS = {"datetime_debut", "datetime_fin", "datetime_mediane"}
|
||||||
|
|
||||||
REF_COMP_FIELDS = {
|
REF_COMP_FIELDS = {
|
||||||
"dept_id",
|
|
||||||
"annexe",
|
"annexe",
|
||||||
"specialite",
|
"competences",
|
||||||
"specialite_long",
|
"dept_id",
|
||||||
"type_structure",
|
"parcours",
|
||||||
"type_departement",
|
|
||||||
"type_titre",
|
|
||||||
"version_orebut",
|
|
||||||
"scodoc_date_loaded",
|
"scodoc_date_loaded",
|
||||||
"scodoc_orig_filename",
|
"scodoc_orig_filename",
|
||||||
"competences",
|
"specialite_long",
|
||||||
"parcours",
|
"specialite",
|
||||||
|
"type_departement",
|
||||||
|
"type_structure",
|
||||||
|
"type_titre",
|
||||||
|
"version_orebut",
|
||||||
}
|
}
|
||||||
|
|
||||||
ABSENCES_FIELDS = {
|
ABSENCES_FIELDS = {
|
||||||
"jour",
|
"begin",
|
||||||
"matin",
|
"description",
|
||||||
|
"end",
|
||||||
"estabs",
|
"estabs",
|
||||||
"estjust",
|
"estjust",
|
||||||
"description",
|
"jour",
|
||||||
"begin",
|
"matin",
|
||||||
"end",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ABSENCES_GROUP_ETAT_FIELDS = {"etudid", "list_abs"}
|
ABSENCES_GROUP_ETAT_FIELDS = {"etudid", "list_abs"}
|
||||||
|
|
||||||
|
|
||||||
FORMSEMESTRE_ETUD_FIELDS = {
|
FORMSEMESTRE_ETUD_FIELDS = {
|
||||||
"id",
|
|
||||||
"code_nip",
|
|
||||||
"code_ine",
|
|
||||||
"nom",
|
|
||||||
"nom_usuel",
|
|
||||||
"prenom",
|
|
||||||
"civilite",
|
"civilite",
|
||||||
|
"code_ine",
|
||||||
|
"code_nip",
|
||||||
"groups",
|
"groups",
|
||||||
|
"id",
|
||||||
|
"nom_usuel",
|
||||||
|
"nom",
|
||||||
|
"prenom",
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMSEMESTRE_ETUS_GROUPS_FIELDS = {
|
FORMSEMESTRE_ETUS_GROUPS_FIELDS = {
|
||||||
"partition_id",
|
|
||||||
"id",
|
|
||||||
"formsemestre_id",
|
|
||||||
"partition_name",
|
|
||||||
"numero",
|
|
||||||
"bul_show_rank",
|
"bul_show_rank",
|
||||||
"show_in_lists",
|
"formsemestre_id",
|
||||||
"group_id",
|
"group_id",
|
||||||
"group_name",
|
"group_name",
|
||||||
|
"id",
|
||||||
|
"numero",
|
||||||
|
"partition_id",
|
||||||
|
"partition_name",
|
||||||
|
"show_in_lists",
|
||||||
}
|
}
|
||||||
|
|
||||||
EVALUATIONS_FIELDS = {
|
EVALUATIONS_FIELDS = {
|
||||||
@ -573,107 +577,107 @@ EVALUATIONS_FIELDS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NOTES_FIELDS = {
|
NOTES_FIELDS = {
|
||||||
"etudid",
|
|
||||||
"evaluation_id",
|
|
||||||
"value",
|
|
||||||
"comment",
|
"comment",
|
||||||
"date",
|
"date",
|
||||||
|
"etudid",
|
||||||
|
"evaluation_id",
|
||||||
"uid",
|
"uid",
|
||||||
|
"value",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PARTITIONS_FIELDS = {
|
PARTITIONS_FIELDS = {
|
||||||
"id",
|
|
||||||
"formsemestre_id",
|
|
||||||
"partition_name",
|
|
||||||
"numero",
|
|
||||||
"bul_show_rank",
|
"bul_show_rank",
|
||||||
|
"formsemestre_id",
|
||||||
|
"id",
|
||||||
|
"numero",
|
||||||
|
"partition_name",
|
||||||
"show_in_lists",
|
"show_in_lists",
|
||||||
}
|
}
|
||||||
|
|
||||||
PARTITION_GROUPS_ETUD_FIELDS = {
|
PARTITION_GROUPS_ETUD_FIELDS = {
|
||||||
"id",
|
"civilite",
|
||||||
|
"code_ine",
|
||||||
|
"code_nip",
|
||||||
"dept_id",
|
"dept_id",
|
||||||
|
"id",
|
||||||
|
"nom_usuel",
|
||||||
"nom",
|
"nom",
|
||||||
"prenom",
|
"prenom",
|
||||||
"nom_usuel",
|
|
||||||
"civilite",
|
|
||||||
"code_nip",
|
|
||||||
"code_ine",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMSEMESTRE_BULLETINS_FIELDS = {
|
FORMSEMESTRE_BULLETINS_FIELDS = {
|
||||||
"version",
|
|
||||||
"type",
|
|
||||||
"date",
|
"date",
|
||||||
"publie",
|
"etat_inscription",
|
||||||
"etudiant",
|
"etudiant",
|
||||||
"formation",
|
"formation",
|
||||||
"formsemestre_id",
|
"formsemestre_id",
|
||||||
"etat_inscription",
|
|
||||||
"options",
|
"options",
|
||||||
|
"publie",
|
||||||
"ressources",
|
"ressources",
|
||||||
"saes",
|
"saes",
|
||||||
"ues",
|
|
||||||
"semestre",
|
"semestre",
|
||||||
|
"type",
|
||||||
|
"ues",
|
||||||
|
"version",
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMSEMESTRE_BULLETINS_ETU_FIELDS = {
|
FORMSEMESTRE_BULLETINS_ETU_FIELDS = {
|
||||||
|
"boursier",
|
||||||
"civilite",
|
"civilite",
|
||||||
"code_ine",
|
"code_ine",
|
||||||
"code_nip",
|
"code_nip",
|
||||||
|
"codepostaldomicile",
|
||||||
"date_naissance",
|
"date_naissance",
|
||||||
"dept_id",
|
|
||||||
"dept_acronym",
|
"dept_acronym",
|
||||||
|
"dept_id",
|
||||||
|
"dept_naissance",
|
||||||
|
"description",
|
||||||
|
"domicile",
|
||||||
"email",
|
"email",
|
||||||
"emailperso",
|
"emailperso",
|
||||||
"etudid",
|
"etudid",
|
||||||
"nom",
|
"fax",
|
||||||
"prenom",
|
|
||||||
"nomprenom",
|
|
||||||
"lieu_naissance",
|
|
||||||
"dept_naissance",
|
|
||||||
"nationalite",
|
|
||||||
"boursier",
|
|
||||||
"fiche_url",
|
"fiche_url",
|
||||||
"photo_url",
|
|
||||||
"id",
|
"id",
|
||||||
"codepostaldomicile",
|
"lieu_naissance",
|
||||||
|
"nationalite",
|
||||||
|
"nom",
|
||||||
|
"nomprenom",
|
||||||
"paysdomicile",
|
"paysdomicile",
|
||||||
|
"photo_url",
|
||||||
|
"prenom",
|
||||||
|
"telephone",
|
||||||
"telephonemobile",
|
"telephonemobile",
|
||||||
"typeadresse",
|
"typeadresse",
|
||||||
"domicile",
|
|
||||||
"villedomicile",
|
"villedomicile",
|
||||||
"telephone",
|
|
||||||
"fax",
|
|
||||||
"description",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMSEMESTRE_BULLETINS_FORMATION_FIELDS = {
|
FORMSEMESTRE_BULLETINS_FORMATION_FIELDS = {
|
||||||
"id",
|
|
||||||
"acronyme",
|
"acronyme",
|
||||||
|
"id",
|
||||||
"titre_officiel",
|
"titre_officiel",
|
||||||
"titre",
|
"titre",
|
||||||
}
|
}
|
||||||
|
|
||||||
FORMSEMESTRE_BULLETINS_OPT_FIELDS = {
|
FORMSEMESTRE_BULLETINS_OPT_FIELDS = {
|
||||||
"show_abs",
|
|
||||||
"show_abs_modules",
|
"show_abs_modules",
|
||||||
"show_ects",
|
"show_abs",
|
||||||
"show_codemodules",
|
"show_codemodules",
|
||||||
|
"show_coef",
|
||||||
|
"show_date_inscr",
|
||||||
|
"show_ects",
|
||||||
"show_matieres",
|
"show_matieres",
|
||||||
"show_rangs",
|
"show_minmax_eval",
|
||||||
"show_ue_rangs",
|
"show_minmax_mod",
|
||||||
|
"show_minmax",
|
||||||
"show_mod_rangs",
|
"show_mod_rangs",
|
||||||
"show_moypromo",
|
"show_moypromo",
|
||||||
"show_minmax",
|
"show_rangs",
|
||||||
"show_minmax_mod",
|
|
||||||
"show_minmax_eval",
|
|
||||||
"show_coef",
|
|
||||||
"show_ue_cap_details",
|
|
||||||
"show_ue_cap_current",
|
|
||||||
"show_temporary",
|
"show_temporary",
|
||||||
"temporary_txt",
|
"show_ue_cap_current",
|
||||||
|
"show_ue_cap_details",
|
||||||
|
"show_ue_rangs",
|
||||||
"show_uevalid",
|
"show_uevalid",
|
||||||
"show_date_inscr",
|
"temporary_txt",
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ Usage: pytest tests/scenarios/test_scenario1_formation.py
|
|||||||
# code écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en août 2021
|
# code écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en août 2021
|
||||||
|
|
||||||
from tests.unit import sco_fake_gen
|
from tests.unit import sco_fake_gen
|
||||||
from app.formations import edit_module, formation_io
|
from app.formations import formation_io
|
||||||
|
from app.models import Formation
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip # test obsolete
|
@pytest.mark.skip # test obsolete
|
||||||
@ -52,11 +53,11 @@ def run_scenario1():
|
|||||||
]
|
]
|
||||||
|
|
||||||
# --- Implémentation des modules
|
# --- Implémentation des modules
|
||||||
modules = edit_module.module_list({"formation_id": formation_id})
|
formation = Formation.get_formation(formation_id)
|
||||||
mods_imp = []
|
mods_imp = []
|
||||||
for mod in modules:
|
for mod in formation.modules:
|
||||||
mi = G.create_moduleimpl(
|
mi = G.create_moduleimpl(
|
||||||
module_id=mod["module_id"],
|
module_id=mod.id,
|
||||||
formsemestre_id=sems[mod["semestre_id"] - 1]["formsemestre_id"],
|
formsemestre_id=sems[mod.semestre_id - 1]["formsemestre_id"],
|
||||||
)
|
)
|
||||||
mods_imp.append(mi)
|
mods_imp.append(mi)
|
||||||
|
@ -16,7 +16,7 @@ import typing
|
|||||||
|
|
||||||
from app import db, log
|
from app import db, log
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.formations import edit_matiere, edit_module, edit_ue
|
from app.formations import edit_ue
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Departement,
|
Departement,
|
||||||
Evaluation,
|
Evaluation,
|
||||||
@ -24,13 +24,13 @@ from app.models import (
|
|||||||
FormationModalite,
|
FormationModalite,
|
||||||
Identite,
|
Identite,
|
||||||
Matiere,
|
Matiere,
|
||||||
|
Module,
|
||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
)
|
)
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_formsemestre_validation
|
from app.scodoc import sco_formsemestre_validation
|
||||||
from app.scodoc import sco_moduleimpl
|
|
||||||
from app.scodoc import sco_saisie_notes
|
from app.scodoc import sco_saisie_notes
|
||||||
from app.scodoc import sco_synchro_etuds
|
from app.scodoc import sco_synchro_etuds
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
@ -228,11 +228,8 @@ class ScoFake(object):
|
|||||||
matiere = db.session.get(Matiere, matiere_id)
|
matiere = db.session.get(Matiere, matiere_id)
|
||||||
ue_id = matiere.ue.id
|
ue_id = matiere.ue.id
|
||||||
formation_id = matiere.ue.formation.id
|
formation_id = matiere.ue.formation.id
|
||||||
oid = edit_module.do_module_create(locals())
|
module = Module.create_from_dict(locals(), news=True, inval_cache=True)
|
||||||
oids = edit_module.module_list(args={"module_id": oid})
|
return module.id
|
||||||
if not oids:
|
|
||||||
raise ScoValueError(f"module not created ! (oid={oid})")
|
|
||||||
return oid
|
|
||||||
|
|
||||||
@logging_meth
|
@logging_meth
|
||||||
def create_formsemestre(
|
def create_formsemestre(
|
||||||
@ -276,11 +273,8 @@ class ScoFake(object):
|
|||||||
) -> int:
|
) -> int:
|
||||||
if not responsable_id:
|
if not responsable_id:
|
||||||
responsable_id = self.default_user.id
|
responsable_id = self.default_user.id
|
||||||
oid = sco_moduleimpl.do_moduleimpl_create(locals())
|
modimpl = ModuleImpl.create_from_dict(locals())
|
||||||
oids = sco_moduleimpl.moduleimpl_list(moduleimpl_id=oid) # API inconsistency
|
return modimpl.id
|
||||||
if not oids:
|
|
||||||
raise ScoValueError("moduleimpl not created !")
|
|
||||||
return oid
|
|
||||||
|
|
||||||
@logging_meth
|
@logging_meth
|
||||||
def inscrit_etudiant(self, formsemestre_id: int, etud: dict):
|
def inscrit_etudiant(self, formsemestre_id: int, etud: dict):
|
||||||
|
@ -34,8 +34,7 @@
|
|||||||
# - moduleimpl_list
|
# - moduleimpl_list
|
||||||
# - do_module_impl_with_module_list
|
# - do_module_impl_with_module_list
|
||||||
# - do_formsemestre_delete
|
# - do_formsemestre_delete
|
||||||
# - module_list
|
# - Module.delete
|
||||||
# - do_module_delete
|
|
||||||
# - ue_list
|
# - ue_list
|
||||||
# - do_ue_delete
|
# - do_ue_delete
|
||||||
# - do_formation_delete
|
# - do_formation_delete
|
||||||
@ -52,7 +51,7 @@ from app.formations import (
|
|||||||
edit_ue,
|
edit_ue,
|
||||||
formation_io,
|
formation_io,
|
||||||
)
|
)
|
||||||
from app.models import Formation, Matiere, ModuleImpl
|
from app.models import Formation, Matiere, Module, 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
|
||||||
@ -259,7 +258,7 @@ def test_formations(test_client):
|
|||||||
formsemestre_id=sem2["formsemestre_id"]
|
formsemestre_id=sem2["formsemestre_id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
li_module = edit_module.module_list()
|
li_module = Module.query.all()
|
||||||
assert len(li_module) == 4
|
assert len(li_module) == 4
|
||||||
# Suppression impossible car utilisé dans le semestre formsemestre_idt:
|
# Suppression impossible car utilisé dans le semestre formsemestre_idt:
|
||||||
module3 = db.session.get(ModuleImpl, mi3).module
|
module3 = db.session.get(ModuleImpl, mi3).module
|
||||||
@ -268,13 +267,14 @@ def test_formations(test_client):
|
|||||||
|
|
||||||
sco_formsemestre_edit.do_formsemestre_delete(formsemestre_idt)
|
sco_formsemestre_edit.do_formsemestre_delete(formsemestre_idt)
|
||||||
|
|
||||||
li_module2_before = edit_module.module_list()
|
li_module2_before = Module.query.all()
|
||||||
|
|
||||||
edit_module.do_module_delete(module3.id)
|
module3.delete()
|
||||||
edit_module.do_module_delete(module_id_t)
|
module_t = db.session.get(Module, module_id_t)
|
||||||
|
module_t.delete()
|
||||||
|
|
||||||
# deuxieme methode de supression d'un module
|
# deuxieme methode de supression d'un module
|
||||||
li_module2_after = edit_module.module_list()
|
li_module2_after = Module.query.all()
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
len(li_module2_after) == len(li_module2_before) - 2
|
len(li_module2_after) == len(li_module2_before) - 2
|
||||||
@ -340,14 +340,14 @@ def test_import_formation(test_client, filename="formation-exemple-1.xml"):
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
# et les modules
|
# et les modules
|
||||||
modules = edit_module.module_list({"formation_id": formation_id})
|
formation = Formation.get_formation(formation_id)
|
||||||
for mod in modules:
|
for mod in formation.modules:
|
||||||
moduleimpl_id = G.create_moduleimpl(
|
moduleimpl_id = G.create_moduleimpl(
|
||||||
module_id=mod["module_id"],
|
module_id=mod.id,
|
||||||
formsemestre_id=formsemestre_ids[mod["semestre_id"] - 1],
|
formsemestre_id=formsemestre_ids[mod.semestre_id - 1],
|
||||||
)
|
)
|
||||||
mi = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
|
mi = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
|
||||||
assert mi["module_id"] == mod["module_id"]
|
assert mi["module_id"] == mod.id
|
||||||
|
|
||||||
# --- Export formation en XML
|
# --- Export formation en XML
|
||||||
doc1 = formation_io.formation_export(formation_id, fmt="xml").get_data(as_text=True)
|
doc1 = formation_io.formation_export(formation_id, fmt="xml").get_data(as_text=True)
|
||||||
|
Loading…
Reference in New Issue
Block a user