diff --git a/app/api/partitions.py b/app/api/partitions.py index 5d7f56421..207fa9ec4 100644 --- a/app/api/partitions.py +++ b/app/api/partitions.py @@ -220,20 +220,9 @@ def group_remove_etud(group_id: int, etudid: int): group = query.first_or_404() if not group.partition.formsemestre.etat: return json_error(403, "formsemestre verrouillé") - if etud in group.etuds: - group.etuds.remove(etud) - db.session.commit() - Scolog.logdb( - method="group_remove_etud", - etudid=etud.id, - msg=f"Retrait du groupe {group.group_name} de {group.partition.partition_name}", - commit=True, - ) - # Update parcours - group.partition.formsemestre.update_inscriptions_parcours_from_groups( - etudid=etudid - ) - sco_cache.invalidate_formsemestre(group.partition.formsemestre_id) + + group.remove_etud(etud) + return {"group_id": group_id, "etudid": etudid} diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 677bfb178..20a1cf359 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -841,7 +841,7 @@ class FormSemestre(db.Model): Les groupes de parcours sont ceux de la partition scu.PARTITION_PARCOURS et leur nom est le code du parcours (eg "Cyber"). - Si etudid est sépcifié, n'affecte que cet étudiant, + Si etudid est spécifié, n'affecte que cet étudiant, sinon traite tous les inscrits du semestre. """ if self.formation.referentiel_competence_id is None: diff --git a/app/models/groups.py b/app/models/groups.py index f2982cf42..b8fb5f46d 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -11,8 +11,8 @@ from operator import attrgetter from sqlalchemy.exc import IntegrityError from app import db, log -from app.models import SHORT_STR_LEN -from app.models import GROUPNAME_STR_LEN +from app.models import Scolog, GROUPNAME_STR_LEN, SHORT_STR_LEN +from app.scodoc import sco_cache from app.scodoc import sco_utils as scu from app.scodoc.sco_exceptions import AccessDenied, ScoValueError @@ -83,6 +83,14 @@ class Partition(db.Model): return False return True + @classmethod + def formsemestre_remove_etud(cls, formsemestre_id: int, etud: "Identite"): + "retire l'étudiant de toutes les partitions de ce semestre" + for group in GroupDescr.query.join(Partition).filter_by( + formsemestre_id=formsemestre_id + ): + group.remove_etud(etud) + def is_parcours(self) -> bool: "Vrai s'il s'agit de la partition de parcours" return self.partition_name == scu.PARTITION_PARCOURS @@ -248,6 +256,24 @@ class GroupDescr(db.Model): return False return True + def remove_etud(self, etud: "Identite"): + "Enlève l'étudiant de ce groupe s'il en fait partie (ne fait rien sinon)" + if etud in self.etuds: + self.etuds.remove(etud) + db.session.commit() + Scolog.logdb( + method="group_remove_etud", + etudid=etud.id, + msg=f"Retrait du groupe {self.group_name} de {self.partition.partition_name}", + commit=True, + ) + # Update parcours + if self.partition.partition_name == scu.PARTITION_PARCOURS: + self.partition.formsemestre.update_inscriptions_parcours_from_groups( + etudid=etud.id + ) + sco_cache.invalidate_formsemestre(self.partition.formsemestre_id) + group_membership = db.Table( "group_membership", diff --git a/app/scodoc/sco_formsemestre_inscriptions.py b/app/scodoc/sco_formsemestre_inscriptions.py index 24943558e..282b51957 100644 --- a/app/scodoc/sco_formsemestre_inscriptions.py +++ b/app/scodoc/sco_formsemestre_inscriptions.py @@ -38,7 +38,7 @@ from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.models import Formation, FormSemestre, FormSemestreInscription, Scolog from app.models.etudiants import Identite -from app.models.groups import GroupDescr +from app.models.groups import Partition, GroupDescr from app.models.validations import ScolarEvent import app.scodoc.sco_utils as scu from app import log @@ -236,6 +236,10 @@ def do_formsemestre_desinscription(etudid, formsemestre_id): sco_moduleimpl.do_moduleimpl_inscription_delete( moduleimpl_inscription_id, formsemestre_id=formsemestre_id ) + + # -- désincription de tous les groupes des partitions de ce semestre + Partition.formsemestre_remove_etud(formsemestre_id, etud) + # -- désincription du semestre do_formsemestre_inscription_delete( insem["formsemestre_inscription_id"], formsemestre_id=formsemestre_id @@ -259,7 +263,7 @@ def do_formsemestre_desinscription(etudid, formsemestre_id): cnx, method="formsemestre_desinscription", etudid=etudid, - msg="desinscription semestre %s" % formsemestre_id, + msg=f"desinscription semestre {formsemestre_id}", commit=False, ) diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index 68d838b5a..5a44ddbd7 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -171,12 +171,16 @@ def list_inscrits_date(sem): return [x[0] for x in cursor.fetchall()] -def do_inscrit(sem, etudids, inscrit_groupes=False): +def do_inscrit(sem, etudids, inscrit_groupes=False, inscrit_parcours=False): """Inscrit ces etudiants dans ce semestre (la liste doit avoir été vérifiée au préalable) - En option: inscrit aux mêmes groupes que dans le semestre origine + En option: + - Si inscrit_groupes, inscrit aux mêmes groupes que dans le semestre origine + (toutes partitions, y compris parcours) + - Si inscrit_parcours, inscrit au même groupe de parcours (mais ignore les autres partitions) + (si les deux sont vrais, inscrit_parcours n'a pas d'effet) """ - # TODO à ré-écrire pour utiliser le smodèle, notamment GroupDescr + # TODO à ré-écrire pour utiliser les modèles, notamment GroupDescr formsemestre: FormSemestre = db.session.get(FormSemestre, sem["formsemestre_id"]) formsemestre.setup_parcours_groups() log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}") @@ -187,7 +191,7 @@ def do_inscrit(sem, etudids, inscrit_groupes=False): etat=scu.INSCRIT, method="formsemestre_inscr_passage", ) - if inscrit_groupes: + if inscrit_groupes or inscrit_parcours: # Inscription dans les mêmes groupes que ceux du semestre d'origine, # s'ils existent. # (mise en correspondance à partir du nom du groupe, sans tenir compte @@ -223,11 +227,16 @@ def do_inscrit(sem, etudids, inscrit_groupes=False): group: GroupDescr = db.session.get( GroupDescr, partition_group["group_id"] ) - sco_groups.change_etud_group_in_partition(etudid, group) + if inscrit_groupes or ( + group.partition.partition_name == scu.PARTITION_PARCOURS + and inscrit_parcours + ): + sco_groups.change_etud_group_in_partition(etudid, group) -def do_desinscrit(sem, etudids): - log("do_desinscrit: %s" % etudids) +def do_desinscrit(sem: dict, etudids: list[int]): + "désinscrit les étudiants indiqués du formsemestre" + log(f"do_desinscrit: {etudids}") for etudid in etudids: sco_formsemestre_inscriptions.do_formsemestre_desinscription( etudid, sem["formsemestre_id"] @@ -273,6 +282,7 @@ def formsemestre_inscr_passage( formsemestre_id, etuds=[], inscrit_groupes=False, + inscrit_parcours=False, submitted=False, dialog_confirmed=False, ignore_jury=False, @@ -291,6 +301,7 @@ def formsemestre_inscr_passage( """ inscrit_groupes = int(inscrit_groupes) + inscrit_parcours = int(inscrit_parcours) ignore_jury = int(ignore_jury) sem = sco_formsemestre.get_formsemestre(formsemestre_id) # -- check lock @@ -335,6 +346,7 @@ def formsemestre_inscr_passage( candidats_non_inscrits, inscrits_ailleurs, inscrit_groupes=inscrit_groupes, + inscrit_parcours=inscrit_parcours, ignore_jury=ignore_jury, ) else: @@ -376,6 +388,7 @@ def formsemestre_inscr_passage( "formsemestre_id": formsemestre_id, "etuds": ",".join([str(x) for x in etuds]), "inscrit_groupes": inscrit_groupes, + "inscrit_parcours": inscrit_parcours, "ignore_jury": ignore_jury, "submitted": 1, }, @@ -388,6 +401,7 @@ def formsemestre_inscr_passage( sem, a_inscrire, inscrit_groupes=inscrit_groupes, + inscrit_parcours=inscrit_parcours, ) # Désinscriptions: do_desinscrit(sem, a_desinscrire) @@ -433,15 +447,21 @@ def _build_page( candidats_non_inscrits, inscrits_ailleurs, inscrit_groupes=False, + inscrit_parcours=False, ignore_jury=False, ): formsemestre: FormSemestre = db.session.get(FormSemestre, sem["formsemestre_id"]) inscrit_groupes = int(inscrit_groupes) + inscrit_parcours = int(inscrit_parcours) ignore_jury = int(ignore_jury) if inscrit_groupes: inscrit_groupes_checked = " checked" else: inscrit_groupes_checked = "" + if inscrit_parcours: + inscrit_parcours_checked = " checked" + else: + inscrit_parcours_checked = "" if ignore_jury: ignore_jury_checked = " checked" else: @@ -458,17 +478,23 @@ def _build_page( aide inscrire aux mêmes groupes + {inscrit_groupes_checked}>inscrire aux mêmes groupes (y compris parcours) + + inscrire aux mêmes parcours inclure tous les étudiants (même sans décision de jury) -
Cette page permet d'inscrire des étudiants dans le semestre destination {sem['titreannee']}, et d'en désincrire si besoin.
-Les étudiants sont groupés par semestres d'origines. Ceux qui sont en caractères +
Les étudiants sont groupés par semestre d'origine. Ceux qui sont en caractères gras sont déjà inscrits dans le semestre destination. Ceux qui sont en gras et en rouge sont inscrits - dans un autre semestre.
-Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter d'autres - étudiants à inscrire dans le semestre destination.
-Si vous dé-selectionnez un étudiant déjà inscrit (en gras), il sera désinscrit.
-Le bouton inscrire aux mêmes groupes ne prend en compte que les groupes qui existent - dans les deux semestres: pensez à créer les partitions et groupes que vous souhaitez conserver - avant d'inscrire les étudiants. + dans un autre semestre. +
+Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter + d'autres étudiants à inscrire dans le semestre destination. +
+ +Si vous dé-selectionnez un étudiant déjà inscrit (en gras), il sera désinscrit. +
+ +Le bouton inscrire aux mêmes groupes ne prend en compte que les groupes + qui existent dans les deux semestres: pensez à créer les partitions et groupes que + vous souhaitez conserver avant d'inscrire les étudiants. +
+ +Les parcours de BUT sont gérés comme des groupes de la partition parcours: si on + conserve les groupes, on conserve les parcours (là aussi, pensez à les cocher dans + modifier le semestre avant de faire passer les étudiants). + + +
Aucune action ne sera effectuée si vous n'appuyez pas sur le bouton + "Appliquer les modifications" !
-Aucune action ne sera effectuée si vous n'appuyez pas sur le bouton "Appliquer les modifications" !