Parser_Destinos/main.py
2025-06-03 17:20:09 +02:00

205 lines
7.3 KiB
Python

import os
import curses
import pandas as pd
import numpy as np
from colorama import init, Fore, Style
from helpers import CabColumna, ListaCabColumnas
# Inicializa colorama
init(autoreset=True)
def limpiar_pantalla():
os.system('cls' if os.name == 'nt' else 'clear')
def seleccionar_archivo_curses(stdscr):
curses.curs_set(0) # Ocultar cursor
archivos = [f for f in os.listdir() if f.lower().endswith(('.xls', '.xlsx'))]
if not archivos:
stdscr.addstr(0, 0, "No se encontraron archivos .xls o .xlsx en el directorio actual.")
stdscr.getch()
return None
seleccion = 0
while True:
stdscr.clear()
stdscr.addstr(0, 0, "Selecciona un archivo Excel (.xls, .xlsx):\n\n")
for idx, archivo in enumerate(archivos):
if idx == seleccion:
stdscr.addstr(idx + 2, 0, f"> {archivo}", curses.A_REVERSE)
else:
stdscr.addstr(idx + 2, 0, f" {archivo}")
stdscr.addstr(len(archivos) + 3, 0, "Usa ↑↓ para moverte, Enter para seleccionar, q para salir.")
tecla = stdscr.getch()
if tecla == curses.KEY_UP and seleccion > 0:
seleccion -= 1
elif tecla == curses.KEY_DOWN and seleccion < len(archivos) - 1:
seleccion += 1
elif tecla == ord('q'):
return None
elif tecla == 10: # Enter
return os.path.abspath(archivos[seleccion])
def seleccionar_archivo():
return curses.wrapper(seleccionar_archivo_curses)
memoria_combinaciones = {}
def elegir_combinacion(strtemp):
combinaciones = []
if len(strtemp) == 3:
combinaciones = [
(' '.join(strtemp[:1]), ' '.join(strtemp[1:])),
(' '.join(strtemp[:2]), strtemp[2])
]
elif len(strtemp) == 4:
combinaciones = [
(' '.join(strtemp[:1]), ' '.join(strtemp[1:])),
(' '.join(strtemp[:2]), ' '.join(strtemp[2:])),
(' '.join(strtemp[:3]), strtemp[3])
]
else:
return strtemp
claves_posibles = [parte1 for parte1, parte2 in combinaciones]
for i, clave in enumerate(claves_posibles):
if clave in memoria_combinaciones:
return (clave, memoria_combinaciones[clave])
# Mostrar menú al usuario
print("\nSe detectaron múltiples líneas. Elige cómo dividirlas:")
for i, (parte1, parte2) in enumerate(combinaciones):
print(f"{i+1}) Parte 1: \"{parte1}\" | Parte 2: \"{parte2}\"")
while True:
opcion = input("Introduce el número de la opción deseada: ").strip()
if opcion.isdigit() and 1 <= int(opcion) <= len(combinaciones):
seleccion = combinaciones[int(opcion) - 1]
clave_general = seleccion[0] # Solo recordamos la parte 1
memoria_combinaciones[clave_general] = seleccion[1] # Guardamos parte 2 asociada
return seleccion
else:
print("Opción inválida. Intenta de nuevo.")
def parsear_documento(archivo):
print("\n>>> Parsear documento")
print(f"Parseando archivo: {archivo} ...")
df = pd.read_excel(archivo, engine='openpyxl', header=None)
df = df.replace(r'^\s*$', np.nan, regex=True)
df = df.dropna(how='all').dropna(axis=1,how='all')
lista_cab_columnas=ListaCabColumnas()
lista_cab_columnas.importar('cabs.json')
cabeceras = []
ministerio = ''
for cabecera in lista_cab_columnas.lista:
if cabecera.nombre_cabecera!="": #
cabecera.indice=df.columns[df.apply(lambda col: col.astype(str).str.contains(cabecera.nombre_cabecera)).any()].tolist()[0]
cabeceras.extend(list(map(str, cabecera.campos)))
df_parseado=pd.DataFrame(columns=cabeceras)
for index, row in df.iterrows():
valor_celda = row.iloc[0]
if isinstance(valor_celda, str) and lista_cab_columnas.lista[0].nombre_cabecera not in valor_celda:
row=row.dropna()
ministerio = ' '.join(row.astype(str))
elif isinstance(valor_celda, str) and lista_cab_columnas.lista[0].nombre_cabecera in valor_celda:
continue
else:
fila_index = len(df_parseado)
for cabecera in lista_cab_columnas.lista:
if cabecera.nombre_cabecera!="": #
if len(cabecera.campos)>1:
strtemp = row[cabecera.indice].split('\n')
if len(strtemp) in (3, 4):
strtemp = elegir_combinacion(strtemp)
else:
strtemp = [' '.join(strtemp[:-1]), strtemp[-1]]
for i, campo in enumerate(cabecera.campos):
valor = strtemp[i] if i < len(strtemp) else ""
df_parseado.at[fila_index, campo] = valor
else:
df_parseado.at[fila_index,cabecera.campos[0]] = str(row[cabecera.indice]).replace('\n',' ')
else:
df_parseado.at[fila_index,cabecera.campos[0]] = ministerio
input("\nPresiona Enter para continuar...")
return df_parseado
def exportar_a_excel(documento_parseado):
print("\n>>> Exportar a Excel")
try:
documento_parseado.to_excel('DestinosParseados.xlsx', index=False)
print("Documento exportado a Excel.")
except Exception as e:
print(f"Error al exportar: {e}")
input("\nPresiona Enter para continuar...")
def mostrar_menu(archivo, documento_parseado):
print("=== Menú Principal ===\n")
print("1) Seleccionar archivo")
if archivo:
print(f"2) Parsear documento {Fore.GREEN}{os.path.basename(archivo)}{Fore.RESET}")
else:
print(Fore.LIGHTBLACK_EX + "2) Parsear documento [desactivado]")
if archivo and documento_parseado is not None and not documento_parseado.empty:
print("3) Exportar a Excel")
else:
print(Fore.LIGHTBLACK_EX + "3) Exportar a Excel [desactivado]")
print("0) Salir")
def main():
archivo = None
archivoParser = None
documento_parseado = None
while True:
limpiar_pantalla()
mostrar_menu(archivo, documento_parseado)
opcion = input("\nSeleccione una opción: ").strip()
if opcion == "1":
archivo = seleccionar_archivo()
if archivo:
print(f"\nArchivo seleccionado: {archivo}")
else:
print("\nNo se seleccionó ningún archivo.")
documento_parseado = None
elif opcion == "2":
if not archivo:
print(Fore.RED + "Opción desactivada: debes seleccionar un archivo primero.")
else:
documento_parseado = parsear_documento(archivo)
elif opcion == "3":
if not archivo:
print(Fore.RED + "Opción desactivada: primero selecciona un archivo.")
elif documento_parseado is None or documento_parseado.empty:
print(Fore.RED + "Opción desactivada: debes parsear el archivo antes de exportar.")
else:
exportar_a_excel(documento_parseado)
elif opcion == "0":
print("\nSaliendo del programa...")
break
else:
print(Fore.RED + "Opción no válida.")
input("\nPresiona Enter para continuar...")
if __name__ == "__main__":
main()