Interface locale : rtg interface (voyages, sources, journées, récit, génération)
Serveur web local sans dépendance nouvelle (http.server, Jinja2, htmx et Leaflet fournis). Écrans : Mes voyages, nouveau voyage, un voyage (journées avec carte, rattachement des traces sans heures à un jour avec suggestion d'après les photos, sources avec choix de dossier, infos et récit, génération avec avancement et arrêt), réglages du site et vérification des outils. Chaîne : jours imposés ([dates_traces] dans voyage.toml), arrêt propre de la compression, suivi des étapes, état structuré (site.tableau_etat), titre du voyage repris de voyage.toml sur l'accueil. 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
608334b9d8
commit
06153a227e
37 files changed
+2618
-57
No files matched your search
@@ -0,0 +1,100 @@
|
||||
"""Interface : jours imposés aux traces sans heures, suggestion d'après les
|
||||
photos, et quelques échanges avec le serveur local."""
|
||||
import os
|
||||
import threading
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from datetime import date
|
||||
from http.server import ThreadingHTTPServer
|
||||
|
||||
import pytest
|
||||
|
||||
from rtg import config, sources
|
||||
from rtg.interface import serveur
|
||||
from test_chaine import _photo, pytestmark # noqa: F401 (mêmes prérequis)
|
||||
|
||||
|
||||
def _etape(chemin, lat0, lon0):
|
||||
pts = "".join(f'<trkpt lat="{lat0 + i * 0.01:.5f}" lon="{lon0 + i * 0.012:.5f}"></trkpt>' for i in range(50))
|
||||
with open(chemin, "w") as f:
|
||||
f.write(f'<?xml version="1.0"?><gpx version="1.1" creator="68degres.fr" '
|
||||
f'xmlns="http://www.topografix.com/GPX/1/1"><trk><trkseg>{pts}</trkseg></trk></gpx>')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def voyage_sans_heures(tmp_path):
|
||||
racine = tmp_path / "site"
|
||||
voyage = racine / "Ecosse-2027"
|
||||
gpx, photos = tmp_path / "gpx", tmp_path / "photos"
|
||||
for d in (voyage, gpx, photos):
|
||||
d.mkdir(parents=True)
|
||||
_etape(gpx / "Etape 1.gpx", 55.9, 3.2)
|
||||
_etape(gpx / "Etape 2.gpx", 56.7, 2.24)
|
||||
_photo(str(photos / "P1.jpg"), "2027:06:11 10:00:00", gps=(56.75, 2.30))
|
||||
config.ecrire_toml(str(racine / "site.toml"), {"titre": "Test"})
|
||||
config.ecrire_toml(str(voyage / "voyage.toml"), {
|
||||
"titre": "Écosse", "sources": {"traces": [str(gpx)], "videos": [], "photos": [str(photos)]}})
|
||||
return racine, voyage
|
||||
|
||||
|
||||
def _cfg(racine, voyage):
|
||||
return config.reglages_voyage(str(voyage), config.reglages_site(str(racine)))
|
||||
|
||||
|
||||
def test_jour_impose_et_suggestion(voyage_sans_heures):
|
||||
racine, voyage = voyage_sans_heures
|
||||
a = sources.analyser(str(voyage), _cfg(racine, voyage))
|
||||
assert not a.journees and len(a.non_datees) == 2
|
||||
medias = sources.positions_medias(str(voyage), a)
|
||||
par_nom = {os.path.basename(f): p for f, p in a.non_datees.items()}
|
||||
assert sources.suggerer_jour(par_nom["Etape 2.gpx"], medias) == (date(2027, 6, 11), 1)
|
||||
assert sources.suggerer_jour(par_nom["Etape 1.gpx"], medias) == (None, 0)
|
||||
|
||||
config.modifier_toml(str(voyage / "voyage.toml"), {"dates_traces": {"Etape 1.gpx": "2027-06-10"}})
|
||||
a = sources.analyser(str(voyage), _cfg(racine, voyage))
|
||||
assert sorted(a.journees) == [date(2027, 6, 10)]
|
||||
assert [os.path.basename(f) for f in a.non_datees] == ["Etape 2.gpx"]
|
||||
# Le titre et les sources ont survécu à la réécriture du fichier
|
||||
assert config.lire_toml(str(voyage / "voyage.toml"))["titre"] == "Écosse"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(voyage_sans_heures):
|
||||
racine, voyage = voyage_sans_heures
|
||||
app = serveur.Application(str(racine))
|
||||
srv = ThreadingHTTPServer(("127.0.0.1", 0), None)
|
||||
port = srv.server_address[1]
|
||||
srv.RequestHandlerClass = serveur.creer_gestionnaire(app, port)
|
||||
threading.Thread(target=srv.serve_forever, daemon=True).start()
|
||||
|
||||
def appel(chemin, donnees=None, htmx=True):
|
||||
req = urllib.request.Request(f"http://127.0.0.1:{port}{chemin}",
|
||||
data=urllib.parse.urlencode(donnees).encode() if donnees else None)
|
||||
if htmx:
|
||||
req.add_header("HX-Request", "true")
|
||||
try:
|
||||
with urllib.request.urlopen(req) as r:
|
||||
return r.status, r.read().decode()
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode()
|
||||
yield appel, racine, voyage
|
||||
srv.shutdown()
|
||||
|
||||
|
||||
def test_serveur(client):
|
||||
appel, racine, voyage = client
|
||||
code, html = appel("/")
|
||||
assert code == 200 and "Écosse" in html
|
||||
code, html = appel("/voyage/analyse?v=Ecosse-2027")
|
||||
assert "2 traces à rattacher" in html and "11 juin 2027" in html
|
||||
# Répartir : un jour par trace dans l'ordre des noms
|
||||
code, html = appel("/voyage/dates-suite", {"v": "Ecosse-2027", "debut": "2027-06-10"})
|
||||
assert "2 journées" in html
|
||||
assert config.lire_toml(str(voyage / "voyage.toml"))["dates_traces"] == {
|
||||
"Etape 1.gpx": "2027-06-10", "Etape 2.gpx": "2027-06-11"}
|
||||
# Requête de modification sans l'en-tête htmx : refusée
|
||||
code, _ = appel("/voyage/date", {"v": "Ecosse-2027", "fichier": "Etape 1.gpx", "jour": ""}, htmx=False)
|
||||
assert code == 403
|
||||
# Voyage hors du site : refusé
|
||||
code, html = appel("/voyage/analyse?v=../../etc")
|
||||
assert "introuvable" in html.lower()
|
||||
Reference in new issue
Block a user