first commit
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import pytz
|
||||
from datetime import datetime, timedelta, time
|
||||
|
||||
class ConfigDatabase:
|
||||
db_host= 'sql.glmanalytics.com'
|
||||
db_user = 'vmglm001'
|
||||
db_password = "A16n)J@^fj<]G('{"
|
||||
|
||||
class ConfigFTP:
|
||||
ftp_server = '000oqta.rcomhost.com'
|
||||
ftp_user = "ftp3592287"
|
||||
ftp_password = 'mpFFMRW4W9w@SuK'
|
||||
ftp_port = 21
|
||||
ftp_path = "/www/TranferData_APP_PYTHON/"
|
||||
|
||||
|
||||
class ConfigAPI:
|
||||
pass
|
||||
|
||||
class GeneralConfig:
|
||||
|
||||
#DataControl
|
||||
numDays = 3
|
||||
zoneTime = pytz.timezone("America/El_Salvador")
|
||||
zoneTimeCP = pytz.timezone("America/Santo_Domingo")
|
||||
cutTime = time(17, 30, 00)
|
||||
current_time = datetime.now(zoneTime)
|
||||
current_hour = current_time.hour
|
||||
nowTime = current_time.strftime("%Y-%m-%d")
|
||||
nowTimeFormat = current_time.strftime("%Y%m%d")
|
||||
execution_time = datetime.now(zoneTime)
|
||||
|
||||
|
||||
#time = datetime.now(zoneTime)
|
||||
|
||||
#Para emails
|
||||
fromEmail = 'notifications.noreply@gomezleemarketing.com'
|
||||
smtp_server="smtp.gmail.com"
|
||||
smtp_port=587
|
||||
passwordEmail = 'GLM123456new1'
|
||||
|
||||
|
||||
|
||||
class ConfigNotifications:
|
||||
webhookURL_wps_notifcation = "https://automation.glmanalytics.com/webhook/wsp-notifcation"
|
||||
|
||||
webhookURL = "https://automation.glmanalytics.com/webhook/205ae0d5-fc9c-44da-951c-84c033236579" # Ajusta con tu URL real
|
||||
@@ -0,0 +1,9 @@
|
||||
#from .GeneralConfig import *
|
||||
#from .corripioConfig import *
|
||||
#from .corripioConfig2 import *
|
||||
#from .dipoConfig import *
|
||||
#from .pgConfig import *
|
||||
#from .feduroConfig import *
|
||||
#from .cimbertonConfig import *
|
||||
#from .echamorroConfig import *
|
||||
#from .ftp_service import *
|
||||
@@ -0,0 +1,42 @@
|
||||
import os
|
||||
|
||||
class FeduroConfig:
|
||||
userSharepoint = 'feddatadeployment.im@pg.com' #os.getenv('sharepointPG_user')
|
||||
passwordSharepoint = 'Feduro_PG_2025$' #= os.getenv('sharepointPG_password')
|
||||
siteSharepoint = 'https://pgone.sharepoint.com/sites/feduroinfinitystores' #os.getenv('sharepointPG_site')
|
||||
database = 'IS001'
|
||||
databaseID = '00010007'
|
||||
areaID = '0007'
|
||||
project_name = 'FEDURO'
|
||||
classID = '0003'
|
||||
|
||||
|
||||
reportsID = {'Cobertura':'0001',
|
||||
'NegociadasIS001':'0002',
|
||||
'Promociones':'0003',
|
||||
#'Precios':'0004',
|
||||
'Fmot':'0005',
|
||||
'QualityDisplay':'0006',
|
||||
'Checkout':'0007',
|
||||
'QualityShelf':'0008',
|
||||
'SOSFeduro':'0009',
|
||||
#'OsaTorre':'0010',
|
||||
#'OsaPsmt':'0011',
|
||||
#'OsaFarmacia':'0012',
|
||||
'OsaFotografico':'0013',
|
||||
#'DataAvailability':'0014',
|
||||
'OsaAnalogo':'0015',
|
||||
'ModuloTareas':'0016',
|
||||
#'SOSAPDO_IS001':'0017',
|
||||
#'SOSBabyCare_IS001':'0018',
|
||||
#'SOSFabricCare_IS001':'0019',
|
||||
#'SOSFamilyCare_IS001':'0020',
|
||||
#'SOSFemCare_IS001':'0012',
|
||||
#'SOSHairCare_IS001':'0022',
|
||||
#'SOSHomeCare_IS001':'0023',
|
||||
#'SOSOralCare_IS001':'0024',
|
||||
#'SOSPHC_IS001':'0025'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import os
|
||||
|
||||
from App.Config.GeneralConfig import GeneralConfig
|
||||
from App.Config.GeneralConfig import ConfigFTP
|
||||
from App.Utils.db_utils import DatabaseManager
|
||||
from App.Utils.ftp_utils import FTPManager
|
||||
from App.Utils.sftp_utils import SFTPManager
|
||||
|
||||
|
||||
|
||||
class FeduroConfig:
|
||||
|
||||
def __init__(self):
|
||||
#Inicializando la base de datos
|
||||
self.databaseCode = 'IS001'
|
||||
self.databaseID = '00010007'
|
||||
|
||||
self.database = DatabaseManager(self.databaseCode, self.databaseID)
|
||||
|
||||
#Inicialzando el FTP
|
||||
self.ftpdatabase = FTPManager(ConfigFTP.ftp_server, ConfigFTP.ftp_user, ConfigFTP.ftp_password, ConfigFTP.ftp_port)
|
||||
|
||||
#Inicializando el SFTP
|
||||
self.sftp_host = 'c6a3nxpth6mf4.eastus2.azurecontainer.io'
|
||||
self.sftp_port = 22
|
||||
self.sftp_username = 'eLeader01'
|
||||
self.sftp_password = 'c8gNBhJE@zt$ZkT&'
|
||||
|
||||
self.sftpdatabase = SFTPManager(self.sftp_host, self.sftp_port, self.sftp_username, self.sftp_password)
|
||||
|
||||
|
||||
#Inicializando SharePoint
|
||||
|
||||
self.userSharepoint = 'feddatadeployment.im@pg.com' #os.getenv('sharepointPG_user')
|
||||
self.passwordSharepoint = 'Feduro_PG_2025$' #= os.getenv('sharepointPG_password')
|
||||
self.siteSharepoint = 'https://pgone.sharepoint.com/sites/feduroinfinitystores' #os.getenv('sharepointPG_site')
|
||||
|
||||
|
||||
#General configuration feduro
|
||||
self.areaID = '0007'
|
||||
self.project_name = 'FEDURO'
|
||||
self.classID = '0003'
|
||||
|
||||
|
||||
self.reports = self.database.get_execute_query(f"select * from TransferData where active = 1")
|
||||
|
||||
|
||||
|
||||
|
||||
self.reports_sftp = {'Cobertura':'/upload/FEDURO/DataCollection/Cobertura/CoberturaCheckIn-Out/',
|
||||
'NegociadasIS001':'/upload/FEDURO/DataCollection/Negociadas/AC Ejecutado/',
|
||||
'Promociones':'/upload/FEDURO/DataCollection/Promociones/PROMO EJECUTADA/',
|
||||
'Precios':'',
|
||||
'Fmot':'/upload/FEDURO/DataCollection/Fmot/fmot Data/',
|
||||
'QualityDisplay':'/upload/FEDURO/DataCollection/Quality Display/',
|
||||
'Checkout':'/upload/FEDURO/DataCollection/CheckOut/Data/',
|
||||
'QualityShelf':'/upload/FEDURO/DataCollection/QualityShelf/QualityShelf_Data/',
|
||||
'SOSFeduro':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'OsaTorre':'',
|
||||
'OsaPsmt':'',
|
||||
'OsaFarmacia':'',
|
||||
'OsaFotografico':'/upload/FEDURO/DataCollection/OSA/OSA_Fotos_Categoria/',
|
||||
'DataAvailability':'',
|
||||
'OsaAnalogo': '/upload/FEDURO/DataCollection/OSA/Data/',
|
||||
'ModuloTareas': '/upload/FEDURO/DataCollection/ModuloTareas/Data/',
|
||||
'SOSAPDO_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSBabyCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSFabricCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSFamilyCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSFemCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSHairCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSHomeCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSOralCare_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/',
|
||||
'SOSPHC_IS001':'/upload/FEDURO/DataCollection/SOS/Share of Share Ejecutado/'
|
||||
}
|
||||
|
||||
|
||||
|
||||
self.reportsID = {'Cobertura':'0001',
|
||||
'NegociadasIS001':'0002',
|
||||
'Promociones':'0003',
|
||||
#'Precios':'0004',
|
||||
'Fmot':'0005',
|
||||
'QualityDisplay':'0006',
|
||||
'Checkout':'0007',
|
||||
'QualityShelf':'0008',
|
||||
'SOSFeduro':'0009',
|
||||
#'OsaTorre':'0010',
|
||||
#'OsaPsmt':'0011',
|
||||
#'OsaFarmacia':'0012',
|
||||
'OsaFotografico':'0013',
|
||||
#'DataAvailability':'0014',
|
||||
'OsaAnalogo':'0015',
|
||||
'ModuloTareas':'0016',
|
||||
#'SOSAPDO_IS001':'0017',
|
||||
#'SOSBabyCare_IS001':'0018',
|
||||
#'SOSFabricCare_IS001':'0019',
|
||||
#'SOSFamilyCare_IS001':'0020',
|
||||
#'SOSFemCare_IS001':'0012',
|
||||
#'SOSHairCare_IS001':'0022',
|
||||
#'SOSHomeCare_IS001':'0023',
|
||||
#'SOSOralCare_IS001':'0024',
|
||||
#'SOSPHC_IS001':'0025'
|
||||
}
|
||||
|
||||
self.steps = {
|
||||
"sftp": True,
|
||||
"ftp": True,
|
||||
"sharepoint_python": True,
|
||||
"sharepoint_N8N": False,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import os
|
||||
|
||||
from App.Config.GeneralConfig import GeneralConfig
|
||||
from App.Config.GeneralConfig import ConfigFTP
|
||||
from App.Utils.db_utils import DatabaseManager
|
||||
from App.Utils.ftp_utils import FTPManager
|
||||
from App.Utils.sftp_utils import SFTPManager
|
||||
from App.Utils.sharepoint_utils import SharepointManager
|
||||
|
||||
|
||||
|
||||
class PGConfig:
|
||||
def __init__(self):
|
||||
#Inicializando la base de datos
|
||||
self.databaseCode = 'glm_pg'
|
||||
self.databaseID = '00080001'
|
||||
|
||||
self.database = DatabaseManager(self.databaseCode, self.databaseID)
|
||||
|
||||
#Inicializando SharePoint
|
||||
self.userSharepoint = 'gldatadeployment.im@pg.com'
|
||||
self.passwordSharepoint = 'Glm-infinity-2025'
|
||||
self.siteSharepoint = 'https://pgone.sharepoint.com/sites/eLeaderDataSharing'
|
||||
|
||||
data = self.database.get_execute_query("select reportID, parameter from TransferDataParameter where category = 'Sharepoint'")
|
||||
|
||||
self.sharepointPath = {
|
||||
row['reportID']: row['parameter']
|
||||
for row in data
|
||||
}
|
||||
|
||||
|
||||
self.sharepoint = SharepointManager(self.siteSharepoint, self.userSharepoint, self.passwordSharepoint)
|
||||
|
||||
#Inicialzando el FTP
|
||||
self.ftpdatabase = FTPManager(ConfigFTP.ftp_server, ConfigFTP.ftp_user, ConfigFTP.ftp_password, ConfigFTP.ftp_port)
|
||||
|
||||
#Inicializando el SFTP
|
||||
#self.sftp_host = 'c6a3nxpth6mf4.eastus2.azurecontainer.io'
|
||||
#self.sftp_port = 22
|
||||
#self.sftp_username = 'eLeader01'
|
||||
#self.sftp_password = 'c8gNBhJE@zt$ZkT&'
|
||||
|
||||
#self.sftpdatabase = SFTPManager(self.sftp_host, self.sftp_port, self.sftp_username, self.sftp_password)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#General configuration PG
|
||||
self.areaID = '0001'
|
||||
self.project_name = 'PG'
|
||||
self.classID = '0003'
|
||||
|
||||
self.reports = self.database.get_execute_query(f"select * from TransferData where active = 1")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self.reportsID = {'Cobertura':'0001',
|
||||
'Negociadas':'0002',
|
||||
'Promociones':'0003',
|
||||
#'Precios':'0004',
|
||||
#'Fmot':'0005',
|
||||
'QualityDisplay':'0006',
|
||||
'Checkout':'0007',
|
||||
'QualityShelf':'0008',
|
||||
'SOS':'0009',
|
||||
#'OsaTorre':'0010',
|
||||
#'OsaPsmt':'0011',
|
||||
'OsaFarmacia':'0012',
|
||||
'OsaMassy':'0013',
|
||||
'OsaPenny':'0014',
|
||||
'OsaFotografico':'0015',
|
||||
'DataAvailability':'0016'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
APP_NAME = "PYTHON_TRANSFERDATA_APP"
|
||||
VERSION = "2.0.0"
|
||||
AUTHOR = "eurbina"
|
||||
@@ -0,0 +1,2 @@
|
||||
# Modelos de datos (ej. esquemas de tablas si usas ORM)
|
||||
# Por ahora vacío; agrega clases si usas SQLAlchemy
|
||||
@@ -0,0 +1 @@
|
||||
#from .etl_service import *
|
||||
@@ -0,0 +1,23 @@
|
||||
# Servicio API: endpoints GET para descarga
|
||||
from flask import Flask, request, jsonify
|
||||
from App.Services.logic_service import run_etl # O importa db_utils directamente
|
||||
import pandas as pd
|
||||
import logging
|
||||
|
||||
app = Flask(__name__)
|
||||
logging.basicConfig(filename='Logs/api.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
@app.route('/api/download', methods=['GET'])
|
||||
def download_data():
|
||||
try:
|
||||
project = request.args.get('project')
|
||||
task = request.args.get('task')
|
||||
# Lógica: query DB y devolver JSON/CSV
|
||||
conn = get_db_connection() # Importa de Utils
|
||||
query = f"SELECT * FROM {task} WHERE project = '{project}'"
|
||||
df = pd.read_sql(query, conn)
|
||||
conn.close()
|
||||
return jsonify(df.to_dict(orient='records'))
|
||||
except Exception as e:
|
||||
logging.error(f"API falló: {str(e)}")
|
||||
return jsonify({"error": "Error interno del servidor"}), 500 # Devuelve error, pero Flask sigue vivo
|
||||
@@ -0,0 +1,339 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
from typeguard import config
|
||||
|
||||
# subir 2 niveles hasta la raíz (donde está App)
|
||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
|
||||
|
||||
from App.Utils.db_utils import DatabaseManager # Reutilizado de db_utils.py
|
||||
from App.Config.GeneralConfig import GeneralConfig # Reutilizado de GeneralConfig.py
|
||||
from App.Config.pgConfig import PGConfig # Reutilizado de PGConfig.py
|
||||
from App.Config.version import APP_NAME, VERSION, AUTHOR # Reutilizado de version.py
|
||||
from App.Utils.report_utils import ReportGenerator # Nueva utilidad para generar reportes (ej. Excel)
|
||||
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# Configura logging
|
||||
logging.basicConfig(filename='Logs/etl.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
|
||||
|
||||
|
||||
def run_etl(config, data_report, target_date_report):
|
||||
'''Función principal para ejecutar ETL.
|
||||
Lee los datos desde la base de datos, los transforma en un excel y los sube a SharePoint.
|
||||
Args:
|
||||
config: Configuración general de la aplicación.
|
||||
database: Instancia de la clase DatabaseManager.
|
||||
data_report: Diccionario con la información del reporte a generar.
|
||||
target_date_report: Fecha objetivo para filtrar datos (str, formato 'YYYY-MM-DD')
|
||||
Returns:
|
||||
Tuple[bool, str]: Una tupla con el resultado de la operación y un mensaje informativo.
|
||||
'''
|
||||
date_execution = datetime.now(GeneralConfig.zoneTime)
|
||||
target_date_report_format = target_date_report.strftime('%Y-%m-%d')
|
||||
|
||||
result = {
|
||||
"errors": []
|
||||
} # Lista para almacenar errores y mensajes informativos
|
||||
################################## INICIANDO REPORTE ##################################
|
||||
config.database.log_execution(procedure_id=str(config.database.db_databaseID)+str('0003')+str('0189')+str(data_report['reportID']),
|
||||
procedure_name='Trasmision Datos '+str(data_report['project_name'])+' '+str(data_report['report_name']),
|
||||
status='STARTING',
|
||||
message='Inicializando la trasmision de reporte: '+str(data_report['report_name']),
|
||||
executed_by=str(config.database.db_user),
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=str(date_execution))
|
||||
|
||||
|
||||
################################ OBTENIENDO DATOS DE LA BASE DE DATOS ##################################
|
||||
try:
|
||||
query = f"SELECT * FROM {data_report['Table_Name']} WHERE Fecha = '{target_date_report_format}'"
|
||||
print(query)
|
||||
data_sql_report = config.database.get_execute_query(query)
|
||||
|
||||
if not data_sql_report:
|
||||
print(f"No se encontraron datos para el reporte {data_report['report_name']} en la fecha {target_date_report_format}")
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'WARNING',
|
||||
message=f"No se encontraron datos para el reporte {data_report['report_name']} en la fecha {target_date_report_format}",
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
return {
|
||||
"status": "WARNING",
|
||||
"report": data_report["report_name"],
|
||||
"message": "Sin datos en la base de datos"
|
||||
}
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'SUCCESS',
|
||||
message=f'Lectura de la base datos para crear reporte :{data_report["report_name"]}',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'FAILED',
|
||||
message=f'Error en la lectura de la base datos para crear reporte :{data_report["report_name"]}',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=str(e),
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
return {
|
||||
"status": "FAILED",
|
||||
"report": data_report["report_name"],
|
||||
"message": f"Error lectura BD {str(e)}"
|
||||
}
|
||||
|
||||
|
||||
################################ CREANDO REPORTE EN EXCEL ##################################
|
||||
|
||||
try:
|
||||
doc_report = ReportGenerator(target_date_report,**data_report)
|
||||
doc_report.createReportData(data_sql_report)
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'SUCCESS',
|
||||
message=f'Creación del reporte :{data_report["report_name"] } en formato Excel',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
|
||||
except Exception as e:
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'FAILED',
|
||||
message=f'Error en la creación del reporte :{data_report["report_name"] } en formato Excel',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=str(e),
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
return {
|
||||
"status": "FAILED",
|
||||
"report": data_report["report_name"],
|
||||
"message": f"Error creación del reporte {str(e)}"
|
||||
}
|
||||
|
||||
|
||||
################################ SUBIR REPORTE A FTP ###############################
|
||||
if data_report.get('FTP', False):
|
||||
try:
|
||||
ftp_path = f"/www/TranferData_APP_PYTHON/{config.project_name}/{data_report['report_name']}/"
|
||||
#print(ftp_path)
|
||||
config.ftpdatabase.uploadFile(ftp_path, doc_report.docsend_Path) # Asegúrate de que la configuración FTP esté presente en el config general
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'SUCCESS',
|
||||
message=f'Subida del reporte :{data_report["report_name"] } a FTP',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
except Exception as e:
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'FAILED',
|
||||
message=f'Error en la subida del reporte :{data_report["report_name"] } a FTP',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=str(e),
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
result["errors"].append(f"Error subir reporte a FTP {str(e)}")
|
||||
|
||||
|
||||
#return {
|
||||
# "status": "FAILED",
|
||||
# "report": data_report["report_name"],
|
||||
# "message": f"Error subir reporte a FTP {str(e)}"
|
||||
#}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
############################### SUBIR REPORTA A SFTP ##################################
|
||||
if data_report.get('SFTP', False):
|
||||
|
||||
try:
|
||||
# Lógica para subir a SFTP (puedes usar paramiko o alguna librería similar)
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'SUCCESS',
|
||||
message=f'Subida del reporte :{data_report["report_name"] } a SFTP',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
except Exception as e:
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'FAILED',
|
||||
message=f'Error en la subida del reporte :{data_report["report_name"] } a SFTP',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=str(e),
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
result["errors"].append(f"Error subir reporte a SFTP {str(e)}")
|
||||
#return {
|
||||
# "status": "FAILED",
|
||||
# "report": data_report["report_name"],
|
||||
# "message": f"Error subir reporte a SFTP {str(e)}"
|
||||
#}
|
||||
|
||||
|
||||
|
||||
|
||||
################################ SUBIR REPORTE A SHAREPOINT PYTHON ##################################
|
||||
if data_report.get('SharePoint', False):
|
||||
|
||||
try:
|
||||
sharepoint_folder = config.sharepointPath.get(data_report['reportID'])
|
||||
|
||||
if not sharepoint_folder:
|
||||
raise Exception(f"No existe ruta SharePoint para reportID {data_report['reportID']}")
|
||||
|
||||
config.sharepoint.uploadFile(doc_report, sharepoint_folder)
|
||||
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'SUCCESS',
|
||||
message=f'Subida del reporte :{data_report["report_name"] } a SharePoint usando Python',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
except Exception as e:
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'FAILED',
|
||||
message=f'Error en la subida del reporte :{data_report["report_name"] } a SharePoint usando Python',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=str(e),
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
result["errors"].append(f"Error subir reporte a SharePoint usando Python {str(e)}" )
|
||||
#return {
|
||||
# "status": "FAILED",
|
||||
# "report": data_report["report_name"],
|
||||
# "message": f"Error subir reporte a SharePoint usando Python {str(e)}"
|
||||
#}
|
||||
|
||||
|
||||
|
||||
################################ SUBIR REPORTE A SHAREPOINT N8N ##################################
|
||||
|
||||
if data_report.get('N8N', False):
|
||||
try:
|
||||
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'SUCCESS',
|
||||
message=f'Subida del reporte :{data_report["report_name"] } a SharePoint usando N8N',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=None,
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
except Exception as e:
|
||||
config.database.log_execution(procedure_id=f'{config.database.db_databaseID}00030189{data_report["reportID"]}',
|
||||
procedure_name=f'Trasmision Datos {data_report["project_name"]} {data_report["report_name"]}',
|
||||
status=f'FAILED',
|
||||
message=f'Error en la subida del reporte :{data_report["report_name"] } a SharePoint usando N8N',
|
||||
executed_by=f'{config.database.db_user}',
|
||||
source= f'{APP_NAME}_{VERSION}',
|
||||
extra_info=str(e),
|
||||
target_date=target_date_report_format,
|
||||
date=f'{date_execution}')
|
||||
|
||||
result["errors"].append(f"Error subir reporte a SharePoint usando N8N {str(e)}")
|
||||
#return {
|
||||
# "status": "FAILED",
|
||||
# "report": data_report["report_name"],
|
||||
# "message": f"Error subir reporte a SharePoint usando N8N {str(e)}"
|
||||
#}
|
||||
|
||||
|
||||
|
||||
#################################################################################################
|
||||
if result["errors"]:
|
||||
return {
|
||||
"status": "COMPLETED_WITH_ERRORS",
|
||||
"report": data_report["report_name"],
|
||||
"message": " | ".join(result["errors"])
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "SUCCESS",
|
||||
"report": data_report["report_name"],
|
||||
"message": "ETL ejecutado exitosamente"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Aquí va la lógica de ETL específica para cada proyecto y reporte
|
||||
# Por simplicidad, loggea que se ejecutó
|
||||
#logging.info(f"ETL ejecutado para proyecto: {project}, reporte: {report_name}, usando config: {config.project_name}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
pg_config = PGConfig()
|
||||
database = DatabaseManager(pg_config.database, pg_config.databaseID)
|
||||
|
||||
data_report = {
|
||||
'project_name': 'PG',
|
||||
'report_name': 'Cobertura',
|
||||
'Table_Name': 'ReportCobertura',
|
||||
'nowTime': GeneralConfig.nowTime,
|
||||
'nowTimeFormat': GeneralConfig.nowTimeFormat,
|
||||
'project_name': pg_config.project_name,
|
||||
'current_time': GeneralConfig.execution_time,
|
||||
'reportID': '0001'
|
||||
}
|
||||
|
||||
run_etl(pg_config, database, data_report, target_date_report='2026-03-26')"""
|
||||
@@ -0,0 +1,13 @@
|
||||
# Servicio UI: manejo de forms web
|
||||
from flask import render_template, request
|
||||
from App.Services.logic_service import run_etl
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
if request.method == 'POST':
|
||||
project = request.form['project']
|
||||
task = request.form['task']
|
||||
# Trigger ETL manual
|
||||
run_etl(project, task)
|
||||
return "ETL ejecutado"
|
||||
return render_template('index.html')
|
||||
@@ -0,0 +1,3 @@
|
||||
/* Estilos básicos para UI */
|
||||
body { font-family: Arial; }
|
||||
form { max-width: 400px; }
|
||||
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>DataTransfer UI</title></head>
|
||||
<body>
|
||||
<h1>Actualizar Datos</h1>
|
||||
<form method="POST">
|
||||
Proyecto: <select name="project"><option>Feduro</option><option>Comberton</option></select><br>
|
||||
Tarea: <select name="task"><option>negociadas</option><option>promociones</option></select><br>
|
||||
Fecha inicio: <input type="date" name="start_date"><br>
|
||||
Fecha fin: <input type="date" name="end_date"><br>
|
||||
<button type="submit">Actualizar</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,111 @@
|
||||
SELECT TOP (1000) [ID]
|
||||
,[ExecutionDate]
|
||||
,[ReportName]
|
||||
,[ReportID]
|
||||
,[Active]
|
||||
,[TableName]
|
||||
FROM [glm_pg].[dbo].[TransferDataParameters]
|
||||
|
||||
CREATE TABLE TransferData (
|
||||
ID INT IDENTITY(1,1) PRIMARY KEY,
|
||||
ExportDate DATETIME DEFAULT GETDATE(),
|
||||
|
||||
ReportID AS RIGHT('0000' + CAST(ID AS VARCHAR), 4) PERSISTED, -- 👈 automático
|
||||
|
||||
ReportName VARCHAR(100) NOT NULL,
|
||||
TableName VARCHAR(200) NOT NULL, -- 👈 AQUÍ lo importante
|
||||
|
||||
Active BIT NOT NULL DEFAULT 1,
|
||||
FTP BIT NOT NULL DEFAULT 1,
|
||||
FTPS BIT NOT NULL DEFAULT 1,
|
||||
SharePoint BIT NOT NULL DEFAULT 1,
|
||||
N8N BIT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
|
||||
INSERT INTO TransferData (ReportName, TableName, Active, FTP, FTPS, SharePoint, N8N)
|
||||
VALUES
|
||||
-- 0001
|
||||
('Cobertura','ReportCobertura',1,1,1,1,0),
|
||||
|
||||
-- 0002
|
||||
('NegociadasIS001','IS001.dbo.AcuerdosComerciales_Report',1,1,1,1,0),
|
||||
|
||||
-- 0003
|
||||
('Promociones','PromocionesAC_Report',1,1,1,1,0),
|
||||
|
||||
-- 0004 (desactivado)
|
||||
('Precios','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0005
|
||||
('Fmot','Fmot_Report',1,1,1,1,0),
|
||||
|
||||
-- 0006
|
||||
('QualityDisplay','dbo.QualityDisplay_Report',1,1,1,1,0),
|
||||
|
||||
-- 0007
|
||||
('Checkout','IS001.dbo.Checkout_Report',1,1,1,1,0),
|
||||
|
||||
-- 0008
|
||||
('QualityShelf','QualityShelf_Report',1,1,1,1,0),
|
||||
|
||||
-- 0009
|
||||
('SOSFeduro','View_Medicion_SOS_PG',1,1,1,1,0),
|
||||
|
||||
-- 0010 (desactivado)
|
||||
('OsaTorre','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0011 (desactivado)
|
||||
('OsaPsmt','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0012 (desactivado)
|
||||
('OsaFarmacia','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0013
|
||||
('OsaFotografico','OsaManualFotos_Report',1,1,1,1,0),
|
||||
|
||||
-- 0014 (desactivado)
|
||||
('DataAvailability','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0015
|
||||
('OsaAnalogo','OsaManualAnalogo_Report',1,1,1,1,0),
|
||||
|
||||
-- 0016
|
||||
('ModuloTareas','ModuloTareas_Report',1,1,1,1,0),
|
||||
|
||||
-- 0017 (desactivado)
|
||||
('SOSAPDO_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0018 (desactivado)
|
||||
('SOSBabyCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0019 (desactivado)
|
||||
('SOSFabricCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0020 (desactivado)
|
||||
('SOSFamilyCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0021 (desactivado)
|
||||
('SOSFemCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0022 (desactivado)
|
||||
('SOSHairCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0023 (desactivado)
|
||||
('SOSHomeCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0024 (desactivado)
|
||||
('SOSOralCare_IS001','s/d',0,1,1,1,0),
|
||||
|
||||
-- 0025 (desactivado)
|
||||
('SOSPHC_IS001','s/d',0,1,1,1,0);
|
||||
|
||||
select * from TransferData
|
||||
|
||||
select * from TransferData where active = 1
|
||||
|
||||
UPDATE TransferData
|
||||
SET Active = 0
|
||||
WHERE ReportID = '0005'
|
||||
|
||||
--drop table TransferData
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .Utils import *
|
||||
from .Config import *
|
||||
from .Services import *
|
||||
Reference in New Issue
Block a user