139 lines
5.1 KiB
Python
Executable File
139 lines
5.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Page d'accueil listant tous les voyages du site.
|
|
|
|
Usage : ./genere_index_general.py [racine_du_site]
|
|
|
|
Un voyage est reconnu à la présence de son fichier mini.html (la carte
|
|
miniature produite par genere_carte.py).
|
|
"""
|
|
import os
|
|
import re
|
|
import sys
|
|
import html
|
|
from urllib.parse import quote
|
|
|
|
EXCLURE = {'html', 'Scripts', '__pycache__', '.git', 'node_modules', 'videos',
|
|
'photos', 'rushs', 'routes'}
|
|
OUTPUT_FILE = "index.html"
|
|
|
|
|
|
def cle_de_tri(nom):
|
|
"""Trie du plus récent au plus ancien quand un millésime est détecté
|
|
dans le nom du dossier, alphabétiquement sinon."""
|
|
m = re.search(r'(19|20)\d{2}', nom)
|
|
annee = int(m.group(0)) if m else 0
|
|
return (-annee, nom.lower())
|
|
|
|
|
|
def lien(trip, suffixe=""):
|
|
"""URL sûre : les espaces et les & des noms de dossier sont encodés."""
|
|
return html.escape(quote(trip) + suffixe)
|
|
|
|
|
|
def generer_index_general(racine="."):
|
|
road_trips = []
|
|
for d in sorted(os.listdir(racine)):
|
|
if d in EXCLURE or d.startswith('.'):
|
|
continue
|
|
chemin = os.path.join(racine, d)
|
|
if os.path.isdir(chemin) and os.path.exists(os.path.join(chemin, 'mini.html')):
|
|
road_trips.append(d)
|
|
|
|
road_trips.sort(key=cle_de_tri)
|
|
|
|
cartes = []
|
|
for trip in road_trips:
|
|
# Le nom est échappé : un dossier « rando & vercors » produisait
|
|
# sinon du HTML invalide (& non échappé) et un lien cassé.
|
|
titre = html.escape(trip.replace('-', ' ').replace('_', ' '))
|
|
cartes.append(f""" <article class="card">
|
|
<iframe class="map-container" src="{lien(trip, '/mini.html')}"
|
|
loading="lazy" title="Carte de {titre}"></iframe>
|
|
<div class="card-content">
|
|
<h2>{titre}</h2>
|
|
<a href="{lien(trip, '/index.html')}" class="btn">Voir le voyage</a>
|
|
</div>
|
|
</article>""")
|
|
|
|
if road_trips:
|
|
corps = '<div class="grid">\n' + "\n".join(cartes) + '\n </div>'
|
|
sous_titre = f"{len(road_trips)} voyage" + ("s" if len(road_trips) > 1 else "")
|
|
else:
|
|
corps = ('<p class="vide">Aucun voyage pour l\'instant.<br>'
|
|
'Lance <code>genere_site.sh</code> pour en générer.</p>')
|
|
sous_titre = ""
|
|
|
|
# Pas d'espace avant <!DOCTYPE html> : la version précédente commençait par
|
|
# un saut de ligne et de l'indentation, ce qui bascule certains navigateurs
|
|
# en mode « quirks » et fausse la mise en page.
|
|
html_content = f"""<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Nos Road-Trips</title>
|
|
<style>
|
|
:root {{ color-scheme: light dark; }}
|
|
* {{ box-sizing: border-box; }}
|
|
body {{
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
background: #f4f4f9; margin: 0; padding: 20px; color: #222;
|
|
}}
|
|
header {{ text-align: center; margin-bottom: 30px; }}
|
|
h1 {{ margin: 0 0 6px; font-size: clamp(1.6rem, 4vw, 2.4rem); }}
|
|
.sous-titre {{ color: #777; font-size: 0.9rem; }}
|
|
/* min() évite le débordement horizontal sur téléphone : l'ancienne règle
|
|
minmax(400px, 1fr) forçait 400 px de large sur un écran de 375 px. */
|
|
.grid {{
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(min(400px, 100%), 1fr));
|
|
gap: 30px; max-width: 1200px; margin: 0 auto;
|
|
}}
|
|
.card {{
|
|
background: #fff; border-radius: 15px; overflow: hidden;
|
|
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
|
|
transition: transform .2s, box-shadow .2s;
|
|
}}
|
|
.card:hover {{ transform: translateY(-4px); box-shadow: 0 14px 28px rgba(0,0,0,0.15); }}
|
|
/* loading="lazy" : sans ça, ouvrir la page chargeait une carte Leaflet
|
|
complète par voyage, d'un coup. */
|
|
.map-container {{ width: 100%; height: 300px; border: none; pointer-events: none; display: block; }}
|
|
.card-content {{ padding: 20px; text-align: center; }}
|
|
h2 {{ text-transform: capitalize; margin: 0 0 14px; font-size: 1.25rem; }}
|
|
.btn {{
|
|
display: inline-block; padding: 10px 25px; background: #3498db; color: #fff;
|
|
text-decoration: none; border-radius: 25px; font-weight: bold;
|
|
}}
|
|
.btn:hover {{ background: #2980b9; }}
|
|
.vide {{ text-align: center; color: #777; margin-top: 60px; line-height: 1.7; }}
|
|
code {{ background: #e8e8ef; padding: 2px 6px; border-radius: 4px; }}
|
|
@media (prefers-color-scheme: dark) {{
|
|
body {{ background: #16161a; color: #e8e8ea; }}
|
|
.card {{ background: #232329; box-shadow: 0 10px 20px rgba(0,0,0,0.4); }}
|
|
.sous-titre, .vide {{ color: #9a9aa2; }}
|
|
code {{ background: #2e2e36; }}
|
|
}}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>🌍 Nos Road-Trips</h1>
|
|
<div class="sous-titre">{sous_titre}</div>
|
|
</header>
|
|
<main>
|
|
{corps}
|
|
</main>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
sortie = os.path.join(racine, OUTPUT_FILE)
|
|
with open(sortie, "w", encoding="utf-8") as f:
|
|
f.write(html_content)
|
|
print(f"🌍 Index général : {len(road_trips)} voyage(s) → {sortie}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
racine = sys.argv[1] if len(sys.argv) > 1 else "."
|
|
generer_index_general(racine)
|