commit f86df8f09a15d01bae3fdaf53e350b0e458a4e4b Author: Miguel Date: Tue Jul 30 11:30:08 2024 +0200 Primer diff --git a/__pycache__/manejoArchivos.cpython-310.pyc b/__pycache__/manejoArchivos.cpython-310.pyc new file mode 100644 index 0000000..cbe1d3e Binary files /dev/null and b/__pycache__/manejoArchivos.cpython-310.pyc differ diff --git a/hmi_master_translates.xlsx b/hmi_master_translates.xlsx new file mode 100644 index 0000000..7413c7a Binary files /dev/null and b/hmi_master_translates.xlsx differ diff --git a/importar_to_master.py b/importar_to_master.py new file mode 100644 index 0000000..a25bcab --- /dev/null +++ b/importar_to_master.py @@ -0,0 +1,90 @@ +import pandas as pd +import os +import re +from manejoArchivos import select_file + +def es_columna_tipo_xxYY(columna): + # Verificar si la columna es del tipo "xx-YY" usando una expresión regular + return bool(re.match(r'^[a-z]{2}-[A-Z]{2}$', columna)) + +def sustituir_digitos(celda): + # Convertir a cadena y sustituir secuencias de dígitos por [[digits]] + if pd.isnull(celda): + return celda + return re.sub(r'\d+', '[[digits]]', str(celda)) + +def preprocesar_importacion(df_importacion): + # Iterar sobre las filas del DataFrame de importación + for index, fila in df_importacion.iterrows(): + clave_original = str(fila['it-IT']) + clave_sustituida = sustituir_digitos(clave_original) + + # Sustituir en las demás columnas del tipo "xx-YY" + for columna in df_importacion.columns: + if columna != 'it-IT' and es_columna_tipo_xxYY(columna): + df_importacion.at[index, columna] = sustituir_digitos(fila[columna]) + + # Guardar la clave sustituida + df_importacion.at[index, 'it-IT'] = clave_sustituida + + return df_importacion + +def importar(archivo_maestro, archivo_importacion): + if not os.path.exists(archivo_maestro): + # Crear un DataFrame maestro vacío con la columna "it-IT" + df_maestro = pd.DataFrame(columns=["it-IT"]) + else: + df_maestro = pd.read_excel(archivo_maestro) + + df_importacion = pd.read_excel(archivo_importacion) + + # Preprocesar el archivo de importación + df_importacion = preprocesar_importacion(df_importacion) + + # Obtener las claves existentes en el archivo maestro + claves_maestro = set(df_maestro["it-IT"].dropna().astype(str)) + + # Filtrar filas del archivo de importación que no están en el archivo maestro + nuevas_filas = df_importacion[ + df_importacion["it-IT"].apply( + lambda x: len(str(x)) > 5 and str(x) not in claves_maestro + ) + ] + + # Si no hay filas nuevas, terminar + if nuevas_filas.empty: + print("No hay nuevas filas para agregar.") + return + + # Agregar columnas del tipo "xx-YY" que no existen en el archivo maestro + for columna in nuevas_filas.columns: + if es_columna_tipo_xxYY(columna) and columna not in df_maestro.columns: + df_maestro[columna] = None + + # Crear una lista de diccionarios para las filas que se van a agregar + filas_a_agregar = [] + + # Iterar sobre las nuevas filas para agregarlas y actualizar las claves + for _, fila in nuevas_filas.iterrows(): + clave = str(fila["it-IT"]) + if clave not in claves_maestro: + claves_maestro.add(clave) + # Solo agregar las columnas del tipo "xx-YY" y "it-IT" + fila_filtrada = {col: fila[col] for col in fila.index if col == "it-IT" or es_columna_tipo_xxYY(col)} + filas_a_agregar.append(fila_filtrada) + + # Concatenar las nuevas filas al DataFrame maestro + if filas_a_agregar: + df_nuevas_filas = pd.DataFrame(filas_a_agregar) + df_maestro = pd.concat([df_maestro, df_nuevas_filas], ignore_index=True) + + # Guardar el archivo maestro actualizado + df_maestro.to_excel(archivo_maestro, index=False) + print(f"Se han agregado {len(filas_a_agregar)} nuevas filas al archivo maestro.") + +if __name__ == "__main__": + # Cargar el archivo maestro y el archivo de importación + archivo_maestro = "hmi_master_translates.xlsx" + archivo_importacion = select_file("xlsx") + if archivo_importacion: + importar(archivo_maestro, archivo_importacion) diff --git a/manejoArchivos.py b/manejoArchivos.py new file mode 100644 index 0000000..f99cc52 --- /dev/null +++ b/manejoArchivos.py @@ -0,0 +1,50 @@ +import pandas as pd +import tkinter as tk +from tkinter import filedialog +import os +import subprocess + +def select_file(extension = "txt"): + """ + Opens a file dialog to select a .db file and returns the selected file path. + """ + root = tk.Tk() + root.withdraw() # Use to hide the tkinter root window + + # Open file dialog and return the selected file path + file_path = filedialog.askopenfilename( + title="Select a .db file", + filetypes=(("DB files", f"*.{extension}"), ("All files", "*.*")) + ) + return file_path + +def open_file_explorer(path): + """ + Opens the file explorer at the given path, correctly handling paths with spaces. + """ + # Normalize the path to ensure it's in the correct format + normalized_path = os.path.normpath(path) + + # Check if the path is a directory or a file and format the command accordingly + if os.path.isdir(normalized_path): + # If it's a directory, use the 'explorer' command directly + command = f'explorer "{normalized_path}"' + else: + # If it's a file, use the 'explorer /select,' command to highlight the file in its folder + command = f'explorer /select,"{normalized_path}"' + + # Execute the command using subprocess.run, with shell=True to handle paths with spaces correctly + subprocess.run(command, shell=True) + +def select_directory(): + """ + Opens a file dialog to select a directory and returns the selected directory path. + """ + root = tk.Tk() + root.withdraw() # Use to hide the tkinter root window + + # Open directory dialog and return the selected directory path + directory_path = filedialog.askdirectory( + title="Select a directory" + ) + return directory_path \ No newline at end of file diff --git a/update_from_master.py b/update_from_master.py new file mode 100644 index 0000000..544d6cd --- /dev/null +++ b/update_from_master.py @@ -0,0 +1,102 @@ +import pandas as pd +import os +import re +import logging +from manejoArchivos import select_file + +def es_columna_tipo_xxYY(columna): + # Verificar si la columna es del tipo "xx-YY" usando una expresión regular + return bool(re.match(r'^[a-z]{2}-[A-Z]{2}$', columna)) + +def sustituir_digitos(celda): + # Convertir a cadena y sustituir secuencias de dígitos por [[digits]] + if pd.isnull(celda): + return celda, [] + digitos = re.findall(r'\d+', str(celda)) + celda_sustituida = re.sub(r'\d+', '[[digits]]', str(celda)) + return celda_sustituida, digitos + +def preprocesar_update(df_update): + # Diccionario para almacenar los dígitos reemplazados por fila + digitos_reemplazados = {} + + # Iterar sobre las filas del DataFrame de actualización + for index, fila in df_update.iterrows(): + clave_original = str(fila['it-IT']) + clave_sustituida, digitos = sustituir_digitos(clave_original) + digitos_reemplazados[index] = digitos + df_update.at[index, 'it-IT_preprocessed'] = clave_sustituida # Agregar una columna preprocesada + + return df_update, digitos_reemplazados + +def configurar_logger(ruta_log): + logger = logging.getLogger('actualizacion_logger') + logger.setLevel(logging.INFO) + fh = logging.FileHandler(ruta_log, encoding='utf-8') + fh.setLevel(logging.INFO) + formatter = logging.Formatter('%(asctime)s - %(message)s') + fh.setFormatter(formatter) + logger.addHandler(fh) + return logger + +def update_from_master(archivo_maestro, archivo_to_update): + if not os.path.exists(archivo_maestro): + print("El archivo maestro no existe.") + return + + df_maestro = pd.read_excel(archivo_maestro) + df_to_update = pd.read_excel(archivo_to_update) + + # Configurar el logger + directorio = os.path.dirname(archivo_to_update) + nombre_log = os.path.join(directorio, 'actualizacion.log') + logger = configurar_logger(nombre_log) + + # Preprocesar el archivo de actualización + df_to_update, digitos_reemplazados = preprocesar_update(df_to_update) + + # Obtener las claves del archivo maestro + claves_maestro = set(df_maestro["it-IT"].dropna().astype(str)) + + # Iterar sobre las filas del archivo de actualización para actualizarlas + for index, fila in df_to_update.iterrows(): + clave_preprocesada = str(fila['it-IT_preprocessed']) + if clave_preprocesada in claves_maestro: + # Obtener los dígitos originales para esta fila + digitos = digitos_reemplazados.get(index, []) + df_maestro_fila = df_maestro[df_maestro['it-IT'] == clave_preprocesada].iloc[0] + + # Verificar que la cantidad de dígitos coincida con la cantidad de [[digits]] en la clave + cantidad_digits = clave_preprocesada.count('[[digits]]') + if len(digitos) != cantidad_digits: + logger.info(f'Fila {index}: no se actualiza porque la cantidad de dígitos no coincide.') + continue + + # Actualizar solo las columnas que existen en df_to_update y que no sean 'it-IT' + for columna in df_to_update.columns: + if columna != 'it-IT' and columna != 'it-IT_preprocessed' and columna in df_maestro.columns: + valor = df_maestro_fila[columna] + # Convertir a cadena si no lo es y reemplazar [[digits]] con los dígitos originales + if not pd.isnull(valor): + valor_original = str(valor) + valor_actualizado = valor_original + for d in digitos: + valor_actualizado = valor_actualizado.replace('[[digits]]', d, 1) + # Solo actualizar si el valor ha cambiado + if str(fila[columna]) != valor_actualizado: + df_to_update.at[index, columna] = valor_actualizado + # Registrar el cambio + logger.info(f'Fila {index}, Columna {columna}: "{fila[columna]}" actualizado a "{valor_actualizado}"') + + # Eliminar la columna preprocesada + df_to_update.drop(columns=['it-IT_preprocessed'], inplace=True) + + # Guardar el archivo actualizado + df_to_update.to_excel(archivo_to_update, index=False) + print(f"Se han actualizado las filas en {archivo_to_update} desde el archivo maestro. Detalles de los cambios en {nombre_log}") + +if __name__ == "__main__": + archivo_maestro = "hmi_master_translates.xlsx" + archivo_to_update = select_file("xlsx") + if archivo_to_update: + update_from_master(archivo_maestro, archivo_to_update)