forked from ScoDoc/ScoDoc
BUT: jury inter-année pour les redoublants
This commit is contained in:
parent
1324d93b80
commit
5488475416
@ -91,7 +91,7 @@ from app.models.ues import UniteEns
|
|||||||
from app.models.validations import ScolarFormSemestreValidation
|
from app.models.validations import ScolarFormSemestreValidation
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import sco_codes_parcours as sco_codes
|
from app.scodoc import sco_codes_parcours as sco_codes
|
||||||
from app.scodoc.sco_codes_parcours import RED, UE_STANDARD
|
from app.scodoc.sco_codes_parcours import CODES_UE_VALIDES, RED, UE_STANDARD
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc.sco_exceptions import ScoException, ScoValueError
|
from app.scodoc.sco_exceptions import ScoException, ScoValueError
|
||||||
|
|
||||||
@ -205,6 +205,8 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
formsemestre: FormSemestre,
|
formsemestre: FormSemestre,
|
||||||
):
|
):
|
||||||
super().__init__(etud=etud)
|
super().__init__(etud=etud)
|
||||||
|
self.formsemestre = formsemestre
|
||||||
|
"le formsemestre utilisé pour construire ce deca"
|
||||||
self.formsemestre_id = formsemestre.id
|
self.formsemestre_id = formsemestre.id
|
||||||
"l'id du formsemestre utilisé pour construire ce deca"
|
"l'id du formsemestre utilisé pour construire ce deca"
|
||||||
formsemestre_impair, formsemestre_pair = self.comp_formsemestres(formsemestre)
|
formsemestre_impair, formsemestre_pair = self.comp_formsemestres(formsemestre)
|
||||||
@ -219,23 +221,34 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# Si les années scolaires sont distinctes, on est "à cheval"
|
||||||
|
self.a_cheval = (
|
||||||
|
formsemestre_impair
|
||||||
|
and formsemestre_pair
|
||||||
|
and formsemestre_impair.annee_scolaire()
|
||||||
|
!= formsemestre_pair.annee_scolaire()
|
||||||
|
)
|
||||||
|
"vrai si on groupe deux semestres d'années scolaires différentes"
|
||||||
# Si on part d'un semestre IMPAIR, il n'y aura pas de décision année proposée
|
# Si on part d'un semestre IMPAIR, il n'y aura pas de décision année proposée
|
||||||
# (mais on pourra évidemment valider des UE et même des RCUE)
|
# (mais on pourra évidemment valider des UE et même des RCUE)
|
||||||
self.jury_annuel: bool = formsemestre.semestre_id in (2, 4, 6)
|
self.jury_annuel: bool = formsemestre.semestre_id in (2, 4, 6)
|
||||||
"vrai si jury de fin d'année scolaire (propose code annuel)"
|
"vrai si jury de fin d'année scolaire (propose code annuel)"
|
||||||
|
|
||||||
self.formsemestre_impair = formsemestre_impair
|
self.formsemestre_impair = formsemestre_impair
|
||||||
"le 1er semestre de l'année scolaire considérée (S1, S3, S5)"
|
"le 1er semestre du groupement (S1, S3, S5)"
|
||||||
self.formsemestre_pair = formsemestre_pair
|
self.formsemestre_pair = formsemestre_pair
|
||||||
"le second formsemestre de la même année scolaire (S2, S4, S6)"
|
"le second formsemestre (S2, S4, S6), de la même année scolaire ou d'une précédente"
|
||||||
formsemestre_last = formsemestre_pair or formsemestre_impair
|
formsemestre_last = formsemestre_pair or formsemestre_impair
|
||||||
"le formsemestre le plus avancé dans cette année"
|
"le formsemestre le plus avancé (en indice de semestre) dans le groupement"
|
||||||
|
|
||||||
self.annee_but = (formsemestre_last.semestre_id + 1) // 2
|
self.annee_but = (formsemestre_last.semestre_id + 1) // 2
|
||||||
"le rang de l'année dans le BUT: 1, 2, 3"
|
"le rang de l'année dans le BUT: 1, 2, 3"
|
||||||
assert self.annee_but in (1, 2, 3)
|
assert self.annee_but in (1, 2, 3)
|
||||||
self.rcues_annee = []
|
self.rcues_annee = []
|
||||||
"RCUEs de l'année"
|
"""RCUEs de l'année
|
||||||
|
(peuvent concerner l'année scolaire antérieur pour les redoublants
|
||||||
|
avec UE capitalisées)
|
||||||
|
"""
|
||||||
self.inscription_etat = etud.inscription_etat(formsemestre_last.id)
|
self.inscription_etat = etud.inscription_etat(formsemestre_last.id)
|
||||||
"état de l'inscription dans le semestre le plus avancé (pair si année complète)"
|
"état de l'inscription dans le semestre le plus avancé (pair si année complète)"
|
||||||
self.inscription_etat_pair = (
|
self.inscription_etat_pair = (
|
||||||
@ -526,9 +539,9 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
|
|
||||||
def compute_rcues_annee(self) -> list[RegroupementCoherentUE]:
|
def compute_rcues_annee(self) -> list[RegroupementCoherentUE]:
|
||||||
"""Liste des regroupements d'UE à considérer cette année.
|
"""Liste des regroupements d'UE à considérer cette année.
|
||||||
Pour le moment on ne considère pas de RCUE à cheval sur plusieurs années (redoublants).
|
On peut avoir un RCUE à cheval sur plusieurs années (redoublants avec UE capitalisées).
|
||||||
Si on n'a pas les deux semestres, aucun RCUE.
|
Si on n'a pas les deux semestres, aucun RCUE.
|
||||||
Raises ScoValueError s'il y a des UE sans RCUE.
|
Raises ScoValueError s'il y a des UE sans RCUE. <= ??? XXX
|
||||||
"""
|
"""
|
||||||
if self.formsemestre_pair is None or self.formsemestre_impair is None:
|
if self.formsemestre_pair is None or self.formsemestre_impair is None:
|
||||||
return []
|
return []
|
||||||
@ -537,6 +550,13 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
for ue_pair in self.ues_pair:
|
for ue_pair in self.ues_pair:
|
||||||
rcue = None
|
rcue = None
|
||||||
for ue_impair in self.ues_impair:
|
for ue_impair in self.ues_impair:
|
||||||
|
if self.a_cheval:
|
||||||
|
# l'UE paire DOIT être capitalisée pour être utilisée
|
||||||
|
if (
|
||||||
|
self.decisions_ues[ue_pair.id].code_valide
|
||||||
|
not in CODES_UE_VALIDES
|
||||||
|
):
|
||||||
|
continue # ignore cette UE antérieure non capitalisée
|
||||||
if ue_pair.niveau_competence_id == ue_impair.niveau_competence_id:
|
if ue_pair.niveau_competence_id == ue_impair.niveau_competence_id:
|
||||||
rcue = RegroupementCoherentUE(
|
rcue = RegroupementCoherentUE(
|
||||||
self.etud,
|
self.etud,
|
||||||
@ -548,10 +568,12 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
)
|
)
|
||||||
ues_impair_sans_rcue.discard(ue_impair.id)
|
ues_impair_sans_rcue.discard(ue_impair.id)
|
||||||
break
|
break
|
||||||
if rcue is None:
|
if rcue is None and not self.a_cheval:
|
||||||
raise NoRCUEError(deca=self, ue=ue_pair)
|
raise NoRCUEError(deca=self, ue=ue_pair)
|
||||||
rcues_annee.append(rcue)
|
if rcue is not None:
|
||||||
if len(ues_impair_sans_rcue) > 0:
|
rcues_annee.append(rcue)
|
||||||
|
# Si jury annuel (pas à cheval), on doit avoir tous les RCUEs:
|
||||||
|
if len(ues_impair_sans_rcue) > 0 and not self.a_cheval:
|
||||||
ue = UniteEns.query.get(ues_impair_sans_rcue.pop())
|
ue = UniteEns.query.get(ues_impair_sans_rcue.pop())
|
||||||
raise NoRCUEError(deca=self, ue=ue)
|
raise NoRCUEError(deca=self, ue=ue)
|
||||||
return rcues_annee
|
return rcues_annee
|
||||||
@ -1026,6 +1048,10 @@ class DecisionsProposeesUE(DecisionsProposees):
|
|||||||
self.moy_ue_with_cap = ue_status["moy"]
|
self.moy_ue_with_cap = ue_status["moy"]
|
||||||
self.ue_status = ue_status
|
self.ue_status = ue_status
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"""<{self.__class__.__name__} ue={self.ue.acronyme} valid={self.code_valide
|
||||||
|
} codes={self.codes} explanation={self.explanation}"""
|
||||||
|
|
||||||
def set_rcue(self, rcue: RegroupementCoherentUE):
|
def set_rcue(self, rcue: RegroupementCoherentUE):
|
||||||
"""Rattache cette UE à un RCUE. Cela peut modifier les codes
|
"""Rattache cette UE à un RCUE. Cela peut modifier les codes
|
||||||
proposés (si compensation)"""
|
proposés (si compensation)"""
|
||||||
|
@ -72,16 +72,24 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
|
|||||||
else:
|
else:
|
||||||
avertissement_redoublement = ""
|
avertissement_redoublement = ""
|
||||||
|
|
||||||
|
formsemestre_1 = deca.formsemestre_impair
|
||||||
|
formsemestre_2 = deca.formsemestre_pair
|
||||||
|
# Ordonne selon les dates des 2 semestres considérés (pour les redoublants à cheval):
|
||||||
|
if deca.formsemestre_pair.date_debut < deca.formsemestre_impair.date_debut:
|
||||||
|
formsemestre_1, formsemestre_2 = formsemestre_2, formsemestre_1
|
||||||
H.append(
|
H.append(
|
||||||
f"""
|
f"""
|
||||||
<div class="titre_niveaux"><b>Niveaux de compétences et unités d'enseignement du BUT{deca.annee_but}</b></div>
|
<div class="titre_niveaux"><b>Niveaux de compétences et unités d'enseignement du BUT{deca.annee_but}</b></div>
|
||||||
<div class="but_annee">
|
<div class="but_annee">
|
||||||
<div class="titre"></div>
|
<div class="titre"></div>
|
||||||
<div class="titre">S{deca.formsemestre_impair.semestre_id
|
<div class="titre">S{formsemestre_1.semestre_id
|
||||||
if deca.formsemestre_impair else "-"}</div>
|
if formsemestre_1 else "-"}
|
||||||
<div class="titre">S{deca.formsemestre_pair.semestre_id
|
<span class="avertissement_redoublement">{formsemestre_1.annee_scolaire_str() if formsemestre_1 else ""}</span>
|
||||||
if deca.formsemestre_pair else "-"}
|
</div>
|
||||||
<span class="avertissement_redoublement">{avertissement_redoublement}</span></div>
|
<div class="titre">S{formsemestre_2.semestre_id
|
||||||
|
if formsemestre_2 else "-"}
|
||||||
|
<span class="avertissement_redoublement">{formsemestre_2.annee_scolaire_str() if formsemestre_2 else ""}</span>
|
||||||
|
</div>
|
||||||
<div class="titre">RCUE</div>
|
<div class="titre">RCUE</div>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
@ -91,43 +99,54 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
|
|||||||
<div title="{niveau.competence.titre_long}">{niveau.competence.titre}</div>
|
<div title="{niveau.competence.titre_long}">{niveau.competence.titre}</div>
|
||||||
</div>"""
|
</div>"""
|
||||||
)
|
)
|
||||||
dec_rcue = deca.decisions_rcue_by_niveau.get(niveau.id)
|
dec_rcue = deca.decisions_rcue_by_niveau.get(niveau.id) # peut être None
|
||||||
if dec_rcue is None:
|
ues = [ue for ue in deca.ues_impair if ue.niveau_competence.id == niveau.id]
|
||||||
|
ue_impair = ues[0] if ues else None
|
||||||
|
ues = [ue for ue in deca.ues_pair if ue.niveau_competence.id == niveau.id]
|
||||||
|
ue_pair = ues[0] if ues else None
|
||||||
|
# Les UEs à afficher, toujours en readonly sur le formsemestre de l'année précédente du redoublant
|
||||||
|
ues_ro = [
|
||||||
|
(
|
||||||
|
ue_impair,
|
||||||
|
(deca.a_cheval and deca.formsemestre_id != deca.formsemestre_impair.id),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
ue_pair,
|
||||||
|
deca.a_cheval and deca.formsemestre_id != deca.formsemestre_pair.id,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
# Ordonne selon les dates des 2 semestres considérés:
|
||||||
|
if deca.formsemestre_pair.date_debut < deca.formsemestre_impair.date_debut:
|
||||||
|
ues_ro[0], ues_ro[1] = ues_ro[1], ues_ro[0]
|
||||||
|
# Colonnes d'UE:
|
||||||
|
for ue, ue_read_only in ues_ro:
|
||||||
H.append(
|
H.append(
|
||||||
"""<div class="niveau_vide"></div><div class="niveau_vide"></div><div class="niveau_vide"></div>"""
|
_gen_but_niveau_ue(
|
||||||
)
|
ue,
|
||||||
continue
|
deca.decisions_ues[ue.id],
|
||||||
# Semestre impair
|
disabled=read_only or ue_read_only,
|
||||||
H.append(
|
annee_prec=ue_read_only,
|
||||||
_gen_but_niveau_ue(
|
|
||||||
dec_rcue.rcue.ue_1,
|
|
||||||
deca.decisions_ues[dec_rcue.rcue.ue_1.id],
|
|
||||||
disabled=read_only,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# Semestre pair
|
|
||||||
H.append(
|
|
||||||
_gen_but_niveau_ue(
|
|
||||||
dec_rcue.rcue.ue_2,
|
|
||||||
deca.decisions_ues[dec_rcue.rcue.ue_2.id],
|
|
||||||
disabled=read_only,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# RCUE
|
|
||||||
H.append(
|
|
||||||
f"""<div class="but_niveau_rcue
|
|
||||||
{'recorded' if dec_rcue.code_valide is not None else ''}
|
|
||||||
">
|
|
||||||
<div class="but_note">{scu.fmt_note(dec_rcue.rcue.moy_rcue)}</div>
|
|
||||||
<div class="but_code">{
|
|
||||||
_gen_but_select("code_rcue_"+str(niveau.id),
|
|
||||||
dec_rcue.codes,
|
|
||||||
dec_rcue.code_valide,
|
|
||||||
disabled=True, klass="manual"
|
|
||||||
)
|
)
|
||||||
}</div>
|
)
|
||||||
</div>"""
|
|
||||||
)
|
# RCUE
|
||||||
|
if dec_rcue is None:
|
||||||
|
H.append("""<div class="niveau_vide"></div>""")
|
||||||
|
else:
|
||||||
|
H.append(
|
||||||
|
f"""<div class="but_niveau_rcue
|
||||||
|
{'recorded' if dec_rcue.code_valide is not None else ''}
|
||||||
|
">
|
||||||
|
<div class="but_note">{scu.fmt_note(dec_rcue.rcue.moy_rcue)}</div>
|
||||||
|
<div class="but_code">{
|
||||||
|
_gen_but_select("code_rcue_"+str(niveau.id),
|
||||||
|
dec_rcue.codes,
|
||||||
|
dec_rcue.code_valide,
|
||||||
|
disabled=True, klass="manual"
|
||||||
|
)
|
||||||
|
}</div>
|
||||||
|
</div>"""
|
||||||
|
)
|
||||||
H.append("</div>") # but_annee
|
H.append("</div>") # but_annee
|
||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
@ -140,6 +159,8 @@ def _gen_but_select(
|
|||||||
klass: str = "",
|
klass: str = "",
|
||||||
) -> str:
|
) -> str:
|
||||||
"Le menu html select avec les codes"
|
"Le menu html select avec les codes"
|
||||||
|
# if disabled: # mauvaise idée car le disabled est traité en JS
|
||||||
|
# return f"""<div class="but_code {klass}">{code_valide}</div>"""
|
||||||
h = "\n".join(
|
h = "\n".join(
|
||||||
[
|
[
|
||||||
f"""<option value="{code}"
|
f"""<option value="{code}"
|
||||||
@ -157,7 +178,12 @@ def _gen_but_select(
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _gen_but_niveau_ue(ue: UniteEns, dec_ue: DecisionsProposeesUE, disabled=False):
|
def _gen_but_niveau_ue(
|
||||||
|
ue: UniteEns,
|
||||||
|
dec_ue: DecisionsProposeesUE,
|
||||||
|
disabled: bool = False,
|
||||||
|
annee_prec: bool = False,
|
||||||
|
):
|
||||||
if dec_ue.ue_status and dec_ue.ue_status["is_capitalized"]:
|
if dec_ue.ue_status and dec_ue.ue_status["is_capitalized"]:
|
||||||
moy_ue_str = f"""<span class="ue_cap">{
|
moy_ue_str = f"""<span class="ue_cap">{
|
||||||
scu.fmt_note(dec_ue.moy_ue_with_cap)}</span>"""
|
scu.fmt_note(dec_ue.moy_ue_with_cap)}</span>"""
|
||||||
@ -178,6 +204,7 @@ def _gen_but_niveau_ue(ue: UniteEns, dec_ue: DecisionsProposeesUE, disabled=Fals
|
|||||||
|
|
||||||
return f"""<div class="but_niveau_ue {
|
return f"""<div class="but_niveau_ue {
|
||||||
'recorded' if dec_ue.code_valide is not None else ''}
|
'recorded' if dec_ue.code_valide is not None else ''}
|
||||||
|
{'annee_prec' if annee_prec else ''}
|
||||||
">
|
">
|
||||||
<div title="{ue.titre}">{ue.acronyme}</div>
|
<div title="{ue.titre}">{ue.acronyme}</div>
|
||||||
<div class="but_note with_scoplement">
|
<div class="but_note with_scoplement">
|
||||||
|
@ -58,7 +58,7 @@ class ScolarFormSemestreValidation(db.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"{self.__class__.__name__}({self.formsemestre_id}, {self.etudid}, code={self.code}, ue={self.ue}, moy_ue={self.moy_ue})"
|
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):
|
def __str__(self):
|
||||||
if self.ue_id:
|
if self.ue_id:
|
||||||
|
@ -122,17 +122,29 @@ select:invalid {
|
|||||||
background: red;
|
background: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
select.but_code option.recorded {
|
select.but_code option.recorded,
|
||||||
|
div.but_code recorded {
|
||||||
color: rgb(3, 157, 3);
|
color: rgb(3, 157, 3);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.but_code {
|
||||||
|
font-weight: bold;
|
||||||
|
color: blue;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
div.but_niveau_ue.recorded,
|
div.but_niveau_ue.recorded,
|
||||||
div.but_niveau_rcue.recorded {
|
div.but_niveau_rcue.recorded {
|
||||||
border-color: rgb(136, 252, 136);
|
border-color: rgb(136, 252, 136);
|
||||||
border-width: 2px;
|
border-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.but_niveau_ue.annee_prec {
|
||||||
|
background-color: rgb(167, 167, 0);
|
||||||
|
}
|
||||||
|
|
||||||
div.but_niveau_ue.modified {
|
div.but_niveau_ue.modified {
|
||||||
background-color: rgb(255, 214, 254);
|
background-color: rgb(255, 214, 254);
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,9 @@ def test_but_jury_GMP_lm(test_client):
|
|||||||
# puis lance les tests de jury
|
# puis lance les tests de jury
|
||||||
doc = yaml_setup.setup_from_yaml("tests/unit/cursus_but_gmp_iutlm.yaml")
|
doc = yaml_setup.setup_from_yaml("tests/unit/cursus_but_gmp_iutlm.yaml")
|
||||||
|
|
||||||
formsemestres = FormSemestre.query.order_by(FormSemestre.semestre_id).all()
|
formsemestres = FormSemestre.query.order_by(
|
||||||
|
FormSemestre.date_debut, FormSemestre.semestre_id
|
||||||
|
).all()
|
||||||
# Vérifie les deca de tous les semestres:
|
# Vérifie les deca de tous les semestres:
|
||||||
for formsemestre in formsemestres:
|
for formsemestre in formsemestres:
|
||||||
_check_deca(formsemestre)
|
_check_deca(formsemestre)
|
||||||
@ -68,7 +70,6 @@ def test_but_jury_GMP_lm(test_client):
|
|||||||
# Saisie de toutes les décisions de jury
|
# Saisie de toutes les décisions de jury
|
||||||
for formsemestre in formsemestres:
|
for formsemestre in formsemestres:
|
||||||
formsemestre_validation_auto_but(formsemestre, only_adm=False)
|
formsemestre_validation_auto_but(formsemestre, only_adm=False)
|
||||||
|
|
||||||
# Vérifie résultats attendus:
|
# Vérifie résultats attendus:
|
||||||
for formsemestre in formsemestres:
|
for formsemestre in formsemestres:
|
||||||
_test_but_jury(formsemestre, doc)
|
_test_but_jury(formsemestre, doc)
|
||||||
|
Loading…
Reference in New Issue
Block a user