"""ScoDoc 9 models : Modules """ from app import db from app.models import APO_CODE_STR_LEN from app.scodoc import sco_utils as scu from app.scodoc.sco_codes_parcours import UE_SPORT from app.scodoc.sco_utils import ModuleType class Module(db.Model): """Module""" __tablename__ = "notes_modules" id = db.Column(db.Integer, primary_key=True) module_id = db.synonym("id") titre = db.Column(db.Text()) abbrev = db.Column(db.Text()) # nom court # certains départements ont des codes infiniment longs: donc Text ! code = db.Column(db.Text(), nullable=False) heures_cours = db.Column(db.Float) heures_td = db.Column(db.Float) heures_tp = db.Column(db.Float) coefficient = db.Column(db.Float) # coef PPN (sauf en APC) ects = db.Column(db.Float) # Crédits ECTS ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), index=True) formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id")) matiere_id = db.Column(db.Integer, db.ForeignKey("notes_matieres.id")) # pas un id mais le numéro du semestre: 1, 2, ... # note: en APC, le semestre qui fait autorité est celui de l'UE semestre_id = db.Column(db.Integer, nullable=False, default=1, server_default="1") numero = db.Column(db.Integer) # ordre de présentation # id de l'element pedagogique Apogee correspondant: code_apogee = db.Column(db.String(APO_CODE_STR_LEN)) # Type: ModuleType: DEFAULT, MALUS, RESSOURCE, MODULE_SAE (enum) module_type = db.Column(db.Integer, nullable=False, default=0, server_default="0") # Relations: modimpls = db.relationship("ModuleImpl", backref="module", lazy="dynamic") ues_apc = db.relationship("UniteEns", secondary="module_ue_coef", viewonly=True) tags = db.relationship( "NotesTag", secondary="notes_modules_tags", lazy=True, backref=db.backref("modules", lazy=True), ) def __init__(self, **kwargs): self.ue_coefs = [] super(Module, self).__init__(**kwargs) def __repr__(self): return f"" def to_dict(self): e = dict(self.__dict__) e.pop("_sa_instance_state", None) # ScoDoc7 output_formators: (backward compat) e["module_id"] = self.id e["heures_cours"] = 0.0 if self.heures_cours is None else self.heures_cours e["heures_td"] = 0.0 if self.heures_td is None else self.heures_td e["heures_tp"] = 0.0 if self.heures_tp is None else self.heures_tp e["numero"] = 0 if self.numero is None else self.numero e["coefficient"] = 0.0 if self.coefficient is None else self.coefficient e["module_type"] = 0 if self.module_type is None else self.module_type e["code_apogee"] = e["code_apogee"] or "" # pas de None return e def is_apc(self): "True si module SAÉ ou Ressource" return self.module_type and scu.ModuleType(self.module_type) in { scu.ModuleType.RESSOURCE, scu.ModuleType.SAE, } def type_name(self): return scu.MODULE_TYPE_NAMES[self.module_type] def set_ue_coef(self, ue, coef: float) -> None: """Set coef module vers cette UE""" self.update_ue_coef_dict({ue.id: coef}) def set_ue_coef_dict(self, ue_coef_dict: dict) -> None: """set coefs vers les UE (remplace existants) ue_coef_dict = { ue_id : coef } Les coefs nuls (zéro) ne sont pas stockés: la relation est supprimée. """ changed = False for ue_id, coef in ue_coef_dict.items(): # Existant ? coefs = [c for c in self.ue_coefs if c.ue_id == ue_id] if coefs: ue_coef = coefs[0] if coef == 0.0: # supprime ce coef db.session.delete(ue_coef) changed = True elif coef != ue_coef.coef: ue_coef.coef = coef db.session.add(ue_coef) changed = True else: # crée nouveau coef: if coef != 0.0: ue = UniteEns.query.get(ue_id) ue_coef = ModuleUECoef(module=self, ue=ue, coef=coef) self.ue_coefs.append(ue_coef) changed = True if changed: self.formation.invalidate_module_coefs() def update_ue_coef_dict(self, ue_coef_dict: dict): """update coefs vers UE (ajoute aux existants)""" current = self.get_ue_coef_dict() current.update(ue_coef_dict) self.set_ue_coef_dict(current) def get_ue_coef_dict(self): """returns { ue_id : coef }""" return {p.ue.id: p.coef for p in self.ue_coefs} def delete_ue_coef(self, ue): """delete coef""" ue_coef = ModuleUECoef.query.get((self.id, ue.id)) if ue_coef: db.session.delete(ue_coef) self.formation.invalidate_module_coefs() def get_ue_coefs_sorted(self): "les coefs d'UE, trié par numéro d'UE" # je n'ai pas su mettre un order_by sur le backref sans avoir # à redéfinir les relationships... return sorted(self.ue_coefs, key=lambda x: x.ue.numero) def ue_coefs_list(self, include_zeros=True): """Liste des coefs vers les UE (pour les modules APC). Si include_zeros, liste aussi les UE sans coef (donc nul) de ce semestre, sauf UE bonus sport. Result: List of tuples [ (ue, coef) ] """ if not self.is_apc(): return [] if include_zeros: # Toutes les UE du même semestre: ues_semestre = ( self.formation.ues.filter_by(semestre_idx=self.ue.semestre_idx) .filter(UniteEns.type != UE_SPORT) .order_by(UniteEns.numero) .all() ) coefs_dict = self.get_ue_coef_dict() coefs_list = [] for ue in ues_semestre: coefs_list.append((ue, coefs_dict.get(ue.id, 0.0))) return coefs_list # Liste seulement les coefs définis: return [(c.ue, c.coef) for c in self.get_ue_coefs_sorted()] class ModuleUECoef(db.Model): """Coefficients des modules vers les UE (APC, BUT) En mode APC, ces coefs remplacent le coefficient "PPN" du module. """ __tablename__ = "module_ue_coef" module_id = db.Column( db.Integer, db.ForeignKey("notes_modules.id", ondelete="CASCADE"), primary_key=True, ) ue_id = db.Column( db.Integer, db.ForeignKey("notes_ue.id", ondelete="CASCADE"), primary_key=True, ) coef = db.Column( db.Float, nullable=False, ) module = db.relationship( Module, backref=db.backref( "ue_coefs", passive_deletes=True, cascade="save-update, merge, delete, delete-orphan", ), ) ue = db.relationship( "UniteEns", backref=db.backref( "module_ue_coefs", passive_deletes=True, cascade="save-update, merge, delete, delete-orphan", ), ) class NotesTag(db.Model): """Tag sur un module""" __tablename__ = "notes_tags" __table_args__ = (db.UniqueConstraint("title", "dept_id"),) id = db.Column(db.Integer, primary_key=True) tag_id = db.synonym("id") dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) title = db.Column(db.Text(), nullable=False) # Association tag <-> module notes_modules_tags = db.Table( "notes_modules_tags", db.Column( "tag_id", db.Integer, db.ForeignKey("notes_tags.id", ondelete="CASCADE"), ), db.Column( "module_id", db.Integer, db.ForeignKey("notes_modules.id", ondelete="CASCADE") ), ) from app.models.ues import UniteEns