Subir archivos a "/"
This commit is contained in:
173
FUTUROS_CAMBIOS.md
Normal file
173
FUTUROS_CAMBIOS.md
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
# FUTUROS CAMBIOS — Dashboard LEADS
|
||||||
|
|
||||||
|
Lista de cosas pendientes que el usuario quiere hacer más adelante.
|
||||||
|
Cuando el usuario pida "futuros cambios", recordarle TODO esto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Mapa de campañas (frases de pauta) → mover a GitHub o Supabase
|
||||||
|
- Hoy está incrustado en el código: `backend/data_manager_v2.py`, lista `CAMPANIAS`
|
||||||
|
(las ~90 frases con emojis + programa/sede/código/rango de fechas).
|
||||||
|
- Objetivo: sacarlo a un JSON en GitHub (o tabla en Supabase) para editarlo
|
||||||
|
sin tocar el código (igual que se hace en el otro dashboard con las clasificaciones).
|
||||||
|
|
||||||
|
## 2. Leyenda de los códigos de pauta → a GitHub/Supabase
|
||||||
|
- Los códigos de campaña (191, 193, 63a, etc.) y su significado.
|
||||||
|
- Mismo objetivo: que sea editable fuera del código.
|
||||||
|
|
||||||
|
## 3. Excluir cursos (num_indice) puntuales del cálculo
|
||||||
|
- Por ahora EXCLUIR del cálculo de Ocupabilidad/cursos: **num_indice 1154 y 1121**.
|
||||||
|
- Motivo: el usuario indicó que no deben contar (ej. reprogramados a otro mes).
|
||||||
|
- Idea a futuro: manejar esta lista de exclusión desde GitHub/Supabase, no en código.
|
||||||
|
- (Relacionado: el PBI filtra cursos por FECHA_MOSTRAR_COL = fecha con reprogramación
|
||||||
|
de SharePoint; aquí no tenemos esa columna todavía → Cursos Reprogramados quedó en fase 2.)
|
||||||
|
|
||||||
|
## 4. Reprogramación de cursos (SharePoint) — FASE 2 [DEPENDE DE LA FUENTE]
|
||||||
|
La fecha ORIGINAL planificada de cada curso venía de SharePoint
|
||||||
|
(BaseBI_Programacion: FECHA CALENDARIO vs FECHA REPROGRAMADO). Sin esa fuente,
|
||||||
|
varios KPIs no se pueden calcular exacto. Pendiente: traerla de Google Sheets / Supabase / JSON
|
||||||
|
y construir FECHA_MOSTRAR_COL, FECHA_CUADRO_BI y ESTADO_CUADRO_BI.
|
||||||
|
|
||||||
|
KPIs/cosas que dependen de esto (hoy aproximados o en 0):
|
||||||
|
- **Cursos Reprogramados** → hoy SALE 0 (no hay fecha original para comparar).
|
||||||
|
- **Cursos Programados** → hoy usa solo `fch_inicio` (el PBI usa planificada + reprogramada).
|
||||||
|
Diferencia chica, se cuadrará en fase 2.
|
||||||
|
- **Cursos Iniciados / Suspendidos** → hoy por `fch_inicio` y `cod_estado`; revisar con reprogramación.
|
||||||
|
- La exclusión manual de cursos (1154, 1121) es justo por reprogramación: cuando llegue
|
||||||
|
la fuente, esto debería resolverse solo (ya no haría falta la lista manual).
|
||||||
|
|
||||||
|
## 5. Matriz por Curso (num_indice) con métricas de Leads por Pauta — NUEVA
|
||||||
|
El usuario quiere una MATRIZ debajo de la tabla por Sede, con:
|
||||||
|
- FILA: Fact_SQL_Base_Cursos[Personalizado], separada por cada num_indice (cursos se repiten).
|
||||||
|
- VALORES: Fecha_Inicio_Visual, Leads_Acumulados_Historico, Cant_Leads_Nuevos,
|
||||||
|
Cant_Leads_Nuevos_Con_Asesor, Cant_Leads_Nuevo_x_Mes, Cant_Leads_Nuevos_x_Mes_Con_Asesor.
|
||||||
|
|
||||||
|
DEPENDE DE: el campo **Pauta** (código de campaña) de CADA curso, que en el PBI venía de
|
||||||
|
SharePoint (BaseBI_Programacion → PAUTA_CODIGO). El cruce de TODAS esas medidas es por
|
||||||
|
TREATAS(Cursos[Pauta], Leads[codigo]) — o sea por CÓDIGO, no por teléfono.
|
||||||
|
|
||||||
|
Lo que se necesita para construirla (traer a Google Sheets / Supabase / JSON):
|
||||||
|
- Por cada num_indice (curso): su **código(s) de Pauta** asociado.
|
||||||
|
- Idealmente también FECHA_CUADRO_BI (fecha real/reprogramada) — mismo paquete que el punto 4.
|
||||||
|
|
||||||
|
Medidas (ya tengo las fórmulas DAX exactas guardadas):
|
||||||
|
- Cant_Leads_Nuevos = leads únicos (Cantidad_Veces=1) con codigo = Pauta del curso, histórico.
|
||||||
|
- ..._Con_Asesor = + asesor no vacío.
|
||||||
|
- ..._x_Mes = igual pero respeta filtro de fecha del mes.
|
||||||
|
- ..._x_Mes_Con_Asesor = del mes + con asesor.
|
||||||
|
- Leads_Acumulados_Historico = suma de Cant_Leads_Nuevos de cursos del mismo Personalizado
|
||||||
|
con fecha <= la del curso actual.
|
||||||
|
- Fecha_Inicio_Visual = FECHA_CUADRO_BI del curso.
|
||||||
|
|
||||||
|
ESTADO: ✅ La MATRIZ YA ESTÁ CONSTRUIDA en el dashboard (backend `leads_logic.matriz_cursos`,
|
||||||
|
frontend "Detalle por Curso"). Hoy muestra Programa + Fecha Inicio reales; las 5 columnas de
|
||||||
|
leads salen en 0 porque FALTA el código de Pauta por curso. Cuando llegue la fuente, solo se
|
||||||
|
rellena el cruce y se activan las 5 medidas.
|
||||||
|
|
||||||
|
### Fuente SharePoint que hay que conectar (acordado)
|
||||||
|
Archivo: Base_BI_2.xlsx
|
||||||
|
Ruta: https://escuelarefrigeracion.sharepoint.com/sites/ASESORASCOMERCIALES/
|
||||||
|
Documentos compartidos/2. BASE PROSPECTOS/BASE GENERAL/Patricia/Base_BI_2.xlsx
|
||||||
|
Tabla: BaseBI_Programacion
|
||||||
|
Columnas: NUM_INDICE, FECHA CALENDARIO, FECHA REPROGRAMADO, ESTADO, CONTAR, PAUTA_CODIGO
|
||||||
|
Cruce: Cursos[NUM_INDICE] → BaseBI_Programacion[NUM_INDICE] ⇒ PAUTA_CODIGO
|
||||||
|
(en el PBI: columna calculada Pauta = RELATED(BaseBI_Programacion[PAUTA_CODIGO]))
|
||||||
|
Luego: TREATAS(Cursos[Pauta], Leads[codigo]) para contar leads.
|
||||||
|
|
||||||
|
Cómo leerla (3 opciones, de menos a más esfuerzo):
|
||||||
|
1) (Recomendado) Que Patricia/usuario suba a Supabase una tabla `cursos_pauta`
|
||||||
|
(num_indice, pauta_codigo, fecha_cuadro_bi). El backend ya tiene credenciales Supabase.
|
||||||
|
→ cero auth de Microsoft, instantáneo, editable.
|
||||||
|
2) Exportar esa hoja a un Google Sheet / CSV en GitHub (GITHUB_BASE ya configurado).
|
||||||
|
3) Lectura directa SharePoint con Office365-REST-Python-Client (requiere usuario+clave M365
|
||||||
|
o app registration). Más frágil; dejarlo como última opción.
|
||||||
|
|
||||||
|
Cuando exista la fuente, en `data_manager_v2.py` agregar `traer_cursos_pauta()` y mapear
|
||||||
|
num_indice→pauta sobre los cursos; en `leads_logic.matriz_cursos` reemplazar los 0 por el
|
||||||
|
conteo de leads cruzado por codigo (las fórmulas DAX de arriba ya están claras).
|
||||||
|
|
||||||
|
## 6. Base Junta (identificador de canal de origen por teléfono) — FUTURO
|
||||||
|
Objetivo: cuando un número se MATRICULA, saber su CANAL DE ORIGEN (y sede/programa/código)
|
||||||
|
para clasificarlo. Se usará como "memoria" de consulta por teléfono.
|
||||||
|
|
||||||
|
Fuente/lógica (ya PROBADA y validada con el usuario):
|
||||||
|
- Une leads de POSTGRE (Chatwoot + mapa de campañas CAMPANIAS → canal/sede/programa/código)
|
||||||
|
+ SUPABASE `datos_unificados` (Telefono, Fechacreada, Canal, Sede, Programa, Codigo).
|
||||||
|
- Teléfono NORMALIZADO: quita '+51', '+', espacios, '-', '(', ')'.
|
||||||
|
- DEDUP por teléfono → se queda con el MÁS ANTIGUO (solo por FECHA, sin hora).
|
||||||
|
- Desempate misma fecha: 1) PAUTA_WSP 2) PAUTA_WSP_FACE 3) orden alfabético del canal.
|
||||||
|
- Texto en MAYÚSCULAS; '-' de Supabase → vacío.
|
||||||
|
- Resultado prueba: ~45,054 teléfonos únicos.
|
||||||
|
|
||||||
|
Script de prueba (standalone, corre en cualquier carpeta): `base_junta_full.py`
|
||||||
|
(genera base_junta_full.xlsx con 6 columnas: Telefono, Canal, Fecha Creada, Sede,
|
||||||
|
Programa, Codigo). NO está conectado al dashboard todavía.
|
||||||
|
|
||||||
|
Cómo conectarlo a futuro (acordado, debe ser LIVIANO):
|
||||||
|
- Subir la base junta a una tabla en Supabase (ej. `base_junta`).
|
||||||
|
- El backend la carga UNA vez en un dict {telefono → datos}, cacheado (refresco ~15 min).
|
||||||
|
- Al cruzar una matrícula por teléfono → se obtiene su canal de origen y se clasifica.
|
||||||
|
- 44-73k filas en dict = instantáneo, sin impacto en la carga.
|
||||||
|
|
||||||
|
ESTADO: pendiente. Cuando se retome: crear tabla Supabase + traer_base_junta() en backend
|
||||||
|
+ cruce por teléfono en el módulo que corresponda.
|
||||||
|
|
||||||
|
## 7. Simplificar/normalizar nombres de programa (Personalizado) — MEJORA OPCIONAL
|
||||||
|
Los nombres largos de los cursos se acortan con una lista de reemplazos de texto
|
||||||
|
en `backend/leads_logic.py` → `_PERS_REEMPLAZOS` (hardcoded).
|
||||||
|
|
||||||
|
Problemas/mejoras:
|
||||||
|
- Está en el código: cada nombre nuevo/largo hay que agregarlo a mano.
|
||||||
|
- Algunos reemplazos quedan SIN sede ni frecuencia consistente
|
||||||
|
(ej. "VOLUMEN VARIABLE VRF" quedó sin "SEDE -" ni "- FREC").
|
||||||
|
- Ideal: formato uniforme tipo "SEDE - CORTO - FREC" para todos.
|
||||||
|
|
||||||
|
Ideas (opcional, cuando haya tiempo):
|
||||||
|
1. Mover `_PERS_REEMPLAZOS` a un JSON en GitHub o tabla Supabase → editable sin tocar código.
|
||||||
|
2. Normalizar TODOS los nombres con un mismo criterio (sede + nombre corto + frecuencia),
|
||||||
|
no solo los VRF/CO2 ya hechos.
|
||||||
|
|
||||||
|
ESTADO: opcional. No urgente; los reemplazos actuales funcionan.
|
||||||
|
|
||||||
|
## 8. Manual de tablas de Supabase — PENDIENTE
|
||||||
|
Mantener UN solo documento que liste cada tabla de Supabase y para qué sirve
|
||||||
|
(evitar olvidarse). Ej.:
|
||||||
|
- `datos_unificados` → leads (proyecto ogzjtkxnfs...).
|
||||||
|
- `basebi_programacion` → pauta/estado/contar por num_indice (proyecto uztqscimts...).
|
||||||
|
- `cartera_junta` → cartera total combinada por teléfono + es_origen (proyecto uztqscimts...).
|
||||||
|
- (futuras: `programa_alias` → diccionario de normalización).
|
||||||
|
Cada vez que se cree una tabla nueva, agregarla aquí.
|
||||||
|
|
||||||
|
## 9. Normalización de programa/sede en cartera_junta + alerta de valores nuevos — PENDIENTE
|
||||||
|
Problema: en `cartera_junta` hay valores repetidos con distinta escritura
|
||||||
|
(CÁMARAS/CAMARAS, GESTION/GESTIÓN, VRF / VRV vs VRF, SUP. DE OBRAS/SUPER.OBRAS/
|
||||||
|
SUPERVISIÓN DE OBRAS, DIPLONADO REF typo, LINA→LIMA) y basura (vacío, "0").
|
||||||
|
Como la tabla se regenera a diario (GitHub Actions), limpiar a mano NO sirve
|
||||||
|
(se sobrescribe). Hay que normalizar en la GENERACIÓN.
|
||||||
|
|
||||||
|
Plan acordado (mínimo de tablas):
|
||||||
|
- En `carga_cartera.py`: (a) reglas automáticas → MAYÚSCULAS + sin acentos + sin
|
||||||
|
espacios extra (une CÁMARAS=CAMARAS, GESTIÓN=GESTION, etc. sin mantener nada);
|
||||||
|
(b) UNA tabla-diccionario `programa_alias` (y sede) en Supabase con
|
||||||
|
alias → valor_correcto para los sinónimos/typos que las reglas no resuelven.
|
||||||
|
- Detección de NUEVOS: NO usar tabla de pendientes. Calcular al vuelo en el
|
||||||
|
dashboard, comparando los valores únicos de cartera_junta vs el diccionario.
|
||||||
|
- Alerta: 🔔 campanita/panel de notificaciones en el dashboard. Silencioso si todo
|
||||||
|
es conocido; si aparece sede/programa nuevo no reconocido, se enciende con la
|
||||||
|
lista para que el usuario decida (unir como alias o dejar como nuevo real).
|
||||||
|
- Decidir aparte: valores vacíos y "0" → dejar o convertir a SIN PROGRAMA/SIN SEDE.
|
||||||
|
|
||||||
|
## 10. Clasificar valores no identificados DESDE la campanita — MEJORA OPCIONAL
|
||||||
|
Hoy la campanita 🔔 solo AVISA de los programas/sedes no identificados; para
|
||||||
|
corregirlos hay que ir a Supabase y agregar la fila en `alias_normalizacion`.
|
||||||
|
Mejora: permitir hacerlo desde la misma web, en la notificación:
|
||||||
|
- Al lado de cada valor no identificado, un desplegable con los programas/sedes
|
||||||
|
YA EXISTENTES (canónicos) + botón "Asignar".
|
||||||
|
- Al asignar, el backend inserta la fila (alias → correcto) en `alias_normalizacion`
|
||||||
|
directamente desde la web (nuevo endpoint POST), sin entrar a Supabase.
|
||||||
|
- Así se limpia al instante y en la siguiente corrida del workflow ya sale unido.
|
||||||
|
ESTADO: opcional. Hoy funciona avisando; esto solo agiliza la corrección.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(Agregar aquí cualquier otro pendiente que vaya saliendo.)
|
||||||
12
INICIAR_LEADS.bat
Normal file
12
INICIAR_LEADS.bat
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
REM ============================================================
|
||||||
|
REM INICIAR DASHBOARD LEADS - Backend (8001) + Frontend (5174)
|
||||||
|
REM ============================================================
|
||||||
|
echo Iniciando BACKEND y FRONTEND de LEADS...
|
||||||
|
|
||||||
|
start "LEADS - BACKEND" cmd /k ""%~dp0_run_backend.bat""
|
||||||
|
start "LEADS - FRONTEND" cmd /k ""%~dp0_run_frontend.bat""
|
||||||
|
|
||||||
|
echo Listo. Se abrieron 2 ventanas (Backend y Frontend).
|
||||||
|
echo Cuando cargue, abre el navegador en: http://localhost:5174
|
||||||
|
timeout /t 4 >nul
|
||||||
78
OPTIMIZACION_CACHE.md
Normal file
78
OPTIMIZACION_CACHE.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# OPTIMIZACIÓN — Caché de descargas pesadas (reutilizable en Ventas, Comisiones, etc.)
|
||||||
|
|
||||||
|
## El problema (síntoma)
|
||||||
|
Al cambiar un filtro (mes, sede, etc.) el dashboard se demora 15-130 segundos,
|
||||||
|
aunque los datos "ya deberían estar cargados". Se siente como si cada filtro
|
||||||
|
volviera a descargar todo desde cero.
|
||||||
|
|
||||||
|
## La causa raíz
|
||||||
|
Una función que **descarga datos de una fuente externa** (Supabase, SQL Server,
|
||||||
|
Postgre, API) se llama SIN caché. Cada vez que el usuario filtra, esa función
|
||||||
|
**vuelve a descargar TODO** desde internet.
|
||||||
|
|
||||||
|
En LEADS el culpable fue `traer_cartera_rows()` (bajaba 86,000 filas de Supabase
|
||||||
|
de 1000 en 1000 = 86 pedidos en serie ≈ 52-130s) y se re-descargaba en CADA
|
||||||
|
cambio de mes, porque `matriz_web_formulario` la llamaba directo:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# ANTES (malo): re-descarga en cada llamada
|
||||||
|
def _load():
|
||||||
|
rows = get_dm().traer_cartera_rows() # baja 86k filas CADA vez
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cómo diagnosticarlo (script de tiempos)
|
||||||
|
Medir cada parte por separado para encontrar QUÉ tarda. Clave: medir un cambio
|
||||||
|
de mes CON los insumos ya cargados (2a vez). Si sigue lento, algo se re-descarga.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time, services as S
|
||||||
|
def cron(label, fn):
|
||||||
|
t0 = time.time(); fn(); print(f" {label:38} {time.time()-t0:6.1f}s")
|
||||||
|
|
||||||
|
cron("dashboard MES1 (1a vez)", lambda: S.mi_funcion("2026","1",...))
|
||||||
|
cron("dashboard MES2 (2a vez)", lambda: S.mi_funcion("2026","2",...)) # <-- debe ser rapido
|
||||||
|
# Si MES2 sale lento, desglosar: medir cada traer_* y cada matriz por separado
|
||||||
|
# hasta ver cual linea se lleva los segundos (casi siempre un traer_* sin cache).
|
||||||
|
```
|
||||||
|
|
||||||
|
## La solución (2 líneas, NO toca lógica ni resultados)
|
||||||
|
Envolver la descarga pesada en `cache_get_or_set` con clave GLOBAL, y que TODAS
|
||||||
|
las funciones usen ese helper cacheado en vez de descargar directo.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 1) Helper cacheado (se baja UNA vez, se reutiliza)
|
||||||
|
def _cartera_rows_cache():
|
||||||
|
return cache_get_or_set("cartera_rows", ("GLOBAL",),
|
||||||
|
lambda: get_dm().traer_cartera_rows())
|
||||||
|
|
||||||
|
# 2) Reemplazar TODAS las llamadas directas:
|
||||||
|
# get_dm().traer_cartera_rows() -> _cartera_rows_cache()
|
||||||
|
```
|
||||||
|
|
||||||
|
Resultado en LEADS:
|
||||||
|
- Cambiar de mes: de 69-133s -> 0.4s (solo filtra lo que ya está en memoria)
|
||||||
|
- La descarga pesada solo se paga 1 vez (en la precarga de arranque, en background).
|
||||||
|
|
||||||
|
## Regla general para CUALQUIER dashboard (Ventas, Comisiones, Rentabilidad...)
|
||||||
|
1. Toda función `traer_*` / consulta a SQL/Supabase/API que se use en varias
|
||||||
|
vistas o en cada filtro, debe estar cacheada con `cache_get_or_set`.
|
||||||
|
2. NUNCA llamar `get_dm().traer_xxx()` directo dentro de un `_load` que depende
|
||||||
|
de filtros; usar el helper cacheado.
|
||||||
|
3. El filtro (mes/sede/programa) debe operar sobre datos YA en memoria, no
|
||||||
|
re-descargar. Filtrar en memoria = milisegundos.
|
||||||
|
4. Precargar en segundo plano (hilo daemon al startup) el mes actual/anterior
|
||||||
|
para que el usuario no espere la 1a carga.
|
||||||
|
5. El refresco periódico (cada 15 min) invalida caché -> considerar bajar datos
|
||||||
|
que cambian 1 vez al día (ej. cartera) con menos frecuencia, o recargar en
|
||||||
|
background sin dejar hueco.
|
||||||
|
|
||||||
|
## Mejora opcional pendiente (no aplicada aún)
|
||||||
|
La 1a descarga de la cartera es lenta (100-155s) porque pagina de 1000 en 1000.
|
||||||
|
Subir el tamaño de lote (ej. limit=10000) reduce los viajes: de ~155s a ~10-15s.
|
||||||
|
Aplica a cualquier tabla grande que se baje paginada.
|
||||||
|
|
||||||
|
## Verificación (que no cambien números)
|
||||||
|
Antes de optimizar, guardar una "línea base" con los totales actuales
|
||||||
|
(diag_base.json). Tras optimizar, comparar: deben ser IDÉNTICOS. La optimización
|
||||||
|
solo cambia CUÁNDO/CÓMO se descarga, nunca el cálculo.
|
||||||
11
_run_backend.bat
Normal file
11
_run_backend.bat
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@echo off
|
||||||
|
REM Lanzador interno del BACKEND de LEADS
|
||||||
|
cd /d "%~dp0backend"
|
||||||
|
echo ====================================
|
||||||
|
echo LEADS - BACKEND (FastAPI) puerto 8001
|
||||||
|
echo Carpeta: %CD%
|
||||||
|
echo ====================================
|
||||||
|
py -3.12 main.py
|
||||||
|
echo.
|
||||||
|
echo (El backend termino o fallo. Revisa los mensajes de arriba.)
|
||||||
|
pause
|
||||||
12
_run_frontend.bat
Normal file
12
_run_frontend.bat
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d "%~dp0frontend"
|
||||||
|
echo ====================================
|
||||||
|
echo LEADS - FRONTEND (Vite) puerto 5174
|
||||||
|
echo Carpeta: %CD%
|
||||||
|
echo ====================================
|
||||||
|
if not exist "node_modules\.bin\vite.cmd" call npm install
|
||||||
|
echo Iniciando servidor de desarrollo...
|
||||||
|
call npm run dev
|
||||||
|
echo.
|
||||||
|
echo (npm run dev termino o fallo.)
|
||||||
|
pause
|
||||||
Reference in New Issue
Block a user