diff --git a/app/but/jury_but.py b/app/but/jury_but.py index 5c3d04934..1d8c08fce 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -87,6 +87,7 @@ from app.models.formsemestre import FormSemestre, FormSemestreInscription from app.models.ues import UniteEns from app.models.validations import ScolarFormSemestreValidation 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.sco_exceptions import ScoException, ScoValueError @@ -211,13 +212,17 @@ class DecisionsProposeesAnnee(DecisionsProposees): for ue in self.ues_pair } ) - assert self.parcour is not None self.rcues_annee = self.compute_rcues_annee() "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.parcour, self.annee_but - ).all() # XXX à trier, selon l'ordre des UE associées ? + self.parcour, self.annee_but, formation.referentiel_competence + ).all() # non triés "liste des niveaux de compétences associés à cette année" self.decisions_rcue_by_niveau = self.compute_decisions_niveaux() "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 if res.etuds_parcour_id[etudid] is None: # 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: parcour = ApcParcours.query.get(res.etuds_parcour_id[etudid]) if parcour is not None: @@ -402,11 +407,12 @@ class DecisionsProposeesAnnee(DecisionsProposees): if rc.ue_1.niveau_competence_id == niveau.id: rcue = rc break - dec_rcue = DecisionsProposeesRCUE(self, rcue) - rc_niveaux.append((dec_rcue, niveau.id)) - # prévient les UE concernées :-) - self.decisions_ues[dec_rcue.rcue.ue_1.id].set_rcue(dec_rcue.rcue) - self.decisions_ues[dec_rcue.rcue.ue_2.id].set_rcue(dec_rcue.rcue) + if rcue is not None: + dec_rcue = DecisionsProposeesRCUE(self, rcue) + rc_niveaux.append((dec_rcue, niveau.id)) + # prévient les UE concernées :-) + self.decisions_ues[dec_rcue.rcue.ue_1.id].set_rcue(dec_rcue.rcue) + self.decisions_ues[dec_rcue.rcue.ue_2.id].set_rcue(dec_rcue.rcue) # Ordonne par numéro d'UE rc_niveaux.sort(key=lambda x: x[0].rcue.ue_1.numero) decisions_rcue_by_niveau = {x[1]: x[0] for x in rc_niveaux} @@ -458,7 +464,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): def record(self, code: str): """Enregistre le code""" - if not code in self.codes: + if code and not code in self.codes: raise ScoValueError( f"code annee {html.escape(code)} invalide pour formsemestre {html.escape(self.formsemestre)}" ) @@ -467,20 +473,22 @@ class DecisionsProposeesAnnee(DecisionsProposees): if self.validation: db.session.delete(self.validation) db.session.flush() - - self.validation = ApcValidationAnnee( - etudid=self.etud.id, - formsemestre=self.formsemestre_impair, - ordre=self.annee_but, - annee_scolaire=self.annee_scolaire(), - code=code, - ) - Scolog.logdb( - method="jury_but", - etudid=self.etud.id, - msg=f"Validation année BUT{self.annee_but}: {code}", - ) - db.session.add(self.validation) + if code is None: + self.validation = None + else: + self.validation = ApcValidationAnnee( + etudid=self.etud.id, + formsemestre=self.formsemestre_impair, + ordre=self.annee_but, + annee_scolaire=self.annee_scolaire(), + code=code, + ) + Scolog.logdb( + method="jury_but", + etudid=self.etud.id, + msg=f"Validation année BUT{self.annee_but}: {code}", + ) + db.session.add(self.validation) self.recorded = True def record_all(self): @@ -494,7 +502,10 @@ class DecisionsProposeesAnnee(DecisionsProposees): ) for dec in decisions: 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): @@ -516,6 +527,9 @@ class DecisionsProposeesRCUE(DecisionsProposees): ): super().__init__(etud=dec_prop_annee.etud) self.rcue = rcue + if rcue is None: # RCUE non dispo, eg un seul semestre + self.codes = [] + return self.parcour = dec_prop_annee.parcour self.validation = rcue.query_validations().first() if self.validation is not None: @@ -529,7 +543,7 @@ class DecisionsProposeesRCUE(DecisionsProposees): def record(self, code: str): """Enregistre le code""" - if not code in self.codes: + if code and not code in self.codes: raise ScoValueError( f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}" ) @@ -539,20 +553,23 @@ class DecisionsProposeesRCUE(DecisionsProposees): if self.validation: db.session.delete(self.validation) db.session.flush() - self.validation = ApcValidationRCUE( - etudid=self.etud.id, - formsemestre_id=self.rcue.formsemestre_2.id, - ue1_id=self.rcue.ue_1.id, - ue2_id=self.rcue.ue_2.id, - parcours_id=parcours_id, - code=code, - ) - Scolog.logdb( - method="jury_but", - etudid=self.etud.id, - msg=f"Validation RCUE {repr(self.rcue)}", - ) - db.session.add(self.validation) + if code is None: + self.validation = None + else: + self.validation = ApcValidationRCUE( + etudid=self.etud.id, + formsemestre_id=self.rcue.formsemestre_2.id, + ue1_id=self.rcue.ue_1.id, + ue2_id=self.rcue.ue_2.id, + parcours_id=parcours_id, + code=code, + ) + Scolog.logdb( + method="jury_but", + etudid=self.etud.id, + msg=f"Validation RCUE {repr(self.rcue)}", + ) + db.session.add(self.validation) self.recorded = True @@ -633,7 +650,7 @@ class DecisionsProposeesUE(DecisionsProposees): def record(self, code: str): """Enregistre le code""" - if not code in self.codes: + if code and not code in self.codes: raise ScoValueError( f"code UE invalide pour ue_id={self.ue.id}: {html.escape(code)}" ) @@ -642,18 +659,21 @@ class DecisionsProposeesUE(DecisionsProposees): if self.validation: db.session.delete(self.validation) db.session.flush() - self.validation = ScolarFormSemestreValidation( - etudid=self.etud.id, - formsemestre_id=self.formsemestre.id, - ue_id=self.ue.id, - code=code, - ) - Scolog.logdb( - method="jury_but", - etudid=self.etud.id, - msg=f"Validation UE {self.ue.id}", - ) - db.session.add(self.validation) + if code is None: + self.validation = None + else: + self.validation = ScolarFormSemestreValidation( + etudid=self.etud.id, + formsemestre_id=self.formsemestre.id, + ue_id=self.ue.id, + code=code, + ) + Scolog.logdb( + method="jury_but", + etudid=self.etud.id, + msg=f"Validation UE {self.ue.id}", + ) + db.session.add(self.validation) self.recorded = True diff --git a/app/comp/res_but.py b/app/comp/res_but.py index b139a6ab0..10eef7c67 100644 --- a/app/comp/res_but.py +++ b/app/comp/res_but.py @@ -228,5 +228,5 @@ class ResultatsSemestreBUT(NotesTableCompat): return s.index[s.notna()] 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)) diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index da697baa4..5db968e3d 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -270,21 +270,33 @@ class ApcNiveau(db.Model, XMLModel): @classmethod def niveaux_annee_de_parcours( - cls, parcour: "ApcParcours", annee: int + cls, + parcour: "ApcParcours", + annee: int, + referentiel_competence: ApcReferentielCompetences = None, ) -> 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}: raise ValueError("annee invalide pour un parcours BUT") annee_formation = f"BUT{annee}" - return ApcNiveau.query.filter( - ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id, - ApcParcours.id == ApcAnneeParcours.parcours_id, - ApcParcours.referentiel == parcour.referentiel, - ApcParcoursNiveauCompetence.competence_id == ApcCompetence.id, - ApcCompetence.id == ApcNiveau.competence_id, - ApcAnneeParcours.parcours == parcour, - ApcNiveau.annee == annee_formation, - ) + 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( + ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id, + ApcParcours.id == ApcAnneeParcours.parcours_id, + ApcParcours.referentiel == parcour.referentiel, + ApcParcoursNiveauCompetence.competence_id == ApcCompetence.id, + ApcCompetence.id == ApcNiveau.competence_id, + ApcAnneeParcours.parcours == parcour, + ApcNiveau.annee == annee_formation, + ) app_critiques_modules = db.Table( diff --git a/app/models/formations.py b/app/models/formations.py index 9f460cff8..f35a48ce6 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -18,6 +18,7 @@ from app.models.ues import UniteEns from app.scodoc import sco_cache from app.scodoc import sco_codes_parcours from app.scodoc import sco_utils as scu +from app.scodoc.sco_codes_parcours import UE_STANDARD class Formation(db.Model): @@ -166,6 +167,7 @@ class Formation(db.Model): """ return UniteEns.query.filter_by(formation=self).filter( UniteEns.niveau_competence_id == ApcNiveau.id, + UniteEns.type == UE_STANDARD, ApcParcoursNiveauCompetence.competence_id == ApcNiveau.competence_id, ApcParcoursNiveauCompetence.annee_parcours_id == ApcAnneeParcours.id, ApcAnneeParcours.parcours_id == parcour.id, diff --git a/app/views/notes.py b/app/views/notes.py index c43eb6366..ddb83f56b 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -2248,6 +2248,8 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int): etud = Identite.query.get_or_404(etudid) res: ResultatsSemestreBUT = res_sem.load_formsemestre_results(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": deca.record_form(request.form) flash("codes enregistrés") @@ -2264,7 +2266,7 @@ def formsemestre_validation_but(formsemestre_id: int, etudid: int):