WIP: jurys BUT: force jury annuel (en attendant page dédiée pour semestres isolés)

This commit is contained in:
Emmanuel Viennet 2022-06-22 14:09:08 +02:00
parent c17e2bae47
commit 0939feb9fc
5 changed files with 102 additions and 73 deletions

View File

@ -87,6 +87,7 @@ from app.models.formsemestre import FormSemestre, FormSemestreInscription
from app.models.ues import UniteEns from app.models.ues import UniteEns
from app.models.validations import ScolarFormSemestreValidation from app.models.validations import ScolarFormSemestreValidation
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 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
@ -211,13 +212,17 @@ class DecisionsProposeesAnnee(DecisionsProposees):
for ue in self.ues_pair for ue in self.ues_pair
} }
) )
assert self.parcour is not None
self.rcues_annee = self.compute_rcues_annee() self.rcues_annee = self.compute_rcues_annee()
"RCUEs de l'année" "RCUEs de l'année"
formation = (
self.formsemestre_impair.formation
if self.formsemestre_impair
else self.formsemestre_pair.formation
)
self.niveaux_competences = ApcNiveau.niveaux_annee_de_parcours( self.niveaux_competences = ApcNiveau.niveaux_annee_de_parcours(
self.parcour, self.annee_but self.parcour, self.annee_but, formation.referentiel_competence
).all() # XXX à trier, selon l'ordre des UE associées ? ).all() # non triés
"liste des niveaux de compétences associés à cette année" "liste des niveaux de compétences associés à cette année"
self.decisions_rcue_by_niveau = self.compute_decisions_niveaux() self.decisions_rcue_by_niveau = self.compute_decisions_niveaux()
"les décisions rcue associées aux niveau_id" "les décisions rcue associées aux niveau_id"
@ -327,7 +332,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
# Parcour dans lequel l'étudiant est inscrit, et liste des UEs # Parcour dans lequel l'étudiant est inscrit, et liste des UEs
if res.etuds_parcour_id[etudid] is None: if res.etuds_parcour_id[etudid] is None:
# pas de parcour: prend toutes les UEs (non bonus) # pas de parcour: prend toutes les UEs (non bonus)
ues = list(res.etud_ues(etudid)) ues = [ue for ue in res.etud_ues(etudid) if ue.type == UE_STANDARD]
else: else:
parcour = ApcParcours.query.get(res.etuds_parcour_id[etudid]) parcour = ApcParcours.query.get(res.etuds_parcour_id[etudid])
if parcour is not None: if parcour is not None:
@ -402,6 +407,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
if rc.ue_1.niveau_competence_id == niveau.id: if rc.ue_1.niveau_competence_id == niveau.id:
rcue = rc rcue = rc
break break
if rcue is not None:
dec_rcue = DecisionsProposeesRCUE(self, rcue) dec_rcue = DecisionsProposeesRCUE(self, rcue)
rc_niveaux.append((dec_rcue, niveau.id)) rc_niveaux.append((dec_rcue, niveau.id))
# prévient les UE concernées :-) # prévient les UE concernées :-)
@ -458,7 +464,7 @@ class DecisionsProposeesAnnee(DecisionsProposees):
def record(self, code: str): def record(self, code: str):
"""Enregistre le code""" """Enregistre le code"""
if not code in self.codes: if code and not code in self.codes:
raise ScoValueError( raise ScoValueError(
f"code annee <tt>{html.escape(code)}</tt> invalide pour formsemestre {html.escape(self.formsemestre)}" f"code annee <tt>{html.escape(code)}</tt> invalide pour formsemestre {html.escape(self.formsemestre)}"
) )
@ -467,7 +473,9 @@ class DecisionsProposeesAnnee(DecisionsProposees):
if self.validation: if self.validation:
db.session.delete(self.validation) db.session.delete(self.validation)
db.session.flush() db.session.flush()
if code is None:
self.validation = None
else:
self.validation = ApcValidationAnnee( self.validation = ApcValidationAnnee(
etudid=self.etud.id, etudid=self.etud.id,
formsemestre=self.formsemestre_impair, formsemestre=self.formsemestre_impair,
@ -494,7 +502,10 @@ class DecisionsProposeesAnnee(DecisionsProposees):
) )
for dec in decisions: for dec in decisions:
if not dec.recorded: if not dec.recorded:
dec.record(dec.codes[0]) # rappel: le code par défaut est en tête # rappel: le code par défaut est en tête
code = dec.codes[0] if dec.codes else None
# s'il n'y a pas de codee, efface
dec.record(dec.codes[0])
class DecisionsProposeesRCUE(DecisionsProposees): class DecisionsProposeesRCUE(DecisionsProposees):
@ -516,6 +527,9 @@ class DecisionsProposeesRCUE(DecisionsProposees):
): ):
super().__init__(etud=dec_prop_annee.etud) super().__init__(etud=dec_prop_annee.etud)
self.rcue = rcue self.rcue = rcue
if rcue is None: # RCUE non dispo, eg un seul semestre
self.codes = []
return
self.parcour = dec_prop_annee.parcour self.parcour = dec_prop_annee.parcour
self.validation = rcue.query_validations().first() self.validation = rcue.query_validations().first()
if self.validation is not None: if self.validation is not None:
@ -529,7 +543,7 @@ class DecisionsProposeesRCUE(DecisionsProposees):
def record(self, code: str): def record(self, code: str):
"""Enregistre le code""" """Enregistre le code"""
if not code in self.codes: if code and not code in self.codes:
raise ScoValueError( raise ScoValueError(
f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}" f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}"
) )
@ -539,6 +553,9 @@ class DecisionsProposeesRCUE(DecisionsProposees):
if self.validation: if self.validation:
db.session.delete(self.validation) db.session.delete(self.validation)
db.session.flush() db.session.flush()
if code is None:
self.validation = None
else:
self.validation = ApcValidationRCUE( self.validation = ApcValidationRCUE(
etudid=self.etud.id, etudid=self.etud.id,
formsemestre_id=self.rcue.formsemestre_2.id, formsemestre_id=self.rcue.formsemestre_2.id,
@ -633,7 +650,7 @@ class DecisionsProposeesUE(DecisionsProposees):
def record(self, code: str): def record(self, code: str):
"""Enregistre le code""" """Enregistre le code"""
if not code in self.codes: if code and not code in self.codes:
raise ScoValueError( raise ScoValueError(
f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}" f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}"
) )
@ -642,6 +659,9 @@ class DecisionsProposeesUE(DecisionsProposees):
if self.validation: if self.validation:
db.session.delete(self.validation) db.session.delete(self.validation)
db.session.flush() db.session.flush()
if code is None:
self.validation = None
else:
self.validation = ScolarFormSemestreValidation( self.validation = ScolarFormSemestreValidation(
etudid=self.etud.id, etudid=self.etud.id,
formsemestre_id=self.formsemestre.id, formsemestre_id=self.formsemestre.id,

View File

@ -228,5 +228,5 @@ class ResultatsSemestreBUT(NotesTableCompat):
return s.index[s.notna()] return s.index[s.notna()]
def etud_ues(self, etudid: int) -> Generator[UniteEns]: def etud_ues(self, etudid: int) -> Generator[UniteEns]:
"""Liste des UE auxquelles l'étudiant est inscrit (sans bonus).""" """Liste des UE auxquelles l'étudiant est inscrit."""
return (UniteEns.query.get(ue_id) for ue_id in self.etud_ues_ids(etudid)) return (UniteEns.query.get(ue_id) for ue_id in self.etud_ues_ids(etudid))

View File

@ -270,12 +270,24 @@ class ApcNiveau(db.Model, XMLModel):
@classmethod @classmethod
def niveaux_annee_de_parcours( def niveaux_annee_de_parcours(
cls, parcour: "ApcParcours", annee: int cls,
parcour: "ApcParcours",
annee: int,
referentiel_competence: ApcReferentielCompetences = None,
) -> flask_sqlalchemy.BaseQuery: ) -> flask_sqlalchemy.BaseQuery:
"""Les niveaux de l'année du parcours""" """Les niveaux de l'année du parcours
Si le parcour est None, tous les niveaux de l'année
"""
if annee not in {1, 2, 3}: if annee not in {1, 2, 3}:
raise ValueError("annee invalide pour un parcours BUT") raise ValueError("annee invalide pour un parcours BUT")
annee_formation = f"BUT{annee}" annee_formation = f"BUT{annee}"
if parcour is None:
return ApcNiveau.query.filter(
ApcNiveau.annee == annee_formation,
ApcCompetence.id == ApcNiveau.competence_id,
ApcCompetence.referentiel_id == referentiel_competence.id,
)
else:
return ApcNiveau.query.filter( return ApcNiveau.query.filter(
ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id, ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id,
ApcParcours.id == ApcAnneeParcours.parcours_id, ApcParcours.id == ApcAnneeParcours.parcours_id,

View File

@ -18,6 +18,7 @@ from app.models.ues import UniteEns
from app.scodoc import sco_cache from app.scodoc import sco_cache
from app.scodoc import sco_codes_parcours from app.scodoc import sco_codes_parcours
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
from app.scodoc.sco_codes_parcours import UE_STANDARD
class Formation(db.Model): class Formation(db.Model):
@ -166,6 +167,7 @@ class Formation(db.Model):
""" """
return UniteEns.query.filter_by(formation=self).filter( return UniteEns.query.filter_by(formation=self).filter(
UniteEns.niveau_competence_id == ApcNiveau.id, UniteEns.niveau_competence_id == ApcNiveau.id,
UniteEns.type == UE_STANDARD,
ApcParcoursNiveauCompetence.competence_id == ApcNiveau.competence_id, ApcParcoursNiveauCompetence.competence_id == ApcNiveau.competence_id,
ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id, ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id,
ApcAnneeParcours.parcours_id == parcour.id, ApcAnneeParcours.parcours_id == parcour.id,

View File

@ -2248,6 +2248,8 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
etud = Identite.query.get_or_404(etudid) etud = Identite.query.get_or_404(etudid)
res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre) res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(formsemestre)
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre) deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
if len(deca.rcues_annee) == 0:
raise ScoValueError("année incomplète: pas de jury BUT annuel possible")
if request.method == "POST": if request.method == "POST":
deca.record_form(request.form) deca.record_form(request.form)
flash("codes enregistrés") flash("codes enregistrés")
@ -2264,7 +2266,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
<form method="POST"> <form method="POST">
<div class="titre_parcours"> <div class="titre_parcours">
<h2>Jury BUT{deca.annee_but} <h2>Jury BUT{deca.annee_but}
- Parcours {deca.parcour.libelle or "non spécifié"} - Parcours {(deca.parcour.libelle if deca.parcour else False) or "non spécifié"}
- {deca.annee_scolaire_str()}</h2> - {deca.annee_scolaire_str()}</h2>
</div> </div>
<div class="but_section_annee"> <div class="but_section_annee">
@ -2339,13 +2341,6 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):
) )
H.append("</form>") # but_annee H.append("</form>") # but_annee
# ---- Toutes les UEs, pour infos
H.append(f"<ul>")
for ue in formsemestre.query_ues(): # volontairement toutes les UE
dec_proposee = jury_but.DecisionsProposeesUE(etud, formsemestre, ue)
H.append("<li>" + html.escape(f"""{ue} : {dec_proposee}""") + "</li>")
H.append(f"</ul>")
H.append(f"</div>")
return "\n".join(H) + html_sco_header.sco_footer() return "\n".join(H) + html_sco_header.sco_footer()