HMI_Translate_Helper_wMaste.../x2_master_export2translate.py

238 lines
10 KiB
Python
Raw Normal View History

import pandas as pd
import os
import PyLibrary.funciones_comunes as fc
from translation_config import TranslationConfig
import langid
from openpyxl import load_workbook
2024-10-15 10:00:51 -03:00
from openpyxl.styles import PatternFill, Alignment, Font
from collections import defaultdict
from openai_api_key import openai_api_key
# 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):
"""
Exporta los textos del archivo maestro para su traducción, realizando validaciones
y cálculos de afinidad en lotes. Los textos idénticos reciben afinidad 1 automáticamente.
"""
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)
# Preparar DataFrame de exportación
df_export = pd.DataFrame()
primera_columna = df_maestro.columns[0]
df_export[primera_columna] = df_maestro[primera_columna]
columna_propuesta = f"{config.codigo_idioma_seleccionado}_Propuesto"
df_export[columna_propuesta] = df_maestro[config.codigo_idioma_seleccionado]
# Agregar columnas de validación si los idiomas son diferentes
if config.codigo_columna_maestra != config.codigo_idioma_seleccionado:
df_export["Validation_Error"] = ""
df_export["Affinity_Score"] = None
df_export["Idioma_Detectado"] = ""
# Agregar columna del idioma secundario
if config.codigo_idioma_secundario in df_maestro.columns:
df_export[config.codigo_idioma_secundario] = df_maestro[config.codigo_idioma_secundario]
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"]
# Inmovilizar paneles en A2
worksheet.freeze_panes = "A2"
# Configurar estilos
wrap_alignment = Alignment(wrap_text=True, vertical="top")
red_fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
yellow_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
blue_fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
# Ajustar anchos de columna
for col in worksheet.columns:
max_length = 0
column = col[0].column_letter
for cell in col:
try:
if cell.value:
text_length = len(str(cell.value))
if text_length > 50:
cell.alignment = wrap_alignment
text_length = min(50, max(len(word) for word in str(cell.value).split()))
max_length = max(max_length, text_length)
except:
pass
adjusted_width = min(50, max_length + 2)
worksheet.column_dimensions[column].width = adjusted_width if adjusted_width > 8 else 8
# Primera fase: Procesar detección de idioma y recopilar textos para afinidad
texts_to_check = {} # Para textos que necesitan cálculo de afinidad
identical_texts = {} # Para textos idénticos (afinidad 1)
2024-10-15 10:00:51 -03:00
texto_a_filas = defaultdict(list)
inconsistencias = 0
afinidad_baja = 0
progress_bar = fc.ProgressBar(
worksheet.max_row - 1, prefix="Procesando textos:", suffix="Completado"
)
for row in range(2, worksheet.max_row + 1):
texto_original = worksheet.cell(row=row, column=1).value
texto_propuesto = worksheet.cell(row=row, column=2).value
if texto_original and texto_propuesto:
# Detección de idioma
texto_limpio = fc.limpiar_texto(config.codigo_tipo_PLC, texto_propuesto)
if texto_propuesto == texto_limpio:
texto_a_filas[texto_propuesto].append(row)
idioma_detectado = detectar_idioma(texto_propuesto, config.codigo_tipo_PLC)
idioma_esperado = fc.idiomas_shortcodefromcode(config.codigo_idioma_seleccionado)
if idioma_detectado != "unknown" and idioma_detectado != idioma_esperado:
worksheet.cell(row=row, column=2).fill = blue_fill
nombre_idioma = obtener_nombre_idioma(idioma_detectado)
worksheet.cell(
row=row,
column=df_export.columns.get_loc("Idioma_Detectado") + 1
).value = nombre_idioma
# Recopilar textos para afinidad si los idiomas son diferentes
if config.codigo_columna_maestra != config.codigo_idioma_seleccionado:
if pd.notnull(texto_propuesto) and texto_propuesto.strip() != "":
# Compactar los textos para comparación
texto_original_comp = fc.compactar_celda_traducida(config.codigo_tipo_PLC, str(texto_original))
texto_propuesto_comp = fc.compactar_celda_traducida(config.codigo_tipo_PLC, str(texto_propuesto))
# Si los textos son idénticos después de compactar, afinidad automática de 1
if texto_original_comp == texto_propuesto_comp:
identical_texts[texto_original] = row
else:
texts_to_check[texto_original] = texto_propuesto
2024-10-15 10:00:51 -03:00
progress_bar.increment()
progress_bar.finish()
# Configurar el modelo a usar
modelo_llm = fc.LLM_MODELS["OpenAI"] # o el que se prefiera
api_key = openai_api_key() # solo necesario para OpenAI y Grok
# Segunda fase: Procesar textos idénticos y calcular afinidades en lote
if config.codigo_columna_maestra != config.codigo_idioma_seleccionado:
# Asignar afinidad 1 a textos idénticos
logger.info(f"Asignando afinidad 1 a {len(identical_texts)} textos idénticos")
for _, row in identical_texts.items():
col_idx = df_export.columns.get_loc("Affinity_Score") + 1
worksheet.cell(row=row, column=col_idx).value = 1.0
# Calcular afinidades para textos diferentes
if texts_to_check:
logger.info(f"Calculando afinidad para {len(texts_to_check)} textos diferentes")
try:
# Calcular afinidades
affinities = fc.calcular_afinidad_batch(
texts_to_check,
config.codigo_tipo_PLC,
modelo_llm,
logger,
api_key
)
# Aplicar resultados de afinidad
progress_bar = fc.ProgressBar(
len(affinities), prefix="Aplicando afinidades:", suffix="Completado"
)
for texto_original, afinidad in affinities.items():
row = next(row for row in range(2, worksheet.max_row + 1)
if worksheet.cell(row=row, column=1).value == texto_original)
col_idx = df_export.columns.get_loc("Affinity_Score") + 1
worksheet.cell(row=row, column=col_idx).value = afinidad
if afinidad < 1:
worksheet.cell(row=row, column=2).fill = yellow_fill
afinidad_baja += 1
progress_bar.increment()
progress_bar.finish()
except Exception as e:
logger.error(f"Error en el cálculo de afinidad por lotes: {str(e)}")
print(f"Error en el cálculo de afinidad por lotes: {str(e)}")
# Marcar celdas duplicadas
bold_font = Font(bold=True)
2024-10-15 10:00:51 -03:00
celdas_duplicadas = 0
for filas in texto_a_filas.values():
if len(filas) > 1:
for row in filas:
cell = worksheet.cell(row=row, column=2)
cell.font = bold_font
celdas_duplicadas += len(filas)
# Imprimir resumen
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.")
print(f"Se ha agregado la columna del idioma secundario ({config.codigo_idioma_secundario}) al final de la planilla.")
if config.codigo_columna_maestra != config.codigo_idioma_seleccionado:
print(f"Se encontraron {len(identical_texts)} textos idénticos (afinidad 1)")
print(f"Se encontraron {inconsistencias} celdas con errores de validación (marcadas en rojo)")
print(f"Se encontraron {afinidad_baja} celdas con afinidad menor a 1 (marcadas en amarillo)")
print(f"Se han marcado {celdas_duplicadas} celdas en negrita por tener texto duplicado.")
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)
if __name__ == "__main__":
import menu_pasos_traduccion
menu_pasos_traduccion.main()