Permet de distribuer l'application en executable Windows double-clic, sans Python/Node/terminal pour l'utilisateur final. - `desktop.py` : demarre le serveur sur un port libre dans un thread et ouvre une fenetre native (pywebview). `PLESNA_DEBUG=1` active les DevTools. Commande `plesna-gerance desktop` (import paresseux). - `paths.py` : resolution centralisee des chemins selon le contexte (dev, executable PyInstaller, surcharge par variables d'env). Les donnees vont dans un dossier utilisateur inscriptible (%APPDATA%), les ressources dans le bundle. connection.py/storage.py utilisent `get_data_dir()`, app.py `resource_path()`. - app.py : type MIME `.mjs` force (modules ES / worker PDF.js sous Windows). - Groupes de deps optionnels desktop / desktop-linux / build. - packaging/ : spec PyInstaller, build_windows.ps1, installeur Inno Setup. Workflow GitHub build-windows.yml (runner Windows). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
"""Résolution centralisée des chemins (données utilisateur et ressources).
|
|
|
|
Gère trois contextes d'exécution :
|
|
|
|
- **Développement** : lancé depuis les sources. Les données vivent dans
|
|
``<racine_projet>/data`` et les ressources (frontend buildé) dans
|
|
``<racine_projet>/frontend/dist``.
|
|
- **Exécutable empaqueté** (PyInstaller, ex. ``.exe`` Windows) : les données
|
|
doivent aller dans un dossier utilisateur inscriptible (``%APPDATA%`` sous
|
|
Windows) — l'exécutable lui-même est souvent en lecture seule
|
|
(``Program Files``). Les ressources sont extraites dans ``sys._MEIPASS``.
|
|
- **Surcharge explicite** via variables d'environnement (tests, Docker).
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
APP_NAME = "PlesnaGerance"
|
|
|
|
|
|
def is_frozen() -> bool:
|
|
"""Vrai si on tourne depuis un exécutable PyInstaller."""
|
|
return getattr(sys, "frozen", False)
|
|
|
|
|
|
def get_bundle_dir() -> Path:
|
|
"""Répertoire racine des ressources embarquées (lecture seule).
|
|
|
|
- Empaqueté : ``sys._MEIPASS`` (dossier d'extraction PyInstaller), avec
|
|
repli sur le dossier de l'exécutable.
|
|
- Développement : racine du projet (``src/plesna_gerance/paths.py`` -> 3 crans).
|
|
"""
|
|
if is_frozen():
|
|
meipass = getattr(sys, "_MEIPASS", None)
|
|
if meipass:
|
|
return Path(meipass)
|
|
return Path(sys.executable).resolve().parent
|
|
return Path(__file__).resolve().parent.parent.parent
|
|
|
|
|
|
def resource_path(*parts: str) -> Path:
|
|
"""Chemin d'une ressource embarquée (ex. ``resource_path('frontend', 'dist')``)."""
|
|
return get_bundle_dir().joinpath(*parts)
|
|
|
|
|
|
def get_data_dir() -> Path:
|
|
"""Répertoire inscriptible des données utilisateur (DB, documents).
|
|
|
|
Priorité :
|
|
1. ``PLESNA_DATA_DIR`` si défini ;
|
|
2. dossier applicatif utilisateur si empaqueté ;
|
|
3. ``<racine_projet>/data`` en développement (comportement historique).
|
|
"""
|
|
env = os.environ.get("PLESNA_DATA_DIR")
|
|
if env:
|
|
return Path(env)
|
|
|
|
if is_frozen():
|
|
if sys.platform == "win32":
|
|
base = Path(os.environ.get("APPDATA") or Path.home())
|
|
elif sys.platform == "darwin":
|
|
base = Path.home() / "Library" / "Application Support"
|
|
else:
|
|
base = Path(
|
|
os.environ.get("XDG_DATA_HOME") or (Path.home() / ".local" / "share")
|
|
)
|
|
return base / APP_NAME
|
|
|
|
return get_bundle_dir() / "data"
|