diff --git a/app/scodoc/sco_portal_apogee.py b/app/scodoc/sco_portal_apogee.py index 4da1700b3..d0ef82c4a 100644 --- a/app/scodoc/sco_portal_apogee.py +++ b/app/scodoc/sco_portal_apogee.py @@ -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 XXX - 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" diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index 4f7168f61..80f8fcf16 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -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( diff --git a/app/views/scolar.py b/app/views/scolar.py index ae2c6ef98..756b051e1 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -1374,15 +1374,17 @@ def _etudident_create_or_edit_form(edit): submitlabel = "Ajouter cet étudiant" H.append( """

Création d'un étudiant

-

En général, il est recommandé d'importer les - étudiants depuis Apogée ou via un fichier Excel (menu Inscriptions - dans le semestre). -

-

- N'utilisez ce formulaire au cas par cas que pour les cas particuliers - ou si votre établissement n'utilise pas d'autre logiciel de gestion des - inscriptions. -

+
Attention +

En général, il est recommandé d'importer les + étudiants depuis Apogée ou via un fichier Excel (menu Inscriptions + dans le semestre). +

+

+ N'utilisez ce formulaire au cas par cas que pour les cas particuliers + ou si votre établissement n'utilise pas d'autre logiciel de gestion des + inscriptions. +

+

L'étudiant créé ne sera pas inscrit. Pensez à l'inscrire dans un semestre !

""" @@ -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"""

Modification des données de {etud_o.html_link_fiche()}

""") - 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 = [ - """ - -
    """ - ] - nanswers = len(infos) - nmax = 10 # nb max de reponse montrées - infos = infos[:nmax] - for i in infos: - formatted_infos.append("
  1. ") - formatted_infos.append("
") - m = "%d étudiants trouvés" % nanswers - if len(infos) != nanswers: - m += " (%d montrés)" % len(infos) - A = """
-
Informations Apogée
-

%s

- %s -
""" % ( - m, - "\n".join(formatted_infos), - ) - else: - A = """

Pas d'informations d'Apogée

""" 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] + "

" + 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] - + "

" - + 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] - + "

" - + 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 = [ + """ + +

    """ + ] + 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("
  1. ") + formatted_infos.append("
") + m = f"{nanswers} étudiants trouvés" + if len(etuds_apo) != nanswers: + m += " ({len(etuds_apo)} affichés)" + return f""" +
+
Informations Apogée
+

{m}

+ {''.join(formatted_infos)} +
""" + return """
Pas d'informations d'Apogée
""" + + @bp.route("/etud_copy_in_other_dept/", 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"""Pas d'information (