diff --git a/pyproject.toml b/pyproject.toml index 4f2bc88..0a3f617 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ dependencies = [ 'pyyaml<6.1', 'pywin32<=306; sys_platform == "win32"', 'httpx<0.28', + 'platformdirs<=4.2.2', ] [project.urls] diff --git a/src/antares_web_installer/app.py b/src/antares_web_installer/app.py index 98cc600..6963701 100644 --- a/src/antares_web_installer/app.py +++ b/src/antares_web_installer/app.py @@ -21,8 +21,8 @@ # List of files and directories to exclude during installation COMMON_EXCLUDED_FILES = {"config.prod.yaml", "config.yaml", "examples", "logs", "matrices", "tmp"} -POSIX_EXCLUDED_FILES = COMMON_EXCLUDED_FILES | {"AntaresWebWorker"} -WINDOWS_EXCLUDED_FILES = COMMON_EXCLUDED_FILES | {"AntaresWebWorker.exe"} +POSIX_EXCLUDED_FILES = COMMON_EXCLUDED_FILES | {"AntaresWebWorker", "AntaresWebInstaller"} +WINDOWS_EXCLUDED_FILES = COMMON_EXCLUDED_FILES | {"AntaresWebWorker.exe", "AntaresWebInstaller.exe"} EXCLUDED_FILES = POSIX_EXCLUDED_FILES if os.name == "posix" else WINDOWS_EXCLUDED_FILES SERVER_NAMES = {"posix": "AntaresWebServer", "nt": "AntaresWebServer.exe"} diff --git a/src/antares_web_installer/gui/controller.py b/src/antares_web_installer/gui/controller.py index 087d266..de35d7c 100644 --- a/src/antares_web_installer/gui/controller.py +++ b/src/antares_web_installer/gui/controller.py @@ -12,7 +12,7 @@ from .model import WizardModel from .view import WizardView -from antares_web_installer.app import App +from antares_web_installer.app import App, InstallError logger = logging.getLogger(__name__) @@ -47,11 +47,16 @@ def install(self): launch=self.launch, ) - self.app.run() - - logger.debug("Launch installer worker") - logger.debug("Installation complete") - self.view.frames[self.view._current_index].event_generate("<>") + try: + self.app.run() + except InstallError as e: + logger.error(e) + self.view.raise_error("The installation encountered an error. The target directory may have be corrupted. " + "Please check its integrity and try again.") + else: + logger.debug("Launch installer worker") + logger.debug("Installation complete") + self.view.frames[self.view.current_index].event_generate("<>") def save_target_dir(self, path: str): self.target_dir = Path(path) diff --git a/src/antares_web_installer/gui/view.py b/src/antares_web_installer/gui/view.py index acd0b7a..1752ca7 100644 --- a/src/antares_web_installer/gui/view.py +++ b/src/antares_web_installer/gui/view.py @@ -1,5 +1,6 @@ import tkinter as tk from tkinter import ttk, font +from tkinter.messagebox import showerror from typing import TYPE_CHECKING from .mvc import View @@ -92,3 +93,7 @@ def change_frame(self): frame.update() frame.update_idletasks() frame.event_generate("<>") + + def raise_error(self, msg): + showerror('Error', msg) + self.quit() diff --git a/src/antares_web_installer/gui/widgets/frame.py b/src/antares_web_installer/gui/widgets/frame.py index 17ee2df..a69d3ba 100644 --- a/src/antares_web_installer/gui/widgets/frame.py +++ b/src/antares_web_installer/gui/widgets/frame.py @@ -1,9 +1,9 @@ import logging +import os import tkinter as tk import typing from threading import Thread from tkinter import ttk, filedialog -from tkinter.messagebox import showerror from typing import TYPE_CHECKING from antares_web_installer.shortcuts import get_homedir @@ -15,13 +15,16 @@ FORMAT = "[%(asctime)-15s] %(message)s" +class ViewError(Exception): + pass + + class LogManager(logging.Handler): def __init__(self, progress_frame: "ProgressFrame"): logging.Handler.__init__(self) self.setLevel(logging.INFO) formatter = logging.Formatter(FORMAT) self.setFormatter(formatter) - self.progress_frame = progress_frame def emit(self, logs: logging.LogRecord): @@ -249,6 +252,9 @@ def __init__(self, master: tk.Misc, window: "WizardView", index: int, *args, **k self.bind("<>", self.on_active_frame) self.bind("<>", self.on_installation_complete) + # thread handling + self.thread = None + def progress_update(self, value: float): self.progress_bar["value"] = value @@ -256,12 +262,25 @@ def on_active_frame(self, event): # Lazy import for typing and testing purposes from antares_web_installer.gui.controller import WizardController + # retrieve app logger main_logger = logging.getLogger("antares_web_installer.app") - # retrieve app logger if isinstance(self.window.controller, WizardController): + # create new tmp directory if it does not exist yet + tmp_dir_path = self.window.controller.target_dir.joinpath("tmp/") + + try: + os.mkdir(tmp_dir_path) + except FileExistsError: + main_logger.warning("Directory already exists. Skip directory creation step.") + except FileNotFoundError as e: + msg = "An error occured while saving logs." + main_logger.error(f"'tmp' directory was not found in {tmp_dir_path}: {e}") + main_logger.info(msg) + self.window.raise_error(msg) + # redirect logs in the target `tmp` directory - file_logger = logging.FileHandler(self.window.controller.target_dir.joinpath("tmp/web-installer.log")) + file_logger = logging.FileHandler(tmp_dir_path.joinpath("web-installer.log")) file_logger.setFormatter(logging.Formatter(FORMAT)) file_logger.setLevel(logging.ERROR) main_logger.addHandler(file_logger) @@ -270,15 +289,13 @@ def on_active_frame(self, event): log_manager = LogManager(self) main_logger.addHandler(log_manager) - # Launching installation in concurrency with current process - thread = Thread(target=self.window.controller.install) - thread.start() + # Launching installation in concurrency with the current process + self.thread = Thread(target=self.window.controller.install, args=()) + self.thread.start() else: + msg = "The installer encounters an issue while instantiating controller (code 'NotImplementedError')." main_logger.error(f"Not implemented {type(self.window.controller)}.") - showerror( - "Error", "Installer encounters an issue while instantiating controller (code 'NotImplementedError')." - ) - self.window.quit() + self.window.raise_error(msg) def on_installation_complete(self, event): self.control_btn.btns["next"].toggle_btn(True)