Herramienta de mantenimiento del Proyecto WWW (índices de secciones)

Descripción: Herramienta en Python para generar automáticamente páginas índice bilingües del Proyecto WWW a partir de archivos de texto con secciones y artículos.

Introducción

Esta herramienta de mantenimiento en Python genera automáticamente las páginas índice bilingües del Proyecto WWW a partir de archivos de texto con las secciones y los artículos publicados.

Archivos de entrada del índice

El script utiliza dos archivos de texto plano como fuente de datos: secciones-es.txt para la versión en español y secciones-en.txt para la versión en inglés.

Cada archivo está organizado por secciones; cada sección comienza con su nombre en una línea independiente, seguida de pares de líneas que contienen, primero, el título del artículo y luego la URL correspondiente.

Entre una sección y la siguiente se deja al menos una línea en blanco, lo que permite al script identificar correctamente el inicio de cada categoría de contenidos.

Funcionamiento general del script

El script lee ambos archivos de texto, interpreta la estructura de secciones, títulos y URLs, y construye en memoria una representación ordenada del contenido del Proyecto WWW.

A partir de esa representación, genera dos archivos HTML (output-es.html y output-en.html) que incluyen las pestañas de secciones, las listas de artículos y los bloques JSON-LD necesarios para los buscadores.

Detalles de implementación

Lectura de archivos y estructura interna (read_sections)

La función read_sections recorre cada archivo línea por línea, detecta el nombre de cada sección y agrupa los títulos y URLs en un diccionario, donde cada clave es el nombre de la sección y cada valor es la lista de artículos asociados.

La función admite URLs en texto plano y también URLs escritas con formato Markdown, que son limpiadas para extraer el enlace real antes de incorporarlo a la estructura interna.

Generación de JSON-LD (generate_json_ld)

La función generate_json_ld construye, para cada sección, un bloque de datos estructurados con el tipo BreadcrumbList de schema.org, incluyendo los títulos y las URLs de los artículos.

Estos bloques se insertan en el HTML final dentro de etiquetas <script type="application/ld+json">, mejorando la comprensión del contenido por parte de los motores de búsqueda.

Construcción del HTML y pestañas (write_html_and_jsonld)

La función write_html_and_jsonld genera la estructura HTML completa, añade el comentario descriptivo para buscadores y crea los botones de cada sección, que funcionan como pestañas de navegación.

Para cada sección, genera un contenedor con su título y una lista ordenada de enlaces a los artículos; además, adapta los textos visibles según el idioma y añade la sección de artículos destacados del Proyecto WWW.

Uso práctico en el Proyecto WWW

Para actualizar los índices, basta con editar secciones-es.txt y secciones-en.txt, añadiendo o modificando secciones, títulos y URLs según sea necesario.

Una vez guardados los cambios, se ejecuta el script en Python; esto genera los archivos output-es.html y output-en.html, cuyo contenido se copia en las páginas correspondientes del Proyecto WWW en español e inglés.

Código fuente completo

A continuación se muestra el código fuente completo del script, listo para ser copiado, adaptado o reutilizado en futuras tareas de mantenimiento del Proyecto WWW.


import json

def read_sections(file_path):
    """
    Lee un archivo donde las categorías están separadas por líneas en blanco.
    Cada categoría comienza con su nombre en una línea, seguida de pares de líneas:
    nombre del artículo y URL.
    Retorna un diccionario {categoria: [(nombre, url), ...], ...}
    """
    sections = {}
    current_category = None

    with open(file_path, 'r', encoding='utf-8') as f:
        lines = [line.rstrip('\n') for line in f]

    i = 0
    n = len(lines)
    while i < n:
        line = lines[i].strip()

        # Saltar líneas vacías
        if not line:
            i += 1
            continue

        # Detectar categoría: línea después de línea vacía o al inicio
        if current_category is None or (i > 0 and lines[i-1].strip() == ''):
            current_category = line
            if current_category not in sections:
                sections[current_category] = []
            i += 1
            continue

        # Si llegamos aquí, estamos dentro de una categoría y esta línea es nombre de artículo
        name = line
        url = None

        # Mirar la siguiente línea para ver si es una URL
        if i + 1 < n:
            url_candidate = lines[i + 1].strip()
            # Aceptamos URLs con o sin formato Markdown
            if url_candidate.startswith('http') or url_candidate.startswith('['):
                # Ejemplo de Markdown: [https://...](https://...)
                if url_candidate.startswith('[') and '](' in url_candidate:
                    url_inside = url_candidate.split('](')[1].rstrip(')')
                    url = url_inside
                else:
                    url = url_candidate.strip('[]')
                i += 2
            else:
                i += 1
        else:
            i += 1

        if current_category is None:
            raise ValueError("Artículo sin categoría definida")

        if url:
            sections[current_category].append((name, url))

    return sections


def generate_json_ld(category, items):
    """Genera la estructura JSON-LD para una categoría dada."""
    item_list = []
    position = 1
    for name, url in items:
        if url:
            item_list.append({
                "@type": "ListItem",
                "position": position,
                "item": {
                    "@id": url,
                    "name": name
                }
            })
            position += 1

    return {
        "@context": "https://schema.org",
        "@type": "BreadcrumbList",
        "name": category,
        "itemListElement": item_list
    }


def write_html_and_jsonld(sections, language='es'):
    """
    Genera el HTML completo (comentario + JSON-LD + contenido)
    a partir del diccionario de secciones.

    language:
      'es' → textos visibles en español (output-es.html)
      'en' → textos visibles en inglés (output-en.html)
    """
    html = ""

    # Comentario con descripción corta para motores de búsqueda / Blogger
    if language == 'es':
        html += "<!-- Secciones del Proyecto WWW, con enlaces a los artículos publicados en español. -->\n\n"
    else:
        html += "<!-- Sections of Proyecto WWW, with links to articles published in English. -->\n\n"

    # JSON-LD por cada categoría
    for category, items in sections.items():
        json_ld = generate_json_ld(category, items)
        json_ld_str = json.dumps(json_ld, ensure_ascii=False, indent=4)
        html += f'<script type="application/ld+json">\n{json_ld_str}\n</script>\n\n'

    # Bloque inicial según idioma
    if language == 'es':
        html += (
            '<div>\n'
            '\t<p>Secciones establecidas para el contenido del Proyecto WWW.</p><br>\n'
            '\t<a id="resumen" class="subrayadoSolidoAnaranjado01 letra_capital">Resumen del contenido</a>:<br>\n'
            '\t<p class="FuenteNoventaPorCiento" style="padding: 3%;">\n'
            '\t\tCada una de las partes o divisiones establecidas para el contenido del Proyecto WWW\n'
            '\t</p>\n'
            '</div>\n\n'
        )
    else:
        html += (
            '<div>\n'
            '\t<p>Sections established for the content of Proyecto WWW.</p><br>\n'
            '\t<a id="resumen" class="subrayadoSolidoAnaranjado01 letra_capital">Content summary</a>:<br>\n'
            '\t<p class="FuenteNoventaPorCiento" style="padding: 3%;">\n'
            '\t\tEach of the parts or divisions established for the content of Proyecto WWW\n'
            '\t</p>\n'
            '</div>\n\n'
        )

    html += '<div class="contenedor_full">\n'

    # Botones (pestañas) por categoría
    for i, category in enumerate(sections.keys()):
        is_active = (i == 0)
        aria_expanded = 'true' if is_active else 'false'
        id_attr = ' id="abrirPorDefecto"' if is_active else ''

        # Nombre visible según idioma (Arte → Arte Visual, Art → Visual Art)
        if language == 'es' and category == 'Arte':
            visible_name = 'Arte Visual'
        elif language == 'en' and category == 'Art':
            visible_name = 'Visual Art'
        else:
            visible_name = category

        html += (
            f'<button class="etiqueta_enlace" aria-controls="{category.lower()}" '
            f'aria-expanded="{aria_expanded}" tabindex="0" '
            f'onclick="seccionAbrirPagina(\'{category.lower()}\', this)"{id_attr}>{visible_name}</button>\n'
        )

    # Contenido de cada categoría
    for i, (category, items) in enumerate(sections.items()):
        is_active = (i == 0)
        aria_hidden = 'false' if is_active else 'true'
        html += (
            f'<div id="{category.lower()}" class="etiqueta_contenido" aria-hidden="{aria_hidden}">\n'
        )

        # Misma regla para el título de la sección
        if language == 'es' and category == 'Arte':
            visible_name = 'Arte Visual'
        elif language == 'en' and category == 'Art':
            visible_name = 'Visual Art'
        else:
            visible_name = category

        html += f'\t<h3 class="separador">{visible_name}</h3>\n\t<ol style="font-size: 1.05882em;">\n'
        for name, url in items:
            if url:
                safe_name = name.replace("'", "&#39;").replace('"', "&quot;")
                html += f"\t\t<li><a href='{url}'>{safe_name}</a>.</li>\n"
        html += '\t</ol>\n</div>\n'

    html += '</div><!-- contenedor_full -->\n'

    # Sección Destacados / Featured
    if language == 'es':
        html += (
            '<div id="destacados">\n'
            '\t<h3>Destacados</h3>\n'
            '\t<ol>\n'
            "\t\t<li>\n"
            "\t\t\t<a href='https://www.proyectowww.com.ar/search/label/DESTACADOS'>\n"
            "\t\t\t\tListado de los artículos mas notorios o relevantes, de todas las secciones\n"
            "\t\t\t</a>.\n"
            "\t\t</li>\n"
            '\t</ol>\n'
            '</div>\n'
        )
    else:
        html += (
            '<div id="destacados">\n'
            '\t<h3>Featured</h3>\n'
            '\t<ol>\n'
            "\t\t<li>\n"
            "\t\t\t<a href='https://www.proyectowww.com.ar/search/label/DESTACADOS'>\n"
            "\t\t\t\tList of the most notable or relevant articles from all sections\n"
            "\t\t\t</a>.\n"
            "\t\t</li>\n"
            '\t</ol>\n'
            '</div>\n'
        )

    return html


def main():
    # Versión en español: leer secciones-es.txt → output-es.html
    sections_es = read_sections('secciones-es.txt')
    html_es = write_html_and_jsonld(sections_es, language='es')
    with open('output-es.html', 'w', encoding='utf-8') as f:
        f.write(html_es)

    # Versión en inglés: leer secciones-en.txt → output-en.html
    sections_en = read_sections('secciones-en.txt')
    html_en = write_html_and_jsonld(sections_en, language='en')
    with open('output-en.html', 'w', encoding='utf-8') as f:
        f.write(html_en)


if __name__ == "__main__":
    main()
Artículos relacionados
Inicio