From 0ddf1faed411b651d7df6e691b2f79e53dcd5fe2 Mon Sep 17 00:00:00 2001 From: viennet Date: Wed, 14 Oct 2020 12:36:18 +0200 Subject: [PATCH] Accelerate resync data ident with Apo --- sco_portal_apogee.py | 44 ++++++++++++++++++++++++-------------------- sco_synchro_etuds.py | 17 ++++++++++++++++- sco_utils.py | 3 ++- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/sco_portal_apogee.py b/sco_portal_apogee.py index e4bcae95c..e3ab0d07c 100644 --- a/sco_portal_apogee.py +++ b/sco_portal_apogee.py @@ -99,8 +99,7 @@ class PortalInterface: return photo_url def get_maquette_url(self, context): - """Full URL of service giving Apogee maquette pour une étape (fichier "CSV") - """ + """Full URL of service giving Apogee maquette pour une étape (fichier "CSV")""" maquette_url = context.get_preference("maquette_url") if not maquette_url: # Default: @@ -128,9 +127,10 @@ get_maquette_url = _PI.get_maquette_url get_portal_api_version = _PI.get_portal_api_version -def get_inscrits_etape(context, code_etape, anneeapogee=None): +def get_inscrits_etape(context, code_etape, anneeapogee=None, ntrials=2): """Liste des inscrits à une étape Apogée Result = list of dicts + ntrials: try several time the same request, useful for some bad web services """ log("get_inscrits_etape: code=%s anneeapogee=%s" % (code_etape, anneeapogee)) if anneeapogee is None: @@ -149,7 +149,13 @@ def get_inscrits_etape(context, code_etape, anneeapogee=None): ) else: req = etud_url + "?" + urllib.urlencode((("etape", code_etape),)) - doc = query_portal(req, timeout=portal_timeout) + actual_timeout = float(portal_timeout) / ntrials + if portal_timeout > 0: + actual_timeout = max(1, actual_timeout) + for _ntrial in range(ntrials): + doc = query_portal(req, timeout=actual_timeout) + if doc: + break if not doc: raise ScoValueError("pas de réponse du portail ! (timeout=%s)" % portal_timeout) etuds = _normalize_apo_fields(xml_to_list_of_dicts(doc, req=req)) @@ -197,30 +203,31 @@ def query_apogee_portal(context, **args): def xml_to_list_of_dicts(doc, req=None): - """Convert an XML 1.0 str to a list of dicts. - """ + """Convert an XML 1.0 str to a list of dicts.""" if not doc: return [] # Fix for buggy XML returned by some APIs (eg USPN) invalid_entities = { - 'Ç' : 'Ç', - '& ' : '& ', # only when followed by a space (avoid affecting entities) + "Ç": "Ç", + "& ": "& ", # only when followed by a space (avoid affecting entities) # to be completed... } for k in invalid_entities: - doc = doc.replace(k,invalid_entities[k]) + doc = doc.replace(k, invalid_entities[k]) # try: dom = xml.dom.minidom.parseString(doc) except xml.parsers.expat.ExpatError as e: # Find faulty part - err_zone = doc.splitlines()[e.lineno-1][e.offset:e.offset+20] + err_zone = doc.splitlines()[e.lineno - 1][e.offset : e.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 ) - raise ScoValueError("erreur dans la réponse reçue du portail ! (peut être : \"%s\")" % err_zone) + raise ScoValueError( + 'erreur dans la réponse reçue du portail ! (peut être : "%s")' % err_zone + ) infos = [] try: if dom.childNodes[0].nodeName != u"etudiants": @@ -275,8 +282,7 @@ def get_infos_apogee_allaccents(context, nom, prenom): def get_infos_apogee(context, nom, prenom): - """recupere les codes Apogee en utilisant le web service CRIT - """ + """recupere les codes Apogee en utilisant le web service CRIT""" if (not nom) and (not prenom): return [] # essaie plusieurs codages: tirets, accents @@ -322,8 +328,7 @@ def get_etud_apogee(context, code_nip): def get_default_etapes(context): - """Liste par défaut: devrait etre lue d'un fichier de config - """ + """Liste par défaut: devrait etre lue d'un fichier de config""" filename = context.file_path + "/config/default-etapes.txt" log("get_default_etapes: reading %s" % filename) f = open(filename) @@ -417,7 +422,7 @@ def get_etapes_apogee_dept(context): Si xml_etapes_by_dept est faux (nouveau format XML depuis sept 2014), le departement n'est pas utilisé: toutes les étapes sont présentées. - + Returns [ ( code, intitule) ], ordonnée """ xml_etapes_by_dept = context.get_preference("xml_etapes_by_dept") @@ -466,7 +471,7 @@ def _normalize_apo_fields(infolist): infolist: liste de dict renvoyés par le portail Apogee recode les champs: paiementinscription (-> booleen), datefinalisationinscription (date) - ajoute le champs 'paiementinscription_str' : 'ok', 'Non' ou '?' + ajoute le champs 'paiementinscription_str' : 'ok', 'Non' ou '?' ajuoute le champs 'etape' (= None) s'il n'est pas présent """ for infos in infolist: @@ -503,7 +508,7 @@ def check_paiement_etuds(context, etuds): """Interroge le portail pour vérifier l'état de "paiement" et l'étape d'inscription. Seuls les etudiants avec code NIP sont renseignés. - + Renseigne l'attribut booleen 'paiementinscription' dans chaque etud. En sortie: modif les champs de chaque etud @@ -539,8 +544,7 @@ def check_paiement_etuds(context, etuds): def get_maquette_apogee(context, etape="", annee_scolaire=""): - """Maquette CSV Apogee pour une étape et une annee scolaire - """ + """Maquette CSV Apogee pour une étape et une annee scolaire""" maquette_url = get_maquette_url(context) if not maquette_url: return None diff --git a/sco_synchro_etuds.py b/sco_synchro_etuds.py index c27074c94..26874f36b 100644 --- a/sco_synchro_etuds.py +++ b/sco_synchro_etuds.py @@ -778,6 +778,17 @@ def formsemestre_import_etud_admission( changed_mails = [] # modification d'adresse mails cnx = context.GetDBConnexion() + # 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 + anneeapogee = str(annee_scolaire_debut(sem["annee_debut"], sem["mois_debut_ord"])) + apo_etuds = {} # nip : etud apo + for etape in sem["etapes"]: + etudsapo = sco_portal_apogee.get_inscrits_etape( + context, etape, anneeapogee=anneeapogee + ) + apo_etuds.update({e["nip"]: e for e in etudsapo}) + for i in ins: etudid = i["etudid"] info = context.getEtudInfo(etudid=etudid, filled=1)[0] @@ -785,7 +796,10 @@ def formsemestre_import_etud_admission( if not code_nip: no_nip.append(etudid) else: - etud = sco_portal_apogee.get_etud_apogee(context, code_nip) + etud = apo_etuds.get(code_nip) + if not etud: + # pas vu dans les etudiants de l'étape, tente en individuel + etud = sco_portal_apogee.get_etud_apogee(context, code_nip) if etud: do_import_etud_admission( context, @@ -818,6 +832,7 @@ def formsemestre_import_etud_admission( changed_mails.append((info, etud["mail"])) else: unknowns.append(code_nip) + context._inval_cache(formsemestre_id=sem["formsemestre_id"]) return no_nip, unknowns, changed_mails diff --git a/sco_utils.py b/sco_utils.py index 596e0069c..7cb6c6b94 100644 --- a/sco_utils.py +++ b/sco_utils.py @@ -836,8 +836,9 @@ def sort_dates(L, reverse=False): def query_portal(req, msg="Portail Apogee", timeout=3): - """retreive external data using http request + """Retreives external data using HTTP request (used to connect to Apogee portal, or ScoDoc server) + returns a string, "" on error """ log("query_portal: %s" % req)