diff --git a/app/api/formations.py b/app/api/formations.py index d131184b..bce8861b 100644 --- a/app/api/formations.py +++ b/app/api/formations.py @@ -328,14 +328,12 @@ def desassoc_ue_niveau(ue_id: int): if g.scodoc_dept: query = query.join(Formation).filter_by(dept_id=g.scodoc_dept_id) ue: UniteEns = query.first_or_404() - ue.niveau_competence = None - db.session.add(ue) - db.session.commit() - # Invalidation du cache - ue.formation.invalidate_cached_sems() - log(f"desassoc_ue_niveau: {ue}") - if g.scodoc_dept: - # "usage web" + ok, error_message = ue.set_niveau_competence(None) + if not ok: + if g.scodoc_dept: # "usage web" + flash(error_message, "error") + return json_error(404, error_message) + if g.scodoc_dept: # "usage web" flash(f"UE {ue.acronyme} dé-associée") return {"status": 0} diff --git a/app/models/but_validations.py b/app/models/but_validations.py index 31b7f630..bd1aea00 100644 --- a/app/models/but_validations.py +++ b/app/models/but_validations.py @@ -250,15 +250,19 @@ def _build_decisions_rcue_list(decisions_rcue: dict) -> list[str]: validation_by_competence = defaultdict(list) for validation in decisions_rcue: if validation: + # Attention, certaines validations de RCUE peuvent ne plus être associées + # à un niveau de compétence si l'UE a été déassociée (ce qui ne devrait pas être fait) competence_id = ( - validation.get("niveau", {}).get("competence", {}).get("id_orebut") - ) + (validation.get("niveau") or {}).get("competence") or {} + ).get("id_orebut") validation_by_competence[competence_id].append(validation) # Tri des listes de validation par numéro de compétence validations_niveaux = sorted( validation_by_competence.values(), key=lambda v: ( - v[0].get("niveau", {}).get("competence", {}).get("numero", 0) if v else -1 + ((v[0].get("niveau") or {}).get("competence") or {}).get("numero", 0) + if v + else -1 ), ) titres_rcues = [] @@ -266,14 +270,14 @@ def _build_decisions_rcue_list(decisions_rcue: dict) -> list[str]: for validations in validations_niveaux: if validations: v = validations[0] - titre_competence = ( - v.get("niveau", {}).get("competence", {}).get("titre", "sans titre") + titre_competence = ((v.get("niveau") or {}).get("competence", {})).get( + "titre", "sans titre ! A vérifier !" ) titres_rcues.append( f"""{titre_competence} : """ + ", ".join( [ - f"niveau {v.get('niveau',empty).get('ordre','?')} {v.get('code', '?')}" + f"niveau {((v.get('niveau') or empty).get('ordre') or '?')} {v.get('code', '?')}" for v in validations ] ) diff --git a/app/models/ues.py b/app/models/ues.py index ae3653dd..e5aa8445 100644 --- a/app/models/ues.py +++ b/app/models/ues.py @@ -396,7 +396,21 @@ class UniteEns(models.ScoDocModel): return True, "" - def set_niveau_competence(self, niveau: ApcNiveau) -> tuple[bool, str]: + def is_used_in_validation_rcue(self) -> bool: + """Vrai si cette UE est utilisée dans une validation enregistrée d'RCUE.""" + from app.models.but_validations import ApcValidationRCUE + + return ( + ApcValidationRCUE.query.filter( + db.or_( + ApcValidationRCUE.ue1_id == self.id, + ApcValidationRCUE.ue2_id == self.id, + ) + ).count() + > 0 + ) + + def set_niveau_competence(self, niveau: ApcNiveau | None) -> tuple[bool, str]: """Associe cette UE au niveau de compétence indiqué. Le niveau doit être dans l'un des parcours de l'UE (si elle n'est pas de tronc commun). @@ -404,7 +418,12 @@ class UniteEns(models.ScoDocModel): Sinon, raises ScoFormationConflict. Si niveau est None, désassocie. - Returns True if (de)association done, False on error. + + Si l'UE est utilisée dans un validation de RCUE, on ne peut plus la changer de niveau. + + Returns + - True if (de)association done, False on error. + - Error message (string) """ # Sanity checks if not self.formation.referentiel_competence: @@ -412,6 +431,12 @@ class UniteEns(models.ScoDocModel): False, "La formation n'est pas associée à un référentiel de compétences", ) + # UE utilisée dans des validations RCUE ? + if self.is_used_in_validation_rcue(): + return ( + False, + "UE utilisée dans un RCUE validé: son niveau ne peut plus être modifié", + ) if niveau is not None: if self.niveau_competence_id is not None: return (