Proyecto completo con configuracion de variables de entorno para EasyPanel

This commit is contained in:
Panchito
2026-07-21 16:36:17 -05:00
commit 9cc2d28b9a
45 changed files with 95958 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
# backend/export_base_junta.py
"""
Une los leads de Postgre (Fact_Leads_Procesados, mapa de campañas) + Supabase,
deduplica por teléfono quedándose con el MÁS ANTIGUO (desempate: Pauta_wsp_face),
y exporta un Excel con: Telefono, Canal, Fecha Creada.
Uso: py -3.12 export_base_junta.py
"""
import os
from datetime import datetime
from dotenv import load_dotenv
from data_manager_v2 import DataManager
load_dotenv()
# Canal preferido en caso de empate de fecha
CANAL_PREFERIDO = "Pauta_wsp_face"
def _norm_tel(v):
s = str(v or "")
for x in ("+51", "+", " ", "-", "(", ")"):
s = s.replace(x, "")
return s.strip()
def _txt(v):
"""Texto en MAYÚSCULAS; '-' o vacío → ''."""
s = str(v or "").strip()
if s == "-":
s = ""
return s.upper()
def _to_dt(v):
if v is None:
return None
if isinstance(v, datetime):
return v
s = str(v)[:19]
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d", "%d/%m/%Y", "%d-%m-%Y"):
try:
return datetime.strptime(s[:len(fmt) + 2] if "%H" in fmt else s[:10], fmt)
except Exception:
pass
return None
def traer_postgre(dm):
"""Mismos leads que Fact_Leads_Procesados (mapa de campañas): tel, fecha, canal(origen)."""
filas = dm.traer_leads_chatwoot() # ya aplica el mapa de campañas y trae 'origen'
out = []
for f in filas:
tel = _norm_tel(f.get("telefono"))
if not tel:
continue
out.append({
"telefono": tel,
"fecha": _to_dt(f.get("fecha_creada")),
"canal": _txt(f.get("origen")),
"sede": _txt(f.get("sede")),
"programa": _txt(f.get("programa")),
"codigo": _txt(f.get("codigo")),
})
return out
def traer_supabase(dm):
"""Lee la tabla de Supabase con columnas: telefono, fechacreada, canal."""
try:
from supabase import create_client
url = os.getenv("SUPABASE_URL", "")
key = os.getenv("SUPABASE_KEY", "")
if not url or not key:
print("⚠️ Falta SUPABASE_URL / SUPABASE_KEY en .env — se omite Supabase")
return []
sb = create_client(url, key)
tabla = os.getenv("SUPABASE_TABLA_LEADS", "leads") # ajustar nombre real si difiere
res = sb.table(tabla).select("Telefono,Fechacreada,Canal,Sede,Programa,Codigo").execute()
out = []
for r in (res.data or []):
tel = _norm_tel(r.get("Telefono"))
if not tel:
continue
out.append({
"telefono": tel,
"fecha": _to_dt(r.get("Fechacreada")),
"canal": _txt(r.get("Canal")),
"sede": _txt(r.get("Sede")),
"programa": _txt(r.get("Programa")),
"codigo": _txt(r.get("Codigo")),
})
return out
except Exception as e:
print(f"⚠️ Error leyendo Supabase: {e}")
return []
def main():
dm = DataManager()
print("Trayendo Postgre (Fact_Leads_Procesados)...")
pg = traer_postgre(dm)
print(f" Postgre: {len(pg)} filas")
print("Trayendo Supabase...")
sup = traer_supabase(dm)
print(f" Supabase: {len(sup)} filas")
todos = pg + sup
print(f"Total combinado (con repetidos): {len(todos)}")
# Dedup por teléfono: quedarse con el MÁS ANTIGUO.
# Desempate (misma fecha): preferir CANAL_PREFERIDO.
FUTURO = datetime(9999, 1, 1)
mejor = {}
for r in todos:
tel = r["telefono"]
f = r["fecha"] or FUTURO
actual = mejor.get(tel)
if actual is None:
mejor[tel] = r
continue
fa = actual["fecha"] or FUTURO
if f < fa:
mejor[tel] = r
elif f == fa:
# empate de fecha → preferir el canal preferido
if r["canal"] == CANAL_PREFERIDO and actual["canal"] != CANAL_PREFERIDO:
mejor[tel] = r
final = list(mejor.values())
print(f"Teléfonos únicos (sin repetir): {len(final)}")
# Exportar a Excel
import openpyxl
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Base_Junta"
ws.append(["Telefono", "Canal", "Fecha Creada", "Sede", "Programa", "Codigo"])
for r in sorted(final, key=lambda x: (x["fecha"] or FUTURO)):
fecha = r["fecha"].strftime("%d/%m/%Y") if r["fecha"] and r["fecha"] != FUTURO else ""
ws.append([r["telefono"], r["canal"], fecha,
r.get("sede", ""), r.get("programa", ""), r.get("codigo", "")])
salida = os.path.join(os.path.dirname(__file__), "base_junta.xlsx")
wb.save(salida)
print(f"\n✅ Exportado: {salida}")
print(f" Filas: {len(final)}")
if __name__ == "__main__":
main()