File size: 7,245 Bytes
a8a2139
dcf8a98
f415fc9
a8a2139
8b7112a
 
a8a2139
90799a0
f415fc9
63fe26b
a8a2139
dcf8a98
 
f415fc9
 
a8a2139
 
2e8fa9d
a8a2139
f415fc9
 
90799a0
2e8fa9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb43f76
7d39cf2
f415fc9
2e8fa9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f415fc9
63fe26b
f415fc9
 
 
 
 
63fe26b
7d39cf2
f415fc9
 
 
 
 
2e8fa9d
63fe26b
3969e8a
f415fc9
3969e8a
 
f415fc9
3969e8a
f415fc9
 
a8a2139
f415fc9
 
 
2e8fa9d
f415fc9
 
 
 
2e8fa9d
 
 
 
 
 
bb43f76
f415fc9
 
2e8fa9d
f415fc9
 
2e8fa9d
f415fc9
3971861
f415fc9
 
 
 
 
 
 
 
 
 
 
 
 
7d39cf2
2e8fa9d
3969e8a
f415fc9
 
 
 
2e8fa9d
 
3969e8a
f415fc9
 
 
 
 
 
 
 
 
 
2e8fa9d
 
f415fc9
bb43f76
c561b6f
 
63fe26b
f415fc9
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
import gradio as gr
import json
import pandas as pd
import spacy
import subprocess
import sys
import logging
from pathlib import Path
from seo_analyzer import SEOSpaceAnalyzer

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def setup_spacy_model():
    """Carga o descarga el modelo spaCy necesario."""
    try:
        spacy.load("es_core_news_lg")
        logger.info("Modelo spaCy 'es_core_news_lg' cargado correctamente.")
    except OSError:
        logger.info("Descargando spaCy model es_core_news_lg...")
        subprocess.run([sys.executable, "-m", "spacy", "download", "es_core_news_lg"], check=True)

def list_content_storage_files() -> list:
    """Devuelve la lista de archivos en la carpeta content_storage."""
    base_dir = Path("content_storage")
    if not base_dir.exists():
        return []
    return [str(file.relative_to(base_dir)) for file in base_dir.glob("**/*") if file.is_file()]

def download_storage_file(selected_file: str) -> str:
    """Dado el nombre del archivo (relativo a content_storage), devuelve la ruta para descarga."""
    if not selected_file:
        return ""
    file_path = Path("content_storage") / selected_file
    return str(file_path) if file_path.exists() else ""

def refresh_file_list() -> list:
    """Actualiza la lista de archivos disponibles en content_storage."""
    return list_content_storage_files()

# Creamos la interfaz
def create_interface() -> gr.Blocks:
    analyzer = SEOSpaceAnalyzer()

    # Definimos una función envoltorio para incluir callbacks de estado
    def analyze_with_callbacks(sitemap_url: str):
        status_msgs = []

        def status_callback(msg: str):
            status_msgs.append(msg)
            logger.info(msg)

        def progress_callback(current: int, total: int):
            logger.info(f"Batch {current} de {total} procesado.")

        # Se llama al método modificado que procesa en lotes de 5
        results = analyzer.analyze_sitemap(sitemap_url, progress_callback=progress_callback, status_callback=status_callback)
        final_status = "\n".join(status_msgs) if status_msgs else "Análisis completado."
        # 'results' es una tupla de 7 elementos:
        # (stats, recommendations, content_analysis, links, details, similarities, seo_tags)
        # Devolvemos esos 7 outputs más el mensaje final en estado (total 8)
        return (*results, final_status)

    with gr.Blocks(title="SEO Analyzer Pro", theme=gr.themes.Soft()) as demo:
        gr.Markdown("""
        # 🧠 SEO Analyzer Pro
        Este espacio analiza contenido web orientado a normativa bancaria y genera:
        - Temas inferidos automáticamente
        - Títulos y meta descripciones SEO
        - Alertas por lenguaje de riesgo
        """)
        with gr.Row():
            sitemap_input = gr.Textbox(label="📍 URL del Sitemap", placeholder="https://ejemplo.com/sitemap.xml")
            analyze_btn = gr.Button("🔍 Analizar")
            clear_btn = gr.Button("🧹 Limpiar")
            download_json_btn = gr.Button("📥 Descargar JSON")
            download_csv_btn = gr.Button("📤 Descargar CSV")
        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")
                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 por página")
            with gr.Tab("🧠 SEO y Temas"):
                seo_tags_output = gr.JSON(label="Metadatos SEO generados")
                # Los siguientes se actualizan vía change en seo_tags_output
                topics_output = gr.JSON(label="Temas inferidos")
                flags_output = gr.JSON(label="Términos prohibidos detectados")
            with gr.Tab("🔗 Similitud"):
                similarity_output = gr.JSON(label="Similitud entre URLs")
            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 export_json() -> str:
            if analyzer.current_analysis:
                path = Path("content_storage/seo_report.json")
                with open(path, "w", encoding="utf-8") as f:
                    json.dump(analyzer.current_analysis, f, indent=2, ensure_ascii=False)
                return str(path)
            return ""
        def export_csv() -> str:
            if not analyzer.current_analysis:
                return ""
            path = Path("content_storage/seo_summary.csv")
            data = []
            for url, seo in analyzer.current_analysis.get("seo_tags", {}).items():
                data.append({
                    "url": url,
                    "title": seo.get("title", ""),
                    "meta_description": seo.get("meta_description", ""),
                    "flags": ", ".join(seo.get("flags", [])),
                    "topics": ", ".join(analyzer.current_analysis.get("topics", {}).get(url, [])),
                    "summary": analyzer.current_analysis.get("summaries", {}).get(url, "")
                })
            pd.DataFrame(data).to_csv(path, index=False)
            return str(path)
        analyze_btn.click(
            fn=analyze_with_callbacks,
            inputs=sitemap_input,
            outputs=[
                stats_output, recommendations_output, content_output,
                links_output, details_output, similarity_output,
                seo_tags_output, status_output
            ],
            show_progress=True
        )
        clear_btn.click(fn=lambda: [None]*8, outputs=[
            stats_output, recommendations_output, content_output,
            links_output, details_output, similarity_output,
            seo_tags_output, status_output
        ])
        download_json_btn.click(fn=export_json, outputs=status_output)
        download_csv_btn.click(fn=export_csv, outputs=status_output)
        links_output.change(fn=analyzer.plot_internal_links, inputs=links_output, outputs=links_plot)
        seo_tags_output.change(fn=lambda: analyzer.current_analysis.get("topics", {}), outputs=topics_output)
        seo_tags_output.change(fn=lambda: analyzer.current_analysis.get("flags", {}), outputs=flags_output)
        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 demo

if __name__ == "__main__":
    setup_spacy_model()
    app = create_interface()
    app.launch(server_name="0.0.0.0", server_port=7860)