Passage étudiants depuis autres semestres: ajout option pour conserver juste le groupe de parcours + Fix désinscription

This commit is contained in:
Emmanuel Viennet 2023-08-31 13:38:17 +02:00
parent 224ec7be81
commit e0c6439c92
5 changed files with 103 additions and 41 deletions

View File

@ -220,20 +220,9 @@ def group_remove_etud(group_id: int, etudid: int):
group = query.first_or_404() group = query.first_or_404()
if not group.partition.formsemestre.etat: if not group.partition.formsemestre.etat:
return json_error(403, "formsemestre verrouillé") return json_error(403, "formsemestre verrouillé")
if etud in group.etuds:
group.etuds.remove(etud) group.remove_etud(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)
return {"group_id": group_id, "etudid": etudid} return {"group_id": group_id, "etudid": etudid}

View File

@ -841,7 +841,7 @@ class FormSemestre(db.Model):
Les groupes de parcours sont ceux de la partition scu.PARTITION_PARCOURS Les groupes de parcours sont ceux de la partition scu.PARTITION_PARCOURS
et leur nom est le code du parcours (eg "Cyber"). 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. sinon traite tous les inscrits du semestre.
""" """
if self.formation.referentiel_competence_id is None: if self.formation.referentiel_competence_id is None:

View File

@ -11,8 +11,8 @@ from operator import attrgetter
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from app import db, log from app import db, log
from app.models import SHORT_STR_LEN from app.models import Scolog, GROUPNAME_STR_LEN, SHORT_STR_LEN
from app.models import GROUPNAME_STR_LEN from app.scodoc import sco_cache
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
@ -83,6 +83,14 @@ class Partition(db.Model):
return False return False
return True 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: def is_parcours(self) -> bool:
"Vrai s'il s'agit de la partition de parcours" "Vrai s'il s'agit de la partition de parcours"
return self.partition_name == scu.PARTITION_PARCOURS return self.partition_name == scu.PARTITION_PARCOURS
@ -248,6 +256,24 @@ class GroupDescr(db.Model):
return False return False
return True 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 = db.Table(
"group_membership", "group_membership",

View File

@ -38,7 +38,7 @@ from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import Formation, FormSemestre, FormSemestreInscription, Scolog from app.models import Formation, FormSemestre, FormSemestreInscription, Scolog
from app.models.etudiants import Identite 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 from app.models.validations import ScolarEvent
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import log from app import log
@ -236,6 +236,10 @@ def do_formsemestre_desinscription(etudid, formsemestre_id):
sco_moduleimpl.do_moduleimpl_inscription_delete( sco_moduleimpl.do_moduleimpl_inscription_delete(
moduleimpl_inscription_id, formsemestre_id=formsemestre_id 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 # -- désincription du semestre
do_formsemestre_inscription_delete( do_formsemestre_inscription_delete(
insem["formsemestre_inscription_id"], formsemestre_id=formsemestre_id insem["formsemestre_inscription_id"], formsemestre_id=formsemestre_id
@ -259,7 +263,7 @@ def do_formsemestre_desinscription(etudid, formsemestre_id):
cnx, cnx,
method="formsemestre_desinscription", method="formsemestre_desinscription",
etudid=etudid, etudid=etudid,
msg="desinscription semestre %s" % formsemestre_id, msg=f"desinscription semestre {formsemestre_id}",
commit=False, commit=False,
) )

View File

@ -171,12 +171,16 @@ def list_inscrits_date(sem):
return [x[0] for x in cursor.fetchall()] 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 """Inscrit ces etudiants dans ce semestre
(la liste doit avoir été vérifiée au préalable) (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: FormSemestre = db.session.get(FormSemestre, sem["formsemestre_id"])
formsemestre.setup_parcours_groups() formsemestre.setup_parcours_groups()
log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}") log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}")
@ -187,7 +191,7 @@ def do_inscrit(sem, etudids, inscrit_groupes=False):
etat=scu.INSCRIT, etat=scu.INSCRIT,
method="formsemestre_inscr_passage", 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, # Inscription dans les mêmes groupes que ceux du semestre d'origine,
# s'ils existent. # s'ils existent.
# (mise en correspondance à partir du nom du groupe, sans tenir compte # (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( group: GroupDescr = db.session.get(
GroupDescr, partition_group["group_id"] 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): def do_desinscrit(sem: dict, etudids: list[int]):
log("do_desinscrit: %s" % etudids) "désinscrit les étudiants indiqués du formsemestre"
log(f"do_desinscrit: {etudids}")
for etudid in etudids: for etudid in etudids:
sco_formsemestre_inscriptions.do_formsemestre_desinscription( sco_formsemestre_inscriptions.do_formsemestre_desinscription(
etudid, sem["formsemestre_id"] etudid, sem["formsemestre_id"]
@ -273,6 +282,7 @@ def formsemestre_inscr_passage(
formsemestre_id, formsemestre_id,
etuds=[], etuds=[],
inscrit_groupes=False, inscrit_groupes=False,
inscrit_parcours=False,
submitted=False, submitted=False,
dialog_confirmed=False, dialog_confirmed=False,
ignore_jury=False, ignore_jury=False,
@ -291,6 +301,7 @@ def formsemestre_inscr_passage(
""" """
inscrit_groupes = int(inscrit_groupes) inscrit_groupes = int(inscrit_groupes)
inscrit_parcours = int(inscrit_parcours)
ignore_jury = int(ignore_jury) ignore_jury = int(ignore_jury)
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
# -- check lock # -- check lock
@ -335,6 +346,7 @@ def formsemestre_inscr_passage(
candidats_non_inscrits, candidats_non_inscrits,
inscrits_ailleurs, inscrits_ailleurs,
inscrit_groupes=inscrit_groupes, inscrit_groupes=inscrit_groupes,
inscrit_parcours=inscrit_parcours,
ignore_jury=ignore_jury, ignore_jury=ignore_jury,
) )
else: else:
@ -376,6 +388,7 @@ def formsemestre_inscr_passage(
"formsemestre_id": formsemestre_id, "formsemestre_id": formsemestre_id,
"etuds": ",".join([str(x) for x in etuds]), "etuds": ",".join([str(x) for x in etuds]),
"inscrit_groupes": inscrit_groupes, "inscrit_groupes": inscrit_groupes,
"inscrit_parcours": inscrit_parcours,
"ignore_jury": ignore_jury, "ignore_jury": ignore_jury,
"submitted": 1, "submitted": 1,
}, },
@ -388,6 +401,7 @@ def formsemestre_inscr_passage(
sem, sem,
a_inscrire, a_inscrire,
inscrit_groupes=inscrit_groupes, inscrit_groupes=inscrit_groupes,
inscrit_parcours=inscrit_parcours,
) )
# Désinscriptions: # Désinscriptions:
do_desinscrit(sem, a_desinscrire) do_desinscrit(sem, a_desinscrire)
@ -433,15 +447,21 @@ def _build_page(
candidats_non_inscrits, candidats_non_inscrits,
inscrits_ailleurs, inscrits_ailleurs,
inscrit_groupes=False, inscrit_groupes=False,
inscrit_parcours=False,
ignore_jury=False, ignore_jury=False,
): ):
formsemestre: FormSemestre = db.session.get(FormSemestre, sem["formsemestre_id"]) formsemestre: FormSemestre = db.session.get(FormSemestre, sem["formsemestre_id"])
inscrit_groupes = int(inscrit_groupes) inscrit_groupes = int(inscrit_groupes)
inscrit_parcours = int(inscrit_parcours)
ignore_jury = int(ignore_jury) ignore_jury = int(ignore_jury)
if inscrit_groupes: if inscrit_groupes:
inscrit_groupes_checked = " checked" inscrit_groupes_checked = " checked"
else: else:
inscrit_groupes_checked = "" inscrit_groupes_checked = ""
if inscrit_parcours:
inscrit_parcours_checked = " checked"
else:
inscrit_parcours_checked = ""
if ignore_jury: if ignore_jury:
ignore_jury_checked = " checked" ignore_jury_checked = " checked"
else: else:
@ -458,17 +478,23 @@ def _build_page(
&nbsp;<a href="#help">aide</a> &nbsp;<a href="#help">aide</a>
<input name="inscrit_groupes" type="checkbox" value="1" <input name="inscrit_groupes" type="checkbox" value="1"
{inscrit_groupes_checked}>inscrire aux mêmes groupes</input> {inscrit_groupes_checked}>inscrire aux mêmes groupes (y compris parcours)</input>
<input name="inscrit_parcours" type="checkbox" value="1"
{inscrit_parcours_checked}>inscrire aux mêmes parcours</input>
<input name="ignore_jury" type="checkbox" value="1" onchange="document.f.submit()" <input name="ignore_jury" type="checkbox" value="1" onchange="document.f.submit()"
{ignore_jury_checked}>inclure tous les étudiants (même sans décision de jury)</input> {ignore_jury_checked}>inclure tous les étudiants (même sans décision de jury)</input>
<div class="pas_recap">Actuellement <span id="nbinscrits">{len(inscrits)}</span> inscrits <div class="pas_recap">Actuellement <span id="nbinscrits">{len(inscrits)}</span>
et {len(candidats_non_inscrits)} candidats supplémentaires inscrits et {len(candidats_non_inscrits)} candidats supplémentaires.
</div> </div>
<div>{scu.EMO_WARNING} <em>Seuls les semestres dont la date de fin est antérieure à la date de début <div>{scu.EMO_WARNING}
de ce semestre ({formsemestre.date_debut.strftime("%d/%m/%Y")}) sont pris en compte.</em></div> <em>Seuls les semestres dont la date de fin est antérieure à la date de début
de ce semestre ({formsemestre.date_debut.strftime("%d/%m/%Y")}) sont pris en
compte.</em>
</div>
{etuds_select_boxes(auth_etuds_by_sem, inscrits_ailleurs)} {etuds_select_boxes(auth_etuds_by_sem, inscrits_ailleurs)}
<input type="submit" name="submitted" value="Appliquer les modifications"/> <input type="submit" name="submitted" value="Appliquer les modifications"/>
@ -498,7 +524,8 @@ def _build_page(
return H return H
def formsemestre_inscr_passage_help(sem): def formsemestre_inscr_passage_help(sem: dict):
"texte d'aide en bas de la page passage des étudiants"
return f"""<div class="pas_help"><h3><a name="help">Explications</a></h3> return f"""<div class="pas_help"><h3><a name="help">Explications</a></h3>
<p>Cette page permet d'inscrire des étudiants dans le semestre destination <p>Cette page permet d'inscrire des étudiants dans le semestre destination
<a class="stdlink" <a class="stdlink"
@ -507,18 +534,34 @@ def formsemestre_inscr_passage_help(sem):
}">{sem['titreannee']}</a>, }">{sem['titreannee']}</a>,
et d'en désincrire si besoin. et d'en désincrire si besoin.
</p> </p>
<p>Les étudiants sont groupés par semestres d'origines. Ceux qui sont en caractères <p>Les étudiants sont groupés par semestre d'origine. Ceux qui sont en caractères
<span class="inscrit">gras</span> sont déjà inscrits dans le semestre destination. <span class="inscrit">gras</span> sont déjà inscrits dans le semestre destination.
Ceux qui sont en <span class"inscrailleurs">gras et en rouge</span> sont inscrits Ceux qui sont en <span class"inscrailleurs">gras et en rouge</span> sont inscrits
dans un <em>autre</em> semestre.</p> dans un <em>autre</em> semestre.
<p>Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter d'autres </p>
étudiants à inscrire dans le semestre destination.</p> <p>Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter
<p>Si vous -selectionnez un étudiant déjà inscrit (en gras), il sera désinscrit.</p> d'autres étudiants à inscrire dans le semestre destination.
<p>Le bouton <em>inscrire aux mêmes groupes</em> ne prend en compte que les groupes qui existent </p>
dans les deux semestres: pensez à créer les partitions et groupes que vous souhaitez conserver
<b>avant</b> d'inscrire les étudiants. <p>Si vous -selectionnez un étudiant déjà inscrit (en gras), il sera désinscrit.
</p>
<p>Le bouton <em>inscrire aux mêmes groupes</em> ne prend en compte que les groupes
qui existent dans les deux semestres: pensez à créer les partitions et groupes que
vous souhaitez conserver <b>avant</b> d'inscrire les étudiants.
</p>
<p>Les parcours de BUT sont gérés comme des groupes de la partition parcours: si on
conserve les groupes, on conserve les parcours ( aussi, pensez à les cocher dans
<a class="stdlink" href="{
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
formsemestre_id=sem["formsemestre_id"] )
}">modifier le semestre</a> avant de faire passer les étudiants).
</a>
<p class="help">Aucune action ne sera effectuée si vous n'appuyez pas sur le bouton
"Appliquer les modifications" !
</p> </p>
<p class="help">Aucune action ne sera effectuée si vous n'appuyez pas sur le bouton "Appliquer les modifications" !</p>
</div> </div>
""" """