PyLibrary/funciones_comunes/funciones_base.py

525 lines
20 KiB
Python
Raw Normal View History

2024-10-08 12:34:42 -03:00
import re
import time
import pandas as pd
from openpyxl import load_workbook
import logging
import os
from openpyxl.utils.escape import unescape
# Diccionario de idiomas
IDIOMAS = {
0: ("Italian", "it-IT"),
1: ("English", "en-GB"),
2: ("Portuguese", "pt-PT"),
3: ("Spanish", "es-ES"),
4: ("Russian", "ru-RU"),
5: ("French", "fr-FR"),
6: ("German", "de-DE"),
7: ("English_US", "en-US"),
}
# Función para obtener el shortcode a partir del código completo
def idiomas_shortcodefromcode(code):
if code:
return code.split('-')[0]
return None # O puedes lanzar una excepción si prefieres
# Función para obtener el idioma a partir del código
def idiomas_idiomafromcode(code):
for idx, (idioma, codigo) in IDIOMAS.items():
if codigo == code:
return idioma
return None # O puedes lanzar una excepción si prefieres
# Función para obtener el código a partir del idioma
def idiomas_codefromidioma(idioma):
for idx, (nombre_idioma, codigo) in IDIOMAS.items():
if nombre_idioma == idioma:
return codigo
return None # O lanzar una excepción
# Función para verificar si un código existe
def idiomas_existecode(code):
return any(codigo == code for idx, (idioma, codigo) in IDIOMAS.items())
# Función para verificar si un idioma existe
def idiomas_existeidioma(idioma):
return any(nombre_idioma == idioma for idx, (nombre_idioma, codigo) in IDIOMAS.items())
# Función para obtener el idioma a partir del índice
def idiomas_idiomafromindex(index):
if index in IDIOMAS:
return IDIOMAS[index][0]
else:
return None # O lanzar una excepción
# Función para obtener el código a partir del índice
def idiomas_codefromindex(index):
if index in IDIOMAS:
return IDIOMAS[index][1]
else:
return None # O lanzar una excepción
# Función para obtener el índice a partir del código
def idiomas_indexfromcode(code):
for idx, (idioma, codigo) in IDIOMAS.items():
if codigo == code:
return idx
return None # O lanzar una excepción
# Función para obtener el índice a partir del idioma
def idiomas_indexfromidioma(idioma):
for idx, (nombre_idioma, codigo) in IDIOMAS.items():
if nombre_idioma == idioma:
return idx
return None # O lanzar una excepción
def mostrar_idiomas():
print("Selecciona el idioma de destino:")
for numero, (nombre, _) in IDIOMAS.items():
print(f"{numero}: {nombre}")
def configurar_logger():
logger = logging.getLogger("translate_logger")
# Si el logger ya tiene handlers, asumimos que ya está configurado y lo devolvemos
if logger.handlers:
return logger
logger.setLevel(logging.DEBUG)
os.makedirs(".\\logs", exist_ok=True)
fh = logging.FileHandler(".\\logs\\translate_log.log", encoding="utf-8")
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s : ")
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
def limpiar_caracteres_especiales(texto):
if isinstance(texto, str):
return unescape(texto)
return texto
def read_excel_with_cleanup(file_path, **kwargs):
df = pd.read_excel(file_path, **kwargs)
for col in df.columns:
df[col] = df[col].apply(lambda x: limpiar_caracteres_especiales(x) if pd.notna(x) else x)
return df
def read_dataframe_with_cleanup_retries(file_path, max_retries=5, retry_delay=5, **kwargs):
"""
Lee un archivo Excel con limpieza de caracteres especiales, reintentando si el archivo está en uso.
:param file_path: La ruta del archivo Excel a leer.
:param max_retries: El número máximo de reintentos en caso de error.
:param retry_delay: El tiempo de espera (en segundos) entre cada reintento.
:param kwargs: Argumentos adicionales para pd.read_excel.
:return: DataFrame con los datos limpios.
"""
retries = 0
while retries < max_retries:
try:
df = pd.read_excel(file_path, **kwargs)
# Aplicar limpieza de caracteres especiales
for col in df.columns:
df[col] = df[col].apply(lambda x: limpiar_caracteres_especiales(x) if pd.notna(x) else x)
print(f"Archivo leído y limpiado exitosamente: {file_path}")
return df
except PermissionError as e:
print(f"Error de permiso: {e}. Por favor cierre el archivo. Reintentando en {retry_delay} segundos...")
retries += 1
time.sleep(retry_delay)
except Exception as e:
print(f"Error inesperado: {e}. Reintentando en {retry_delay} segundos...")
retries += 1
time.sleep(retry_delay)
raise Exception(f"No se pudo leer el archivo después de {max_retries} intentos.")
#
# Salvar archivo Excel controlando que no este abierto. Sino espera.
#
def save_dataframe_with_retries(df, output_path, max_retries=5, retry_delay=5):
"""
Guarda un DataFrame en un archivo Excel, reintentando si el archivo está en uso.
:param df: El DataFrame a guardar.
:param output_path: La ruta del archivo donde se guardará el DataFrame.
:param max_retries: El número máximo de reintentos en caso de error.
:param retry_delay: El tiempo de espera (en segundos) entre cada reintento.
"""
retries = 0
while retries < max_retries:
try:
df.to_excel(output_path, sheet_name="User Texts", index=False)
print("Archivo guardado exitosamente.")
return
except PermissionError as e:
print(
f"Error de permiso: {e}. Por favor cierre el archivo. Reintentando en {retry_delay} segundos..."
)
retries += 1
time.sleep(retry_delay)
print(f"No se pudo guardar el archivo después de {max_retries} intentos.")
def cambiar_nombre_hoja(archivo_excel, nombre_hoja_actual, nombre_hoja_nuevo):
# Cargar el archivo Excel existente
libro = load_workbook(archivo_excel)
# Verificar si la hoja existe en el archivo
if nombre_hoja_actual in libro.sheetnames:
# Obtener la hoja actual
hoja = libro[nombre_hoja_actual]
# Cambiar el nombre de la hoja
hoja.title = nombre_hoja_nuevo
# Guardar los cambios en el archivo Excel
libro.save(archivo_excel)
print(
f"El nombre de la hoja ha sido cambiado de '{nombre_hoja_actual}' a '{nombre_hoja_nuevo}'."
)
else:
print(f"La hoja '{nombre_hoja_actual}' no existe en el archivo.")
# Verificar si la columna es del tipo "xx-YY" usando una expresión regular
def es_columna_tipo_xxYY(columna):
# Verificar si la columna es del tipo "xx-YY" usando una expresión regular
return bool(re.match(r"^[a-z]{2}-[A-Z]{2}$", columna))
def compactar_celda_clave(tipo_PLC, celda_original):
if tipo_PLC == "siemens" :
return compactar_celda_clave_siemens(celda_original)
else :
return compactar_celda_clave_ab(celda_original)
def compactar_celda_traducida(tipo_PLC, celda_traducida):
if tipo_PLC == "siemens" :
return compactar_celda_traducida_siemens(celda_traducida)
else :
return compactar_celda_traducida_ab(celda_traducida)
def obtener_digitos_celda_original(tipo_PLC, celda_original):
if tipo_PLC == "siemens" :
return obtener_digitos_celda_original_siemens(celda_original)
else :
return obtener_digitos_celda_original_ab(celda_original)
def decompactar_celda_traducida(tipo_PLC, celda_original, celda_traducida):
if tipo_PLC == "siemens" :
return decompactar_celda_traducida_siemens(celda_original, celda_traducida)
else :
return decompactar_celda_traducida_ab(celda_original, celda_traducida)
def verificar_celda_traducida(tipo_PLC, celda_clave, celda_traducida):
if tipo_PLC == "siemens" :
return verificar_celda_traducida_siemens(celda_clave, celda_traducida)
else :
return verificar_celda_traducida_ab(celda_clave, celda_traducida)
def texto_requiere_traduccion(tipo_PLC, texto, logger):
if tipo_PLC == "siemens" :
return texto_requiere_traduccion_siemens(texto, logger)
else :
return texto_requiere_traduccion_ab(texto, logger)
def texto_con_campos_especiales(tipo_PLC, texto):
if tipo_PLC == "siemens" :
return texto_con_campos_especiales_siemens(texto)
else :
return texto_con_campos_especiales_ab(texto)
# SIEMENS
#
# Transforma: "A271/47/6 Air - M<field ref="0" /> - Necessaria Manutenzione Filtro" ->
# "A[[digits]]/[[digits]]/[[digits]] Air - M<field ref="[[digits]]" /> - Necessaria Manutenzione Filtro"
#
# Este procesamiento se aplica a las celdas clave
def compactar_celda_clave_siemens(celda_original):
if pd.isnull(celda_original):
return celda_original
def reemplazar(match):
if match.group(1): # Si hay contenido dentro de <>
return f"<{match.group(1)}>"
return "[[digits]]"
# Reemplaza dígitos fuera de <> con [[digits]], y preserva el contenido dentro de <>
return re.sub(r"<(.*?)>|\d+", reemplazar, str(celda_original))
# SIEMENS
def texto_requiere_traduccion_siemens(texto, logger):
palabras = re.findall(r"\b\w{4,}\b", texto)
campos_especiales = re.findall(r"<.*?>", texto)
requiere_traduccion = len(palabras) > 0 or len(campos_especiales) != len(
re.findall(r"<#>", texto)
)
logger.debug(
f"Decisión de traducción para texto '{texto}': {'' if requiere_traduccion else 'No'} (palabras > 3 letras: {len(palabras) > 0}, solo campos especiales: {len(campos_especiales) == len(re.findall(r'<#>', texto))})"
)
return requiere_traduccion
# SIEMENS
def texto_con_campos_especiales_siemens(texto):
campos_especiales = len(re.findall(r"<#>", texto) )
return campos_especiales > 0
# SIEMENS
#
# Transforma: "A[[digits]]/[[digits]]/[[digits]] Air - M<field ref="[[digits]]" /> - Necessaria Manutenzione Filtro" ->
# "A<>/<>/<> Air - M<#>" /> - Necessaria Manutenzione Filtro"
#
# Este procesamiento se aplica a las celdas traducidas
def compactar_celda_traducida_siemens(celda_traducida):
if pd.isnull(celda_traducida):
return celda_traducida
celda_traducida = compactar_celda_clave_siemens(celda_traducida)
def reemplazar(match):
if match.group(1): # Si hay contenido dentro de <>
return "<#>"
return "<>"
# Reemplaza <...> con <#> y [[digits]] con <>
return re.sub(r"<(.*?)>|\[\[digits\]\]", reemplazar, str(celda_traducida))
# SIEMENS
# de "A271/47/6 Air - M<field ref="0" /> - Necessaria Manutenzione Filtro" -> [271,47,6]
# Obtener la secuencias de dígitos por [[digits]]
def obtener_digitos_celda_original_siemens(celda_original):
if pd.isnull(celda_original):
return []
# Primero, reemplazamos temporalmente el contenido de los tags con un marcador
texto_sin_tags = re.sub(r'<[^>]*>', '<<TAG>>', str(celda_original))
# Ahora buscamos los dígitos
digitos = re.findall(r'\d+', texto_sin_tags)
return digitos
# SIEMENS
# Original Traducida
# Transforma: "A271/47/6 Air - M<field ref="0" /> - Necessaria Manutenzione Filtro" , "A<>/<>/<> Air - M<#> - Filter Maintenance Required" ->
# "A271/47/6 Air - M<field ref="0" /> - Necessaria Manutenzione Filtro"
#
# Este procesamiento se aplica a las celdas traducidas para regresar al valor original
def decompactar_celda_traducida_siemens(celda_original, celda_traducida):
digitos = obtener_digitos_celda_original_siemens(celda_original)
celda_destino = celda_traducida
# Replace <> with digits
for d in digitos:
celda_destino = celda_destino.replace("<>", d, 1)
# Replace <#> with original content within <>
original_tags = re.findall(r"<.*?>", celda_original)
translated_tags = re.findall(r"<#>", celda_destino)
for orig, trans in zip(original_tags, translated_tags):
celda_destino = celda_destino.replace(trans, orig, 1)
return celda_destino
#
# SIEMENS
#
def verificar_celda_traducida_siemens(celda_clave, celda_traducida):
# Contar los placeholders de dígitos
digitos_clave = celda_clave.count("[[digits]]")
digitos_traducida = celda_traducida.count("<>")
# Contar los placeholders de tags
tags_clave = sum(1 for tag in re.findall(r"<.*?>", celda_clave) if tag != "[[digits]]")
tags_traducida = celda_traducida.count("<#>")
# Verificar si las cantidades coinciden
if digitos_clave == digitos_traducida and tags_clave == tags_traducida:
return True , ""
else:
text_error = f"Error de verificación:" + f" - Celda clave: {celda_clave}" + f" - Celda traducida: {celda_traducida}"
text_error += f" - Dígitos en clave: {digitos_clave}, Dígitos en traducida: {digitos_traducida}"
text_error += f" - Tags en clave: {tags_clave}, Tags en traducida: {tags_traducida}"
return False, text_error
# ALLEN BRADLEY
def texto_requiere_traduccion_ab(texto, logger):
palabras = re.findall(r"\b\w{4,}\b", texto)
campos_especiales = re.findall(r"/\*.*?\*/", texto)
total_palabras_largas = len(palabras) > 0
total_campos_especiales = len(campos_especiales)
total_campos_marcados = len(re.findall(r"/\*#\*/", texto))
solo_campos_especiales = total_campos_especiales == total_campos_marcados
requiere_traduccion = total_palabras_largas or not solo_campos_especiales
logger.debug(
f"Decisión de traducción para texto '{texto}': {'' if requiere_traduccion else 'No'} "
f"(palabras > 3 letras: {total_palabras_largas}, "
f"solo campos especiales: {solo_campos_especiales})"
)
return requiere_traduccion
# ALLEN BRADLEY
# Transforma: "A271/47/6 Air - M/*field ref="0" */ - Necessaria Manutenzione Filtro" ->
# "A[[digits]]/[[digits]]/[[digits]] Air - M/*field ref="[[digits]]" */ - Necessaria Manutenzione Filtro"
#
# Este procesamiento se aplica a las celdas clave
def compactar_celda_clave_ab(celda_original):
if pd.isnull(celda_original):
return celda_original
def reemplazar(match):
if match.group(1): # Si hay contenido dentro de /*...*/
return f"/*{match.group(1)}*/"
return "[[digits]]"
# Reemplaza dígitos fuera de /*...*/ con [[digits]], y preserva el contenido dentro de /*...*/
return re.sub(r"/\*(.*?)\*/|\d+", reemplazar, str(celda_original))
# ALLEN BRADLEY
# Transforma: "A[[digits]]/[[digits]]/[[digits]] Air - M/*field ref="[[digits]]" */ - Necessaria Manutenzione Filtro" ->
# "A<>/<>/<> Air - M/*#*/ - Necessaria Manutenzione Filtro"
#
# Este procesamiento se aplica a las celdas traducidas
def compactar_celda_traducida_ab(celda_traducida):
if pd.isnull(celda_traducida):
return celda_traducida
celda_traducida = compactar_celda_clave_ab(celda_traducida)
def reemplazar(match):
if match.group(1): # Si hay contenido dentro de /*...*/
return "/*#*/"
return "<>"
# Reemplaza /*...*/ con /*#*/ y [[digits]] con <>
return re.sub(r"/\*(.*?)\*/|\[\[digits\]\]", reemplazar, str(celda_traducida))
# ALLEN BRADLEY
# De "A271/47/6 Air - M/*field ref="0" */ - Necessaria Manutenzione Filtro" -> [271,47,6]
# Obtener las secuencias de dígitos para [[digits]]
def obtener_digitos_celda_original_ab(celda_original):
if pd.isnull(celda_original):
return []
# Reemplazamos temporalmente el contenido de los comentarios con un marcador
texto_sin_tags = re.sub(r'/\*[^*]*\*/', '<<TAG>>', str(celda_original))
# Ahora buscamos los dígitos
digitos = re.findall(r'\d+', texto_sin_tags)
return digitos
# ALLEN BRADLEY
# Transformación para regresar al valor original
def decompactar_celda_traducida_ab(celda_original, celda_traducida):
digitos = obtener_digitos_celda_original_ab(celda_original)
celda_destino = celda_traducida
# Reemplaza <> con dígitos
for d in digitos:
celda_destino = celda_destino.replace("<>", d, 1)
# Reemplaza /*#*/ con el contenido original dentro de /*...*/
original_tags = re.findall(r"/\*.*?\*/", celda_original)
translated_tags = re.findall(r"/\*#\*/", celda_destino)
for orig, trans in zip(original_tags, translated_tags):
celda_destino = celda_destino.replace(trans, orig, 1)
return celda_destino
#
# ALLEN BRADLEY
#
def verificar_celda_traducida_ab(celda_clave, celda_traducida):
# Contar los placeholders de dígitos
digitos_clave = celda_clave.count("[[digits]]")
digitos_traducida = celda_traducida.count("<>")
# Contar los placeholders de comentarios
tags_clave = sum(1 for tag in re.findall(r"/\*.*?\*/", celda_clave) if tag != "[[digits]]")
tags_traducida = celda_traducida.count("/*#*/")
# Verificar si las cantidades coinciden
if digitos_clave == digitos_traducida and tags_clave == tags_traducida:
return True, ""
else:
text_error = f"Error de verificación:" + f" - Celda clave: {celda_clave}" + f" - Celda traducida: {celda_traducida}"
text_error += f" - Dígitos en clave: {digitos_clave}, Dígitos en traducida: {digitos_traducida}"
text_error += f" - Comentarios en clave: {tags_clave}, Comentarios en traducida: {tags_traducida}"
return False, text_error
#
# ALLEN BRADLEY
#
def texto_con_campos_especiales_ab(texto):
campos_especiales = len(re.findall(r"/\*#\*/", texto))
return campos_especiales > 0
if __name__ == "__main__":
# SIEMENS
print("****************** SIEMENS ***************************")
celda_original = 'A271/47/6 Air - M<field ref="0" /> - Necessaria Manutenzione Filtro'
celda_original = 'DB<field ref="0" />/DB<field ref="1" />/DB<field ref="2" />'
celda_original = 'Text<field ref="0" />'
celda_clave = compactar_celda_clave_siemens(celda_original)
celda_tradc = compactar_celda_traducida_siemens(celda_original) + " TEXTO "
print()
print("Celda Original : " +celda_original)
print("Celda Clave : " + celda_clave)
print("Celda Traducida: " + celda_tradc)
print("Digitos : " + ','.join(obtener_digitos_celda_original_siemens(celda_original)))
print("Celda : " + decompactar_celda_traducida_siemens(celda_original, celda_tradc))
print("Celda Original : " + celda_original)
print(verificar_celda_traducida_siemens(celda_clave=celda_clave, celda_traducida= celda_tradc))
# ALLEN BRADLEY
print("****************** ALLEN BRADLEY ***************************")
celda_original = 'A271/47/6 /*N:4 {#1.#4.VFix[1]} NOFILL DP:1*/ m/min SPEED'
celda_original = 'Formats_x000D_Comparing'
celda_clave = compactar_celda_clave_ab(celda_original)
celda_tradc = compactar_celda_traducida_ab(celda_original) + " TEXTO "
celda_tradc = 'Formatos_x<>D_Comparación'
print()
print("Celda Original : " +celda_original)
print("Celda Clave : " + celda_clave)
print("Celda Traducida: " + celda_tradc)
print("Digitos : " + ','.join(obtener_digitos_celda_original_ab(celda_original)))
print("Celda : " + decompactar_celda_traducida_ab(celda_original, celda_tradc))
print("Celda Original : " + celda_original)
print(verificar_celda_traducida_ab(celda_clave=celda_clave, celda_traducida= celda_tradc))