419 lines
14 KiB
Python
419 lines
14 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
|
|
#
|
|
##############################################################################
|
|
|
|
"""Recherche d'étudiants
|
|
"""
|
|
import flask
|
|
from flask import url_for, g, request
|
|
from flask_login import current_user
|
|
|
|
import app
|
|
from app.models import Departement
|
|
import app.scodoc.sco_utils as scu
|
|
import app.scodoc.notesdb as ndb
|
|
from app.scodoc.gen_tables import GenTable
|
|
from app.scodoc import html_sco_header
|
|
from app.scodoc import sco_etud
|
|
from app.scodoc import sco_groups
|
|
from app.scodoc.sco_exceptions import ScoException
|
|
from app.scodoc.sco_permissions import Permission
|
|
from app.scodoc import sco_preferences
|
|
from app.scodoc import sco_utils as scu
|
|
|
|
|
|
def form_search_etud(
|
|
dest_url=None,
|
|
parameters=None,
|
|
parameters_keys=None,
|
|
title="Rechercher un étudiant par nom : ",
|
|
add_headers=False, # complete page
|
|
):
|
|
"form recherche par nom"
|
|
H = []
|
|
H.append(
|
|
f"""<form action="{ url_for("scolar.search_etud_in_dept", scodoc_dept=g.scodoc_dept) }" method="POST">
|
|
<b>{title}</b>
|
|
<input type="text" name="expnom" class="in-expnom" width="12" spellcheck="false" value="">
|
|
<input type="submit" value="Chercher">
|
|
<br>(entrer une partie du nom)
|
|
"""
|
|
)
|
|
if dest_url:
|
|
H.append('<input type="hidden" name="dest_url" value="%s"/>' % dest_url)
|
|
if parameters:
|
|
for param in parameters.keys():
|
|
H.append(
|
|
'<input type="hidden" name="%s" value="%s"/>'
|
|
% (param, parameters[param])
|
|
)
|
|
H.append(
|
|
'<input type="hidden" name="parameters_keys" value="%s"/>'
|
|
% (",".join(parameters.keys()))
|
|
)
|
|
elif parameters_keys:
|
|
if request.method == "POST":
|
|
vals = request.form
|
|
elif request.method == "GET":
|
|
vals = request.args
|
|
else:
|
|
vals = {}
|
|
for key in parameters_keys.split(","):
|
|
v = vals.get(key, False)
|
|
if v:
|
|
H.append('<input type="hidden" name="%s" value="%s"/>' % (key, v))
|
|
H.append(
|
|
'<input type="hidden" name="parameters_keys" value="%s"/>' % parameters_keys
|
|
)
|
|
H.append("</form>")
|
|
|
|
if add_headers:
|
|
return (
|
|
html_sco_header.sco_header(page_title="Choix d'un étudiant")
|
|
+ "\n".join(H)
|
|
+ html_sco_header.sco_footer()
|
|
)
|
|
else:
|
|
return "\n".join(H)
|
|
|
|
|
|
def search_etuds_infos_from_exp(expnom: str = "") -> list[dict]:
|
|
"""Cherche étudiants, expnom peut être, dans cet ordre:
|
|
un etudid (int), un code NIP, ou le début d'un nom.
|
|
"""
|
|
if not isinstance(expnom, int) and len(expnom) <= 1:
|
|
return [] # si expnom est trop court, n'affiche rien
|
|
try:
|
|
etudid = int(expnom)
|
|
except ValueError:
|
|
etudid = None
|
|
if etudid is not None:
|
|
etuds = sco_etud.get_etud_info(filled=True, etudid=expnom)
|
|
if len(etuds) == 1:
|
|
return etuds
|
|
expnom_str = str(expnom)
|
|
if scu.is_valid_code_nip(expnom_str):
|
|
return search_etuds_infos(code_nip=expnom_str)
|
|
return search_etuds_infos(expnom=expnom_str)
|
|
|
|
|
|
def search_etud_in_dept(expnom=""):
|
|
"""Page recherche d'un etudiant.
|
|
|
|
Affiche la fiche de l'étudiant, ou, si la recherche donne plusieurs résultats,
|
|
la liste des étudiants correspondants.
|
|
Appelée par:
|
|
- boite de recherche barre latérale gauche.
|
|
- choix d'un étudiant à inscrire (en POST avec dest_url et parameters_keys)
|
|
|
|
Args:
|
|
expnom: string, regexp sur le nom ou un code_nip ou un etudid
|
|
"""
|
|
etuds = search_etuds_infos_from_exp(expnom)
|
|
|
|
if request.method == "POST":
|
|
vals = request.form
|
|
elif request.method == "GET":
|
|
vals = request.args
|
|
else:
|
|
vals = {}
|
|
|
|
url_args = {"scodoc_dept": g.scodoc_dept}
|
|
if "dest_url" in vals:
|
|
endpoint = vals["dest_url"]
|
|
else:
|
|
endpoint = "scolar.ficheEtud"
|
|
if "parameters_keys" in vals:
|
|
for key in vals["parameters_keys"].split(","):
|
|
url_args[key] = vals[key]
|
|
|
|
if len(etuds) == 1:
|
|
# va directement a la fiche
|
|
url_args["etudid"] = etuds[0]["etudid"]
|
|
return flask.redirect(url_for(endpoint, **url_args))
|
|
|
|
H = [
|
|
html_sco_header.sco_header(
|
|
page_title="Recherche d'un étudiant",
|
|
no_side_bar=False,
|
|
init_qtip=True,
|
|
javascripts=["js/etud_info.js"],
|
|
)
|
|
]
|
|
if len(etuds) == 0 and len(etuds) <= 1:
|
|
H.append("""<h2>chercher un étudiant:</h2>""")
|
|
else:
|
|
H.append(
|
|
f"""<h2>{len(etuds)} résultats pour "<tt>{expnom}</tt>": choisissez un étudiant:</h2>"""
|
|
)
|
|
H.append(
|
|
form_search_etud(
|
|
dest_url=endpoint,
|
|
parameters=vals.get("parameters"),
|
|
parameters_keys=vals.get("parameters_keys"),
|
|
title="Autre recherche",
|
|
)
|
|
)
|
|
if len(etuds) > 0:
|
|
# Choix dans la liste des résultats:
|
|
for e in etuds:
|
|
url_args["etudid"] = e["etudid"]
|
|
target = url_for(endpoint, **url_args)
|
|
e["_nomprenom_target"] = target
|
|
e["inscription_target"] = target
|
|
e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
|
|
sco_groups.etud_add_group_infos(
|
|
e, e["cursem"]["formsemestre_id"] if e["cursem"] else None
|
|
)
|
|
|
|
tab = GenTable(
|
|
columns_ids=("nomprenom", "code_nip", "inscription", "groupes"),
|
|
titles={
|
|
"nomprenom": "Étudiant",
|
|
"code_nip": "NIP",
|
|
"inscription": "Inscription",
|
|
"groupes": "Groupes",
|
|
},
|
|
rows=etuds,
|
|
html_sortable=True,
|
|
html_class="table_leftalign",
|
|
preferences=sco_preferences.SemPreferences(),
|
|
)
|
|
H.append(tab.html())
|
|
if len(etuds) > 20: # si la page est grande
|
|
H.append(
|
|
form_search_etud(
|
|
dest_url=endpoint,
|
|
parameters=vals.get("parameters"),
|
|
parameters_keys=vals.get("parameters_keys"),
|
|
title="Autre recherche",
|
|
)
|
|
)
|
|
else:
|
|
H.append('<h2 style="color: red;">Aucun résultat pour "%s".</h2>' % expnom)
|
|
H.append(
|
|
"""<p class="help">La recherche porte sur tout ou partie du NOM ou du NIP de l'étudiant. Saisir au moins deux caractères.</p>"""
|
|
)
|
|
return "\n".join(H) + html_sco_header.sco_footer()
|
|
|
|
|
|
# Was chercheEtudsInfo()
|
|
def search_etuds_infos(expnom=None, code_nip=None):
|
|
"""recherche les étudiants correspondants à expnom ou au code_nip
|
|
et ramene liste de mappings utilisables en DTML.
|
|
"""
|
|
may_be_nip = scu.is_valid_code_nip(expnom)
|
|
cnx = ndb.GetDBConnexion()
|
|
if expnom and not may_be_nip:
|
|
expnom = expnom.upper() # les noms dans la BD sont en uppercase
|
|
try:
|
|
etuds = sco_etud.etudident_list(cnx, args={"nom": expnom}, test="~")
|
|
except ScoException:
|
|
etuds = []
|
|
else:
|
|
code_nip = code_nip or expnom
|
|
if code_nip:
|
|
etuds = sco_etud.etudident_list(cnx, args={"code_nip": str(code_nip)})
|
|
else:
|
|
etuds = []
|
|
sco_etud.fill_etuds_info(etuds)
|
|
return etuds
|
|
|
|
|
|
def search_etud_by_name(term: str) -> list:
|
|
"""Recherche noms étudiants par début du nom, pour autocomplete
|
|
Accepte aussi un début de code NIP (au moins 6 caractères)
|
|
Renvoie une liste de dicts
|
|
{ "label" : "<nip> <nom> <prenom>", "value" : etudid }
|
|
"""
|
|
may_be_nip = scu.is_valid_code_nip(term)
|
|
# term = term.upper() # conserve les accents
|
|
term = term.upper()
|
|
if (
|
|
not scu.ALPHANUM_EXP.match(term) # n'autorise pas les caractères spéciaux
|
|
and not may_be_nip
|
|
):
|
|
data = []
|
|
else:
|
|
if may_be_nip:
|
|
r = ndb.SimpleDictFetch(
|
|
"""SELECT nom, prenom, code_nip
|
|
FROM identite
|
|
WHERE
|
|
dept_id = %(dept_id)s
|
|
AND code_nip LIKE %(beginning)s
|
|
ORDER BY nom
|
|
""",
|
|
{"beginning": term + "%", "dept_id": g.scodoc_dept_id},
|
|
)
|
|
data = [
|
|
{
|
|
"label": "%s %s %s"
|
|
% (x["code_nip"], x["nom"], scu.format_prenom(x["prenom"])),
|
|
"value": x["code_nip"],
|
|
}
|
|
for x in r
|
|
]
|
|
else:
|
|
r = ndb.SimpleDictFetch(
|
|
"""SELECT id AS etudid, nom, prenom
|
|
FROM identite
|
|
WHERE
|
|
dept_id = %(dept_id)s
|
|
AND nom LIKE %(beginning)s
|
|
ORDER BY nom
|
|
""",
|
|
{"beginning": term + "%", "dept_id": g.scodoc_dept_id},
|
|
)
|
|
|
|
data = [
|
|
{
|
|
"label": "%s %s" % (x["nom"], scu.format_prenom(x["prenom"])),
|
|
"value": x["etudid"],
|
|
}
|
|
for x in r
|
|
]
|
|
return data
|
|
|
|
|
|
# ---------- Recherche sur plusieurs département
|
|
|
|
|
|
def search_etud_in_accessible_depts(expnom=None, code_nip=None):
|
|
"""
|
|
result is a list of (sorted) etuds, one list per dept.
|
|
"""
|
|
result = []
|
|
accessible_depts = []
|
|
depts = Departement.query.filter_by(visible=True).all()
|
|
for dept in depts:
|
|
if current_user.has_permission(Permission.ScoView, dept=dept.acronym):
|
|
if expnom or code_nip:
|
|
accessible_depts.append(dept.acronym)
|
|
app.set_sco_dept(dept.acronym)
|
|
etuds = search_etuds_infos(expnom=expnom, code_nip=code_nip)
|
|
else:
|
|
etuds = []
|
|
result.append(etuds)
|
|
return result, accessible_depts
|
|
|
|
|
|
def table_etud_in_accessible_depts(expnom=None):
|
|
"""
|
|
Page avec table étudiants trouvés, dans tous les departements.
|
|
Attention: nous sommes ici au niveau de ScoDoc, pas dans un département
|
|
"""
|
|
result, accessible_depts = search_etud_in_accessible_depts(expnom=expnom)
|
|
H = [
|
|
"""<div class="table_etud_in_accessible_depts">""",
|
|
"""<h3>Recherche multi-département de "<tt>%s</tt>"</h3>""" % expnom,
|
|
]
|
|
for etuds in result:
|
|
if etuds:
|
|
dept_id = etuds[0]["dept"]
|
|
# H.append('<h3>Département %s</h3>' % DeptId)
|
|
for e in etuds:
|
|
e["_nomprenom_target"] = url_for(
|
|
"scolar.ficheEtud", scodoc_dept=dept_id, etudid=e["etudid"]
|
|
)
|
|
e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
|
|
|
|
tab = GenTable(
|
|
titles={"nomprenom": "Étudiants en " + dept_id},
|
|
columns_ids=("nomprenom",),
|
|
rows=etuds,
|
|
html_sortable=True,
|
|
html_class="table_leftalign",
|
|
)
|
|
|
|
H.append('<div class="table_etud_in_dept">')
|
|
H.append(tab.html())
|
|
H.append("</div>")
|
|
if len(accessible_depts) > 1:
|
|
ss = "s"
|
|
else:
|
|
ss = ""
|
|
H.append(
|
|
f"""<p>(recherche menée dans le{ss} département{ss}:
|
|
{", ".join(accessible_depts)})
|
|
</p>
|
|
<p>
|
|
<a href="{url_for("scodoc.index")}" class="stdlink">Retour à l'accueil</a>
|
|
</p>
|
|
</div>
|
|
"""
|
|
)
|
|
return (
|
|
html_sco_header.scodoc_top_html_header(page_title="Choix d'un étudiant")
|
|
+ "\n".join(H)
|
|
+ html_sco_header.standard_html_footer()
|
|
)
|
|
|
|
|
|
def search_inscr_etud_by_nip(code_nip, fmt="json"):
|
|
"""Recherche multi-departement d'un étudiant par son code NIP
|
|
Seuls les départements accessibles par l'utilisateur sont cherchés.
|
|
|
|
Renvoie une liste des inscriptions de l'étudiants dans tout ScoDoc:
|
|
code_nip, nom, prenom, civilite_str, dept, formsemestre_id, date_debut_sem, date_fin_sem
|
|
"""
|
|
result, _ = search_etud_in_accessible_depts(code_nip=code_nip)
|
|
|
|
T = []
|
|
for etuds in result:
|
|
if etuds:
|
|
dept_id = etuds[0]["dept"]
|
|
for e in etuds:
|
|
for sem in e["sems"]:
|
|
T.append(
|
|
{
|
|
"dept": dept_id,
|
|
"etudid": e["etudid"],
|
|
"code_nip": e["code_nip"],
|
|
"civilite_str": e["civilite_str"],
|
|
"nom": e["nom"],
|
|
"prenom": e["prenom"],
|
|
"formsemestre_id": sem["formsemestre_id"],
|
|
"date_debut_iso": sem["date_debut_iso"],
|
|
"date_fin_iso": sem["date_fin_iso"],
|
|
}
|
|
)
|
|
|
|
columns_ids = (
|
|
"dept",
|
|
"etudid",
|
|
"code_nip",
|
|
"civilite_str",
|
|
"nom",
|
|
"prenom",
|
|
"formsemestre_id",
|
|
"date_debut_iso",
|
|
"date_fin_iso",
|
|
)
|
|
tab = GenTable(columns_ids=columns_ids, rows=T)
|
|
|
|
return tab.make_page(fmt=fmt, with_html_headers=False, publish=True)
|