Actualizar backend/leads_logic.py
This commit is contained in:
@@ -1,4 +1,14 @@
|
||||
# backend/leads_logic.py
|
||||
|
||||
# Ganchos de clasificacion por JSON (los setea services.py al arrancar). Si estan
|
||||
# definidos, meta_curso/tipo_programa_curso los usan; si no, cae al metodo viejo.
|
||||
_CLASIF_SEDE = None # funcion(dsc_programa) -> sede
|
||||
_CLASIF_PROG = None # funcion(dsc_programa) -> TEAC/TERC/SEMINARIOS/OTROS
|
||||
|
||||
def set_clasificadores(f_sede, f_prog):
|
||||
global _CLASIF_SEDE, _CLASIF_PROG
|
||||
_CLASIF_SEDE = f_sede
|
||||
_CLASIF_PROG = f_prog
|
||||
"""
|
||||
Lógica del módulo LEADS. Traduce las medidas DAX del PBI a Python.
|
||||
Columnas calculadas replicadas:
|
||||
@@ -97,6 +107,11 @@ def grupo_programa_curso(dsc_programa):
|
||||
SEMINARIOS = SEMINARIOS
|
||||
OTROS = OTROS + CARRERA"""
|
||||
tp = tipo_programa_curso(dsc_programa)
|
||||
# Con el clasificador por JSON, tipo_programa_curso ya devuelve las 4 categorias
|
||||
# simples (TEAC/TERC/SEMINARIOS/OTROS) -> se devuelven tal cual.
|
||||
if tp in ("TEAC", "TERC", "SEMINARIOS", "OTROS"):
|
||||
return tp
|
||||
# Compatibilidad con el metodo viejo (nombres detallados sin JSON).
|
||||
if tp in ("PROGRAMAS TEAC", "PROVINCIA TEAC"):
|
||||
return "TEAC"
|
||||
if tp in ("PROGRAMAS TERC", "PROVINCIA TERC"):
|
||||
@@ -162,8 +177,10 @@ def tipo_programa_cohorte(sede, programa_cat):
|
||||
|
||||
|
||||
def tipo_programa_curso(dsc_programa):
|
||||
"""Réplica EXACTA del DAX Tipo_Programa (columna calculada de Fact_SQL_Base_Cursos).
|
||||
Usa CONTAINSSTRING en el mismo ORDEN que el PBI (el orden importa)."""
|
||||
"""Tipo de programa. Si hay clasificador por JSON, devuelve directo las 4
|
||||
categorias simples (TEAC/TERC/SEMINARIOS/OTROS), igual que Ocupabilidad."""
|
||||
if _CLASIF_PROG:
|
||||
return _CLASIF_PROG(dsc_programa) # TEAC / TERC / SEMINARIOS / OTROS
|
||||
p = str(dsc_programa or "")
|
||||
def has(s): # CONTAINSSTRING es sensible a may/min en DAX; comparamos tal cual
|
||||
return s in p
|
||||
@@ -215,8 +232,15 @@ def tipo_programa_curso(dsc_programa):
|
||||
|
||||
|
||||
def meta_curso(dsc_programa):
|
||||
"""Meta por curso según Tipo_Programa (igual que el DAX Meta_Curso):
|
||||
PROGRAMAS TEAC/TERC=22, PROVINCIA TEAC/TERC=18, resto=15."""
|
||||
"""Meta por curso: LIMA TEAC/TERC=22, PROVINCIA TEAC/TERC=18, resto=15.
|
||||
Usa la clasificacion del JSON (sede+programa) si esta disponible."""
|
||||
if _CLASIF_SEDE and _CLASIF_PROG:
|
||||
sede = _CLASIF_SEDE(dsc_programa)
|
||||
prog = _CLASIF_PROG(dsc_programa)
|
||||
if prog in ("TEAC", "TERC"):
|
||||
return 22 if sede == "LIMA" else 18
|
||||
return 15
|
||||
# respaldo: metodo viejo
|
||||
tp = tipo_programa_curso(dsc_programa)
|
||||
if tp in ("PROGRAMAS TEAC", "PROGRAMAS TERC"):
|
||||
return 22
|
||||
@@ -474,10 +498,14 @@ def leads_por_dia(leads, ano, mes, dia):
|
||||
|
||||
# ── Tabla por Sede_Act -> Tipo_Programa con 4 medidas (cohorte de leads) ──
|
||||
def tabla_pauta(leads, matriculas, ano, mes, dia, importe_por_pauta_periodo=None,
|
||||
resultados_por_pauta_periodo=None, campanias_sede_cargo=None):
|
||||
resultados_por_pauta_periodo=None, campanias_sede_cargo=None,
|
||||
codigos_branding=None):
|
||||
importe_por_pauta_periodo = importe_por_pauta_periodo or {}
|
||||
resultados_por_pauta_periodo = resultados_por_pauta_periodo or {}
|
||||
campanias_sede_cargo = campanias_sede_cargo or {}
|
||||
# Codigos (pautas) de BRANDING/COPEAIRE: su importe suma normal, pero sus
|
||||
# resultados NO se cuentan (se ponen en 0).
|
||||
codigos_branding = codigos_branding or set()
|
||||
# 1) Leads únicos del periodo con su teléfono, fecha, sede y tipo
|
||||
lp = [l for l in leads if l["es_unico"] and _en_periodo(l["fecha"], ano, mes, dia)]
|
||||
# mapa teléfono -> fecha del lead (para "matrícula posterior al lead")
|
||||
@@ -541,6 +569,8 @@ def tabla_pauta(leads, matriculas, ano, mes, dia, importe_por_pauta_periodo=None
|
||||
continue
|
||||
se = str(info.get("sede") or "").upper()
|
||||
tp = grupo_tipo_cohorte(tipo_programa_cohorte(se, info.get("cargo")))
|
||||
# Guarda el resultado REAL (para mostrarlo en el detalle del codigo).
|
||||
# Los BRANDING/COPEAIRE no se restan aqui, sino al sumar subtotales/total.
|
||||
res_st.setdefault((se, tp), {})[str(cod).strip()] = int(res_val or 0)
|
||||
|
||||
filas = []
|
||||
@@ -558,10 +588,14 @@ def tabla_pauta(leads, matriculas, ano, mes, dia, importe_por_pauta_periodo=None
|
||||
d = cods[cod]
|
||||
imp_cod = float(imp_map.get(str(cod).strip(), 0.0))
|
||||
res_cod = int(res_map.get(str(cod).strip(), 0))
|
||||
# El detalle muestra el resultado REAL; pero si es branding NO suma
|
||||
# al subtotal ni al total.
|
||||
es_brand = str(cod).strip() in codigos_branding
|
||||
codigos.append({"codigo": cod, **_fila(d), "importe": round(imp_cod, 2),
|
||||
"resultados": res_cod})
|
||||
t_rec += d["recibidos"]; t_proc += d["procesados"]
|
||||
t_mats |= d["mats"]; t_inv += d["inv"]; t_imp += imp_cod; t_res += res_cod
|
||||
t_mats |= d["mats"]; t_inv += d["inv"]; t_imp += imp_cod
|
||||
t_res += (0 if es_brand else res_cod)
|
||||
subfilas.append({
|
||||
"tipo": tp, "recibidos": t_rec, "procesados": t_proc,
|
||||
"matriculas": len(t_mats), "inversion": round(t_inv, 0),
|
||||
@@ -582,7 +616,8 @@ def tabla_pauta(leads, matriculas, ano, mes, dia, importe_por_pauta_periodo=None
|
||||
t_imp = round(sum(float(v or 0.0) for cod, v in importe_por_pauta_periodo.items()
|
||||
if str(cod).strip() in campanias_sede_cargo), 2)
|
||||
t_res = sum(int(v or 0) for cod, v in resultados_por_pauta_periodo.items()
|
||||
if str(cod).strip() in campanias_sede_cargo)
|
||||
if str(cod).strip() in campanias_sede_cargo
|
||||
and str(cod).strip() not in codigos_branding)
|
||||
# matrículas total: unión global
|
||||
all_mats = set()
|
||||
for tel, e in mat_por_tel.items():
|
||||
@@ -660,7 +695,8 @@ def matriz_cursos(cursos, leads, ano, mes, dia, pauta_map=None,
|
||||
campanias_map=None, cartera_all=None, cartera_asig=None,
|
||||
mat_por_indice=None, cartera_tels=None, cartera_origen=None,
|
||||
importe_por_pauta=None, importe_por_pauta_periodo=None,
|
||||
cartera_canal=None, conjunto_por_pauta=None, pautas_por_indice=None):
|
||||
cartera_canal=None, conjunto_por_pauta=None, pautas_por_indice=None,
|
||||
resultados_por_pauta=None):
|
||||
pauta_map = pauta_map or {}
|
||||
pautas_por_indice = pautas_por_indice or {} # {num_indice: [pautas]} (muchos-a-muchos)
|
||||
campanias_map = campanias_map or {}
|
||||
@@ -671,6 +707,7 @@ def matriz_cursos(cursos, leads, ano, mes, dia, pauta_map=None,
|
||||
cartera_origen = cartera_origen or {}
|
||||
importe_por_pauta = importe_por_pauta or {}
|
||||
importe_por_pauta_periodo = importe_por_pauta_periodo or {}
|
||||
resultados_por_pauta = resultados_por_pauta or {}
|
||||
conjunto_por_pauta = conjunto_por_pauta or {} # {pauta: [conjuntos de anuncios]}
|
||||
# cartera_canal = {"PAUTA": {"all","asig","tels"}, "OTROS": {...}} para el desglose por canal
|
||||
cartera_canal = cartera_canal or {}
|
||||
@@ -734,6 +771,9 @@ def matriz_cursos(cursos, leads, ano, mes, dia, pauta_map=None,
|
||||
pre.append({
|
||||
"num_indice": ni,
|
||||
"personalizado": nombre,
|
||||
# Nombre real del curso (para enlazar al apartado PROGRAMA, cuyo filtro
|
||||
# usa dsc_det_programa). Solo se agrega; no cambia nada mas.
|
||||
"dsc_det_programa": str(c.get("dsc_det_programa") or c.get("dsc_programa") or "").strip(),
|
||||
"fch": fch,
|
||||
"pauta": cods[0] if cods else None, # pauta principal (para mostrar)
|
||||
"pautas": cods, # todas las pautas (para sumar)
|
||||
@@ -816,6 +856,8 @@ def matriz_cursos(cursos, leads, ano, mes, dia, pauta_map=None,
|
||||
importe_pauta = round(sum(float(importe_por_pauta.get(str(cd), 0.0)) for cd in cods), 2)
|
||||
# IMPORTE PAUTA EN EL MES: igual, pero solo del periodo filtrado.
|
||||
importe_pauta_mes = round(sum(float(importe_por_pauta_periodo.get(str(cd), 0.0)) for cd in cods), 2)
|
||||
# LEADS META: Resultados TOTALES de la pauta (sin filtro de fecha).
|
||||
resultados_pauta = int(sum(float(resultados_por_pauta.get(str(cd), 0.0)) for cd in cods))
|
||||
|
||||
# ── SUBFILAS por CANAL (PAUTA / OTROS) ── mismo criterio que la fila padre
|
||||
# (SEDE+PROGRAMA vía campanias, misma fecha) + filtro de canal desde cartera.
|
||||
@@ -870,10 +912,12 @@ def matriz_cursos(cursos, leads, ano, mes, dia, pauta_map=None,
|
||||
filas.append({
|
||||
"num_indice": r["num_indice"],
|
||||
"personalizado": r["personalizado"],
|
||||
"dsc_det_programa": r.get("dsc_det_programa", ""), # para enlazar al apartado PROGRAMA
|
||||
"fecha_inicio": r["fecha_inicio"],
|
||||
"pauta": r.get("pauta"),
|
||||
"importe_pauta": importe_pauta,
|
||||
"importe_pauta_mes": importe_pauta_mes,
|
||||
"resultados_pauta": resultados_pauta,
|
||||
"cartera_total": cartera_total,
|
||||
"cartera_total_asig": cartera_total_asig,
|
||||
"matriculas_no_iden": matriculas_no_iden,
|
||||
|
||||
Reference in New Issue
Block a user