viernes, 11 de marzo de 2022

Clase para el manejo de textos escrita en Python

Clase para el manejo de textos

Clase para el manejo de textos escrita en Python. Esta clase contiene métodos útiles que podrás utilizar en tus proyectos. Puedes usarla mediante herencia o directamente como Text.method ya que todos sus métodos están a nivel de clase.

from typing import Iterable, Union
from decimal import Decimal
import re
import unicodedata
import itertools

from fuente import number, numero_letras, exceptions


class Text(number.Number):
"""
Base class for string management.
"""

@classmethod
def NumberToLetter(self, number: Union[int, float, Decimal, str],
in_moneda: bool = True, moneda: str = "usd"):
"""Convierte un número en un texto leíble. Author: efrenfuentes.
"""
if in_moneda is True:
return numero_letras.numero_a_moneda(number, moneda=moneda)
return numero_letras.numero_a_letras(number)
@classmethod
def is_similar_string(cls, text1: str, text2: str) -> bool:
"""Determina la similitud entre dos textos. Oviando máyusculas o
minúsculas y tildes.

Args:
text1 (str): texto a comparar.
test2 (str): texto a comparar.

Raises:
TypeError: Si los parámetros text1 y text2 no son de tipo str.

Returns:
bool:
"""
if not isinstance(text1, str) and not isinstance(text2, str):
raise TypeError("text1 y text2 deben ser de tipo str")
text1 = cls.Normalize(text1, lower=True)
text2 = cls.Normalize(text2, lower=True)
return text1 == text2

@classmethod
def normalize(cls, string: str, lower: bool = True):
"""Remplaza el texto por uno similiar sin tíldes ni caracteres especiales
como eñes, ni espacios extras, ni comillas y en minuscula si es indicado.

Parameters:
string (str): texto a formatear.

lower (bool): si es True, el resultado será en minúscula.

Returns:
str:
"""
if (string is None) or (string is False) or (string is True):
return ""

string = str(string).replace("'", "").replace('"', '')

out = ''.join((c for c in unicodedata.normalize('NFD', string)
if unicodedata.category(c) != 'Mn'))

out = " ".join(out.split()).strip()
if lower:
out = out.lower()
return out

@classmethod
def get_tag(cls, text: str, combinate: bool = False,
allow: str = None) -> str:
"""Obtiene un texto pre-formateado sin tíldes, ni comillas, ni slash...,
ideal para campo de búsqueda.

Se eliminan las tíldes y se establece todo en minúsculas.

Parameters:
text (str): texto que se desea formatear.

combinate (bool): si el parámetro 'combinate' es True, retornará un
texto más extenso, como resultado de todas las posibles
combinaciones de sus palabras.

allow (str): una cadena de caracteres que le indicarán a este
método que los caracteres que en 'allow' no se especifiquen, serán
excluidos del resultado.
"""
t = cls.Normalize(text, lower=False)

if allow:
if not isinstance(allow, str):
raise exceptions.TextError("El parámetro 'allow' debe ser de "
f"tipo str, pero se indicó {type(allow)}.")

# Solo se permitirán estos caracteres.
# El resto será excluido del resultado.
regexp = "[^" + allow + "]" # Expresión Regular.
t = re.sub(regexp, "", t)

# De ninguna forma están permitidos estos caráctes que eliminaremos.
# Esto es porque podría causar problemas utilizandolo dentro de otros
# strings, consultas SQL y/o otros lenguales de programación.
t = t.replace("/", " ").replace("\\", " ").replace("'", "")
t = t.replace('"', '').replace("$", "").replace("\n", " ")

t = t.lower()
# Combinaciones.
if (combinate == True):
return cls.Permutations(t).strip()
return t

@classmethod
def __gettagsclean(cls, text: str) -> str:
if isinstance(text, (tuple, list)):
text = cls.get_tags(text, combinate=False)
return cls.normalize(text, lower=True)

@classmethod
def get_tags(cls, *args, **kwargs):
"""Obtiene una cadena de texto a partir de los valores pasados (*args).

Se eliminan las tíldes y se establece todo en minúsculas.

Keyword parameters:
*args (tuple) lista de string.

combinate (bool): si el parámetro 'combinate' es True, retornará un
texto más extenso, como resultado de todas las posibles
combinaciones de sus palabras.

comb (bool): igual a combinate.

allow (str): una cadena de caracteres que le indicarán a este
método que los caracteres que en 'allow' no se especifiquen, serán
excluidos del resultado.
"""
combinate = kwargs.get("combinate") or kwargs.get("comb", False)
allow = kwargs.get("allow")

if len(args) == 1:
args = args[0]

if isinstance(args, (list, tuple)):
args = " ".join([cls.Normalize(x) for x in args])
else:
args = cls.Normalize(args)

if (combinate):
args = cls.Permutations(args)

if allow:
if not isinstance(allow, str):
raise exceptions.TextError("El parámetro 'allow' debe ser de "
f"tipo str, pero se indicó {type(allow)}.")

# Solo se permitirán estos caracteres.
# El resto será excluido del resultado.
regexp = "[^ " + allow + "]" # Expresión Regular.
args = re.sub(regexp, "", args)

# De ninguna forma están permitidos estos caráctes que eliminaremos.
# Esto es porque podría causar problemas utilizandolo dentro de otros
# strings, consultas SQL y/o otros lenguales de programación.
args = args.replace("/", " ").replace("\\", " ").replace("'", "")
args = args.replace('"', '').replace("$", "").replace("\n", " ")

return args.strip()
@classmethod
def clean_codename(cls, text: str, replace: str = "",
lower: bool = True, allow: str = None) -> str:
"""Limpia el texto dejando solo los caracteres en el rango a-Z y 0-9.

Args:
text (str): texto a limpiar.
replace (str, optional): texto que remplazará los carácteres no
permitidos. Defaults to ''.
lower (bool, optional): si es True, la salida será minúscula.
Defaults to True.
allow (str, optional): caracteres adicionales que se desean
permitir. Defaults to None.

Returns:
str:
"""
replace = replace or ""
allow = allow or ""
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
digits = "0123456789"
allowed = letters + digits + allow
out = ""

for char in text:
if not char in allowed:
char = replace
out += char

return out.lower() if lower is True else out

@classmethod
def permutations(cls, iterable: Iterable, r: int = 2,
split: str = " ") -> str:
"""Devuelve permutaciones de longitud 'r' sucesivas de elementos en el
iterable.

Si r no se especifica o lo está None, entonces r toma por defecto la
longitud del iterable y se generan todas las posibles permutaciones de
longitud completa.

Las tuplas de permutación se emiten en orden lexicográfico según el
orden de la entrada iterable . Entonces, si la entrada iterable está
ordenada, las tuplas de combinación se producirán en orden ordenado.

Los elementos se tratan como únicos en función de su posición, no de su
valor. Entonces, si los elementos de entrada son únicos, no habrá
valores repetidos en cada permutación.

https://docs.python.org/3/library/itertools.html#itertools.permutations

Si en 'iterable' se especifica un string en vez de un iterable, se
tomará el valor del parámetro 'split' para dividir los elementos del
string.

Returns:
str: Una cadena de texto separada por el valor del parámetro 'split'.
"""
if isinstance(iterable, str):
iterable = iterable.split(split)

# Cuando r es mayor a la longitud del iterable, la función
# itertools.permutations(iterable, r), retorna una lista vacia. Evitamos
# esto ajustando el valor de r a la longitud del iterable.
if (len(iterable) < r):
r = len(iterable)

permutations = itertools.permutations(iterable, r)
return split.join([split.join(x) for x in permutations])

@classmethod
def truncatechars(cls, text: str, length: int, end: str = "...") -> str:
"""Corta el texto según la longitud indicada.

Parameters:
text (str): texto a cortar.

length (int): longitud del texto resultante, desde el inicio.

end (str): texto final que se añadirá a la cadena.

Returns:
str: text[:length] + end
"""
text = str(text)
length = int(length)
lent = len(text)
end = str(end)

if (length >= lent):
return text

if (not length):
return ""

return f"{text[:length - len(end)]}{end}"

@classmethod
def truncatecharscenter(cls, text: str, length: int) -> str:
"""Corta el texto según la longitud indicada, quitando solo el texto
central.

Ejemplo:
TruncateCharsCenter('La chispa adecuada - Heroes Del Silencio', 30)
--> 'La chispa adec/es Del Silencio'

Parameters:
text (str): texto a cortar.

length (int): longitud del texto resultante.

Returns:
str:
"""
text = str(text)
length = int(length)
lent = len(text)

if (length >= lent):
return text

if (not length):
return ""

mid = int(length / 2) # Cantidad de caracteres en ambos extremos.
res = int(lent - length) # Cantidad de caracteres que se van a suprimir.
a1, a2 = (0, mid) # texto inicial
b1, b2 = (lent - mid, lent) # texto final
# Si el espacio en blanco más próximo está cerca del corte,
# entonces cortamos por en el espacio más próximo.
if (mid >= 5):
idx = text.find(" ", a2 - 5, a2 + 5)
if (idx != -1):
a2 = idx
b1 = a2 + res + 1
else:
idx = text.find(" ", b1 - 5, b1 + 5)
if (idx != -1):
b1 = idx + 1
a2 = b1 - res - 1

t1 = text[a1: a2]
t2 = text[b1: b2]
return f"{t1}/{t2}"

@classmethod
def strip(cls, text):
"""
Elimina los espacios extras del texto indicado.

>> ' '.join(' Hello World '.split()).strip() -> 'Hello World'

"""
return " ".join(text.split()).strip()


 

No hay comentarios.:

Publicar un comentario