Compare commits

...
3 Commits
Author SHA1 Message Date
ArtemiaandClaude Opus 5.5 47528bae8d README : générer les documents sans ligne de commande
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:38:02 +02:00
ArtemiaandClaude Opus 5.5 d4faa4872c Ajoute les lanceurs double-cliquables (Windows, macOS, Linux)
Chaque lanceur trouve ou propose d'installer Python puis appelle l'assistant. .gitattributes impose LF pour .sh/.command et CRLF pour .bat ; bit exécutable posé dans git. Sous Windows, chcp et les tests Python lisent <nul pour ne pas vider l'entrée standard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:38:02 +02:00
ArtemiaandClaude Opus 5.5 90982e71c9 Ajoute l'assistant de génération des documents (scripts/assistant.py)
Vérifie ou installe Quarto (winget, Homebrew, paquet officiel), prépare le venv des schémas (réinstallé seulement si requirements.txt change), propose un menu (PDF, ODT, HTML ; tous les supports ou un module ; régénération optionnelle des schémas), génère et ouvre le dossier de sortie. Bibliothèque standard uniquement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:38:01 +02:00
6 changed files with 410 additions and 0 deletions
+4
View File
@@ -1,2 +1,6 @@
*.odt binary *.odt binary
* text=auto * text=auto
# Fins de ligne impos?es pour les lanceurs (sinon ils ?chouent sur l'autre syst?me)
*.sh text eol=lf
*.command text eol=lf
*.bat text eol=crlf
+36
View File
@@ -0,0 +1,36 @@
@echo off
rem Assistant de generation des supports (Windows) : double-cliquer sur ce fichier.
setlocal
chcp 65001 >nul <nul
cd /d "%~dp0"
call :trouver_python
if defined PY goto lancer
echo Python est nécessaire mais n'a pas été trouvé sur cet ordinateur.
choice /c ON /m "Installer Python automatiquement (via winget)"
if errorlevel 2 goto manuel
winget install -e --id Python.Python.3.12 --accept-source-agreements --accept-package-agreements
call :trouver_python
if not defined PY goto manuel
:lancer
%PY% scripts\assistant.py
echo.
pause
exit /b
:manuel
echo.
echo Installez Python depuis la page qui va s'ouvrir,
echo en cochant la case "Add python.exe to PATH", puis relancez ce fichier.
start "" https://www.python.org/downloads/
pause
exit /b 1
:trouver_python
set "PY="
py -3 -c "import venv" <nul >nul 2>nul && set "PY=py -3" && exit /b
python -c "import venv" <nul >nul 2>nul && set "PY=python" && exit /b
for /d %%D in ("%LOCALAPPDATA%\Programs\Python\Python3*") do if exist "%%~D\python.exe" set PY="%%~D\python.exe"
exit /b
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
# Assistant de génération des supports (macOS) : double-cliquer sur ce fichier dans le Finder.
cd "$(dirname "$0")" || exit 1
trouver_python() {
for c in python3 /opt/homebrew/bin/python3 /usr/local/bin/python3; do
if command -v "$c" >/dev/null 2>&1 && "$c" -c "import venv" >/dev/null 2>&1; then
PY="$c"
return 0
fi
done
return 1
}
if ! trouver_python; then
echo "Python 3 est nécessaire mais n'a pas été trouvé."
read -r -p "L'installer automatiquement (outils de développement Apple) ? [O/n] " rep
if [[ ! "$rep" =~ ^[nN] ]]; then
xcode-select --install 2>/dev/null
read -r -p "Suivez la fenêtre d'installation, puis appuyez sur Entrée une fois terminé... "
fi
if ! trouver_python; then
echo "Installez Python depuis la page qui va s'ouvrir, puis relancez ce fichier."
open "https://www.python.org/downloads/"
read -r -p "Appuyez sur Entrée pour fermer... "
exit 1
fi
fi
"$PY" scripts/assistant.py
echo
read -r -p "Appuyez sur Entrée pour fermer... "
+22
View File
@@ -20,10 +20,32 @@ Les sources sont en Markdown [Quarto](https://quarto.org) (`modules/*.qmd`) et p
| `12-projetfinal` | Projet final | | `12-projetfinal` | Projet final |
| `synthese-materiel` | Synthèse et liste de matériel | | `synthese-materiel` | Synthèse et liste de matériel |
## Générer les documents sans ligne de commande
Après avoir téléchargé le dépôt (bouton « Télécharger » → ZIP, puis décompresser), double-cliquez sur le fichier
correspondant à votre système :
| Système | Fichier à lancer |
|---|---|
| Windows | `Generer-documents.bat` |
| macOS | `Generer-documents.command` (la première fois : clic droit → Ouvrir, pour passer l'avertissement de sécurité) |
| Linux | `generer-documents.sh` (clic droit → « Exécuter comme un programme », ou `./generer-documents.sh` dans un terminal) |
Une fenêtre s'ouvre et vous guide :
1. elle vérifie que **Python** et **Quarto** sont installés, et propose de les installer automatiquement sinon ;
2. elle prépare l'environnement Python des schémas (dossier `.venv`, une seule fois) ;
3. elle vous demande le **format** (PDF, LibreOffice .odt ou site web HTML) et les **supports** à produire
(tous, ou un seul module) ;
4. elle génère les documents et ouvre le dossier `_output/modules` qui les contient.
Il suffit de taper le numéro de votre choix puis Entrée (Entrée seul = choix par défaut).
## Organisation ## Organisation
``` ```
modules/ sources des supports (.qmd) modules/ sources des supports (.qmd)
scripts/ assistant de génération (appelé par les fichiers Generer-documents.*)
modele/ gabarit PDF (Typst) et thème de coloration du code modele/ gabarit PDF (Typst) et thème de coloration du code
schemas/ schémas décrits en Python (schemdraw + matplotlib), rendus en svg/ et png/ schemas/ schémas décrits en Python (schemdraw + matplotlib), rendus en svg/ et png/
_quarto.yml configuration du projet _quarto.yml configuration du projet
+53
View File
@@ -0,0 +1,53 @@
#!/bin/bash
# Assistant de génération des supports (Linux) : clic droit > « Exécuter comme un programme »,
# ou dans un terminal : ./generer-documents.sh
cd "$(dirname "$(readlink -f "$0")")" || exit 1
# Lancé hors d'un terminal (double-clic) : on se relance dans une fenêtre de terminal.
if [ ! -t 0 ]; then
for t in x-terminal-emulator gnome-terminal konsole xfce4-terminal xterm; do
if command -v "$t" >/dev/null 2>&1; then
case "$t" in
gnome-terminal) exec "$t" -- "$0" ;;
*) exec "$t" -e "$0" ;;
esac
fi
done
fi
trouver_python() {
if command -v python3 >/dev/null 2>&1 && python3 -c "import venv, ensurepip" >/dev/null 2>&1; then
PY=python3
return 0
fi
return 1
}
if ! trouver_python; then
echo "Python 3 (avec le module venv) est nécessaire mais n'a pas été trouvé."
if command -v apt-get >/dev/null 2>&1; then
cmd="sudo apt-get install -y python3 python3-venv"
elif command -v dnf >/dev/null 2>&1; then
cmd="sudo dnf install -y python3"
elif command -v pacman >/dev/null 2>&1; then
cmd="sudo pacman -S --needed --noconfirm python"
elif command -v zypper >/dev/null 2>&1; then
cmd="sudo zypper install -y python3"
fi
if [ -n "$cmd" ]; then
read -r -p "Lancer l'installation ($cmd) ? Votre mot de passe sera demandé. [O/n] " rep
if [[ ! "$rep" =~ ^[nN] ]]; then
$cmd
fi
fi
if ! trouver_python; then
echo "Installez Python 3 et son module venv avec le gestionnaire de paquets de votre distribution,"
echo "puis relancez ce fichier."
read -r -p "Appuyez sur Entrée pour fermer... "
exit 1
fi
fi
"$PY" scripts/assistant.py
echo
read -r -p "Appuyez sur Entrée pour fermer... "
+263
View File
@@ -0,0 +1,263 @@
"""Assistant de génération des supports de la formation (Windows, macOS, Linux).
Lancé par Generer-documents.bat / Generer-documents.command / generer-documents.sh.
N'utilise que la bibliothèque standard de Python.
Étapes : vérifier (ou installer) Quarto, préparer l'environnement Python des schémas,
demander quoi générer, lancer Quarto, ouvrir le dossier des documents produits.
"""
import hashlib
import json
import os
import platform
import re
import shutil
import subprocess
import sys
import tempfile
import time
import urllib.request
import webbrowser
from pathlib import Path
RACINE = Path(__file__).resolve().parent.parent
MODULES = RACINE / "modules"
SORTIE = RACINE / "_output"
VENV = RACINE / ".venv"
EXIGENCES = RACINE / "schemas" / "requirements.txt"
PAGE_QUARTO = "https://quarto.org/docs/get-started/"
SYSTEME = platform.system() # "Windows", "Darwin" (macOS) ou "Linux"
FORMATS = [ # (libellé, option --to de Quarto, extension produite)
("PDF", "typst", ".pdf"),
("LibreOffice (.odt)", "odt", ".odt"),
("Site web (HTML)", "html", ".html"),
]
# --- Dialogue ----------------------------------------------------------------
def titre(texte):
print("\n" + "=" * 64 + f"\n {texte}\n" + "=" * 64)
def oui_non(question, defaut=True):
suffixe = " [O/n] " if defaut else " [o/N] "
while True:
reponse = input(question + suffixe).strip().lower()
if not reponse:
return defaut
if reponse in ("o", "oui", "y", "yes"):
return True
if reponse in ("n", "non", "no"):
return False
print(" Répondez par o (oui) ou n (non).")
def choisir(question, options, defaut=1):
"""Affiche une liste numérotée et renvoie l'index (0-based) choisi."""
print(f"\n{question}")
for i, option in enumerate(options, 1):
print(f" {i:>2}. {option}" + (" (par défaut)" if i == defaut else ""))
while True:
reponse = input(f"Votre choix [{defaut}] : ").strip()
if not reponse:
return defaut - 1
if reponse.isdigit() and 1 <= int(reponse) <= len(options):
return int(reponse) - 1
print(f" Tapez un nombre entre 1 et {len(options)}.")
def attendre(message="Appuyez sur Entrée pour continuer..."):
try:
input(message)
except EOFError:
pass
# --- Quarto --------------------------------------------------------------------
def trouver_quarto():
trouve = shutil.which("quarto")
if trouve:
return trouve
candidats = {
"Windows": [Path(os.environ.get("ProgramFiles", r"C:\Program Files")) / "Quarto/bin/quarto.exe",
Path(os.environ.get("LOCALAPPDATA", "")) / "Programs/Quarto/bin/quarto.exe"],
"Darwin": [Path("/Applications/quarto/bin/quarto"), Path("/usr/local/bin/quarto"),
Path("/opt/homebrew/bin/quarto")],
"Linux": [Path("/opt/quarto/bin/quarto"), Path("/usr/local/bin/quarto"),
Path.home() / ".local/bin/quarto"],
}.get(SYSTEME, [])
for chemin in candidats:
if chemin.is_file():
return str(chemin)
return None
def telecharger_quarto(motif):
"""Télécharge le fichier d'installation de la dernière version de Quarto dont le nom contient `motif`."""
with urllib.request.urlopen("https://api.github.com/repos/quarto-dev/quarto-cli/releases/latest",
timeout=30) as reponse:
version = json.load(reponse)
fichier = next(a for a in version["assets"] if a["name"].endswith(motif))
destination = Path(tempfile.gettempdir()) / fichier["name"]
print(f" Téléchargement de {fichier['name']} ({fichier['size'] // 1_000_000} Mo)...")
urllib.request.urlretrieve(fichier["browser_download_url"], destination)
return destination
def installer_quarto():
try:
if SYSTEME == "Windows":
if shutil.which("winget"):
subprocess.run(["winget", "install", "-e", "--id", "Posit.Quarto",
"--accept-source-agreements", "--accept-package-agreements"])
else:
subprocess.run(["msiexec", "/i", str(telecharger_quarto("win.msi"))])
elif SYSTEME == "Darwin":
if shutil.which("brew"):
subprocess.run(["brew", "install", "--cask", "quarto"])
else:
print(" L'installateur va s'ouvrir : suivez ses étapes puis revenez ici.")
subprocess.run(["open", "-W", str(telecharger_quarto("macos.pkg"))])
elif SYSTEME == "Linux" and shutil.which("apt-get"):
arch = "arm64" if platform.machine().lower() in ("aarch64", "arm64") else "amd64"
paquet = telecharger_quarto(f"linux-{arch}.deb")
print(" Votre mot de passe peut être demandé (installation système).")
subprocess.run(["sudo", "apt-get", "install", "-y", str(paquet)])
else:
return False
except Exception as erreur: # réseau indisponible, fichier introuvable…
print(f" L'installation automatique a échoué : {erreur}")
return False
return True
def verifier_quarto():
titre("1/3 Quarto (outil de mise en page)")
quarto = trouver_quarto()
if quarto:
print(f" Quarto est installé : {quarto}")
return quarto
print(" Quarto n'est pas installé. Il est indispensable pour produire les documents.")
if oui_non(" L'installer automatiquement maintenant ?"):
installer_quarto()
quarto = trouver_quarto()
while not quarto:
print(f"\n Installez Quarto depuis la page qui va s'ouvrir : {PAGE_QUARTO}")
webbrowser.open(PAGE_QUARTO)
attendre(" Une fois l'installation terminée, appuyez sur Entrée...")
quarto = trouver_quarto()
if not quarto and not oui_non(" Quarto reste introuvable. Réessayer ?"):
raise SystemExit(1)
print(f" Quarto est prêt : {quarto}")
return quarto
# --- Environnement Python des schémas ----------------------------------------------
def python_du_venv():
return VENV / ("Scripts/python.exe" if SYSTEME == "Windows" else "bin/python")
def preparer_venv():
titre("2/3 Environnement Python (schémas)")
empreinte = hashlib.sha256(EXIGENCES.read_bytes()).hexdigest()
temoin = VENV / ".empreinte-requirements"
if python_du_venv().exists() and temoin.exists() and temoin.read_text().strip() == empreinte:
print(" Déjà prêt.")
return True
try:
if not python_du_venv().exists():
print(" Création de l'environnement virtuel .venv ...")
subprocess.run([sys.executable, "-m", "venv", str(VENV)], check=True)
print(" Installation des bibliothèques (la première fois, cela peut prendre quelques minutes)...")
subprocess.run([str(python_du_venv()), "-m", "pip", "install", "--disable-pip-version-check",
"-q", "-r", str(EXIGENCES)], check=True)
except subprocess.CalledProcessError:
print(" Impossible de préparer l'environnement Python (connexion Internet ?).")
print(" Ce n'est pas bloquant : les documents peuvent être produits avec les schémas existants.")
return False
temoin.write_text(empreinte)
print(" Environnement prêt.")
return True
# --- Génération --------------------------------------------------------------------
def lister_supports():
supports = []
for qmd in sorted(MODULES.glob("*.qmd")):
m = re.search(r'^title:\s*"?(.*?)"?\s*$', qmd.read_text(encoding="utf-8"), re.M)
supports.append((qmd, m.group(1) if m else qmd.stem))
return supports
def ouvrir_dossier(dossier):
try:
if SYSTEME == "Windows":
os.startfile(dossier)
elif SYSTEME == "Darwin":
subprocess.run(["open", str(dossier)])
else:
subprocess.run(["xdg-open", str(dossier)])
except Exception:
pass
def generer(quarto, venv_ok):
titre("3/3 Génération des documents")
i_format = choisir("Quel format ?", [f[0] for f in FORMATS])
libelle, cible, extension = FORMATS[i_format]
supports = lister_supports()
i_portee = choisir("Quels supports ?", ["Tous les supports"] + [t for _, t in supports])
if venv_ok and oui_non("\nRégénérer aussi les schémas (utile seulement si vous les avez modifiés) ?",
defaut=False):
subprocess.run([str(python_du_venv()), str(RACINE / "schemas" / "generer.py")], cwd=RACINE)
debut = time.time()
if i_portee == 0:
print(f"\nProduction de tous les supports en {libelle} : patientez...\n")
commandes = [[quarto, "render", "--to", cible]]
else:
qmd = supports[i_portee - 1][0]
print(f"\nProduction de « {supports[i_portee - 1][1]} » en {libelle}...\n")
commandes = [[quarto, "render", str(qmd.relative_to(RACINE)), "--to", cible]]
for commande in commandes:
if subprocess.run(commande, cwd=RACINE).returncode != 0:
print("\n*** La génération a échoué. Le message d'erreur se trouve juste au-dessus. ***")
return False
produits = sorted(p for p in SORTIE.rglob(f"*{extension}") if p.stat().st_mtime >= debut - 1)
titre("Terminé")
for p in produits:
print(f" {p.relative_to(RACINE)}")
print(f"\n {len(produits)} document(s) dans le dossier : {SORTIE}")
ouvrir_dossier(SORTIE / "modules" if (SORTIE / "modules").is_dir() else SORTIE)
return True
def main():
print("Assistant de génération — Formation ESP32-S3 avec Arduino Framework")
if sys.version_info < (3, 9):
print("Python 3.9 ou plus récent est nécessaire.")
raise SystemExit(1)
quarto = verifier_quarto()
venv_ok = preparer_venv()
while True:
generer(quarto, venv_ok)
if not oui_non("\nGénérer d'autres documents ?", defaut=False):
break
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nInterrompu.")
except SystemExit:
raise
except Exception as erreur:
print(f"\nErreur inattendue : {erreur}")
raise SystemExit(1)