From 751825a65964740478643dd6a0c4b00a08ef9db2 Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 18 Nov 2024 11:31:36 +0100 Subject: [PATCH] Mejorando el control de afinidad de x2 y moviendo a funciones comunes la logica de control de afinidad --- .../menu_pasos_traduccion.cpython-310.pyc | Bin 14682 -> 14685 bytes ...x2_master_export2translate.cpython-310.pyc | Bin 3946 -> 5662 bytes .../x3_llm_auto_translate.cpython-310.pyc | Bin 9620 -> 8786 bytes ...anual_translates_to_master.cpython-310.pyc | Bin 3355 -> 3388 bytes ...grate_translates_to_master.cpython-310.pyc | Bin 2588 -> 4034 bytes .../x6_update_from_master.cpython-310.pyc | Bin 3789 -> 5170 bytes translation_config.json | 12 +- x2_master_export2translate.py | 137 +++++++-- x3_llm_auto_translate.py | 286 ++++++++++-------- x4B_integrate_manual_translates_to_master.py | 6 +- x4_integrate_translates_to_master.py | 113 +++++-- x6_update_from_master.py | 178 +++++++---- 12 files changed, 479 insertions(+), 253 deletions(-) diff --git a/__pycache__/menu_pasos_traduccion.cpython-310.pyc b/__pycache__/menu_pasos_traduccion.cpython-310.pyc index a4243fc144ec97cdfbbb4fc92c72535b1ee94222..6353381cc62b0d6e462e615434798269b33d0ff2 100644 GIT binary patch delta 70 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 delta 1887 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 KCaY>d|KxxC{fuG& delta 5133 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 delta 1119 zcmZ{iOHUL*5XZZFUOhXHU0`_{qbLzKM$i+F5QP955)+LOy+AhO&LHeAJJ2(L#$Gps zv+>YJg6`hDcpzN8`T;mPcN0H?F)>;_J{C!wq<&M?U0wC>$A^J~0Vi~v41(`%xAXMn zk<*2<9T3@IvtZFzEJpE-Mr<@G$1Pk}Q;&eBt)sdQW^k=(w$L0}dACgJv~F^ftL2PO z`cXfsXLyFI?YM01Aa(*73d~tBZl9jznF`^`Dy%G9a(&&7ou|AE&X=%yCrifJRnJ0SQjsqpJ*7LwY0vlrE~a%asRuC%b9fj9?Eb)Mz9RWgOP3ecwapEeeE5742W zmZ!ffXlE+8+&ERiDEEPg``EW%5scz~j=8=sy#Y?Rk$S4{a16TLLuDUy$Eu%=&q0

%m4)6JPX|>@&!s*v1sAm=aw)GKMe_FN_5|as1J^gd6fDv8#Xr zO$?8f*4B4Ib{*n`?uM-LBn&Hw7Oe$A%tqy?Q3|Wu8#OP{cD!oH60Hn7b_R~` zukc}!x2iZV_N+ncf-K@nWW{@{Z!VQI0dava9e#-wrB_m{c^ko*Zi&LSYg>>MQGiWs zoP!f$OTx6LyX3JY`leU&y@(A!z+ACx&%o8cvHLEkZDpJE6xp!!mgEXrOgj~eiTGrn Y&$YLgdgDTO3eG&ux;aYWQ4sa}Pu#%y00000 diff --git a/__pycache__/x4_integrate_translates_to_master.cpython-310.pyc b/__pycache__/x4_integrate_translates_to_master.cpython-310.pyc index 21db7713479fb2b87f93842606c37ec264ab3fcb..8c453ada83279a3dc0dc2076d47796d25da42439 100644 GIT binary patch literal 4034 zcmbVPOK%*<5uVrX&OW&$SENXh)QFVitYb5_;TVQ2I1%(njM$0;88J-A7>u@i$suQF zRz1CZYnke`rWk-ix@>71L9K>2F+VcCi=Sz?RTuj=ahs=uyH z>h+3&&r8-jyZ>4;jDJ$&`HzjpFY!(P0>KPsMn=!vvCOeKp1VW)uqR#fbjc1rn} z9l5>oPC3_|sM4$MRCB!my|z>9)pzQXJ~L>rA}@6*q+t$?&11Y(P?}SmL%QvEMH}3IR3J2D7v6HXwm}GnMxq|;Sd~f2L z?ju0T7#U|qW=eD48rjM`Gt;JYn0ac_k?&!AzmQqV`Vt{}Y-KjJq1(`%iS9rz$l|C( ztFvOR86)@1K&yl~Wtw+jWv+6?O=YX%e&x`FR#euC0cl@1LL;MD<;mFZ;6dRz56Y@U zYtleDGp#Vz#4hF*XLC!V>X~uN`1J6;l~uB;s*=?eBdcMbTvIi8Qdjj+?aUD0$oi;( zIfWJYD!v(Om^XJ&n2kBJF=$x>9yV2d#Td<>8LWh!Ih^V%WyhwnvL;3s)SPN|ak6Y) zE~@!c6S8q?9@wx?t0&{v4cQu-ioJloy+9 z9lo~J`ikMdjCD=5$~N$Kk$I2o?2;;vewydR>U|4YQO!29EzExfXm7GhkF4ynT7jRN zk4$yhe^qTkZmCO<+psnG7`MQ|-znl4mtTi3>6b@9X6zZFRK`jFiX7aMmsR=H1a_An z+tYkR|COoUzIu1Kemn94(b)?R6R#KWREorl6OSKGQaMavXuriag#8V}3^$@9l9eUvFEw5XOuj>q<%;ejN0;wr;#Z47rPTCqKO(aiYJr zEsC(1vz{o>25&%oq-~k>VX1s1g1#^LvDEG%7c%Sw(f3G^e4@7^rSEx&KZ5o?ErApb zh1C2#zi*#AHV5`vE;iVklaTobE@a5FB88PAP2;D|3sIs@A8-*_r<$i@`%XX`fSM|B*c1ZvRZr5!B+UWZDJdy>F0_p^ zKm^Cgt$q|rJwI8-LKGhCFp~Rt*qT*x|G7=EimdpCLKI7!$@T5)p#if=E}$?gY9? zq^Hf|(FEIRjR(>~wU;BCL-#|ZYzhT}bquWHUAZTyuoHJFrd49PNw;nUY<~b=LG_pe zQUQv4(tiM4r`e06=8odTbd7T^8;c8MZt;JRP8|d3*AT8*GhNd$i&h0%&8%3^P3Kvy z(6DQ!W7RAgt=Sc`V!9Y>VqODU&9a^~teR;(bL|C;H!#w`%r&faohB?{f1>Y*^T@0c#D@%4b*X;%7zd*|1>YnN@)PtOa{D^i@pj-y8oZzG~5)&uWf{;c|!A ze)=J%q^BbEL9A4CU&+jWV>rOq32KJn$B?Q96|T z0!qx1Uwl|q7D{0hl%^qF=EyR=At(z9%783Bo)xz3(p|BIJwFmxA&09ImYwn{UhfzW zMJVyOE>9`;xn!m1U7OqOilDbk7xCDFYQzU<vAyOgd&ZRiE7+BGXq;X`-M?amhhXxL|9ElA+^Sp7D5cr#=lqE_T6Ev z{$3n*!XReJHSb%qju36b9Pt$Wpc)8xJt10`u9?q5A>P3Z(I)XS&2=9HDbK48*Y|O8 zx$lo-M>*8C#`IN9^G80fPJS34TMXhqV{S@0Rzt2+R*OI6wtbLB82f(voWAhk$@}30 zcyzMe9mL}Y5e(HE5Q)#f&^10t5gbo+^>bLuebx(8Rs{QEIUQ3_q6_-5mnVq0MH&VYR#NVX?FWrEs Y0ed0|`fHhv-LT!m#+>V-&vO3rUtE%hdjJ3c literal 2588 zcmZ`*&1)RV74NG4n4X?b$?`hd57H<(WFS!pEG!7KY~sXZA#oPGxeN+TxvNI%QBT)w zb&V_yJ+PoXY9`P1;bkTRrido*rbQ9e)X!}d-dM?y;mc9y~x4y z{r%p_ZqIT4V<&eX7n3jWmVd{<9Zqs*M#hvhv^(}{o&cn92p81n8JRllos?~HfE&U*L8vGPyr zchq}{(>PB?#O!>nSW)IoXR>%Ci%E6@W-phFC(oridnV->7~W%9XcMIMhLl+0%`?Ea zi}w-U@?Th~oV9b~tcWI4x^^paL&~A{IJqL$m#0{t`YUS3@8R(^UAY_D1?}lz-Lf(( zzowjZc;jFeWX`Hp1?%>W^LyuS|2U?raMiBbl|PNRx9{vbtEh@9|LuI*Id{~*u>-5n z-#zoy54yMRgX8W)qxzwtxPRtu=~V~vgS%R)=!{l$Lie3%$J$wS^|0z*5saNHQcM0{ znTL)>)Ok>a@UvC97{jWIv0ZgAM%V2dL%b*aM_8sAQ61oj(ecu67j(n>CaM>#te@p8YmRRCOC zG0tL9;1DpZ4uQOmTsk6JUfv|#xpr%Nhxws8-*K&PEzwTB~TxnX*N-8 zCX(lwK26eGuwpS!l+Y>@rFme@v4xfi+^S1VG-ksLzA5>8QtbsC)HX(&C zw= zldNF*II_DFaRF`PUK8BS^q-FU74*IO3qLykO36!+YFQpXO;tA6_-0}zit_l6fBy0< z){iefO@IbOglX|MiDiP6ZU8#JH1{?z+#bt<(>BDCV+cu?gv28O?ShIZDB|6E-jMdm z9_fQiZ@V-Ee-D&Lz1KsJy58%0=6-DV`a`S)l)k1e{gK`d1IT>q(TD`MeJ`SWRQ(!; zQd?HrCj=4Tt$T6~LZb&>5p-QTCFr+gP3u1T!7)WA^;Z5WsNAVrd6mz}3%Y8dPf+f@ zK-Wn8D!>?g)2S#rGdd@_NXR|iuF%oZN&L#e7+m{r`bq1_h#D98w)chY2VdI@&SQ(>uPBEZZ3*ZIBsoFtA1gb;r41Xet9ZTuOrLdR3jT(EmDpB`am^on&A+; z)Xy{FhnNpu% zhr(YINA>OA@EI#bfv#o3Btf}|B&j1+!)QdCBJY%SVI+3nI{`py7sX$pskAWb0>VSM zudHuYO`H;1%tC5gF@nqQzNml8Go wwikKUhdW-|zM*XHP52PtT;$?&dw-Q5W9R|`w#eHq)$ diff --git a/__pycache__/x6_update_from_master.cpython-310.pyc b/__pycache__/x6_update_from_master.cpython-310.pyc index 755bedb8939af1edaa94024b12fde2668168fff2..05ab2e845d3ea0e3562adc45794b327695d7f63a 100644 GIT binary patch literal 5170 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 X$kM`V=$5Yh(yUsiOcT*YGrsyS#6ruQ delta 1565 zcmZ8hO>7%Q6yBK~uh;AKdTp=s)BMy;B2+^gR8f@@g+iNvR1OF!7gPnAt#{+Bv)*-Q z*ZI@9IYi=s2tt};70Kp?1VX@tA}$rzAaP!a3m4QI2Tni4gey%0fxSjLKs6t z51ky#vcmF+tj2OcHh(M&`oUeGI5tYl3`Ylo#Wb#%rq%@7q{NC_GCcu;#1*o(WpxY4!aO}>zOqg-5bYnygZ>)5m@g7#nJwrTMLvszf} zy#U#2O~EDN+vcpiSii`B$UWq@w24ywJsP&C8%1|)n#jHz@UFI+O)_5KxV!wZ_KuQq zDGe#t^0U)Zk-hCU96Po*sNHcJJ6^ompl(b(H{xHGrf|K=f6K3Gxn}4$nm#!5zs-fB z-aX6FxNlQB$C-Y#@P$Ng1FgSSoTYW3u;(`8Fj`-0Qm-4s-*Y=aWn8WE&kGX=AAzTe zbfhAJG)$0*$f-s&q+kLX!LT|tiHZy~fh`$>mB`==J}R=nP7sDqd8R7()PNWjso*h& zFg8zd#yB;>Tf-_gu>lwu3vWO{$Ut8a;yQ@LjBVO-01J6O^Ek}HG5`F+b|M$gz+kf7R=}d2o)*F!cao^@V^Ej$bb@Q zi2qTVCS^pga;qGmaei37l($5*DMX2XU7omG5litp)@3RlO1cOl$wz668v)#(bE%=0 zz2IYTic}Cf)}hZ(@HRIq6Us$_VDiPv_1DJ*PC15hM!p?;VF1+#HoO+a!k~s-H|Xx| z`cxKb@qcwOiuZiDrk_^k&6KL3mYx 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 + ) + + 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" ) - fill = PatternFill(start_color="ADD8E6", end_color="ADD8E6", fill_type="solid") - bold_font = Font(bold=True) - total_rows = worksheet.max_row - 1 # Excluimos la fila de encabezado + total_rows = worksheet.max_row - 1 progress_bar = fc.ProgressBar( total_rows, prefix="Procesando filas:", suffix="Completado" ) - print("Iniciando procesamiento de filas...") - texto_a_filas = defaultdict(list) + inconsistencias = 0 + afinidad_baja = 0 + for row in range(2, worksheet.max_row + 1): texto = worksheet.cell(row=row, column=2).value if texto: + # Language detection 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: texto_a_filas[texto].append(row) - # Detectar idioma y marcar si es incorrecto idioma_detectado = detectar_idioma(texto, 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 = fill + worksheet.cell(row=row, column=2).fill = blue_fill 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() - # Marcar celdas duplicadas en negrita + # Mark duplicate cells in bold + bold_font = Font(bold=True) celdas_duplicadas = 0 for filas in texto_a_filas.values(): 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." ) 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." ) diff --git a/x3_llm_auto_translate.py b/x3_llm_auto_translate.py index 8a63d3a..b4975f2 100644 --- a/x3_llm_auto_translate.py +++ b/x3_llm_auto_translate.py @@ -11,10 +11,9 @@ import html from tqdm import tqdm import PyLibrary.funciones_comunes as fc import time -import PyLibrary.funciones_comunes as fc from translation_config import TranslationConfig -from openai import OpenAI -from tqdm import tqdm +from openpyxl.styles import PatternFill, Alignment +import sys openai_client = OpenAI(api_key=openai_api_key()) 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)) -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): - global logger df = fc.read_dataframe_with_cleanup_retries(config.get_translate_path()) 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" check_translate_col = f"{config.codigo_idioma_seleccionado} CheckTranslate" affinity_col = f"{config.codigo_idioma_seleccionado} Affinity" @@ -220,22 +121,39 @@ 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') - + 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() @@ -245,14 +163,16 @@ def main(config: TranslationConfig): print(f"\nNúmero total de textos a traducir: {num_texts}") # 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 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}.") + logger.info(f"Traduciendo: celdas desde {start_idx} a {end_idx}.") retries = 4 for attempt in range(retries): @@ -260,26 +180,36 @@ def main(config: TranslationConfig): batch_translations = translate_batch_openai( batch_texts, 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) break except Exception as e: 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...") - print(f"Error en el intento {attempt + 1} de traducción de celdas desde {start_idx} a {end_idx}: {e}. Reintentando...") + logger.warning( + 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) 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}") - + 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') + 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 df.iterrows(): @@ -289,52 +219,78 @@ def main(config: TranslationConfig): try: google_translation = google_translate( 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 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, 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') + 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}.") + logger.info(f"Afinidad: celdas desde {start_idx} a {end_idx}.") retries = 2 for attempt in range(retries): 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) break except Exception as e: 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...") - print(f"Error en el intento {attempt + 1} de Afinidad de celdas desde {start_idx} a {end_idx}: {e}. Reintentando...") + logger.warning( + 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) else: - logger.error(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}") + logger.error( + 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(): 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 except Exception as ind_e: 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}") - + 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]) @@ -342,10 +298,70 @@ def main(config: TranslationConfig): df.at[index, affinity_col] = affinities[celda_clave] 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}") print(f"\nArchivo traducido guardado en: {output_path}") + def run(config: TranslationConfig): global logger logger = fc.configurar_logger(config.work_dir) @@ -353,6 +369,8 @@ def run(config: TranslationConfig): print(f"\rIniciando: {script_name}\r") main(config) + if __name__ == "__main__": import menu_pasos_traduccion - menu_pasos_traduccion.main() \ No newline at end of file + + menu_pasos_traduccion.main() diff --git a/x4B_integrate_manual_translates_to_master.py b/x4B_integrate_manual_translates_to_master.py index fdd6a08..03ef62f 100644 --- a/x4B_integrate_manual_translates_to_master.py +++ b/x4B_integrate_manual_translates_to_master.py @@ -18,6 +18,7 @@ def importar_traduccion_manual(config: TranslationConfig): archivo_traduccion = config.get_translate_path() 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_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(): clave = fila[df_maestro.columns[0]] 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[ df_maestro[df_maestro.columns[0]] == clave, master_col ].values[0] @@ -38,6 +39,7 @@ def importar_traduccion_manual(config: TranslationConfig): and valor_traducido != "" and str(valor_original) != str(valor_traducido) ): + okToSave, Error = fc.verificar_celda_traducida( 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}" ) 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 fc.save_dataframe_with_retries(df_maestro, output_path=archivo_maestro) diff --git a/x4_integrate_translates_to_master.py b/x4_integrate_translates_to_master.py index 17caf09..3b405dc 100644 --- a/x4_integrate_translates_to_master.py +++ b/x4_integrate_translates_to_master.py @@ -4,6 +4,7 @@ import os from translation_config import TranslationConfig from openpyxl import load_workbook from openpyxl.styles import Font +from openpyxl.styles import PatternFill, Alignment, Font # Definir el logger a nivel de módulo logger = None @@ -16,6 +17,7 @@ def importar_traduccion(config: TranslationConfig): return master_col = config.codigo_idioma_seleccionado + master_propuesto_col = f"{master_col}_Propuesto" translated_col = f"{config.codigo_idioma_seleccionado} Translated" affinity_col = f"{config.codigo_idioma_seleccionado} Affinity" @@ -24,47 +26,106 @@ def importar_traduccion(config: TranslationConfig): 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 = {} + if config.codigo_columna_maestra not in df_maestro.columns: + print(f"Error: Columnas requeridas no encontradas en el archivo maestro") + return + + if ( + config.codigo_columna_maestra not in df_traduccion.columns + or translated_col not in df_traduccion.columns + 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[df_maestro.columns[0]] - if clave in df_maestro[df_maestro.columns[0]].values: + clave = fila[config.codigo_columna_maestra] + mascara = df_maestro[config.codigo_columna_maestra] == clave + + if mascara.any(): if ( - fila[affinity_col] >= config.nivel_afinidad_minimo - and pd.notnull(fila[translated_col]) - and fila[translated_col] != "" + 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_original = df_maestro.loc[ - df_maestro[df_maestro.columns[0]] == clave, master_col - ].values[0] + indice_maestro = df_maestro.index[mascara].tolist()[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): - df_maestro.loc[ - df_maestro[df_maestro.columns[0]] == clave, master_col - ] = valor_traducido + df_maestro.loc[indice_maestro, columna_destino] = valor_traducido 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 = ( - df_maestro.index[ - df_maestro[df_maestro.columns[0]] == clave - ].tolist()[0] - + 2 - ) - columna_excel = df_maestro.columns.get_loc(master_col) + 1 + fila_excel = indice_maestro + 2 + columna_excel = df_maestro.columns.get_loc(columna_destino) + 1 celdas_modificadas[(fila_excel, columna_excel)] = valor_traducido - else : - logger.error( - f'Clave {clave} no encontrada en master.' + + # Guardar con formato Excel + 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) - - print(f"Traducciones importadas y archivo maestro actualizado: {archivo_maestro}.") + if celdas_modificadas: + print( + 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): diff --git a/x6_update_from_master.py b/x6_update_from_master.py index ec5cc02..e376a7d 100644 --- a/x6_update_from_master.py +++ b/x6_update_from_master.py @@ -16,91 +16,139 @@ def update_from_master(config: TranslationConfig, archivo_to_update): print("El archivo maestro no existe.") 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_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 + # 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 + + if master_col not in df_maestro.columns: + print(f"Error: Columna {master_col} no encontrada en el archivo maestro") + return + + celdas_modificadas = [] - celdas_con_errores = {} - celdas_vacias = {} - - progress_bar = fc.ProgressBar( - 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(): valor_original = fila[col_clave] clave = fc.compactar_celda_clave(config.codigo_tipo_PLC, valor_original) 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, config.codigo_idioma_seleccionado - ] + indice_maestro = df_maestro.index[df_maestro[col_clave] == clave].tolist()[0] + valor_traducido_compacto = df_maestro.loc[indice_maestro, master_col] if pd.isna(valor_traducido_compacto): - celdas_vacias[indice_maestro] = "Celda vacía en el archivo maestro" - logger.info(f"Fila {index}: Celda vacía en el archivo maestro") - else: - valor_traducido = fc.decompactar_celda_traducida( - config.codigo_tipo_PLC, - celda_original=valor_original, - celda_traducida=valor_traducido_compacto, + continue + + valor_traducido = fc.decompactar_celda_traducida( + config.codigo_tipo_PLC, + celda_original=valor_original, + celda_traducida=valor_traducido_compacto + ) + + if not pd.isna(valor_traducido) and fila[config.codigo_idioma_seleccionado] != valor_traducido: + okToSave, Error = fc.verificar_celda_traducida( + config.codigo_tipo_PLC, clave, valor_traducido_compacto ) - if ( - not pd.isna(valor_traducido) - and fila[config.codigo_idioma_seleccionado] != valor_traducido - ): - okToSave, Error = fc.verificar_celda_traducida( - config.codigo_tipo_PLC, clave, valor_traducido_compacto - ) - if okToSave: - 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 + 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}") progress_bar.increment() progress_bar.finish() + # Save updated file with formatting nombre, extension = os.path.splitext(archivo_to_update) nuevo_nombre = f"{nombre}_import{extension}" - fc.save_dataframe_with_retries(df_to_update, output_path=nuevo_nombre) + + with pd.ExcelWriter(nuevo_nombre, engine='openpyxl') as writer: + df_to_update.to_excel(writer, index=False) + + workbook = writer.book + worksheet = writer.sheets['Sheet1'] + + # Format columns + from openpyxl.utils import get_column_letter + from openpyxl.styles import Alignment, 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 - marcar_celdas_con_errores( - archivo_maestro, - celdas_con_errores, - celdas_vacias, - config.codigo_idioma_seleccionado, - ) + # 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) - print( - f"Se han actualizado las filas en {archivo_to_update} desde el archivo maestro." - ) - print( - f"Se han marcado {len(celdas_con_errores)} celdas con errores en el archivo maestro." - ) - print(f"Se han marcado {len(celdas_vacias)} celdas vacías en el archivo maestro.") + 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(" .... ") - -def marcar_celdas_con_errores(archivo_maestro, celdas_con_errores, celdas_vacias, target_lang_code): +def marcar_celdas_con_errores( + archivo_maestro, celdas_con_errores, celdas_vacias, target_lang_code +): workbook = load_workbook(archivo_maestro) 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}") return - error_fill = PatternFill(start_color="FF0000", end_color="FF0000", fill_type="solid") - empty_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid") + error_fill = PatternFill( + 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 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 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):