first commit

This commit is contained in:
Eidan T.
2026-05-09 14:00:04 -04:00
commit e0ec11977c
42 changed files with 4596 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
#from .db_utils import *
#from .report_utils import *
#from .sharepoint_utils import *
#from .ftp_utils import *
#from .sftp_utils import *
+134
View File
@@ -0,0 +1,134 @@
# Utilidades para conexión a BD - Reutilizado y ajustado de database.py v1
import pyodbc
from App.Config.GeneralConfig import ConfigDatabase # Ajusta si GeneralConfig tiene otra estructura
class DatabaseManager:
def __init__(self, database, databaseID):
self.db_database = database
self.db_user = ConfigDatabase.db_user
self.db_password = ConfigDatabase.db_password
self.db_host = ConfigDatabase.db_host
self.db_databaseID = databaseID
def _connect(self):
try:
self.conn_str = (
'DRIVER={ODBC Driver 18 for SQL Server};'
f'SERVER={self.db_host};'
f'DATABASE={self.db_database};'
f'UID={self.db_user};'
f'PWD={self.db_password};'
f'Encrypt=yes;'
f'TrustServerCertificate=yes;'
)
#print(self.conn_str)
self.connection = pyodbc.connect(self.conn_str)
except pyodbc.Error as e:
print(f"Error al conectar a la base de datos: {e}")
self.connection = None
def _disconnect(self):
if self.connection:
self.connection.close()
# Función para ejecutar queries que retornan valores
def get_execute_query(self, query):
self._connect()
if not self.connection:
return None, "Error de conexión"
self.cursor = self.connection.cursor()
self.cursor.execute(query)
columns = [col[0] for col in self.cursor.description]
data = self.cursor.fetchall()
results = [dict(zip(columns, row)) for row in data]
self.cursor.close()
self._disconnect()
return results
# Función para ejecutar queries que retornan valores
def get_execute_query2(self, query):
self._connect()
if not self.connection:
return None, "Error de conexión"
try:
self.cursor = self.connection.cursor()
self.cursor.execute(query)
columns = [col[0] for col in self.cursor.description]
data = self.cursor.fetchall()
results = [dict(zip(columns, row)) for row in data]
self.cursor.close()
return results, None
except Exception as e:
print(f"Error al ejecutar la consulta SELECT: {e} \n {query}")
return None, str(e)
finally:
self._disconnect()
# Función para ejecutar queries que no retornan valores
def set_execute_query(self, query):
self._connect()
if not self.connection:
return False
try:
cursor = self.connection.cursor()
cursor.execute(query)
self.connection.commit()
cursor.close()
return True
except Exception as e:
print(f"Error al ejecutar la consulta de modificación: {e}")
return False
finally:
self._disconnect()
def log_execution(self, procedure_id, procedure_name, status, message, executed_by, extra_info, target_date, date,
procedure_type='PYTHON_APP', source='PYTHON_TRANSFERDATA_APP'):
self._connect()
if not self.connection:
return
self.cursor = self.connection.cursor()
sql = """
DECLARE @ExecID VARCHAR(50), @FieldID VARCHAR(50);
EXEC dbo.InsertExecutionLog
@ProcedureID = ?,
@ProcedureName = ?,
@ProcedureType = ?,
@Date = ?,
@TargetDate = ?,
@Status = ?,
@ExecutedBy = ?,
@MessageDescription = ?,
@Source = ?,
@ExtraInfo = ?,
@ExecutionIDGenerated = @ExecID OUTPUT,
@FieldIDGenerated = @FieldID OUTPUT;
SELECT @ExecID AS ExecutionID, @FieldID AS FieldID;
"""
try:
self.cursor.execute(sql, (
procedure_id, procedure_name, procedure_type,
date, target_date, status,
executed_by, message, source, extra_info
))
row = self.cursor.fetchone()
self.connection.commit()
except Exception as e:
print(f"Error en log_execution: {e}")
finally:
self._disconnect()
# Función simple para compatibilidad (usa DatabaseManager internamente)
def get_db_connection(database='tu_db', databaseID='default'):
manager = DatabaseManager(database, databaseID)
manager._connect()
return manager.connection # Devuelve la conexión cruda para pandas, etc.
+133
View File
@@ -0,0 +1,133 @@
from ftplib import FTP
import os
import time
class FTPManager:
def __init__(self, siteFTP, userFTP, passwordFTP, postFTP):
self.server = siteFTP
self.user = userFTP
self.password = passwordFTP
self.port = postFTP # Puerto por defecto: 21
self.ftp_client = None
self.list_failed_uploads = []
#def __init__(self, **kwargs):
# self.server = kwargs.get('server')
# self.user = kwargs.get('user')
# self.password = kwargs.get('password')
# self.port = kwargs.get('port', 21) # Puerto por defecto: 21
# self.ftp_client = None
# self.list_failed_uploads = []
def connect(self):
"""Conecta al servidor FTP"""
try:
self.ftp_client = FTP()
self.ftp_client.connect(self.server, self.port)
self.ftp_client.login(self.user, self.password)
self.ftp_client.encoding = 'latin-1'
#print(f"Conexión exitosa al servidor FTP: {self.server}")
except Exception as e:
#print(f"Error al conectar al servidor FTP: {e}")
raise
def disconnect(self):
"""Cierra la conexión FTP"""
if self.ftp_client:
self.ftp_client.quit()
#print("Conexión cerrada con éxito")
def upload_photos(self, local_folder, remote_folder, batch_size=100):
"""
Sube archivos en lotes de 100. Si hay un error, omite el archivo y lo guarda para intentarlo al final.
"""
try:
self.ftp_client.cwd(remote_folder)
except:
# if not exit create folder
self.ftp_client.mkd(remote_folder)
self.ftp_client.cwd(remote_folder)
files = [f for f in os.listdir(local_folder) if os.path.isfile(os.path.join(local_folder, f))]
total_files = len(files)
print(f"Total de archivos a subir: {total_files}")
for i in range(0, total_files, batch_size):
batch = files[i:i+batch_size]
print(f"Subiendo lote {i//batch_size + 1}: {len(batch)} archivos")
for file in batch:
local_path = os.path.join(local_folder, file)
try:
with open(local_path, 'rb') as local_file:
self.ftp_client.storbinary(f"STOR {file}", local_file)
print(f"Subido: {file}")
except Exception as e:
print(f"Error al subir {file}: {e}")
self.list_failed_uploads.append(file)
time.sleep(2) # Pequeña pausa entre lotes para estabilidad
# Intentar subir nuevamente los fallidos
if self.list_failed_uploads:
print("Reintentando archivos fallidos...")
for file in self.list_failed_uploads:
local_path = os.path.join(local_folder, file)
try:
with open(local_path, 'rb') as local_file:
self.ftp_client.storbinary(f"STOR {file}", local_file)
print(f"Subido en reintento: {file}")
except Exception as e:
print(f"Fallo definitivo en {file}: {e}")
print("Proceso de subida finalizado.")
def uploadFile(self, remote_folder, local_file_path):
self.connect()
try:
self.ftp_client.cwd("/") # 👈 IMPORTANTE
self.ensure_remote_path(remote_folder)
with open(local_file_path, "rb") as f:
file_name = os.path.basename(local_file_path)
self.ftp_client.storbinary(f"STOR {file_name}", f)
finally:
self.disconnect()
def ensure_remote_path(self, remote_folder):
folders = remote_folder.strip("/").split("/")
# SIEMPRE empezar desde raíz
self.ftp_client.cwd("/")
for folder in folders:
if folder == "":
continue
try:
self.ftp_client.cwd(folder)
except:
self.ftp_client.mkd(folder)
#print(f"Creando/navegando carpeta: {folder}")
self.ftp_client.cwd(folder)
# Ejemplo de uso
if __name__ == "__main__":
config = {
"server": "glmservice.com",
"user": "ftpeleaderuser",
"password": "AdAw#BaAjwQdc4B",
"port": 21
}
ftp_manager = FTPManager(**config)
try:
ftp_manager.connect()
ftp_manager.upload_photos("C:\\Users\\emili\\Downloads\\sfotos", "/imglm/test/glmpg01/")
finally:
ftp_manager.disconnect()
+50
View File
@@ -0,0 +1,50 @@
import requests
from datetime import datetime, timedelta
from App.Config.GeneralConfig import ConfigNotifications
def send_whatsapp(message):
url = ConfigNotifications.webhookURL_wps_notifcation # URL del webhook de WhatsApp en WPS
payload = {
"message": message
}
requests.post(url, json=payload)
def build_message(project_name, summary, duration):
status = "FINALIZADO" if not summary["failed"] else "FINALIZADO CON ERRORES ❌"
msg = f"""PROYECTO: {project_name}
Fecha: {datetime.now().strftime('%Y-%m-%d')}
Estado: {status}
Resumen:
- Reportes procesados: {summary['total']}
- Exitosos: {summary['success']}
- Con errores: {len(summary['failed'])}
"""
if summary["failed"]:
msg += "❌ Errores críticos:\n"
for e in summary["failed"]:
msg += f"- {e}\n"
if summary["errors"]:
msg += "\n🟠 Errores no bloqueantes:\n"
for e in summary["errors"]:
msg += f"- {e}\n"
if summary["warnings"]:
msg += "\n⚠️ Advertencias:\n"
for w in summary["warnings"]:
msg += f"- {w}\n"
msg += f"\n🕐 Duración: {str(duration).split('.')[0]}"
return msg
+53
View File
@@ -0,0 +1,53 @@
# Utilidades para generación de reportes - Reutilizado y ajustado de report.py v1
import os
import pandas as pd
import logging
from datetime import datetime
# Configura logging
logging.basicConfig(filename='Logs/reports.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
class ReportGenerator:
"""Clase para generar y exportar reportes en Excel"""
def __init__(self, target_date_report, **kwargs ):
"""
Inicializa generador de reportes
Args:
name: Nombre del reporte
nowTime: Fecha actual (datetime)
nowTimeFormat: Formato de fecha (str)
project_name: Nombre del proyecto
current_time: Tiempo de ejecución (datetime)
reportID: ID del reporte (str)
"""
self.name = kwargs.get('report_name',None)
#self.date = kwargs.get('nowTime',None)
self.dateFormat = target_date_report.strftime("%Y%m%d")
self.project_name = kwargs.get('project_name',None)
#self.current_time = kwargs.get('current_time',None)
#self.reportID = kwargs.get('reportID',None)
self.actual_path = os.path.dirname(os.path.abspath(__file__))
#self.download_dir = os.path.join(self.actual_path, '..', '..','Reportes', str(self.project_name), str(self.name), 'download')
self.docsend_dir = os.path.join(self.actual_path, '..', '..','Reportes', str(self.project_name), str(self.name))
#os.makedirs(self.download_dir, exist_ok=True)
os.makedirs(self.docsend_dir, exist_ok=True)
#self.dowload_path = os.path.normpath(self.download_dir)
self.docsend_Path = os.path.normpath(os.path.join(self.docsend_dir, f"{self.name} - {self.dateFormat}.xlsx"))
self.report_name = f"{self.name} - {self.dateFormat}.xlsx"
print(self.report_name)
def createReportData(self,data_sql_report):
self.data_sql_report = data_sql_report
self.df = pd.DataFrame(self.data_sql_report)
self.df.to_excel(self.docsend_Path,index=False)
#return False
#print(self.docsend_Path)
+44
View File
@@ -0,0 +1,44 @@
import paramiko
import os
class SFTPManager():
def __init__(self, sftp_host, sftp_port, sftp_username, sftp_password):
self.sftp_host = sftp_host
self.sftp_username = sftp_username
self.sftp_password = sftp_password
self.sftp_port = sftp_port or 22 # Puerto por defecto: 22
self.ssh_client = None
self.sftp_client = None
def _SFTPConect(self):
self.ssh_client = paramiko.SSHClient()
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # Acepta automáticamente la clave del host
self.ssh_client.connect(self.sftp_host, self.sftp_port, self.sftp_username, self.sftp_password)
self.sftp_client = self.ssh_client.open_sftp()
def _SFTPDisconnect(self):
if self.sftp_client:
self.sftp_client.close()
#print('SFTP desconectado.')
if self.ssh_client:
self.ssh_client.close()
#print('SSH desconectado.')
def uploadFile(self, task, sftp_path):
self._SFTPConect()
file_name = os.path.basename(task.docsend_Path)
pathFTP = os.path.join(sftp_path, file_name)
self.sftp_client.put(task.docsend_Path, pathFTP)
#print(f'Archivo {task.docsendPath} subido exitosamente a {pathFTP}')
self._SFTPDisconnect()
+109
View File
@@ -0,0 +1,109 @@
import requests
import os
import msal
from office365.sharepoint.client_context import ClientContext
from office365.runtime.auth.user_credential import UserCredential
from office365.runtime.auth.client_credential import ClientCredential
class SharepointManager:
def __init__(self, site, userSharepoint, passwordSharepoint):#,client_id,tenant_id,client_secret):
self.site = site
self.userSharepoint = userSharepoint
self.passwordSharepoint = passwordSharepoint
#self.client_id = client_id
#self.tenant_id = tenant_id
#self.client_secret = client_secret
#print(self.client_secret)
def uploadFile2(self, task, sharepoint_path):
self._connect2()
file_name = os.path.basename(task.docsend_Path)
with open(task.docsend_Path, "rb") as file_content:
folder = self.ctx.web.get_folder_by_server_relative_url(sharepoint_path)
upload = folder.upload_file(file_name, file_content.read()) # solo prepara la query
self.ctx.execute_query() # ejecuta en el contexto
print(f"✅ Archivo {file_name} subido a {upload.serverRelativeUrl}")
self._disconnect()
def _connect2(self):
#"""Conexión a SharePoint con access_token"""
#token = self._get_token()
#self.ctx = ClientContext(self.site).with_access_token(token)
#self.ctx.load(self.ctx.web)
#self.ctx.execute_query()
self.ctx = ClientContext(self.site).with_credentials(ClientCredential(self.client_id, self.client_secret))
self.ctx.load(self.ctx.web)
self.ctx.execute_query()
def _connect(self):
# 👇 Nada de AuthenticationContext aquí
self.ctx = ClientContext(self.site).with_credentials(
UserCredential(self.userSharepoint, self.passwordSharepoint)
)
self.ctx.load(self.ctx.web)
self.ctx.execute_query()
def _disconnect(self):
self.ctx = None
def uploadFile(self, task, sharepoint_path):
self._connect()
file_name = os.path.basename(task.docsend_Path)
with open(task.docsend_Path, "rb") as file_content:
response = (
self.ctx.web.get_folder_by_server_relative_url(sharepoint_path)
.upload_file(file_name, file_content.read())
.execute_query()
)
print(f"Archivo {file_name} subido a {response.serverRelativeUrl}")
self._disconnect()
def uploadFile2(self, task, sharepoint_path):
"""Subida con MSAL (App Registration)"""
self._connect2()
file_name = os.path.basename(task.docsend_Path)
with open(task.docsend_Path, "rb") as file_content:
folder = self.ctx.web.get_folder_by_server_relative_url(sharepoint_path)
upload = folder.upload_file(file_name, file_content.read())
self.ctx.execute_query()
print(f"✅ Archivo {file_name} subido a {upload.serverRelativeUrl}")
self._disconnect()
def uploadFileTemp(self, task, data):
self._connect()
file_name = os.path.basename(task.docsend_Path)
with open(task.docsend_Path, "rb") as file_content:
response = (
self.ctx.web.get_folder_by_server_relative_url(data[6])
.upload_file(file_name, file_content.read())
.execute_query()
)
print(f"Archivo {file_name} subido a {response.serverRelativeUrl}")
self._disconnect()
def dropFilesFolder(self, data):
self._connect()
folder = (
self.ctx.web.get_folder_by_server_relative_url(data[6])
.expand(["Files"])
.get()
.execute_query()
)
for file in folder.files:
print(f"Eliminando archivo: {file.name}")
file.delete_object()
self.ctx.execute_query()
self._disconnect()