forked from ScoDoc/ScoDoc
838 lines
31 KiB
Python
838 lines
31 KiB
Python
# -*- mode: python -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
##############################################################################
|
|
#
|
|
# Gestion scolarite IUT
|
|
#
|
|
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
|
#
|
|
##############################################################################
|
|
|
|
"""Form. pour inscription rapide des etudiants d'un semestre dans un autre
|
|
Utilise les autorisations d'inscription délivrées en jury.
|
|
"""
|
|
import datetime
|
|
from operator import itemgetter
|
|
|
|
from flask import url_for, g, render_template, request
|
|
|
|
import app.scodoc.notesdb as ndb
|
|
import app.scodoc.sco_utils as scu
|
|
from app import db, log
|
|
from app.models import Formation, FormSemestre, GroupDescr, Identite
|
|
from app.scodoc.gen_tables import GenTable
|
|
from app.scodoc import sco_cache
|
|
from app.scodoc import codes_cursus
|
|
from app.scodoc import sco_etud
|
|
from app.scodoc import sco_formsemestre_inscriptions
|
|
from app.scodoc import sco_groups
|
|
from app.scodoc import sco_preferences
|
|
from app.scodoc import sco_pv_dict
|
|
from app.scodoc.sco_exceptions import ScoValueError
|
|
|
|
|
|
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.
|
|
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
|
|
s'ils n'ont pas de décision de jury.
|
|
"""
|
|
src_sems = _list_source_sems(formsemestre)
|
|
inscrits = list_inscrits(formsemestre.id)
|
|
r = {}
|
|
candidats = {} # etudid : etud (tous les etudiants candidats)
|
|
nb = 0 # debug
|
|
src_formsemestre: FormSemestre
|
|
for src_formsemestre in src_sems:
|
|
if ignore_jury:
|
|
# liste de tous les inscrits au semestre (sans dems)
|
|
etud_list = list_inscrits(src_formsemestre.id).values()
|
|
else:
|
|
# liste des étudiants autorisés par le jury à s'inscrire ici
|
|
etud_list = _list_etuds_from_sem(src_formsemestre, formsemestre)
|
|
liste_filtree = []
|
|
for e in etud_list:
|
|
# Filtre ceux qui se sont déjà inscrit dans un semestre APRES le semestre src
|
|
auth_used = False # autorisation deja utilisée ?
|
|
etud = Identite.get_etud(e["etudid"])
|
|
for inscription in etud.inscriptions():
|
|
if inscription.formsemestre.date_debut >= src_formsemestre.date_fin:
|
|
auth_used = True
|
|
if not auth_used:
|
|
candidats[e["etudid"]] = etud
|
|
liste_filtree.append(e)
|
|
nb += 1
|
|
r[src_formsemestre.id] = {
|
|
"etuds": liste_filtree,
|
|
"infos": {
|
|
"id": src_formsemestre.id,
|
|
"title": src_formsemestre.titre_annee(),
|
|
"title_target": url_for(
|
|
"notes.formsemestre_status",
|
|
scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=src_formsemestre.id,
|
|
),
|
|
"filename": "etud_autorises",
|
|
},
|
|
}
|
|
# ajoute attribut inscrit qui indique si l'étudiant est déjà inscrit dans le semestre dest.
|
|
for e in r[src_formsemestre.id]["etuds"]:
|
|
e["inscrit"] = e["etudid"] in inscrits
|
|
|
|
# Ajoute liste des etudiants actuellement inscrits
|
|
for e in inscrits.values():
|
|
e["inscrit"] = True
|
|
r[formsemestre.id] = {
|
|
"etuds": list(inscrits.values()),
|
|
"infos": {
|
|
"id": formsemestre.id,
|
|
"title": "Semestre cible: " + formsemestre.titre_annee(),
|
|
"title_target": url_for(
|
|
"notes.formsemestre_status",
|
|
scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=formsemestre.id,
|
|
),
|
|
"comment": " actuellement inscrits dans ce semestre",
|
|
"help": "Ces étudiants sont actuellement inscrits dans ce semestre. Si vous les décochez, il seront désinscrits.",
|
|
"filename": "etud_inscrits",
|
|
},
|
|
}
|
|
|
|
return r, inscrits, candidats
|
|
|
|
|
|
def list_inscrits(formsemestre_id: int, with_dems=False) -> list[dict]:
|
|
"""Étudiants déjà inscrits à ce semestre
|
|
{ etudid : etud }
|
|
"""
|
|
if not with_dems:
|
|
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(
|
|
formsemestre_id
|
|
) # optimized
|
|
else:
|
|
args = {"formsemestre_id": formsemestre_id}
|
|
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(args=args)
|
|
inscr = {}
|
|
for i in ins:
|
|
etudid = i["etudid"]
|
|
inscr[etudid] = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
|
return inscr
|
|
|
|
|
|
def _list_etuds_from_sem(src: FormSemestre, dst: FormSemestre) -> list[dict]:
|
|
"""Liste des étudiants du semestre src qui sont autorisés à passer dans le semestre dst."""
|
|
target_semestre_id = dst.semestre_id
|
|
dpv = sco_pv_dict.dict_pvjury(src.id)
|
|
if not dpv:
|
|
return []
|
|
etuds = [
|
|
x["identite"]
|
|
for x in dpv["decisions"]
|
|
if target_semestre_id in [a["semestre_id"] for a in x["autorisations"]]
|
|
]
|
|
return etuds
|
|
|
|
|
|
def list_inscrits_date(formsemestre: FormSemestre):
|
|
"""Liste les etudiants inscrits à la date de début de formsemestre
|
|
dans n'importe quel semestre du même département
|
|
SAUF formsemestre
|
|
"""
|
|
cnx = ndb.GetDBConnexion()
|
|
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
|
cursor.execute(
|
|
"""SELECT ins.etudid
|
|
FROM
|
|
notes_formsemestre_inscription ins,
|
|
notes_formsemestre S
|
|
WHERE ins.formsemestre_id = S.id
|
|
AND S.id != %(formsemestre_id)s
|
|
AND S.date_debut <= %(date_debut_iso)s
|
|
AND S.date_fin >= %(date_debut_iso)s
|
|
AND S.dept_id = %(dept_id)s
|
|
""",
|
|
{
|
|
"formsemestre_id": formsemestre.id,
|
|
"date_debut_iso": formsemestre.date_debut.isoformat(),
|
|
"dept_id": formsemestre.dept_id,
|
|
},
|
|
)
|
|
return [x[0] for x in cursor.fetchall()]
|
|
|
|
|
|
def do_inscrit(
|
|
formsemestre: FormSemestre, 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:
|
|
- 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 les modèles, notamment GroupDescr
|
|
formsemestre.setup_parcours_groups()
|
|
log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}")
|
|
for etudid in etudids:
|
|
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
|
|
formsemestre.id,
|
|
etudid,
|
|
etat=scu.INSCRIT,
|
|
method="formsemestre_inscr_passage",
|
|
)
|
|
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
|
|
# du nom de la partition: évidemment, cela ne marche pas si on a les
|
|
# même noms de groupes dans des partitions différentes)
|
|
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
|
|
|
# recherche le semestre origine (il serait plus propre de l'avoir conservé!)
|
|
if len(etud["sems"]) < 2:
|
|
continue
|
|
prev_formsemestre = etud["sems"][1]
|
|
sco_groups.etud_add_group_infos(
|
|
etud,
|
|
prev_formsemestre["formsemestre_id"] if prev_formsemestre else None,
|
|
)
|
|
|
|
cursem_groups_by_name = {
|
|
g["group_name"]: g
|
|
for g in sco_groups.get_sem_groups(formsemestre.id)
|
|
if g["group_name"]
|
|
}
|
|
|
|
# forme la liste des groupes présents dans les deux semestres:
|
|
partition_groups = [] # [ partition+group ] (ds nouveau sem.)
|
|
for partition_id in etud["partitions"]:
|
|
prev_group_name = etud["partitions"][partition_id]["group_name"]
|
|
if prev_group_name in cursem_groups_by_name:
|
|
new_group = cursem_groups_by_name[prev_group_name]
|
|
partition_groups.append(new_group)
|
|
|
|
# Inscrit aux groupes
|
|
for partition_group in partition_groups:
|
|
group: GroupDescr = db.session.get(
|
|
GroupDescr, partition_group["group_id"]
|
|
)
|
|
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(
|
|
formsemestre: FormSemestre, etudids: list[int], check_has_dec_jury=True
|
|
):
|
|
"désinscrit les étudiants indiqués du formsemestre"
|
|
log(f"do_desinscrit: {etudids}")
|
|
for etudid in etudids:
|
|
sco_formsemestre_inscriptions.do_formsemestre_desinscription(
|
|
etudid, formsemestre.id, check_has_dec_jury=check_has_dec_jury
|
|
)
|
|
|
|
|
|
def _list_source_sems(formsemestre: FormSemestre) -> list[FormSemestre]:
|
|
"""Liste des semestres sources
|
|
formsemestre est le semestre destination
|
|
"""
|
|
# liste des semestres du même type de cursus terminant
|
|
# pas trop loin de la date de début du semestre destination
|
|
date_fin_min = formsemestre.date_debut - datetime.timedelta(days=275)
|
|
date_fin_max = formsemestre.date_debut + datetime.timedelta(days=45)
|
|
return (
|
|
FormSemestre.query.filter(
|
|
FormSemestre.dept_id == formsemestre.dept_id,
|
|
# saute le semestre destination:
|
|
FormSemestre.id != formsemestre.id,
|
|
# et les semestres de formations speciales (monosemestres):
|
|
FormSemestre.semestre_id != codes_cursus.NO_SEMESTRE_ID,
|
|
# semestre pas trop dans le futur
|
|
FormSemestre.date_fin <= date_fin_max,
|
|
# ni trop loin dans le passé
|
|
FormSemestre.date_fin >= date_fin_min,
|
|
)
|
|
.join(Formation)
|
|
.filter_by(type_parcours=formsemestre.formation.type_parcours)
|
|
).all()
|
|
|
|
|
|
# view, GET, POST
|
|
def formsemestre_inscr_passage(
|
|
formsemestre_id,
|
|
etuds: str | list[int] | list[str] | int | None = None,
|
|
inscrit_groupes=False,
|
|
inscrit_parcours=False,
|
|
submitted=False,
|
|
dialog_confirmed=False,
|
|
ignore_jury=False,
|
|
) -> str:
|
|
"""Page Form. pour inscription des etudiants d'un semestre dans un autre
|
|
(donné par formsemestre_id).
|
|
Permet de selectionner parmi les etudiants autorisés à s'inscrire.
|
|
Principe:
|
|
- trouver liste d'etud, par semestre
|
|
- afficher chaque semestre "boites" avec cases à cocher
|
|
- si l'étudiant est déjà inscrit, le signaler (gras, nom de groupes): il peut être désinscrit
|
|
- on peut choisir les groupes TD, TP, TA
|
|
- seuls les étudiants non inscrits changent (de groupe)
|
|
- les étudiants inscrit qui se trouvent décochés sont désinscrits
|
|
- Confirmation: indiquer les étudiants inscrits et ceux désinscrits, le total courant.
|
|
|
|
"""
|
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
|
inscrit_groupes = int(inscrit_groupes)
|
|
inscrit_parcours = int(inscrit_parcours)
|
|
ignore_jury = int(ignore_jury)
|
|
# -- check lock
|
|
if not formsemestre.etat:
|
|
raise ScoValueError("opération impossible: semestre verrouille")
|
|
H = []
|
|
etuds = [] if etuds is None else etuds
|
|
if isinstance(etuds, str):
|
|
# string, vient du form de confirmation
|
|
etuds = [int(x) for x in etuds.split(",") if x]
|
|
elif isinstance(etuds, int):
|
|
etuds = [etuds]
|
|
elif etuds and isinstance(etuds[0], str):
|
|
etuds = [int(x) for x in etuds]
|
|
|
|
auth_etuds_by_sem, inscrits, candidats = _list_authorized_etuds_by_sem(
|
|
formsemestre, ignore_jury=ignore_jury
|
|
)
|
|
etuds_set = set(etuds)
|
|
candidats_set = set(candidats)
|
|
inscrits_set = set(inscrits)
|
|
candidats_non_inscrits = candidats_set - inscrits_set
|
|
inscrits_ailleurs = set(list_inscrits_date(formsemestre))
|
|
|
|
def set_to_sorted_etud_list(etudset) -> list[Identite]:
|
|
etuds = [candidats[etudid] for etudid in etudset]
|
|
etuds.sort(key=lambda e: e.sort_key)
|
|
return etuds
|
|
|
|
if submitted:
|
|
a_inscrire = etuds_set.intersection(candidats_set) - inscrits_set
|
|
a_desinscrire = inscrits_set - etuds_set
|
|
else:
|
|
a_inscrire = a_desinscrire = []
|
|
|
|
if not submitted:
|
|
H += _build_page(
|
|
formsemestre,
|
|
auth_etuds_by_sem,
|
|
inscrits,
|
|
candidats_non_inscrits,
|
|
inscrits_ailleurs,
|
|
inscrit_groupes=inscrit_groupes,
|
|
inscrit_parcours=inscrit_parcours,
|
|
ignore_jury=ignore_jury,
|
|
with_apo_cols=False,
|
|
)
|
|
else:
|
|
if not dialog_confirmed:
|
|
# Confirmation
|
|
if a_inscrire:
|
|
H.append("<h3>Étudiants à inscrire</h3><ol>")
|
|
for etud in set_to_sorted_etud_list(a_inscrire):
|
|
H.append(f"<li>{etud.nomprenom}</li>")
|
|
H.append("</ol>")
|
|
a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire)
|
|
if a_inscrire_en_double:
|
|
H.append("<h3>dont étudiants déjà inscrits:</h3><ul>")
|
|
for etud in set_to_sorted_etud_list(a_inscrire_en_double):
|
|
H.append(f'<li class="inscrit-ailleurs">{etud.nomprenom}</li>')
|
|
H.append("</ul>")
|
|
if a_desinscrire:
|
|
H.append("<h3>Étudiants à désinscrire</h3><ol>")
|
|
a_desinscrire_ident = sorted(
|
|
(db.session.get(Identite, eid) for eid in a_desinscrire),
|
|
key=lambda x: x.sort_key,
|
|
)
|
|
for etud in a_desinscrire_ident:
|
|
H.append(f'<li class="desinscription">{etud.nomprenom}</li>')
|
|
H.append("</ol>")
|
|
todo = a_inscrire or a_desinscrire
|
|
if not todo:
|
|
H.append("""<h3>Il n'y a rien à modifier !</h3>""")
|
|
H.append(
|
|
scu.confirm_dialog(
|
|
dest_url=(
|
|
"formsemestre_inscr_passage" if todo else "formsemestre_status"
|
|
),
|
|
message="<p>Confirmer ?</p>" if todo else "",
|
|
add_headers=False,
|
|
cancel_url="formsemestre_inscr_passage?formsemestre_id="
|
|
+ str(formsemestre_id),
|
|
OK="Effectuer l'opération" if todo else "",
|
|
parameters={
|
|
"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,
|
|
},
|
|
)
|
|
)
|
|
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():
|
|
# Inscription des étudiants au nouveau semestre:
|
|
do_inscrit(
|
|
formsemestre,
|
|
a_inscrire,
|
|
inscrit_groupes=inscrit_groupes,
|
|
inscrit_parcours=inscrit_parcours,
|
|
)
|
|
# Désinscriptions:
|
|
do_desinscrit(formsemestre, a_desinscrire, check_has_dec_jury=False)
|
|
|
|
H.append(
|
|
f"""<h3>Opération effectuée</h3>
|
|
<ul>
|
|
<li><a class="stdlink" href="{
|
|
url_for("notes.formsemestre_inscr_passage",
|
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
|
|
}">Continuer les inscriptions</a>
|
|
</li>
|
|
<li><a class="stdlink" href="{
|
|
url_for("notes.formsemestre_status",
|
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
|
|
}">Tableau de bord du semestre</a>
|
|
</li>"""
|
|
)
|
|
partition = sco_groups.formsemestre_get_main_partition(formsemestre_id)
|
|
if (
|
|
partition["partition_id"]
|
|
!= sco_groups.formsemestre_get_main_partition(formsemestre_id)[
|
|
"partition_id"
|
|
]
|
|
): # il y a au moins une vraie partition
|
|
H.append(
|
|
f"""<li><a class="stdlink" href="{
|
|
url_for("scolar.partition_editor", scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=formsemestre_id)
|
|
}">Répartir les groupes de {partition["partition_name"]}</a></li>
|
|
"""
|
|
)
|
|
|
|
return render_template(
|
|
"formsemestre/synchro_etuds.j2",
|
|
title="Passage des étudiants",
|
|
content="\n".join(H),
|
|
)
|
|
|
|
|
|
def _build_page(
|
|
formsemestre: FormSemestre,
|
|
auth_etuds_by_sem,
|
|
inscrits,
|
|
candidats_non_inscrits,
|
|
inscrits_ailleurs,
|
|
inscrit_groupes=False,
|
|
inscrit_parcours=False,
|
|
ignore_jury=False,
|
|
with_apo_cols: bool = True,
|
|
):
|
|
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:
|
|
ignore_jury_checked = ""
|
|
H = [
|
|
f"""
|
|
<h2 class="formsemestre">Passages dans le semestre</h2>
|
|
<form name="f" method="post" action="{request.base_url}">
|
|
|
|
<input type="hidden" name="formsemestre_id" value="{formsemestre.id}"/>
|
|
|
|
<input type="submit" name="submitted" value="Appliquer les modifications"/>
|
|
<a href="#help">aide</a>
|
|
|
|
<input name="inscrit_groupes" type="checkbox" value="1"
|
|
{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()"
|
|
{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 et {len(candidats_non_inscrits)} candidats supplémentaires.
|
|
</div>
|
|
|
|
<div>{scu.EMO_WARNING}
|
|
<em>Seuls les semestres dont la date de fin est proche de la date de début
|
|
de ce semestre ({formsemestre.date_debut.strftime(scu.DATE_FMT)}) sont pris en
|
|
compte.</em>
|
|
</div>
|
|
{etuds_select_boxes(auth_etuds_by_sem, inscrits_ailleurs, with_apo_cols=with_apo_cols)}
|
|
|
|
<input type="submit" name="submitted" value="Appliquer les modifications"/>
|
|
|
|
{formsemestre_inscr_passage_help(formsemestre)}
|
|
|
|
</form>
|
|
""",
|
|
]
|
|
|
|
# Semestres sans étudiants autorisés
|
|
empty_sems = []
|
|
for formsemestre_id in auth_etuds_by_sem.keys():
|
|
if not auth_etuds_by_sem[formsemestre_id]["etuds"]:
|
|
empty_sems.append(auth_etuds_by_sem[formsemestre_id]["infos"])
|
|
if empty_sems:
|
|
H.append(
|
|
"""<div class="pas_empty_sems"><h3>Autres semestres sans candidats :</h3><ul>"""
|
|
)
|
|
for infos in empty_sems:
|
|
H.append(
|
|
"""<li><a class="stdlink" href="%(title_target)s">%(title)s</a></li>"""
|
|
% infos
|
|
)
|
|
H.append("""</ul></div>""")
|
|
|
|
return H
|
|
|
|
|
|
def formsemestre_inscr_passage_help(formsemestre: FormSemestre):
|
|
"texte d'aide en bas de la page passage des étudiants"
|
|
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
|
|
<a class="stdlink"
|
|
href="{
|
|
url_for("notes.formsemestre_status",
|
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id )
|
|
}">{formsemestre.titre_annee()}</a>,
|
|
et d'en désincrire si besoin.
|
|
</p>
|
|
<p>Les étudiants sont groupés par semestre d'origine. Ceux qui sont en caractères
|
|
<span class="deja-inscrit">gras</span> sont déjà inscrits dans le semestre destination.
|
|
Ceux qui sont en <span class="inscrit-ailleurs">gras et en rouge</span> sont inscrits
|
|
dans un <em>autre</em> semestre.
|
|
</p>
|
|
<p>Au départ, les étudiants déjà inscrits sont sélectionnés; vous pouvez ajouter
|
|
d'autres étudiants à inscrire dans le semestre destination.
|
|
</p>
|
|
|
|
<p>Si vous dé-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 (là aussi, pensez à les cocher dans
|
|
<a class="stdlink" href="{
|
|
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=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>
|
|
</div>
|
|
"""
|
|
|
|
|
|
def etuds_select_boxes(
|
|
auth_etuds_by_cat,
|
|
inscrits_ailleurs: dict = None,
|
|
sel_inscrits=True,
|
|
show_empty_boxes=False,
|
|
export_cat_xls=None,
|
|
base_url="",
|
|
read_only=False,
|
|
with_apo_cols: bool = True,
|
|
):
|
|
"""Boites pour selection étudiants par catégorie
|
|
auth_etuds_by_cat = { category : { 'info' : {}, 'etuds' : ... }
|
|
inscrits_ailleurs =
|
|
sel_inscrits=
|
|
export_cat_xls =
|
|
"""
|
|
inscrits_ailleurs = inscrits_ailleurs or {}
|
|
if export_cat_xls:
|
|
return etuds_select_box_xls(auth_etuds_by_cat[export_cat_xls])
|
|
|
|
H = [
|
|
"""
|
|
<div class="etuds_select_boxes">"""
|
|
] # "
|
|
# Élimine les boites vides:
|
|
auth_etuds_by_cat = {
|
|
k: auth_etuds_by_cat[k]
|
|
for k in auth_etuds_by_cat
|
|
if auth_etuds_by_cat[k]["etuds"]
|
|
}
|
|
for src_cat in auth_etuds_by_cat.keys():
|
|
infos = auth_etuds_by_cat[src_cat]["infos"]
|
|
infos["comment"] = infos.get("comment", "") # commentaire dans sous-titre boite
|
|
etuds = auth_etuds_by_cat[src_cat]["etuds"]
|
|
etuds.sort(key=itemgetter("nom"))
|
|
with_checkbox = (not read_only) and auth_etuds_by_cat[src_cat]["infos"].get(
|
|
"with_checkbox", True
|
|
)
|
|
if etuds or show_empty_boxes:
|
|
infos["nbetuds"] = len(etuds)
|
|
xls_url = (
|
|
scu.build_url_query(base_url, export_cat_xls=src_cat)
|
|
if base_url and etuds
|
|
else ""
|
|
)
|
|
H.append(
|
|
etuds_select_box(
|
|
etuds,
|
|
infos,
|
|
with_checkbox=with_checkbox,
|
|
sel_inscrits=sel_inscrits,
|
|
xls_url=xls_url,
|
|
inscrits_ailleurs=inscrits_ailleurs,
|
|
with_apo_cols=with_apo_cols,
|
|
)
|
|
)
|
|
|
|
H.append("</div>")
|
|
return "\n".join(H)
|
|
|
|
|
|
def etuds_select_box(
|
|
etuds: list[dict],
|
|
infos: dict,
|
|
with_checkbox: bool = True,
|
|
sel_inscrits: bool = True,
|
|
xls_url: str = "",
|
|
inscrits_ailleurs: set = None,
|
|
with_apo_cols: bool = True,
|
|
) -> str:
|
|
"""HTML pour une "boite" avec une liste d'étudiants à sélectionner"""
|
|
inscrits_ailleurs = inscrits_ailleurs or {}
|
|
box_id = infos["id"]
|
|
H = []
|
|
H.append(
|
|
f"""<div class="pas_sembox" id="{box_id}">
|
|
<div class="pas_sembox_title"><a href="{infos['title_target']}" """
|
|
)
|
|
help_txt = infos.get("help")
|
|
if help_txt: # bubble
|
|
H.append(f'title="{help_txt}"')
|
|
H.append(
|
|
""">%(title)s</a></div>
|
|
<div class="pas_sembox_subtitle">(%(nbetuds)d étudiants%(comment)s)"""
|
|
% infos
|
|
)
|
|
if with_checkbox:
|
|
H.append(
|
|
f""" (Select.
|
|
<a href="#" class="stdlink" onclick="sem_select('{box_id}', true);">tous</a>
|
|
<a href="#" class="stdlink" onclick="sem_select('{box_id}', false );">aucun</a>"""
|
|
)
|
|
if sel_inscrits:
|
|
H.append(
|
|
f"""<a href="#"
|
|
class="stdlink" onclick="sem_select_inscrits('{box_id}');"
|
|
>inscrits</a>"""
|
|
)
|
|
if with_checkbox or sel_inscrits:
|
|
H.append(")")
|
|
if xls_url:
|
|
H.append(f'<a href="{xls_url}">{scu.ICON_XLS}</a> ')
|
|
H.append("</div>")
|
|
checkbox_title = """<th class="no-sort"></th>""" if with_checkbox else ""
|
|
ths = (
|
|
f"<th>Étape</th>{checkbox_title}<th>Nom</th><th>Paiement</th><th>Finalisé</th>"
|
|
if with_apo_cols
|
|
else f"{checkbox_title}<th>Nom</th>"
|
|
)
|
|
H.append(
|
|
f"""<table class="etuds-box">
|
|
<thead>
|
|
<tr>
|
|
{ths}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
"""
|
|
)
|
|
for etud in etuds:
|
|
is_inscrit = etud.get("inscrit", False)
|
|
extra_class = (
|
|
"deja-inscrit"
|
|
if is_inscrit
|
|
else ("inscrit-ailleurs" if etud["etudid"] in inscrits_ailleurs else "")
|
|
)
|
|
H.append(
|
|
_etud_row(
|
|
etud,
|
|
with_checkbox=with_checkbox,
|
|
checkbox_name=infos.get("checkbox_name", "etuds"),
|
|
etud_key=infos.get("etud_key", "etudid"),
|
|
is_inscrit=is_inscrit,
|
|
extra_class=extra_class,
|
|
with_apo_cols=with_apo_cols,
|
|
)
|
|
)
|
|
H.append(
|
|
"""</tbody>
|
|
</table>"""
|
|
)
|
|
|
|
H.append("</div>")
|
|
return "\n".join(H)
|
|
|
|
|
|
def _etud_row(
|
|
etud: dict,
|
|
with_checkbox: bool = True,
|
|
checkbox_name: str = "etuds",
|
|
etud_key: str = "",
|
|
is_inscrit: bool = False,
|
|
extra_class: str = "",
|
|
with_apo_cols: bool = True,
|
|
) -> str:
|
|
"""HTML 'row' for this etud"""
|
|
H = []
|
|
nomprenom = scu.format_nomprenom(etud)
|
|
if etud["etudid"]:
|
|
elink = f"""<a id="{etud['etudid']}" class="discretelink etudinfo {extra_class}"
|
|
href="{ url_for(
|
|
'scolar.fiche_etud',
|
|
scodoc_dept=g.scodoc_dept,
|
|
etudid=etud['etudid'],
|
|
)
|
|
}">{nomprenom}</a>
|
|
"""
|
|
else:
|
|
# ce n'est pas un etudiant ScoDoc
|
|
elink = nomprenom
|
|
|
|
checkbox_cell = (
|
|
f"""<td><input type="checkbox" name="{checkbox_name}:list"
|
|
value="{etud[etud_key]}" {'checked="checked"' if is_inscrit else ''}>
|
|
</td>
|
|
"""
|
|
if with_checkbox
|
|
else ""
|
|
)
|
|
paiement = etud.get("paiementinscription", True)
|
|
datefinalisation = etud.get("datefinalisationinscription")
|
|
if with_apo_cols:
|
|
H.append(
|
|
f"""
|
|
<tr class="{extra_class}">
|
|
<td class="etape">{etud.get("etape", "") or ""}</td>
|
|
{checkbox_cell}
|
|
<td data-order="{etud.get("nom", "").upper()}">{elink}</td>
|
|
<td class="paiement {'' if paiement else 'paspaye'}">{
|
|
'' if paiement else 'non paiement'
|
|
}</td>
|
|
<td class="finalise" data-order="{
|
|
datefinalisation.isoformat() if datefinalisation else ''
|
|
}">{"inscription finalisée le " + datefinalisation.strftime(scu.DATE_FMT)
|
|
if datefinalisation else "" }
|
|
</td>
|
|
</tr>
|
|
"""
|
|
)
|
|
else: # juste checkbox et nom
|
|
H.append(
|
|
f"""
|
|
<tr class="{extra_class}">
|
|
{checkbox_cell}
|
|
<td data-order="{etud.get("nom", "").upper()}">{elink}</td>
|
|
</tr>
|
|
"""
|
|
)
|
|
|
|
return "\n".join(H)
|
|
|
|
|
|
def etuds_select_box_xls(src_cat):
|
|
"export a box to excel"
|
|
etuds = src_cat["etuds"]
|
|
columns_ids = [
|
|
"etudid",
|
|
"ine",
|
|
"nip",
|
|
"civilite_str",
|
|
"nom",
|
|
"prenom",
|
|
"etape",
|
|
"paiementinscription_str",
|
|
"datefinalisationinscription",
|
|
]
|
|
titles = {x: x for x in columns_ids} | {
|
|
"paiementinscription_str": "Paiement inscr.",
|
|
"datefinalisationinscription": "Finalisation inscr.",
|
|
}
|
|
for e in etuds:
|
|
if not e.get("paiementinscription", True):
|
|
e["paiementinscription_str"] = "NON"
|
|
else:
|
|
e["paiementinscription_str"] = "-"
|
|
# si e est un étudiant Apo, on a nip et ine
|
|
# mais si e est ScoDoc, on a code_nip et code_ine:
|
|
e["nip"] = e.get("nip", e.get("code_nip"))
|
|
e["ine"] = e.get("ine", e.get("code_ine"))
|
|
# Pour excel, datefinalisationinscription doit être datetime
|
|
dat = e.get("datefinalisationinscription")
|
|
if isinstance(dat, datetime.date):
|
|
e["datefinalisationinscription"] = datetime.datetime.combine(
|
|
dat, datetime.time.min
|
|
)
|
|
tab = GenTable(
|
|
caption="%(title)s. %(help)s" % src_cat["infos"],
|
|
columns_ids=columns_ids,
|
|
preferences=sco_preferences.SemPreferences(),
|
|
rows=etuds,
|
|
table_id="etuds_select_box_xls",
|
|
titles=titles,
|
|
)
|
|
return tab.excel()
|