Optimisation de la synchro inscription

This commit is contained in:
Emmanuel Viennet 2021-09-02 18:05:22 +02:00
parent ca4d9817e6
commit afaab2d5e0
10 changed files with 87 additions and 86 deletions

View File

@ -251,6 +251,9 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
""" """
from app.scodoc import sco_parcours_dut from app.scodoc import sco_parcours_dut
if g.defer_cache_invalidation:
g.sem_to_invalidate.add(formsemestre_id)
return
log("inval_cache, formsemestre_id=%s pdfonly=%s" % (formsemestre_id, pdfonly)) log("inval_cache, formsemestre_id=%s pdfonly=%s" % (formsemestre_id, pdfonly))
if formsemestre_id is None: if formsemestre_id is None:
# clear all caches # clear all caches
@ -286,3 +289,25 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
SemInscriptionsCache.delete_many(formsemestre_ids) SemInscriptionsCache.delete_many(formsemestre_ids)
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids) SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)
class DefferedSemCacheManager:
"""Experimental: pour effectuer des opérations indépendantes dans la
même requete qui invalident le cache. Par exemple, quand on inscrit
des étudiants un par un à un semestre, chaque inscription va invalider
le cache, et la suivante va le reconstruire... pour l'invalider juste après.
Ce context manager permet de grouper les invalidations.
"""
def __enter__(self):
assert not hasattr(g, "defer_cache_invalidation")
g.defer_cache_invalidation = True
g.sem_to_invalidate = set()
return True
def __exit__(self, exc_type, exc_value, exc_traceback):
assert g.defer_cache_invalidation
g.defer_cache_invalidation = False
while g.sem_to_invalidate:
formsemestre_id = g.sem_to_invalidate.pop()
invalidate_formsemestre(formsemestre_id)

View File

@ -74,7 +74,6 @@ def formsemestre_ext_create(etudid, sem_params, REQUEST=None):
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules( sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre_id, formsemestre_id,
etudid, etudid,
REQUEST=REQUEST,
method="formsemestre_ext_create", method="formsemestre_ext_create",
) )
return formsemestre_id return formsemestre_id

View File

@ -135,7 +135,7 @@ def do_formsemestre_inscription_edit(args=None, formsemestre_id=None):
) # > modif inscription semestre (demission ?) ) # > modif inscription semestre (demission ?)
def do_formsemestre_desinscription(etudid, formsemestre_id, REQUEST=None): def do_formsemestre_desinscription(etudid, formsemestre_id):
"""Désinscription d'un étudiant. """Désinscription d'un étudiant.
Si semestre extérieur et dernier inscrit, suppression de ce semestre. Si semestre extérieur et dernier inscrit, suppression de ce semestre.
""" """
@ -194,14 +194,13 @@ def do_formsemestre_desinscription(etudid, formsemestre_id, REQUEST=None):
) )
sco_formsemestre_edit.do_formsemestre_delete(formsemestre_id) sco_formsemestre_edit.do_formsemestre_delete(formsemestre_id)
if REQUEST: logdb(
logdb( cnx,
cnx, method="formsemestre_desinscription",
method="formsemestre_desinscription", etudid=etudid,
etudid=etudid, msg="desinscription semestre %s" % formsemestre_id,
msg="desinscription semestre %s" % formsemestre_id, commit=False,
commit=False, )
)
def do_formsemestre_inscription_with_modules( def do_formsemestre_inscription_with_modules(
@ -210,7 +209,6 @@ def do_formsemestre_inscription_with_modules(
group_ids=[], group_ids=[],
etat="I", etat="I",
etape=None, etape=None,
REQUEST=None,
method="inscription_with_modules", method="inscription_with_modules",
): ):
"""Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS """Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
@ -245,7 +243,6 @@ def do_formsemestre_inscription_with_modules(
if mod["ue"]["type"] != UE_SPORT: if mod["ue"]["type"] != UE_SPORT:
sco_moduleimpl.do_moduleimpl_inscription_create( sco_moduleimpl.do_moduleimpl_inscription_create(
{"moduleimpl_id": mod["moduleimpl_id"], "etudid": etudid}, {"moduleimpl_id": mod["moduleimpl_id"], "etudid": etudid},
REQUEST=REQUEST,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )
@ -406,7 +403,6 @@ def formsemestre_inscription_with_modules(
etudid, etudid,
group_ids=group_ids, group_ids=group_ids,
etat="I", etat="I",
REQUEST=REQUEST,
method="formsemestre_inscription_with_modules", method="formsemestre_inscription_with_modules",
) )
return flask.redirect( return flask.redirect(
@ -691,7 +687,6 @@ def do_moduleimpl_incription_options(
mod = mods[0] mod = mods[0]
sco_moduleimpl.do_moduleimpl_inscription_create( sco_moduleimpl.do_moduleimpl_inscription_create(
{"moduleimpl_id": moduleimpl_id, "etudid": etudid}, {"moduleimpl_id": moduleimpl_id, "etudid": etudid},
REQUEST=REQUEST,
formsemestre_id=mod["formsemestre_id"], formsemestre_id=mod["formsemestre_id"],
) )
# desinscriptions # desinscriptions

View File

@ -601,7 +601,7 @@ def set_group(etudid, group_id):
return True return True
def change_etud_group_in_partition(etudid, group_id, partition=None, REQUEST=None): def change_etud_group_in_partition(etudid, group_id, partition=None):
"""Inscrit etud au groupe de cette partition, et le desinscrit d'autres groupes de cette partition.""" """Inscrit etud au groupe de cette partition, et le desinscrit d'autres groupes de cette partition."""
log("change_etud_group_in_partition: etudid=%s group_id=%s" % (etudid, group_id)) log("change_etud_group_in_partition: etudid=%s group_id=%s" % (etudid, group_id))
@ -632,16 +632,15 @@ def change_etud_group_in_partition(etudid, group_id, partition=None, REQUEST=Non
# 3- log # 3- log
formsemestre_id = partition["formsemestre_id"] formsemestre_id = partition["formsemestre_id"]
if REQUEST: cnx = ndb.GetDBConnexion()
cnx = ndb.GetDBConnexion() logdb(
logdb( cnx,
cnx, method="changeGroup",
method="changeGroup", etudid=etudid,
etudid=etudid, msg="formsemestre_id=%s,partition_name=%s, group_name=%s"
msg="formsemestre_id=%s,partition_name=%s, group_name=%s" % (formsemestre_id, partition["partition_name"], group["group_name"]),
% (formsemestre_id, partition["partition_name"], group["group_name"]), )
) cnx.commit()
cnx.commit()
# 4- invalidate cache # 4- invalidate cache
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
@ -696,9 +695,7 @@ def setGroups(
if (etudid not in etud_groups) or ( if (etudid not in etud_groups) or (
group_id != etud_groups[etudid].get(partition_id, "") group_id != etud_groups[etudid].get(partition_id, "")
): # pas le meme groupe qu'actuel ): # pas le meme groupe qu'actuel
change_etud_group_in_partition( change_etud_group_in_partition(etudid, group_id, partition)
etudid, group_id, partition, REQUEST=REQUEST
)
# Retire les anciens membres: # Retire les anciens membres:
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@ -734,7 +731,7 @@ def setGroups(
group_id = createGroup(partition_id, group_name) group_id = createGroup(partition_id, group_name)
# Place dans ce groupe les etudiants indiqués: # Place dans ce groupe les etudiants indiqués:
for etudid in fs[1:-1]: for etudid in fs[1:-1]:
change_etud_group_in_partition(etudid, group_id, partition, REQUEST=REQUEST) change_etud_group_in_partition(etudid, group_id, partition)
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
return ( return (
@ -1329,9 +1326,7 @@ def groups_auto_repartition(partition_id=None, REQUEST=None):
etudid = listes[civilite].pop()[1] etudid = listes[civilite].pop()[1]
group_id = group_ids[igroup] group_id = group_ids[igroup]
igroup = (igroup + 1) % nbgroups igroup = (igroup + 1) % nbgroups
change_etud_group_in_partition( change_etud_group_in_partition(etudid, group_id, partition)
etudid, group_id, partition, REQUEST=REQUEST
)
log("%s in group %s" % (etudid, group_id)) log("%s in group %s" % (etudid, group_id))
return flask.redirect(dest_url) return flask.redirect(dest_url)

View File

@ -572,7 +572,6 @@ def _import_one_student(
etudid, etudid,
group_ids, group_ids,
etat="I", etat="I",
REQUEST=REQUEST,
method="import_csv_file", method="import_csv_file",
) )
return args["formsemestre_id"] return args["formsemestre_id"]
@ -716,7 +715,7 @@ def scolars_import_admission(
for group_id in group_ids: for group_id in group_ids:
sco_groups.change_etud_group_in_partition( sco_groups.change_etud_group_in_partition(
args["etudid"], group_id, REQUEST=REQUEST args["etudid"], group_id
) )
# #
diag.append("import de %s" % (etud["nomprenom"])) diag.append("import de %s" % (etud["nomprenom"]))

View File

@ -157,7 +157,7 @@ def list_inscrits_date(sem):
return [x[0] for x in cursor.fetchall()] return [x[0] for x in cursor.fetchall()]
def do_inscrit(sem, etudids, REQUEST=None, inscrit_groupes=False): def do_inscrit(sem, etudids, inscrit_groupes=False):
"""Inscrit ces etudiants dans ce semestre """Inscrit ces etudiants dans ce semestre
(la liste doit avoir été vérifiée au préalable) (la liste doit avoir été vérifiée au préalable)
En option: inscrit aux mêmes groupes que dans le semestre origine En option: inscrit aux mêmes groupes que dans le semestre origine
@ -168,7 +168,6 @@ def do_inscrit(sem, etudids, REQUEST=None, inscrit_groupes=False):
sem["formsemestre_id"], sem["formsemestre_id"],
etudid, etudid,
etat="I", etat="I",
REQUEST=REQUEST,
method="formsemestre_inscr_passage", method="formsemestre_inscr_passage",
) )
if inscrit_groupes: if inscrit_groupes:
@ -208,15 +207,14 @@ def do_inscrit(sem, etudids, REQUEST=None, inscrit_groupes=False):
etudid, etudid,
partition_group["group_id"], partition_group["group_id"],
partition_group, partition_group,
REQUEST=REQUEST,
) )
def do_desinscrit(sem, etudids, REQUEST): def do_desinscrit(sem, etudids):
log("do_desinscrit: %s" % etudids) log("do_desinscrit: %s" % etudids)
for etudid in etudids: for etudid in etudids:
sco_formsemestre_inscriptions.do_formsemestre_desinscription( sco_formsemestre_inscriptions.do_formsemestre_desinscription(
etudid, sem["formsemestre_id"], REQUEST=REQUEST etudid, sem["formsemestre_id"]
) )
@ -361,12 +359,11 @@ def formsemestre_inscr_passage(
do_inscrit( do_inscrit(
sem, sem,
a_inscrire, a_inscrire,
REQUEST=REQUEST,
inscrit_groupes=inscrit_groupes, inscrit_groupes=inscrit_groupes,
) )
# Desincriptions: # Desincriptions:
do_desinscrit(sem, a_desinscrire, REQUEST) do_desinscrit(sem, a_desinscrire)
H.append( H.append(
"""<h3>Opération effectuée</h3> """<h3>Opération effectuée</h3>

View File

@ -218,7 +218,7 @@ _moduleimpl_inscriptionEditor = ndb.EditableTable(
) )
def do_moduleimpl_inscription_create(args, REQUEST=None, formsemestre_id=None): def do_moduleimpl_inscription_create(args, formsemestre_id=None):
"create a moduleimpl_inscription" "create a moduleimpl_inscription"
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
log("do_moduleimpl_inscription_create: " + str(args)) log("do_moduleimpl_inscription_create: " + str(args))
@ -226,14 +226,13 @@ def do_moduleimpl_inscription_create(args, REQUEST=None, formsemestre_id=None):
sco_cache.invalidate_formsemestre( sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id formsemestre_id=formsemestre_id
) # > moduleimpl_inscription ) # > moduleimpl_inscription
if REQUEST: scolog.logdb(
scolog.logdb( cnx,
cnx, method="moduleimpl_inscription",
method="moduleimpl_inscription", etudid=args["etudid"],
etudid=args["etudid"], msg="inscription module %s" % args["moduleimpl_id"],
msg="inscription module %s" % args["moduleimpl_id"], commit=False,
commit=False, )
)
return r return r
@ -283,7 +282,6 @@ def do_moduleimpl_inscrit_etuds(
if not etudid in inmod_set: if not etudid in inmod_set:
do_moduleimpl_inscription_create( do_moduleimpl_inscription_create(
{"moduleimpl_id": moduleimpl_id, "etudid": etudid}, {"moduleimpl_id": moduleimpl_id, "etudid": etudid},
REQUEST=REQUEST,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )

View File

@ -582,6 +582,5 @@ def do_etud_inscrit_ue(etudid, formsemestre_id, ue_id, REQUEST=None):
for moduleimpl_id in [x["moduleimpl_id"] for x in res]: for moduleimpl_id in [x["moduleimpl_id"] for x in res]:
sco_moduleimpl.do_moduleimpl_inscription_create( sco_moduleimpl.do_moduleimpl_inscription_create(
{"moduleimpl_id": moduleimpl_id, "etudid": etudid}, {"moduleimpl_id": moduleimpl_id, "etudid": etudid},
REQUEST=REQUEST,
formsemestre_id=formsemestre_id, formsemestre_id=formsemestre_id,
) )

View File

@ -32,7 +32,8 @@ import time
import pprint import pprint
from operator import itemgetter from operator import itemgetter
from flask import g, url_for from flask import g, url_for, send_file
from flask_login import current_user
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
@ -55,7 +56,7 @@ EKEY_APO = "nip"
EKEY_SCO = "code_nip" EKEY_SCO = "code_nip"
EKEY_NAME = "code NIP" EKEY_NAME = "code NIP"
# view:
def formsemestre_synchro_etuds( def formsemestre_synchro_etuds(
formsemestre_id, formsemestre_id,
etuds=[], # liste des codes NIP des etudiants a inscrire (ou deja inscrits) etuds=[], # liste des codes NIP des etudiants a inscrire (ou deja inscrits)
@ -65,7 +66,6 @@ def formsemestre_synchro_etuds(
dialog_confirmed=False, dialog_confirmed=False,
export_cat_xls=None, export_cat_xls=None,
read_only=False, # Affiche sans permettre modifications read_only=False, # Affiche sans permettre modifications
REQUEST=None,
): ):
"""Synchronise les étudiants de ce semestre avec ceux d'Apogée. """Synchronise les étudiants de ce semestre avec ceux d'Apogée.
On a plusieurs cas de figure: L'étudiant peut être On a plusieurs cas de figure: L'étudiant peut être
@ -85,15 +85,14 @@ def formsemestre_synchro_etuds(
- l'utilisateur valide (cocher les étudiants à importer/inscrire) - l'utilisateur valide (cocher les étudiants à importer/inscrire)
- go - go
etuds: apres selection par utilisateur, la liste des etudiants selectionnes etuds: apres sélection par l'utilisateur, la liste des étudiants selectionnés
que l'on va importer/inscrire que l'on va importer/inscrire
""" """
log("formsemestre_synchro_etuds: formsemestre_id=%s" % formsemestre_id) log("formsemestre_synchro_etuds: formsemestre_id=%s" % formsemestre_id)
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
sem["etape_apo_str"] = sco_formsemestre.formsemestre_etape_apo_str(sem) sem["etape_apo_str"] = sco_formsemestre.formsemestre_etape_apo_str(sem)
# Write access ? # Write access ?
authuser = REQUEST.AUTHENTICATED_USER if not current_user.has_permission(Permission.ScoEtudInscrit):
if not authuser.has_permission(Permission.ScoEtudInscrit):
read_only = True read_only = True
if read_only: if read_only:
submitted = False submitted = False
@ -110,9 +109,11 @@ def formsemestre_synchro_etuds(
) )
header = html_sco_header.sco_header(page_title="Synchronisation étudiants") header = html_sco_header.sco_header(page_title="Synchronisation étudiants")
footer = html_sco_header.sco_footer() footer = html_sco_header.sco_footer()
base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id) base_url = url_for(
if anneeapogee: "notes.formsemestre_synchro_etuds",
base_url += "&anneeapogee=%s" % anneeapogee scodoc_dept=g.scodoc_dept,
anneeapogee=anneeapogee or None, # si None, le param n'est pas dans l'URL
)
if anneeapogee is None: # année d'inscription par défaut if anneeapogee is None: # année d'inscription par défaut
anneeapogee = scu.annee_scolaire_debut( anneeapogee = scu.annee_scolaire_debut(
@ -145,7 +146,12 @@ def formsemestre_synchro_etuds(
base_url=base_url, base_url=base_url,
read_only=read_only, read_only=read_only,
) )
return sco_excel.send_excel_file(REQUEST, xls, filename + scu.XLSX_SUFFIX) return send_file(
xls,
mimetype=scu.XLS_MIMETYPE,
download_name=scu.sanitize_filename(filename + scu.XLSX_SUFFIX),
as_attachment=True,
)
H = [header] H = [header]
if not submitted: if not submitted:
@ -235,9 +241,10 @@ def formsemestre_synchro_etuds(
etudids_a_desinscrire = [nip2etudid(x) for x in a_desinscrire] etudids_a_desinscrire = [nip2etudid(x) for x in a_desinscrire]
etudids_a_desinscrire += a_desinscrire_without_key etudids_a_desinscrire += a_desinscrire_without_key
# #
do_import_etuds_from_portal(sem, a_importer, etudsapo_ident, REQUEST) with sco_cache.DefferedSemCacheManager():
sco_inscr_passage.do_inscrit(sem, etudids_a_inscrire, REQUEST) do_import_etuds_from_portal(sem, a_importer, etudsapo_ident)
sco_inscr_passage.do_desinscrit(sem, etudids_a_desinscrire, REQUEST) sco_inscr_passage.do_inscrit(sem, etudids_a_inscrire)
sco_inscr_passage.do_desinscrit(sem, etudids_a_desinscrire)
H.append( H.append(
"""<h3>Opération effectuée</h3> """<h3>Opération effectuée</h3>
@ -522,9 +529,9 @@ def formsemestre_synchro_etuds_help(sem):
href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a> href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a>
les étudiants inscrits dans l'étape Apogée correspondante (<b><tt>%(etape_apo_str)s</tt></b>) les étudiants inscrits dans l'étape Apogée correspondante (<b><tt>%(etape_apo_str)s</tt></b>)
</p> </p>
<p>Au départ, tous les étudiants d'Apogée sont sélectionnés; vous pouvez <p>Au départ, tous les étudiants d'Apogée sont sélectionnés; vous pouvez
en déselectionner certains. Tous les étudiants cochés seront inscrits au semestre ScoDoc, en déselectionner certains. Tous les étudiants cochés seront inscrits au semestre ScoDoc,
les autres seront si besoin désinscrits. Aucune modification n'est effectuée avant les autres seront si besoin désinscrits. Aucune modification n'est effectuée avant
d'appuyer sur le bouton "Appliquer les modifications".</p> d'appuyer sur le bouton "Appliquer les modifications".</p>
<h4>Autres fonctions utiles</h4> <h4>Autres fonctions utiles</h4>
@ -557,16 +564,17 @@ def get_opt_str(etud, k):
return v.strip() return v.strip()
def get_annee_naissance(ddmmyyyyy): # stokee en dd/mm/yyyy dans le XML portail def get_annee_naissance(ddmmyyyyy: str) -> int:
"""Extrait l'année de la date stockée en dd/mm/yyyy dans le XML portail"""
if not ddmmyyyyy: if not ddmmyyyyy:
return None return None
try: try:
return int(ddmmyyyyy.split("/")[2]) return int(ddmmyyyyy.split("/")[2])
except: except (ValueError, IndexError):
return None return None
def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident, REQUEST): def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident):
"""Inscrit les etudiants Apogee dans ce semestre.""" """Inscrit les etudiants Apogee dans ce semestre."""
log("do_import_etuds_from_portal: a_importer=%s" % a_importer) log("do_import_etuds_from_portal: a_importer=%s" % a_importer)
if not a_importer: if not a_importer:
@ -623,7 +631,6 @@ def do_import_etuds_from_portal(sem, a_importer, etudsapo_ident, REQUEST):
args["etudid"], args["etudid"],
etat="I", etat="I",
etape=args["etape"], etape=args["etape"],
REQUEST=REQUEST,
method="synchro_apogee", method="synchro_apogee",
) )
except: except:
@ -838,16 +845,3 @@ def formsemestre_import_etud_admission(
unknowns.append(code_nip) unknowns.append(code_nip)
sco_cache.invalidate_formsemestre(formsemestre_id=sem["formsemestre_id"]) sco_cache.invalidate_formsemestre(formsemestre_id=sem["formsemestre_id"])
return no_nip, unknowns, changed_mails return no_nip, unknowns, changed_mails
def do_synch_inscrits_etuds(sem, etuds, REQUEST=None): # unused ?
"""inscrits ces etudiants (déja dans ScoDoc) au semestre"""
log("do_synch_inscrits_etuds: inscription de %d etudiants" % len(etuds))
for etud in etuds:
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
sem["formsemestre_id"],
etud["etudid"],
etat="I",
REQUEST=REQUEST,
method="synchro_apogee",
)

View File

@ -1358,7 +1358,7 @@ def formsemestre_desinscription(
) )
sco_formsemestre_inscriptions.do_formsemestre_desinscription( sco_formsemestre_inscriptions.do_formsemestre_desinscription(
etudid, formsemestre_id, REQUEST=REQUEST etudid, formsemestre_id
) )
return ( return (