ScoDoc/app/scodoc/sco_formsemestre_inscriptions.py

912 lines
33 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2023 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
#
##############################################################################
"""Opérations d'inscriptions aux semestres et modules
"""
import collections
import time
import flask
from flask import flash, url_for, g, request
from app import db
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.validations import ScolarEvent
import app.scodoc.sco_utils as scu
from app import log
from app.scodoc.scolog import logdb
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.codes_cursus import UE_STANDARD, UE_SPORT, UE_TYPE_NAME
import app.scodoc.notesdb as ndb
from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc import sco_find_etud
from app.scodoc import sco_formsemestre
from app.scodoc import sco_moduleimpl
from app.scodoc import sco_groups
from app.scodoc import sco_etud
from app.scodoc import sco_cache
from app.scodoc import html_sco_header
# --- Gestion des inscriptions aux semestres
_formsemestre_inscriptionEditor = ndb.EditableTable(
"notes_formsemestre_inscription",
"formsemestre_inscription_id",
("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat", "etape"),
sortkey="formsemestre_id",
insert_ignore_conflicts=True,
)
def do_formsemestre_inscription_list(*args, **kw):
"list formsemestre_inscriptions"
cnx = ndb.GetDBConnexion()
return _formsemestre_inscriptionEditor.list(cnx, *args, **kw)
def do_formsemestre_inscription_listinscrits(formsemestre_id):
"""Liste les inscrits (état I) à ce semestre et cache le résultat.
Result: [ { "etudid":, "formsemestre_id": , "etat": , "etape": }]
"""
r = sco_cache.SemInscriptionsCache.get(formsemestre_id)
if r is None:
# retreive list
r = do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id, "etat": scu.INSCRIT}
)
sco_cache.SemInscriptionsCache.set(formsemestre_id, r)
return r
def do_formsemestre_inscription_create(args, method=None):
"create a formsemestre_inscription (and sco event)"
cnx = ndb.GetDBConnexion()
log(f"do_formsemestre_inscription_create: args={args}")
sems = sco_formsemestre.do_formsemestre_list(
{"formsemestre_id": args["formsemestre_id"]}
)
if len(sems) != 1:
raise ScoValueError(f"code de semestre invalide: {args['formsemestre_id']}")
sem = sems[0]
# check lock
if not sem["etat"]:
raise ScoValueError("inscription: semestre verrouille")
#
r = _formsemestre_inscriptionEditor.create(cnx, args)
# Evenement
sco_etud.scolar_events_create(
cnx,
args={
"etudid": args["etudid"],
"event_date": time.strftime("%d/%m/%Y"),
"formsemestre_id": args["formsemestre_id"],
"event_type": "INSCRIPTION",
},
)
# Log etudiant
logdb(
cnx,
method=method,
etudid=args["etudid"],
msg=f"inscription en semestre {args['formsemestre_id']}",
commit=False,
)
#
sco_cache.invalidate_formsemestre(formsemestre_id=args["formsemestre_id"])
return r
def do_formsemestre_inscription_delete(oid, formsemestre_id=None):
"delete formsemestre_inscription"
cnx = ndb.GetDBConnexion()
_formsemestre_inscriptionEditor.delete(cnx, oid)
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
def do_formsemestre_demission(
etudid,
formsemestre_id,
event_date=None,
etat_new=scu.DEMISSION, # DEMISSION or DEF
operation_method="dem_etudiant",
event_type="DEMISSION",
):
"Démission ou défaillance d'un étudiant"
# marque 'D' ou DEF dans l'inscription au semestre et ajoute
# un "evenement" scolarite
if etat_new not in (scu.DEF, scu.DEMISSION):
raise ScoValueError("nouveau code d'état invalide")
try:
event_date_iso = ndb.DateDMYtoISO(event_date)
except ValueError as exc:
raise ScoValueError("format de date invalide") from exc
etud = Identite.get_etud(etudid)
# check lock
formsemestre: FormSemestre = FormSemestre.query.filter_by(
id=formsemestre_id, dept_id=g.scodoc_dept_id
).first_or_404()
if not formsemestre.etat:
raise ScoValueError("Modification impossible: semestre verrouille")
#
if formsemestre_id not in (inscr.formsemestre_id for inscr in etud.inscriptions()):
raise ScoValueError("étudiant non inscrit dans ce semestre !")
inscr = next(
inscr
for inscr in etud.inscriptions()
if inscr.formsemestre_id == formsemestre_id
)
inscr.etat = etat_new
db.session.add(inscr)
Scolog.logdb(method=operation_method, etudid=etudid)
event = ScolarEvent(
etudid=etudid,
event_date=event_date_iso,
formsemestre_id=formsemestre_id,
event_type=event_type,
)
db.session.add(event)
db.session.commit()
sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id
) # > démission ou défaillance
if etat_new == scu.DEMISSION:
flash("Démission enregistrée")
elif etat_new == scu.DEF:
flash("Défaillance enregistrée")
def do_formsemestre_inscription_edit(args=None, formsemestre_id=None):
"edit a formsemestre_inscription"
cnx = ndb.GetDBConnexion()
_formsemestre_inscriptionEditor.edit(cnx, args)
sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id
) # > modif inscription semestre
def do_formsemestre_desinscription(etudid, formsemestre_id):
"""Désinscription d'un étudiant.
Si semestre extérieur et dernier inscrit, suppression de ce semestre.
"""
from app.scodoc import sco_formsemestre_edit
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
etud = Identite.get_etud(etudid)
# -- check lock
if not formsemestre.etat:
raise ScoValueError("désinscription impossible: semestre verrouille")
# -- Si decisions de jury, désinscription interdite
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
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(
args={"formsemestre_id": formsemestre_id, "etudid": etudid}
)
if not insem:
raise ScoValueError(f"{etud.nomprenom} n'est pas inscrit au semestre !")
insem = insem[0]
# -- desinscription de tous les modules
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
cursor.execute(
"""SELECT Im.id AS moduleimpl_inscription_id
FROM notes_moduleimpl_inscription Im, notes_moduleimpl M
WHERE Im.etudid=%(etudid)s
and Im.moduleimpl_id = M.id
and M.formsemestre_id = %(formsemestre_id)s
""",
{"etudid": etudid, "formsemestre_id": formsemestre_id},
)
res = cursor.fetchall()
moduleimpl_inscription_ids = [x[0] for x in res]
for moduleimpl_inscription_id in moduleimpl_inscription_ids:
sco_moduleimpl.do_moduleimpl_inscription_delete(
moduleimpl_inscription_id, formsemestre_id=formsemestre_id
)
# -- désincription du semestre
do_formsemestre_inscription_delete(
insem["formsemestre_inscription_id"], formsemestre_id=formsemestre_id
)
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
# --- Semestre extérieur
if formsemestre.modalite == "EXT":
inscrits = do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id}
)
nbinscrits = len(inscrits)
if nbinscrits == 0:
log(
f"""do_formsemestre_desinscription:
suppression du semestre extérieur {formsemestre}"""
)
flash("Semestre exterieur supprimé")
sco_formsemestre_edit.do_formsemestre_delete(formsemestre_id)
logdb(
cnx,
method="formsemestre_desinscription",
etudid=etudid,
msg="desinscription semestre %s" % formsemestre_id,
commit=False,
)
def do_formsemestre_inscription_with_modules(
formsemestre_id,
etudid,
group_ids: list = None,
etat=scu.INSCRIT,
etape=None,
method="inscription_with_modules",
):
"""Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
(donc sauf le sport)
"""
group_ids = group_ids or []
if isinstance(group_ids, int):
group_ids = [group_ids]
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
# inscription au semestre
args = {"formsemestre_id": formsemestre_id, "etudid": etudid}
if etat is not None:
args["etat"] = etat
if etape is not None:
args["etape"] = etape
do_formsemestre_inscription_create(args, method=method)
log(
f"""do_formsemestre_inscription_with_modules: etudid={
etudid} formsemestre_id={formsemestre_id}"""
)
# inscriptions aux groupes
# 1- inscrit au groupe 'tous'
group_id = sco_groups.get_default_group(formsemestre_id)
sco_groups.set_group(etudid, group_id)
gdone = {group_id: 1} # empeche doublons
# 2- inscrit aux groupes
for group_id in group_ids:
if group_id and group_id not in gdone:
group = GroupDescr.query.get_or_404(group_id)
sco_groups.set_group(etudid, group_id)
gdone[group_id] = 1
# Inscription à tous les modules de ce semestre
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
)
for mod in modimpls:
if mod["ue"]["type"] != UE_SPORT:
sco_moduleimpl.do_moduleimpl_inscription_create(
{"moduleimpl_id": mod["moduleimpl_id"], "etudid": etudid},
formsemestre_id=formsemestre_id,
)
# Mise à jour des inscriptions aux parcours:
formsemestre.update_inscriptions_parcours_from_groups()
def formsemestre_inscription_with_modules_etud(
formsemestre_id, etudid=None, group_ids=None
):
"""Form. inscription d'un étudiant au semestre.
Si etudid n'est pas specifié, form. choix etudiant.
"""
if etudid is None:
return sco_find_etud.form_search_etud(
title="Choix de l'étudiant à inscrire dans ce semestre",
add_headers=True,
dest_url="notes.formsemestre_inscription_with_modules_etud",
parameters={"formsemestre_id": formsemestre_id},
parameters_keys="formsemestre_id",
)
return formsemestre_inscription_with_modules(
etudid, formsemestre_id, group_ids=group_ids
)
def formsemestre_inscription_with_modules_form(etudid, only_ext=False):
"""Formulaire inscription de l'etud dans l'un des semestres existants.
Si only_ext, ne montre que les semestre extérieurs.
"""
etud: Identite = Identite.query.filter_by(
id=etudid, dept_id=g.scodoc_dept_id
).first_or_404()
H = [
html_sco_header.sco_header(),
f"<h2>Inscription de {etud.nomprenom}",
]
if only_ext:
H.append(" dans un semestre extérieur")
H.append(
"""</h2>
<p class="help">L'étudiant sera inscrit à <em>tous</em> les modules du semestre
choisi (sauf Sport &amp; Culture).
</p>
<h3>Choisir un semestre:</h3>"""
)
footer = html_sco_header.sco_footer()
# sems = sco_formsemestre.do_formsemestre_list(args={"etat": "1"})
formsemestres = (
FormSemestre.query.filter_by(etat=True, dept_id=g.scodoc_dept_id)
.join(Formation)
.order_by(
Formation.acronyme,
FormSemestre.semestre_id,
FormSemestre.modalite,
FormSemestre.date_debut,
)
.all()
)
if len(formsemestres):
H.append("<ul>")
for formsemestre in formsemestres:
# Ne propose que les semestres où etudid n'est pas déjà inscrit
if formsemestre.id not in {
ins.formsemestre_id for ins in etud.inscriptions()
}:
if (not only_ext) or (formsemestre.modalite == "EXT"):
H.append(
f"""
<li>
<a class="stdlink" href="{
url_for("notes.formsemestre_inscription_with_modules",
scodoc_dept=g.scodoc_dept,
etudid=etudid, formsemestre_id=formsemestre.id
)}">{formsemestre.titre_mois()}</a>
</li>
"""
)
H.append("</ul>")
else:
H.append("<p>aucune session de formation !</p>")
H.append(
f"""<h3>ou</h3> <a class="stdlink" href="{
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}">retour à la fiche de {etud.nomprenom}</a>"""
)
return "\n".join(H) + footer
def formsemestre_inscription_with_modules(
etudid, formsemestre_id, group_ids=None, multiple_ok=False
):
"""
Inscription de l'etud dans ce semestre.
Formulaire avec choix groupe.
"""
log(
f"""formsemestre_inscription_with_modules: etudid={etudid} formsemestre_id={
formsemestre_id} group_ids={group_ids}"""
)
if multiple_ok:
multiple_ok = int(multiple_ok)
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
etud = Identite.get_etud(etudid)
if etud.dept_id != formsemestre.dept_id:
raise ScoValueError("l'étudiant n'est pas dans ce département")
H = [
html_sco_header.html_sem_header(
f"Inscription de {etud.nomprenom} dans ce semestre",
)
]
footer = html_sco_header.sco_footer()
# Check 1: déjà inscrit ici ?
inscr = FormSemestreInscription.query.filter_by(
etudid=etud.id, formsemestre_id=formsemestre.id
).first()
if inscr is not None:
H.append(
f"""
<p class="warning">{etud.nomprenom} est déjà inscrit
dans le semestre {formsemestre.titre_mois()}
</p>
<ul>
<li><a href="{url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
}" class="stdlink">retour à la fiche de {etud.nomprenom}</a>
</li>
<li><a href="{url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)}" class="stdlink">retour au tableau de bord de {formsemestre.titre_mois()}</a></li>
</ul>
"""
)
return "\n".join(H) + footer
# Check 2: déjà inscrit dans un semestre recouvrant les même dates ?
# Informe et propose dé-inscriptions
others = est_inscrit_ailleurs(etudid, formsemestre_id)
if others and not multiple_ok:
l = []
for s in others:
l.append(
f"""<a class="discretelink" href="{
url_for("notes.formsemestre_status",
scodoc_dept=g.scodoc_dept, formsemestre_id=s['formsemestre_id'])
}">{s['titremois']}</a>"""
)
H.append(
f"""<p class="warning">Attention: {etud.nomprenom} est déjà inscrit sur
la même période dans: {", ".join(l)}.
</p>"""
)
H.append("<ul>")
for s in others:
H.append(
f"""<li><a href="{
url_for("notes.formsemestre_desinscription", scodoc_dept=g.scodoc_dept,
formsemestre_id=s["formsemestre_id"], etudid=etudid )
}" class="stdlink">désinscrire de {s["titreannee"]}
</li>"""
)
H.append("</ul>")
H.append(
f"""<p><a href="{ url_for( "notes.formsemestre_inscription_with_modules",
scodoc_dept=g.scodoc_dept, etudid=etudid, formsemestre_id=formsemestre_id,
multiple_ok=1,
group_ids=group_ids )
}">Continuer quand même l'inscription</a>
</p>"""
# was sco_groups.make_query_groups(group_ids)
)
return "\n".join(H) + footer
#
if group_ids is not None:
# OK, inscription
do_formsemestre_inscription_with_modules(
formsemestre_id,
etudid,
group_ids=group_ids,
etat=scu.INSCRIT,
method="formsemestre_inscription_with_modules",
)
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
else:
# formulaire choix groupe
H.append(
f"""<form method="GET" name="groupesel" action="{request.base_url}">
<input type="hidden" name="etudid" value="{etudid}">
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}">
"""
)
H.append(sco_groups.form_group_choice(formsemestre_id, allow_none=True))
#
H.append(
"""
<input type="submit" value="Inscrire"/>
<p>Note: l'étudiant sera inscrit dans les groupes sélectionnés</p>
</form>
"""
)
return "\n".join(H) + footer
def formsemestre_inscription_option(etudid, formsemestre_id):
"""Dialogue pour (dés)inscription à des modules optionnels."""
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
if not sem["etat"]:
raise ScoValueError("Modification impossible: semestre verrouille")
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
footer = html_sco_header.sco_footer()
H = [
html_sco_header.sco_header()
+ "<h2>Inscription de %s aux modules de %s (%s - %s)</h2>"
% (etud["nomprenom"], sem["titre_num"], sem["date_debut"], sem["date_fin"])
]
# Cherche les moduleimpls et les inscriptions
mods = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
inscr = sco_moduleimpl.do_moduleimpl_inscription_list(etudid=etudid)
# Formulaire
modimpls_by_ue_ids = collections.defaultdict(list) # ue_id : [ moduleimpl_id ]
modimpls_by_ue_names = collections.defaultdict(list) # ue_id : [ moduleimpl_name ]
ues = []
ue_ids = set()
initvalues = {}
for mod in mods:
ue_id = mod["ue"]["ue_id"]
if not ue_id in ue_ids:
ues.append(mod["ue"])
ue_ids.add(ue_id)
modimpls_by_ue_ids[ue_id].append(mod["moduleimpl_id"])
modimpls_by_ue_names[ue_id].append(
"%s %s" % (mod["module"]["code"] or "", mod["module"]["titre"] or "")
)
vals = scu.get_request_args()
if not vals.get("tf_submitted", False):
# inscrit ?
for ins in inscr:
if ins["moduleimpl_id"] == mod["moduleimpl_id"]:
key = "moduleimpls_%s" % ue_id
if key in initvalues:
initvalues[key].append(str(mod["moduleimpl_id"]))
else:
initvalues[key] = [str(mod["moduleimpl_id"])]
break
descr = [
("formsemestre_id", {"input_type": "hidden"}),
("etudid", {"input_type": "hidden"}),
]
for ue in ues:
ue_id = ue["ue_id"]
ue_descr = ue["acronyme"]
if ue["type"] != UE_STANDARD:
ue_descr += " <em>%s</em>" % UE_TYPE_NAME[ue["type"]]
ue_status = nt.get_etud_ue_status(etudid, ue_id)
if ue_status and ue_status["is_capitalized"]:
sem_origin = sco_formsemestre.get_formsemestre(ue_status["formsemestre_id"])
ue_descr += (
' <a class="discretelink" href="formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s" title="%s">(capitalisée le %s)'
% (
sem_origin["formsemestre_id"],
etudid,
sem_origin["titreannee"],
ndb.DateISOtoDMY(ue_status["event_date"]),
)
)
descr.append(
(
"sec_%s" % ue_id,
{
"input_type": "separator",
"title": """<b>%s :</b> <a href="#" onclick="chkbx_select('%s', true);">inscrire</a> | <a href="#" onclick="chkbx_select('%s', false);">désinscrire</a> à tous les modules"""
% (ue_descr, ue_id, ue_id),
},
)
)
descr.append(
(
"moduleimpls_%s" % ue_id,
{
"input_type": "checkbox",
"title": "",
"dom_id": ue_id,
"allowed_values": [str(x) for x in modimpls_by_ue_ids[ue_id]],
"labels": modimpls_by_ue_names[ue_id],
"vertical": True,
},
)
)
H.append(
"""<script type="text/javascript">
function chkbx_select(field_id, state) {
var elems = document.getElementById(field_id).getElementsByTagName("input");
for (var i=0; i < elems.length; i++) {
elems[i].checked=state;
}
}
</script>
"""
)
tf = TrivialFormulator(
request.base_url,
scu.get_request_args(),
descr,
initvalues,
cancelbutton="Annuler",
submitlabel="Modifier les inscriptions",
cssclass="inscription",
name="tf",
)
if tf[0] == 0:
H.append(
"""
<p>Voici la liste des modules du semestre choisi.</p>
<p>
Les modules cochés sont ceux dans lesquels l'étudiant est inscrit.
Vous pouvez l'inscrire ou le désincrire d'un ou plusieurs modules.
</p>
<p>Attention: cette méthode ne devrait être utilisée que pour les modules
<b>optionnels</b> (ou les activités culturelles et sportives) et pour désinscrire
les étudiants dispensés (UE validées).
</p>
"""
)
return "\n".join(H) + "\n" + tf[1] + footer
elif tf[0] == -1:
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
else:
# Inscriptions aux modules choisis
# il faut desinscrire des modules qui ne figurent pas
# et inscrire aux autres, sauf si deja inscrit
a_desinscrire = {}.fromkeys([x["moduleimpl_id"] for x in mods])
insdict = {}
for ins in inscr:
insdict[ins["moduleimpl_id"]] = ins
for ue in ues:
ue_id = ue["ue_id"]
for moduleimpl_id in [int(x) for x in tf[2]["moduleimpls_%s" % ue_id]]:
if moduleimpl_id in a_desinscrire:
del a_desinscrire[moduleimpl_id]
# supprime ceux auxquel pas inscrit
moduleimpls_a_desinscrire = list(a_desinscrire.keys())
for moduleimpl_id in moduleimpls_a_desinscrire:
if moduleimpl_id not in insdict:
del a_desinscrire[moduleimpl_id]
a_inscrire = set()
for ue in ues:
ue_id = ue["ue_id"]
a_inscrire.update(
int(x) for x in tf[2]["moduleimpls_%s" % ue_id]
) # conversion en int !
# supprime ceux auquel deja inscrit:
for ins in inscr:
if ins["moduleimpl_id"] in a_inscrire:
a_inscrire.remove(ins["moduleimpl_id"])
# dict des modules:
modsdict = {}
for mod in mods:
modsdict[mod["moduleimpl_id"]] = mod
#
if (not a_inscrire) and (not a_desinscrire):
H.append(
"""<h3>Aucune modification à effectuer</h3>
<p><a class="stdlink" href="%s">retour à la fiche étudiant</a></p>
"""
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
return "\n".join(H) + footer
H.append("<h3>Confirmer les modifications:</h3>")
if a_desinscrire:
H.append(
"<p>%s va être <b>désinscrit%s</b> des modules:<ul><li>"
% (etud["nomprenom"], etud["ne"])
)
H.append(
"</li><li>".join(
[
"%s (%s)"
% (
modsdict[x]["module"]["titre"],
modsdict[x]["module"]["code"] or "(module sans code)",
)
for x in a_desinscrire
]
)
+ "</p>"
)
H.append("</li></ul>")
if a_inscrire:
H.append(
"<p>%s va être <b>inscrit%s</b> aux modules:<ul><li>"
% (etud["nomprenom"], etud["ne"])
)
H.append(
"</li><li>".join(
[
"%s (%s)"
% (
modsdict[x]["module"]["titre"],
modsdict[x]["module"]["code"] or "(module sans code)",
)
for x in a_inscrire
]
)
+ "</p>"
)
H.append("</li></ul>")
modulesimpls_ainscrire = ",".join(str(x) for x in a_inscrire)
modulesimpls_adesinscrire = ",".join(str(x) for x in a_desinscrire)
H.append(
"""<form action="do_moduleimpl_incription_options">
<input type="hidden" name="etudid" value="%s"/>
<input type="hidden" name="modulesimpls_ainscrire" value="%s"/>
<input type="hidden" name="modulesimpls_adesinscrire" value="%s"/>
<input type ="submit" value="Confirmer"/>
<input type ="button" value="Annuler" onclick="document.location='%s';"/>
</form>
"""
% (
etudid,
modulesimpls_ainscrire,
modulesimpls_adesinscrire,
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
)
)
return "\n".join(H) + footer
def do_moduleimpl_incription_options(
etudid, modulesimpls_ainscrire, modulesimpls_adesinscrire
):
"""
Effectue l'inscription et la description aux modules optionnels
"""
if isinstance(modulesimpls_ainscrire, int):
modulesimpls_ainscrire = str(modulesimpls_ainscrire)
if isinstance(modulesimpls_adesinscrire, int):
modulesimpls_adesinscrire = str(modulesimpls_adesinscrire)
if modulesimpls_ainscrire:
a_inscrire = [int(x) for x in modulesimpls_ainscrire.split(",")]
else:
a_inscrire = []
if modulesimpls_adesinscrire:
a_desinscrire = [int(x) for x in modulesimpls_adesinscrire.split(",")]
else:
a_desinscrire = []
# inscriptions
for moduleimpl_id in a_inscrire:
# verifie que ce module existe bien
mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
if len(mods) != 1:
raise ScoValueError(
"inscription: invalid moduleimpl_id: %s" % moduleimpl_id
)
mod = mods[0]
sco_moduleimpl.do_moduleimpl_inscription_create(
{"moduleimpl_id": moduleimpl_id, "etudid": etudid},
formsemestre_id=mod["formsemestre_id"],
)
# desinscriptions
for moduleimpl_id in a_desinscrire:
# verifie que ce module existe bien
mods = sco_moduleimpl.moduleimpl_list(moduleimpl_id=moduleimpl_id)
if len(mods) != 1:
raise ScoValueError(
"desinscription: invalid moduleimpl_id: %s" % moduleimpl_id
)
mod = mods[0]
inscr = sco_moduleimpl.do_moduleimpl_inscription_list(
moduleimpl_id=moduleimpl_id, etudid=etudid
)
if not inscr:
raise ScoValueError(
"pas inscrit a ce module ! (etudid=%s, moduleimpl_id=%s)"
% (etudid, moduleimpl_id)
)
oid = inscr[0]["moduleimpl_inscription_id"]
sco_moduleimpl.do_moduleimpl_inscription_delete(
oid, formsemestre_id=mod["formsemestre_id"]
)
H = [
html_sco_header.sco_header(),
"""<h3>Modifications effectuées</h3>
<p><a class="stdlink" href="%s">
Retour à la fiche étudiant</a></p>
"""
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
html_sco_header.sco_footer(),
]
return "\n".join(H)
def est_inscrit_ailleurs(etudid, formsemestre_id):
"""Vrai si l'étudiant est inscrit dans un semestre en même
temps que celui indiqué (par formsemestre_id).
Retourne la liste des semestres concernés (ou liste vide).
"""
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
debut_s = sem["dateord"]
fin_s = ndb.DateDMYtoISO(sem["date_fin"])
r = []
for s in etud["sems"]:
if s["formsemestre_id"] != formsemestre_id:
debut = s["dateord"]
fin = ndb.DateDMYtoISO(s["date_fin"])
if debut < fin_s and fin > debut_s:
r.append(s) # intersection
return r
def list_inscrits_ailleurs(formsemestre_id):
"""Liste des etudiants inscrits ailleurs en même temps que formsemestre_id.
Pour chacun, donne la liste des semestres.
{ etudid : [ liste de sems ] }
"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
etudids = nt.get_etudids()
d = {}
for etudid in etudids:
d[etudid] = est_inscrit_ailleurs(etudid, formsemestre_id)
return d
def formsemestre_inscrits_ailleurs(formsemestre_id):
"""Page listant les étudiants inscrits dans un autre semestre
dont les dates recouvrent le semestre indiqué.
"""
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [
html_sco_header.html_sem_header(
"Inscriptions multiples parmi les étudiants du semestre ",
)
]
insd = list_inscrits_ailleurs(formsemestre_id)
# liste ordonnée par nom
etudlist = [
sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
for etudid in insd.keys()
if insd[etudid]
]
etudlist.sort(key=lambda x: x["nom"])
if etudlist:
H.append("<ul>")
for etud in etudlist:
H.append(
'<li><a href="%s" class="discretelink">%s</a> : '
% (
url_for(
"scolar.ficheEtud",
scodoc_dept=g.scodoc_dept,
etudid=etud["etudid"],
),
etud["nomprenom"],
)
)
l = []
for s in insd[etud["etudid"]]:
l.append(
'<a class="discretelink" href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titremois)s</a>'
% s
)
H.append(", ".join(l))
H.append("</li>")
H.append("</ul>")
H.append("<p>Total: %d étudiants concernés.</p>" % len(etudlist))
H.append(
"""<p class="help">Ces étudiants sont inscrits dans le semestre sélectionné et aussi dans d'autres semestres qui se déroulent en même temps ! <br>Sauf exception, cette situation est anormale:</p>
<ul>
<li>vérifier que les dates des semestres se suivent sans se chevaucher</li>
<li>ou si besoin désinscrire le(s) étudiant(s) de l'un des semestres (via leurs fiches individuelles).</li>
</ul>
"""
)
else:
H.append("""<p>Aucun étudiant en inscription multiple (c'est normal) !</p>""")
return "\n".join(H) + html_sco_header.sco_footer()