forked from ScoDoc/ScoDoc
WIP: BUT association modules <-> parcours
This commit is contained in:
parent
5d7085b858
commit
72dc72d286
@ -1,14 +1,25 @@
|
|||||||
# -*- coding: UTF-8 -*
|
# -*- coding: UTF-8 -*
|
||||||
|
|
||||||
"""Modèles base de données ScoDoc
|
"""Modèles base de données ScoDoc
|
||||||
XXX version préliminaire ScoDoc8 #sco8 sans département
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
CODE_STR_LEN = 16 # chaine pour les codes
|
CODE_STR_LEN = 16 # chaine pour les codes
|
||||||
SHORT_STR_LEN = 32 # courtes chaine, eg acronymes
|
SHORT_STR_LEN = 32 # courtes chaine, eg acronymes
|
||||||
APO_CODE_STR_LEN = 512 # nb de car max d'un code Apogée (il peut y en avoir plusieurs)
|
APO_CODE_STR_LEN = 512 # nb de car max d'un code Apogée (il peut y en avoir plusieurs)
|
||||||
GROUPNAME_STR_LEN = 64
|
GROUPNAME_STR_LEN = 64
|
||||||
|
|
||||||
|
convention = {
|
||||||
|
"ix": "ix_%(column_0_label)s",
|
||||||
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||||
|
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||||
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||||
|
"pk": "pk_%(table_name)s",
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata_obj = sqlalchemy.MetaData(naming_convention=convention)
|
||||||
|
|
||||||
from app.models.raw_sql_init import create_database_functions
|
from app.models.raw_sql_init import create_database_functions
|
||||||
|
|
||||||
from app.models.absences import Absence, AbsenceNotification, BilletAbsence
|
from app.models.absences import Absence, AbsenceNotification, BilletAbsence
|
||||||
|
@ -321,6 +321,21 @@ ApcAppCritiqueModules = db.Table(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
parcours_modules = db.Table(
|
||||||
|
"parcours_modules",
|
||||||
|
db.Column(
|
||||||
|
"parcours_id", db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True
|
||||||
|
),
|
||||||
|
db.Column(
|
||||||
|
"module_id",
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_modules.id", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
"""Association parcours <-> modules (many-to-many)"""
|
||||||
|
|
||||||
|
|
||||||
class ApcParcours(db.Model, XMLModel):
|
class ApcParcours(db.Model, XMLModel):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
referentiel_id = db.Column(
|
referentiel_id = db.Column(
|
||||||
@ -335,6 +350,11 @@ class ApcParcours(db.Model, XMLModel):
|
|||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
)
|
)
|
||||||
|
# modules = db.relationship(
|
||||||
|
# "Module",
|
||||||
|
# secondary=parcours_modules,
|
||||||
|
# back_populates="parcours",
|
||||||
|
# )
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__} {self.code}>"
|
return f"<{self.__class__.__name__} {self.code}>"
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
|
from app.models.but_refcomp import parcours_modules
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT
|
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
@ -44,6 +45,14 @@ class Module(db.Model):
|
|||||||
lazy=True,
|
lazy=True,
|
||||||
backref=db.backref("modules", lazy=True),
|
backref=db.backref("modules", lazy=True),
|
||||||
)
|
)
|
||||||
|
# BUT
|
||||||
|
parcours = db.relationship(
|
||||||
|
"ApcParcours",
|
||||||
|
secondary=parcours_modules,
|
||||||
|
lazy="subquery",
|
||||||
|
# cascade="all, delete",
|
||||||
|
backref=db.backref("modules", lazy=True),
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
self.ue_coefs = []
|
self.ue_coefs = []
|
||||||
|
@ -134,7 +134,10 @@ def html_edit_formation_apc(
|
|||||||
tag_editable=tag_editable,
|
tag_editable=tag_editable,
|
||||||
icons=icons,
|
icons=icons,
|
||||||
scu=scu,
|
scu=scu,
|
||||||
),
|
semestre_id=semestre_idx,
|
||||||
|
)
|
||||||
|
if ues_by_sem[semestre_idx].count() > 0
|
||||||
|
else "",
|
||||||
render_template(
|
render_template(
|
||||||
"pn/form_mods.html",
|
"pn/form_mods.html",
|
||||||
formation=formation,
|
formation=formation,
|
||||||
@ -147,7 +150,10 @@ def html_edit_formation_apc(
|
|||||||
tag_editable=tag_editable,
|
tag_editable=tag_editable,
|
||||||
icons=icons,
|
icons=icons,
|
||||||
scu=scu,
|
scu=scu,
|
||||||
),
|
semestre_id=semestre_idx,
|
||||||
|
)
|
||||||
|
if ues_by_sem[semestre_idx].count() > 0
|
||||||
|
else "",
|
||||||
render_template(
|
render_template(
|
||||||
"pn/form_mods.html",
|
"pn/form_mods.html",
|
||||||
formation=formation,
|
formation=formation,
|
||||||
@ -159,7 +165,10 @@ def html_edit_formation_apc(
|
|||||||
tag_editable=tag_editable,
|
tag_editable=tag_editable,
|
||||||
icons=icons,
|
icons=icons,
|
||||||
scu=scu,
|
scu=scu,
|
||||||
),
|
semestre_id=semestre_idx,
|
||||||
|
)
|
||||||
|
if ues_by_sem[semestre_idx].count() > 0
|
||||||
|
else """<span class="fontred">créer une UE pour pouvoir ajouter des modules</span>""",
|
||||||
]
|
]
|
||||||
|
|
||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
@ -33,12 +33,13 @@ from flask import 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 log
|
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 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 import ScolarNews
|
||||||
|
from app.models.but_refcomp import ApcParcours
|
||||||
|
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
@ -121,6 +122,13 @@ def module_create(
|
|||||||
Sinon, donne le choix de l'UE de rattachement et utilise la premiè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).
|
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,
|
||||||
|
)
|
||||||
if matiere_id:
|
if matiere_id:
|
||||||
matiere = Matiere.query.get_or_404(matiere_id)
|
matiere = Matiere.query.get_or_404(matiere_id)
|
||||||
ue = matiere.ue
|
ue = matiere.ue
|
||||||
@ -472,30 +480,56 @@ def check_module_code_unicity(code, field, formation_id, module_id=None):
|
|||||||
return len(Mods) == 0
|
return len(Mods) == 0
|
||||||
|
|
||||||
|
|
||||||
def module_edit(module_id=None):
|
def module_edit(
|
||||||
"""Edit a module"""
|
module_id=None,
|
||||||
from app.scodoc import sco_formations
|
create=False,
|
||||||
|
matiere_id=None,
|
||||||
|
module_type=None,
|
||||||
|
semestre_id=None,
|
||||||
|
formation_id=None,
|
||||||
|
):
|
||||||
|
"""Formulaire édition ou création module.
|
||||||
|
Si create, création nouveau 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).
|
||||||
|
"""
|
||||||
from app.scodoc import sco_tag_module
|
from app.scodoc import sco_tag_module
|
||||||
|
|
||||||
if not module_id:
|
# --- Détermination de la formation
|
||||||
raise ScoValueError("invalid module !")
|
orig_semestre_idx = None
|
||||||
modules = module_list(args={"module_id": module_id})
|
if create:
|
||||||
if not modules:
|
if matiere_id:
|
||||||
raise ScoValueError("invalid module !")
|
matiere = Matiere.query.get_or_404(matiere_id)
|
||||||
module = modules[0]
|
ue = matiere.ue
|
||||||
a_module = models.Module.query.get(module_id)
|
formation = ue.formation
|
||||||
unlocked = not module_is_locked(module_id)
|
orig_semestre_idx = ue.semestre_idx if semestre_id is None else semestre_id
|
||||||
formation_id = module["formation_id"]
|
else:
|
||||||
formation = sco_formations.formation_list(args={"formation_id": formation_id})[0]
|
formation = Formation.query.get_or_404(formation_id)
|
||||||
parcours = sco_codes_parcours.get_parcours_from_code(formation["type_parcours"])
|
module = None
|
||||||
|
unlocked = True
|
||||||
|
else:
|
||||||
|
if not module_id:
|
||||||
|
raise ValueError("missing module_id !")
|
||||||
|
module = models.Module.query.get_or_404(module_id)
|
||||||
|
module_dict = module.to_dict()
|
||||||
|
formation = module.formation
|
||||||
|
unlocked = not module_is_locked(module_id)
|
||||||
|
|
||||||
|
parcours = sco_codes_parcours.get_parcours_from_code(formation.type_parcours)
|
||||||
is_apc = parcours.APC_SAE # BUT
|
is_apc = parcours.APC_SAE # BUT
|
||||||
in_use = len(a_module.modimpls.all()) > 0 # il y a des modimpls
|
if not create:
|
||||||
|
orig_semestre_idx = module.ue.semestre_idx if is_apc else module.semestre_id
|
||||||
|
if orig_semestre_idx is None:
|
||||||
|
orig_semestre_idx = 1
|
||||||
|
# il y a-t-il des modimpls ?
|
||||||
|
in_use = (module is not None) and (len(module.modimpls.all()) > 0)
|
||||||
matieres = Matiere.query.filter(
|
matieres = Matiere.query.filter(
|
||||||
Matiere.ue_id == UniteEns.id, UniteEns.formation_id == formation_id
|
Matiere.ue_id == UniteEns.id, UniteEns.formation_id == formation.id
|
||||||
).order_by(UniteEns.semestre_idx, UniteEns.numero, Matiere.numero)
|
).order_by(UniteEns.semestre_idx, UniteEns.numero, Matiere.numero)
|
||||||
if in_use:
|
if in_use:
|
||||||
# restreint aux matières du même semestre
|
# restreint aux matières du même semestre
|
||||||
matieres = matieres.filter(UniteEns.semestre_idx == a_module.ue.semestre_idx)
|
matieres = matieres.filter(UniteEns.semestre_idx == module.ue.semestre_idx)
|
||||||
|
|
||||||
if is_apc:
|
if is_apc:
|
||||||
# ne conserve que la 1ere matière de chaque UE,
|
# ne conserve que la 1ere matière de chaque UE,
|
||||||
@ -503,7 +537,8 @@ def module_edit(module_id=None):
|
|||||||
matieres = [
|
matieres = [
|
||||||
mat
|
mat
|
||||||
for mat in matieres
|
for mat in matieres
|
||||||
if a_module.matiere.id == mat.id or mat.id == mat.ue.matieres.first().id
|
if ((module is not None) and (module.matiere.id == mat.id))
|
||||||
|
or (mat.id == mat.ue.matieres.first().id)
|
||||||
]
|
]
|
||||||
mat_names = [
|
mat_names = [
|
||||||
"S%s / %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
|
||||||
@ -511,14 +546,43 @@ def module_edit(module_id=None):
|
|||||||
else:
|
else:
|
||||||
mat_names = ["%s / %s" % (mat.ue.acronyme, mat.titre or "") for mat in matieres]
|
mat_names = ["%s / %s" % (mat.ue.acronyme, mat.titre or "") for mat in matieres]
|
||||||
|
|
||||||
ue_mat_ids = ["%s!%s" % (mat.ue.id, mat.id) for mat in matieres]
|
if module: # edition
|
||||||
module["ue_matiere_id"] = "%s!%s" % (module["ue_id"], module["matiere_id"])
|
ue_mat_ids = ["%s!%s" % (mat.ue.id, mat.id) for mat in matieres]
|
||||||
|
module_dict["ue_matiere_id"] = "%s!%s" % (
|
||||||
|
module_dict["ue_id"],
|
||||||
|
module_dict["matiere_id"],
|
||||||
|
)
|
||||||
|
|
||||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||||
|
# Toutes les UE de la formation (tout parcours):
|
||||||
|
ues = formation.ues.order_by(
|
||||||
|
UniteEns.semestre_idx, UniteEns.numero, UniteEns.acronyme
|
||||||
|
).all()
|
||||||
|
|
||||||
|
# --- Titre de la page
|
||||||
|
if create:
|
||||||
|
if is_apc and module_type is not None:
|
||||||
|
object_name = scu.MODULE_TYPE_NAMES[module_type]
|
||||||
|
else:
|
||||||
|
object_name = "Module"
|
||||||
|
page_title = f"Création {object_name}"
|
||||||
|
if matiere_id:
|
||||||
|
title = f"""Création {object_name} dans la matière
|
||||||
|
{matiere.titre},
|
||||||
|
(UE {ue.acronyme}), semestre {ue.semestre_idx}
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
title = f"""Création {object_name} dans la formation
|
||||||
|
{formation.acronyme}"""
|
||||||
|
else:
|
||||||
|
page_title = "Modification du module {module.code or module.titre or ''}"
|
||||||
|
title = f"""Modification du module {module.code or ''} {module.titre or ''}
|
||||||
|
(formation {formation.acronyme}, version {formation.version})
|
||||||
|
"""
|
||||||
|
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
page_title=f"Modification du module {a_module.code or a_module.titre or ''}",
|
page_title=page_title,
|
||||||
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
|
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
|
||||||
javascripts=[
|
javascripts=[
|
||||||
"libjs/jQuery-tagEditor/jquery.tag-editor.min.js",
|
"libjs/jQuery-tagEditor/jquery.tag-editor.min.js",
|
||||||
@ -526,17 +590,19 @@ def module_edit(module_id=None):
|
|||||||
"js/module_tag_editor.js",
|
"js/module_tag_editor.js",
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
f"""<h2>Modification du module {a_module.code or ''} {a_module.titre or ''}""",
|
f"""<h2>{title}</h2>""",
|
||||||
""" (formation %(acronyme)s, version %(version)s)</h2>""" % formation,
|
|
||||||
render_template(
|
render_template(
|
||||||
"scodoc/help/modules.html",
|
"scodoc/help/modules.html",
|
||||||
is_apc=is_apc,
|
is_apc=is_apc,
|
||||||
|
semestre_id=semestre_id,
|
||||||
formsemestres=FormSemestre.query.filter(
|
formsemestres=FormSemestre.query.filter(
|
||||||
ModuleImpl.formsemestre_id == FormSemestre.id,
|
ModuleImpl.formsemestre_id == FormSemestre.id,
|
||||||
ModuleImpl.module_id == module_id,
|
ModuleImpl.module_id == module_id,
|
||||||
)
|
)
|
||||||
.order_by(FormSemestre.date_debut)
|
.order_by(FormSemestre.date_debut)
|
||||||
.all(),
|
.all()
|
||||||
|
if not create
|
||||||
|
else None,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
if not unlocked:
|
if not unlocked:
|
||||||
@ -547,28 +613,55 @@ def module_edit(module_id=None):
|
|||||||
module_types = scu.ModuleType # tous les types
|
module_types = scu.ModuleType # tous les types
|
||||||
else:
|
else:
|
||||||
# ne propose pas SAE et Ressources, sauf si déjà de ce type...
|
# ne propose pas SAE et Ressources, sauf si déjà de ce type...
|
||||||
module_types = (
|
module_types = set(scu.ModuleType) - {
|
||||||
set(scu.ModuleType) - {scu.ModuleType.RESSOURCE, scu.ModuleType.SAE}
|
scu.ModuleType.RESSOURCE,
|
||||||
) | {
|
scu.ModuleType.SAE,
|
||||||
scu.ModuleType(a_module.module_type)
|
|
||||||
if a_module.module_type
|
|
||||||
else scu.ModuleType.STANDARD
|
|
||||||
}
|
}
|
||||||
|
if module:
|
||||||
|
module_types |= {
|
||||||
|
scu.ModuleType(module.module_type)
|
||||||
|
if module.module_type
|
||||||
|
else scu.ModuleType.STANDARD
|
||||||
|
}
|
||||||
|
# Numéro du module
|
||||||
|
# cherche le numero adéquat (pour placer le module en fin de liste)
|
||||||
|
if module:
|
||||||
|
default_num = module.numero
|
||||||
|
else:
|
||||||
|
modules = formation.modules.all()
|
||||||
|
if modules:
|
||||||
|
default_num = max([m.numero or 0 for m in modules]) + 10
|
||||||
|
else:
|
||||||
|
default_num = 10
|
||||||
|
|
||||||
descr = [
|
descr = [
|
||||||
(
|
(
|
||||||
"code",
|
"code",
|
||||||
{
|
{
|
||||||
"size": 10,
|
"size": 10,
|
||||||
"explanation": "code du module (issu du programme, exemple M1203 ou R2.01. Doit être unique dans la formation)",
|
"explanation": "code du module (issu du programme, exemple M1203, 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, field, formation_id=formation.id: check_module_code_unicity(
|
||||||
val, field, formation_id, module_id=module_id
|
val, field, formation_id, module_id=module.id if module else None
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("titre", {"size": 30, "explanation": "nom du module"}),
|
(
|
||||||
("abbrev", {"size": 20, "explanation": "nom abrégé (pour bulletins)"}),
|
"titre",
|
||||||
|
{
|
||||||
|
"size": 30,
|
||||||
|
"explanation": """nom du module. Exemple:
|
||||||
|
<em>Introduction à la démarche ergonomique</em>""",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"abbrev",
|
||||||
|
{
|
||||||
|
"size": 20,
|
||||||
|
"explanation": """nom abrégé (pour bulletins).
|
||||||
|
Exemple: <em>Intro. à l'ergonomie</em>""",
|
||||||
|
},
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"module_type",
|
"module_type",
|
||||||
{
|
{
|
||||||
@ -583,50 +676,63 @@ def module_edit(module_id=None):
|
|||||||
(
|
(
|
||||||
"heures_cours",
|
"heures_cours",
|
||||||
{
|
{
|
||||||
"title": "Heures CM :",
|
"title": "Heures cours :",
|
||||||
"size": 4,
|
"size": 4,
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"explanation": "nombre d'heures de cours",
|
"explanation": "nombre d'heures de cours (optionnel)",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"heures_td",
|
"heures_td",
|
||||||
{
|
{
|
||||||
"title": "Heures TD :",
|
"title": "Heures de TD :",
|
||||||
"size": 4,
|
"size": 4,
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"explanation": "nombre d'heures de Travaux Dirigés",
|
"explanation": "nombre d'heures de Travaux Dirigés (optionnel)",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"heures_tp",
|
"heures_tp",
|
||||||
{
|
{
|
||||||
"title": "Heures TP :",
|
"title": "Heures de TP :",
|
||||||
"size": 4,
|
"size": 4,
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"explanation": "nombre d'heures de Travaux Pratiques",
|
"explanation": "nombre d'heures de Travaux Pratiques (optionnel)",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
if is_apc:
|
if is_apc:
|
||||||
coefs_lst = a_module.ue_coefs_list()
|
if module:
|
||||||
if coefs_lst:
|
coefs_lst = module.ue_coefs_list()
|
||||||
coefs_descr_txt = ", ".join(
|
if coefs_lst:
|
||||||
[f"{ue.acronyme}: {c}" for (ue, c) in coefs_lst]
|
coefs_descr_txt = ", ".join(
|
||||||
)
|
[f"{ue.acronyme}: {c}" for (ue, c) in coefs_lst]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
coefs_descr_txt = """<span class="missing_value">non définis</span>"""
|
||||||
|
descr += [
|
||||||
|
(
|
||||||
|
"ue_coefs",
|
||||||
|
{
|
||||||
|
"readonly": True,
|
||||||
|
"title": "Coefficients vers les UE ",
|
||||||
|
"default": coefs_descr_txt,
|
||||||
|
"explanation": " <br>(passer par la page d'édition de la formation pour modifier les coefficients)",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
else:
|
else:
|
||||||
coefs_descr_txt = """<span class="missing_value">non définis</span>"""
|
descr += [
|
||||||
descr += [
|
(
|
||||||
(
|
"sep_ue_coefs",
|
||||||
"ue_coefs",
|
{
|
||||||
{
|
"input_type": "separator",
|
||||||
"readonly": True,
|
"title": """
|
||||||
"title": "Coefficients vers les UE ",
|
<div>(<em>les coefficients vers les UE se fixent sur la page dédiée</em>)
|
||||||
"default": coefs_descr_txt,
|
</div>""",
|
||||||
"explanation": " <br>(passer par la page d'édition de la formation pour modifier les coefficients)",
|
},
|
||||||
},
|
),
|
||||||
)
|
]
|
||||||
]
|
|
||||||
else: # Module classique avec coef scalaire:
|
else: # Module classique avec coef scalaire:
|
||||||
descr += [
|
descr += [
|
||||||
(
|
(
|
||||||
@ -641,30 +747,64 @@ def module_edit(module_id=None):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
descr += [
|
descr += [
|
||||||
("formation_id", {"input_type": "hidden"}),
|
|
||||||
("ue_id", {"input_type": "hidden"}),
|
|
||||||
("module_id", {"input_type": "hidden"}),
|
|
||||||
(
|
(
|
||||||
"ue_matiere_id",
|
"formation_id",
|
||||||
{
|
{
|
||||||
"input_type": "menu",
|
"input_type": "hidden",
|
||||||
"title": "Rattachement :" if is_apc else "Matière :",
|
"default": formation.id,
|
||||||
"explanation": (
|
|
||||||
"UE de rattachement"
|
|
||||||
+ (
|
|
||||||
" module utilisé, ne peut pas être changé de semestre"
|
|
||||||
if in_use
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if is_apc
|
|
||||||
else "un module appartient à une seule matière.",
|
|
||||||
"labels": mat_names,
|
|
||||||
"allowed_values": ue_mat_ids,
|
|
||||||
"enabled": unlocked,
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
if module:
|
||||||
|
descr += [
|
||||||
|
("ue_id", {"input_type": "hidden"}),
|
||||||
|
("module_id", {"input_type": "hidden"}),
|
||||||
|
(
|
||||||
|
"ue_matiere_id",
|
||||||
|
{
|
||||||
|
"input_type": "menu",
|
||||||
|
"title": "Rattachement :" if is_apc else "Matière :",
|
||||||
|
"explanation": (
|
||||||
|
"UE de rattachement, utilisée notamment pour les malus"
|
||||||
|
+ (
|
||||||
|
" (module utilisé, ne peut pas être changé de semestre)"
|
||||||
|
if in_use
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if is_apc
|
||||||
|
else "un module appartient à une seule matière.",
|
||||||
|
"labels": mat_names,
|
||||||
|
"allowed_values": ue_mat_ids,
|
||||||
|
"enabled": unlocked,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
else: # Création
|
||||||
|
if matiere_id:
|
||||||
|
descr += [
|
||||||
|
("ue_id", {"default": ue.id, "input_type": "hidden"}),
|
||||||
|
("matiere_id", {"default": matiere_id, "input_type": "hidden"}),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# choix de l'UE de rattachement
|
||||||
|
descr += [
|
||||||
|
(
|
||||||
|
"ue_id",
|
||||||
|
{
|
||||||
|
"input_type": "menu",
|
||||||
|
"type": "int",
|
||||||
|
"title": "UE de rattachement",
|
||||||
|
"explanation": "utilisée notamment pour les malus",
|
||||||
|
"labels": [
|
||||||
|
f"S{u.semestre_idx if u.semestre_idx is not None else '.'} / {u.acronyme} {u.titre}"
|
||||||
|
for u in ues
|
||||||
|
],
|
||||||
|
"allowed_values": [u.id for u in ues],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
if is_apc:
|
if is_apc:
|
||||||
# le semestre du module est toujours celui de son UE
|
# le semestre du module est toujours celui de son UE
|
||||||
descr += [
|
descr += [
|
||||||
@ -710,17 +850,56 @@ def module_edit(module_id=None):
|
|||||||
"numero",
|
"numero",
|
||||||
{
|
{
|
||||||
"size": 2,
|
"size": 2,
|
||||||
"explanation": "numéro (1,2,3,4...) pour ordre d'affichage",
|
"explanation": "numéro (1, 2, 3, 4, ...) pour ordre d'affichage",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
|
"default": default_num,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
# Choix des parcours
|
||||||
|
if is_apc:
|
||||||
|
ref_comp = formation.referentiel_competence
|
||||||
|
if ref_comp:
|
||||||
|
descr += [
|
||||||
|
(
|
||||||
|
"parcours",
|
||||||
|
{
|
||||||
|
"input_type": "checkbox",
|
||||||
|
"vertical": True,
|
||||||
|
"labels": [parcour.libelle for parcour in ref_comp.parcours],
|
||||||
|
"allowed_values": [
|
||||||
|
str(parcour.id) for parcour in ref_comp.parcours
|
||||||
|
],
|
||||||
|
"explanation": "parcours dans lesquels est utilisé ce module.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if module:
|
||||||
|
module_dict["parcours"] = [
|
||||||
|
str(parcour.id) for parcour in module.parcours
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
descr += [
|
||||||
|
(
|
||||||
|
"parcours",
|
||||||
|
{
|
||||||
|
"input_type": "separator",
|
||||||
|
"title": f"""<span class="fontred">Pas de parcours:
|
||||||
|
<a class="stdlink" href="{ url_for('notes.refcomp_assoc_formation',
|
||||||
|
scodoc_dept=g.scodoc_dept, formation_id=formation.id)
|
||||||
|
}">associer un référentiel de compétence</a>
|
||||||
|
</span>""",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
# force module semestre_idx to its UE
|
# force module semestre_idx to its UE
|
||||||
if a_module.ue.semestre_idx:
|
if module:
|
||||||
module["semestre_id"] = a_module.ue.semestre_idx
|
if module.ue.semestre_idx is None:
|
||||||
# Filet de sécurité si jamais l'UE n'a pas non plus de semestre:
|
# Filet de sécurité si jamais l'UE n'a pas non plus de semestre:
|
||||||
if not module["semestre_id"]:
|
module_dict["semestre_id"] = 1
|
||||||
module["semestre_id"] = 1
|
else:
|
||||||
|
module_dict["semestre_id"] = module.ue.semestre_idx
|
||||||
|
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
request.base_url,
|
request.base_url,
|
||||||
scu.get_request_args(),
|
scu.get_request_args(),
|
||||||
@ -728,8 +907,9 @@ def module_edit(module_id=None):
|
|||||||
html_foot_markup="""<div style="width: 90%;"><span class="sco_tag_edit"><textarea data-module_id="{}" class="module_tag_editor">{}</textarea></span></div>""".format(
|
html_foot_markup="""<div style="width: 90%;"><span class="sco_tag_edit"><textarea data-module_id="{}" class="module_tag_editor">{}</textarea></span></div>""".format(
|
||||||
module_id, ",".join(sco_tag_module.module_tag_list(module_id))
|
module_id, ",".join(sco_tag_module.module_tag_list(module_id))
|
||||||
),
|
),
|
||||||
initvalues=module,
|
initvalues=module_dict if module else {},
|
||||||
submitlabel="Modifier ce module",
|
submitlabel="Modifier ce module" if module else "Créer ce module",
|
||||||
|
cancelbutton="Annuler",
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
if tf[0] == 0:
|
if tf[0] == 0:
|
||||||
@ -739,38 +919,66 @@ def module_edit(module_id=None):
|
|||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=formation_id,
|
formation_id=formation.id,
|
||||||
semestre_idx=module["semestre_id"],
|
semestre_idx=orig_semestre_idx,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# l'UE de rattachement peut changer
|
if create:
|
||||||
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
if not matiere_id:
|
||||||
x, y = tf[2]["ue_matiere_id"].split("!")
|
# formulaire avec choix UE de rattachement
|
||||||
tf[2]["ue_id"] = int(x)
|
ue = UniteEns.query.get(tf[2]["ue_id"])
|
||||||
tf[2]["matiere_id"] = int(y)
|
if ue is None:
|
||||||
old_ue_id = a_module.ue.id
|
raise ValueError("UE invalide")
|
||||||
new_ue_id = tf[2]["ue_id"]
|
matiere = ue.matieres.first()
|
||||||
if (old_ue_id != new_ue_id) and in_use:
|
if matiere:
|
||||||
new_ue = UniteEns.query.get_or_404(new_ue_id)
|
tf[2]["matiere_id"] = matiere.id
|
||||||
if new_ue.semestre_idx != a_module.ue.semestre_idx:
|
else:
|
||||||
# pas changer de semestre un module utilisé !
|
matiere_id = sco_edit_matiere.do_matiere_create(
|
||||||
raise ScoValueError(
|
{"ue_id": ue.id, "titre": ue.titre, "numero": 1},
|
||||||
"Module utilisé: il ne peut pas être changé de semestre !"
|
)
|
||||||
)
|
tf[2]["matiere_id"] = matiere_id
|
||||||
# En APC, force le semestre égal à celui de l'UE
|
|
||||||
if is_apc:
|
tf[2]["semestre_id"] = ue.semestre_idx
|
||||||
selected_ue = UniteEns.query.get(tf[2]["ue_id"])
|
module_id = do_module_create(tf[2])
|
||||||
if selected_ue is None:
|
module = Module.query.get(module_id)
|
||||||
raise ValueError("UE invalide")
|
else: # EDITION MODULE
|
||||||
tf[2]["semestre_id"] = selected_ue.semestre_idx
|
# l'UE de rattachement peut changer
|
||||||
# Check unicité code module dans la formation
|
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
||||||
do_module_edit(tf[2])
|
x, y = tf[2]["ue_matiere_id"].split("!")
|
||||||
|
tf[2]["ue_id"] = int(x)
|
||||||
|
tf[2]["matiere_id"] = int(y)
|
||||||
|
old_ue_id = module.ue.id
|
||||||
|
new_ue_id = tf[2]["ue_id"]
|
||||||
|
if (old_ue_id != new_ue_id) and in_use:
|
||||||
|
new_ue = UniteEns.query.get_or_404(new_ue_id)
|
||||||
|
if new_ue.semestre_idx != module.ue.semestre_idx:
|
||||||
|
# pas changer de semestre un module utilisé !
|
||||||
|
raise ScoValueError(
|
||||||
|
"Module utilisé: il ne peut pas être changé de semestre !"
|
||||||
|
)
|
||||||
|
# En APC, force le semestre égal à celui de l'UE
|
||||||
|
if is_apc:
|
||||||
|
selected_ue = UniteEns.query.get(tf[2]["ue_id"])
|
||||||
|
if selected_ue is None:
|
||||||
|
raise ValueError("UE invalide")
|
||||||
|
tf[2]["semestre_id"] = selected_ue.semestre_idx
|
||||||
|
# Check unicité code module dans la formation
|
||||||
|
# ??? TODO
|
||||||
|
#
|
||||||
|
do_module_edit(tf[2])
|
||||||
|
# Modifie les parcours
|
||||||
|
module.parcours = [
|
||||||
|
ApcParcours.query.get(int(parcour_id_str))
|
||||||
|
for parcour_id_str in tf[2]["parcours"]
|
||||||
|
]
|
||||||
|
db.session.add(module)
|
||||||
|
db.session.commit()
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"notes.ue_table",
|
"notes.ue_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=formation_id,
|
formation_id=formation.id,
|
||||||
semestre_idx=tf[2]["semestre_id"],
|
semestre_idx=tf[2]["semestre_id"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -255,7 +255,9 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
title = f"Modification de l'UE {ue.acronyme} {ue.titre}"
|
title = f"Modification de l'UE {ue.acronyme} {ue.titre}"
|
||||||
initvalues = ue_dict
|
initvalues = ue_dict
|
||||||
submitlabel = "Modifier les valeurs"
|
submitlabel = "Modifier les valeurs"
|
||||||
can_change_semestre_id = (ue.modules.count() == 0) or (ue.semestre_idx is None)
|
can_change_semestre_id = (
|
||||||
|
(ue.modules.count() == 0) or (ue.semestre_idx is None)
|
||||||
|
) and ue.niveau_competence is None
|
||||||
else:
|
else:
|
||||||
ue = None
|
ue = None
|
||||||
title = "Création d'une UE"
|
title = "Création d'une UE"
|
||||||
@ -287,7 +289,7 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
f"""
|
f"""
|
||||||
<h4>UE du semestre S{ue.semestre_idx}</h4>
|
<h4>UE du semestre S{ue.semestre_idx}</h4>
|
||||||
"""
|
"""
|
||||||
if is_apc
|
if is_apc and ue
|
||||||
else "",
|
else "",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1015,9 +1017,6 @@ def _ue_table_ues(
|
|||||||
}">transformer en UE ordinaire</a> """
|
}">transformer en UE ordinaire</a> """
|
||||||
)
|
)
|
||||||
H.append("</span>")
|
H.append("</span>")
|
||||||
breakpoint()
|
|
||||||
if ue.niveau_competence is None:
|
|
||||||
H.append(" pas de compétence associée ")
|
|
||||||
ue_editable = editable and not ue_is_locked(ue["ue_id"])
|
ue_editable = editable and not ue_is_locked(ue["ue_id"])
|
||||||
if ue_editable:
|
if ue_editable:
|
||||||
H.append(
|
H.append(
|
||||||
|
@ -84,13 +84,15 @@
|
|||||||
url_for("notes.module_create",
|
url_for("notes.module_create",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
module_type=module_type|int,
|
module_type=module_type|int,
|
||||||
matiere_id=matiere_parent.id
|
matiere_id=matiere_parent.id,
|
||||||
|
semestre_id=semestre_id,
|
||||||
)}}"
|
)}}"
|
||||||
{% else %}"{{
|
{% else %}"{{
|
||||||
url_for("notes.module_create",
|
url_for("notes.module_create",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
module_type=module_type|int,
|
module_type=module_type|int,
|
||||||
formation_id=formation.id
|
formation_id=formation.id,
|
||||||
|
semestre_id=semestre_id,
|
||||||
)}}"
|
)}}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
>{{create_element_msg}}</a>
|
>{{create_element_msg}}</a>
|
||||||
|
@ -22,13 +22,39 @@ def upgrade():
|
|||||||
"notes_ue", sa.Column("niveau_competence_id", sa.Integer(), nullable=True)
|
"notes_ue", sa.Column("niveau_competence_id", sa.Integer(), nullable=True)
|
||||||
)
|
)
|
||||||
op.create_foreign_key(
|
op.create_foreign_key(
|
||||||
None, "notes_ue", "apc_niveau", ["niveau_competence_id"], ["id"]
|
"notes_ue_niveau_competence_id_fkey",
|
||||||
|
"notes_ue",
|
||||||
|
"apc_niveau",
|
||||||
|
["niveau_competence_id"],
|
||||||
|
["id"],
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"parcours_modules",
|
||||||
|
sa.Column("parcours_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("module_id", sa.Integer(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["module_id"],
|
||||||
|
["notes_modules.id"],
|
||||||
|
# nom ajouté manuellement:
|
||||||
|
name="parcours_modules_module_id_fkey",
|
||||||
|
ondelete="CASCADE",
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["parcours_id"],
|
||||||
|
["apc_parcours.id"],
|
||||||
|
# nom ajouté manuellement:
|
||||||
|
name="parcours_modules_parcours_id_fkey",
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("parcours_id", "module_id"),
|
||||||
)
|
)
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
def downgrade():
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
op.drop_constraint(None, "notes_ue", type_="foreignkey")
|
op.drop_constraint(
|
||||||
|
"notes_ue_niveau_competence_id_fkey", "notes_ue", type_="foreignkey"
|
||||||
|
)
|
||||||
op.drop_column("notes_ue", "niveau_competence_id")
|
op.drop_column("notes_ue", "niveau_competence_id")
|
||||||
|
op.drop_table("parcours_modules")
|
||||||
# ### end Alembic commands ###
|
# ### end Alembic commands ###
|
||||||
|
@ -4,35 +4,57 @@ Utiliser par exemple comme:
|
|||||||
pytest tests/unit/test_refcomp.py
|
pytest tests/unit/test_refcomp.py
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import io
|
|
||||||
from flask import g
|
from flask import g
|
||||||
import app
|
|
||||||
from app import db
|
from app import db
|
||||||
from app import models
|
from app import models
|
||||||
from app.but.import_refcomp import orebut_import_refcomp
|
from app.but.import_refcomp import orebut_import_refcomp
|
||||||
|
from app.models import UniteEns
|
||||||
from app.models.but_refcomp import (
|
from app.models.but_refcomp import (
|
||||||
ApcReferentielCompetences,
|
ApcReferentielCompetences,
|
||||||
ApcCompetence,
|
ApcCompetence,
|
||||||
ApcSituationPro,
|
ApcSituationPro,
|
||||||
|
ApcNiveau,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from tests.unit import setup
|
||||||
|
|
||||||
|
REF_RT_XML = open(
|
||||||
|
"ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml"
|
||||||
|
).read()
|
||||||
|
|
||||||
|
|
||||||
def test_but_refcomp(test_client):
|
def test_but_refcomp(test_client):
|
||||||
"""modèles ref. comp."""
|
"""modèles ref. comp."""
|
||||||
xml_data = open(
|
|
||||||
"ressources/referentiels/but2022/competences/but-RT-05012022-081735.xml"
|
|
||||||
).read()
|
|
||||||
dept_id = models.Departement.query.first().id
|
dept_id = models.Departement.query.first().id
|
||||||
ref = orebut_import_refcomp(xml_data, dept_id)
|
ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_RT_XML, dept_id)
|
||||||
assert ref.competences.count() == 13
|
assert ref_comp.competences.count() == 13
|
||||||
assert ref.competences[0].situations.count() == 3
|
assert ref_comp.competences[0].situations.count() == 3
|
||||||
assert ref.competences[0].situations[0].libelle.startswith("Conception ")
|
assert ref_comp.competences[0].situations[0].libelle.startswith("Conception ")
|
||||||
assert (
|
assert (
|
||||||
ref.competences[-1].situations[-1].libelle
|
ref_comp.competences[-1].situations[-1].libelle
|
||||||
== "Administration des services multimédia"
|
== "Administration des services multimédia"
|
||||||
)
|
)
|
||||||
# test cascades on delete
|
# test cascades on delete
|
||||||
db.session.delete(ref)
|
db.session.delete(ref_comp)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
assert ApcCompetence.query.count() == 0
|
assert ApcCompetence.query.count() == 0
|
||||||
assert ApcSituationPro.query.count() == 0
|
assert ApcSituationPro.query.count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_but_assoc_ue_parcours(test_client):
|
||||||
|
"""Association UE / Niveau compétence"""
|
||||||
|
dept_id = models.Departement.query.first().id
|
||||||
|
G, formation_id, (ue1_id, ue2_id, ue3_id), module_ids = setup.build_formation_test()
|
||||||
|
ref_comp: ApcReferentielCompetences = orebut_import_refcomp(REF_RT_XML, dept_id)
|
||||||
|
ue = UniteEns.query.get(ue1_id)
|
||||||
|
assert ue.niveau_competence is None
|
||||||
|
niveau = ApcNiveau.query.first()
|
||||||
|
ue.niveau_competence = niveau
|
||||||
|
db.session.add(ue)
|
||||||
|
db.session.commit()
|
||||||
|
ue = UniteEns.query.get(ue1_id)
|
||||||
|
assert ue.niveau_competence == niveau
|
||||||
|
assert len(niveau.ues) == 1
|
||||||
|
assert niveau.ues[0] == ue
|
||||||
|
Loading…
Reference in New Issue
Block a user