diff --git a/pattern_detection/data/gloss_fragments.json b/pattern_detection/data/gloss_fragments.json new file mode 100644 index 0000000..09f9cd5 --- /dev/null +++ b/pattern_detection/data/gloss_fragments.json @@ -0,0 +1,173 @@ +[ + { + "pattern": "(?P__TERM__)(?P, es decir,)", + "term": "inteligencia artificial", + "fragment": "En el sector tecnológico, la inteligencia artificial, es decir, un conjunto de técnicas y algoritmos que simulan el razonamiento humano, está transformando industrias completas al permitir automatizar procesos complejos y extraer conocimientos de grandes volúmenes de datos para mejorar la toma de decisiones." + }, + { + "pattern": "(?Pes decir, )(?P__TERM__)", + "term": "arquitectura orientada a servicios", + "fragment": "La empresa migró su plataforma monolítica a microservicios, es decir, arquitectura orientada a servicios que descompone la aplicación en componentes independientes y desplegables de forma autónoma, lo que facilita las actualizaciones continuas y la escalabilidad en la nube." + }, + { + "pattern": "(?P__TERM__)(?P o sea)", + "term": "aprendizaje profundo", + "fragment": "El aprendizaje profundo o sea la rama del aprendizaje automático que utiliza redes neuronales con múltiples capas para modelar representaciones abstractas de datos se ha consolidado como la técnica más efectiva para tareas de visión por computadora y procesamiento de lenguaje natural." + }, + { + "pattern": "(?Po sea, )(?P__TERM__)", + "term": "blockchain", + "fragment": "El sistema garantiza la inmutabilidad y la transparencia de las transacciones, o sea, blockchain como tecnología de contabilidad distribuida revolucionaria que elimina la necesidad de intermediarios al registrar cada operación en un libro mayor compartido entre múltiples nodos." + }, + { + "pattern": "(?P__TERM__)(?P.{0,5} o )", + "term": "modelo predictivo", + "fragment": "El modelo predictivo bien o mal funciona según la calidad de los datos y las variables consideradas, lo cual afecta la precisión de los resultados obtenidos y la robustez de las predicciones en escenarios reales de negocio." + }, + { + "pattern": "(?P__TERM__)(?P, en otras palabras)", + "term": "transformación digital", + "fragment": "La tendencia hacia la transformación digital, en otras palabras, la integración de tecnologías digitales en todos los aspectos de una organización, ha permitido mejorar la eficiencia operativa, optimizar procesos internos y ofrecer nuevas experiencias personalizadas a los clientes." + }, + { + "pattern": "(?Pen otras palabras, )(?P__TERM__)", + "term": "innovación abierta", + "fragment": "Para afrontar los retos del mercado, muchas organizaciones adoptan prácticas colaborativas, en otras palabras, innovación abierta que implica compartir recursos y conocimientos con socios externos para acelerar la creación de valor y explorar nuevas oportunidades de negocio." + }, + { + "pattern": "(?P__TERM__)(?P, por ejemplo)", + "term": "machine learning", + "fragment": "Para automatizar la clasificación de documentos, machine learning, por ejemplo, técnicas como SVM, árboles de decisión o redes neuronales se aplican para entrenar modelos capaces de reconocer categorías y asignar etiquetas de forma autónoma basándose en características extraídas del texto." + }, + { + "pattern": "(?Ppor ejemplo, )(?P__TERM__)", + "term": "ciberseguridad", + "fragment": "En el entorno digital actual, las organizaciones enfrentan riesgos crecientes, por ejemplo, ciberseguridad que abarca políticas, herramientas y prácticas destinadas a salvaguardar la confidencialidad, integridad y disponibilidad de la información frente a ataques maliciosos y vulnerabilidades emergentes." + }, + { + "pattern": "(?P__TERM__)(?P.{0,4}como por ejemplo)", + "term": "API RESTful", + "fragment": "En el diseño moderno de software, la API RESTful, como por ejemplo la implementada con Flask y FastAPI, permite estandarizar las comunicaciones entre microservicios, mejorando la interoperabilidad y facilitando el mantenimiento de grandes plataformas distribuidas en la nube." + }, + { + "pattern": "(?Ppor ejemplo, como )(?P__TERM__)", + "term": "Microservicios", + "fragment": "Muchas organizaciones adoptan arquitecturas modulares, por ejemplo, como Microservicios que dividen la aplicación en componentes autónomos y desplegables independientemente, lo que acelera las actualizaciones y reduce el riesgo de fallo generalizado." + }, + { + "pattern": "(?P__TERM__)(?P o que)", + "term": "Contenedores", + "fragment": "El uso de Contenedores o que encapsulan tanto la aplicación como sus dependencias en un único paquete ligero ha revolucionado el despliegue de software, garantizando consistencia entre entornos de desarrollo, testing y producción." + }, + { + "pattern": "(?P__TERM__)(?P denominad(o|a)s?)", + "term": "DevOps", + "fragment": "En la industria de TI, las metodologías DevOps denominadas prácticas culturales y técnicas colaborativas fomentan la comunicación entre equipos de desarrollo y operaciones, permitiendo ciclos de entrega más cortos y un feedback continuo que mejora la calidad del software." + }, + { + "pattern": "(denominad(o|a)s? )(?P__TERM__)", + "term": "Microservicios", + "fragment": "En los últimos años, han surgido distintos enfoques de arquitectura, siendo denominados Microservicios aquellos que fragmentan las funcionalidades en servicios independientes, facilitando la escalabilidad horizontal y la resiliencia ante fallos." + }, + { + "pattern": "(?P__TERM__)(?P alude a)", + "term": "GraphQL", + "fragment": "La especificación GraphQL alude a un lenguaje de consulta para APIs desarrollado por Facebook que permite a los clientes solicitar exactamente los datos que necesitan, reduciendo el volumen de peticiones y optimizando el rendimiento en aplicaciones móviles y web." + }, + { + "pattern": "(?P__TERM__)(?P.{0,5}se refiere al?)", + "term": "Serverless", + "fragment": "El paradigma Serverless se refiere al modelo en el que el proveedor gestiona automáticamente la infraestructura subyacente, liberando a los desarrolladores de la necesidad de aprovisionar servidores y permitiendo escalar según la demanda real." + }, + { + "pattern": "(?P__TERM__)(?P.{0,25}hace referencia a)", + "term": "Big Data", + "fragment": "La analítica avanzada Big Data hace referencia a la recolección y procesamiento masivo de datos estructurados y no estructurados, con técnicas de minería y aprendizaje automático, para extraer información valiosa que apoye la toma de decisiones estratégicas." + }, + { + "pattern": "(?Pque se conoce como )(?P__TERM__)", + "term": "Inteligencia Artificial", + "fragment": "Existen múltiples enfoques de aprendizaje automático, que se conoce como Inteligencia Artificial cuando las máquinas realizan tareas que normalmente requieren inteligencia humana, como el reconocimiento de voz, la visión por computadora y la toma de decisiones autónomas." + }, + { + "pattern": "(?Pse entiende por .{0,10})(?P__TERM__)", + "term": "REST", + "fragment": "En la documentación oficial, se entiende por API REST la implementación que sigue los principios de la arquitectura REST para diseñar servicios web escalables, mantenibles y basados en recursos accesibles a través de HTTP." + }, + { + "pattern": "(?P__TERM__)(?P, definido como)", + "term": "cognición", + "fragment": "En las teorías contemporáneas de la mente, la cognición, definido como el conjunto de procesos mentales responsables de la percepción, el pensamiento, la memoria y la toma de decisiones, se estudia mediante métodos experimentales y neuroimagen para comprender cómo representamos y manipulamos la información." + }, + { + "pattern": "(?P__TERM__)(?P, que se define como)", + "term": "ansiedad", + "fragment": "Desde la perspectiva clínica, la ansiedad, que se define como una respuesta emocional adaptativa caracterizada por tensión, preocupación y activación fisiológica, puede volverse patológica cuando interfiere de forma significativa con las actividades cotidianas y el bienestar del individuo." + }, + { + "pattern": "(?P__TERM__)(?P, cuya definición es)", + "term": "resiliencia", + "fragment": "En psicología positiva, la resiliencia, cuya definición es la capacidad de una persona para afrontar, adaptarse y recuperarse de situaciones adversas o de estrés crónico, se considera un factor clave en la prevención de trastornos mentales y en la promoción del bienestar a largo plazo." + }, + { + "pattern": "(?P__TERM__)(?P, esto es)", + "term": "empatía", + "fragment": "El desarrollo socioemocional en la infancia enfatiza la empatía, esto es la habilidad para comprender y compartir los sentimientos de otros, como fundamento de las relaciones interpersonales saludables y de la regulación emocional en contextos familiares y escolares." + }, + { + "pattern": "(?P__TERM__)(?P.{0,5} que es)", + "term": "autoestima", + "fragment": "La autoestima alta o baja depende de la historia de experiencias y evaluaciones personales, que es un constructo que refleja la valoración subjetiva que cada individuo hace de sí mismo en función de sus logros, relaciones y normas sociales." + }, + { + "pattern": "(?P__TERM__)(?P, dicho de otra (manera|modo))", + "term": "neuroplasticidad", + "fragment": "La neuroplasticidad, dicho de otra manera, es la capacidad del cerebro para reorganizar sus conexiones y adaptarse a cambios en el entorno o tras lesiones, lo que permite la recuperación funcional y el aprendizaje a lo largo de toda la vida." + }, + { + "pattern": "(?P__TERM__)(?P.{0,9} también llamado)", + "term": "aprendizaje social", + "fragment": "El aprendizaje social o vicario, también llamado aprendizaje observacional, describe el proceso mediante el cual las personas adquieren comportamientos, habilidades o actitudes al observar y emular a modelos significativos en su entorno social, sin necesidad de experiencia directa." + }, + { + "pattern": "(?Ptambién llamad(o|a).{0,30})(?P__TERM__)", + "term": "motricidad fina", + "fragment": "En la evaluación del desarrollo infantil, también llamada coordinación motora de precisión o control manual, motricidad fina es la destreza que permite realizar acciones delicadas con las manos y los dedos, esenciales para actividades como escribir, abotonar prendas o manipular objetos pequeños." + }, + { + "pattern": "(?P__TERM__)(?P, a saber)", + "term": "contrato de arrendamiento", + "fragment": "El contrato de arrendamiento, a saber, el acuerdo mediante el cual una parte llamada arrendador concede a otra denominada arrendatario el uso y disfrute de un bien inmueble por un plazo determinado y previa contraprestación económica, debe formalizarse por escrito para garantizar la seguridad jurídica de ambas partes." + }, + { + "pattern": "(?P__TERM__)(?P.{0,3}es .{0,4}(proceso que|proceso mediante el que|material que)?)", + "term": "evidencia pericial", + "fragment": "La evidencia pericial es proceso que requiere la intervención de un experto idóneo para emitir un dictamen técnico sobre hechos controvertidos en el procedimiento judicial, aportando material que facilite al juez la correcta interpretación de aspectos científicos o especializados." + }, + { + "pattern": "(?P__TERM__)(?P.{0,10}son)", + "term": "derechos subjetivos", + "fragment": "Los derechos subjetivos son prerrogativas conferidas a las personas por el ordenamiento jurídico que les facultan para exigir de otros el respeto a sus intereses legítimos, constituyendo el núcleo esencial de la pretensión jurídica en cualquier litigio." + }, + { + "pattern": "(?P__TERM__)(?P.{0,3} es )", + "term": "jurisdicción contencioso-administrativa", + "fragment": "La jurisdicción contencioso-administrativa es la rama del poder judicial encargada de resolver los conflictos que surgen entre los ciudadanos y la administración pública cuando estos reclaman la protección de sus derechos frente a actos administrativos." + }, + { + "pattern": "(?P__TERM__)(?P.{0,3}sería)", + "term": "principio de legalidad", + "fragment": "El principio de legalidad sería norma fundamental según la cual toda actuación de los poderes públicos debe estar previamente autorizada por una ley, garantizando así un Estado de Derecho en el que no existe arbitrariedad." + }, + { + "pattern": "(?Ptambién conocido como.{0,30})(?P__TERM__)", + "term": "instrumento público", + "fragment": "El documento notarial, también conocido como instrumento público autorizado por un fedatario, es aquel que goza de presunción de veracidad en cuanto a su contenido y fecha, siendo imprescindible para actos como la compraventa de inmuebles y la constitución de sociedades." + }, + { + "pattern": "(?P__TERM__)(?P, también conocido como)", + "term": "testamento ológrafo", + "fragment": "El testamento ológrafo, también conocido como testamento manuscrito, es aquel que el propio testador redacta, firma y fecha sin intervención notarial, aunque para su validez debe conservarse en su integridad hasta su presentación ante el juez tras el fallecimiento." + } +] + diff --git a/pattern_detection/data/loan_fragments.json b/pattern_detection/data/loan_fragments.json new file mode 100644 index 0000000..57ae2f6 --- /dev/null +++ b/pattern_detection/data/loan_fragments.json @@ -0,0 +1,37 @@ +[ + { + "pattern": "(?P__TERM__)(?P, en (español|inglés|francés|alemán|portugués))", + "term": "sesgo de confirmación", + "fragment": "En los estudios de toma de decisiones, el sesgo de confirmación, en inglés \"confirmation bias\", se refiere a la tendencia a buscar, interpretar y recordar información de forma que confirme las propias creencias, ignorando o minimizando datos contradictorios para mantener la coherencia cognitiva." + }, + { + "pattern": "(?Ptambién conocido en (español|inglés|francés|alemán|portugués) como)", + "term": "procrastinación", + "fragment": "Este comportamiento, también conocido en alemán como \"Aufschieberitis\", se caracteriza por el aplazamiento voluntario de tareas necesarias, acompañado de una sensación subjetiva de urgencia que provoca estrés cuando se acerca el plazo de entrega." + }, + { + "pattern": "(?P__TERM__)(?P.{0,5} proviene del \\w{4,11})", + "term": "autoencoder", + "fragment": "En el campo del aprendizaje profundo, el autoencoder proviene del griego y se utiliza para aprender representaciones compactas de los datos de entrada mediante un esquema de codificación y decodificación que minimiza la pérdida de información." + }, + { + "pattern": "(?Pproviene del \\w{4,11} )(?P__TERM__)", + "term": "psicología", + "fragment": "La etimología de la palabra proviene del griego psicología y describe la disciplina científica dedicada al estudio de la mente, los procesos cognitivos y el comportamiento humano tanto en contextos normales como patológicos." + }, + { + "pattern": "(?P__TERM__)(?P, que proviene del \\w{4,11})", + "term": "neuroplasticidad", + "fragment": "La neuroplasticidad, que proviene del griego, hace referencia a la capacidad del sistema nervioso para reorganizar sus conexiones estructurales y funcionales en respuesta a experiencias, aprendizajes y lesiones, facilitando la adaptación y recuperación cerebral." + }, + { + "pattern": "(?P__TERM__)(?P, que se origina en el \\w{4,11})", + "term": "overfitting", + "fragment": "El fenómeno overfitting, que se origina en el inglés, ocurre cuando un modelo de machine learning ajusta excesivamente sus parámetros a los datos de entrenamiento, perdiendo capacidad de generalización y obteniendo malos resultantes en datos nuevos." + }, + { + "pattern": "(?Pque se origina en el \\w{4,11} )(?P__TERM__)", + "term": "memoria", + "fragment": "En los enfoques cognitivos, se origina en el latín memoria como concepto central para describir el proceso mediante el cual el cerebro codifica, almacena y recupera información a lo largo del tiempo." + } +] diff --git a/pattern_detection/pattern_matching/__pycache__/pattern_matching.cpython-312.pyc b/pattern_detection/pattern_matching/__pycache__/pattern_matching.cpython-312.pyc new file mode 100644 index 0000000..6ccf5f2 Binary files /dev/null and b/pattern_detection/pattern_matching/__pycache__/pattern_matching.cpython-312.pyc differ diff --git a/pattern_detection/pattern_matching/pattern_matching.py b/pattern_detection/pattern_matching/pattern_matching.py new file mode 100644 index 0000000..c7baff5 --- /dev/null +++ b/pattern_detection/pattern_matching/pattern_matching.py @@ -0,0 +1,134 @@ +import json +from typing import List, Dict, Union, List +from pathlib import Path +import re + +def load_text_patterns(file_path: str, term: str, pattern_label: str = None) -> List[str]: + """ + Carga patrones desde un archivo JSON y los adapta al término especificado. + + Args: + file_path: Ruta al archivo JSON que contiene los patrones + term: Término que se usará para reemplazar __TERM__ en los patrones + pattern_label: Nombre de patrón a cargar (opcional si el archivo solo tiene un tipo) + + Returns: + Lista de patrones adaptados al término + + Raises: + FileNotFoundError: Si el archivo no existe + ValueError: Si el pattern_label no existe en el archivo + json.JSONDecodeError: Si el archivo no es un JSON válido + """ + try: + file_path = Path(file_path) + if not file_path.exists(): + raise FileNotFoundError(f"El archivo de patrones no existe: {file_path}") + + with open(file_path, 'r', encoding='utf-8') as f: + patterns_data = json.load(f) + + # Si no se especifica pattern_label y solo hay un tipo en el archivo, usarlo + if pattern_label is None: + if len(patterns_data) == 1: + pattern_label = next(iter(patterns_data.keys())) + else: + raise ValueError("Debe especificar pattern_label cuando el archivo contiene múltiples tipos") + + if pattern_label not in patterns_data: + available_types = list(patterns_data.keys()) + raise ValueError( + f"Tipo de patrón '{pattern_label}' no encontrado. " + f"Tipos disponibles: {', '.join(available_types)}" + ) + + return [pattern.replace("__TERM__", term) for pattern in patterns_data[pattern_label]] + + except json.JSONDecodeError as e: + raise json.JSONDecodeError(f"Error al decodificar JSON en {file_path}: {e.msg}", e.doc, e.pos) + + +def detect_patterns(text: str, patterns: List[str]) -> bool: + """ + Detecta si alguno de los patrones se encuentra en el texto. + Args: + text (str): El texto en el que buscar los patrones. + patterns (list): Lista de patrones a buscar. + Returns: + bool: True si se encuentra al menos un patrón, False en caso contrario. + """ + compiled_patterns = [re.compile(pattern, re.IGNORECASE) for pattern in patterns] + found = False + processed_text = text + + for pattern in compiled_patterns: + match = pattern.search(processed_text) + if match and match.group(0).strip() != "": + found = True + return found + + +def highlight_patterns(text: str, patterns: list) -> str: + """ + Resalta el término y los patrones de contexto sin alterar el orden original. + Args: + text (str): El texto en el que buscar los patrones. + patterns (list): Lista de patrones a resaltar, donde cada patrón puede contener grupos nombrados. + Returns: + str: El texto con los patrones resaltados, o None si no se encuentra ninguno. + """ + regexes = [re.compile(p, re.IGNORECASE) for p in patterns] + found = False + highlighted_text = text + + for regex in regexes: + def replacer(match): + nonlocal found + # Validar que la coincidencia no esté vacía + if not match or match.group(0).strip() == "": + return match.group(0) + found = True + result = match.group(0) + replaced = result + spans = [] + + # Ordenar los grupos según su posición en el texto + spans_data = [] + for name, value in match.groupdict().items(): + if value: + start = match.start(name) - match.start() + end = match.end(name) - match.start() + span_class = 'highlight-term' if name == 'term' else 'highlight-pattern' + spans_data.append((start, end, span_class, value)) + + # Aplicar los span *desde el final hacia el inicio* para no romper los índices + for start, end, cls, val in sorted(spans_data, reverse=True): + replaced = replaced[:start] + f'{val}' + replaced[end:] + + return replaced + + highlighted_text = regex.sub(replacer, highlighted_text) + + return highlighted_text if found else None + + +def highlight_term(text: str, term: str) -> str: + """ + Resalta un término específico en el texto, envolviéndolo en una etiqueta . + Si el término no se encuentra, devuelve el texto original. + Args: + text (str): El texto en el que buscar el término. + term (str): El término a resaltar. + Returns: + str: El texto con el término resaltado, o el texto original si no se encuentra. + """ + found = False + pattern = re.compile(term, re.IGNORECASE) + if pattern.search(text): + found = True + highlighted_text = pattern.sub(lambda match: f'{match.group(0)}', text) + + return highlighted_text if found else text + + + diff --git a/pattern_detection/patterns/gloss_patterns.json b/pattern_detection/patterns/gloss_patterns.json new file mode 100644 index 0000000..5f24540 --- /dev/null +++ b/pattern_detection/patterns/gloss_patterns.json @@ -0,0 +1,42 @@ +{ + "gloss": [ + "(?P__TERM__)(?P, es decir,)", + "(?Pes decir, )(?P__TERM__)", + "(?P__TERM__)(?P o sea)", + "(?Po sea, )(?P__TERM__)", + "(?P__TERM__)(?P.{0,5} o )", + "(?P__TERM__)(?P, en otras palabras)", + "(?Pen otras palabras, )(?P__TERM__)", + "(?P__TERM__)(?P, por ejemplo)", + "(?Ppor ejemplo, )(?P__TERM__)", + + "(?P__TERM__)(?P.{0,4}como por ejemplo)", + "(?Ppor ejemplo, como )(?P__TERM__)", + "(?P__TERM__)(?P o que)", + "(?P__TERM__)(?P denominad(o|a)s?)", + "(denominad(o|a)s? )(?P__TERM__)", + "(?P__TERM__)(?P alude a)", + "(?P__TERM__)(?P.{0,5}se refiere al?)", + "(?P__TERM__)(?P.{0,25}hace referencia a)", + "(?Pque se conoce como )(?P__TERM__)", + "(?Pse entiende por .{0,10})(?P__TERM__)", + + "(?P__TERM__)(?P, definido como)", + "(?P__TERM__)(?P, que se define como)", + "(?P__TERM__)(?P, cuya definición es)", + "(?P__TERM__)(?P, esto es)", + "(?P__TERM__)(?P.{0,5} que es)", + "(?P__TERM__)(?P, dicho de otra (manera|modo))", + "(?P__TERM__)(?P.{0,9} también llamado)", + "(?Ptambién llamad(o|a).{0,30})(?P__TERM__)", + + "(?P__TERM__)(?P, a saber)", + "(?P__TERM__)(?P.{0,3}es .{0,4}(proceso que|proceso mediante el que|material que)?)", + "(?P__TERM__)(?P.{0,10}son)", + "(?P__TERM__)(?P.{0,3} es )", + "(?P__TERM__)(?P.{0,3}sería)", + "(?Ptambién conocido como.{0,30})(?P__TERM__)", + "(?P__TERM__)(?P, también conocido como)" + ] + } + \ No newline at end of file diff --git a/pattern_detection/patterns/loan_patterns.json b/pattern_detection/patterns/loan_patterns.json new file mode 100644 index 0000000..f9db7bc --- /dev/null +++ b/pattern_detection/patterns/loan_patterns.json @@ -0,0 +1,11 @@ +{ + "loan": [ + "(?P__TERM__)(?P, en (español|inglés|francés|alemán|portugués))", + "(?Ptambién conocido en (español|inglés|francés|alemán|portugués) como)", + "(?P__TERM__)(?P.{0,5} proviene del \\w{4,11})", + "(?Pproviene del \\w{4,11} )(?P__TERM__)", + "(?P__TERM__)(?P, que proviene del \\w{4,11})", + "(?P__TERM__)(?P, que se origina en el \\w{4,11})", + "(?Pque se origina en el \\w{4,11} )(?P__TERM__)" + ] +} \ No newline at end of file diff --git a/pattern_detection/readme.md b/pattern_detection/readme.md new file mode 100644 index 0000000..693efde --- /dev/null +++ b/pattern_detection/readme.md @@ -0,0 +1,16 @@ + +# Text-Patterns + +Este repositorio contiene un pequeño módulo para: + +2. Detectar si ciertos patrones de glosa y préstamo referidos a un término aparecen en un texto. +3. Resaltar en formato HTML el término y el patrón detectado dentro del contexto. +4. Resaltar un término concreto dentro de un fragmento. + +este módulo es parte de una funcionalidad de una página web que donde se le pasan los fragmentos de la base de datos y ya realiza el trabajo de detectar los patrones. Esto sirve para que lo/as experto/as y lingüistas puedan buscar dentro del contexto la existencia de glosas explicativas o indicios de que el término es un préstamo + +## Requisitos + +```bash +pip install -r requirements.txt + diff --git a/pattern_detection/reports/pytest_output.txt b/pattern_detection/reports/pytest_output.txt new file mode 100644 index 0000000..8160357 Binary files /dev/null and b/pattern_detection/reports/pytest_output.txt differ diff --git a/pattern_detection/reports/results.xml b/pattern_detection/reports/results.xml new file mode 100644 index 0000000..7ae944c --- /dev/null +++ b/pattern_detection/reports/results.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pattern_detection/requirements.txt b/pattern_detection/requirements.txt new file mode 100644 index 0000000..af53886 --- /dev/null +++ b/pattern_detection/requirements.txt @@ -0,0 +1 @@ +pytest>=7.0.0 diff --git a/pattern_detection/tests/__pycache__/test_patterns.cpython-312-pytest-8.4.1.pyc b/pattern_detection/tests/__pycache__/test_patterns.cpython-312-pytest-8.4.1.pyc new file mode 100644 index 0000000..8888881 Binary files /dev/null and b/pattern_detection/tests/__pycache__/test_patterns.cpython-312-pytest-8.4.1.pyc differ diff --git a/pattern_detection/tests/test_patterns.py b/pattern_detection/tests/test_patterns.py new file mode 100644 index 0000000..e38ae33 --- /dev/null +++ b/pattern_detection/tests/test_patterns.py @@ -0,0 +1,65 @@ +import json +import os +import pytest + +from pattern_matching.pattern_matching import load_text_patterns, detect_patterns, highlight_patterns, highlight_term + +BASE = os.path.dirname(__file__) + +def load_fragments(file_name): + """ + Carga fragments desde el directorio data. + Lanza AssertionError si no existe el archivo. + """ + path = os.path.join(BASE, os.pardir, "data", file_name) + assert os.path.exists(path), ( + f"No existe {file_name}; ejecuta el script de conversión para generarlo en data/{file_name}" + ) + with open(path, encoding="utf-8") as f: + return json.load(f) + +@pytest.mark.parametrize("file_name,pattern_label", [ + ("loan_fragments.json", "loan"), + ("gloss_fragments.json", "gloss"), +]) +def test_detect_patterns_with_fragments(file_name, pattern_label): + """ + Para cada fragmento en los archivos generados, comprueba que detect_patterns devuelve booleano. + Fallará si faltan archivos o patrones. + """ + frags = load_fragments(file_name) + # Directorio de patrones + pattern_dir = os.path.join(BASE, os.pardir, "patterns") + patterns_path = os.path.join(pattern_dir, f"{pattern_label}_patterns.json") + assert os.path.exists(patterns_path), ( + f"No existe el fichero de patrones {patterns_path}. Comprueba la ruta y los nombres." + ) + for entry in frags: + term = entry.get("term") or entry.get("fragmento") or entry.get("fragment") + fragment = entry.get("fragment") or entry.get("fragmento") + # Carga los patrones para el término + pats = load_text_patterns(patterns_path, term=term, pattern_label=pattern_label) + # detect_patterns debe devolver un booleano y no lanzar excepción + result = detect_patterns(fragment, pats) + assert isinstance(result, bool), ( + f"detect_patterns no devolvió bool para {file_name} con término {term}" + ) + + +def test_highlight_patterns_and_term(): + """ + Prueba highlight_patterns y highlight_term con ejemplos simples. + """ + pattern_dir = os.path.join(BASE, os.pardir, "patterns") + gloss_file = os.path.join(pattern_dir, "gloss_patterns.json") + assert os.path.exists(gloss_file), ( + f"No existe el fichero de patrones {gloss_file}. Comprueba la ruta." + ) + term = "prueba" + pats = load_text_patterns(gloss_file, term=term, pattern_label="gloss") + text = "La prueba, es decir, un ejemplo claro." + highlighted = highlight_patterns(text, pats) + assert 'prueba' in highlighted + # Prueba highlight_term + out = highlight_term("Hola mundo", "mundo") + assert 'mundo' in out