forked from ScoDoc/ScoDoc
913 lines
32 KiB
Python
913 lines
32 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
|
|
#
|
|
##############################################################################
|
|
|
|
"""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 Partition, GroupDescr
|
|
from app.models.scolar_event import ScolarEvent
|
|
import app.scodoc.sco_utils as scu
|
|
from app import log
|
|
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(scu.DATE_FMT),
|
|
"formsemestre_id": args["formsemestre_id"],
|
|
"event_type": "INSCRIPTION",
|
|
},
|
|
)
|
|
# Log etudiant
|
|
Scolog.logdb(
|
|
method=method,
|
|
etudid=args["etudid"],
|
|
msg=f"inscription en semestre {args['formsemestre_id']}",
|
|
commit=True,
|
|
)
|
|
#
|
|
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)
|
|
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 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 = db.session.get(Identite, 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.
|
|
Si semestre extérieur et dernier inscrit, suppression de ce semestre.
|
|
"""
|
|
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
|
|
if check_has_dec_jury:
|
|
check_if_has_decision_jury(formsemestre, [etudid])
|
|
|
|
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 de tous les groupes des partitions de ce semestre
|
|
Partition.formsemestre_remove_etud(formsemestre_id, etud)
|
|
|
|
# -- désincription du semestre
|
|
do_formsemestre_inscription_delete(
|
|
insem["formsemestre_inscription_id"], formsemestre_id=formsemestre_id
|
|
)
|
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
|
|
# --- Semestre extérieur
|
|
if formsemestre.modalite == "EXT":
|
|
if 0 == len(formsemestre.inscriptions):
|
|
log(
|
|
f"""do_formsemestre_desinscription:
|
|
suppression du semestre extérieur {formsemestre}"""
|
|
)
|
|
db.session.delete(formsemestre)
|
|
db.session.commit()
|
|
flash(f"Semestre extérieur supprimé: {formsemestre.titre_annee()}")
|
|
|
|
Scolog.logdb(
|
|
method="formsemestre_desinscription",
|
|
etudid=etudid,
|
|
msg=f"desinscription semestre {formsemestre_id}",
|
|
commit=True,
|
|
)
|
|
|
|
|
|
def do_formsemestre_inscription_with_modules(
|
|
formsemestre_id,
|
|
etudid,
|
|
group_ids: list = None,
|
|
etat=scu.INSCRIT,
|
|
etape=None,
|
|
method="inscription_with_modules",
|
|
dept_id: int = None,
|
|
):
|
|
"""Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
|
|
(donc sauf le sport)
|
|
Si dept_id est spécifié, utilise ce département au lieu du courant.
|
|
"""
|
|
group_ids = group_ids or []
|
|
if isinstance(group_ids, int):
|
|
group_ids = [group_ids]
|
|
# Check that all groups exist before creating the inscription
|
|
groups = [GroupDescr.query.get_or_404(group_id) for group_id in group_ids]
|
|
formsemestre = FormSemestre.get_formsemestre(formsemestre_id, dept_id=dept_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} # empeche doublons
|
|
|
|
# 2- inscrit aux groupes
|
|
for group in groups:
|
|
if group.id not in gdone:
|
|
sco_groups.set_group(etudid, group_id)
|
|
gdone.add(group_id)
|
|
|
|
# Inscription à tous les modules de ce semestre
|
|
for modimpl in formsemestre.modimpls:
|
|
if modimpl.module.ue.type != UE_SPORT:
|
|
sco_moduleimpl.do_moduleimpl_inscription_create(
|
|
{"moduleimpl_id": modimpl.id, "etudid": etudid},
|
|
formsemestre_id=formsemestre_id,
|
|
)
|
|
# Mise à jour des inscriptions aux parcours:
|
|
formsemestre.update_inscriptions_parcours_from_groups(etudid=etudid)
|
|
|
|
|
|
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 & 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.fiche_etud", 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.fiche_etud", 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.fiche_etud", 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 = Identite.get_etud(etudid)
|
|
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(),
|
|
f"""<h2>Inscription de {etud.nomprenom} aux modules de {formsemestre.titre_mois()}</h2>""",
|
|
]
|
|
|
|
# Cherche les moduleimpls et les inscriptions
|
|
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 modimpl in formsemestre.modimpls:
|
|
ue_id = modimpl.module.ue.id
|
|
if not ue_id in ue_ids:
|
|
ues.append(modimpl.module.ue)
|
|
ue_ids.add(ue_id)
|
|
modimpls_by_ue_ids[ue_id].append(modimpl.id)
|
|
|
|
modimpls_by_ue_names[ue_id].append(
|
|
f"{modimpl.module.code or ''} {modimpl.module.titre or ''}"
|
|
)
|
|
vals = scu.get_request_args()
|
|
if not vals.get("tf_submitted", False):
|
|
# inscrit ?
|
|
for ins in inscr:
|
|
if ins["moduleimpl_id"] == modimpl.id:
|
|
key = f"moduleimpls_{ue_id}"
|
|
if key in initvalues:
|
|
initvalues[key].append(str(modimpl.id))
|
|
else:
|
|
initvalues[key] = [str(modimpl.id)]
|
|
break
|
|
|
|
descr = [
|
|
("formsemestre_id", {"input_type": "hidden"}),
|
|
("etudid", {"input_type": "hidden"}),
|
|
]
|
|
for ue in ues:
|
|
ue_id = ue.id
|
|
ue_descr = ue.acronyme
|
|
if ue.type != UE_STANDARD:
|
|
ue_descr += f" <em>{UE_TYPE_NAME[ue.type]}</em>"
|
|
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 += f"""
|
|
<a class="discretelink" href="{ url_for(
|
|
'notes.formsemestre_bulletinetud', scodoc_dept=g.scodoc_dept,
|
|
formsemestre_id=sem_origin["formsemestre_id"],
|
|
etudid = etudid
|
|
)}" title="{sem_origin['titreannee']}">(capitalisée le {
|
|
ndb.DateISOtoDMY(ue_status["event_date"])
|
|
})
|
|
"""
|
|
descr.append(
|
|
(
|
|
f"sec_{ue_id}",
|
|
{
|
|
"input_type": "separator",
|
|
"title": f"""<b>{ue_descr} :</b>
|
|
<a href="#" onclick="chkbx_select('{ue_id}', true);">inscrire</a> | <a
|
|
href="#" onclick="chkbx_select('{ue_id}', false);">désinscrire</a>
|
|
à tous les modules
|
|
""",
|
|
},
|
|
)
|
|
)
|
|
descr.append(
|
|
(
|
|
f"moduleimpls_{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
|
|
if tf[0] == -1:
|
|
return flask.redirect(
|
|
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
|
)
|
|
|
|
# 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.id for x in formsemestre.modimpls])
|
|
insdict = {}
|
|
for ins in inscr:
|
|
insdict[ins["moduleimpl_id"]] = ins
|
|
for ue in ues:
|
|
for moduleimpl_id in [int(x) for x in tf[2][f"moduleimpls_{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:
|
|
a_inscrire.update(
|
|
int(x) for x in tf[2][f"moduleimpls_{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:
|
|
modimpls_by_id = {modimpl.id: modimpl for modimpl in formsemestre.modimpls}
|
|
#
|
|
if (not a_inscrire) and (not a_desinscrire):
|
|
H.append(
|
|
f"""<h3>Aucune modification à effectuer</h3>
|
|
<p><a class="stdlink" href="{
|
|
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
|
}">retour à la fiche étudiant</a></p>
|
|
"""
|
|
)
|
|
return "\n".join(H) + footer
|
|
|
|
H.append("<h3>Confirmer les modifications:</h3>")
|
|
if a_desinscrire:
|
|
H.append(
|
|
f"""<p>{etud.nomprenom} va être <b>désinscrit{etud.e}</b> des modules:<ul><li>"""
|
|
)
|
|
H.append(
|
|
"</li><li>".join(
|
|
[
|
|
f"""{modimpls_by_id[x].module.titre or ''} ({
|
|
modimpls_by_id[x].module.code or '(module sans code)'})"""
|
|
for x in a_desinscrire
|
|
]
|
|
)
|
|
+ "</p>"
|
|
)
|
|
H.append("</li></ul>")
|
|
if a_inscrire:
|
|
H.append(
|
|
f"""<p>{etud.nomprenom} va être <b>inscrit{etud.e}</b> aux modules:<ul><li>"""
|
|
)
|
|
H.append(
|
|
"</li><li>".join(
|
|
[
|
|
f"""{modimpls_by_id[x].module.titre or ''} ({
|
|
modimpls_by_id[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(
|
|
f"""
|
|
<form action="do_moduleimpl_incription_options">
|
|
<input type="hidden" name="etudid" value="{etudid}"/>
|
|
<input type="hidden" name="modulesimpls_ainscrire" value="{modulesimpls_ainscrire}"/>
|
|
<input type="hidden" name="modulesimpls_adesinscrire" value="{modulesimpls_adesinscrire}"/>
|
|
<input type ="submit" value="Confirmer"/>
|
|
<input type ="button" value="Annuler" onclick="document.location='{
|
|
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
|
}';"/>
|
|
</form>
|
|
"""
|
|
)
|
|
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(f"inscription: invalid moduleimpl_id: {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(
|
|
f"desinscription: invalid moduleimpl_id: {moduleimpl_id}"
|
|
)
|
|
mod = mods[0]
|
|
inscr = sco_moduleimpl.do_moduleimpl_inscription_list(
|
|
moduleimpl_id=moduleimpl_id, etudid=etudid
|
|
)
|
|
if not inscr:
|
|
raise ScoValueError(
|
|
f"pas inscrit a ce module ! (etudid={etudid}, moduleimpl_id={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(),
|
|
f"""<h3>Modifications effectuées</h3>
|
|
<p><a class="stdlink" href="{
|
|
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
|
}">
|
|
Retour à la fiche étudiant</a>
|
|
</p>
|
|
""",
|
|
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é.
|
|
"""
|
|
H = [
|
|
html_sco_header.html_sem_header(
|
|
"Inscriptions multiples parmi les étudiants du semestre ",
|
|
init_qtip=True,
|
|
javascripts=["js/etud_info.js"],
|
|
)
|
|
]
|
|
insd = list_inscrits_ailleurs(formsemestre_id)
|
|
# liste ordonnée par nom
|
|
etudlist = [Identite.get_etud(etudid) for etudid, sems in insd.items() if sems]
|
|
etudlist.sort(key=lambda x: x.sort_key)
|
|
if etudlist:
|
|
H.append("<ul>")
|
|
for etud in etudlist:
|
|
H.append(
|
|
f"""<li><a id="{etud.id}" class="discretelink etudinfo"
|
|
href={
|
|
url_for(
|
|
"scolar.fiche_etud",
|
|
scodoc_dept=g.scodoc_dept,
|
|
etudid=etud.id,
|
|
)
|
|
}
|
|
>{etud.nomprenom}</a> :
|
|
"""
|
|
)
|
|
l = []
|
|
for s in insd[etud.id]:
|
|
l.append(
|
|
f"""<a class="discretelink" href="{
|
|
url_for('notes.formsemestre_status',
|
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id
|
|
)}">{s['titremois']}</a>"""
|
|
)
|
|
H.append(", ".join(l))
|
|
H.append("</li>")
|
|
H.append(
|
|
f"""
|
|
</ul>
|
|
<p><b>Total: {len(etudlist)} étudiants concernés.</b></p>
|
|
|
|
<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 !
|
|
</p>
|
|
<p>
|
|
<b>Sauf exception, cette situation est anormale:</b>
|
|
</p>
|
|
<ul>
|
|
<li>vérifier que les dates des semestres se suivent <em>sans se chevaucher</em>
|
|
</li>
|
|
<li>ou bien 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()
|