first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
#from .db_utils import *
|
||||
#from .report_utils import *
|
||||
#from .sharepoint_utils import *
|
||||
#from .ftp_utils import *
|
||||
#from .sftp_utils import *
|
||||
@@ -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.
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user