Compare commits

..
5 Commits
Author SHA1 Message Date
ArtemiaandClaude Opus 5.5 503aa66dc0 Polices libres Noto Sans / Noto Sans Mono, vérifiées et installées par l'assistant
Segoe UI et Consolas (polices Microsoft, non redistribuables) provoquaient des avertissements Typst sous Linux et macOS. Le gabarit utilise désormais Noto Sans et Noto Sans Mono. L'assistant vérifie leur présence (quarto typst fonts) et sinon les installe : paquets système sous Linux, ou téléchargement depuis le dépôt officiel Noto dans le dossier de polices de l'utilisateur (Windows, macOS, et secours Linux).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 17:00:53 +02:00
ArtemiaandClaude Opus 5.5 5b312f7459 Assistant : installe Quarto sous Arch Linux via l'AUR (yay ou paru)
Quarto n'est pas dans les dépôts officiels d'Arch. Si pacman ne connaît pas le paquet, l'assistant utilise yay (ou paru) pour installer quarto-cli-bin (binaire officiel, à jour), sans sudo puisque ces outils l'appellent eux-mêmes. Sans assistant AUR, il explique la marche à suivre.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 16:48:17 +02:00
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
7 changed files with 517 additions and 3 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... "
+26 -1
View File
@@ -20,10 +20,33 @@ 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**, **Quarto** et les polices **Noto Sans / Noto Sans Mono** sont installés,
et propose de les installer automatiquement sinon (sous Linux, les polices sont généralement déjà présentes) ;
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
@@ -32,7 +55,9 @@ _output/ PDF générés (non versionnés)
## Produire les PDF ## Produire les PDF
Prérequis : [Quarto](https://quarto.org/docs/get-started/) (inclut Pandoc et Typst). Prérequis : [Quarto](https://quarto.org/docs/get-started/) (inclut Pandoc et Typst) et les polices libres
[Noto Sans et Noto Sans Mono](https://notofonts.github.io/) (paquets `noto-fonts` sous Arch,
`fonts-noto-core fonts-noto-mono` sous Debian/Ubuntu).
``` ```
quarto render # tous les supports quarto render # tous les supports
+2 -2
View File
@@ -17,9 +17,9 @@ format:
margin: margin:
x: 2cm x: 2cm
y: 2.2cm y: 2.2cm
mainfont: "Segoe UI" mainfont: "Noto Sans"
fontsize: 10pt fontsize: 10pt
codefont: "Consolas" codefont: "Noto Sans Mono"
toc: false toc: false
highlight-style: modele/code.theme highlight-style: modele/code.theme
include-in-header: modele/entete.typ include-in-header: modele/entete.typ
+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... "
+364
View File
@@ -0,0 +1,364 @@
"""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)])
elif SYSTEME == "Linux" and shutil.which("pacman"):
return installer_quarto_arch()
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 installer_quarto_arch():
"""Arch Linux et dérivées : dépôts officiels si le paquet y existe, sinon AUR via yay (ou paru)."""
for nom in ("quarto-cli", "quarto"):
if subprocess.run(["pacman", "-Si", nom], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL).returncode == 0:
print(" Votre mot de passe peut être demandé (installation système).")
subprocess.run(["sudo", "pacman", "-S", "--needed", nom])
return True
paquet_aur = "quarto-cli-bin" # binaire officiel empaqueté, à jour (quarto-cli compile depuis les sources)
for assistant_aur in ("yay", "paru"):
if shutil.which(assistant_aur):
print(f" Quarto n'est disponible que dans l'AUR : installation de « {paquet_aur} » avec {assistant_aur}.")
print(" Validez les questions posées ; votre mot de passe sera demandé à la fin.")
# Jamais avec sudo : yay et paru s'exécutent en utilisateur normal et appellent sudo eux-mêmes.
subprocess.run([assistant_aur, "-S", "--needed", paquet_aur])
return True
print(" Sous Arch, Quarto n'est disponible que dans l'AUR (paquet « quarto-cli-bin »),")
print(" et aucun assistant AUR (yay ou paru) n'a été trouvé sur cet ordinateur.")
print(" Installez yay (https://github.com/Jguer/yay) puis relancez, ou installez le paquet à la main.")
return False
def verifier_quarto():
titre("1/4 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
# --- Polices -----------------------------------------------------------------------
DEPOT_NOTO = "https://github.com/notofonts/notofonts.github.io/raw/main/fonts"
POLICES = { # famille attendue par _quarto.yml -> fichiers à installer si elle manque
"Noto Sans": [f"NotoSans/hinted/ttf/NotoSans-{s}.ttf" for s in
("Regular", "Italic", "SemiBold", "SemiBoldItalic", "Bold", "BoldItalic")],
"Noto Sans Mono": [f"NotoSansMono/hinted/ttf/NotoSansMono-{s}.ttf" for s in ("Regular", "Bold")],
}
PAQUETS_POLICES = { # gestionnaire Linux -> commande d'installation des deux familles
"apt-get": ["sudo", "apt-get", "install", "-y", "fonts-noto-core", "fonts-noto-mono"],
"pacman": ["sudo", "pacman", "-S", "--needed", "noto-fonts"],
"dnf": ["sudo", "dnf", "install", "-y", "google-noto-sans-fonts", "google-noto-sans-mono-fonts"],
"zypper": ["sudo", "zypper", "install", "-y", "noto-sans-fonts", "noto-sans-mono-fonts"],
}
def polices_manquantes(quarto):
"""Familles absentes, d'après la liste des polices réellement visibles par Typst."""
resultat = subprocess.run([quarto, "typst", "fonts"], capture_output=True, text=True,
encoding="utf-8", errors="replace")
sortie = resultat.stdout + resultat.stderr # Quarto écrit la liste sur la sortie d'erreur
familles = {ligne.strip() for ligne in sortie.splitlines()}
return [f for f in POLICES if f not in familles]
def dossier_polices_utilisateur():
if SYSTEME == "Windows":
return Path(os.environ["LOCALAPPDATA"]) / "Microsoft/Windows/Fonts"
if SYSTEME == "Darwin":
return Path.home() / "Library/Fonts"
return Path.home() / ".local/share/fonts"
def telecharger_polices(familles):
"""Installe les polices pour l'utilisateur courant (aucun droit administrateur nécessaire)."""
dossier = dossier_polices_utilisateur()
dossier.mkdir(parents=True, exist_ok=True)
for famille in familles:
for chemin in POLICES[famille]:
cible = dossier / Path(chemin).name
print(f" Téléchargement de {cible.name}...")
urllib.request.urlretrieve(f"{DEPOT_NOTO}/{chemin}", cible)
if SYSTEME == "Windows": # déclarer la police pour l'utilisateur (comme « Installer » dans l'Explorateur)
import winreg
with winreg.OpenKey(winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows NT\CurrentVersion\Fonts", 0,
winreg.KEY_SET_VALUE) as cle:
winreg.SetValueEx(cle, f"{cible.stem} (TrueType)", 0, winreg.REG_SZ, str(cible))
if SYSTEME == "Linux" and shutil.which("fc-cache"):
subprocess.run(["fc-cache", "-f", str(dossier)], stdout=subprocess.DEVNULL)
def verifier_polices(quarto):
titre("2/4 Polices (Noto Sans et Noto Sans Mono)")
manquantes = polices_manquantes(quarto)
if not manquantes:
print(" Les polices sont installées.")
return
print(f" Polices manquantes : {', '.join(manquantes)}.")
print(" Sans elles, les documents seraient produits avec une police de remplacement.")
if not oui_non(" Les installer maintenant ?"):
return
gestionnaire = next((g for g in PAQUETS_POLICES if SYSTEME == "Linux" and shutil.which(g)), None)
try:
if gestionnaire:
print(" Votre mot de passe peut être demandé (installation système).")
subprocess.run(PAQUETS_POLICES[gestionnaire])
if not gestionnaire or polices_manquantes(quarto):
telecharger_polices(polices_manquantes(quarto))
except Exception as erreur:
print(f" L'installation des polices a échoué : {erreur}")
reste = polices_manquantes(quarto)
print(" Polices installées." if not reste else
f" Toujours introuvables : {', '.join(reste)} (les documents seront produits quand même).")
# --- Environnement Python des schémas ----------------------------------------------
def python_du_venv():
return VENV / ("Scripts/python.exe" if SYSTEME == "Windows" else "bin/python")
def preparer_venv():
titre("3/4 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("4/4 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()
verifier_polices(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)