Mejora: Optimización radical de carga del panel mediante pre-calentamiento asíncrono de caché UI (#5)

This commit is contained in:
2026-03-20 17:19:11 -06:00
parent f2466fc49e
commit 5633f51b24

View File

@@ -1,5 +1,8 @@
import os import os
import sys import sys
import glob
import re
import asyncio
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from fastapi import FastAPI, HTTPException, status from fastapi import FastAPI, HTTPException, status
@@ -67,12 +70,82 @@ def translate_filter(text, lang="es"):
templates.env.filters["translate"] = translate_filter templates.env.filters["translate"] = translate_filter
_warmed_languages = set()
async def pre_warm_ui_translations(lang: str):
if lang == "es" or lang in _warmed_languages:
return
_warmed_languages.add(lang)
print(f"[Panel] Pre-calentando traducciones para idioma UI: {lang}...")
strings_to_translate = set()
for f in glob.glob(os.path.join(os.path.dirname(__file__), "templates", "*.html")):
try:
with open(f, "r", encoding="utf-8") as fd:
content = fd.read()
strings_to_translate.update(re.findall(r'\"([^\"]+)\"\s*\|\s*translate', content))
strings_to_translate.update(re.findall(r'\'([^\']+)\'\s*\|\s*translate', content))
except Exception:
pass
missing_strings = []
for text in strings_to_translate:
norm_text = _normalize_text(text)
if not norm_text: continue
cache_key = f"{lang}:{norm_text}"
if cache_key in _ui_memory_cache: continue
cached = get_ui_translation(norm_text, lang)
if cached:
_ui_memory_cache[cache_key] = cached
else:
missing_strings.append(norm_text)
if not missing_strings:
print(f"[Panel] Pre-calentamiento completo: 0 cadenas faltantes para {lang}")
return
print(f"[Panel] Falta traducir {len(missing_strings)} cadenas UI al {lang}. Empezando concurrencia...")
from botdiscord.translate import translate_text
async def _fetch_and_save(text):
translated = await translate_text(text, lang)
if translated and translated != text:
save_ui_translation(text, lang, translated)
_ui_memory_cache[f"{lang}:{text}"] = translated
tasks = [_fetch_and_save(t) for t in missing_strings]
await asyncio.gather(*tasks)
print(f"[Panel] Pre-calentamiento terminado para {lang}.")
@app.middleware("http")
async def translation_pre_warm_middleware(request: Request, call_next):
# Detectamos el idioma que va a usar la página para asegurarnos de que esté cargado
if request.url.path.startswith("/static") or request.url.path.startswith("/api"):
return await call_next(request)
lang = request.cookies.get("panel_lang")
if not lang:
accept_lang = request.headers.get("accept-language", "es")
lang = accept_lang.split(",")[0].split("-")[0]
if lang and len(lang) == 2:
await pre_warm_ui_translations(lang)
return await call_next(request)
@app.get("/set-lang/{lang}") @app.get("/set-lang/{lang}")
async def set_lang(lang: str, request: Request): async def set_lang(lang: str, request: Request):
# Validamos que el idioma sea de 2 letras # Validamos que el idioma sea de 2 letras
if len(lang) != 2: if len(lang) != 2:
return RedirectResponse(url="/dashboard") return RedirectResponse(url="/dashboard")
# Ejecutamos el calentamiento asíncrono para que la redirección sea instantánea
await pre_warm_ui_translations(lang)
response = RedirectResponse(url=request.headers.get("referer", "/dashboard")) response = RedirectResponse(url=request.headers.get("referer", "/dashboard"))
response.set_cookie(key="panel_lang", value=lang, max_age=31536000) # 1 año response.set_cookie(key="panel_lang", value=lang, max_age=31536000) # 1 año
return response return response