|
import gradio as gr |
|
import json |
|
from seo_analyzer import SEOSpaceAnalyzer |
|
import spacy |
|
import subprocess |
|
import sys |
|
import logging |
|
import os |
|
from pathlib import Path |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
logger = logging.getLogger(__name__) |
|
|
|
def setup_spacy_model() -> None: |
|
""" |
|
Verifica y descarga el modelo spaCy 'es_core_news_lg' si no está instalado. |
|
""" |
|
try: |
|
spacy.load("es_core_news_lg") |
|
logger.info("Modelo spaCy 'es_core_news_lg' cargado correctamente.") |
|
except OSError: |
|
logger.info("Descargando modelo spaCy 'es_core_news_lg'...") |
|
try: |
|
subprocess.run( |
|
[sys.executable, "-m", "spacy", "download", "es_core_news_lg"], |
|
check=True, |
|
stdout=subprocess.PIPE, |
|
stderr=subprocess.PIPE |
|
) |
|
logger.info("Modelo descargado exitosamente.") |
|
except subprocess.CalledProcessError as e: |
|
logger.error(f"Error al descargar modelo: {e.stderr.decode()}") |
|
raise RuntimeError("No se pudo descargar el modelo spaCy") from e |
|
|
|
def list_content_storage_files() -> list: |
|
""" |
|
Busca todos los archivos dentro de la carpeta content_storage y |
|
devuelve una lista de rutas relativas. |
|
""" |
|
base_dir = Path("content_storage") |
|
if not base_dir.exists(): |
|
return [] |
|
file_list = [str(file.relative_to(base_dir)) for file in base_dir.glob("**/*") if file.is_file()] |
|
logger.info(f"Archivos encontrados en content_storage: {file_list}") |
|
return file_list |
|
|
|
def download_storage_file(selected_file: str) -> str: |
|
""" |
|
Dado el nombre de un archivo (ruta relativa respecto a content_storage), |
|
devuelve la ruta completa del archivo para descargarlo. |
|
""" |
|
if not selected_file: |
|
return "" |
|
file_path = Path("content_storage") / selected_file |
|
if file_path.exists(): |
|
return str(file_path) |
|
else: |
|
return "" |
|
|
|
def refresh_file_list() -> list: |
|
"""Función para actualizar la lista de archivos en content_storage.""" |
|
return list_content_storage_files() |
|
|
|
def create_interface() -> gr.Blocks: |
|
analyzer = SEOSpaceAnalyzer() |
|
with gr.Blocks(title="SEO Analyzer Pro", theme=gr.themes.Soft()) as interface: |
|
gr.Markdown(""" |
|
# 🕵️ SEO Analyzer Pro |
|
**Analizador SEO avanzado con modelos de lenguaje** |
|
|
|
Ingresa la URL de un sitemap.xml para analizar el sitio web. |
|
""") |
|
with gr.Row(): |
|
with gr.Column(): |
|
sitemap_input = gr.Textbox( |
|
label="URL del Sitemap", |
|
placeholder="https://ejemplo.com/sitemap.xml", |
|
interactive=True |
|
) |
|
analyze_btn = gr.Button("Analizar Sitio", variant="primary") |
|
with gr.Row(): |
|
clear_btn = gr.Button("Limpiar") |
|
download_btn = gr.Button("Descargar Reporte", variant="secondary") |
|
plot_btn = gr.Button("Visualizar Enlaces Internos", variant="secondary") |
|
with gr.Column(): |
|
status_output = gr.Textbox(label="Estado del Análisis", interactive=False) |
|
with gr.Tabs(): |
|
with gr.Tab("📊 Resumen"): |
|
stats_output = gr.JSON(label="Estadísticas Generales") |
|
recommendations_output = gr.JSON(label="Recomendaciones SEO") |
|
with gr.Tab("📝 Contenido"): |
|
content_output = gr.JSON(label="Análisis de Contenido") |
|
with gr.Tab("🔗 Enlaces"): |
|
links_output = gr.JSON(label="Análisis de Enlaces") |
|
links_plot = gr.Plot(label="Visualización de Enlaces Internos") |
|
with gr.Tab("📄 Detalles"): |
|
details_output = gr.JSON(label="Detalles Individuales") |
|
with gr.Tab("📁 Archivos"): |
|
file_dropdown = gr.Dropdown(label="Archivos en content_storage", choices=list_content_storage_files()) |
|
refresh_btn = gr.Button("Actualizar lista") |
|
download_file_btn = gr.Button("Descargar Archivo Seleccionado", variant="secondary") |
|
file_download = gr.File(label="Archivo Seleccionado") |
|
def generate_report() -> str: |
|
""" |
|
Genera un informe en formato JSON con el análisis SEO y lo guarda en content_storage. |
|
Retorna la ruta del archivo generado. |
|
""" |
|
if analyzer.current_analysis: |
|
report_path = "content_storage/seo_report.json" |
|
try: |
|
with open(report_path, 'w', encoding='utf-8') as f: |
|
json.dump(analyzer.current_analysis, f, indent=2, ensure_ascii=False) |
|
logger.info(f"Reporte generado en: {report_path}") |
|
return report_path |
|
except Exception as e: |
|
logger.error(f"Error generando reporte: {e}") |
|
return "" |
|
else: |
|
logger.warning("No hay análisis para generar el reporte.") |
|
return "" |
|
def plot_internal_links(links_json: dict) -> any: |
|
return analyzer.plot_internal_links(links_json) |
|
analyze_btn.click( |
|
fn=analyzer.analyze_sitemap, |
|
inputs=sitemap_input, |
|
outputs=[stats_output, recommendations_output, content_output, links_output, details_output], |
|
show_progress=True |
|
) |
|
clear_btn.click( |
|
fn=lambda: [None, None, None, None, None], |
|
outputs=[stats_output, recommendations_output, content_output, links_output, details_output] |
|
) |
|
|
|
download_btn.click( |
|
fn=generate_report, |
|
outputs=file_download |
|
) |
|
plot_btn.click( |
|
fn=plot_internal_links, |
|
inputs=links_output, |
|
outputs=links_plot |
|
) |
|
refresh_btn.click( |
|
fn=refresh_file_list, |
|
outputs=file_dropdown |
|
) |
|
download_file_btn.click( |
|
fn=download_storage_file, |
|
inputs=file_dropdown, |
|
outputs=file_download |
|
) |
|
return interface |
|
|
|
if __name__ == "__main__": |
|
setup_spacy_model() |
|
app = create_interface() |
|
app.launch(server_name="0.0.0.0", server_port=7860, show_error=True, share=False) |
|
|
|
|