File size: 6,398 Bytes
a8a2139
dcf8a98
a8a2139
 
8b7112a
 
a8a2139
90799a0
 
63fe26b
a8a2139
dcf8a98
 
a8a2139
bb43f76
3971861
bb43f76
a8a2139
 
 
 
 
63fe26b
a8a2139
 
 
 
 
 
 
 
 
 
bb43f76
90799a0
 
 
 
 
 
 
 
 
3971861
90799a0
 
 
 
 
3971861
90799a0
 
 
 
3971861
 
 
 
90799a0
 
3971861
90799a0
 
bb43f76
7d39cf2
 
63fe26b
 
3969e8a
 
a8a2139
63fe26b
7d39cf2
63fe26b
a8a2139
 
3971861
a8a2139
 
3969e8a
 
 
 
bb43f76
63fe26b
3969e8a
63fe26b
3969e8a
 
 
 
 
 
 
bb43f76
a8a2139
 
90799a0
 
 
 
 
a8a2139
3971861
 
d3a54e9
3971861
bb43f76
 
3971861
 
 
 
 
 
 
 
 
 
 
a8a2139
 
7d39cf2
 
3969e8a
a8a2139
3969e8a
 
 
a8a2139
 
3969e8a
c4827cf
3969e8a
6c55771
c4827cf
dcf8a98
bb43f76
a8a2139
bb43f76
 
 
90799a0
 
 
 
 
 
 
 
 
7d39cf2
bb43f76
c561b6f
 
63fe26b
a8a2139
08b832d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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]
        )
        # Se elimina el parámetro _js para evitar el error.
        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)