# -*- 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, render_template, request from flask_login import current_user import sqlalchemy as sa from app import db from app.models import Departement, Identite 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"""
{title}
(entrer une partie du nom) """ ) if dest_url: H.append('' % dest_url) if parameters: for param in parameters.keys(): H.append( '' % (param, parameters[param]) ) H.append( '' % (",".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('' % (key, v)) H.append( '' % parameters_keys ) H.append("
") 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 = "", dept_id: int | None = None ) -> list[Identite]: """Cherche étudiants, expnom peut être, dans cet ordre: un etudid (int), un code NIP, ou une partie d'un nom (case insensitive). Si dept_id est None, cherche dans le dept courant, sinon cherche dans le dept indiqué. """ 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 dept_id = g.scodoc_dept_id if dept_id is None else dept_id if etudid is not None: etud = Identite.query.filter_by(dept_id=dept_id, id=etudid).first() if etud: return [etud] expnom_str = str(expnom) if scu.is_valid_code_nip(expnom_str): etuds = sorted( Identite.query.filter_by(dept_id=dept_id, code_nip=expnom_str).all(), key=lambda e: e.sort_key, ) if etuds: return etuds try: return sorted( Identite.query.filter_by(dept_id=dept_id) .filter( Identite.nom.op("~*")(expnom_str) ) # ~* matches regular expression, case-insensitive .all(), key=lambda e: e.sort_key, ) except sa.exc.DataError: db.session.rollback() return [] 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.fiche_etud" 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].id return flask.redirect(url_for(endpoint, **url_args)) H = [] if len(etuds) == 0 and len(etuds) <= 1: H.append("""

chercher un étudiant:

""") else: H.append( f"""

{len(etuds)} résultats pour "{expnom}": choisissez un étudiant:

""" ) 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: rows = [] e: Identite for e in sorted(etuds, key=lambda e: e.sort_key): url_args["etudid"] = e.id target = url_for(endpoint, **url_args) cur_inscription = e.inscription_courante() inscription = ( e.inscription_descr().get("inscription_str", "") if cur_inscription else "" ) groupes = ( ", ".join( gr.group_name for gr in sco_groups.get_etud_formsemestre_groups( e, cur_inscription.formsemestre ) ) if cur_inscription else "" ) rows.append( { "code_nip": e.code_nip or "", "etudid": e.id, "inscription": inscription, "inscription_target": target, "groupes": groupes, "nomprenom": e.nomprenom, "_nomprenom_order": e.sort_key, "_nomprenom_target": target, "_nomprenom_td_attrs": f'id="{e.id}" class="etudinfo"', } ) tab = GenTable( columns_ids=("nomprenom", "code_nip", "inscription", "groupes"), titles={ "nomprenom": "Étudiant", "code_nip": "NIP", "inscription": "Inscription", "groupes": "Groupes", }, rows=rows, html_sortable=True, html_class="table_leftalign", preferences=sco_preferences.SemPreferences(), table_id="search_etud_in_dept", ) 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(f'

Aucun résultat pour "{expnom}".

') H.append( """

La recherche porte sur tout ou partie du NOM ou du NIP de l'étudiant. Saisir au moins deux caractères.

""" ) return render_template( "sco_page_dept.j2", title="Recherche d'un étudiant", content="\n".join(H) ) def search_etuds_infos(expnom=None, code_nip=None) -> list[dict]: """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" : " ", "value" : etudid } """ may_be_nip = scu.is_valid_code_nip(term) etuds = search_etuds_infos_from_exp(term) return [ { "label": f"""{(etud.code_nip+' ') if (etud.code_nip and may_be_nip) else ""}{ etud.nom_prenom()}""", "value": etud.id, } for etud in etuds ] # ---------- Recherche sur plusieurs département def search_etuds_in_accessible_depts( expnom=None, ) -> tuple[list[list[Identite]], list[str]]: """ result: list of (sorted) etuds, one list per dept. accessible_depts: list of dept acronyms """ 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: accessible_depts.append(dept.acronym) etuds = search_etuds_infos_from_exp(expnom=expnom, dept_id=dept.id) else: etuds = [] result.append(etuds) return result, accessible_depts def table_etuds_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_etuds_in_accessible_depts(expnom=expnom) H = [ f"""

Recherche multi-département de "{expnom}"

""", ] for etuds in result: if etuds: dept = etuds[0].departement rows = [ { "nomprenom": etud.nom_prenom(), "_nomprenom_target": url_for( "scolar.fiche_etud", scodoc_dept=dept.acronym, etudid=etud.id ), "_nomprenom_td_attrs": f"""id="{etud.id}" class="etudinfo" """, "_nomprenom_order": etud.sort_key, } for etud in etuds ] tab = GenTable( titles={"nomprenom": "Étudiants en " + dept.acronym}, columns_ids=("nomprenom",), rows=rows, html_sortable=True, html_class="table_leftalign", table_id="etuds_in_accessible_depts", ) H.append('
') H.append(tab.html()) H.append("
") if len(accessible_depts) > 1: ss = "s" else: ss = "" H.append( f"""

(recherche menée dans le{ss} département{ss}: {", ".join(accessible_depts)})

Retour à l'accueil

""" ) return render_template( "base.j2", title="Choix d'un étudiant", content="\n".join(H), javascripts=["DataTables/datatables.min.js"], cssstyles=["DataTables/datatables.min.css"], )