forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -284,15 +284,11 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
# ---- Décision année et autorisation
|
||||
self.autorisations_recorded = False
|
||||
"vrai si on a enregistré l'autorisation de passage"
|
||||
self.validation = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=self.annee_but,
|
||||
)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=self.formsemestre.formation.formation_code)
|
||||
.first()
|
||||
)
|
||||
self.validation = ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=self.annee_but,
|
||||
referentiel_competence_id=self.formsemestre.formation.referentiel_competence_id,
|
||||
).first()
|
||||
"Validation actuellement enregistrée pour cette année BUT"
|
||||
self.code_valide = self.validation.code if self.validation is not None else None
|
||||
"Le code jury annuel enregistré, ou None"
|
||||
@ -689,13 +685,13 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
self.validation = ApcValidationAnnee(
|
||||
etudid=self.etud.id,
|
||||
formsemestre=self.formsemestre_impair,
|
||||
formation_id=self.formsemestre.formation_id,
|
||||
referentiel_competence_id=self.formsemestre.formation.referentiel_competence_id,
|
||||
ordre=self.annee_but,
|
||||
annee_scolaire=self.annee_scolaire(),
|
||||
code=code,
|
||||
)
|
||||
else: # Update validation année BUT
|
||||
self.validation.etud = self.etud
|
||||
assert self.validation.etudid == self.etud.id
|
||||
self.validation.formsemestre = self.formsemestre_impair
|
||||
self.validation.formation_id = self.formsemestre.formation_id
|
||||
self.validation.ordre = self.annee_but
|
||||
@ -852,13 +848,10 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
||||
|
||||
# Efface les validations concernant l'année BUT
|
||||
# de ce semestre
|
||||
validations = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=self.annee_but,
|
||||
)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=self.formsemestre.formation.formation_code)
|
||||
validations = ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=self.annee_but,
|
||||
referentiel_competence_id=self.formsemestre.formation.referentiel_competence_id,
|
||||
)
|
||||
for validation in validations:
|
||||
db.session.delete(validation)
|
||||
@ -1287,15 +1280,11 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
if annee_inferieure < 1:
|
||||
return
|
||||
# Garde-fou: Année déjà validée ?
|
||||
validations_annee: ApcValidationAnnee = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=annee_inferieure,
|
||||
)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=self.deca.formsemestre.formation.formation_code)
|
||||
.all()
|
||||
)
|
||||
validations_annee: ApcValidationAnnee = ApcValidationAnnee.query.filter_by(
|
||||
etudid=self.etud.id,
|
||||
ordre=annee_inferieure,
|
||||
referentiel_competence_id=self.deca.formsemestre.formation.referentiel_competence_id,
|
||||
).all()
|
||||
if len(validations_annee) > 1:
|
||||
log(
|
||||
f"warning: {len(validations_annee)} validations d'année\n{validations_annee}"
|
||||
@ -1332,8 +1321,8 @@ class DecisionsProposeesRCUE(DecisionsProposees):
|
||||
validation_annee = ApcValidationAnnee(
|
||||
etudid=self.etud.id,
|
||||
ordre=annee_inferieure,
|
||||
referentiel_competence_id=self.deca.formsemestre.formation.referentiel_competence_id,
|
||||
code=sco_codes.ADSUP,
|
||||
formation_id=self.deca.formsemestre.formation_id,
|
||||
# met cette validation sur l'année scolaire actuelle, pas la précédente
|
||||
annee_scolaire=self.deca.formsemestre.annee_scolaire(),
|
||||
)
|
||||
@ -1575,16 +1564,11 @@ class DecisionsProposeesUE(DecisionsProposees):
|
||||
|
||||
# def est_annee_validee(self, ordre: int) -> bool:
|
||||
# """Vrai si l'année BUT ordre est validée"""
|
||||
# # On cherche les validations d'annee avec le même
|
||||
# # code formation que nous.
|
||||
# return (
|
||||
# ApcValidationAnnee.query.filter_by(
|
||||
# etudid=self.etud.id,
|
||||
# ordre=ordre,
|
||||
# )
|
||||
# .join(Formation)
|
||||
# .filter(
|
||||
# Formation.formation_code == self.formsemestre.formation.formation_code
|
||||
# referentiel_competence_id=self.formsemestre.formation.referentiel_competence_id
|
||||
# )
|
||||
# .count()
|
||||
# > 0
|
||||
|
@ -231,12 +231,11 @@ def erase_decisions_annee_formation(
|
||||
.all()
|
||||
)
|
||||
# Année BUT
|
||||
validations += (
|
||||
ApcValidationAnnee.query.filter_by(etudid=etud.id, ordre=annee)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=formation.formation_code)
|
||||
.all()
|
||||
)
|
||||
validations += ApcValidationAnnee.query.filter_by(
|
||||
etudid=etud.id,
|
||||
ordre=annee,
|
||||
referentiel_competence_id=formation.referentiel_competence_id,
|
||||
).all()
|
||||
# Autorisations vers les semestres suivants ceux de l'année:
|
||||
validations += (
|
||||
ScolarAutorisationInscription.query.filter_by(
|
||||
|
@ -337,17 +337,15 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
||||
if self.validations_annee:
|
||||
return self.validations_annee
|
||||
annee_but = (self.formsemestre.semestre_id + 1) // 2
|
||||
validations = (
|
||||
ApcValidationAnnee.query.filter_by(ordre=annee_but)
|
||||
.join(Formation)
|
||||
.filter_by(formation_code=self.formsemestre.formation.formation_code)
|
||||
.join(
|
||||
FormSemestreInscription,
|
||||
db.and_(
|
||||
FormSemestreInscription.etudid == ApcValidationAnnee.etudid,
|
||||
FormSemestreInscription.formsemestre_id == self.formsemestre.id,
|
||||
),
|
||||
)
|
||||
validations = ApcValidationAnnee.query.filter_by(
|
||||
ordre=annee_but,
|
||||
referentiel_competence_id=self.formsemestre.formation.referentiel_competence_id,
|
||||
).join(
|
||||
FormSemestreInscription,
|
||||
db.and_(
|
||||
FormSemestreInscription.etudid == ApcValidationAnnee.etudid,
|
||||
FormSemestreInscription.formsemestre_id == self.formsemestre.id,
|
||||
),
|
||||
)
|
||||
validation_by_etud = {}
|
||||
for validation in validations:
|
||||
|
@ -94,6 +94,11 @@ class ApcReferentielCompetences(db.Model, XMLModel):
|
||||
backref="referentiel_competence",
|
||||
order_by="Formation.acronyme, Formation.version",
|
||||
)
|
||||
validations_annee = db.relationship(
|
||||
"ApcValidationAnnee",
|
||||
backref="referentiel_competence",
|
||||
lazy="dynamic",
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ApcReferentielCompetences {self.id} {self.specialite!r} {self.departement!r}>"
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
"""Décisions de jury (validations) des RCUE et années du BUT
|
||||
"""
|
||||
from typing import Union
|
||||
|
||||
|
||||
from app import db
|
||||
from app.models import CODE_STR_LEN
|
||||
@ -106,71 +104,14 @@ class ApcValidationRCUE(db.Model):
|
||||
}
|
||||
|
||||
|
||||
# unused
|
||||
# def find_rcues(
|
||||
# formsemestre: FormSemestre, ue: UniteEns, etud: Identite, inscription_etat: str
|
||||
# ) -> list[RegroupementCoherentUE]:
|
||||
# """Les RCUE (niveau de compétence) à considérer pour cet étudiant dans
|
||||
# ce semestre pour cette UE.
|
||||
|
||||
# Cherche les UEs du même niveau de compétence auxquelles l'étudiant est inscrit.
|
||||
# En cas de redoublement, il peut y en avoir plusieurs, donc plusieurs RCUEs.
|
||||
|
||||
# Résultat: la liste peut être vide.
|
||||
# """
|
||||
# if (ue.niveau_competence is None) or (ue.semestre_idx is None):
|
||||
# return []
|
||||
|
||||
# if ue.semestre_idx % 2: # S1, S3, S5
|
||||
# other_semestre_idx = ue.semestre_idx + 1
|
||||
# else:
|
||||
# other_semestre_idx = ue.semestre_idx - 1
|
||||
|
||||
# cursor = db.session.execute(
|
||||
# text(
|
||||
# """SELECT
|
||||
# ue.id, formsemestre.id
|
||||
# FROM
|
||||
# notes_ue ue,
|
||||
# notes_formsemestre_inscription inscr,
|
||||
# notes_formsemestre formsemestre
|
||||
|
||||
# WHERE
|
||||
# inscr.etudid = :etudid
|
||||
# AND inscr.formsemestre_id = formsemestre.id
|
||||
|
||||
# AND formsemestre.semestre_id = :other_semestre_idx
|
||||
# AND ue.formation_id = formsemestre.formation_id
|
||||
# AND ue.niveau_competence_id = :ue_niveau_competence_id
|
||||
# AND ue.semestre_idx = :other_semestre_idx
|
||||
# """
|
||||
# ),
|
||||
# {
|
||||
# "etudid": etud.id,
|
||||
# "other_semestre_idx": other_semestre_idx,
|
||||
# "ue_niveau_competence_id": ue.niveau_competence_id,
|
||||
# },
|
||||
# )
|
||||
# rcues = []
|
||||
# for ue_id, formsemestre_id in cursor:
|
||||
# other_ue = UniteEns.query.get(ue_id)
|
||||
# other_formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
# rcues.append(
|
||||
# RegroupementCoherentUE(
|
||||
# etud, formsemestre, ue, other_formsemestre, other_ue, inscription_etat
|
||||
# )
|
||||
# )
|
||||
# # safety check: 1 seul niveau de comp. concerné:
|
||||
# assert len({rcue.ue_1.niveau_competence_id for rcue in rcues}) == 1
|
||||
# return rcues
|
||||
|
||||
|
||||
class ApcValidationAnnee(db.Model):
|
||||
"""Validation des années du BUT"""
|
||||
|
||||
__tablename__ = "apc_validation_annee"
|
||||
# Assure unicité de la décision:
|
||||
__table_args__ = (db.UniqueConstraint("etudid", "annee_scolaire", "ordre"),)
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint("etudid", "ordre", "referentiel_competence_id"),
|
||||
)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
etudid = db.Column(
|
||||
db.Integer,
|
||||
@ -184,10 +125,8 @@ class ApcValidationAnnee(db.Model):
|
||||
db.Integer, db.ForeignKey("notes_formsemestre.id"), nullable=True
|
||||
)
|
||||
"le semestre origine, normalement l'IMPAIR (le 1er) de l'année"
|
||||
formation_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_formations.id"),
|
||||
nullable=False,
|
||||
referentiel_competence_id = db.Column(
|
||||
db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False
|
||||
)
|
||||
annee_scolaire = db.Column(db.Integer, nullable=False) # eg 2021
|
||||
date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||
@ -207,17 +146,22 @@ class ApcValidationAnnee(db.Model):
|
||||
"dict pour bulletins"
|
||||
return {
|
||||
"annee_scolaire": self.annee_scolaire,
|
||||
"date": self.date.isoformat(),
|
||||
"date": self.date.isoformat() if self.date else "",
|
||||
"code": self.code,
|
||||
"ordre": self.ordre,
|
||||
}
|
||||
|
||||
def html(self) -> str:
|
||||
"Affichage html"
|
||||
date_str = (
|
||||
f"""le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}"""
|
||||
if self.date
|
||||
else "(sans date)"
|
||||
)
|
||||
return f"""Validation <b>année BUT{self.ordre}</b> émise par
|
||||
{self.formsemestre.html_link_status() if self.formsemestre else "-"}
|
||||
: <b>{self.code}</b>
|
||||
le {self.date.strftime("%d/%m/%Y")} à {self.date.strftime("%Hh%M")}
|
||||
{date_str}
|
||||
"""
|
||||
|
||||
|
||||
@ -259,15 +203,11 @@ def dict_decision_jury(etud: Identite, formsemestre: FormSemestre) -> dict:
|
||||
decisions["descr_decisions_rcue"] = ""
|
||||
decisions["descr_decisions_niveaux"] = ""
|
||||
# --- Année: prend la validation pour l'année scolaire de ce semestre
|
||||
validation = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=etud.id,
|
||||
annee_scolaire=formsemestre.annee_scolaire(),
|
||||
)
|
||||
.join(Formation)
|
||||
.filter(Formation.formation_code == formsemestre.formation.formation_code)
|
||||
.first()
|
||||
)
|
||||
validation = ApcValidationAnnee.query.filter_by(
|
||||
etudid=etud.id,
|
||||
annee_scolaire=formsemestre.annee_scolaire(),
|
||||
referentiel_competence_id=formsemestre.formation.referentiel_competence_id,
|
||||
).first()
|
||||
if validation:
|
||||
decisions["decision_annee"] = validation.to_dict_bul()
|
||||
else:
|
||||
|
@ -867,15 +867,12 @@ class FormSemestre(db.Model):
|
||||
.order_by(UniteEns.numero)
|
||||
.all()
|
||||
)
|
||||
vals_annee = ( # issues de ce formsemestre seulement
|
||||
vals_annee = ( # issues de cette année scolaire seulement
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
etudid=etudid,
|
||||
annee_scolaire=self.annee_scolaire(),
|
||||
)
|
||||
.join(ApcValidationAnnee.formsemestre)
|
||||
.join(FormSemestre.formation)
|
||||
.filter(Formation.formation_code == self.formation.formation_code)
|
||||
.all()
|
||||
referentiel_competence_id=self.formation.referentiel_competence_id,
|
||||
).all()
|
||||
)
|
||||
H = []
|
||||
for vals in (vals_sem, vals_ues, vals_rcues, vals_annee):
|
||||
|
@ -491,15 +491,11 @@ class ApoEtud(dict):
|
||||
# ne trouve pas de semestre impair
|
||||
self.validation_annee_but = None
|
||||
return
|
||||
self.validation_annee_but: ApcValidationAnnee = (
|
||||
ApcValidationAnnee.query.filter_by(
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=self.etud["etudid"],
|
||||
formation_id=self.cur_sem[
|
||||
"formation_id"
|
||||
], # XXX utiliser formation_code
|
||||
).first()
|
||||
)
|
||||
self.validation_annee_but: ApcValidationAnnee = ApcValidationAnnee.query.filter_by(
|
||||
formsemestre_id=formsemestre.id,
|
||||
etudid=self.etud["etudid"],
|
||||
referentiel_competence_id=self.cur_res.formsemestre.formation.referentiel_competence_id,
|
||||
).first()
|
||||
self.is_nar = (
|
||||
self.validation_annee_but and self.validation_annee_but.code == NAR
|
||||
)
|
||||
|
112
migrations/versions/829683efddc4_change_apcvalidationannee.py
Normal file
112
migrations/versions/829683efddc4_change_apcvalidationannee.py
Normal file
@ -0,0 +1,112 @@
|
||||
"""Change ApcValidationAnnee
|
||||
|
||||
Revision ID: 829683efddc4
|
||||
Revises: c701224fa255
|
||||
Create Date: 2023-06-28 09:47:16.591028
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import sessionmaker # added by ev
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "829683efddc4"
|
||||
down_revision = "c701224fa255"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
Session = sessionmaker()
|
||||
|
||||
|
||||
# Voir https://stackoverflow.com/questions/24082542/check-if-a-table-column-exists-in-the-database-using-sqlalchemy-and-alembic
|
||||
from sqlalchemy import inspect
|
||||
|
||||
|
||||
def column_exists(table_name, column_name):
|
||||
bind = op.get_context().bind
|
||||
insp = inspect(bind)
|
||||
columns = insp.get_columns(table_name)
|
||||
return any(c["name"] == column_name for c in columns)
|
||||
|
||||
|
||||
def upgrade():
|
||||
if column_exists("apc_validation_annee", "referentiel_competence_id"):
|
||||
return # utile durant developpement
|
||||
# Enleve la contrainte erronée
|
||||
with op.batch_alter_table("apc_validation_annee", schema=None) as batch_op:
|
||||
batch_op.drop_constraint(
|
||||
"apc_validation_annee_etudid_annee_scolaire_ordre_key", type_="unique"
|
||||
)
|
||||
# Ajoute colonne referentiel, nullable pour l'instant
|
||||
batch_op.add_column(
|
||||
sa.Column("referentiel_competence_id", sa.Integer(), nullable=True)
|
||||
)
|
||||
|
||||
# Affecte le referentiel des anciennes validations
|
||||
bind = op.get_bind()
|
||||
session = Session(bind=bind)
|
||||
session.execute(
|
||||
sa.text(
|
||||
"""
|
||||
UPDATE apc_validation_annee AS a
|
||||
SET referentiel_competence_id = (
|
||||
SELECT f.referentiel_competence_id
|
||||
FROM notes_formations f
|
||||
WHERE f.id = a.formation_id
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
# En principe, on n'a pas pu entrer de validation sur des formations sans referentiel
|
||||
# par prudence, on les supprime avant d'ajouter la contrainte
|
||||
session.execute(
|
||||
sa.text(
|
||||
"DELETE FROM apc_validation_annee WHERE referentiel_competence_id is NULL"
|
||||
)
|
||||
)
|
||||
op.alter_column(
|
||||
"apc_validation_annee",
|
||||
"referentiel_competence_id",
|
||||
nullable=False,
|
||||
)
|
||||
op.create_foreign_key(
|
||||
"apc_validation_annee_refcomp_fkey",
|
||||
"apc_validation_annee",
|
||||
"apc_referentiel_competences",
|
||||
["referentiel_competence_id"],
|
||||
["id"],
|
||||
)
|
||||
# Efface les validations d'année dupliquées
|
||||
# (garde le premier code dans l'ordre alphabétique... mieux que rien)
|
||||
session.execute(
|
||||
sa.text(
|
||||
"""
|
||||
DELETE FROM apc_validation_annee t1
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM apc_validation_annee t2
|
||||
WHERE t1.etudid = t2.etudid
|
||||
AND t1.referentiel_competence_id = t2.referentiel_competence_id
|
||||
AND t1.ordre = t2.ordre
|
||||
AND t1.code > t2.code
|
||||
);
|
||||
"""
|
||||
)
|
||||
)
|
||||
# Et ajoute la contrainte unicité de décision année par étudiant/ref. comp.:
|
||||
op.create_unique_constraint(
|
||||
"apc_validation_annee_etudid_ordre_refcomp_key",
|
||||
"apc_validation_annee",
|
||||
["etudid", "ordre", "referentiel_competence_id"],
|
||||
)
|
||||
op.drop_column("apc_validation_annee", "formation_id")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# Se contente de ré-ajouter la colonne formation_id sans re-générer son contenu
|
||||
with op.batch_alter_table("apc_validation_annee", schema=None) as batch_op:
|
||||
# batch_op.drop_constraint(
|
||||
# "apc_validation_annee_etudid_ordre_refcomp_key", type_="unique"
|
||||
# )
|
||||
# batch_op.drop_column("referentiel_competence_id")
|
||||
batch_op.add_column(sa.Column("formation_id", sa.Integer(), nullable=True))
|
Loading…
Reference in New Issue
Block a user