Table recap: optimisation et cache

This commit is contained in:
Emmanuel Viennet 2022-04-05 22:23:55 +02:00
parent 01c0328636
commit 3ab0e89c2f
5 changed files with 67 additions and 13 deletions

View File

@ -14,6 +14,7 @@ import pandas as pd
from flask import g, url_for from flask import g, url_for
from app.auth.models import User
from app.comp.res_cache import ResultatsCache from app.comp.res_cache import ResultatsCache
from app.comp import res_sem from app.comp import res_sem
from app.comp.moy_mod import ModuleImplResults from app.comp.moy_mod import ModuleImplResults
@ -26,7 +27,6 @@ from app.scodoc.sco_codes_parcours import UE_SPORT, DEF, DEM
from app.scodoc import sco_evaluation_db from app.scodoc import sco_evaluation_db
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_users
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
# Il faut bien distinguer # Il faut bien distinguer
@ -414,7 +414,6 @@ class ResultatsSemestre(ResultatsCache):
- les colonnes de modules (SAE ou res.) d'une UE ont la classe mod_ue_<ue_id> - les colonnes de modules (SAE ou res.) d'une UE ont la classe mod_ue_<ue_id>
_<column_id>_order : clé de tri _<column_id>_order : clé de tri
""" """
if convert_values: if convert_values:
fmt_note = scu.fmt_note fmt_note = scu.fmt_note
else: else:
@ -430,6 +429,7 @@ class ResultatsSemestre(ResultatsCache):
titles = {} titles = {}
# les titres en footer: les mêmes, mais avec des bulles et liens: # les titres en footer: les mêmes, mais avec des bulles et liens:
titles_bot = {} titles_bot = {}
dict_nom_res = {} # cache uid : nomcomplet
def add_cell( def add_cell(
row: dict, row: dict,
@ -602,11 +602,14 @@ class ResultatsSemestre(ResultatsCache):
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
moduleimpl_id=modimpl.id, moduleimpl_id=modimpl.id,
) )
nom_resp = dict_nom_res.get(modimpl.responsable_id)
if nom_resp is None:
user = User.query.get(modimpl.responsable_id)
nom_resp = user.get_nomcomplet() if user else ""
dict_nom_res[modimpl.responsable_id] = nom_resp
titles_bot[ titles_bot[
f"_{col_id}_target_attrs" f"_{col_id}_target_attrs"
] = f""" ] = f""" title="{modimpl.module.titre} ({nom_resp})" """
title="{modimpl.module.titre}
({sco_users.user_info(modimpl.responsable_id)['nomcomplet']})" """
modimpl_ids.add(modimpl.id) modimpl_ids.add(modimpl.id)
ue_valid_txt = f"{nb_ues_validables}/{len(ues_sans_bonus)}" ue_valid_txt = f"{nb_ues_validables}/{len(ues_sans_bonus)}"
if nb_ues_warning: if nb_ues_warning:

View File

@ -16,6 +16,7 @@ from app import models
from app.scodoc import notesdb as ndb from app.scodoc import notesdb as ndb
from app.scodoc.sco_bac import Baccalaureat from app.scodoc.sco_bac import Baccalaureat
from app.scodoc.sco_exceptions import ScoValueError
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
@ -354,7 +355,10 @@ def make_etud_args(
""" """
args = None args = None
if etudid: if etudid:
args = {"etudid": etudid} try:
args = {"etudid": int(etudid)}
except ValueError as exc:
raise ScoValueError("Adresse invalide") from exc
elif code_nip: elif code_nip:
args = {"code_nip": code_nip} args = {"code_nip": code_nip}
elif use_request: # use form from current request (Flask global) elif use_request: # use form from current request (Flask global)

View File

@ -49,7 +49,6 @@
# sco_cache.EvaluationCache.get(evaluation_id), set(evaluation_id, value), delete(evaluation_id), # sco_cache.EvaluationCache.get(evaluation_id), set(evaluation_id, value), delete(evaluation_id),
# #
import time
import traceback import traceback
from flask import g from flask import g
@ -198,6 +197,26 @@ class SemInscriptionsCache(ScoDocCache):
duration = 12 * 60 * 60 # ttl 12h duration = 12 * 60 * 60 # ttl 12h
class TableRecapCache(ScoDocCache):
"""Cache table recap (pour get_table_recap)
Clé: formsemestre_id
Valeur: le html (<div class="table_recap">...</div>)
"""
prefix = "RECAP"
duration = 12 * 60 * 60 # ttl 12h
class TableRecapWithEvalsCache(ScoDocCache):
"""Cache table recap (pour get_table_recap)
Clé: formsemestre_id
Valeur: le html (<div class="table_recap">...</div>)
"""
prefix = "RECAPWITHEVALS"
duration = 12 * 60 * 60 # ttl 12h
def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=False) def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=False)
formsemestre_id=None, pdfonly=False formsemestre_id=None, pdfonly=False
): ):
@ -209,7 +228,7 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
if getattr(g, "defer_cache_invalidation", False): if getattr(g, "defer_cache_invalidation", False):
g.sem_to_invalidate.add(formsemestre_id) g.sem_to_invalidate.add(formsemestre_id)
return return
log("inval_cache, formsemestre_id=%s pdfonly=%s" % (formsemestre_id, pdfonly)) log("inval_cache, formsemestre_id={formsemestre_id} pdfonly={pdfonly}")
if formsemestre_id is None: if formsemestre_id is None:
# clear all caches # clear all caches
log("----- invalidate_formsemestre: clearing all caches -----") log("----- invalidate_formsemestre: clearing all caches -----")
@ -247,6 +266,9 @@ def invalidate_formsemestre( # was inval_cache(formsemestre_id=None, pdfonly=Fa
SemInscriptionsCache.delete_many(formsemestre_ids) SemInscriptionsCache.delete_many(formsemestre_ids)
ResultatsSemestreCache.delete_many(formsemestre_ids) ResultatsSemestreCache.delete_many(formsemestre_ids)
ValidationsSemestreCache.delete_many(formsemestre_ids) ValidationsSemestreCache.delete_many(formsemestre_ids)
TableRecapCache.delete_many(formsemestre_ids)
TableRecapWithEvalsCache.delete_many(formsemestre_ids)
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids) SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)

View File

@ -48,6 +48,7 @@ from app.scodoc import html_sco_header
from app.scodoc import sco_bulletins_json from app.scodoc import sco_bulletins_json
from app.scodoc import sco_bulletins_xml from app.scodoc import sco_bulletins_xml
from app.scodoc import sco_bulletins, sco_excel from app.scodoc import sco_bulletins, sco_excel
from app.scodoc import sco_cache
from app.scodoc import sco_codes_parcours from app.scodoc import sco_codes_parcours
from app.scodoc import sco_evaluations from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db from app.scodoc import sco_evaluation_db
@ -1031,8 +1032,35 @@ def gen_formsemestre_recapcomplet_html(
formsemestre: FormSemestre, res: NotesTableCompat, include_evaluations=False formsemestre: FormSemestre, res: NotesTableCompat, include_evaluations=False
): ):
"""Construit table recap pour le BUT """Construit table recap pour le BUT
Cache le résultat pour le semestre.
Return: data, filename Return: data, filename
""" """
filename = scu.sanitize_filename(
f"""recap-{formsemestre.titre_num()}-{time.strftime("%Y-%m-%d")}"""
)
if include_evaluations:
table_html = sco_cache.TableRecapWithEvalsCache.get(formsemestre.id)
else:
table_html = sco_cache.TableRecapCache.get(formsemestre.id)
if table_html is None:
table_html = _gen_formsemestre_recapcomplet_html(
formsemestre, res, include_evaluations, filename
)
if include_evaluations:
sco_cache.TableRecapWithEvalsCache.set(formsemestre.id, table_html)
else:
sco_cache.TableRecapCache.set(formsemestre.id, table_html)
return table_html, filename
def _gen_formsemestre_recapcomplet_html(
formsemestre: FormSemestre,
res: NotesTableCompat,
include_evaluations=False,
filename: str = "",
) -> str:
"""Génère le html"""
rows, footer_rows, titles, column_ids = res.get_table_recap( rows, footer_rows, titles, column_ids = res.get_table_recap(
convert_values=True, include_evaluations=include_evaluations convert_values=True, include_evaluations=include_evaluations
) )
@ -1041,9 +1069,6 @@ def gen_formsemestre_recapcomplet_html(
'<div class="table_recap"><div class="message">aucun étudiant !</div></div>', '<div class="table_recap"><div class="message">aucun étudiant !</div></div>',
"", "",
) )
filename = scu.sanitize_filename(
f"""recap-{formsemestre.titre_num()}-{time.strftime("%Y-%m-%d")}"""
)
H = [ H = [
f"""<div class="table_recap"><table class="table_recap { f"""<div class="table_recap"><table class="table_recap {
'apc' if formsemestre.formation.is_apc() else 'classic'}" 'apc' if formsemestre.formation.is_apc() else 'classic'}"
@ -1074,4 +1099,4 @@ def gen_formsemestre_recapcomplet_html(
</div> </div>
""" """
) )
return ("".join(H), filename) # suffix ? return "".join(H)

View File

@ -227,7 +227,7 @@ def _user_list(user_name):
@cache.memoize(timeout=50) # seconds @cache.memoize(timeout=50) # seconds
def user_info(user_name_or_id=None, user=None): def user_info(user_name_or_id=None, user: User = None):
"""Dict avec infos sur l'utilisateur (qui peut ne pas etre dans notre base). """Dict avec infos sur l'utilisateur (qui peut ne pas etre dans notre base).
Si user_name est specifie (string ou id), interroge la BD. Sinon, user doit etre une instance Si user_name est specifie (string ou id), interroge la BD. Sinon, user doit etre une instance
de User. de User.