From 3ab0e89c2f635c1564c88bb62143f1ba60ee524a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 5 Apr 2022 22:23:55 +0200 Subject: [PATCH] Table recap: optimisation et cache --- app/comp/res_common.py | 13 ++++++++----- app/models/etudiants.py | 6 +++++- app/scodoc/sco_cache.py | 26 ++++++++++++++++++++++++-- app/scodoc/sco_recapcomplet.py | 33 +++++++++++++++++++++++++++++---- app/scodoc/sco_users.py | 2 +- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/app/comp/res_common.py b/app/comp/res_common.py index 5214a2e66..6e37a8c2d 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -14,6 +14,7 @@ import pandas as pd from flask import g, url_for +from app.auth.models import User from app.comp.res_cache import ResultatsCache from app.comp import res_sem 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.sco_exceptions import ScoValueError from app.scodoc import sco_groups -from app.scodoc import sco_users from app.scodoc import sco_utils as scu # 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_ __order : clé de tri """ - if convert_values: fmt_note = scu.fmt_note else: @@ -430,6 +429,7 @@ class ResultatsSemestre(ResultatsCache): titles = {} # les titres en footer: les mêmes, mais avec des bulles et liens: titles_bot = {} + dict_nom_res = {} # cache uid : nomcomplet def add_cell( row: dict, @@ -602,11 +602,14 @@ class ResultatsSemestre(ResultatsCache): scodoc_dept=g.scodoc_dept, 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[ f"_{col_id}_target_attrs" - ] = f""" - title="{modimpl.module.titre} - ({sco_users.user_info(modimpl.responsable_id)['nomcomplet']})" """ + ] = f""" title="{modimpl.module.titre} ({nom_resp})" """ modimpl_ids.add(modimpl.id) ue_valid_txt = f"{nb_ues_validables}/{len(ues_sans_bonus)}" if nb_ues_warning: diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 6c342482c..962e7baaf 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -16,6 +16,7 @@ from app import models from app.scodoc import notesdb as ndb from app.scodoc.sco_bac import Baccalaureat +from app.scodoc.sco_exceptions import ScoValueError import app.scodoc.sco_utils as scu @@ -354,7 +355,10 @@ def make_etud_args( """ args = None if etudid: - args = {"etudid": etudid} + try: + args = {"etudid": int(etudid)} + except ValueError as exc: + raise ScoValueError("Adresse invalide") from exc elif code_nip: args = {"code_nip": code_nip} elif use_request: # use form from current request (Flask global) diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index 6330849c5..2e9792e41 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -49,7 +49,6 @@ # sco_cache.EvaluationCache.get(evaluation_id), set(evaluation_id, value), delete(evaluation_id), # -import time import traceback from flask import g @@ -198,6 +197,26 @@ class SemInscriptionsCache(ScoDocCache): duration = 12 * 60 * 60 # ttl 12h +class TableRecapCache(ScoDocCache): + """Cache table recap (pour get_table_recap) + Clé: formsemestre_id + Valeur: le html (
...
) + """ + + prefix = "RECAP" + duration = 12 * 60 * 60 # ttl 12h + + +class TableRecapWithEvalsCache(ScoDocCache): + """Cache table recap (pour get_table_recap) + Clé: formsemestre_id + Valeur: le html (
...
) + """ + + prefix = "RECAPWITHEVALS" + duration = 12 * 60 * 60 # ttl 12h + + def invalidate_formsemestre( # was inval_cache(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): g.sem_to_invalidate.add(formsemestre_id) 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: # clear 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) ResultatsSemestreCache.delete_many(formsemestre_ids) ValidationsSemestreCache.delete_many(formsemestre_ids) + TableRecapCache.delete_many(formsemestre_ids) + TableRecapWithEvalsCache.delete_many(formsemestre_ids) + SemBulletinsPDFCache.invalidate_sems(formsemestre_ids) diff --git a/app/scodoc/sco_recapcomplet.py b/app/scodoc/sco_recapcomplet.py index f7b365ed0..84fbafc38 100644 --- a/app/scodoc/sco_recapcomplet.py +++ b/app/scodoc/sco_recapcomplet.py @@ -48,6 +48,7 @@ from app.scodoc import html_sco_header from app.scodoc import sco_bulletins_json from app.scodoc import sco_bulletins_xml 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_evaluations from app.scodoc import sco_evaluation_db @@ -1031,8 +1032,35 @@ def gen_formsemestre_recapcomplet_html( formsemestre: FormSemestre, res: NotesTableCompat, include_evaluations=False ): """Construit table recap pour le BUT + Cache le résultat pour le semestre. 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( convert_values=True, include_evaluations=include_evaluations ) @@ -1041,9 +1069,6 @@ def gen_formsemestre_recapcomplet_html( '
aucun étudiant !
', "", ) - filename = scu.sanitize_filename( - f"""recap-{formsemestre.titre_num()}-{time.strftime("%Y-%m-%d")}""" - ) H = [ f"""
""" ) - return ("".join(H), filename) # suffix ? + return "".join(H) diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index 19fdf9793..552d36d6a 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -227,7 +227,7 @@ def _user_list(user_name): @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). Si user_name est specifie (string ou id), interroge la BD. Sinon, user doit etre une instance de User.