From f86df8f09a15d01bae3fdaf53e350b0e458a4e4b Mon Sep 17 00:00:00 2001 From: Miguel Date: Tue, 30 Jul 2024 11:30:08 +0200 Subject: [PATCH] Primer --- __pycache__/manejoArchivos.cpython-310.pyc | Bin 0 -> 1359 bytes hmi_master_translates.xlsx | Bin 0 -> 9029 bytes importar_to_master.py | 90 ++++++++++++++++++ manejoArchivos.py | 50 ++++++++++ update_from_master.py | 102 +++++++++++++++++++++ 5 files changed, 242 insertions(+) create mode 100644 __pycache__/manejoArchivos.cpython-310.pyc create mode 100644 hmi_master_translates.xlsx create mode 100644 importar_to_master.py create mode 100644 manejoArchivos.py create mode 100644 update_from_master.py diff --git a/__pycache__/manejoArchivos.cpython-310.pyc b/__pycache__/manejoArchivos.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbe1d3ece182c49670f25d054644f37179320d7e GIT binary patch literal 1359 zcmZ`(OK;RL5cVUGdqwS3= zzW^c86XMFx@s$$?{s0GH##xpE!jT<&W-{aXzL}_V=XwOj{ZAiuzea?7#>3gc;cy40 zc?^pnf|ews{>?HDZ&tEtkOj2Wg<0rzQ5K1w2t@drW*rfU&TEpzA{JeEcLm)d>B<-I zN1mqC^>&L=iXt!7p5yk|jwxyu1H*?f@540LVX@>rJtZe}PT#XrLW$-3fuMp3J|}O< zJGRS205g2UPS~75ps0D#OJF>!Wo42)!DvYvF0m>xvXo;B@}YRy>hek?TG~Sm=5`_% z7E-j@I=7Rdci5cY`up1^esIs5nTBl+Q`U6vmgPdgH;0=kbD=G4DYGtK!_I1H($Fz` z;Mn$oi(eIXB6R*rGsrYs%gup;Tcc@}Pi4woR~}nPqfnI#v{JV9vxArVNHj9}?S{BH zdal(BvQlQWHP%IK&FJBiN6EI%D^uoHj;47f_tjlJo)kyQ4C|R&X>r)WSRKTA6_z-N zX}}p>r_uKy(0vg7LrwrfPtF6_Sh5ea%*iP|p?=Tj^pxrg0_b`RwepUiuqpzomM0tR zhjX9|34Q=~B)6WHz2Zn#31)sZ87r+J-*T2rfW5M)_PoL* z2xwyJd@Rj^wBFLs#y7gr43@`hEdp0JwxK3NGm&NaG>sfn#swI~#g)=izY7Z^3hjF4 z@MWzP_%@E~!z$%^9YDy|&@eBHMhef2>x|VDAQzsXc3A;KH$ZkhTM%;9-v4#+%U@O#j~mkQUaBo+bb7g5M^H z#RApAvK5x;&J;MuJuKYF0HP>M+U3% zSD{8f0bxY0pJ9WRe@lYT-Keps1|W?Trvcy@tyLT+ok)+B-NC8`;pFulUwf ZiNjLf#y^xnu459@K8@k&v3RvV_z9u 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 K-W4=IR{sY*aj$^@ literal 0 HcmV?d00001 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)