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()