Mejorando el control de afinidad de x2 y moviendo a funciones comunes la logica de control de afinidad
This commit is contained in:
parent
39116ca425
commit
751825a659
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"codigo_tipo_PLC": "allenbradley",
|
"codigo_tipo_PLC": "allenbradley",
|
||||||
"codigo_columna_maestra": "es-ES",
|
"codigo_columna_maestra": "en-US",
|
||||||
"codigo_idioma_seleccionado": "en-US",
|
"codigo_idioma_seleccionado": "it-IT",
|
||||||
"codigo_idioma_secundario": "es-ES",
|
"codigo_idioma_secundario": "en-US",
|
||||||
"work_dir": "C:/Trabajo/VM/33 - 9.4022 - Shibuya - Usa/Reporte/Language",
|
"work_dir": "C:/Trabajo/VM/35 - 9.4023 - Shibuya - Mayo - Usa/Reporte/Language",
|
||||||
"master_name": "1_hmi_master_translates_allenbradley.xlsx",
|
"master_name": "1_hmi_master_translates_allenbradley.xlsx",
|
||||||
"translate_name": "2_master_export2translate_allenbradley_en-US.xlsx",
|
"translate_name": "2_master_export2translate_allenbradley_it-IT.xlsx",
|
||||||
"auto_translate_name": "3_master_export2translate_translated_allenbradley_en-US.xlsx",
|
"auto_translate_name": "3_master_export2translate_translated_allenbradley_it-IT.xlsx",
|
||||||
"nivel_afinidad_minimo": 0.5,
|
"nivel_afinidad_minimo": 0.5,
|
||||||
"traducir_todo": false
|
"traducir_todo": false
|
||||||
}
|
}
|
|
@ -6,9 +6,12 @@ import langid
|
||||||
from openpyxl import load_workbook
|
from openpyxl import load_workbook
|
||||||
from openpyxl.styles import PatternFill, Alignment, Font
|
from openpyxl.styles import PatternFill, Alignment, Font
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from openai import OpenAI
|
||||||
|
from openai_api_key import openai_api_key
|
||||||
|
|
||||||
# Definir el logger a nivel de módulo
|
# Definir el logger a nivel de módulo
|
||||||
logger = None
|
logger = None
|
||||||
|
openai_client = OpenAI(api_key=openai_api_key())
|
||||||
|
|
||||||
|
|
||||||
def configurar_detector_idiomas():
|
def configurar_detector_idiomas():
|
||||||
|
@ -41,67 +44,145 @@ def exportar_para_traduccion(config: TranslationConfig):
|
||||||
return
|
return
|
||||||
|
|
||||||
configurar_detector_idiomas()
|
configurar_detector_idiomas()
|
||||||
|
|
||||||
df_maestro = fc.read_dataframe_with_cleanup_retries(master_path)
|
df_maestro = fc.read_dataframe_with_cleanup_retries(master_path)
|
||||||
|
|
||||||
df_export = pd.DataFrame()
|
df_export = pd.DataFrame()
|
||||||
primera_columna = df_maestro.columns[0]
|
primera_columna = df_maestro.columns[0]
|
||||||
df_export[primera_columna] = df_maestro[primera_columna]
|
df_export[primera_columna] = df_maestro[primera_columna]
|
||||||
df_export[config.codigo_idioma_seleccionado] = df_maestro[
|
|
||||||
config.codigo_idioma_seleccionado
|
columna_propuesta = f"{config.codigo_idioma_seleccionado}_Propuesto"
|
||||||
]
|
df_export[columna_propuesta] = df_maestro[config.codigo_idioma_seleccionado]
|
||||||
|
|
||||||
|
# Add validation columns if source and target languages are different
|
||||||
|
if config.codigo_columna_maestra != config.codigo_idioma_seleccionado:
|
||||||
|
df_export["Validation_Error"] = ""
|
||||||
|
df_export["Affinity_Score"] = None
|
||||||
|
|
||||||
df_export["Idioma_Detectado"] = ""
|
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()
|
ruta_export = config.get_translate_path()
|
||||||
|
|
||||||
with pd.ExcelWriter(ruta_export, engine="openpyxl") as writer:
|
with pd.ExcelWriter(ruta_export, engine="openpyxl") as writer:
|
||||||
df_export.to_excel(writer, index=False, sheet_name="Sheet1")
|
df_export.to_excel(writer, index=False, sheet_name="Sheet1")
|
||||||
|
|
||||||
workbook = writer.book
|
workbook = writer.book
|
||||||
worksheet = writer.sheets["Sheet1"]
|
worksheet = writer.sheets["Sheet1"]
|
||||||
|
|
||||||
|
# Inmovilizar paneles en A2
|
||||||
|
worksheet.freeze_panes = 'A2'
|
||||||
|
|
||||||
wrap_alignment = Alignment(wrap_text=True, vertical="top")
|
wrap_alignment = Alignment(wrap_text=True, vertical="top")
|
||||||
for col in ["A", "B"]:
|
for col in worksheet.columns:
|
||||||
for cell in worksheet[col]:
|
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
|
cell.alignment = wrap_alignment
|
||||||
worksheet.column_dimensions[col].width = 50
|
text_length = min(
|
||||||
|
50, max(len(word) for word in str(cell.value).split())
|
||||||
idioma_esperado = fc.idiomas_shortcodefromcode(
|
|
||||||
config.codigo_idioma_seleccionado
|
|
||||||
)
|
)
|
||||||
fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
|
max_length = max(max_length, text_length)
|
||||||
bold_font = Font(bold=True)
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
total_rows = worksheet.max_row - 1 # Excluimos la fila de encabezado
|
adjusted_width = min(50, max_length + 2)
|
||||||
|
worksheet.column_dimensions[column].width = (
|
||||||
|
adjusted_width if adjusted_width > 8 else 8
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
total_rows = worksheet.max_row - 1
|
||||||
progress_bar = fc.ProgressBar(
|
progress_bar = fc.ProgressBar(
|
||||||
total_rows, prefix="Procesando filas:", suffix="Completado"
|
total_rows, prefix="Procesando filas:", suffix="Completado"
|
||||||
)
|
)
|
||||||
|
|
||||||
print("Iniciando procesamiento de filas...")
|
|
||||||
|
|
||||||
texto_a_filas = defaultdict(list)
|
texto_a_filas = defaultdict(list)
|
||||||
|
inconsistencias = 0
|
||||||
|
afinidad_baja = 0
|
||||||
|
|
||||||
for row in range(2, worksheet.max_row + 1):
|
for row in range(2, worksheet.max_row + 1):
|
||||||
texto = worksheet.cell(row=row, column=2).value
|
texto = worksheet.cell(row=row, column=2).value
|
||||||
if texto:
|
if texto:
|
||||||
|
# Language detection
|
||||||
texto_limpio = fc.limpiar_texto(config.codigo_tipo_PLC, texto)
|
texto_limpio = fc.limpiar_texto(config.codigo_tipo_PLC, texto)
|
||||||
|
|
||||||
# Solo considerar para duplicados si el texto limpio es igual al original
|
|
||||||
if texto == texto_limpio:
|
if texto == texto_limpio:
|
||||||
texto_a_filas[texto].append(row)
|
texto_a_filas[texto].append(row)
|
||||||
|
|
||||||
# Detectar idioma y marcar si es incorrecto
|
|
||||||
idioma_detectado = detectar_idioma(texto, config.codigo_tipo_PLC)
|
idioma_detectado = detectar_idioma(texto, config.codigo_tipo_PLC)
|
||||||
|
idioma_esperado = fc.idiomas_shortcodefromcode(
|
||||||
|
config.codigo_idioma_seleccionado
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
idioma_detectado != "unknown"
|
idioma_detectado != "unknown"
|
||||||
and idioma_detectado != idioma_esperado
|
and idioma_detectado != idioma_esperado
|
||||||
):
|
):
|
||||||
worksheet.cell(row=row, column=2).fill = fill
|
worksheet.cell(row=row, column=2).fill = blue_fill
|
||||||
nombre_idioma = obtener_nombre_idioma(idioma_detectado)
|
nombre_idioma = obtener_nombre_idioma(idioma_detectado)
|
||||||
worksheet.cell(row=row, column=3).value = nombre_idioma
|
worksheet.cell(
|
||||||
|
row=row,
|
||||||
|
column=df_export.columns.get_loc("Idioma_Detectado") + 1,
|
||||||
|
).value = nombre_idioma
|
||||||
|
|
||||||
|
# Validation checks for different languages
|
||||||
|
if config.codigo_columna_maestra != config.codigo_idioma_seleccionado:
|
||||||
|
texts_to_check = {}
|
||||||
|
batch_size = 20
|
||||||
|
|
||||||
|
for row in range(2, worksheet.max_row + 1):
|
||||||
|
clave = worksheet.cell(row=row, column=1).value
|
||||||
|
texto = worksheet.cell(row=row, column=2).value
|
||||||
|
if pd.notnull(texto) and texto.strip() != "":
|
||||||
|
texts_to_check[clave] = texto
|
||||||
|
|
||||||
|
if len(texts_to_check) >= batch_size:
|
||||||
|
try:
|
||||||
|
affinities = fc.affinity_batch_openai(
|
||||||
|
config.codigo_tipo_PLC,
|
||||||
|
texts_to_check,
|
||||||
|
openai_client,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
for check_row, (key, score) in enumerate(
|
||||||
|
affinities.items(), start=2
|
||||||
|
):
|
||||||
|
col_idx = (
|
||||||
|
df_export.columns.get_loc("Affinity_Score")
|
||||||
|
+ 1
|
||||||
|
)
|
||||||
|
worksheet.cell(
|
||||||
|
row=check_row, column=col_idx
|
||||||
|
).value = score
|
||||||
|
if score < 1:
|
||||||
|
worksheet.cell(
|
||||||
|
row=check_row, column=2
|
||||||
|
).fill = yellow_fill
|
||||||
|
afinidad_baja += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error en lote de afinidad: {str(e)}")
|
||||||
|
texts_to_check.clear()
|
||||||
|
|
||||||
progress_bar.increment()
|
progress_bar.increment()
|
||||||
|
|
||||||
# Marcar celdas duplicadas en negrita
|
# Mark duplicate cells in bold
|
||||||
|
bold_font = Font(bold=True)
|
||||||
celdas_duplicadas = 0
|
celdas_duplicadas = 0
|
||||||
for filas in texto_a_filas.values():
|
for filas in texto_a_filas.values():
|
||||||
if len(filas) > 1:
|
if len(filas) > 1:
|
||||||
|
@ -118,7 +199,17 @@ def exportar_para_traduccion(config: TranslationConfig):
|
||||||
"Se ha añadido el nombre del idioma detectado cuando es diferente del esperado."
|
"Se ha añadido el nombre del idioma detectado cuando es diferente del esperado."
|
||||||
)
|
)
|
||||||
print(
|
print(
|
||||||
f"Se han marcado {celdas_duplicadas} celdas en negrita por tener texto duplicado en la columna del idioma seleccionado."
|
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 {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."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,9 @@ import html
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
import PyLibrary.funciones_comunes as fc
|
import PyLibrary.funciones_comunes as fc
|
||||||
import time
|
import time
|
||||||
import PyLibrary.funciones_comunes as fc
|
|
||||||
from translation_config import TranslationConfig
|
from translation_config import TranslationConfig
|
||||||
from openai import OpenAI
|
from openpyxl.styles import PatternFill, Alignment
|
||||||
from tqdm import tqdm
|
import sys
|
||||||
|
|
||||||
openai_client = OpenAI(api_key=openai_api_key())
|
openai_client = OpenAI(api_key=openai_api_key())
|
||||||
GOOGLE_APPLICATION_CREDENTIALS = "translate-431108-020c17463fbb.json"
|
GOOGLE_APPLICATION_CREDENTIALS = "translate-431108-020c17463fbb.json"
|
||||||
|
@ -105,109 +104,11 @@ def translate_batch_openai(texts_dict, source_lang, target_lang):
|
||||||
return dict(zip(texts_dict.keys(), translations))
|
return dict(zip(texts_dict.keys(), translations))
|
||||||
|
|
||||||
|
|
||||||
def affinity_batch_openai(codigo_tipo_PLC, texts_dict):
|
|
||||||
system_prompt = (
|
|
||||||
"Evaluate the semantic similarity between the following table of pairs of texts in json format on a scale from 0 to 1. "
|
|
||||||
"Return the similarity scores for every row in JSON format as a list of numbers, without any additional text or formatting."
|
|
||||||
)
|
|
||||||
original_list = [
|
|
||||||
fc.compactar_celda_traducida(codigo_tipo_PLC, key) for key in texts_dict.keys()
|
|
||||||
]
|
|
||||||
re_translated_list = list(texts_dict.values())
|
|
||||||
|
|
||||||
request_payload = json.dumps(
|
|
||||||
{"original": original_list, "compared": re_translated_list}
|
|
||||||
)
|
|
||||||
logger.info(f"Solicitando Afinidad para el lote de textos:\n{request_payload}")
|
|
||||||
|
|
||||||
response = openai_client.chat.completions.create(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": system_prompt,
|
|
||||||
},
|
|
||||||
{"role": "user", "content": request_payload},
|
|
||||||
],
|
|
||||||
max_tokens=1500,
|
|
||||||
temperature=0.3,
|
|
||||||
)
|
|
||||||
response_content = response.choices[0].message.content
|
|
||||||
|
|
||||||
# Limpiar y convertir el contenido de la respuesta
|
|
||||||
cleaned_response_content = response_content.strip().strip("'```json").strip("```")
|
|
||||||
|
|
||||||
# Intentar convertir el contenido a JSON
|
|
||||||
try:
|
|
||||||
response_payload = json.loads(cleaned_response_content)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
raise ValueError("La respuesta no se pudo decodificar como JSON.")
|
|
||||||
|
|
||||||
# Manejar diferentes formatos de respuesta
|
|
||||||
if isinstance(response_payload, dict) and "similarity_scores" in response_payload:
|
|
||||||
scores = response_payload["similarity_scores"]
|
|
||||||
elif isinstance(response_payload, list):
|
|
||||||
scores = response_payload
|
|
||||||
else:
|
|
||||||
raise ValueError("Formato de respuesta inesperado.")
|
|
||||||
|
|
||||||
logger.info(f"Respuestas recibidas:\n{scores}")
|
|
||||||
|
|
||||||
if len(scores) != len(original_list):
|
|
||||||
raise ValueError(
|
|
||||||
"La cantidad de afinidades recibidas no coincide con la cantidad de textos enviados."
|
|
||||||
)
|
|
||||||
|
|
||||||
return dict(zip(texts_dict.keys(), scores))
|
|
||||||
|
|
||||||
|
|
||||||
# Función que calcula la afinidad entre dos textos
|
|
||||||
def calcular_afinidad(tipo_PLC, texto1, texto2):
|
|
||||||
system_prompt = (
|
|
||||||
"Evaluate the semantic similarity between the following pair of texts on a scale from 0 to 1. "
|
|
||||||
"Return the similarity score as a single number."
|
|
||||||
)
|
|
||||||
|
|
||||||
original_text = fc.compactar_celda_traducida(tipo_PLC, texto1)
|
|
||||||
compared_text = texto2
|
|
||||||
|
|
||||||
request_payload = json.dumps({"original": original_text, "compared": compared_text})
|
|
||||||
logger.info(f"Solicitando afinidad para el par de textos:\n{request_payload}")
|
|
||||||
|
|
||||||
response = openai_client.chat.completions.create(
|
|
||||||
model="gpt-4o-mini",
|
|
||||||
messages=[
|
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": system_prompt,
|
|
||||||
},
|
|
||||||
{"role": "user", "content": request_payload},
|
|
||||||
],
|
|
||||||
max_tokens=1500,
|
|
||||||
temperature=0.3,
|
|
||||||
)
|
|
||||||
response_content = response.choices[0].message.content
|
|
||||||
|
|
||||||
# Limpiar y convertir el contenido de la respuesta
|
|
||||||
cleaned_response_content = response_content.strip().strip("'```json").strip("```")
|
|
||||||
|
|
||||||
# Intentar convertir el contenido a JSON
|
|
||||||
try:
|
|
||||||
score = float(cleaned_response_content)
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(
|
|
||||||
f"La respuesta no se pudo decodificar como un número: {cleaned_response_content}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return score
|
|
||||||
|
|
||||||
|
|
||||||
def main(config: TranslationConfig):
|
def main(config: TranslationConfig):
|
||||||
global logger
|
|
||||||
df = fc.read_dataframe_with_cleanup_retries(config.get_translate_path())
|
df = fc.read_dataframe_with_cleanup_retries(config.get_translate_path())
|
||||||
|
|
||||||
source_col = config.codigo_columna_maestra
|
source_col = config.codigo_columna_maestra
|
||||||
source_translated_col = config.codigo_idioma_seleccionado
|
source_translated_col = f"{config.codigo_idioma_seleccionado}_Propuesto"
|
||||||
target_col = f"{config.codigo_idioma_seleccionado} Translated"
|
target_col = f"{config.codigo_idioma_seleccionado} Translated"
|
||||||
check_translate_col = f"{config.codigo_idioma_seleccionado} CheckTranslate"
|
check_translate_col = f"{config.codigo_idioma_seleccionado} CheckTranslate"
|
||||||
affinity_col = f"{config.codigo_idioma_seleccionado} Affinity"
|
affinity_col = f"{config.codigo_idioma_seleccionado} Affinity"
|
||||||
|
@ -220,20 +121,37 @@ def main(config: TranslationConfig):
|
||||||
texts_to_translate = {}
|
texts_to_translate = {}
|
||||||
|
|
||||||
# Inicializar ProgressBar para la fase de preparación
|
# Inicializar ProgressBar para la fase de preparación
|
||||||
prep_progress = fc.ProgressBar(len(df), prefix='Preparando textos:', suffix='Completado')
|
prep_progress = fc.ProgressBar(
|
||||||
|
len(df), prefix="Preparando textos:", suffix="Completado"
|
||||||
|
)
|
||||||
|
|
||||||
for index, row in df.iterrows():
|
for index, row in df.iterrows():
|
||||||
celda_clave = str(row[source_col])
|
celda_clave = str(row[source_col])
|
||||||
source_translated_text = str(row[source_translated_col]) if source_translated_col in df.columns else ""
|
source_translated_text = (
|
||||||
celda_clave_compactada = fc.compactar_celda_traducida(config.codigo_tipo_PLC, celda_clave)
|
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 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] = ""
|
df.at[index, source_translated_col] = ""
|
||||||
texts_to_translate[celda_clave] = celda_clave_compactada
|
texts_to_translate[celda_clave] = celda_clave_compactada
|
||||||
else:
|
else:
|
||||||
if pd.isna(row[source_translated_col]) or source_translated_text.strip() == "":
|
if (
|
||||||
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):
|
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
|
texts_to_translate[celda_clave] = celda_clave_compactada
|
||||||
|
|
||||||
prep_progress.update(index + 1)
|
prep_progress.update(index + 1)
|
||||||
|
@ -245,7 +163,9 @@ def main(config: TranslationConfig):
|
||||||
print(f"\nNúmero total de textos a traducir: {num_texts}")
|
print(f"\nNúmero total de textos a traducir: {num_texts}")
|
||||||
|
|
||||||
# Inicializar ProgressBar para la fase de traducción
|
# Inicializar ProgressBar para la fase de traducción
|
||||||
trans_progress = fc.ProgressBar(num_texts, prefix='Traduciendo:', suffix='Completado')
|
trans_progress = fc.ProgressBar(
|
||||||
|
num_texts, prefix="Traduciendo:", suffix="Completado"
|
||||||
|
)
|
||||||
|
|
||||||
# Traducciones
|
# Traducciones
|
||||||
translations = {}
|
translations = {}
|
||||||
|
@ -260,18 +180,26 @@ def main(config: TranslationConfig):
|
||||||
batch_translations = translate_batch_openai(
|
batch_translations = translate_batch_openai(
|
||||||
batch_texts,
|
batch_texts,
|
||||||
fc.idiomas_idiomafromcode(config.codigo_columna_maestra),
|
fc.idiomas_idiomafromcode(config.codigo_columna_maestra),
|
||||||
fc.idiomas_idiomafromcode(config.codigo_idioma_seleccionado)
|
fc.idiomas_idiomafromcode(config.codigo_idioma_seleccionado),
|
||||||
)
|
)
|
||||||
translations.update(batch_translations)
|
translations.update(batch_translations)
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if attempt < retries - 1:
|
if attempt < retries - 1:
|
||||||
logger.warning(f"Error en el intento {attempt + 1} de traducción de celdas desde {start_idx} a {end_idx}: {e}. Reintentando...")
|
logger.warning(
|
||||||
print(f"Error en el intento {attempt + 1} de traducción de celdas desde {start_idx} a {end_idx}: {e}. Reintentando...")
|
f"Error en el intento {attempt + 1} de traducción de celdas desde {start_idx} a {end_idx}: {e}. Reintentando..."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Error en el intento {attempt + 1} de traducción de celdas desde {start_idx} a {end_idx}: {e}. Reintentando..."
|
||||||
|
)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Error en todos los intentos de traducción de celdas desde {start_idx} a {end_idx}: {e}")
|
logger.error(
|
||||||
print(f"Error en todos los intentos de traducción de celdas desde {start_idx} a {end_idx}: {e}")
|
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.update(end_idx)
|
||||||
|
|
||||||
|
@ -279,7 +207,9 @@ def main(config: TranslationConfig):
|
||||||
logger.info(f"Número total de traducciones recibidas: {len(translations)}")
|
logger.info(f"Número total de traducciones recibidas: {len(translations)}")
|
||||||
|
|
||||||
# Inicializar ProgressBar para la fase de actualización del DataFrame
|
# Inicializar ProgressBar para la fase de actualización del DataFrame
|
||||||
update_progress = fc.ProgressBar(len(df), prefix='Actualizando DataFrame:', suffix='Completado')
|
update_progress = fc.ProgressBar(
|
||||||
|
len(df), prefix="Actualizando DataFrame:", suffix="Completado"
|
||||||
|
)
|
||||||
|
|
||||||
# Actualizar el DataFrame con las traducciones y hacemos la Traduccion inversa
|
# Actualizar el DataFrame con las traducciones y hacemos la Traduccion inversa
|
||||||
for index, row in df.iterrows():
|
for index, row in df.iterrows():
|
||||||
|
@ -289,18 +219,23 @@ def main(config: TranslationConfig):
|
||||||
try:
|
try:
|
||||||
google_translation = google_translate(
|
google_translation = google_translate(
|
||||||
translations[celda_clave],
|
translations[celda_clave],
|
||||||
fc.idiomas_shortcodefromcode(config.codigo_columna_maestra)
|
fc.idiomas_shortcodefromcode(config.codigo_columna_maestra),
|
||||||
)
|
)
|
||||||
df.at[index, check_translate_col] = google_translation
|
df.at[index, check_translate_col] = google_translation
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error en la traducción de Google para el texto '{celda_clave}': {e}")
|
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, check_translate_col] = "Error en la traducción"
|
||||||
df.at[index, affinity_col] = 0.0
|
df.at[index, affinity_col] = 0.0
|
||||||
update_progress.increment()
|
update_progress.increment()
|
||||||
|
|
||||||
update_progress.finish()
|
update_progress.finish()
|
||||||
|
|
||||||
# Inicializar ProgressBar para la fase de cálculo de afinidad
|
# Inicializar ProgressBar para la fase de cálculo de afinidad
|
||||||
affinity_progress = fc.ProgressBar(num_texts, prefix='Calculando afinidad:', suffix='Completado')
|
affinity_progress = fc.ProgressBar(
|
||||||
|
num_texts, prefix="Calculando afinidad:", suffix="Completado"
|
||||||
|
)
|
||||||
|
|
||||||
# Afinidades
|
# Afinidades
|
||||||
affinities = {}
|
affinities = {}
|
||||||
|
@ -312,29 +247,50 @@ def main(config: TranslationConfig):
|
||||||
retries = 2
|
retries = 2
|
||||||
for attempt in range(retries):
|
for attempt in range(retries):
|
||||||
try:
|
try:
|
||||||
batch_affinities = affinity_batch_openai(config.codigo_tipo_PLC, batch_texts)
|
batch_affinities = fc.affinity_batch_openai(
|
||||||
|
config.codigo_tipo_PLC, batch_texts, openai_client, logger
|
||||||
|
)
|
||||||
affinities.update(batch_affinities)
|
affinities.update(batch_affinities)
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if attempt < retries - 1:
|
if attempt < retries - 1:
|
||||||
logger.warning(f"Error en el intento {attempt + 1} de Afinidad de celdas desde {start_idx} a {end_idx}: {e}. Reintentando...")
|
logger.warning(
|
||||||
print(f"Error en el intento {attempt + 1} de Afinidad de celdas desde {start_idx} a {end_idx}: {e}. Reintentando...")
|
f"Error en el intento {attempt + 1} de Afinidad de celdas desde {start_idx} a {end_idx}: {e}. Reintentando..."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Error en el intento {attempt + 1} de Afinidad de celdas desde {start_idx} a {end_idx}: {e}. Reintentando..."
|
||||||
|
)
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
else:
|
else:
|
||||||
logger.error(f"Error en todos los intentos de Afinidad de celdas desde {start_idx} a {end_idx}: {e}")
|
logger.error(
|
||||||
print(f"Error en todos los intentos de Afinidad de celdas desde {start_idx} a {end_idx}: {e}")
|
f"Error en todos los intentos de Afinidad de celdas desde {start_idx} a {end_idx}: {e}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
f"Error en todos los intentos de Afinidad de celdas desde {start_idx} a {end_idx}: {e}"
|
||||||
|
)
|
||||||
for key, value in batch_texts.items():
|
for key, value in batch_texts.items():
|
||||||
try:
|
try:
|
||||||
score = calcular_afinidad(config.codigo_tipo_PLC, key, value)
|
score = fc.calcular_afinidad(
|
||||||
|
config.codigo_tipo_PLC,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
openai_client,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
affinities[key] = score
|
affinities[key] = score
|
||||||
except Exception as ind_e:
|
except Exception as ind_e:
|
||||||
affinities[key] = "0"
|
affinities[key] = "0"
|
||||||
logger.error(f"Error en el cálculo individual de Afinidad para el texto '{key}': {ind_e}")
|
logger.error(
|
||||||
print(f"Error en el cálculo individual de Afinidad para el texto '{key}': {ind_e}")
|
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.increment()
|
||||||
|
|
||||||
affinity_progress.finish()
|
affinity_progress.finish()
|
||||||
|
|
||||||
# Actualizar el DataFrame con las Afinidades
|
# Actualizar el DataFrame con las Afinidades
|
||||||
for index, row in df.iterrows():
|
for index, row in df.iterrows():
|
||||||
celda_clave = str(row[source_col])
|
celda_clave = str(row[source_col])
|
||||||
|
@ -342,10 +298,70 @@ def main(config: TranslationConfig):
|
||||||
df.at[index, affinity_col] = affinities[celda_clave]
|
df.at[index, affinity_col] = affinities[celda_clave]
|
||||||
|
|
||||||
output_path = config.get_auto_translate_path()
|
output_path = config.get_auto_translate_path()
|
||||||
fc.save_dataframe_with_retries(df, output_path=output_path)
|
|
||||||
|
with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
|
||||||
|
df.to_excel(writer, index=False, sheet_name="Sheet1")
|
||||||
|
|
||||||
|
workbook = writer.book
|
||||||
|
worksheet = writer.sheets["Sheet1"]
|
||||||
|
# Inmovilizar paneles en A2
|
||||||
|
worksheet.freeze_panes = "A2"
|
||||||
|
|
||||||
|
# Configurar ancho de columnas basado en contenido
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
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))
|
||||||
|
# Si el texto es más largo que 50, aplicamos wrap_text
|
||||||
|
if text_length > 50:
|
||||||
|
cell.alignment = Alignment(wrap_text=True, vertical="top")
|
||||||
|
text_length = min(
|
||||||
|
50, max(len(word) for word in str(cell.value).split())
|
||||||
|
)
|
||||||
|
max_length = max(max_length, text_length)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Ajustar el ancho con un pequeño padding
|
||||||
|
adjusted_width = min(50, max_length + 2)
|
||||||
|
worksheet.column_dimensions[column].width = (
|
||||||
|
adjusted_width if adjusted_width > 8 else 8
|
||||||
|
)
|
||||||
|
|
||||||
|
# Colores para el formato condicional
|
||||||
|
light_blue = PatternFill(
|
||||||
|
start_color="ADD8E6", end_color="ADD8E6", fill_type="solid"
|
||||||
|
)
|
||||||
|
yellow = PatternFill(
|
||||||
|
start_color="FFFF00", end_color="FFFF00", fill_type="solid"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Aplicar formatos
|
||||||
|
for row in worksheet.iter_rows(min_row=2):
|
||||||
|
translated_cell = row[df.columns.get_loc(target_col)]
|
||||||
|
if translated_cell.value:
|
||||||
|
affinity_cell = row[df.columns.get_loc(affinity_col)]
|
||||||
|
try:
|
||||||
|
affinity_value = float(
|
||||||
|
affinity_cell.value if affinity_cell.value else 0
|
||||||
|
)
|
||||||
|
if affinity_value == 1:
|
||||||
|
translated_cell.fill = light_blue
|
||||||
|
elif affinity_value < 1:
|
||||||
|
translated_cell.fill = yellow
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
logger.info(f"Archivo traducido guardado en: {output_path}")
|
logger.info(f"Archivo traducido guardado en: {output_path}")
|
||||||
print(f"\nArchivo traducido guardado en: {output_path}")
|
print(f"\nArchivo traducido guardado en: {output_path}")
|
||||||
|
|
||||||
|
|
||||||
def run(config: TranslationConfig):
|
def run(config: TranslationConfig):
|
||||||
global logger
|
global logger
|
||||||
logger = fc.configurar_logger(config.work_dir)
|
logger = fc.configurar_logger(config.work_dir)
|
||||||
|
@ -353,6 +369,8 @@ def run(config: TranslationConfig):
|
||||||
print(f"\rIniciando: {script_name}\r")
|
print(f"\rIniciando: {script_name}\r")
|
||||||
main(config)
|
main(config)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import menu_pasos_traduccion
|
import menu_pasos_traduccion
|
||||||
|
|
||||||
menu_pasos_traduccion.main()
|
menu_pasos_traduccion.main()
|
|
@ -18,6 +18,7 @@ def importar_traduccion_manual(config: TranslationConfig):
|
||||||
|
|
||||||
archivo_traduccion = config.get_translate_path()
|
archivo_traduccion = config.get_translate_path()
|
||||||
master_col = config.codigo_idioma_seleccionado
|
master_col = config.codigo_idioma_seleccionado
|
||||||
|
propuesto_col = f"{config.codigo_idioma_seleccionado}_Propuesto"
|
||||||
|
|
||||||
df_maestro = fc.read_dataframe_with_cleanup_retries(archivo_maestro)
|
df_maestro = fc.read_dataframe_with_cleanup_retries(archivo_maestro)
|
||||||
df_traduccion = fc.read_dataframe_with_cleanup_retries(archivo_traduccion)
|
df_traduccion = fc.read_dataframe_with_cleanup_retries(archivo_traduccion)
|
||||||
|
@ -28,7 +29,7 @@ def importar_traduccion_manual(config: TranslationConfig):
|
||||||
for index, fila in df_traduccion.iterrows():
|
for index, fila in df_traduccion.iterrows():
|
||||||
clave = fila[df_maestro.columns[0]]
|
clave = fila[df_maestro.columns[0]]
|
||||||
if clave in df_maestro[df_maestro.columns[0]].values:
|
if clave in df_maestro[df_maestro.columns[0]].values:
|
||||||
valor_traducido = fila[master_col]
|
valor_traducido = fila[propuesto_col] # Use propuesto column
|
||||||
valor_original = df_maestro.loc[
|
valor_original = df_maestro.loc[
|
||||||
df_maestro[df_maestro.columns[0]] == clave, master_col
|
df_maestro[df_maestro.columns[0]] == clave, master_col
|
||||||
].values[0]
|
].values[0]
|
||||||
|
@ -38,6 +39,7 @@ def importar_traduccion_manual(config: TranslationConfig):
|
||||||
and valor_traducido != ""
|
and valor_traducido != ""
|
||||||
and str(valor_original) != str(valor_traducido)
|
and str(valor_original) != str(valor_traducido)
|
||||||
):
|
):
|
||||||
|
|
||||||
okToSave, Error = fc.verificar_celda_traducida(
|
okToSave, Error = fc.verificar_celda_traducida(
|
||||||
config.codigo_tipo_PLC, clave, valor_traducido
|
config.codigo_tipo_PLC, clave, valor_traducido
|
||||||
)
|
)
|
||||||
|
@ -62,7 +64,7 @@ def importar_traduccion_manual(config: TranslationConfig):
|
||||||
f"Fila {index}, Columna {master_col}: No actualizado porque: {Error}"
|
f"Fila {index}, Columna {master_col}: No actualizado porque: {Error}"
|
||||||
)
|
)
|
||||||
fila_excel = index + 2
|
fila_excel = index + 2
|
||||||
columna_excel = df_traduccion.columns.get_loc(master_col) + 1
|
columna_excel = df_traduccion.columns.get_loc(propuesto_col) + 1
|
||||||
celdas_con_errores[(fila_excel, columna_excel)] = Error
|
celdas_con_errores[(fila_excel, columna_excel)] = Error
|
||||||
|
|
||||||
fc.save_dataframe_with_retries(df_maestro, output_path=archivo_maestro)
|
fc.save_dataframe_with_retries(df_maestro, output_path=archivo_maestro)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import os
|
||||||
from translation_config import TranslationConfig
|
from translation_config import TranslationConfig
|
||||||
from openpyxl import load_workbook
|
from openpyxl import load_workbook
|
||||||
from openpyxl.styles import Font
|
from openpyxl.styles import Font
|
||||||
|
from openpyxl.styles import PatternFill, Alignment, Font
|
||||||
|
|
||||||
# Definir el logger a nivel de módulo
|
# Definir el logger a nivel de módulo
|
||||||
logger = None
|
logger = None
|
||||||
|
@ -16,6 +17,7 @@ def importar_traduccion(config: TranslationConfig):
|
||||||
return
|
return
|
||||||
|
|
||||||
master_col = config.codigo_idioma_seleccionado
|
master_col = config.codigo_idioma_seleccionado
|
||||||
|
master_propuesto_col = f"{master_col}_Propuesto"
|
||||||
translated_col = f"{config.codigo_idioma_seleccionado} Translated"
|
translated_col = f"{config.codigo_idioma_seleccionado} Translated"
|
||||||
affinity_col = f"{config.codigo_idioma_seleccionado} Affinity"
|
affinity_col = f"{config.codigo_idioma_seleccionado} Affinity"
|
||||||
|
|
||||||
|
@ -24,47 +26,106 @@ def importar_traduccion(config: TranslationConfig):
|
||||||
config.get_auto_translate_path()
|
config.get_auto_translate_path()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Caso especial: columna maestra es igual al idioma seleccionado
|
||||||
|
is_same_column = config.codigo_columna_maestra == config.codigo_idioma_seleccionado
|
||||||
|
|
||||||
|
# Si es el mismo, asegurarse que existe la columna propuesta
|
||||||
|
if is_same_column and master_propuesto_col not in df_maestro.columns:
|
||||||
|
df_maestro[master_propuesto_col] = df_maestro[master_col]
|
||||||
|
|
||||||
celdas_modificadas = {}
|
celdas_modificadas = {}
|
||||||
|
|
||||||
for index, fila in df_traduccion.iterrows():
|
if config.codigo_columna_maestra not in df_maestro.columns:
|
||||||
clave = fila[df_maestro.columns[0]]
|
print(f"Error: Columnas requeridas no encontradas en el archivo maestro")
|
||||||
if clave in df_maestro[df_maestro.columns[0]].values:
|
return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
fila[affinity_col] >= config.nivel_afinidad_minimo
|
config.codigo_columna_maestra not in df_traduccion.columns
|
||||||
and pd.notnull(fila[translated_col])
|
or translated_col not in df_traduccion.columns
|
||||||
and fila[translated_col] != ""
|
or affinity_col not in df_traduccion.columns
|
||||||
|
):
|
||||||
|
print(f"Error: Columnas requeridas no encontradas en el archivo de traducción")
|
||||||
|
return
|
||||||
|
|
||||||
|
for index, fila in df_traduccion.iterrows():
|
||||||
|
clave = fila[config.codigo_columna_maestra]
|
||||||
|
mascara = df_maestro[config.codigo_columna_maestra] == clave
|
||||||
|
|
||||||
|
if mascara.any():
|
||||||
|
if (
|
||||||
|
pd.notna(fila[affinity_col])
|
||||||
|
and fila[affinity_col] >= config.nivel_afinidad_minimo
|
||||||
|
and pd.notna(fila[translated_col])
|
||||||
):
|
):
|
||||||
valor_traducido = fila[translated_col]
|
valor_traducido = fila[translated_col]
|
||||||
valor_original = df_maestro.loc[
|
indice_maestro = df_maestro.index[mascara].tolist()[0]
|
||||||
df_maestro[df_maestro.columns[0]] == clave, master_col
|
|
||||||
].values[0]
|
# Determinar la columna a actualizar según el caso
|
||||||
|
columna_destino = master_propuesto_col if is_same_column else master_col
|
||||||
|
valor_original = df_maestro.loc[indice_maestro, columna_destino]
|
||||||
|
|
||||||
if str(valor_original) != str(valor_traducido):
|
if str(valor_original) != str(valor_traducido):
|
||||||
df_maestro.loc[
|
df_maestro.loc[indice_maestro, columna_destino] = valor_traducido
|
||||||
df_maestro[df_maestro.columns[0]] == clave, master_col
|
|
||||||
] = valor_traducido
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f'Fila {index}, Columna {translated_col}: "{valor_original}" actualizado a "{valor_traducido}"'
|
f'Fila {index}, Columna {columna_destino}: "{valor_original}" actualizado a "{valor_traducido}"'
|
||||||
)
|
)
|
||||||
|
|
||||||
fila_excel = (
|
fila_excel = indice_maestro + 2
|
||||||
df_maestro.index[
|
columna_excel = df_maestro.columns.get_loc(columna_destino) + 1
|
||||||
df_maestro[df_maestro.columns[0]] == clave
|
|
||||||
].tolist()[0]
|
|
||||||
+ 2
|
|
||||||
)
|
|
||||||
columna_excel = df_maestro.columns.get_loc(master_col) + 1
|
|
||||||
celdas_modificadas[(fila_excel, columna_excel)] = valor_traducido
|
celdas_modificadas[(fila_excel, columna_excel)] = valor_traducido
|
||||||
else :
|
|
||||||
logger.error(
|
# Guardar con formato Excel
|
||||||
f'Clave {clave} no encontrada en master.'
|
with pd.ExcelWriter(archivo_maestro, engine="openpyxl") as writer:
|
||||||
|
df_maestro.to_excel(writer, index=False, sheet_name="Sheet1")
|
||||||
|
|
||||||
|
workbook = writer.book
|
||||||
|
worksheet = writer.sheets["Sheet1"]
|
||||||
|
|
||||||
|
# Inmovilizar paneles en A2
|
||||||
|
worksheet.freeze_panes = "A2"
|
||||||
|
|
||||||
|
# Configurar ancho de columnas basado en contenido
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
from openpyxl.styles import Alignment, Font, PatternFill
|
||||||
|
|
||||||
|
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 = Alignment(wrap_text=True, vertical="top")
|
||||||
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
fc.save_dataframe_with_retries(df_maestro, output_path=archivo_maestro)
|
# Aplicar negrita y color a las celdas modificadas
|
||||||
|
light_blue = PatternFill(
|
||||||
|
start_color="ADD8E6", end_color="ADD8E6", fill_type="solid"
|
||||||
|
)
|
||||||
|
for (fila, columna), valor in celdas_modificadas.items():
|
||||||
|
celda = worksheet.cell(row=fila, column=columna)
|
||||||
|
celda.font = Font(bold=True)
|
||||||
|
celda.value = valor
|
||||||
|
celda.fill = light_blue
|
||||||
|
|
||||||
aplicar_negrita_celdas_modificadas(archivo_maestro, celdas_modificadas)
|
if celdas_modificadas:
|
||||||
|
print(
|
||||||
print(f"Traducciones importadas y archivo maestro actualizado: {archivo_maestro}.")
|
f"Se han actualizado y marcado {len(celdas_modificadas)} celdas en el archivo maestro."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("No se realizaron modificaciones en el archivo maestro.")
|
||||||
|
|
||||||
|
|
||||||
def aplicar_negrita_celdas_modificadas(archivo, celdas_modificadas):
|
def aplicar_negrita_celdas_modificadas(archivo, celdas_modificadas):
|
||||||
|
|
|
@ -16,91 +16,139 @@ def update_from_master(config: TranslationConfig, archivo_to_update):
|
||||||
print("El archivo maestro no existe.")
|
print("El archivo maestro no existe.")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(" .... ")
|
logger.info(f"Iniciando actualización en {archivo_to_update} desde el archivo maestro. Para {config.codigo_idioma_seleccionado}")
|
||||||
logger.info(
|
|
||||||
f"Iniciando actualización en {archivo_to_update} desde el archivo maestro. Para {config.codigo_idioma_seleccionado}"
|
|
||||||
)
|
|
||||||
|
|
||||||
df_maestro = fc.read_dataframe_with_cleanup_retries(archivo_maestro)
|
df_maestro = fc.read_dataframe_with_cleanup_retries(archivo_maestro)
|
||||||
df_to_update = fc.read_dataframe_with_cleanup_retries(archivo_to_update)
|
df_to_update = fc.read_dataframe_with_cleanup_retries(archivo_to_update)
|
||||||
|
|
||||||
|
# Create copy for changes tracking
|
||||||
|
df_changes = df_to_update.copy()
|
||||||
|
df_changes["Original_Value"] = ""
|
||||||
|
|
||||||
col_clave = config.codigo_columna_maestra
|
col_clave = config.codigo_columna_maestra
|
||||||
|
# Si la columna maestra es igual al idioma seleccionado, usamos la columna propuesta
|
||||||
|
is_same_column = config.codigo_columna_maestra == config.codigo_idioma_seleccionado
|
||||||
|
master_col = f"{config.codigo_idioma_seleccionado}_Propuesto" if is_same_column else config.codigo_idioma_seleccionado
|
||||||
|
|
||||||
celdas_con_errores = {}
|
if master_col not in df_maestro.columns:
|
||||||
celdas_vacias = {}
|
print(f"Error: Columna {master_col} no encontrada en el archivo maestro")
|
||||||
|
return
|
||||||
|
|
||||||
progress_bar = fc.ProgressBar(
|
celdas_modificadas = []
|
||||||
len(df_to_update), prefix="Actualizando filas:", suffix="Completado"
|
|
||||||
)
|
progress_bar = fc.ProgressBar(len(df_to_update), prefix="Actualizando filas:", suffix="Completado")
|
||||||
|
|
||||||
for index, fila in df_to_update.iterrows():
|
for index, fila in df_to_update.iterrows():
|
||||||
valor_original = fila[col_clave]
|
valor_original = fila[col_clave]
|
||||||
clave = fc.compactar_celda_clave(config.codigo_tipo_PLC, valor_original)
|
clave = fc.compactar_celda_clave(config.codigo_tipo_PLC, valor_original)
|
||||||
|
|
||||||
if not pd.isna(clave) and clave in df_maestro[col_clave].values:
|
if not pd.isna(clave) and clave in df_maestro[col_clave].values:
|
||||||
# logger.info(f"Fila {index} : Clave: {clave}")
|
indice_maestro = df_maestro.index[df_maestro[col_clave] == clave].tolist()[0]
|
||||||
|
valor_traducido_compacto = df_maestro.loc[indice_maestro, master_col]
|
||||||
indice_maestro = df_maestro.index[df_maestro[col_clave] == clave].tolist()[
|
|
||||||
0
|
|
||||||
]
|
|
||||||
valor_traducido_compacto = df_maestro.loc[
|
|
||||||
indice_maestro, config.codigo_idioma_seleccionado
|
|
||||||
]
|
|
||||||
|
|
||||||
if pd.isna(valor_traducido_compacto):
|
if pd.isna(valor_traducido_compacto):
|
||||||
celdas_vacias[indice_maestro] = "Celda vacía en el archivo maestro"
|
continue
|
||||||
logger.info(f"Fila {index}: Celda vacía en el archivo maestro")
|
|
||||||
else:
|
|
||||||
valor_traducido = fc.decompactar_celda_traducida(
|
valor_traducido = fc.decompactar_celda_traducida(
|
||||||
config.codigo_tipo_PLC,
|
config.codigo_tipo_PLC,
|
||||||
celda_original=valor_original,
|
celda_original=valor_original,
|
||||||
celda_traducida=valor_traducido_compacto,
|
celda_traducida=valor_traducido_compacto
|
||||||
)
|
)
|
||||||
if (
|
|
||||||
not pd.isna(valor_traducido)
|
if not pd.isna(valor_traducido) and fila[config.codigo_idioma_seleccionado] != valor_traducido:
|
||||||
and fila[config.codigo_idioma_seleccionado] != valor_traducido
|
|
||||||
):
|
|
||||||
okToSave, Error = fc.verificar_celda_traducida(
|
okToSave, Error = fc.verificar_celda_traducida(
|
||||||
config.codigo_tipo_PLC, clave, valor_traducido_compacto
|
config.codigo_tipo_PLC, clave, valor_traducido_compacto
|
||||||
)
|
)
|
||||||
if okToSave:
|
if okToSave:
|
||||||
|
# Store original value in changes DataFrame
|
||||||
|
df_changes.at[index, "Original_Value"] = fila[config.codigo_idioma_seleccionado]
|
||||||
|
|
||||||
|
# Update both DataFrames
|
||||||
|
df_to_update.at[index, config.codigo_idioma_seleccionado] = valor_traducido
|
||||||
|
df_changes.at[index, config.codigo_idioma_seleccionado] = valor_traducido
|
||||||
|
|
||||||
|
celdas_modificadas.append(index)
|
||||||
logger.info(f"Actualizado: Fila {index} : Clave: {clave}")
|
logger.info(f"Actualizado: Fila {index} : Clave: {clave}")
|
||||||
df_to_update.at[index, config.codigo_idioma_seleccionado] = (
|
|
||||||
valor_traducido
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
df_to_update.at[index, config.codigo_idioma_seleccionado] = (
|
|
||||||
valor_original
|
|
||||||
)
|
|
||||||
logger.error(f"No actualizado porque: {Error}")
|
|
||||||
celdas_con_errores[indice_maestro] = Error
|
|
||||||
|
|
||||||
progress_bar.increment()
|
progress_bar.increment()
|
||||||
|
|
||||||
progress_bar.finish()
|
progress_bar.finish()
|
||||||
|
|
||||||
|
# Save updated file with formatting
|
||||||
nombre, extension = os.path.splitext(archivo_to_update)
|
nombre, extension = os.path.splitext(archivo_to_update)
|
||||||
nuevo_nombre = f"{nombre}_import{extension}"
|
nuevo_nombre = f"{nombre}_import{extension}"
|
||||||
fc.save_dataframe_with_retries(df_to_update, output_path=nuevo_nombre)
|
|
||||||
|
|
||||||
marcar_celdas_con_errores(
|
with pd.ExcelWriter(nuevo_nombre, engine='openpyxl') as writer:
|
||||||
archivo_maestro,
|
df_to_update.to_excel(writer, index=False)
|
||||||
celdas_con_errores,
|
|
||||||
celdas_vacias,
|
|
||||||
config.codigo_idioma_seleccionado,
|
|
||||||
)
|
|
||||||
|
|
||||||
print(
|
workbook = writer.book
|
||||||
f"Se han actualizado las filas en {archivo_to_update} desde el archivo maestro."
|
worksheet = writer.sheets['Sheet1']
|
||||||
)
|
|
||||||
print(
|
# Format columns
|
||||||
f"Se han marcado {len(celdas_con_errores)} celdas con errores en el archivo maestro."
|
from openpyxl.utils import get_column_letter
|
||||||
)
|
from openpyxl.styles import Alignment, PatternFill
|
||||||
print(f"Se han marcado {len(celdas_vacias)} celdas vacías en el archivo maestro.")
|
|
||||||
|
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 = Alignment(wrap_text=True, vertical='top')
|
||||||
|
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
|
||||||
|
|
||||||
|
# Save changes file with highlighting
|
||||||
|
changes_nombre = f"{nombre}_changes{extension}"
|
||||||
|
if len(celdas_modificadas) > 0:
|
||||||
|
with pd.ExcelWriter(changes_nombre, engine="openpyxl") as writer:
|
||||||
|
df_changes.to_excel(writer, index=False)
|
||||||
|
|
||||||
|
workbook = writer.book
|
||||||
|
worksheet = writer.sheets["Sheet1"]
|
||||||
|
|
||||||
|
light_blue = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid")
|
||||||
|
|
||||||
|
for row_idx in celdas_modificadas:
|
||||||
|
for col in range(1, len(df_changes.columns) + 1):
|
||||||
|
cell = worksheet.cell(row=row_idx + 2, column=col)
|
||||||
|
cell.fill = light_blue
|
||||||
|
|
||||||
|
# Format columns in changes file too
|
||||||
|
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 = Alignment(wrap_text=True, vertical='top')
|
||||||
|
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
|
||||||
|
|
||||||
|
print(f"Se han actualizado las filas en {archivo_to_update} desde el archivo maestro.")
|
||||||
|
print(f"Archivo de cambios guardado en: {changes_nombre}")
|
||||||
|
print(f"Se han marcado {len(celdas_modificadas)} filas modificadas.")
|
||||||
logger.info(" .... ")
|
logger.info(" .... ")
|
||||||
|
|
||||||
|
def marcar_celdas_con_errores(
|
||||||
def marcar_celdas_con_errores(archivo_maestro, celdas_con_errores, celdas_vacias, target_lang_code):
|
archivo_maestro, celdas_con_errores, celdas_vacias, target_lang_code
|
||||||
|
):
|
||||||
workbook = load_workbook(archivo_maestro)
|
workbook = load_workbook(archivo_maestro)
|
||||||
sheet = workbook.active
|
sheet = workbook.active
|
||||||
|
|
||||||
|
@ -112,8 +160,12 @@ def marcar_celdas_con_errores(archivo_maestro, celdas_con_errores, celdas_vacias
|
||||||
print(f"No se encontró la columna para el idioma {target_lang_code}")
|
print(f"No se encontró la columna para el idioma {target_lang_code}")
|
||||||
return
|
return
|
||||||
|
|
||||||
error_fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid")
|
error_fill = PatternFill(
|
||||||
empty_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
|
start_color="FF0000", end_color="FF0000", fill_type="solid"
|
||||||
|
)
|
||||||
|
empty_fill = PatternFill(
|
||||||
|
start_color="FFFF00", end_color="FFFF00", fill_type="solid"
|
||||||
|
)
|
||||||
white_font = Font(color="FFFFFF") # Fuente blanca para celdas con fondo rojo
|
white_font = Font(color="FFFFFF") # Fuente blanca para celdas con fondo rojo
|
||||||
|
|
||||||
for indice_maestro, mensaje_error in celdas_con_errores.items():
|
for indice_maestro, mensaje_error in celdas_con_errores.items():
|
||||||
|
@ -132,7 +184,9 @@ def marcar_celdas_con_errores(archivo_maestro, celdas_con_errores, celdas_vacias
|
||||||
cell.comment = comment
|
cell.comment = comment
|
||||||
|
|
||||||
workbook.save(archivo_maestro)
|
workbook.save(archivo_maestro)
|
||||||
print(f"Se han marcado las celdas con errores y vacías en el archivo maestro {archivo_maestro}")
|
print(
|
||||||
|
f"Se han marcado las celdas con errores y vacías en el archivo maestro {archivo_maestro}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run(config: TranslationConfig, archivo_to_update):
|
def run(config: TranslationConfig, archivo_to_update):
|
||||||
|
|
Loading…
Reference in New Issue