Modernise code inscription/passage semestre. Closes #859

This commit is contained in:
Emmanuel Viennet 2024-02-19 19:10:12 +01:00
parent e6d61fcd8a
commit 56aa5fbba3
5 changed files with 194 additions and 155 deletions

View File

@ -85,17 +85,6 @@ UE_ELECTIVE = 4 # UE "élective" dans certains cursus (UCAC?, ISCID)
UE_PROFESSIONNELLE = 5 # UE "professionnelle" (ISCID, ...) UE_PROFESSIONNELLE = 5 # UE "professionnelle" (ISCID, ...)
UE_OPTIONNELLE = 6 # UE non fondamentales (ILEPS, ...) UE_OPTIONNELLE = 6 # UE non fondamentales (ILEPS, ...)
def ue_is_fondamentale(ue_type):
return ue_type in (UE_STANDARD, UE_STAGE_LP, UE_PROFESSIONNELLE)
def ue_is_professionnelle(ue_type):
return (
ue_type == UE_PROFESSIONNELLE
) # NB: les UE_PROFESSIONNELLE sont à la fois fondamentales et pro
UE_TYPE_NAME = { UE_TYPE_NAME = {
UE_STANDARD: "Standard", UE_STANDARD: "Standard",
UE_SPORT: "Sport/Culture (points bonus)", UE_SPORT: "Sport/Culture (points bonus)",
@ -104,8 +93,6 @@ UE_TYPE_NAME = {
UE_ELECTIVE: "Elective (ISCID)", UE_ELECTIVE: "Elective (ISCID)",
UE_PROFESSIONNELLE: "Professionnelle (ISCID)", UE_PROFESSIONNELLE: "Professionnelle (ISCID)",
UE_OPTIONNELLE: "Optionnelle", UE_OPTIONNELLE: "Optionnelle",
# UE_FONDAMENTALE : '"Fondamentale" (eg UCAC)',
# UE_OPTIONNELLE : '"Optionnelle" (UCAC)'
} }
# Couleurs RGB (dans [0.,1.]) des UE pour les bulletins: # Couleurs RGB (dans [0.,1.]) des UE pour les bulletins:

View File

@ -191,7 +191,23 @@ def do_formsemestre_inscription_edit(args=None, formsemestre_id=None):
) # > modif inscription semestre ) # > modif inscription semestre
def do_formsemestre_desinscription(etudid, formsemestre_id): def check_if_has_decision_jury(
formsemestre: FormSemestre, etudids: list[int] | set[int]
):
"raise exception if one of the etuds has a decision in formsemestre"
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
for etudid in etudids:
if nt.etud_has_decision(etudid):
etud = Identite.query.get(etudid)
raise ScoValueError(
f"""désinscription impossible: l'étudiant {etud.nomprenom} a
une décision de jury (la supprimer avant si nécessaire)"""
)
def do_formsemestre_desinscription(
etudid, formsemestre_id: int, check_has_dec_jury=True
):
"""Désinscription d'un étudiant. """Désinscription d'un étudiant.
Si semestre extérieur et dernier inscrit, suppression de ce semestre. Si semestre extérieur et dernier inscrit, suppression de ce semestre.
""" """
@ -204,13 +220,8 @@ def do_formsemestre_desinscription(etudid, formsemestre_id):
raise ScoValueError("désinscription impossible: semestre verrouille") raise ScoValueError("désinscription impossible: semestre verrouille")
# -- Si decisions de jury, désinscription interdite # -- Si decisions de jury, désinscription interdite
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) if check_has_dec_jury:
check_if_has_decision_jury(formsemestre, [etudid])
if nt.etud_has_decision(etudid):
raise ScoValueError(
f"""désinscription impossible: l'étudiant {etud.nomprenom} a
une décision de jury (la supprimer avant si nécessaire)"""
)
insem = do_formsemestre_inscription_list( insem = do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id, "etudid": etudid} args={"formsemestre_id": formsemestre_id, "etudid": etudid}

View File

@ -36,13 +36,14 @@ from flask import url_for, g, request
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import db, log from app import db, log
from app.models import Formation, FormSemestre, GroupDescr from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import Formation, FormSemestre, GroupDescr, Identite
from app.scodoc.gen_tables import GenTable from app.scodoc.gen_tables import GenTable
from app.scodoc import html_sco_header from app.scodoc import html_sco_header
from app.scodoc import sco_cache from app.scodoc import sco_cache
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_etud from app.scodoc import sco_etud
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
@ -50,62 +51,69 @@ from app.scodoc import sco_pv_dict
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
def list_authorized_etuds_by_sem(sem, delai=274, ignore_jury=False): def _list_authorized_etuds_by_sem(
formsemestre: FormSemestre, ignore_jury=False
) -> tuple[dict[int, dict], list[dict], dict[int, Identite]]:
"""Liste des etudiants autorisés à s'inscrire dans sem. """Liste des etudiants autorisés à s'inscrire dans sem.
delai = nb de jours max entre la date de l'autorisation et celle de debut du semestre cible. delai = nb de jours max entre la date de l'autorisation et celle de debut du semestre cible.
ignore_jury: si vrai, considère tous les étudiants comme autorisés, même ignore_jury: si vrai, considère tous les étudiants comme autorisés, même
s'ils n'ont pas de décision de jury. s'ils n'ont pas de décision de jury.
""" """
src_sems = list_source_sems(sem, delai=delai) src_sems = _list_source_sems(formsemestre)
inscrits = list_inscrits(sem["formsemestre_id"]) inscrits = list_inscrits(formsemestre.id)
r = {} r = {}
candidats = {} # etudid : etud (tous les etudiants candidats) candidats = {} # etudid : etud (tous les etudiants candidats)
nb = 0 # debug nb = 0 # debug
for src in src_sems: src_formsemestre: FormSemestre
for src_formsemestre in src_sems:
if ignore_jury: if ignore_jury:
# liste de tous les inscrits au semestre (sans dems) # liste de tous les inscrits au semestre (sans dems)
liste = list_inscrits(src["formsemestre_id"]).values() etud_list = list_inscrits(formsemestre.id).values()
else: else:
# liste des étudiants autorisés par le jury à s'inscrire ici # liste des étudiants autorisés par le jury à s'inscrire ici
liste = list_etuds_from_sem(src, sem) etud_list = _list_etuds_from_sem(src_formsemestre, formsemestre)
liste_filtree = [] liste_filtree = []
for e in liste: for e in etud_list:
# Filtre ceux qui se sont déjà inscrit dans un semestre APRES le semestre src # Filtre ceux qui se sont déjà inscrit dans un semestre APRES le semestre src
auth_used = False # autorisation deja utilisée ? auth_used = False # autorisation deja utilisée ?
etud = sco_etud.get_etud_info(etudid=e["etudid"], filled=True)[0] etud = Identite.get_etud(e["etudid"])
for isem in etud["sems"]: for inscription in etud.inscriptions():
if ndb.DateDMYtoISO(isem["date_debut"]) >= ndb.DateDMYtoISO( if inscription.formsemestre.date_debut >= src_formsemestre.date_fin:
src["date_fin"]
):
auth_used = True auth_used = True
if not auth_used: if not auth_used:
candidats[e["etudid"]] = etud candidats[e["etudid"]] = etud
liste_filtree.append(e) liste_filtree.append(e)
nb += 1 nb += 1
r[src["formsemestre_id"]] = { r[src_formsemestre.id] = {
"etuds": liste_filtree, "etuds": liste_filtree,
"infos": { "infos": {
"id": src["formsemestre_id"], "id": src_formsemestre.id,
"title": src["titreannee"], "title": src_formsemestre.titre_annee(),
"title_target": "formsemestre_status?formsemestre_id=%s" "title_target": url_for(
% src["formsemestre_id"], "notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=src_formsemestre.id,
),
"filename": "etud_autorises", "filename": "etud_autorises",
}, },
} }
# ajoute attribut inscrit qui indique si l'étudiant est déjà inscrit dans le semestre dest. # ajoute attribut inscrit qui indique si l'étudiant est déjà inscrit dans le semestre dest.
for e in r[src["formsemestre_id"]]["etuds"]: for e in r[src_formsemestre.id]["etuds"]:
e["inscrit"] = e["etudid"] in inscrits e["inscrit"] = e["etudid"] in inscrits
# Ajoute liste des etudiants actuellement inscrits # Ajoute liste des etudiants actuellement inscrits
for e in inscrits.values(): for e in inscrits.values():
e["inscrit"] = True e["inscrit"] = True
r[sem["formsemestre_id"]] = { r[formsemestre.id] = {
"etuds": list(inscrits.values()), "etuds": list(inscrits.values()),
"infos": { "infos": {
"id": sem["formsemestre_id"], "id": formsemestre.id,
"title": "Semestre cible: " + sem["titreannee"], "title": "Semestre cible: " + formsemestre.titre_annee(),
"title_target": "formsemestre_status?formsemestre_id=%s" "title_target": url_for(
% sem["formsemestre_id"], "notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre.id,
),
"comment": " actuellement inscrits dans ce semestre", "comment": " actuellement inscrits dans ce semestre",
"help": "Ces étudiants sont actuellement inscrits dans ce semestre. Si vous les décochez, il seront désinscrits.", "help": "Ces étudiants sont actuellement inscrits dans ce semestre. Si vous les décochez, il seront désinscrits.",
"filename": "etud_inscrits", "filename": "etud_inscrits",
@ -115,7 +123,7 @@ def list_authorized_etuds_by_sem(sem, delai=274, ignore_jury=False):
return r, inscrits, candidats return r, inscrits, candidats
def list_inscrits(formsemestre_id, with_dems=False): def list_inscrits(formsemestre_id: int, with_dems=False) -> list[dict]:
"""Étudiants déjà inscrits à ce semestre """Étudiants déjà inscrits à ce semestre
{ etudid : etud } { etudid : etud }
""" """
@ -133,28 +141,27 @@ def list_inscrits(formsemestre_id, with_dems=False):
return inscr return inscr
def list_etuds_from_sem(src, dst) -> list[dict]: def _list_etuds_from_sem(src: FormSemestre, dst: FormSemestre) -> list[dict]:
"""Liste des etudiants du semestre src qui sont autorisés à passer dans le semestre dst.""" """Liste des étudiants du semestre src qui sont autorisés à passer dans le semestre dst."""
target = dst["semestre_id"] target_semestre_id = dst.semestre_id
dpv = sco_pv_dict.dict_pvjury(src["formsemestre_id"]) dpv = sco_pv_dict.dict_pvjury(src.id)
if not dpv: if not dpv:
return [] return []
etuds = [ etuds = [
x["identite"] x["identite"]
for x in dpv["decisions"] for x in dpv["decisions"]
if target in [a["semestre_id"] for a in x["autorisations"]] if target_semestre_id in [a["semestre_id"] for a in x["autorisations"]]
] ]
return etuds return etuds
def list_inscrits_date(sem): def list_inscrits_date(formsemestre: FormSemestre):
"""Liste les etudiants inscrits dans n'importe quel semestre """Liste les etudiants inscrits à la date de début de formsemestre
du même département dans n'importe quel semestre du même département
SAUF sem à la date de début de sem. SAUF formsemestre
""" """
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"])
cursor.execute( cursor.execute(
"""SELECT ins.etudid """SELECT ins.etudid
FROM FROM
@ -166,12 +173,18 @@ def list_inscrits_date(sem):
AND S.date_fin >= %(date_debut_iso)s AND S.date_fin >= %(date_debut_iso)s
AND S.dept_id = %(dept_id)s AND S.dept_id = %(dept_id)s
""", """,
sem, {
"formsemestre_id": formsemestre.id,
"date_debut_iso": formsemestre.date_debut.isoformat(),
"dept_id": formsemestre.dept_id,
},
) )
return [x[0] for x in cursor.fetchall()] return [x[0] for x in cursor.fetchall()]
def do_inscrit(sem, etudids, inscrit_groupes=False, inscrit_parcours=False): def do_inscrit(
formsemestre: FormSemestre, 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: En option:
@ -181,12 +194,11 @@ def do_inscrit(sem, etudids, inscrit_groupes=False, inscrit_parcours=False):
(si les deux sont vrais, inscrit_parcours n'a pas d'effet) (si les deux sont vrais, inscrit_parcours n'a pas d'effet)
""" """
# TODO à ré-écrire pour utiliser les modèles, 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() formsemestre.setup_parcours_groups()
log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}") log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}")
for etudid in etudids: for etudid in etudids:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
sem["formsemestre_id"], formsemestre.id,
etudid, etudid,
etat=scu.INSCRIT, etat=scu.INSCRIT,
method="formsemestre_inscr_passage", method="formsemestre_inscr_passage",
@ -210,7 +222,7 @@ def do_inscrit(sem, etudids, inscrit_groupes=False, inscrit_parcours=False):
cursem_groups_by_name = { cursem_groups_by_name = {
g["group_name"]: g g["group_name"]: g
for g in sco_groups.get_sem_groups(sem["formsemestre_id"]) for g in sco_groups.get_sem_groups(formsemestre.id)
if g["group_name"] if g["group_name"]
} }
@ -234,53 +246,46 @@ def do_inscrit(sem, etudids, inscrit_groupes=False, inscrit_parcours=False):
sco_groups.change_etud_group_in_partition(etudid, group) sco_groups.change_etud_group_in_partition(etudid, group)
def do_desinscrit(sem: dict, etudids: list[int]): def do_desinscrit(
formsemestre: FormSemestre, etudids: list[int], check_has_dec_jury=True
):
"désinscrit les étudiants indiqués du formsemestre" "désinscrit les étudiants indiqués du formsemestre"
log(f"do_desinscrit: {etudids}") 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, formsemestre.id, check_has_dec_jury=check_has_dec_jury
) )
def list_source_sems(sem, delai=None) -> list[dict]: def _list_source_sems(formsemestre: FormSemestre) -> list[FormSemestre]:
"""Liste des semestres sources """Liste des semestres sources
sem est le semestre destination formsemestre est le semestre destination
""" """
# liste des semestres débutant a moins # liste des semestres du même type de cursus terminant
# de delai (en jours) de la date de fin du semestre d'origine. # pas trop loin de la date de début du semestre destination
sems = sco_formsemestre.do_formsemestre_list() date_fin_min = formsemestre.date_debut - datetime.timedelta(days=275)
othersems = [] date_fin_max = formsemestre.date_debut + datetime.timedelta(days=45)
d, m, y = [int(x) for x in sem["date_debut"].split("/")] return (
date_debut_dst = datetime.date(y, m, d) FormSemestre.query.filter(
FormSemestre.dept_id == formsemestre.dept_id,
delais = datetime.timedelta(delai) # saute le semestre destination:
for s in sems: FormSemestre.id != formsemestre.id,
if s["formsemestre_id"] == sem["formsemestre_id"]: # et les semestres de formations speciales (monosemestres):
continue # saute le semestre destination FormSemestre.semestre_id != codes_cursus.NO_SEMESTRE_ID,
if s["date_fin"]: # semestre pas trop dans le futur
d, m, y = [int(x) for x in s["date_fin"].split("/")] FormSemestre.date_fin <= date_fin_max,
date_fin = datetime.date(y, m, d) # ni trop loin dans le passé
if date_debut_dst - date_fin > delais: FormSemestre.date_fin >= date_fin_min,
continue # semestre trop ancien )
if date_fin > date_debut_dst: .join(Formation)
continue # semestre trop récent .filter_by(type_parcours=formsemestre.formation.type_parcours)
# Elimine les semestres de formations speciales (sans parcours) ).all()
if s["semestre_id"] == codes_cursus.NO_SEMESTRE_ID:
continue
#
formation: Formation = Formation.query.get_or_404(s["formation_id"])
parcours = codes_cursus.get_cursus_from_code(formation.type_parcours)
if not parcours.ALLOW_SEM_SKIP:
if s["semestre_id"] < (sem["semestre_id"] - 1):
continue
othersems.append(s)
return othersems
# view, GET, POST
def formsemestre_inscr_passage( def formsemestre_inscr_passage(
formsemestre_id, formsemestre_id,
etuds=[], etuds: str | list[int] | list[str] | int | None = None,
inscrit_groupes=False, inscrit_groupes=False,
inscrit_parcours=False, inscrit_parcours=False,
submitted=False, submitted=False,
@ -300,36 +305,41 @@ def formsemestre_inscr_passage(
- Confirmation: indiquer les étudiants inscrits et ceux désinscrits, le total courant. - Confirmation: indiquer les étudiants inscrits et ceux désinscrits, le total courant.
""" """
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
inscrit_groupes = int(inscrit_groupes) inscrit_groupes = int(inscrit_groupes)
inscrit_parcours = int(inscrit_parcours) inscrit_parcours = int(inscrit_parcours)
ignore_jury = int(ignore_jury) ignore_jury = int(ignore_jury)
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
# -- check lock # -- check lock
if not sem["etat"]: if not formsemestre.etat:
raise ScoValueError("opération impossible: semestre verrouille") raise ScoValueError("opération impossible: semestre verrouille")
header = html_sco_header.sco_header(page_title="Passage des étudiants") header = html_sco_header.sco_header(
page_title="Passage des étudiants",
init_qtip=True,
javascripts=["js/etud_info.js"],
)
footer = html_sco_header.sco_footer() footer = html_sco_header.sco_footer()
H = [header] H = [header]
etuds = [] if etuds is None else etuds
if isinstance(etuds, str): if isinstance(etuds, str):
# list de strings, vient du form de confirmation # string, vient du form de confirmation
etuds = [int(x) for x in etuds.split(",") if x] etuds = [int(x) for x in etuds.split(",") if x]
elif isinstance(etuds, int): elif isinstance(etuds, int):
etuds = [etuds] etuds = [etuds]
elif etuds and isinstance(etuds[0], str): elif etuds and isinstance(etuds[0], str):
etuds = [int(x) for x in etuds] etuds = [int(x) for x in etuds]
auth_etuds_by_sem, inscrits, candidats = list_authorized_etuds_by_sem( auth_etuds_by_sem, inscrits, candidats = _list_authorized_etuds_by_sem(
sem, ignore_jury=ignore_jury formsemestre, ignore_jury=ignore_jury
) )
etuds_set = set(etuds) etuds_set = set(etuds)
candidats_set = set(candidats) candidats_set = set(candidats)
inscrits_set = set(inscrits) inscrits_set = set(inscrits)
candidats_non_inscrits = candidats_set - inscrits_set candidats_non_inscrits = candidats_set - inscrits_set
inscrits_ailleurs = set(list_inscrits_date(sem)) inscrits_ailleurs = set(list_inscrits_date(formsemestre))
def set_to_sorted_etud_list(etudset): def set_to_sorted_etud_list(etudset) -> list[Identite]:
etuds = [candidats[etudid] for etudid in etudset] etuds = [candidats[etudid] for etudid in etudset]
etuds.sort(key=itemgetter("nom")) etuds.sort(key=lambda e: e.sort_key)
return etuds return etuds
if submitted: if submitted:
@ -340,7 +350,7 @@ def formsemestre_inscr_passage(
if not submitted: if not submitted:
H += _build_page( H += _build_page(
sem, formsemestre,
auth_etuds_by_sem, auth_etuds_by_sem,
inscrits, inscrits,
candidats_non_inscrits, candidats_non_inscrits,
@ -355,30 +365,31 @@ def formsemestre_inscr_passage(
if a_inscrire: if a_inscrire:
H.append("<h3>Étudiants à inscrire</h3><ol>") H.append("<h3>Étudiants à inscrire</h3><ol>")
for etud in set_to_sorted_etud_list(a_inscrire): for etud in set_to_sorted_etud_list(a_inscrire):
H.append("<li>%(nomprenom)s</li>" % etud) H.append(f"<li>{etud.nomprenom}</li>")
H.append("</ol>") H.append("</ol>")
a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire) a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire)
if a_inscrire_en_double: if a_inscrire_en_double:
H.append("<h3>dont étudiants déjà inscrits:</h3><ul>") H.append("<h3>dont étudiants déjà inscrits:</h3><ul>")
for etud in set_to_sorted_etud_list(a_inscrire_en_double): for etud in set_to_sorted_etud_list(a_inscrire_en_double):
H.append('<li class="inscrailleurs">%(nomprenom)s</li>' % etud) H.append(f'<li class="inscrit-ailleurs">{etud.nomprenom}</li>')
H.append("</ul>") H.append("</ul>")
if a_desinscrire: if a_desinscrire:
H.append("<h3>Étudiants à désinscrire</h3><ol>") H.append("<h3>Étudiants à désinscrire</h3><ol>")
for etudid in a_desinscrire: a_desinscrire_ident = sorted(
H.append( (Identite.query.get(eid) for eid in a_desinscrire),
'<li class="desinscription">%(nomprenom)s</li>' key=lambda x: x.sort_key,
% inscrits[etudid] )
) for etud in a_desinscrire_ident:
H.append(f'<li class="desinscription">{etud.nomprenom}</li>')
H.append("</ol>") H.append("</ol>")
todo = a_inscrire or a_desinscrire todo = a_inscrire or a_desinscrire
if not todo: if not todo:
H.append("""<h3>Il n'y a rien à modifier !</h3>""") H.append("""<h3>Il n'y a rien à modifier !</h3>""")
H.append( H.append(
scu.confirm_dialog( scu.confirm_dialog(
dest_url="formsemestre_inscr_passage" dest_url=(
if todo "formsemestre_inscr_passage" if todo else "formsemestre_status"
else "formsemestre_status", ),
message="<p>Confirmer ?</p>" if todo else "", message="<p>Confirmer ?</p>" if todo else "",
add_headers=False, add_headers=False,
cancel_url="formsemestre_inscr_passage?formsemestre_id=" cancel_url="formsemestre_inscr_passage?formsemestre_id="
@ -395,16 +406,26 @@ def formsemestre_inscr_passage(
) )
) )
else: else:
# check decisions jury ici pour éviter de recontruire le cache
# après chaque desinscription
sco_formsemestre_inscriptions.check_if_has_decision_jury(
formsemestre, a_desinscrire
)
# check decisions jury ici pour éviter de recontruire le cache
# après chaque desinscription
sco_formsemestre_inscriptions.check_if_has_decision_jury(
formsemestre, a_desinscrire
)
with sco_cache.DeferredSemCacheManager(): with sco_cache.DeferredSemCacheManager():
# Inscription des étudiants au nouveau semestre: # Inscription des étudiants au nouveau semestre:
do_inscrit( do_inscrit(
sem, formsemestre,
a_inscrire, a_inscrire,
inscrit_groupes=inscrit_groupes, inscrit_groupes=inscrit_groupes,
inscrit_parcours=inscrit_parcours, inscrit_parcours=inscrit_parcours,
) )
# Désinscriptions: # Désinscriptions:
do_desinscrit(sem, a_desinscrire) do_desinscrit(formsemestre, a_desinscrire, check_has_dec_jury=False)
H.append( H.append(
f"""<h3>Opération effectuée</h3> f"""<h3>Opération effectuée</h3>
@ -441,7 +462,7 @@ def formsemestre_inscr_passage(
def _build_page( def _build_page(
sem, formsemestre: FormSemestre,
auth_etuds_by_sem, auth_etuds_by_sem,
inscrits, inscrits,
candidats_non_inscrits, candidats_non_inscrits,
@ -450,7 +471,6 @@ def _build_page(
inscrit_parcours=False, inscrit_parcours=False,
ignore_jury=False, ignore_jury=False,
): ):
formsemestre: FormSemestre = db.session.get(FormSemestre, sem["formsemestre_id"])
inscrit_groupes = int(inscrit_groupes) inscrit_groupes = int(inscrit_groupes)
inscrit_parcours = int(inscrit_parcours) inscrit_parcours = int(inscrit_parcours)
ignore_jury = int(ignore_jury) ignore_jury = int(ignore_jury)
@ -472,7 +492,7 @@ def _build_page(
), ),
f"""<form name="f" method="post" action="{request.base_url}"> f"""<form name="f" method="post" action="{request.base_url}">
<input type="hidden" name="formsemestre_id" value="{sem['formsemestre_id']}"/> <input type="hidden" name="formsemestre_id" value="{formsemestre.id}"/>
<input type="submit" name="submitted" value="Appliquer les modifications"/> <input type="submit" name="submitted" value="Appliquer les modifications"/>
&nbsp;<a href="#help">aide</a> &nbsp;<a href="#help">aide</a>
@ -491,7 +511,7 @@ def _build_page(
</div> </div>
<div>{scu.EMO_WARNING} <div>{scu.EMO_WARNING}
<em>Seuls les semestres dont la date de fin est antérieure à la date de début <em>Seuls les semestres dont la date de fin est proche de la date de début
de ce semestre ({formsemestre.date_debut.strftime("%d/%m/%Y")}) sont pris en de ce semestre ({formsemestre.date_debut.strftime("%d/%m/%Y")}) sont pris en
compte.</em> compte.</em>
</div> </div>
@ -499,7 +519,7 @@ def _build_page(
<input type="submit" name="submitted" value="Appliquer les modifications"/> <input type="submit" name="submitted" value="Appliquer les modifications"/>
{formsemestre_inscr_passage_help(sem)} {formsemestre_inscr_passage_help(formsemestre)}
</form> </form>
""", """,
@ -524,19 +544,20 @@ def _build_page(
return H return H
def formsemestre_inscr_passage_help(sem: dict): def formsemestre_inscr_passage_help(formsemestre: FormSemestre):
"texte d'aide en bas de la page passage des étudiants" "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"
href="{ href="{
url_for("notes.formsemestre_status", scodoc_dept=g.scodoc_dept, formsemestre_id=sem["formsemestre_id"] ) url_for("notes.formsemestre_status",
}">{sem['titreannee']}</a>, scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id )
}">{formsemestre.titre_annee()}</a>,
et d'en désincrire si besoin. et d'en désincrire si besoin.
</p> </p>
<p>Les étudiants sont groupés par semestre d'origine. 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="deja-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="inscrit-ailleurs">gras et en rouge</span> sont inscrits
dans un <em>autre</em> semestre. dans un <em>autre</em> semestre.
</p> </p>
<p>Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter <p>Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter
@ -555,7 +576,7 @@ def formsemestre_inscr_passage_help(sem: dict):
conserve les groupes, on conserve les parcours ( aussi, pensez à les cocher dans conserve les groupes, on conserve les parcours ( aussi, pensez à les cocher dans
<a class="stdlink" href="{ <a class="stdlink" href="{
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept, url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
formsemestre_id=sem["formsemestre_id"] ) formsemestre_id=formsemestre.id )
}">modifier le semestre</a> avant de faire passer les étudiants). }">modifier le semestre</a> avant de faire passer les étudiants).
</a> </a>
@ -656,25 +677,24 @@ def etuds_select_boxes(
H.append("</div>") H.append("</div>")
for etud in etuds: for etud in etuds:
if etud.get("inscrit", False): if etud.get("inscrit", False):
c = " inscrit" c = " deja-inscrit"
checked = 'checked="checked"' checked = 'checked="checked"'
else: else:
checked = "" checked = ""
if etud["etudid"] in inscrits_ailleurs: if etud["etudid"] in inscrits_ailleurs:
c = " inscrailleurs" c = " inscrit-ailleurs"
else: else:
c = "" c = ""
sco_etud.format_etud_ident(etud) sco_etud.format_etud_ident(etud)
if etud["etudid"]: if etud["etudid"]:
elink = """<a class="discretelink %s" href="%s">%s</a>""" % ( elink = f"""<a id="{etud['etudid']}" class="discretelink etudinfo {c}"
c, href="{ url_for(
url_for( 'scolar.fiche_etud',
"scolar.fiche_etud",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
etudid=etud["etudid"], etudid=etud['etudid'],
), )
etud["nomprenom"], }">{etud['nomprenom']}</a>
) """
else: else:
# ce n'est pas un etudiant ScoDoc # ce n'est pas un etudiant ScoDoc
elink = etud["nomprenom"] elink = etud["nomprenom"]

View File

@ -35,7 +35,7 @@ from flask import g, url_for
from flask_login import current_user from flask_login import current_user
from app import db, log from app import db, log
from app.models import Admission, Adresse, Identite, ScolarNews from app.models import Admission, Adresse, FormSemestre, Identite, ScolarNews
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
@ -94,6 +94,7 @@ def formsemestre_synchro_etuds(
que l'on va importer/inscrire que l'on va importer/inscrire
""" """
etuds = etuds or [] etuds = etuds or []
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
inscrits_without_key = inscrits_without_key or [] inscrits_without_key = inscrits_without_key or []
log(f"formsemestre_synchro_etuds: formsemestre_id={formsemestre_id}") log(f"formsemestre_synchro_etuds: formsemestre_id={formsemestre_id}")
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
@ -184,7 +185,7 @@ def formsemestre_synchro_etuds(
inscrits_without_key inscrits_without_key
) )
log("a_desinscrire_without_key=%s" % a_desinscrire_without_key) log("a_desinscrire_without_key=%s" % a_desinscrire_without_key)
inscrits_ailleurs = set(sco_inscr_passage.list_inscrits_date(sem)) inscrits_ailleurs = set(sco_inscr_passage.list_inscrits_date(formsemestre))
a_inscrire = a_inscrire.intersection(etuds_set) a_inscrire = a_inscrire.intersection(etuds_set)
if not dialog_confirmed: if not dialog_confirmed:
@ -205,10 +206,12 @@ def formsemestre_synchro_etuds(
a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire) a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire)
if a_inscrire_en_double: if a_inscrire_en_double:
H.append("<h3>dont étudiants déjà inscrits:</h3><ol>") H.append(
"<h3>dont étudiants déjà inscrits dans un autre semestre:</h3><ol>"
)
for key in a_inscrire_en_double: for key in a_inscrire_en_double:
nom = f"""{etudsapo_ident[key]['nom']} {etudsapo_ident[key].get("prenom", "")}""" nom = f"""{etudsapo_ident[key]['nom']} {etudsapo_ident[key].get("prenom", "")}"""
H.append(f'<li class="inscrailleurs">{nom}</li>') H.append(f'<li class="inscrit-ailleurs">{nom}</li>')
H.append("</ol>") H.append("</ol>")
if a_desinscrire: if a_desinscrire:
@ -260,16 +263,26 @@ def formsemestre_synchro_etuds(
etudids_a_desinscrire = [nip2etudid(x) for x in a_desinscrire] etudids_a_desinscrire = [nip2etudid(x) for x in a_desinscrire]
etudids_a_desinscrire += a_desinscrire_without_key etudids_a_desinscrire += a_desinscrire_without_key
# #
# check decisions jury ici pour éviter de recontruire le cache
# après chaque desinscription
sco_formsemestre_inscriptions.check_if_has_decision_jury(
formsemestre, a_desinscrire
)
with sco_cache.DeferredSemCacheManager(): with sco_cache.DeferredSemCacheManager():
do_import_etuds_from_portal(sem, a_importer, etudsapo_ident) do_import_etuds_from_portal(formsemestre, a_importer, etudsapo_ident)
sco_inscr_passage.do_inscrit(sem, etudids_a_inscrire) sco_inscr_passage.do_inscrit(formsemestre, etudids_a_inscrire)
sco_inscr_passage.do_desinscrit(sem, etudids_a_desinscrire) sco_inscr_passage.do_desinscrit(
formsemestre, etudids_a_desinscrire, check_has_dec_jury=False
)
H.append( H.append(
"""<h3>Opération effectuée</h3> f"""<h3>Opération effectuée</h3>
<ul> <ul>
<li><a class="stdlink" href="formsemestre_synchro_etuds?formsemestre_id=%s">Continuer la synchronisation</a></li>""" <li><a class="stdlink" href="{
% formsemestre_id url_for('notes.formsemestre_synchro_etuds',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id
)}">Continuer la synchronisation</a>
</li>"""
) )
# #
partitions = sco_groups.get_partitions_list( partitions = sco_groups.get_partitions_list(
@ -279,8 +292,9 @@ def formsemestre_synchro_etuds(
H.append( H.append(
f"""<li><a class="stdlink" href="{ f"""<li><a class="stdlink" href="{
url_for("scolar.partition_editor", url_for("scolar.partition_editor",
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id) scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id
}">Répartir les groupes de {partitions[0]["partition_name"]}</a></li> )}">Répartir les groupes de {partitions[0]["partition_name"]}</a>
</li>
""" """
) )
@ -618,7 +632,7 @@ def get_annee_naissance(ddmmyyyyy: str) -> int:
return None return None
def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident): def do_import_etuds_from_portal(formsemestre: FormSemestre, a_importer, etudsapo_ident):
"""Inscrit les etudiants Apogee dans ce semestre.""" """Inscrit les etudiants Apogee dans ce semestre."""
log(f"do_import_etuds_from_portal: a_importer={a_importer}") log(f"do_import_etuds_from_portal: a_importer={a_importer}")
if not a_importer: if not a_importer:
@ -672,7 +686,7 @@ def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident):
# Inscription au semestre # Inscription au semestre
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
sem["formsemestre_id"], formsemestre.id,
etud.id, etud.id,
etat=scu.INSCRIT, etat=scu.INSCRIT,
etape=args["etape"], etape=args["etape"],
@ -716,7 +730,7 @@ def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident):
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_INSCR, typ=ScolarNews.NEWS_INSCR,
text=f"Import Apogée de {len(created_etudids)} étudiants en ", text=f"Import Apogée de {len(created_etudids)} étudiants en ",
obj=sem["formsemestre_id"], obj=formsemestre.id,
) )

View File

@ -3714,10 +3714,17 @@ span.sp_etape {
color: black; color: black;
} }
.inscrailleurs { .deja-inscrit {
font-weight: bold;
color: rgb(1, 76, 1) !important;
}
.inscrit-ailleurs {
font-weight: bold; font-weight: bold;
color: red !important; color: red !important;
} }
div.etuds_select_boxes {
margin-bottom: 16px;
}
span.paspaye, span.paspaye,
span.paspaye a { span.paspaye a {