134 lines
4.2 KiB
Python
134 lines
4.2 KiB
Python
from __future__ import annotations
|
||
|
||
import importlib.util
|
||
import os
|
||
import shutil
|
||
import sys
|
||
from dataclasses import dataclass
|
||
from pathlib import Path
|
||
|
||
from copienator.platform import find_executable, 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")
|
||
or sys.platform == "darwin",
|
||
f"{sys.platform} — Linux, Windows et macOS 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 = find_executable(*candidates)
|
||
if label == "PDF Arranger" and not executable and sys.platform == "darwin":
|
||
system_open = Path("/usr/bin/open")
|
||
opener = find_executable("open") or (
|
||
str(system_open) if system_open.is_file() else None
|
||
)
|
||
executable = f"{opener} (Aperçu)" if opener else 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 l’environnement",
|
||
)
|
||
)
|
||
|
||
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 copienator.configuration 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
|