diff --git a/__pycache__/menu_pasos_traduccion.cpython-310.pyc b/__pycache__/menu_pasos_traduccion.cpython-310.pyc index 4716be2..a4243fc 100644 Binary files a/__pycache__/menu_pasos_traduccion.cpython-310.pyc and b/__pycache__/menu_pasos_traduccion.cpython-310.pyc differ diff --git a/__pycache__/x1_importar_to_master.cpython-310.pyc b/__pycache__/x1_importar_to_master.cpython-310.pyc index 37bbc48..7315931 100644 Binary files a/__pycache__/x1_importar_to_master.cpython-310.pyc and b/__pycache__/x1_importar_to_master.cpython-310.pyc differ diff --git a/__pycache__/x2_master_export2translate.cpython-310.pyc b/__pycache__/x2_master_export2translate.cpython-310.pyc index a8f9a64..ae0cef5 100644 Binary files a/__pycache__/x2_master_export2translate.cpython-310.pyc and b/__pycache__/x2_master_export2translate.cpython-310.pyc differ diff --git a/__pycache__/x3_llm_auto_translate.cpython-310.pyc b/__pycache__/x3_llm_auto_translate.cpython-310.pyc index e603e56..9935c54 100644 Binary files a/__pycache__/x3_llm_auto_translate.cpython-310.pyc and b/__pycache__/x3_llm_auto_translate.cpython-310.pyc differ diff --git a/__pycache__/x4B_integrate_manual_translates_to_master.cpython-310.pyc b/__pycache__/x4B_integrate_manual_translates_to_master.cpython-310.pyc index 578ce5c..858c170 100644 Binary files a/__pycache__/x4B_integrate_manual_translates_to_master.cpython-310.pyc and b/__pycache__/x4B_integrate_manual_translates_to_master.cpython-310.pyc differ diff --git a/__pycache__/x4_integrate_translates_to_master.cpython-310.pyc b/__pycache__/x4_integrate_translates_to_master.cpython-310.pyc index 3e71af0..21db771 100644 Binary files a/__pycache__/x4_integrate_translates_to_master.cpython-310.pyc and b/__pycache__/x4_integrate_translates_to_master.cpython-310.pyc differ diff --git a/__pycache__/x5_complete_empty_cells_master.cpython-310.pyc b/__pycache__/x5_complete_empty_cells_master.cpython-310.pyc index 6d634d8..72f4fb0 100644 Binary files a/__pycache__/x5_complete_empty_cells_master.cpython-310.pyc and b/__pycache__/x5_complete_empty_cells_master.cpython-310.pyc differ diff --git a/__pycache__/x6_update_from_master.cpython-310.pyc b/__pycache__/x6_update_from_master.cpython-310.pyc index dddbb95..b79170e 100644 Binary files a/__pycache__/x6_update_from_master.cpython-310.pyc and b/__pycache__/x6_update_from_master.cpython-310.pyc differ diff --git a/menu_pasos_traduccion.py b/menu_pasos_traduccion.py index 0e6f7ba..fb7b2a8 100644 --- a/menu_pasos_traduccion.py +++ b/menu_pasos_traduccion.py @@ -12,7 +12,19 @@ from translation_config import TranslationConfig import sys import threading import os -import subprocess +import queue + + +class RedirectText: + def __init__(self, queue): + self.queue = queue + + def write(self, string): + self.queue.put(string) + + def flush(self): + pass + class TranslationApp: def __init__(self, master): @@ -26,142 +38,265 @@ class TranslationApp: self.create_widgets() self.place_widgets() + self.queue = queue.Queue() + self.update_output() + + # Cargar el log existente al iniciar + self.load_existing_log() + def create_variables(self): self.codigo_tipo_PLC_var = tk.StringVar(value=self.config.codigo_tipo_PLC) - self.codigo_columna_maestra_var = tk.StringVar(value=self.config.codigo_columna_maestra) - self.codigo_idioma_var = tk.StringVar(value=self.config.codigo_idioma_seleccionado) - self.codigo_idioma_var2 = tk.StringVar(value=self.config.codigo_idioma_secundario) + self.codigo_columna_maestra_var = tk.StringVar( + value=self.config.codigo_columna_maestra + ) + self.codigo_idioma_var = tk.StringVar( + value=self.config.codigo_idioma_seleccionado + ) + self.codigo_idioma_var2 = tk.StringVar( + value=self.config.codigo_idioma_secundario + ) self.work_dir_var = tk.StringVar(value=self.config.work_dir) self.master_name_var = tk.StringVar(value=self.config.master_name) self.translate_name_var = tk.StringVar(value=self.config.translate_name) - self.auto_translate_name_var = tk.StringVar(value=self.config.auto_translate_name) + self.auto_translate_name_var = tk.StringVar( + value=self.config.auto_translate_name + ) def create_widgets(self): self.main_frame = ttk.Frame(self.master) - self.main_frame.grid(row=0, column=0, sticky='nsew') + self.main_frame.grid(row=0, column=0, sticky="nsew") self.master.grid_rowconfigure(0, weight=1) self.master.grid_columnconfigure(0, weight=1) self.frame_combos = ttk.Frame(self.main_frame) - - self.label_tipo_PLC = ttk.Label(self.frame_combos, text="Selecciona el tipo de PLC:") + + self.label_tipo_PLC = ttk.Label( + self.frame_combos, text="Selecciona el tipo de PLC:" + ) self.combo_tipo_PLC = ttk.Combobox(self.frame_combos, state="readonly") self.combo_tipo_PLC["values"] = [nombre for nombre, _ in fc.PLCs.values()] - self.combo_tipo_PLC.bind('<>', self.update_tipo_PLC) + self.combo_tipo_PLC.bind("<>", self.update_tipo_PLC) self.set_combo_value(self.combo_tipo_PLC, self.codigo_tipo_PLC_var.get()) - self.label_codigo_maestra = ttk.Label(self.frame_combos, text="Idioma Columna Maestra:") + self.label_codigo_maestra = ttk.Label( + self.frame_combos, text="Idioma Columna Maestra:" + ) self.combo_codigo_maestra = ttk.Combobox(self.frame_combos, state="readonly") - self.combo_codigo_maestra["values"] = [nombre for nombre, _ in fc.IDIOMAS.values()] - self.combo_codigo_maestra.bind('<>', self.update_codigo_maestra) - self.set_combo_value(self.combo_codigo_maestra, self.codigo_columna_maestra_var.get()) + self.combo_codigo_maestra["values"] = [ + nombre for nombre, _ in fc.IDIOMAS.values() + ] + self.combo_codigo_maestra.bind( + "<>", self.update_codigo_maestra + ) + self.set_combo_value( + self.combo_codigo_maestra, self.codigo_columna_maestra_var.get() + ) self.label1 = ttk.Label(self.frame_combos, text="Idioma de Traducción:") self.combo = ttk.Combobox(self.frame_combos, state="readonly") self.combo["values"] = [nombre for nombre, _ in fc.IDIOMAS.values()] - self.combo.bind('<>', self.update_idioma) + self.combo.bind("<>", self.update_idioma) self.set_combo_value(self.combo, self.codigo_idioma_var.get()) self.label2 = ttk.Label(self.frame_combos, text="Selecciona segundo idioma:") self.combo2 = ttk.Combobox(self.frame_combos, state="readonly") self.combo2["values"] = [nombre for nombre, _ in fc.IDIOMAS.values()] - self.combo2.bind('<>', self.update_idioma2) + self.combo2.bind("<>", self.update_idioma2) self.set_combo_value(self.combo2, self.codigo_idioma_var2.get()) self.work_dir_frame = ttk.Frame(self.main_frame) - self.work_dir_label = ttk.Label(self.work_dir_frame, text="Directorio de trabajo:") - self.work_dir_entry = ttk.Entry(self.work_dir_frame, textvariable=self.work_dir_var, width=50) - self.work_dir_button = ttk.Button(self.work_dir_frame, text="Seleccionar", command=self.select_work_dir) - self.open_explorer_button = ttk.Button(self.work_dir_frame, text="Abrir Explorador", command=self.open_explorer) + self.work_dir_label = ttk.Label( + self.work_dir_frame, text="Directorio de trabajo:" + ) + self.work_dir_entry = ttk.Entry( + self.work_dir_frame, textvariable=self.work_dir_var, width=50 + ) + self.work_dir_button = ttk.Button( + self.work_dir_frame, text="Seleccionar", command=self.select_work_dir + ) + self.open_explorer_button = ttk.Button( + self.work_dir_frame, text="Abrir Explorador", command=self.open_explorer + ) style = ttk.Style() - style.configure('TButton', padding=(10, 10), font=('Helvetica', 10, 'bold')) - style.configure('Excel.TButton', padding=(10, 10), font=('Helvetica', 10), background='#69cf81') + style.configure("TButton", padding=(10, 10), font=("Helvetica", 10, "bold")) + style.configure( + "Excel.TButton", + padding=(10, 10), + font=("Helvetica", 10), + background="#69cf81", + ) self.button_width = 60 self.entry_width = 50 - - self.paso1 = ttk.Button(self.main_frame, text="1 - Importar al Master", command=lambda: self.ejecutar_run(x1_importar_to_master.run), width=self.button_width) - self.master_name_button = ttk.Button(self.main_frame, textvariable=self.master_name_var, command=lambda: self.open_file(self.config.get_master_path()), width=self.entry_width, style='Excel.TButton') - self.paso2 = ttk.Button(self.main_frame, text="2 - Exportar Idioma a 2_master_export2translate.xlsx", command=lambda: self.ejecutar_run(x2_master_export2translate.run), width=self.button_width) - self.translate_name_button = ttk.Button(self.main_frame, textvariable=self.translate_name_var, command=lambda: self.open_file(self.config.get_translate_path()), width=self.entry_width, style='Excel.TButton') + self.paso1 = ttk.Button( + self.main_frame, + text="1 - Importar al Master", + command=lambda: self.ejecutar_run(x1_importar_to_master.run), + width=self.button_width, + ) + self.master_name_button = ttk.Button( + self.main_frame, + textvariable=self.master_name_var, + command=lambda: self.open_file(self.config.get_master_path()), + width=self.entry_width, + style="Excel.TButton", + ) - self.paso3 = ttk.Button(self.main_frame, text="3 - Traducir y generar 3_master_export2translate_translated.xlsx", command=lambda: self.ejecutar_run(x3_llm_auto_translate.run), width=self.button_width) - self.auto_translate_name_button = ttk.Button(self.main_frame, textvariable=self.auto_translate_name_var, command=lambda: self.open_file(self.config.get_auto_translate_path()), width=self.entry_width, style='Excel.TButton') + self.paso2 = ttk.Button( + self.main_frame, + text="2 - Exportar Idioma a 2_master_export2translate.xlsx", + command=lambda: self.ejecutar_run(x2_master_export2translate.run), + width=self.button_width, + ) + self.translate_name_button = ttk.Button( + self.main_frame, + textvariable=self.translate_name_var, + command=lambda: self.open_file(self.config.get_translate_path()), + width=self.entry_width, + style="Excel.TButton", + ) - self.paso4 = ttk.Button(self.main_frame, text="4 - Integrar las traducciones al 1_hmi_master_translates", command=lambda: self.ejecutar_run(x4_integrate_translates_to_master.run), width=self.button_width) + self.paso3 = ttk.Button( + self.main_frame, + text="3 - Traducir y generar 3_master_export2translate_translated.xlsx", + command=lambda: self.ejecutar_run(x3_llm_auto_translate.run), + width=self.button_width, + ) + self.auto_translate_name_button = ttk.Button( + self.main_frame, + textvariable=self.auto_translate_name_var, + command=lambda: self.open_file(self.config.get_auto_translate_path()), + width=self.entry_width, + style="Excel.TButton", + ) - self.paso4B = ttk.Button(self.main_frame, text="4B - Integrar traducciones manuales al 1_hmi_master_translates", command=lambda: self.ejecutar_run(x4B_integrate_manual_translates_to_master.run), width=self.button_width) + self.paso4 = ttk.Button( + self.main_frame, + text="4 - Integrar las traducciones al 1_hmi_master_translates", + command=lambda: self.ejecutar_run(x4_integrate_translates_to_master.run), + width=self.button_width, + ) - self.paso5 = ttk.Button(self.main_frame, text="5 - Completar en 1_hmi_master_translates el idioma seleccionado usando el segundo idioma", command=lambda: self.ejecutar_run(x5_complete_empty_cells_master.run), width=self.button_width) + self.paso4B = ttk.Button( + self.main_frame, + text="4B - Integrar traducciones manuales al 1_hmi_master_translates", + command=lambda: self.ejecutar_run( + x4B_integrate_manual_translates_to_master.run + ), + width=self.button_width, + ) - self.paso6 = ttk.Button(self.main_frame, text="6 - Exportar usando un archivo exportado desde TIA Portal usando 1_hmi_master_translates", command=lambda: self.ejecutar_run(x6_update_from_master.run), width=self.button_width) + self.paso5 = ttk.Button( + self.main_frame, + text="5 - Completar en 1_hmi_master_translates el idioma seleccionado usando el segundo idioma", + command=lambda: self.ejecutar_run(x5_complete_empty_cells_master.run), + width=self.button_width, + ) - self.output_text = tk.Text(self.main_frame, wrap='none', height=20) - self.scrollbar_y = ttk.Scrollbar(self.main_frame, orient='vertical', command=self.output_text.yview) - self.scrollbar_x = ttk.Scrollbar(self.main_frame, orient='horizontal', command=self.output_text.xview) - self.output_text.configure(yscrollcommand=self.scrollbar_y.set, xscrollcommand=self.scrollbar_x.set) + self.paso6 = ttk.Button( + self.main_frame, + text="6 - Exportar usando un archivo exportado desde TIA Portal usando 1_hmi_master_translates", + command=lambda: self.ejecutar_run(x6_update_from_master.run), + width=self.button_width, + ) - self.clear_button = ttk.Button(self.main_frame, text="Limpiar salida", command=self.clear_output) - self.open_log_button = ttk.Button(self.main_frame, text="Abrir Log", command=self.open_log_file) + self.output_text = tk.Text(self.main_frame, wrap="none", height=20) + self.scrollbar_y = ttk.Scrollbar( + self.main_frame, orient="vertical", command=self.output_text.yview + ) + self.scrollbar_x = ttk.Scrollbar( + self.main_frame, orient="horizontal", command=self.output_text.xview + ) + self.output_text.configure( + yscrollcommand=self.scrollbar_y.set, xscrollcommand=self.scrollbar_x.set + ) + + self.clear_button = ttk.Button( + self.main_frame, text="Limpiar salida", command=self.clear_output + ) + self.open_log_button = ttk.Button( + self.main_frame, text="Abrir Log", command=self.open_log_file + ) def place_widgets(self): self.main_frame.grid_columnconfigure(0, weight=1) self.main_frame.grid_columnconfigure(1, weight=1) - self.frame_combos.grid(row=0, column=0, columnspan=2, pady=10, sticky='ew') - self.label_tipo_PLC.grid(row=0, column=0, padx=5, pady=5, sticky='e') - self.combo_tipo_PLC.grid(row=0, column=1, padx=5, pady=5, sticky='w') - self.label_codigo_maestra.grid(row=1, column=0, padx=5, pady=5, sticky='e') - self.combo_codigo_maestra.grid(row=1, column=1, padx=5, pady=5, sticky='w') - self.label1.grid(row=0, column=2, padx=5, pady=5, sticky='e') - self.combo.grid(row=0, column=3, padx=5, pady=5, sticky='w') - self.label2.grid(row=1, column=2, padx=5, pady=5, sticky='e') - self.combo2.grid(row=1, column=3, padx=5, pady=5, sticky='w') + self.frame_combos.grid(row=0, column=0, columnspan=2, pady=10, sticky="ew") + self.label_tipo_PLC.grid(row=0, column=0, padx=5, pady=5, sticky="e") + self.combo_tipo_PLC.grid(row=0, column=1, padx=5, pady=5, sticky="w") + self.label_codigo_maestra.grid(row=1, column=0, padx=5, pady=5, sticky="e") + self.combo_codigo_maestra.grid(row=1, column=1, padx=5, pady=5, sticky="w") + self.label1.grid(row=0, column=2, padx=5, pady=5, sticky="e") + self.combo.grid(row=0, column=3, padx=5, pady=5, sticky="w") + self.label2.grid(row=1, column=2, padx=5, pady=5, sticky="e") + self.combo2.grid(row=1, column=3, padx=5, pady=5, sticky="w") - self.work_dir_frame.grid(row=1, column=0, columnspan=2, pady=10, sticky='ew') - self.work_dir_label.grid(row=0, column=0, padx=5, sticky='w') - self.work_dir_entry.grid(row=0, column=1, padx=5, sticky='ew') - self.work_dir_button.grid(row=0, column=2, padx=5, sticky='e') - self.open_explorer_button.grid(row=0, column=3, padx=5, sticky='e') + self.work_dir_frame.grid(row=1, column=0, columnspan=2, pady=10, sticky="ew") + self.work_dir_label.grid(row=0, column=0, padx=5, sticky="w") + self.work_dir_entry.grid(row=0, column=1, padx=5, sticky="ew") + self.work_dir_button.grid(row=0, column=2, padx=5, sticky="e") + self.open_explorer_button.grid(row=0, column=3, padx=5, sticky="e") self.work_dir_frame.grid_columnconfigure(1, weight=1) current_row = 2 - self.paso1.grid(row=current_row, column=0, pady=5, padx=5, sticky='ew') - self.master_name_button.grid(row=current_row, column=1, pady=5, padx=5, sticky='ew') - - current_row += 1 - self.paso2.grid(row=current_row, column=0, pady=5, padx=5, sticky='ew') - self.translate_name_button.grid(row=current_row, column=1, pady=5, padx=5, sticky='ew') - - current_row += 1 - self.paso3.grid(row=current_row, column=0, pady=5, padx=5, sticky='ew') - self.auto_translate_name_button.grid(row=current_row, column=1, pady=5, padx=5, sticky='ew') - - current_row += 1 - self.paso4.grid(row=current_row, column=0, pady=5, padx=5, sticky='ew') - - current_row += 1 - self.paso4B.grid(row=current_row, column=0, pady=5, padx=5, sticky='ew') - - current_row += 1 - self.paso5.grid(row=current_row, column=0, columnspan=2, pady=5, padx=5, sticky='ew') - - current_row += 1 - self.paso6.grid(row=current_row, column=0, columnspan=2, pady=5, padx=5, sticky='ew') + self.paso1.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") + self.master_name_button.grid( + row=current_row, column=1, pady=5, padx=5, sticky="ew" + ) current_row += 1 - self.output_text.grid(row=current_row, column=0, columnspan=2, padx=(10, 0), pady=10, sticky='nsew') - self.scrollbar_y.grid(row=current_row, column=2, pady=10, sticky='ns') - self.scrollbar_x.grid(row=current_row+1, column=0, columnspan=2, padx=10, sticky='ew') + self.paso2.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") + self.translate_name_button.grid( + row=current_row, column=1, pady=5, padx=5, sticky="ew" + ) + + current_row += 1 + self.paso3.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") + self.auto_translate_name_button.grid( + row=current_row, column=1, pady=5, padx=5, sticky="ew" + ) + + current_row += 1 + self.paso4.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") + + current_row += 1 + self.paso4B.grid(row=current_row, column=0, pady=5, padx=5, sticky="ew") + + current_row += 1 + self.paso5.grid( + row=current_row, column=0, columnspan=2, pady=5, padx=5, sticky="ew" + ) + + current_row += 1 + self.paso6.grid( + row=current_row, column=0, columnspan=2, pady=5, padx=5, sticky="ew" + ) + + current_row += 1 + self.output_text.grid( + row=current_row, + column=0, + columnspan=2, + padx=(10, 0), + pady=10, + sticky="nsew", + ) + self.scrollbar_y.grid(row=current_row, column=2, pady=10, sticky="ns") + self.scrollbar_x.grid( + row=current_row + 1, column=0, columnspan=2, padx=10, sticky="ew" + ) current_row += 2 - self.clear_button.grid(row=current_row, column=0, pady=10, padx=5, sticky='e') - self.open_log_button.grid(row=current_row, column=1, pady=10, padx=5, sticky='w') + self.clear_button.grid(row=current_row, column=0, pady=10, padx=5, sticky="e") + self.open_log_button.grid( + row=current_row, column=1, pady=10, padx=5, sticky="w" + ) - self.main_frame.grid_rowconfigure(current_row-2, weight=1) + self.main_frame.grid_rowconfigure(current_row - 2, weight=1) def set_combo_value(self, combo, codigo): for nombre, code in fc.IDIOMAS.values(): @@ -222,8 +357,21 @@ class TranslationApp: dir_path = filedialog.askdirectory() if dir_path: self.work_dir_var.set(dir_path) + self.config.work_dir = dir_path self.save_config() - self.update_file_names() + self.load_existing_log() # Cargar el log del nuevo directorio + + def load_existing_log(self): + log_file = os.path.join(self.config.work_dir, "logs", "app_log.txt") + if os.path.exists(log_file): + with open(log_file, "r", encoding="utf-8") as f: + log_content = f.read() + self.output_text.delete("1.0", tk.END) + self.output_text.insert(tk.END, log_content) + self.output_text.see(tk.END) + else: + self.output_text.delete("1.0", tk.END) + self.output_text.insert(tk.END, "No se encontró un log existente.\n") def open_file(self, file_path): if os.path.exists(file_path): @@ -235,73 +383,115 @@ class TranslationApp: if os.path.exists(self.config.work_dir): os.startfile(self.config.work_dir) else: - messagebox.showerror("Error", f"El directorio {self.config.work_dir} no existe.") + messagebox.showerror( + "Error", f"El directorio {self.config.work_dir} no existe." + ) def open_log_file(self): log_file_path = os.path.join(self.config.work_dir, "logs", "translate_log.log") if os.path.exists(log_file_path): - os.startfile(log_file_path) + if sys.platform == "win32": + os.startfile(log_file_path) + else: + opener = "open" if sys.platform == "darwin" else "xdg-open" + subprocess.call([opener, log_file_path]) else: messagebox.showerror("Error", f"El archivo de log {log_file_path} no existe.") def update_file_names(self, event=None): codigo_tipo_PLC = self.codigo_tipo_PLC_var.get() codigo_idioma_seleccionado = self.codigo_idioma_var.get() - + self.master_name_var.set(f"1_hmi_master_translates_{codigo_tipo_PLC}.xlsx") - self.translate_name_var.set(f'2_master_export2translate_{codigo_tipo_PLC}_{codigo_idioma_seleccionado}.xlsx') - self.auto_translate_name_var.set(f"3_master_export2translate_translated_{codigo_tipo_PLC}_{codigo_idioma_seleccionado}.xlsx") - + self.translate_name_var.set( + f"2_master_export2translate_{codigo_tipo_PLC}_{codigo_idioma_seleccionado}.xlsx" + ) + self.auto_translate_name_var.set( + f"3_master_export2translate_translated_{codigo_tipo_PLC}_{codigo_idioma_seleccionado}.xlsx" + ) + self.save_config() def ejecutar_run(self, funcion_run): self.save_config() - - original_stdout = sys.stdout - sys.stdout = self.RedirectText(self.output_text) - try: - if funcion_run == x1_importar_to_master.run: - funcion_run(self.config) - elif funcion_run == x2_master_export2translate.run: - funcion_run(self.config) - elif funcion_run == x3_llm_auto_translate.run: - traducir_todo = messagebox.askyesno("Traducir todo", "¿Desea traducir todas las celdas?") - self.config.traducir_todo = traducir_todo - funcion_run(self.config) - elif funcion_run == x4_integrate_translates_to_master.run: - funcion_run(self.config) - elif funcion_run == x4B_integrate_manual_translates_to_master.run: - funcion_run(self.config) - elif funcion_run == x5_complete_empty_cells_master.run: - funcion_run(self.config) - elif funcion_run == x6_update_from_master.run: - archivo_to_update = filedialog.askopenfilename(title="Seleccione el archivo a actualizar", filetypes=[("Excel files", "*.xlsx")]) - if archivo_to_update: - funcion_run(self.config, archivo_to_update) - except Exception as e: - messagebox.showerror("Error", str(e)) - finally: - sys.stdout = original_stdout - self.save_config() + # No limpiar el texto anterior + # self.output_text.delete('1.0', tk.END) + + sys.stdout = RedirectText(self.queue) + + def run_script(): + try: + if funcion_run == x1_importar_to_master.run: + archivo_importacion = filedialog.askopenfilename( + title="Seleccione el archivo para Importar", + filetypes=[("Excel files", "*.xls*")], + ) + if archivo_importacion: + funcion_run(self.config,archivo_importacion) + elif funcion_run == x2_master_export2translate.run: + funcion_run(self.config) + elif funcion_run == x3_llm_auto_translate.run: + traducir_todo = messagebox.askyesno( + "Traducir todo", "¿Desea traducir todas las celdas?" + ) + self.config.traducir_todo = traducir_todo + funcion_run(self.config) + elif funcion_run == x4_integrate_translates_to_master.run: + funcion_run(self.config) + elif funcion_run == x4B_integrate_manual_translates_to_master.run: + funcion_run(self.config) + elif funcion_run == x5_complete_empty_cells_master.run: + funcion_run(self.config) + elif funcion_run == x6_update_from_master.run: + archivo_to_update = filedialog.askopenfilename( + title="Seleccione el archivo a actualizar", + filetypes=[("Excel files", "*.xlsx")], + ) + if archivo_to_update: + funcion_run(self.config, archivo_to_update) + except Exception as e: + self.queue.put(f"Error: {str(e)}\n") + finally: + sys.stdout = sys.__stdout__ + self.save_config() + self.save_log() + self.queue.put("DONE") + + threading.Thread(target=run_script, daemon=True).start() + + def update_output(self): + while True: + try: + message = self.queue.get_nowait() + if message == "DONE": + break + self.output_text.insert(tk.END, message) + self.output_text.see(tk.END) + self.output_text.update_idletasks() + except queue.Empty: + break + self.master.after(100, self.update_output) + def clear_output(self): - self.output_text.delete('1.0', tk.END) + self.output_text.delete("1.0", tk.END) + self.save_log() - class RedirectText: - def __init__(self, text_widget): - self.text_widget = text_widget + def save_log(self): + log_content = self.output_text.get("1.0", tk.END) + log_dir = os.path.join(self.config.work_dir, "logs") + os.makedirs(log_dir, exist_ok=True) + log_file = os.path.join(log_dir, "app_log.txt") - def write(self, string): - self.text_widget.insert(tk.END, string) - self.text_widget.see(tk.END) + with open(log_file, "w", encoding="utf-8") as f: + f.write(log_content) - def flush(self): - pass def main(): root = tk.Tk() app = TranslationApp(root) root.mainloop() + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/translation_config.json b/translation_config.json index 39ff6d7..c2f8afc 100644 --- a/translation_config.json +++ b/translation_config.json @@ -1,6 +1,6 @@ { "codigo_tipo_PLC": "allenbradley", - "codigo_columna_maestra": "es-ES", + "codigo_columna_maestra": "en-US", "codigo_idioma_seleccionado": "es-ES", "codigo_idioma_secundario": "en-US", "work_dir": "C:/Trabajo/VM/31 - 9.3953 - Omega - Mexico - (ABradley)/Reporte/Languages", diff --git a/x1_importar_to_master.py b/x1_importar_to_master.py index 8f8500c..97647c6 100644 --- a/x1_importar_to_master.py +++ b/x1_importar_to_master.py @@ -14,6 +14,10 @@ def preprocesar_importacion( ): filas_validas = [] + progress_bar = fc.ProgressBar( + len(df_importacion), prefix="Preprocesando filas:", suffix="Completado" + ) + for index, fila in df_importacion.iterrows(): clave_original = fila[codigo_columna_maestra] @@ -22,6 +26,7 @@ def preprocesar_importacion( logger.info( f"Fila {index}: Ignorada - Celda de columna maestra vacía o solo con espacios en blanco" ) + progress_bar.increment() continue nueva_fila = fila.copy() @@ -40,27 +45,28 @@ def preprocesar_importacion( filas_validas.append(nueva_fila) logger.info(f"Fila {index}: Aceptada - Clave '{clave_sustituida}' procesada") + progress_bar.increment() + progress_bar.finish() df_valido = pd.DataFrame(filas_validas, columns=df_importacion.columns) return df_valido -def importar(config: TranslationConfig): +def importar(config: TranslationConfig, archivo_importacion): logger.info(" .... ") logger.info(f"Iniciando importacion en {config.master_name}") archivo_maestro = os.path.join(config.work_dir, config.master_name) + if not archivo_importacion: + logger.info("No se seleccionó ningún archivo para importar.") + return + if not os.path.exists(archivo_maestro): df_maestro = pd.DataFrame(columns=[config.codigo_columna_maestra]) else: df_maestro = fc.read_dataframe_with_cleanup_retries(archivo_maestro) - archivo_importacion = fc.select_file("xlsx") - if not archivo_importacion: - logger.info("No se seleccionó ningún archivo para importar.") - return - df_importacion = fc.read_dataframe_with_cleanup_retries(archivo_importacion) df_importacion = preprocesar_importacion( @@ -71,6 +77,10 @@ def importar(config: TranslationConfig): filas_a_agregar = [] + progress_bar = fc.ProgressBar( + len(df_importacion), prefix="Procesando filas:", suffix="Completado" + ) + for index, fila in df_importacion.iterrows(): clave = str(fila[config.codigo_columna_maestra]) @@ -78,6 +88,7 @@ def importar(config: TranslationConfig): logger.info( f"Fila {index}: Ignorada - Clave '{clave}' ya existe en el archivo maestro" ) + progress_bar.increment() continue claves_maestro.add(clave) @@ -88,9 +99,13 @@ def importar(config: TranslationConfig): if col == config.codigo_columna_maestra or fc.es_columna_tipo_xxYY(col) } filas_a_agregar.append(fila_filtrada) + progress_bar.increment() + + progress_bar.finish() if not filas_a_agregar: logger.info("No hay nuevas filas válidas para agregar.") + print("No hay nuevas filas válidas para agregar.") return for columna in df_importacion.columns: @@ -109,16 +124,15 @@ def importar(config: TranslationConfig): ) -def run(config: TranslationConfig): +def run(config: TranslationConfig, archivo_importacion): global logger logger = fc.configurar_logger(config.work_dir) - importar(config) + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") + importar(config, archivo_importacion) if __name__ == "__main__": - print( - "Este script debe ser ejecutado desde el menú principal (menu_pasos_traduccion.py)." - ) - print( - "Por favor, ejecute menu_pasos_traduccion.py para utilizar esta funcionalidad." - ) + import menu_pasos_traduccion + + menu_pasos_traduccion.main() diff --git a/x2_master_export2translate.py b/x2_master_export2translate.py index e8c00ea..6f1cab4 100644 --- a/x2_master_export2translate.py +++ b/x2_master_export2translate.py @@ -1,52 +1,170 @@ import pandas as pd import os +import sys +import time import PyLibrary.funciones_comunes as fc from translation_config import TranslationConfig +import langid +from openpyxl import load_workbook +from openpyxl.styles import PatternFill, Alignment # Definir el logger a nivel de módulo logger = None +def configurar_detector_idiomas(): + codigos_idioma = [code.split("-")[0] for _, code in fc.IDIOMAS.values()] + langid.set_languages(codigos_idioma) + + +def detectar_idioma(texto, tipo_PLC): + texto_limpio = fc.limpiar_texto(tipo_PLC, texto) + if len(texto_limpio.strip()) < 3: # No detectar idioma en textos muy cortos + return "unknown" + try: + idioma, _ = langid.classify(texto_limpio) + return idioma + except: + return "unknown" + + +def obtener_nombre_idioma(codigo_corto): + for nombre, codigo in fc.IDIOMAS.values(): + if codigo.startswith(codigo_corto): + return nombre + return "Desconocido" + + def exportar_para_traduccion(config: TranslationConfig): master_path = config.get_master_path() if not os.path.exists(master_path): print("El archivo maestro no existe.") return - # Leer el archivo maestro + configurar_detector_idiomas() + df_maestro = fc.read_dataframe_with_cleanup_retries(master_path) - # Crear un nuevo DataFrame para la exportación df_export = pd.DataFrame() - - # Copiar la primera columna y la columna del idioma de destino primera_columna = df_maestro.columns[0] df_export[primera_columna] = df_maestro[primera_columna] df_export[config.codigo_idioma_seleccionado] = df_maestro[ config.codigo_idioma_seleccionado ] + df_export["Idioma_Detectado"] = "" - # Guardar el archivo exportado ruta_export = config.get_translate_path() - # Usar ExcelWriter para tener más control sobre el proceso de escritura with pd.ExcelWriter(ruta_export, engine="openpyxl") as writer: df_export.to_excel(writer, index=False, sheet_name="Sheet1") - # Ajustar el ancho de las columnas + workbook = writer.book worksheet = writer.sheets["Sheet1"] - for idx, col in enumerate(df_export.columns): - max_length = max(df_export[col].astype(str).map(len).max(), len(col)) - if max_length > 50: - max_length = 50 - worksheet.column_dimensions[chr(65 + idx)].width = max_length + 2 - print(f"Archivo exportado para traducción: {ruta_export}") + wrap_alignment = Alignment(wrap_text=True, vertical="top") + for col in ["A", "B"]: + for cell in worksheet[col]: + cell.alignment = wrap_alignment + worksheet.column_dimensions[col].width = 50 + + idioma_esperado = fc.idiomas_shortcodefromcode( + config.codigo_idioma_seleccionado + ) + fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid") + + total_rows = worksheet.max_row - 1 # Excluimos la fila de encabezado + progress_bar = fc.ProgressBar( + total_rows, prefix="Procesando filas:", suffix="Completado" + ) + + for row in range(2, worksheet.max_row + 1): + texto = worksheet.cell(row=row, column=2).value + if texto: + idioma_detectado = detectar_idioma(texto, config.codigo_tipo_PLC) + if ( + idioma_detectado != "unknown" + and idioma_detectado != idioma_esperado + ): + worksheet.cell(row=row, column=2).fill = fill + nombre_idioma = obtener_nombre_idioma(idioma_detectado) + worksheet.cell(row=row, column=3).value = nombre_idioma + progress_bar.update(row - 1) + + progress_bar.finish() + + print(f"\nArchivo exportado para traducción: {ruta_export}") + print("Las celdas con idioma incorrecto han sido marcadas en azul.") + print( + "Se ha añadido el nombre del idioma detectado cuando es diferente del esperado." + ) + master_path = config.get_master_path() + if not os.path.exists(master_path): + print("El archivo maestro no existe.") + return + + configurar_detector_idiomas() + + df_maestro = fc.read_dataframe_with_cleanup_retries(master_path) + + df_export = pd.DataFrame() + primera_columna = df_maestro.columns[0] + df_export[primera_columna] = df_maestro[primera_columna] + df_export[config.codigo_idioma_seleccionado] = df_maestro[ + config.codigo_idioma_seleccionado + ] + df_export["Idioma_Detectado"] = "" + + ruta_export = config.get_translate_path() + + with pd.ExcelWriter(ruta_export, engine="openpyxl") as writer: + df_export.to_excel(writer, index=False, sheet_name="Sheet1") + + workbook = writer.book + worksheet = writer.sheets["Sheet1"] + + wrap_alignment = Alignment(wrap_text=True, vertical="top") + for col in ["A", "B"]: + for cell in worksheet[col]: + cell.alignment = wrap_alignment + worksheet.column_dimensions[col].width = 50 + + idioma_esperado = fc.idiomas_shortcodefromcode( + config.codigo_idioma_seleccionado + ) + fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid") + + total_rows = worksheet.max_row - 1 # Excluimos la fila de encabezado + progress_bar = fc.ProgressBar( + total_rows, prefix="Progreso:", suffix="Completado" + ) + + for row in range(2, worksheet.max_row + 1): + texto = worksheet.cell(row=row, column=2).value + if texto: + idioma_detectado = detectar_idioma(texto, config.codigo_tipo_PLC) + if ( + idioma_detectado != "unknown" + and idioma_detectado != idioma_esperado + ): + worksheet.cell(row=row, column=2).fill = fill + nombre_idioma = obtener_nombre_idioma(idioma_detectado) + worksheet.cell(row=row, column=3).value = nombre_idioma + progress_bar.increment() + + progress_bar.finish() + + print(f"\nArchivo exportado para traducción: {ruta_export}") + print("Las celdas con idioma incorrecto han sido marcadas en azul.") + print( + "Se ha añadido el nombre del idioma detectado cuando es diferente del esperado." + ) def run(config: TranslationConfig): global logger logger = fc.configurar_logger(config.work_dir) + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") exportar_para_traduccion(config) diff --git a/x3_llm_auto_translate.py b/x3_llm_auto_translate.py index 561337b..8a63d3a 100644 --- a/x3_llm_auto_translate.py +++ b/x3_llm_auto_translate.py @@ -1,7 +1,6 @@ import pandas as pd from openai import OpenAI import os -import re from openai_api_key import openai_api_key from google_api_key import google_api_key import ollama @@ -220,48 +219,40 @@ def main(config: TranslationConfig): texts_to_translate = {} + # Inicializar ProgressBar para la fase de preparación + prep_progress = fc.ProgressBar(len(df), prefix='Preparando textos:', suffix='Completado') + for index, row in df.iterrows(): celda_clave = str(row[source_col]) - source_translated_text = ( - str(row[source_translated_col]) - if source_translated_col in df.columns - else "" - ) - celda_clave_compactada = fc.compactar_celda_traducida( - config.codigo_tipo_PLC, celda_clave - ) + source_translated_text = str(row[source_translated_col]) if source_translated_col in df.columns else "" + celda_clave_compactada = fc.compactar_celda_traducida(config.codigo_tipo_PLC, celda_clave) if config.traducir_todo: - if fc.texto_requiere_traduccion( - config.codigo_tipo_PLC, celda_clave_compactada, logger - ): + if fc.texto_requiere_traduccion(config.codigo_tipo_PLC, celda_clave_compactada, logger): df.at[index, source_translated_col] = "" texts_to_translate[celda_clave] = celda_clave_compactada else: - if ( - pd.isna(row[source_translated_col]) - or source_translated_text.strip() == "" - ): - if fc.texto_requiere_traduccion( - config.codigo_tipo_PLC, celda_clave_compactada, logger - ) or fc.texto_con_campos_especiales( - config.codigo_tipo_PLC, celda_clave_compactada - ): + if pd.isna(row[source_translated_col]) or source_translated_text.strip() == "": + if fc.texto_requiere_traduccion(config.codigo_tipo_PLC, celda_clave_compactada, logger) or fc.texto_con_campos_especiales(config.codigo_tipo_PLC, celda_clave_compactada): texts_to_translate[celda_clave] = celda_clave_compactada + + prep_progress.update(index + 1) + + prep_progress.finish() num_texts = len(texts_to_translate) - # num_texts = 40 - logger.info(f"Número total de textos a traducir: {num_texts}") - print(f"Número total de textos a traducir: {num_texts}") + print(f"\nNúmero total de textos a traducir: {num_texts}") - # Traducciones + # Inicializar ProgressBar para la fase de traducción + trans_progress = fc.ProgressBar(num_texts, prefix='Traduciendo:', suffix='Completado') + + # Traducciones translations = {} for start_idx in range(0, num_texts, batch_size): end_idx = min(start_idx + batch_size, num_texts) batch_texts = dict(list(texts_to_translate.items())[start_idx:end_idx]) - logger.info(f"Traduciendo: celdas desde {start_idx} a {end_idx}.") - print(f"Traduciendo : celdas desde: {start_idx} a :{end_idx}.") + logger.info(f"Traduciendo: celdas desde {start_idx} a {end_idx}.") retries = 4 for attempt in range(retries): @@ -281,11 +272,17 @@ def main(config: TranslationConfig): else: logger.error(f"Error en todos los intentos de traducción de celdas desde {start_idx} a {end_idx}: {e}") print(f"Error en todos los intentos de traducción de celdas desde {start_idx} a {end_idx}: {e}") + + trans_progress.update(end_idx) + trans_progress.finish() logger.info(f"Número total de traducciones recibidas: {len(translations)}") + # Inicializar ProgressBar para la fase de actualización del DataFrame + update_progress = fc.ProgressBar(len(df), prefix='Actualizando DataFrame:', suffix='Completado') + # Actualizar el DataFrame con las traducciones y hacemos la Traduccion inversa - for index, row in tqdm(df.iterrows(), total=df.shape[0], desc="Procesando traducciones"): + for index, row in df.iterrows(): celda_clave = str(row[source_col]) if celda_clave in translations: df.at[index, target_col] = translations[celda_clave] @@ -299,14 +296,18 @@ def main(config: TranslationConfig): logger.error(f"Error en la traducción de Google para el texto '{celda_clave}': {e}") df.at[index, check_translate_col] = "Error en la traducción" df.at[index, affinity_col] = 0.0 + update_progress.increment() + + update_progress.finish() + # Inicializar ProgressBar para la fase de cálculo de afinidad + affinity_progress = fc.ProgressBar(num_texts, prefix='Calculando afinidad:', suffix='Completado') # Afinidades affinities = {} for start_idx in range(0, num_texts, batch_size): end_idx = min(start_idx + batch_size, num_texts) batch_texts = dict(list(texts_to_translate.items())[start_idx:end_idx]) - logger.info(f"Afinidad: celdas desde {start_idx} a {end_idx}.") - print(f"Afinidad: celdas desde: {start_idx} a :{end_idx}.") + logger.info(f"Afinidad: celdas desde {start_idx} a {end_idx}.") retries = 2 for attempt in range(retries): @@ -330,7 +331,10 @@ def main(config: TranslationConfig): affinities[key] = "0" logger.error(f"Error en el cálculo individual de Afinidad para el texto '{key}': {ind_e}") print(f"Error en el cálculo individual de Afinidad para el texto '{key}': {ind_e}") + + affinity_progress.increment() + affinity_progress.finish() # Actualizar el DataFrame con las Afinidades for index, row in df.iterrows(): celda_clave = str(row[source_col]) @@ -340,15 +344,15 @@ def main(config: TranslationConfig): output_path = config.get_auto_translate_path() fc.save_dataframe_with_retries(df, output_path=output_path) logger.info(f"Archivo traducido guardado en: {output_path}") - print(f"Archivo traducido guardado en: {output_path}") + print(f"\nArchivo traducido guardado en: {output_path}") def run(config: TranslationConfig): global logger logger = fc.configurar_logger(config.work_dir) + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") main(config) - if __name__ == "__main__": import menu_pasos_traduccion - - menu_pasos_traduccion.main() + menu_pasos_traduccion.main() \ No newline at end of file diff --git a/x4B_integrate_manual_translates_to_master.py b/x4B_integrate_manual_translates_to_master.py index 064c5ed..fdd6a08 100644 --- a/x4B_integrate_manual_translates_to_master.py +++ b/x4B_integrate_manual_translates_to_master.py @@ -113,6 +113,8 @@ def marcar_errores_archivo_fuente(archivo, celdas_con_errores): def run(config: TranslationConfig): global logger logger = fc.configurar_logger(config.work_dir) + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") importar_traduccion_manual(config) diff --git a/x4_integrate_translates_to_master.py b/x4_integrate_translates_to_master.py index a3fe09b..17caf09 100644 --- a/x4_integrate_translates_to_master.py +++ b/x4_integrate_translates_to_master.py @@ -55,6 +55,10 @@ def importar_traduccion(config: TranslationConfig): ) columna_excel = df_maestro.columns.get_loc(master_col) + 1 celdas_modificadas[(fila_excel, columna_excel)] = valor_traducido + else : + logger.error( + f'Clave {clave} no encontrada en master.' + ) fc.save_dataframe_with_retries(df_maestro, output_path=archivo_maestro) @@ -81,6 +85,8 @@ def aplicar_negrita_celdas_modificadas(archivo, celdas_modificadas): def run(config: TranslationConfig): global logger logger = fc.configurar_logger(config.work_dir) + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") importar_traduccion(config) diff --git a/x5_complete_empty_cells_master.py b/x5_complete_empty_cells_master.py index 5403d54..cd83531 100644 --- a/x5_complete_empty_cells_master.py +++ b/x5_complete_empty_cells_master.py @@ -45,6 +45,8 @@ def complete_emptys(config: TranslationConfig): def run(config: TranslationConfig): global logger logger = fc.configurar_logger(config.work_dir) + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") complete_emptys(config) diff --git a/x6_update_from_master.py b/x6_update_from_master.py index 90544a8..7a6e296 100644 --- a/x6_update_from_master.py +++ b/x6_update_from_master.py @@ -110,11 +110,12 @@ def marcar_celdas_con_errores(archivo_maestro, celdas_con_errores, target_lang_c ) -def run(config: TranslationConfig): +def run(config: TranslationConfig, archivo_to_update): global logger logger = fc.configurar_logger(config.work_dir) - archivo_to_update = fc.select_file("xlsx") if archivo_to_update: + script_name = os.path.basename(__file__) + print(f"\rIniciando: {script_name}\r") update_from_master(config, archivo_to_update)