243 lines
8.6 KiB
Python
243 lines
8.6 KiB
Python
# -*- coding: UTF-8 -*
|
|
|
|
"""Notes, décisions de jury, évènements scolaires
|
|
"""
|
|
|
|
from app import db
|
|
from app import log
|
|
from app.models import SHORT_STR_LEN
|
|
from app.models import CODE_STR_LEN
|
|
from app.models.events import Scolog
|
|
from app.scodoc import sco_cache
|
|
from app.scodoc import sco_utils as scu
|
|
|
|
|
|
class ScolarFormSemestreValidation(db.Model):
|
|
"""Décisions de jury (sur semestre ou UEs)"""
|
|
|
|
__tablename__ = "scolar_formsemestre_validation"
|
|
# Assure unicité de la décision:
|
|
__table_args__ = (db.UniqueConstraint("etudid", "formsemestre_id", "ue_id"),)
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
formsemestre_validation_id = db.synonym("id")
|
|
etudid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
|
index=True,
|
|
)
|
|
formsemestre_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_formsemestre.id"),
|
|
index=True,
|
|
)
|
|
ue_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_ue.id"),
|
|
index=True,
|
|
)
|
|
code = db.Column(db.String(CODE_STR_LEN), nullable=False, index=True)
|
|
# NULL pour les UE, True|False pour les semestres:
|
|
assidu = db.Column(db.Boolean)
|
|
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
|
# NULL sauf si compense un semestre: (pas utilisé pour BUT)
|
|
compense_formsemestre_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_formsemestre.id"),
|
|
)
|
|
moy_ue = db.Column(db.Float)
|
|
# (normalement NULL) indice du semestre, utile seulement pour
|
|
# UE "antérieures" et si la formation définit des UE utilisées
|
|
# dans plusieurs semestres (cas R&T IUTV v2)
|
|
semestre_id = db.Column(db.Integer)
|
|
# Si UE validée dans le cursus d'un autre etablissement
|
|
is_external = db.Column(
|
|
db.Boolean, default=False, server_default="false", index=True
|
|
)
|
|
|
|
ue = db.relationship("UniteEns", lazy="select", uselist=False)
|
|
formsemestre = db.relationship(
|
|
"FormSemestre", lazy="select", uselist=False, foreign_keys=[formsemestre_id]
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"""{self.__class__.__name__}(sem={self.formsemestre_id}, etuid={
|
|
self.etudid}, code={self.code}, ue={self.ue}, moy_ue={self.moy_ue})"""
|
|
|
|
def __str__(self):
|
|
if self.ue_id:
|
|
# Note: si l'objet vient d'être créé, ue_id peut exister mais pas ue !
|
|
return f"""décision sur UE {self.ue.acronyme if self.ue else self.ue_id
|
|
} ({self.ue_id}): {self.code}"""
|
|
return f"""décision sur semestre {self.formsemestre.titre_mois()} du {
|
|
self.event_date.strftime("%d/%m/%Y")}"""
|
|
|
|
def delete(self):
|
|
"Efface cette validation"
|
|
log(f"{self.__class__.__name__}.delete({self})")
|
|
etud = self.etud
|
|
db.session.delete(self)
|
|
db.session.commit()
|
|
sco_cache.invalidate_formsemestre_etud(etud)
|
|
|
|
def to_dict(self) -> dict:
|
|
"as a dict"
|
|
d = dict(self.__dict__)
|
|
d.pop("_sa_instance_state", None)
|
|
return d
|
|
|
|
def html(self, detail=False) -> str:
|
|
"Affichage html"
|
|
if self.ue_id is not None:
|
|
moyenne = (
|
|
f", moyenne {scu.fmt_note(self.moy_ue)}/20 "
|
|
if self.moy_ue is not None
|
|
else ""
|
|
)
|
|
return f"""Validation
|
|
{'<span class="redboldtext">externe</span>' if self.is_external else ""}
|
|
de l'UE <b>{self.ue.acronyme}</b>
|
|
{('parcours <span class="parcours">'
|
|
+ ", ".join([p.code for p in self.ue.parcours]))
|
|
+ "</span>"
|
|
if self.ue.parcours else ""}
|
|
de {self.ue.formation.acronyme}
|
|
{("émise par " + self.formsemestre.html_link_status())
|
|
if self.formsemestre else "externe/antérieure"}
|
|
: <b>{self.code}</b>{moyenne}
|
|
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
|
"""
|
|
else:
|
|
return f"""Validation du semestre S{
|
|
self.formsemestre.semestre_id if self.formsemestre else "?"}
|
|
{self.formsemestre.html_link_status() if self.formsemestre else ""}
|
|
: <b>{self.code}</b>
|
|
le {self.event_date.strftime("%d/%m/%Y")} à {self.event_date.strftime("%Hh%M")}
|
|
"""
|
|
|
|
|
|
class ScolarAutorisationInscription(db.Model):
|
|
"""Autorisation d'inscription dans un semestre"""
|
|
|
|
__tablename__ = "scolar_autorisation_inscription"
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
autorisation_inscription_id = db.synonym("id")
|
|
|
|
etudid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
|
)
|
|
formation_code = db.Column(db.String(SHORT_STR_LEN), nullable=False)
|
|
# Indice du semestre où on peut s'inscrire:
|
|
semestre_id = db.Column(db.Integer)
|
|
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
|
origin_formsemestre_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_formsemestre.id"),
|
|
)
|
|
origin_formsemestre = db.relationship("FormSemestre", lazy="select", uselist=False)
|
|
|
|
def __repr__(self) -> str:
|
|
return f"""{self.__class__.__name__}(id={self.id}, etudid={
|
|
self.etudid}, semestre_id={self.semestre_id})"""
|
|
|
|
def to_dict(self) -> dict:
|
|
"as a dict"
|
|
d = dict(self.__dict__)
|
|
d.pop("_sa_instance_state", None)
|
|
return d
|
|
|
|
def html(self) -> str:
|
|
"Affichage html"
|
|
return f"""Autorisation de passage vers <b>S{self.semestre_id}</b> émise par
|
|
{self.origin_formsemestre.html_link_status()
|
|
if self.origin_formsemestre
|
|
else "-"}
|
|
le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}
|
|
"""
|
|
|
|
@classmethod
|
|
def autorise_etud(
|
|
cls,
|
|
etudid: int,
|
|
formation_code: str,
|
|
origin_formsemestre_id: int,
|
|
semestre_id: int,
|
|
):
|
|
"""Ajoute une autorisation"""
|
|
autorisation = cls(
|
|
etudid=etudid,
|
|
formation_code=formation_code,
|
|
origin_formsemestre_id=origin_formsemestre_id,
|
|
semestre_id=semestre_id,
|
|
)
|
|
db.session.add(autorisation)
|
|
Scolog.logdb(
|
|
"autorise_etud", etudid=etudid, msg=f"Passage vers S{semestre_id}: autorisé"
|
|
)
|
|
log(f"ScolarAutorisationInscription: recording {autorisation}")
|
|
|
|
@classmethod
|
|
def delete_autorisation_etud(
|
|
cls,
|
|
etudid: int,
|
|
origin_formsemestre_id: int,
|
|
):
|
|
"""Efface les autorisations de cet étudiant venant du sem. origine"""
|
|
autorisations = cls.query.filter_by(
|
|
etudid=etudid, origin_formsemestre_id=origin_formsemestre_id
|
|
)
|
|
for autorisation in autorisations:
|
|
db.session.delete(autorisation)
|
|
log(f"ScolarAutorisationInscription: deleting {autorisation}")
|
|
Scolog.logdb(
|
|
"autorise_etud",
|
|
etudid=etudid,
|
|
msg=f"Passage vers S{autorisation.semestre_id}: effacé",
|
|
)
|
|
db.session.flush()
|
|
|
|
|
|
class ScolarEvent(db.Model):
|
|
"""Evenement dans le parcours scolaire d'un étudiant"""
|
|
|
|
__tablename__ = "scolar_events"
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
event_id = db.synonym("id")
|
|
etudid = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
|
)
|
|
event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
|
formsemestre_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_formsemestre.id", ondelete="SET NULL"),
|
|
)
|
|
ue_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_ue.id", ondelete="SET NULL"),
|
|
)
|
|
# 'CREATION', 'INSCRIPTION', 'DEMISSION',
|
|
# 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM'
|
|
# 'ECHEC_SEM'
|
|
# 'UTIL_COMPENSATION'
|
|
event_type = db.Column(db.String(SHORT_STR_LEN))
|
|
# Semestre compensé par formsemestre_id:
|
|
comp_formsemestre_id = db.Column(
|
|
db.Integer,
|
|
db.ForeignKey("notes_formsemestre.id"),
|
|
)
|
|
etud = db.relationship("Identite", lazy="select", backref="events", uselist=False)
|
|
formsemestre = db.relationship(
|
|
"FormSemestre", lazy="select", uselist=False, foreign_keys=[formsemestre_id]
|
|
)
|
|
|
|
def to_dict(self) -> dict:
|
|
"as a dict"
|
|
d = dict(self.__dict__)
|
|
d.pop("_sa_instance_state", None)
|
|
return d
|
|
|
|
def __repr__(self) -> str:
|
|
return f"{self.__class__.__name__}({self.event_type}, {self.event_date.isoformat()}, {self.formsemestre})"
|