diff --git a/app/but/jury_but.py b/app/but/jury_but.py index f8611914d..e75a6fb0a 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -91,7 +91,12 @@ from app.models.ues import UniteEns from app.models.validations import ScolarFormSemestreValidation from app.scodoc import sco_cache from app.scodoc import sco_codes_parcours as sco_codes -from app.scodoc.sco_codes_parcours import CODES_UE_VALIDES, RED, UE_STANDARD +from app.scodoc.sco_codes_parcours import ( + CODES_RCUE_VALIDES, + CODES_UE_VALIDES, + RED, + UE_STANDARD, +) from app.scodoc import sco_utils as scu from app.scodoc.sco_exceptions import ScoNoReferentielCompetences, ScoValueError @@ -365,7 +370,6 @@ class DecisionsProposeesAnnee(DecisionsProposees): "s" if plural else ""} sur {self.nb_competences}""" if self.admis: self.codes = [sco_codes.ADM] + self.codes - self.explanation = expl_rcues # elif not self.jury_annuel: # self.codes = [] # pas de décision annuelle sur semestres impairs elif self.inscription_etat != scu.INSCRIT: @@ -381,9 +385,9 @@ class DecisionsProposeesAnnee(DecisionsProposees): sco_codes.ABL, sco_codes.EXCLU, ] + expl_rcues = "" elif self.passage_de_droit: self.codes = [sco_codes.PASD, sco_codes.ADJ] + self.codes - self.explanation = expl_rcues elif self.valide_moitie_rcue: # mais au moins 1 rcue insuffisante self.codes = [ sco_codes.RED, @@ -391,7 +395,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): sco_codes.PAS1NCI, sco_codes.ADJ, ] + self.codes - self.explanation = expl_rcues + f" et {self.nb_rcues_under_8} < 8" + expl_rcues += f" et {self.nb_rcues_under_8} < 8" else: self.codes = [ sco_codes.RED, @@ -400,17 +404,21 @@ class DecisionsProposeesAnnee(DecisionsProposees): sco_codes.ADJ, sco_codes.PASD, # voir #488 (discutable, conventions locales) ] + self.codes - self.explanation = ( - expl_rcues - + f""" et {self.nb_rcues_under_8} - niveau{'x' if self.nb_rcues_under_8 > 1 else ''} < 8""" - ) + expl_rcues += f""" et {self.nb_rcues_under_8} niveau{'x' if self.nb_rcues_under_8 > 1 else ''} < 8""" + # Si l'un des semestres est extérieur, propose ADM if ( self.formsemestre_impair and self.formsemestre_impair.modalite == "EXT" ) or (self.formsemestre_pair and self.formsemestre_pair.modalite == "EXT"): self.codes.insert(0, sco_codes.ADM) - + self.explanation = f"
{expl_rcues}
" + messages = self.descr_pb_coherence() + if messages: + self.explanation += ( + '
' + + '
'.join(messages) + + "
" + ) # def infos(self) -> str: @@ -670,16 +678,16 @@ class DecisionsProposeesAnnee(DecisionsProposees): # Code annuel code_annee = code - with sco_cache.DeferredSemCacheManager(): - # Enregistre les codes, dans l'ordre UE, RCUE, Année - for dec_ue, code in codes_ues: - dec_ue.record(code) - for dec_rcue, code in codes_rcues: - dec_rcue.record(code) - self.record(code_annee) - self.record_all() + with sco_cache.DeferredSemCacheManager(): + # Enregistre les codes, dans l'ordre UE, RCUE, Année + for dec_ue, code in codes_ues: + dec_ue.record(code) + for dec_rcue, code in codes_rcues: + dec_rcue.record(code) + self.record(code_annee) + self.record_all() - db.session.commit() + db.session.commit() def record(self, code: str, no_overwrite=False): """Enregistre le code de l'année, et au besoin l'autorisation d'inscription. @@ -697,7 +705,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): return # no change if self.validation: db.session.delete(self.validation) - db.session.flush() + db.session.commit() if code is None: self.validation = None else: @@ -708,12 +716,14 @@ class DecisionsProposeesAnnee(DecisionsProposees): annee_scolaire=self.annee_scolaire(), code=code, ) + db.session.add(self.validation) + db.session.commit() Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation année BUT{self.annee_but}: {code}", ) - db.session.add(self.validation) + # --- Autorisation d'inscription dans semestre suivant ? if self.formsemestre_pair is not None: if code is None: @@ -796,13 +806,14 @@ class DecisionsProposeesAnnee(DecisionsProposees): ordre=self.annee_but, ) for validation in validations: + db.session.delete(validation) Scolog.logdb( "jury_but", etudid=self.etud.id, msg=f"Validation année BUT{self.annee_but}: effacée", ) - db.session.delete(validation) - # Efface éventuelle validation de semestre + + # Efface éventuelles validations de semestre # (en principe inutilisées en BUT) # et autres UEs (en cas de changement d'architecture de formation depuis le jury ?) # @@ -811,7 +822,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): ): db.session.delete(validation) - db.session.flush() + db.session.commit() self.invalidate_formsemestre_cache() def get_autorisations_passage(self) -> list[int]: @@ -854,6 +865,27 @@ class DecisionsProposeesAnnee(DecisionsProposees): validations.append(", ".join(v for v in valids if v)) return line_sep.join(validations) + def descr_pb_coherence(self) -> list[str]: + """Description d'éventuels problèmes de cohérence entre + les décisions *enregistrées* d'UE et de RCUE. + Note: en principe, la cohérence RCUE/UE est assurée au moment de + l'enregistrement (record). + Mais la base peut avoir été modifiée par d'autres voies. + """ + messages = [] + for dec_rcue in self.decisions_rcue_by_niveau.values(): + if dec_rcue.code_valide in CODES_RCUE_VALIDES: + for ue in (dec_rcue.rcue.ue_1, dec_rcue.rcue.ue_2): + dec_ue = self.decisions_ues.get(ue.id) + if dec_ue: + if dec_ue.code_valide not in CODES_UE_VALIDES: + messages.append( + f"L'UE {ue.acronyme} n'est pas validée mais son RCUE l'est !" + ) + else: + messages.append(f"L'UE {ue.acronyme} n'a pas décision (???)") + return messages + def list_ue_parcour_etud( formsemestre: FormSemestre, etud: Identite, res: ResultatsSemestreBUT @@ -969,7 +1001,7 @@ class DecisionsProposeesRCUE(DecisionsProposees): parcours_id = self.parcour.id if self.parcour is not None else None if self.validation: db.session.delete(self.validation) - db.session.flush() + db.session.commit() if code is None: self.validation = None else: @@ -984,12 +1016,14 @@ class DecisionsProposeesRCUE(DecisionsProposees): parcours_id=parcours_id, code=code, ) + db.session.add(self.validation) + db.session.commit() Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation {self.rcue}: {code}", + commit=True, ) - db.session.add(self.validation) # Modifie au besoin les codes d'UE if code == "ADJ": deca = self.deca @@ -1156,6 +1190,7 @@ class DecisionsProposeesUE(DecisionsProposees): method="jury_but", etudid=self.etud.id, msg=f"Validation UE {self.ue.id} {self.ue.acronyme}: effacée", + commit=True, ) else: self.validation = ScolarFormSemestreValidation( @@ -1165,12 +1200,14 @@ class DecisionsProposeesUE(DecisionsProposees): code=code, moy_ue=self.moy_ue, ) + db.session.add(self.validation) + db.session.commit() Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation UE {self.ue.id} {self.ue.acronyme}({self.moy_ue}): {code}", + commit=True, ) - db.session.add(self.validation) log(f"DecisionsProposeesUE: recording {self.validation}") sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre.id) @@ -1185,13 +1222,14 @@ class DecisionsProposeesUE(DecisionsProposees): ) for validation in validations: log(f"DecisionsProposeesUE: deleting {validation}") + db.session.delete(validation) Scolog.logdb( method="jury_but", etudid=self.etud.id, msg=f"Validation UE {validation.ue.id} {validation.ue.acronyme}: effacée", ) - db.session.delete(validation) - db.session.flush() + + db.session.commit() def descr_validation(self) -> str: """Description validation niveau enregistrée, pour PV jury.