Feat Detecciones en Bobinas y Montos

This commit is contained in:
2025-11-10 15:36:25 -03:00
parent d040099b9a
commit 615cf282a1
3 changed files with 264 additions and 1 deletions

View File

@@ -30,7 +30,9 @@ DB_DATABASE = 'SistemaGestion'
CONNECTION_STRING = f'DRIVER={{ODBC Driver 18 for SQL Server}};SERVER={DB_SERVER};DATABASE={DB_DATABASE};Trusted_Connection=yes;TrustServerCertificate=yes;'
MODEL_INDIVIDUAL_FILE = 'modelo_anomalias.joblib'
MODEL_SISTEMA_FILE = 'modelo_sistema_anomalias.joblib'
MODEL_DIST_FILE = 'modelo_anomalias_dist.joblib' # << NUEVO: Nombre del modelo de distribuidores
MODEL_DIST_FILE = 'modelo_anomalias_dist.joblib'
MODEL_DANADAS_FILE = 'modelo_danadas.joblib'
MODEL_MONTOS_FILE = 'modelo_montos.joblib'
# --- 2. Determinar Fecha ---
if len(sys.argv) > 1:
@@ -182,6 +184,108 @@ else:
else:
print("INFO: No hay datos de distribuidores para analizar en la fecha seleccionada.")
# --- FASE 4: Detección de Anomalías en Bobinas Dañadas ---
print("\n--- FASE 4: Detección de Anomalías en Bobinas Dañadas ---")
if not os.path.exists(MODEL_DANADAS_FILE):
print(f"ADVERTENCIA: Modelo de bobinas dañadas '{MODEL_DANADAS_FILE}' no encontrado.")
else:
model_danadas = joblib.load(MODEL_DANADAS_FILE)
query_danadas = f"""
SELECT
h.Id_Planta as id_planta,
p.Nombre as nombre_planta,
DATEPART(weekday, h.FechaMod) as dia_semana,
COUNT(DISTINCT h.Id_Bobina) as cantidad_danadas
FROM
bob_StockBobinas_H h
JOIN
bob_dtPlantas p ON h.Id_Planta = p.Id_Planta
WHERE
h.Id_EstadoBobina = 3 -- Asumiendo ID 3 para 'Dañada'
AND h.TipoMod = 'Estado: Dañada'
AND CAST(h.FechaMod AS DATE) = '{target_date.strftime('%Y-%m-%d')}'
GROUP BY
h.Id_Planta, p.Nombre, DATEPART(weekday, h.FechaMod)
"""
df_danadas_new = pd.read_sql(query_danadas, cnxn)
if not df_danadas_new.empty:
for index, row in df_danadas_new.iterrows():
features_danadas = ['id_planta', 'dia_semana', 'cantidad_danadas']
X_danadas_new = row[features_danadas].to_frame().T
prediction = model_danadas.predict(X_danadas_new)
if prediction[0] == -1:
mensaje = f"Se registraron {row['cantidad_danadas']} bobina(s) dañada(s) en la Planta '{row['nombre_planta']}', un valor inusualmente alto."
insertar_alerta_en_db(cursor,
tipo_alerta='ExcesoBobinasDañadas',
id_entidad=row['id_planta'],
entidad='Planta',
mensaje=mensaje,
fecha_anomalia=target_date.date())
print(f"INFO: Análisis de {len(df_danadas_new)} planta(s) con bobinas dañadas completado.")
else:
print("INFO: No se registraron bobinas dañadas en la fecha seleccionada.")
# --- FASE 5: Detección de Anomalías en Montos Contables ---
print("\n--- FASE 5: Detección de Anomalías en Montos Contables ---")
if not os.path.exists(MODEL_MONTOS_FILE):
print(f"ADVERTENCIA: Modelo de montos contables '{MODEL_MONTOS_FILE}' no encontrado.")
else:
model_montos = joblib.load(MODEL_MONTOS_FILE)
# Consulta unificada para obtener todas las transacciones del día
query_transacciones = f"""
SELECT 'Distribuidor' AS entidad, p.Id_Distribuidor AS id_entidad, d.Nombre as nombre_entidad, p.Id_Empresa as id_empresa, p.Fecha as fecha, p.TipoMovimiento as tipo_transaccion, p.Monto as monto
FROM cue_PagosDistribuidor p JOIN dist_dtDistribuidores d ON p.Id_Distribuidor = d.Id_Distribuidor
WHERE CAST(p.Fecha AS DATE) = '{target_date.strftime('%Y-%m-%d')}'
UNION ALL
SELECT
CASE WHEN cd.Destino = 'Distribuidores' THEN 'Distribuidor' ELSE 'Canillita' END AS entidad,
cd.Id_Destino AS id_entidad,
COALESCE(d.Nombre, c.NomApe) as nombre_entidad,
cd.Id_Empresa as id_empresa,
cd.Fecha as fecha,
cd.Tipo as tipo_transaccion,
cd.Monto as monto
FROM cue_CreditosDebitos cd
LEFT JOIN dist_dtDistribuidores d ON cd.Id_Destino = d.Id_Distribuidor AND cd.Destino = 'Distribuidores'
LEFT JOIN dist_dtCanillas c ON cd.Id_Destino = c.Id_Canilla AND cd.Destino = 'Canillas'
WHERE CAST(cd.Fecha AS DATE) = '{target_date.strftime('%Y-%m-%d')}'
"""
df_transacciones_new = pd.read_sql(query_transacciones, cnxn)
if not df_transacciones_new.empty:
# Aplicar exactamente el mismo pre-procesamiento que en el entrenamiento
df_transacciones_new['tipo_transaccion_cat'] = pd.Categorical(df_transacciones_new['tipo_transaccion']).codes
df_transacciones_new['dia_semana'] = pd.to_datetime(df_transacciones_new['fecha']).dt.dayofweek
features = ['id_entidad', 'id_empresa', 'tipo_transaccion_cat', 'dia_semana', 'monto']
X_new = df_transacciones_new[features]
df_transacciones_new['anomalia'] = model_montos.predict(X_new)
anomalias_detectadas = df_transacciones_new[df_transacciones_new['anomalia'] == -1]
if not anomalias_detectadas.empty:
for index, row in anomalias_detectadas.iterrows():
tipo_alerta = 'MontoInusualPago' if row['tipo_transaccion'] in ['Recibido', 'Realizado'] else 'MontoInusualNota'
mensaje = f"Se registró un '{row['tipo_transaccion']}' de ${row['monto']:,} para '{row['nombre_entidad']}', un valor atípico."
insertar_alerta_en_db(cursor,
tipo_alerta=tipo_alerta,
id_entidad=row['id_entidad'],
entidad=row['entidad'],
mensaje=mensaje,
fecha_anomalia=row['fecha'].date())
else:
print("INFO: No se encontraron anomalías en los montos contables registrados.")
else:
print("INFO: No hay transacciones contables para analizar en la fecha seleccionada.")
# --- Finalización ---
cnxn.commit()
cnxn.close()