ScoDoc-Lille/app/models/ues.py

129 lines
5.0 KiB
Python

"""ScoDoc 9 models : Unités d'Enseignement (UE)
"""
from app import db
from app.models import APO_CODE_STR_LEN
from app.models import SHORT_STR_LEN
from app.scodoc import sco_utils as scu
class UniteEns(db.Model):
"""Unité d'Enseignement (UE)"""
__tablename__ = "notes_ue"
id = db.Column(db.Integer, primary_key=True)
ue_id = db.synonym("id")
formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id"))
acronyme = db.Column(db.Text(), nullable=False)
numero = db.Column(db.Integer) # ordre de présentation
titre = db.Column(db.Text())
# Le semestre_idx n'est pas un id mais le numéro du semestre: 1, 2, ...
# En ScoDoc7 et pour les formations classiques, il est NULL
# (le numéro du semestre étant alors déterminé par celui des modules de l'UE)
# Pour les formations APC, il est obligatoire (de 1 à 6 pour le BUT):
semestre_idx = db.Column(db.Integer, nullable=True, index=True)
# Type d'UE: 0 normal ("fondamentale"), 1 "sport", 2 "projet et stage (LP)",
# 4 "élective"
type = db.Column(db.Integer, default=0, server_default="0")
# Les UE sont "compatibles" (pour la capitalisation) ssi elles ont ^m code
# note: la fonction SQL notes_newid_ucod doit être créée à part
ue_code = db.Column(
db.String(SHORT_STR_LEN),
server_default=db.text("notes_newid_ucod()"),
nullable=False,
)
ects = db.Column(db.Float) # nombre de credits ECTS
is_external = db.Column(db.Boolean(), default=False, server_default="false")
# id de l'element pedagogique Apogee correspondant:
code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
# coef UE, utilise seulement si l'option use_ue_coefs est activée:
coefficient = db.Column(db.Float)
# coef. pour le calcul de moyennes de RCUE. Par défaut, 1.
coef_rcue = db.Column(db.Float, nullable=False, default=1.0, server_default="1.0")
color = db.Column(db.Text())
# BUT
niveau_competence_id = db.Column(db.Integer, db.ForeignKey("apc_niveau.id"))
niveau_competence = db.relationship("ApcNiveau", back_populates="ues")
# relations
matieres = db.relationship("Matiere", lazy="dynamic", backref="ue")
modules = db.relationship("Module", lazy="dynamic", backref="ue")
def __repr__(self):
return f"""<{self.__class__.__name__}(id={self.id}, formation_id={
self.formation_id}, acronyme='{self.acronyme}', semestre_idx={
self.semestre_idx} {
'EXTERNE' if self.is_external else ''})>"""
def to_dict(self):
"""as a dict, with the same conversions as in ScoDoc7
(except ECTS: keep None)
"""
e = dict(self.__dict__)
e.pop("_sa_instance_state", None)
# ScoDoc7 output_formators
e["ue_id"] = self.id
e["numero"] = e["numero"] if e["numero"] else 0
e["ects"] = e["ects"]
e["coefficient"] = e["coefficient"] if e["coefficient"] else 0.0
e["code_apogee"] = e["code_apogee"] or "" # pas de None
return e
def is_locked(self):
"""True if UE should not be modified
(contains modules used in a locked formsemestre)
"""
# XXX todo : à ré-écrire avec SQLAlchemy
from app.scodoc import sco_edit_ue
return sco_edit_ue.ue_is_locked(self.id)
def can_be_deleted(self) -> bool:
"""True si l'UE n'est pas utilisée dans des formsemestre
et n'a pas de module rattachés
"""
# "pas un seul module de cette UE n'a de modimpl...""
return (self.modules.count() == 0) or not any(
m.modimpls.all() for m in self.modules
)
def guess_semestre_idx(self) -> None:
"""Lorsqu'on prend une ancienne formation non APC,
les UE n'ont pas d'indication de semestre.
Cette méthode fixe le semestre en prenant celui du premier module,
ou à défaut le met à 1.
"""
if self.semestre_idx is None:
module = self.modules.first()
if module is None:
self.semestre_idx = 1
else:
self.semestre_idx = module.semestre_id
db.session.add(self)
db.session.commit()
def get_ressources(self):
"Liste des modules ressources rattachés à cette UE"
return self.modules.filter_by(module_type=scu.ModuleType.RESSOURCE).all()
def get_saes(self):
"Liste des modules SAE rattachés à cette UE"
return self.modules.filter_by(module_type=scu.ModuleType.SAE).all()
def get_modules_not_apc(self):
"Listes des modules non SAE et non ressource (standards, mais aussi bonus...)"
return self.modules.filter(
(Module.module_type != scu.ModuleType.SAE),
(Module.module_type != scu.ModuleType.RESSOURCE),
).all()
def get_codes_apogee(self) -> set[str]:
"""Les codes Apogée (codés en base comme "VRT1,VRT2")"""
if self.code_apogee:
return {x.strip() for x in self.code_apogee.split(",") if x}
return set()