Windows compatibility

This commit is contained in:
2026-08-20 12:01:31 +02:00
parent 7c366a9ca4
commit ac4ab782b2
20 changed files with 634 additions and 134 deletions
+125
View File
@@ -0,0 +1,125 @@
from __future__ import annotations
import importlib.util
import os
import shutil
import sys
from dataclasses import dataclass
from pathlib import Path
from platform_utils import windows_filename_problems
@dataclass(frozen=True)
class DiagnosticCheck:
name: str
ok: bool
detail: str
required: bool = True
def module_available(module: str) -> bool:
try:
return importlib.util.find_spec(module) is not None
except (ImportError, ModuleNotFoundError, ValueError):
return False
def collect_diagnostics(
show_personal_steps: bool,
api_key_override: str = "",
evaluation: Path | None = None,
) -> list[DiagnosticCheck]:
checks = [
DiagnosticCheck(
"Système",
os.name == "nt" or sys.platform.startswith("linux"),
f"{sys.platform} — Linux et Windows sont pris en charge",
),
DiagnosticCheck("Python", True, sys.executable),
]
modules = {
"numpy": "numpy",
"pandas": "pandas",
"matplotlib": "matplotlib",
"Pillow": "PIL",
"pydantic": "pydantic",
"pypdf": "pypdf",
"pdf2image": "pdf2image",
"reportlab": "reportlab",
"img2pdf": "img2pdf",
"PyMuPDF": "fitz",
"ftfy": "ftfy",
"ezodf": "ezodf",
"Google GenAI": "google.genai",
}
checks.extend(
DiagnosticCheck(label, module_available(module), f"Module Python : {module}")
for label, module in modules.items()
)
programs = (
("Poppler", ("pdftoppm", "pdftocairo"), True),
("LaTeX", ("pdflatex",), True),
("PDF Arranger", ("pdf-arranger", "pdfarranger"), False),
)
for label, candidates, required in programs:
executable = next((shutil.which(candidate) for candidate in candidates if shutil.which(candidate)), None)
detail = executable or "Introuvable dans PATH"
checks.append(DiagnosticCheck(label, executable is not None, detail, required))
api_key = api_key_override.strip() or os.environ.get("GEMINI_API_KEY")
checks.append(
DiagnosticCheck(
"Clé Gemini",
bool(api_key),
"GEMINI_API_KEY est définie" if api_key else "À saisir dans le GUI ou dans lenvironnement",
)
)
if evaluation and (evaluation / "labels").is_file():
labels = [
line.strip()
for line in (evaluation / "labels").read_text(encoding="utf-8").splitlines()
if line.strip()
]
unsafe = [label for label in labels if windows_filename_problems(label)]
detail = (
"Tous les labels sont utilisables comme noms de fichiers"
if not unsafe
else "Caractères Windows interdits : " + ", ".join(unsafe[:3])
)
if len(unsafe) > 3:
detail += f" (+{len(unsafe) - 3})"
checks.append(
DiagnosticCheck(
"Labels compatibles Windows",
not unsafe,
detail,
os.name == "nt",
)
)
if show_personal_steps:
checks.append(
DiagnosticCheck(
"gestion_classe",
shutil.which("gestion_classe") is not None,
shutil.which("gestion_classe") or "Introuvable dans PATH",
False,
)
)
try:
from config import CURRENT_SCORE_ODS_PATH, FINAL_SCORE_ODS_PATH
for label, configured_path in (
("ODS courant", CURRENT_SCORE_ODS_PATH),
("ODS final", FINAL_SCORE_ODS_PATH),
):
path = Path(configured_path).expanduser()
checks.append(DiagnosticCheck(label, path.is_file(), str(path), False))
except ImportError:
checks.append(DiagnosticCheck("Configuration personnelle", False, "Variables absentes", False))
return checks