From 1f391753583515d2e52939082251445fecd82f50 Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 4 Sep 2026 16:24:15 -0500 Subject: [PATCH] Actualizar backend/data_manager_v2.py --- backend/data_manager_v2.py | 58 +++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/backend/data_manager_v2.py b/backend/data_manager_v2.py index d313689..1d210ee 100644 --- a/backend/data_manager_v2.py +++ b/backend/data_manager_v2.py @@ -172,6 +172,9 @@ def _values_sql(): filas = [] for frase, cargo, codigo, sede, dia, origen, fi, ff in _campanias_supabase(): f = frase.replace("'", "''") + # Si la fecha viene vacia, usar un rango por defecto para no romper el CAST. + fi = (str(fi).strip() or "2000-01-01")[:10] + ff = (str(ff).strip() or "2099-12-31")[:10] filas.append(f"('{f}','{cargo}','{codigo}','{sede}','{dia}','{origen}','{fi}','{ff}')") return ",\n".join(filas) @@ -197,24 +200,9 @@ class DataManager: def sql_conn(self): import pyodbc - # Driver ODBC configurable (SQL_DRIVER). Si no se define, se autodetecta: - # en Windows suele existir "SQL Server"; en Linux/Docker, msodbcsql18/17. - driver = os.getenv("SQL_DRIVER") - if not driver: - installed = pyodbc.drivers() - if "ODBC Driver 18 for SQL Server" in installed: - driver = "ODBC Driver 18 for SQL Server" - elif "ODBC Driver 17 for SQL Server" in installed: - driver = "ODBC Driver 17 for SQL Server" - elif "FreeTDS" in installed: - driver = "FreeTDS" - else: - driver = "SQL Server" - conn_str = ( - f"DRIVER={{{driver}}};SERVER={self.sql_server};" - f"DATABASE={self.sql_db};UID={self.sql_user};PWD={self.sql_pass};" - "Encrypt=no;TrustServerCertificate=yes;" + f"DRIVER={{SQL Server}};SERVER={self.sql_server};" + f"DATABASE={self.sql_db};UID={self.sql_user};PWD={self.sql_pass}" ) return pyodbc.connect(conn_str) @@ -565,6 +553,22 @@ class DataManager: return {"borrado": 1} # ── GOOGLE SHEET (CSV publicado): importe gastado por conjunto de anuncios ── + def traer_clasificacion_meta(self): + """Baja el REPLACE_META.json de GitHub (el MISMO que usan Ventas/Ocupabilidad/ + Rentabilidad) para clasificar sede y programa. Devuelve el dict o {} si falla.""" + # URL fija a la carpeta DASHBOARD (donde SI existe el REPLACE_META.json, + # el mismo que edita Ventas). No usa GITHUB_BASE del .env porque ese apunta + # a DASHBOARD_LEADS, que no tiene este archivo (daba 404). + url = os.getenv("META_JSON_URL", + "https://raw.githubusercontent.com/aron1798/prueba1/main/DASHBOARD/REPLACE_META.json") + try: + r = requests.get(url, timeout=15) + r.raise_for_status() + return r.json() + except Exception as e: + print(f"[traer_clasificacion_meta] {e}") + return {} + def traer_meta_importe(self): """Lee el CSV publicado de Meta_Ads_Adsets y devuelve una lista de dicts: [{conjunto, importe, inicio}, ...] (una por fila del CSV, inicio = 'Inicio del informe'). @@ -705,12 +709,17 @@ class DataManager: def traer_actividad_asesores(self): """Mensajes ENVIADOS por asesores reales (sender_id 15-25), en hora Peru (-5h). Sirve para detectar el turno de cada asesor cada dia por concentracion de - actividad. Devuelve [{user_id, created_at}]. Liviano: solo id + fecha.""" + actividad. Devuelve [{user_id, created_at, telefono}]. + Telefono via: messages.conversation_id -> conversations.contact_id -> + contacts.phone_number.""" sql = """ SELECT m.sender_id AS user_id, - (m.created_at - INTERVAL '5 hours') AS created_at + (m.created_at - INTERVAL '5 hours') AS created_at, + ct.phone_number AS telefono FROM messages m + LEFT JOIN conversations cv ON cv.id = m.conversation_id + LEFT JOIN contacts ct ON ct.id = cv.contact_id WHERE m.sender_id BETWEEN 15 AND 25 """ conn = self.pg_conn() @@ -1033,7 +1042,16 @@ class DataManager: WHERE sgede_cronograma_matricula.cod_localidad = sgeca_matricula.cod_localidad AND sgede_cronograma_matricula.num_matricula = sgeca_matricula.num_matricula AND sgede_cronograma_matricula.num_refinanciamiento = 1 - AND sgede_cronograma_matricula.num_cuota = 1), 0) AS imp_saldo_cuota1 + AND sgede_cronograma_matricula.num_cuota = 1), 0) AS imp_saldo_cuota1, + ISNULL((SELECT TOP 1 comp.imp_tc + FROM vtaca_comprobante comp + INNER JOIN sgevi_cuotas_x_comprobante cxc + ON comp.cod_localidad = cxc.cod_localidad + AND comp.num_correlativo = cxc.num_correlativo + WHERE cxc.num_matricula = sgeca_matricula.num_matricula + AND cxc.cod_localidad_c = sgeca_matricula.cod_localidad + AND comp.cod_estado NOT IN ('ANU', 'ELI') + ORDER BY comp.fch_emision DESC), 0) AS imp_tc FROM sgeca_matricula INNER JOIN sgeca_programa ON sgeca_matricula.cod_programa = sgeca_programa.cod_programa LEFT JOIN sgede_RP_programa ON sgeca_matricula.cod_periodo = sgede_RP_programa.cod_detalle