Edition étudiant: interrogation Apgée avec portail v2 (utilise nip)

This commit is contained in:
Emmanuel Viennet 2025-01-16 22:38:48 +01:00
parent 17940d988d
commit f34996c8a7
3 changed files with 117 additions and 118 deletions

View File

@ -202,8 +202,7 @@ def get_inscrits_etape(
return False
else:
log(
"get_inscrits_etape: pas inscription dans code_etape=%s e=%s"
% (code_etape, e)
"get_inscrits_etape: pas inscription dans code_etape={code_etape} e={e}"
)
return False # ??? pas d'annee d'inscription dans la réponse
@ -215,8 +214,7 @@ def get_inscrits_etape(
def query_apogee_portal(**args):
"""Recupere les infos sur les etudiants nommés
args: nom, prenom, code_nip
(nom et prenom matchent des parties de noms)
args: nom, prenom, code_ine, code_nip
"""
etud_url = get_etud_url()
api_ver = get_portal_api_version()
@ -225,7 +223,6 @@ def query_apogee_portal(**args):
if api_ver > 1:
if args["nom"] or args["prenom"]:
# Ne fonctionne pas avec l'API 2 sur nom et prenom
# XXX TODO : va poser problème pour la page modif données étudiants : A VOIR
return []
portal_timeout = sco_preferences.get_preference("portal_timeout")
req = etud_url + "?" + urllib.parse.urlencode(list(args.items()))
@ -243,22 +240,21 @@ def xml_to_list_of_dicts(doc, req=None):
"& ": "& ", # only when followed by a space (avoid affecting entities)
# to be completed...
}
for k in invalid_entities:
doc = doc.replace(k, invalid_entities[k])
for k, repl in invalid_entities.items():
doc = doc.replace(k, repl)
#
try:
dom = xml.dom.minidom.parseString(doc)
except xml.parsers.expat.ExpatError as e:
except xml.parsers.expat.ExpatError as exc:
# Find faulty part
err_zone = doc.splitlines()[e.lineno - 1][e.offset : e.offset + 20]
err_zone = doc.splitlines()[exc.lineno - 1][exc.offset : exc.offset + 20]
# catch bug: log and re-raise exception
log(
"xml_to_list_of_dicts: exception in XML parseString\ndoc:\n%s\n(end xml doc)\n"
% doc
f"xml_to_list_of_dicts: exception in XML parseString\ndoc:\n{doc}\n(end xml doc)\n"
)
raise ScoValueError(
'erreur dans la réponse reçue du portail ! (peut être : "%s")' % err_zone
)
f'erreur dans la réponse reçue du portail ! (peut être : "{err_zone}")'
) from exc
infos = []
try:
if dom.childNodes[0].nodeName != "etudiants":
@ -267,17 +263,19 @@ def xml_to_list_of_dicts(doc, req=None):
for etudiant in etudiants:
d = {}
# recupere toutes les valeurs <valeur>XXX</valeur>
for e in etudiant.childNodes:
if e.nodeType == e.ELEMENT_NODE:
childs = e.childNodes
for exc in etudiant.childNodes:
if exc.nodeType == exc.ELEMENT_NODE:
childs = exc.childNodes
if len(childs):
d[str(e.nodeName)] = childs[0].nodeValue
d[str(exc.nodeName)] = childs[0].nodeValue
infos.append(d)
except:
except Exception as exc:
log("*** invalid XML response from Etudiant Web Service")
log("req=%s" % req)
log("doc=%s" % doc)
raise ValueError("invalid XML response from Etudiant Web Service\n%s" % doc)
log(f"req={req}")
log(f"doc={doc}")
raise ValueError(
f"invalid XML response from Etudiant Web Service\n{doc}"
) from exc
return infos
@ -301,8 +299,8 @@ def get_infos_apogee_allaccents(nom, prenom):
return infos
def get_infos_apogee(nom, prenom):
"""recupere les codes Apogee en utilisant le web service CRIT"""
def get_etuds_apogee_for_nom_prenom(nom: str, prenom: str) -> list[dict]:
"""Récupere les codes Apogee en utilisant le portail Apogée"""
if (not nom) and (not prenom):
return []
# essaie plusieurs codages: tirets, accents
@ -326,27 +324,27 @@ def get_infos_apogee(nom, prenom):
return infos
def get_etud_apogee(code_nip):
def get_etuds_apogee_from_nip(code_nip: str) -> list[dict]:
"""Informations à partir du code NIP.
None si pas d'infos sur cet etudiant.
Liste des étudiants ayant ce code NIP.
Exception si reponse invalide.
"""
if not code_nip:
return {}
return []
etud_url = get_etud_url()
if not etud_url:
return {}
return []
portal_timeout = sco_preferences.get_preference("portal_timeout")
req = etud_url + "?" + urllib.parse.urlencode((("nip", code_nip),))
doc = scu.query_portal(req, timeout=portal_timeout)
d = _normalize_apo_fields(xml_to_list_of_dicts(doc, req=req))
if not d:
return None
return []
if len(d) > 1:
log(f"get_etud_apogee({code_nip}): {len(d)} etudiants !\n{doc}")
flash("Attention: plusieurs étudiants inscrits avec le NIP {code_nip}")
log(f"get_etuds_apogee_from_nip({code_nip}): {len(d)} etudiants !\n{doc}")
flash(f"Attention: plusieurs étudiants inscrits avec le NIP {code_nip}")
# dans ce cas, renvoie le premier étudiant
return d[0]
return d
def get_default_etapes():
@ -413,14 +411,14 @@ def get_etapes_apogee():
SCO_CACHE_ETAPE_FILENAME, "w", encoding=scu.SCO_ENCODING
) as f:
f.write(doc)
except:
except Exception:
log(f"invalid XML response from getEtapes Web Service\n{etapes_url}")
# Avons-nous la copie d'une réponse récente ?
try:
doc = open(SCO_CACHE_ETAPE_FILENAME, encoding=scu.SCO_ENCODING).read()
infos = _parse_etapes_from_xml(doc)
log(f"using last saved version from {SCO_CACHE_ETAPE_FILENAME}")
except:
except Exception:
infos = {}
else:
# Pas de portail: utilise étapes par défaut livrées avec ScoDoc
@ -452,7 +450,7 @@ def get_etapes_apogee_dept():
xml_etapes_by_dept = sco_preferences.get_preference("xml_etapes_by_dept")
if xml_etapes_by_dept:
portal_dept_name = sco_preferences.get_preference("portal_dept_name")
log('get_etapes_apogee_dept: portal_dept_name="%s"' % portal_dept_name)
log(f'get_etapes_apogee_dept: portal_dept_name="{portal_dept_name}"')
else:
portal_dept_name = ""
log("get_etapes_apogee_dept: pas de sections par departement")
@ -460,8 +458,7 @@ def get_etapes_apogee_dept():
infos = get_etapes_apogee()
if portal_dept_name and portal_dept_name not in infos:
log(
"get_etapes_apogee_dept: pas de section '%s' dans la reponse portail"
% portal_dept_name
f"get_etapes_apogee_dept: pas de section '{portal_dept_name}' dans la reponse portail"
)
return []
if portal_dept_name:
@ -469,8 +466,8 @@ def get_etapes_apogee_dept():
else:
# prend toutes les etapes
etapes = []
for k in infos.keys():
etapes += list(infos[k].items())
for info in infos.values():
etapes += list(info.items())
etapes.sort() # tri sur le code etape
return etapes
@ -571,8 +568,8 @@ def check_paiement_etuds(etuds):
etud["etape"] = None
else:
# Modifie certains champs de l'étudiant:
infos = get_etud_apogee(etud["code_nip"])
if infos:
etuds_apo = get_etuds_apogee_from_nip(etud["code_nip"])
if etuds_apo:
for k in (
"paiementinscription",
"paiementinscription_str",
@ -580,7 +577,7 @@ def check_paiement_etuds(etuds):
"datefinalisationinscription_str",
"etape",
):
etud[k] = infos[k]
etud[k] = etuds_apo[0][k]
else:
etud["datefinalisationinscription"] = None
etud["datefinalisationinscription_str"] = "Erreur"

View File

@ -837,7 +837,7 @@ def formsemestre_import_etud_admission(
# Essaie de recuperer les etudiants des étapes, car
# la requete get_inscrits_etape est en général beaucoup plus
# rapide que les requetes individuelles get_etud_apogee
# rapide que les requetes individuelles get_etuds_apogee_from_nip
annee_apogee = str(
scu.annee_scolaire_debut(sem["annee_debut"], sem["mois_debut_ord"])
)
@ -858,7 +858,8 @@ def formsemestre_import_etud_admission(
data_apo = apo_etuds.get(code_nip)
if not data_apo:
# pas vu dans les etudiants de l'étape, tente en individuel
data_apo = sco_portal_apogee.get_etud_apogee(code_nip)
etuds_apo = sco_portal_apogee.get_etuds_apogee_from_nip(code_nip)
data_apo = etuds_apo[0] if etuds_apo else None
if data_apo:
update_etape_formsemestre_inscription(i, data_apo)
do_import_etud_admission(

View File

@ -1374,15 +1374,17 @@ def _etudident_create_or_edit_form(edit):
submitlabel = "Ajouter cet étudiant"
H.append(
"""<h2>Création d'un étudiant</h2>
<p class="warning">En général, il est <b>recommandé</b> d'importer les
étudiants depuis Apogée ou via un fichier Excel (menu <b>Inscriptions</b>
dans le semestre).
</p>
<p>
N'utilisez ce formulaire au cas par cas que <b>pour les cas particuliers</b>
ou si votre établissement n'utilise pas d'autre logiciel de gestion des
inscriptions.
</p>
<div class="scobox warning">Attention
<p>En général, il est <b>recommandé</b> d'importer les
étudiants depuis Apogée ou via un fichier Excel (menu <b>Inscriptions</b>
dans le semestre).
</p>
<p>
N'utilisez ce formulaire au cas par cas que <b>pour les cas particuliers</b>
ou si votre établissement n'utilise pas d'autre logiciel de gestion des
inscriptions.
</p>
</div>
<p class"warning"><em>L'étudiant créé ne sera pas inscrit.
Pensez à l'inscrire dans un semestre !</em></p>
"""
@ -1395,63 +1397,13 @@ def _etudident_create_or_edit_form(edit):
etud_o: Identite = Identite.get_etud(etudid)
descr.append(("etudid", {"default": etudid, "input_type": "hidden"}))
H.append(f"""<h2>Modification des données de {etud_o.html_link_fiche()}</h2>""")
initvalues = sco_etud.etudident_list(cnx, {"etudid": etudid})
initvalues = sco_etud.etudident_list(cnx, {"etudid": etudid}) # XXX TODO
assert len(initvalues) == 1
initvalues = initvalues[0]
submitlabel = "Modifier les données"
infos_apogee_html = _infos_apogee_html_etuds(scu.get_request_args(), initvalues)
vals = scu.get_request_args()
nom = vals.get("nom", None)
if nom is None:
nom = initvalues.get("nom", None)
if nom is None:
infos = []
else:
prenom = vals.get("prenom", "")
if vals.get("tf_submitted", False) and not prenom:
prenom = initvalues.get("prenom", "")
infos = sco_portal_apogee.get_infos_apogee(nom, prenom)
if infos:
formatted_infos = [
"""
<script type="text/javascript">
function copy_nip(nip) {
document.tf.code_nip.value = nip;
}
</script>
<ol>"""
]
nanswers = len(infos)
nmax = 10 # nb max de reponse montrées
infos = infos[:nmax]
for i in infos:
formatted_infos.append("<li><ul>")
for k in i.keys():
if k != "nip":
item = "<li>%s : %s</li>" % (k, i[k])
else:
item = (
'<li><form>%s : %s <input type="button" value="copier ce code" onmousedown="copy_nip(%s);"/></form></li>'
% (k, i[k], i[k])
)
formatted_infos.append(item)
formatted_infos.append("</ul></li>")
formatted_infos.append("</ol>")
m = "%d étudiants trouvés" % nanswers
if len(infos) != nanswers:
m += " (%d montrés)" % len(infos)
A = """<div class="infoapogee">
<h5>Informations Apogée</h5>
<p>%s</p>
%s
</div>""" % (
m,
"\n".join(formatted_infos),
)
else:
A = """<div class="infoapogee"><p>Pas d'informations d'Apogée</p></div>"""
require_ine = sco_preferences.get_preference("always_require_ine")
@ -1727,7 +1679,7 @@ def _etudident_create_or_edit_form(edit):
if tf[0] in (0, -1):
return render_template(
"sco_page_dept.j2",
content="\n".join(H) + tf[1] + "<p>" + A,
content="\n".join(H) + tf[1] + infos_apogee_html,
title="Création/édition d'étudiant",
)
else:
@ -1746,8 +1698,7 @@ def _etudident_create_or_edit_form(edit):
content="\n".join(H)
+ tf_error_message("Nom ou prénom invalide")
+ tf[1]
+ "<p>"
+ A,
+ infos_apogee_html,
title="Création/édition d'étudiant",
)
if not tf[2]["dont_check_homonyms"] and nb_homonyms > 0:
@ -1773,9 +1724,8 @@ def _etudident_create_or_edit_form(edit):
"""
)
+ tf[1]
+ "<p>"
+ A
+ homonyms_html
+ infos_apogee_html
),
)
tf[2]["date_naissance"] = (
@ -1796,19 +1746,70 @@ def _etudident_create_or_edit_form(edit):
etud_o.admission = admission
admission.from_dict(tf[2])
db.session.commit()
etud = sco_etud.etudident_list(cnx, {"etudid": etud_o.id})[0]
sco_etud.fill_etuds_info([etud])
# Inval semesters with this student:
to_inval = [s["formsemestre_id"] for s in etud["sems"]]
for formsemestre_id in to_inval:
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre_id)
#
# Inval semesters with this student:
for inscription in etud_o.formsemestre_inscriptions:
sco_cache.invalidate_formsemestre(
formsemestre_id=inscription.formsemestre_id
)
#
return flask.redirect(
url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
def _infos_apogee_html_etuds(vals: dict, initvalues: dict) -> str:
"fragment de html pour lister les étudiants correspondants"
nom = vals.get("nom", initvalues.get("nom", None))
nip = vals.get("code_nip", initvalues.get("code_nip", "")).strip()
if nom is None and nip is None:
etuds_apo = []
elif nip:
etuds_apo = sco_portal_apogee.get_etuds_apogee_from_nip(nip)
else:
prenom = vals.get("prenom", "")
if vals.get("tf_submitted", False) and not prenom:
prenom = initvalues.get("prenom", "")
etuds_apo = sco_portal_apogee.get_etuds_apogee_for_nom_prenom(nom, prenom)
if etuds_apo:
formatted_infos = [
"""
<script type="text/javascript">
function copy_nip(nip) {
document.tf.code_nip.value = nip;
}
</script>
<ol>"""
]
nanswers = len(etuds_apo)
nmax = 10 # nb max de réponse montrées
etuds_apo = etuds_apo[:nmax]
for i in etuds_apo:
formatted_infos.append("<li><ul>")
for k in i.keys():
if k != "nip":
item = f"<li><tt>{k}</tt> : {i[k]}</li>"
else:
item = f"""<li><form><tt>{k}</tt> : {i[k]}
<input type="button" value="copier ce code" onmousedown="copy_nip({i[k]});"/>
</form>
</li>"""
formatted_infos.append(item)
formatted_infos.append("</ul></li>")
formatted_infos.append("</ol>")
m = f"{nanswers} étudiants trouvés"
if len(etuds_apo) != nanswers:
m += " ({len(etuds_apo)} affichés)"
return f"""
<div class="scobox infoapogee">
<div class="scobox-title">Informations Apogée</div>
<p>{m}</p>
{''.join(formatted_infos)}
</div>"""
return """<div class="scobox infoapogee">Pas d'informations d'Apogée</div>"""
@bp.route("/etud_copy_in_other_dept/<int:etudid>", methods=["GET", "POST"])
@scodoc
@permission_required(
@ -1985,7 +1986,7 @@ def check_group_apogee(group_id, etat=None, fix=False, fixmail=False):
t["email"],
t["code_nip"],
)
infos = sco_portal_apogee.get_infos_apogee(nom, prenom)
infos = sco_portal_apogee.get_etuds_apogee_for_nom_prenom(nom, prenom)
if not infos:
info_apogee = f"""<b>Pas d'information</b>
(<a class="stdlink" href="{