Interface : corrections à la souris et accès protégé
Onglet « Carte et corrections » : glisser une photo ou une vidéo, placer les médias sans position, masquer des zones de trace (pour un voyage, ou zone privée pour tous). Corrections notées dans voyage.toml ([positions], [corrections]) ou site.toml ([confidentialite]), appliquées par les pages ; les originaux ne changent pas. L'extraction garde les médias datés sans position pour qu'on puisse les placer. Accès : clé d'accès aléatoire à chaque lancement, session par cookie HttpOnly/SameSite=Strict, mot de passe facultatif (rtg mot-de-passe, empreinte scrypt hors du site, blocage après 5 échecs), accès réseau seulement avec mot de passe (--distant), HTTPS possible (--certificat, --cle), Content-Security-Policy et interdiction des cadres. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mf2kATMtqVABHSYdxJMnim
This commit is contained in:
1 parent
06153a227e
commit
71779f6f53
17 files changed
+1047
-38
No files matched your search
+23
-8
@@ -35,6 +35,8 @@ from folium import plugins
|
||||
from folium.plugins import MarkerCluster
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from rtg import corrections
|
||||
|
||||
DOSSIER_TEMPLATES = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates")
|
||||
# Fixés par generer() : racine du site et adresse publique.
|
||||
CONTEXTE = {"racine": None, "site_url": "https://roadtrip.ouaibe.fr/"}
|
||||
@@ -101,13 +103,18 @@ def bornes(points, marge=0.02, robuste=True):
|
||||
len(points) - len(gardes))
|
||||
|
||||
|
||||
def lire_csv(chemin):
|
||||
"""CSV d'extraction → DataFrame trié, avec colonnes DT (datetime) et Jour."""
|
||||
def lire_csv(chemin, positions=None):
|
||||
"""CSV d'extraction → DataFrame trié, avec colonnes DT (datetime) et Jour.
|
||||
`positions` : {fichier: (lat, lon)} placés à la main, qui l'emportent."""
|
||||
vide = pd.DataFrame(columns=COLONNES + ['DT', 'Jour'])
|
||||
# Un voyage sans photo produit un CSV de 0 octet, que pandas refuse de lire.
|
||||
if not os.path.exists(chemin) or os.path.getsize(chemin) == 0:
|
||||
return vide
|
||||
df = pd.read_csv(chemin, names=COLONNES, header=None)
|
||||
for nom, (lat, lon) in (positions or {}).items():
|
||||
lignes = df['Fichier'].astype(str).str.strip() == nom
|
||||
df.loc[lignes, 'Latitude'] = lat
|
||||
df.loc[lignes, 'Longitude'] = lon
|
||||
df = df[df['Latitude'].notnull() & df['Longitude'].notnull()]
|
||||
df = df[(df['Latitude'] != 0) & (df['Longitude'] != 0)].copy()
|
||||
if df.empty:
|
||||
@@ -312,12 +319,16 @@ def exporter_trace_globale(base_dir, pistes):
|
||||
class Voyage:
|
||||
"""Données communes à toutes les pages d'un voyage."""
|
||||
|
||||
def __init__(self, base_dir):
|
||||
def __init__(self, base_dir, cfg=None):
|
||||
self.base_dir = base_dir
|
||||
self.nom = os.path.basename(base_dir.rstrip('/')) or "voyage"
|
||||
self.routes_dir = os.path.join(base_dir, "routes")
|
||||
self.df_v = lire_csv(os.path.join(base_dir, "export_videos.csv"))
|
||||
self.df_p = lire_csv(os.path.join(base_dir, "export_photos.csv"))
|
||||
cfg = cfg or {}
|
||||
self.zones = corrections.masques(cfg)
|
||||
pos = corrections.positions(cfg)
|
||||
self.df_v = lire_csv(os.path.join(base_dir, "export_videos.csv"), pos)
|
||||
self.df_p = lire_csv(os.path.join(base_dir, "export_photos.csv"), pos)
|
||||
self.points_masques = 0
|
||||
jours_gpx = set()
|
||||
for f in fichiers_gpx(self.routes_dir):
|
||||
m = re.search(r'(\d{4}-\d{2}-\d{2})', f)
|
||||
@@ -343,6 +354,7 @@ class Voyage:
|
||||
continue
|
||||
self.gpx_du_jour[jour] = gpx_file
|
||||
for track in gpx.tracks:
|
||||
self.points_masques += corrections.filtrer_piste(track, self.zones)
|
||||
pts = [[p.latitude, p.longitude] for seg in track.segments for p in seg.points]
|
||||
if pts:
|
||||
if not track.name:
|
||||
@@ -396,19 +408,22 @@ def carte_du_jour(v, jour, version, journal, pre, lien_accueil, bouton_telecharg
|
||||
return m_day, media_coords
|
||||
|
||||
|
||||
def generer(base_dir, racine=None, site_url=None):
|
||||
"""Pages d'un voyage. `racine` : racine du site (bouton « Retour »)."""
|
||||
def generer(base_dir, racine=None, site_url=None, cfg=None):
|
||||
"""Pages d'un voyage. `racine` : racine du site (bouton « Retour ») ;
|
||||
`cfg` : réglages du voyage (corrections faites dans l'interface)."""
|
||||
CONTEXTE["racine"] = racine
|
||||
if site_url:
|
||||
CONTEXTE["site_url"] = site_url.rstrip('/') + '/'
|
||||
base_dir = os.path.abspath(base_dir)
|
||||
html_dir = os.path.join(base_dir, "html")
|
||||
os.makedirs(html_dir, exist_ok=True)
|
||||
v = Voyage(base_dir)
|
||||
v = Voyage(base_dir, cfg)
|
||||
if not v.jours:
|
||||
print(f"❌ {v.nom} : aucune date trouvée (ni GPX daté, ni média géolocalisé).")
|
||||
return False
|
||||
v.lire_traces()
|
||||
if v.points_masques:
|
||||
print(f" 🙈 {v.points_masques} point(s) de trace retiré(s) par les zones masquées")
|
||||
|
||||
version = installer_assets(base_dir)
|
||||
journal = Journal(base_dir, v.jours)
|
||||
|
||||
Reference in new issue
Block a user