diff --git a/app/but/jury_but.py b/app/but/jury_but.py
index 244989edda..af1eed4ff8 100644
--- a/app/but/jury_but.py
+++ b/app/but/jury_but.py
@@ -630,19 +630,38 @@ class DecisionsProposeesAnnee(DecisionsProposees):
d[dec_rcue.rcue.ue_2.id] = dec_rcue
return d
- def next_annee_semestre_id(self, code: str) -> int:
- """L'indice du semestre dans lequel l'étudiant est autorisé à
- poursuivre l'année suivante. None si aucun."""
- if self.formsemestre_pair is None:
- return None # seulement sur année
- if code == RED:
- return self.formsemestre_pair.semestre_id - 1
- elif (
- code in sco_codes.BUT_CODES_PASSAGE
+ def next_semestre_ids(self, code: str) -> set[int]:
+ """Les indices des semestres dans lequels l'étudiant est autorisé
+ à poursuivre après le semestre courant.
+ """
+ ids = set()
+ # La poursuite d'études dans un semestre pair d’une même année
+ # est de droit pour tout étudiant:
+ if (self.formsemestre.semestre_id % 2) and sco_codes.ParcoursBUT.NB_SEM:
+ ids.add(self.formsemestre.semestre_id + 1)
+
+ # La poursuite d’études dans un semestre impair est possible si
+ # et seulement si l’étudiant a obtenu :
+ # - la moyenne à plus de la moitié des regroupements cohérents d’UE ;
+ # - et une moyenne égale ou supérieure à 8 sur 20 à chaque RCUE.
+ #
+ # La condition a paru trop stricte à de nombreux collègues.
+ # ScoDoc ne contraint donc pas à la respecter strictement.
+ # Si le code est dans BUT_CODES_PASSAGE (ADM, ADJ, PASD, PAS1NCI, ATJ),
+ # autorise à passer dans le semestre suivant
+ if (
+ self.jury_annuel
+ and code in sco_codes.BUT_CODES_PASSAGE
and self.formsemestre_pair.semestre_id < sco_codes.ParcoursBUT.NB_SEM
):
- return self.formsemestre_pair.semestre_id + 1
- return None
+ ids.add(self.formsemestre.semestre_id + 1)
+
+ if code == RED:
+ ids.add(
+ self.formsemestre.semestre_id - (self.formsemestre.semestre_id + 1) % 2
+ )
+
+ return ids
def record_form(self, form: dict):
"""Enregistre les codes de jury en base
@@ -704,47 +723,43 @@ class DecisionsProposeesAnnee(DecisionsProposees):
raise ScoValueError(
f"code annee {html.escape(code)} invalide pour formsemestre {html.escape(self.formsemestre)}"
)
- if code == self.code_valide or (self.code_valide is not None and no_overwrite):
- self.recorded = True
- return False # no change
- if self.validation:
- db.session.delete(self.validation)
- db.session.commit()
- 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,
- )
- db.session.add(self.validation)
- db.session.commit()
- log(f"Recording {self}: {code}")
- Scolog.logdb(
- method="jury_but",
- etudid=self.etud.id,
- msg=f"Validation année BUT{self.annee_but}: {code}",
- )
+
+ if code != self.code_valide and (self.code_valide is None or not no_overwrite):
+ # Enregistrement du code annuel BUT
+ if self.validation:
+ db.session.delete(self.validation)
+ db.session.commit()
+ 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,
+ )
+ db.session.add(self.validation)
+ db.session.commit()
+ log(f"Recording {self}: {code}")
+ Scolog.logdb(
+ method="jury_but",
+ etudid=self.etud.id,
+ msg=f"Validation année BUT{self.annee_but}: {code}",
+ )
# --- Autorisation d'inscription dans semestre suivant ?
- if self.formsemestre_pair is not None:
- if code is None:
- ScolarAutorisationInscription.delete_autorisation_etud(
- etudid=self.etud.id,
- origin_formsemestre_id=self.formsemestre_pair.id,
- )
- else:
- next_semestre_id = self.next_annee_semestre_id(code)
- if next_semestre_id is not None:
- ScolarAutorisationInscription.autorise_etud(
- self.etud.id,
- self.formsemestre_pair.formation.formation_code,
- self.formsemestre_pair.id,
- next_semestre_id,
- )
+ ScolarAutorisationInscription.delete_autorisation_etud(
+ etudid=self.etud.id,
+ origin_formsemestre_id=self.formsemestre.id,
+ )
+ for next_semestre_id in self.next_semestre_ids(code):
+ ScolarAutorisationInscription.autorise_etud(
+ self.etud.id,
+ self.formsemestre.formation.formation_code,
+ self.formsemestre.id,
+ next_semestre_id,
+ )
db.session.commit()
self.recorded = True
@@ -872,18 +887,18 @@ class DecisionsProposeesAnnee(DecisionsProposees):
self.invalidate_formsemestre_cache()
def get_autorisations_passage(self) -> list[int]:
- """Les liste des indices de semestres auxquels on est autorisé à
- s'inscrire depuis cette année"""
- formsemestre = self.formsemestre_pair or self.formsemestre_impair
- if not formsemestre:
- return []
- return [
- a.semestre_id
- for a in ScolarAutorisationInscription.query.filter_by(
- etudid=self.etud.id,
- origin_formsemestre_id=formsemestre.id,
- )
- ]
+ """Liste des indices de semestres auxquels on est autorisé à
+ s'inscrire depuis le semestre courant.
+ """
+ return sorted(
+ [
+ a.semestre_id
+ for a in ScolarAutorisationInscription.query.filter_by(
+ etudid=self.etud.id,
+ origin_formsemestre_id=self.formsemestre.id,
+ )
+ ]
+ )
def descr_niveaux_validation(self, line_sep: str = "\n") -> str:
"""Description textuelle des niveaux validés (enregistrés)
diff --git a/app/but/jury_but_view.py b/app/but/jury_but_view.py
index acb6b695bc..ac454195eb 100644
--- a/app/but/jury_but_view.py
+++ b/app/but/jury_but_view.py
@@ -43,21 +43,20 @@ def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str:
"""
H = []
- H.append("""
""")
- H.append(
- f"""
-
- Décision de jury pour l'année : {
- _gen_but_select("code_annee", deca.codes, deca.code_valide,
- disabled=True, klass="manual")
- }
- ({deca.code_valide or 'non'} enregistrée)
+ if deca.jury_annuel:
+ H.append(
+ f"""
+
+
+ Décision de jury pour l'année : {
+ _gen_but_select("code_annee", deca.codes, deca.code_valide,
+ disabled=True, klass="manual")
+ }
+ ({deca.code_valide or 'non'} enregistrée)
+
+ Autorisé à passer en :
+ { ", ".join( ["S" + str(a.semestre_id or '') for a in autorisations_passage ] )}
+
+ """
+ if autorisations_passage
+ else """
pas d'autorisations de passage enregistrées.
"""
+ )
+ H.append(div_autorisations_passage)
+
if read_only:
H.append(
"""
Vous n'avez pas la permission de modifier ces décisions.
- Les champs entourés en vert sont enregistrés.
"""
+ Les champs entourés en vert sont enregistrés.
+
+ """
)
else:
if formsemestre.semestre_id < formsemestre.formation.get_parcours().NB_SEM:
H.append(
f"""
-
- autoriser à passer dans le semestre S{formsemestre.semestre_id+1}
- {("(autorisations enregistrées: " + ' '.join(
- 'S' + str(a.semestre_id or '') for a in autorisations_passage) + ")"
- ) if autorisations_passage else ""}
-
+
+ autoriser à passer dans le semestre S{formsemestre.semestre_id+1}
+
"""
)
diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py
index d0fdd05b37..9b92a7dfee 100644
--- a/app/scodoc/sco_moduleimpl_status.py
+++ b/app/scodoc/sco_moduleimpl_status.py
@@ -544,7 +544,9 @@ def _ligne_evaluation(
if not first_eval:
H.append("""
""")
else: # il y a deja des notes saisies
gr_moyennes = etat["gr_moyennes"]
@@ -773,7 +773,10 @@ def _ligne_evaluation(
name = "Tous" # tous
else:
name = f"""Groupe {gr_moyenne["group_name"]}"""
- H.append(f"""