From c91e98224318da437281986de20dcfcab4680091 Mon Sep 17 00:00:00 2001 From: Vinicius Moreira Date: Wed, 30 Dec 2020 13:35:15 -0300 Subject: [PATCH] improvement -> all suported types now display a 'Checking configuration file' task during the initialization process --- CHANGELOG.md | 4 +- bauh/app.py | 4 +- bauh/cli/app.py | 5 +- bauh/commons/boot.py | 57 +++++ bauh/commons/category.py | 6 +- bauh/commons/config.py | 69 +++++ bauh/gems/appimage/config.py | 18 +- bauh/gems/appimage/controller.py | 34 +-- bauh/gems/appimage/resources/locale/ca | 1 + bauh/gems/appimage/resources/locale/de | 1 + bauh/gems/appimage/resources/locale/en | 3 +- bauh/gems/appimage/resources/locale/es | 1 + bauh/gems/appimage/resources/locale/fr | 1 + bauh/gems/appimage/resources/locale/it | 1 + bauh/gems/appimage/resources/locale/pt | 1 + bauh/gems/appimage/resources/locale/ru | 1 + bauh/gems/appimage/resources/locale/tr | 1 + bauh/gems/appimage/worker.py | 46 ++-- bauh/gems/arch/config.py | 32 ++- bauh/gems/arch/controller.py | 222 ++++++++-------- bauh/gems/arch/pacman.py | 3 +- bauh/gems/arch/resources/locale/ca | 10 +- bauh/gems/arch/resources/locale/de | 10 +- bauh/gems/arch/resources/locale/en | 10 +- bauh/gems/arch/resources/locale/es | 10 +- bauh/gems/arch/resources/locale/fr | 10 +- bauh/gems/arch/resources/locale/it | 10 +- bauh/gems/arch/resources/locale/pt | 8 +- bauh/gems/arch/resources/locale/ru | 10 +- bauh/gems/arch/resources/locale/tr | 10 +- bauh/gems/arch/worker.py | 239 +++++++++++++----- bauh/gems/flatpak/__init__.py | 5 + bauh/gems/flatpak/config.py | 12 +- bauh/gems/flatpak/controller.py | 25 +- bauh/gems/snap/config.py | 12 +- bauh/gems/snap/controller.py | 61 +++-- bauh/gems/web/config.py | 25 +- bauh/gems/web/controller.py | 82 +++--- bauh/gems/web/environment.py | 41 +-- bauh/gems/web/resources/locale/ca | 4 +- bauh/gems/web/resources/locale/en | 4 +- bauh/gems/web/resources/locale/es | 4 +- bauh/gems/web/resources/locale/fr | 4 +- bauh/gems/web/resources/locale/it | 4 +- bauh/gems/web/resources/locale/pt | 4 +- bauh/gems/web/resources/locale/ru | 4 +- bauh/gems/web/resources/locale/tr | 4 +- bauh/gems/web/suggestions.py | 59 +++++ bauh/gems/web/worker.py | 151 ++++++----- bauh/view/core/config.py | 131 +++++----- bauh/view/core/controller.py | 25 +- bauh/view/core/settings.py | 13 +- bauh/view/qt/prepare.py | 53 +++- bauh/view/qt/root.py | 4 +- bauh/view/qt/thread.py | 19 +- bauh/view/qt/window.py | 4 +- bauh/view/resources/locale/ca | 2 + bauh/view/resources/locale/de | 2 + bauh/view/resources/locale/en | 2 + bauh/view/resources/locale/es | 2 + bauh/view/resources/locale/fr | 2 + bauh/view/resources/locale/it | 2 + bauh/view/resources/locale/pt | 2 + bauh/view/resources/locale/ru | 2 + bauh/view/resources/locale/tr | 2 + bauh/view/resources/style/darcula/darcula.qss | 2 +- bauh/view/resources/style/default/default.qss | 2 +- bauh/view/resources/style/light/light.qss | 4 +- bauh/view/resources/style/sublime/sublime.qss | 2 +- 69 files changed, 1059 insertions(+), 561 deletions(-) create mode 100644 bauh/commons/boot.py create mode 100644 bauh/gems/web/suggestions.py diff --git a/CHANGELOG.md b/CHANGELOG.md index eb0e02f6..1caf713a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,7 +59,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

- + - displaying the "Indexing suggestions" task during the initialization process + +- all types now display an initialization task "Checking configuration file" responsible to create/update the respective configuration file - minor translation improvements diff --git a/bauh/app.py b/bauh/app.py index abc33929..172b0e5a 100755 --- a/bauh/app.py +++ b/bauh/app.py @@ -7,7 +7,7 @@ import urllib3 from PyQt5.QtCore import QCoreApplication, Qt from bauh import __app_name__, app_args -from bauh.view.core import config +from bauh.view.core.config import CoreConfigManager from bauh.view.util import logs @@ -25,7 +25,7 @@ def main(tray: bool = False): if args.offline: logger.warning("offline mode activated") - app_config = config.read_config(update_file=True) + app_config = CoreConfigManager().get_config() if bool(app_config['ui']['auto_scale']): os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1' diff --git a/bauh/cli/app.py b/bauh/cli/app.py index af4a4c4e..232e809a 100644 --- a/bauh/cli/app.py +++ b/bauh/cli/app.py @@ -9,7 +9,8 @@ from bauh.cli import __app_name__, cli_args from bauh.cli.controller import CLIManager from bauh.commons.internet import InternetChecker from bauh.context import generate_i18n, DEFAULT_I18N_KEY -from bauh.view.core import config, gems +from bauh.view.core import gems +from bauh.view.core.config import CoreConfigManager from bauh.view.core.controller import GenericSoftwareManager from bauh.view.core.downloader import AdaptableFileDownloader from bauh.view.util import logs, util, resource @@ -26,7 +27,7 @@ def main(): args = cli_args.read() logger = logs.new_logger(__app_name__, False) - app_config = config.read_config(update_file=True) + app_config = CoreConfigManager().get_config() http_client = HttpClient(logger) i18n = generate_i18n(app_config, resource.get_path('locale')) diff --git a/bauh/commons/boot.py b/bauh/commons/boot.py new file mode 100644 index 00000000..c9874ada --- /dev/null +++ b/bauh/commons/boot.py @@ -0,0 +1,57 @@ +import time +from logging import Logger +from threading import Thread +from typing import Optional + +from bauh.api.abstract.handler import TaskManager +from bauh.commons.config import ConfigManager +from bauh.view.util.translation import I18n + + +class CreateConfigFile(Thread): + """ + Generic initialization task to create a configuration file + """ + + def __init__(self, configman: ConfigManager, taskman: TaskManager, task_icon_path: str, i18n: I18n, logger: Logger, config_instance: Optional[dict] = None): + super(CreateConfigFile, self).__init__(daemon=True) + self.configman = configman + self.taskman = taskman + self.logger = logger + self.config = config_instance + self.task_icon_path = task_icon_path + self.task_id = configman.__class__.__name__ + self.i18n = i18n + self.task_name = self.i18n['task.checking_config'] + self.taskman.register_task(self.task_id, self.task_name, self.task_icon_path) + + def _log(self, msg: str): + self.logger.info('{}: {}'.format(self.configman.__class__.__name__, msg)) + + def run(self): + ti = time.time() + self.taskman.update_progress(self.task_id, 1, None) + + self._log("Reading cached configuration file") + default_config = self.configman.get_default_config() + + cached_config = self.configman.read_config() + + self.taskman.update_progress(self.task_id, 50, None) + + if cached_config: + self._log("Merging configuration file") + self.configman.merge_config(default_config, cached_config) + else: + self._log("No cached configuration file found") + + self.config = default_config + self.taskman.update_progress(self.task_id, 75, self.i18n['task.checking_config.saving']) + + self._log("Writing configuration file") + self.configman.save_config(default_config) + + self.taskman.update_progress(self.task_id, 100, None) + self.taskman.finish_task(self.task_id) + tf = time.time() + self._log("Finished. Took {0:.2f} seconds".format(tf - ti)) diff --git a/bauh/commons/category.py b/bauh/commons/category.py index 19ef9976..12b589d8 100644 --- a/bauh/commons/category.py +++ b/bauh/commons/category.py @@ -18,8 +18,8 @@ from bauh.commons.util import map_timestamp_file class CategoriesDownloader(Thread): def __init__(self, id_: str, http_client: HttpClient, logger: logging.Logger, manager: SoftwareManager, - url_categories_file: str, categories_path: str, expiration: int, internet_checker: InternetChecker, - internet_connection: Optional[bool] = True, before=None, after=None): + url_categories_file: str, categories_path: str, internet_checker: InternetChecker, + expiration: Optional[int] = None, internet_connection: Optional[bool] = True, before=None, after=None): """ :param id_: :param http_client: @@ -121,7 +121,7 @@ class CategoriesDownloader(Thread): self.logger.warning(self._msg("No internet connection. The categories file '{}' cannot be updated.".format(self.categories_path))) return False - if self.expiration <= 0: + if self.expiration is None or self.expiration <= 0: self.logger.warning(self._msg("No expiration set for the categories file '{}'. It should be downloaded".format(self.categories_path))) return True diff --git a/bauh/commons/config.py b/bauh/commons/config.py index cf6fbba6..dcb42638 100644 --- a/bauh/commons/config.py +++ b/bauh/commons/config.py @@ -1,6 +1,9 @@ import os +import traceback +from abc import abstractmethod, ABC from pathlib import Path from threading import Thread +from typing import Optional import yaml @@ -31,3 +34,69 @@ def read_config(file_path: str, template: dict, update_file: bool = False, updat def save_config(config: dict, file_path: str): with open(file_path, 'w+') as f: f.write(yaml.dump(config)) + + +class ConfigManager(ABC): + + @abstractmethod + def read_config(self) -> Optional[dict]: + pass + + @abstractmethod + def get_default_config(self) -> dict: + pass + + @abstractmethod + def is_config_cached(self) -> bool: + pass + + def get_config(self) -> dict: + default_config = self.get_default_config() + + if default_config: + cached_config = self.read_config() + + if cached_config: + self.merge_config(default_config, cached_config) + + return default_config + + @staticmethod + def merge_config(base_config: dict, current_config: dict): + util.deep_update(base_config, current_config) + + @abstractmethod + def save_config(self, config_obj: dict): + pass + + +class YAMLConfigManager(ConfigManager, ABC): + + def __init__(self, config_file_path: str): + self.file_path = config_file_path + + def is_config_cached(self) -> bool: + return os.path.exists(self.file_path) + + def read_config(self) -> Optional[dict]: + if self.is_config_cached(): + with open(self.file_path) as f: + local_config = yaml.safe_load(f.read()) + + if local_config is not None: + return local_config + + def save_config(self, config_obj: dict): + if config_obj: + config_dir = os.path.dirname(self.file_path) + try: + Path(config_dir).mkdir(parents=True, exist_ok=True) + except OSError: + traceback.print_exc() + return + + try: + with open(self.file_path, 'w+') as f: + f.write(yaml.dump(config_obj)) + except: + traceback.print_exc() diff --git a/bauh/gems/appimage/config.py b/bauh/gems/appimage/config.py index 6c80328e..2ced2afb 100644 --- a/bauh/gems/appimage/config.py +++ b/bauh/gems/appimage/config.py @@ -1,11 +1,15 @@ -from bauh.commons.config import read_config as read +from bauh.commons.config import YAMLConfigManager from bauh.gems.appimage import CONFIG_FILE -def read_config(update_file: bool = False) -> dict: - default = { - 'database': { - 'expiration': 60 +class AppImageConfigManager(YAMLConfigManager): + + def __init__(self): + super(AppImageConfigManager, self).__init__(config_file_path=CONFIG_FILE) + + def get_default_config(self) -> dict: + return { + 'database': { + 'expiration': 60 + } } - } - return read(CONFIG_FILE, default, update_file=update_file) diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py index e42b6ffe..821c6a5b 100644 --- a/bauh/gems/appimage/controller.py +++ b/bauh/gems/appimage/controller.py @@ -24,13 +24,13 @@ from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpda from bauh.api.abstract.view import MessageType, ViewComponent, FormComponent, InputOption, SingleSelectComponent, \ SelectViewType, TextInputComponent, PanelComponent, FileChooserComponent, ViewObserver from bauh.commons import resource -from bauh.commons.config import save_config +from bauh.commons.boot import CreateConfigFile from bauh.commons.html import bold from bauh.commons.system import SystemProcess, new_subprocess, ProcessHandler, run_cmd, SimpleProcess -from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, SUGGESTIONS_FILE, CONFIG_FILE, ROOT_DIR, \ +from bauh.gems.appimage import query, INSTALLATION_PATH, LOCAL_PATH, SUGGESTIONS_FILE, ROOT_DIR, \ CONFIG_DIR, UPDATES_IGNORED_FILE, util, get_default_manual_installation_file_dir, DATABASE_APPS_FILE, \ - DATABASE_RELEASES_FILE, DESKTOP_ENTRIES_PATH, DATABASES_DIR -from bauh.gems.appimage.config import read_config + DATABASE_RELEASES_FILE, DESKTOP_ENTRIES_PATH, DATABASES_DIR, get_icon_path +from bauh.gems.appimage.config import AppImageConfigManager from bauh.gems.appimage.model import AppImage from bauh.gems.appimage.util import replace_desktop_entry_exec_command from bauh.gems.appimage.worker import DatabaseUpdater, SymlinksVerifier @@ -76,6 +76,7 @@ class AppImageManager(SoftwareManager): self.http_client = context.http_client self.logger = context.logger self.file_downloader = context.file_downloader + self.configman = AppImageConfigManager() self.custom_actions = [CustomSoftwareAction(i18n_label_key='appimage.custom_action.install_file', i18n_status_key='appimage.custom_action.install_file.status', manager=self, @@ -616,16 +617,17 @@ class AppImageManager(SoftwareManager): return False def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): + create_config = CreateConfigFile(taskman=task_manager, configman=self.configman, i18n=self.i18n, + task_icon_path=get_icon_path(), logger=self.logger) + create_config.start() + symlink_check = SymlinksVerifier(taskman=task_manager, i18n=self.i18n, logger=self.logger) symlink_check.start() if internet_available: - updater = DatabaseUpdater(taskman=task_manager, i18n=self.context.i18n, - http_client=self.context.http_client, logger=self.context.logger) - - if updater.should_update(read_config()): - updater.register_task() - updater.start() + DatabaseUpdater(taskman=task_manager, i18n=self.context.i18n, + create_config=create_config, http_client=self.context.http_client, + logger=self.context.logger).start() def list_updates(self, internet_available: bool) -> List[PackageUpdate]: res = self.read_installed(disk_loader=None, internet_available=internet_available) @@ -729,12 +731,12 @@ class AppImageManager(SoftwareManager): traceback.print_exc() def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: - config = read_config() + appimage_config = self.configman.get_config() max_width = floor(screen_width * 0.15) opts = [ TextInputComponent(label=self.i18n['appimage.config.database.expiration'], - value=int(config['database']['expiration']) if isinstance(config['database']['expiration'], int) else '', + value=int(appimage_config['database']['expiration']) if isinstance(appimage_config['database']['expiration'], int) else '', tooltip=self.i18n['appimage.config.database.expiration.tip'], only_int=True, max_width=max_width, @@ -744,13 +746,13 @@ class AppImageManager(SoftwareManager): return PanelComponent([FormComponent(opts, self.i18n['appimage.config.database'])]) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: - config = read_config() + appimage_config = self.configman.get_config() panel = component.components[0] - config['database']['expiration'] = panel.get_component('appim_db_exp').get_int_value() + appimage_config['database']['expiration'] = panel.get_component('appim_db_exp').get_int_value() try: - save_config(config, CONFIG_FILE) + self.configman.save_config(appimage_config) return True, None except: return False, [traceback.format_exc()] @@ -822,7 +824,7 @@ class AppImageManager(SoftwareManager): def update_database(self, root_password: str, watcher: ProcessWatcher) -> bool: db_updater = DatabaseUpdater(i18n=self.i18n, http_client=self.context.http_client, - logger=self.context.logger, watcher=watcher) + logger=self.context.logger, watcher=watcher, taskman=TaskManager()) res = db_updater.download_databases() return res diff --git a/bauh/gems/appimage/resources/locale/ca b/bauh/gems/appimage/resources/locale/ca index 9f3be860..34b228f7 100644 --- a/bauh/gems/appimage/resources/locale/ca +++ b/bauh/gems/appimage/resources/locale/ca @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Actualització de bases de dades +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Checking symlinks appimage.uninstall.error.remove_folder=No s’ha pogut suprimir el directori d’instal·lació de l’aplicació {} appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/resources/locale/de b/bauh/gems/appimage/resources/locale/de index c924f481..2cccb9bf 100644 --- a/bauh/gems/appimage/resources/locale/de +++ b/bauh/gems/appimage/resources/locale/de @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Updating databases +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Checking symlinks appimage.uninstall.error.remove_folder=Anwendungordner {} konnte nicht entfernt werden appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/resources/locale/en b/bauh/gems/appimage/resources/locale/en index e66cd0b1..04217331 100644 --- a/bauh/gems/appimage/resources/locale/en +++ b/bauh/gems/appimage/resources/locale/en @@ -1,6 +1,6 @@ appimage.config.database=Database appimage.config.database.expiration=Expiration -appimage.config.database.expiration.tip=appimage.config.database.expiration.tip=It defines the period (in minutes) in which the database will be considered up to date. Use 0 if you always want to update it. +appimage.config.database.expiration.tip=It defines the period (in minutes) in which the database will be considered up to date. Use 0 if you always want to update it. appimage.custom_action.install_file=Install AppImage file appimage.custom_action.install_file.details=Installation details appimage.custom_action.install_file.invalid_file=You must define a valid AppImage file @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Updating databases +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Checking symlinks appimage.uninstall.error.remove_folder=Could not remove the application installation directory {} appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/resources/locale/es b/bauh/gems/appimage/resources/locale/es index b1b97600..047ca4fd 100644 --- a/bauh/gems/appimage/resources/locale/es +++ b/bauh/gems/appimage/resources/locale/es @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Eliminando archivos antiguos appimage.update_database.downloading=Descargando archivos de la base de datos appimage.update_database.uncompressing=Descomprindo archivos appimage.task.db_update=Actualizando base de datos +appimage.task.db_update.checking=Buscando actualizaciones appimage.task.symlink_check=Verificando links simbólicos appimage.uninstall.error.remove_folder=No se pudo eliminar el directorio de instalación de la aplicación {} appimage.warning.missing_db_files=No se encontraron archivos de la base de datos {appimage}. Algunas acciones no funcionarán correctamente. diff --git a/bauh/gems/appimage/resources/locale/fr b/bauh/gems/appimage/resources/locale/fr index 0efe8d4a..0730d376 100644 --- a/bauh/gems/appimage/resources/locale/fr +++ b/bauh/gems/appimage/resources/locale/fr @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Mise à jour des bases de données +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Verification des symlinks appimage.uninstall.error.remove_folder=Impossible de supprimer le répertoire d'installation {} appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/resources/locale/it b/bauh/gems/appimage/resources/locale/it index 63f34052..72b78692 100644 --- a/bauh/gems/appimage/resources/locale/it +++ b/bauh/gems/appimage/resources/locale/it @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Aggiornamento dei database +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Checking symlinks appimage.uninstall.error.remove_folder=Impossibile rimuovere la directory di installazione dell'applicazione {} appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/resources/locale/pt b/bauh/gems/appimage/resources/locale/pt index 6cc1060c..f4357e77 100644 --- a/bauh/gems/appimage/resources/locale/pt +++ b/bauh/gems/appimage/resources/locale/pt @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removendo arquicos antigos appimage.update_database.downloading=Baixando arquivos do banco de dados appimage.update_database.uncompressing=Descomprimindo arquivos appimage.task.db_update=Atualizando base de dados +appimage.task.db_update.checking=Checando atualizações appimage.task.symlink_check=Verificando links simbólicos appimage.uninstall.error.remove_folder=Não foi possível remover o diretóri ode instalação do aplicativo {} appimage.warning.missing_db_files=Arquivos do banco de dados {appimage} não foram encontrados. Algumas ações não funcionarão corretamente. diff --git a/bauh/gems/appimage/resources/locale/ru b/bauh/gems/appimage/resources/locale/ru index 06c462d8..adeb3fd1 100644 --- a/bauh/gems/appimage/resources/locale/ru +++ b/bauh/gems/appimage/resources/locale/ru @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Обновление базы данных +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Checking symlinks appimage.uninstall.error.remove_folder=Не удалось удалить каталог приложения {} appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/resources/locale/tr b/bauh/gems/appimage/resources/locale/tr index 8f6c03e0..3173b2f4 100644 --- a/bauh/gems/appimage/resources/locale/tr +++ b/bauh/gems/appimage/resources/locale/tr @@ -36,6 +36,7 @@ appimage.update_database.deleting_old=Removing old files appimage.update_database.downloading=Downloading database files appimage.update_database.uncompressing=Uncompressing files appimage.task.db_update=Veritabanları güncelleniyor +appimage.task.db_update.checking=Checking for updates appimage.task.symlink_check=Checking symlinks appimage.uninstall.error.remove_folder={} uygulama kurulum dizini kaldırılamadı appimage.warning.missing_db_files={appimage} database files were not found. Some actions will not work properly. diff --git a/bauh/gems/appimage/worker.py b/bauh/gems/appimage/worker.py index 0c209d0d..4ebb3920 100644 --- a/bauh/gems/appimage/worker.py +++ b/bauh/gems/appimage/worker.py @@ -12,6 +12,8 @@ from typing import Optional from bauh.api.abstract.handler import TaskManager, ProcessWatcher from bauh.api.http import HttpClient +from bauh.commons.boot import CreateConfigFile +from bauh.commons.html import bold from bauh.gems.appimage import get_icon_path, INSTALLATION_PATH, SYMLINKS_DIR, util, DATABASES_TS_FILE, \ DATABASES_DIR, DATABASE_APPS_FILE, DATABASE_RELEASES_FILE, URL_COMPRESSED_DATABASES from bauh.gems.appimage.model import AppImage @@ -21,7 +23,8 @@ from bauh.view.util.translation import I18n class DatabaseUpdater(Thread): COMPRESS_FILE_PATH = '{}/db.tar.gz'.format(DATABASES_DIR) - def __init__(self, i18n: I18n, http_client: HttpClient, logger: logging.Logger, watcher: Optional[ProcessWatcher] = None, taskman: Optional[TaskManager] = None): + def __init__(self, i18n: I18n, http_client: HttpClient, logger: logging.Logger, taskman: TaskManager, + watcher: Optional[ProcessWatcher] = None, appimage_config: Optional[dict] = None, create_config: Optional[CreateConfigFile] = None): super(DatabaseUpdater, self).__init__(daemon=True) self.http_client = http_client self.logger = logger @@ -29,6 +32,9 @@ class DatabaseUpdater(Thread): self.taskman = taskman self.watcher = watcher self.task_id = 'appim_db' + self.config = appimage_config + self.create_config = create_config + self.taskman.register_task(self.task_id, self.i18n['appimage.task.db_update'], get_icon_path()) def should_update(self, appimage_config: dict) -> bool: ti = time.time() @@ -75,26 +81,14 @@ class DatabaseUpdater(Thread): self.logger.info('Finished. Took {0:.2f} seconds'.format(time.time() - ti)) return update - def register_task(self): - if self.taskman: - self.taskman.register_task(self.task_id, self.i18n['appimage.task.db_update'], get_icon_path()) - - def _finish_task(self): - if self.taskman: - self.taskman.update_progress(self.task_id, 100, None) - self.taskman.finish_task(self.task_id) - self.taskman = None - self.logger.info("Finished") - def _update_task_progress(self, progress: float, substatus: Optional[str] = None): - if self.taskman: - self.taskman.update_progress(self.task_id, progress, substatus) + self.taskman.update_progress(self.task_id, progress, substatus) if self.watcher: self.watcher.change_substatus(substatus) def download_databases(self) -> bool: - self._update_task_progress(1, self.i18n['appimage.update_database.downloading']) + self._update_task_progress(10, self.i18n['appimage.update_database.downloading']) self.logger.info('Retrieving AppImage databases') database_timestamp = datetime.utcnow().timestamp() @@ -106,7 +100,6 @@ class DatabaseUpdater(Thread): if not res: self.logger.warning('Could not download the database file {}'.format(URL_COMPRESSED_DATABASES)) - self._finish_task() return False Path(DATABASES_DIR).mkdir(parents=True, exist_ok=True) @@ -150,11 +143,25 @@ class DatabaseUpdater(Thread): self.logger.info("Database timestamp saved") - self._finish_task() return True def run(self): - self.download_databases() + ti = time.time() + + if self.create_config: + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(bold(self.create_config.task_name))) + self.create_config.join() + self.config = self.create_config.config + + self.taskman.update_progress(self.task_id, 1, self.i18n['appimage.task.db_update.checking']) + + if self.should_update(self.config): + self.download_databases() + + self.taskman.update_progress(self.task_id, 100, None) + self.taskman.finish_task(self.task_id) + tf = time.time() + self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti)) class SymlinksVerifier(Thread): @@ -165,6 +172,7 @@ class SymlinksVerifier(Thread): self.i18n = i18n self.logger = logger self.task_id = 'appim_symlink_check' + self.taskman.register_task(self.task_id, self.i18n['appimage.task.symlink_check'], get_icon_path()) @staticmethod def create_symlink(app: AppImage, file_path: str, logger: logging.Logger, watcher: ProcessWatcher = None): @@ -222,8 +230,6 @@ class SymlinksVerifier(Thread): watcher.print('[error] {}'.format(msg)) def run(self): - self.taskman.register_task(self.task_id, self.i18n['appimage.task.symlink_check'], get_icon_path()) - if os.path.exists(INSTALLATION_PATH): installed_files = glob.glob('{}/*/*.json'.format(INSTALLATION_PATH)) diff --git a/bauh/gems/arch/config.py b/bauh/gems/arch/config.py index f4bea0cd..ecd4f9f7 100644 --- a/bauh/gems/arch/config.py +++ b/bauh/gems/arch/config.py @@ -1,11 +1,26 @@ from pathlib import Path -from bauh.commons.config import read_config as read +from bauh.commons.config import YAMLConfigManager from bauh.gems.arch import CONFIG_FILE, BUILD_DIR -def read_config(update_file: bool = False) -> dict: - template = {'optimize': True, +def get_build_dir(arch_config: dict) -> str: + build_dir = arch_config.get('aur_build_dir') + + if not build_dir: + build_dir = BUILD_DIR + + Path(build_dir).mkdir(parents=True, exist_ok=True) + return build_dir + + +class ArchConfigManager(YAMLConfigManager): + + def __init__(self): + super(ArchConfigManager, self).__init__(config_file_path=CONFIG_FILE) + + def get_default_config(self) -> dict: + return {'optimize': True, "sync_databases": True, "clean_cached": True, 'aur': True, @@ -24,14 +39,3 @@ def read_config(update_file: bool = False) -> dict: 'suggest_optdep_uninstall': False, 'aur_idx_exp': 720, 'categories_exp': 24} - return read(CONFIG_FILE, template, update_file=update_file) - - -def get_build_dir(arch_config: dict) -> str: - build_dir = arch_config.get('aur_build_dir') - - if not build_dir: - build_dir = BUILD_DIR - - Path(build_dir).mkdir(parents=True, exist_ok=True) - return build_dir diff --git a/bauh/gems/arch/controller.py b/bauh/gems/arch/controller.py index a27d8c76..e8ec10a2 100644 --- a/bauh/gems/arch/controller.py +++ b/bauh/gems/arch/controller.py @@ -27,18 +27,18 @@ from bauh.api.abstract.view import MessageType, FormComponent, InputOption, Sing FileChooserComponent, TextComponent from bauh.api.constants import TEMP_DIR from bauh.commons import user, system +from bauh.commons.boot import CreateConfigFile from bauh.commons.category import CategoriesDownloader -from bauh.commons.config import save_config from bauh.commons.html import bold from bauh.commons.system import SystemProcess, ProcessHandler, new_subprocess, run_cmd, SimpleProcess from bauh.commons.util import datetime_as_milis from bauh.commons.view_utils import new_select from bauh.gems.arch import aur, pacman, makepkg, message, confirmation, disk, git, \ gpg, URL_CATEGORIES_FILE, CATEGORIES_FILE_PATH, CUSTOM_MAKEPKG_FILE, SUGGESTIONS_FILE, \ - CONFIG_FILE, get_icon_path, database, mirrors, sorting, cpu_manager, ARCH_CACHE_PATH, UPDATES_IGNORED_FILE, \ + get_icon_path, database, mirrors, sorting, cpu_manager, UPDATES_IGNORED_FILE, \ CONFIG_DIR, EDITABLE_PKGBUILDS_FILE, URL_GPG_SERVERS, BUILD_DIR from bauh.gems.arch.aur import AURClient -from bauh.gems.arch.config import read_config, get_build_dir +from bauh.gems.arch.config import get_build_dir, ArchConfigManager from bauh.gems.arch.dependencies import DependenciesAnalyser from bauh.gems.arch.download import MultithreadedDownloadService, ArchDownloadException from bauh.gems.arch.exceptions import PackageNotFoundException, PackageInHoldException @@ -47,8 +47,8 @@ from bauh.gems.arch.model import ArchPackage from bauh.gems.arch.output import TransactionStatusHandler from bauh.gems.arch.pacman import RE_DEP_OPERATORS from bauh.gems.arch.updates import UpdatesSummarizer -from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer, SyncDatabases, \ - RefreshMirrors +from bauh.gems.arch.worker import AURIndexUpdater, ArchDiskCacheUpdater, ArchCompilationOptimizer, RefreshMirrors, \ + SyncDatabases URL_GIT = 'https://aur.archlinux.org/{}.git' URL_SRC_INFO = 'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h=' @@ -191,11 +191,11 @@ class TransactionContext: class ArchManager(SoftwareManager): - def __init__(self, context: ApplicationContext, disk_cache_updater: ArchDiskCacheUpdater = None): + def __init__(self, context: ApplicationContext, disk_cache_updater: Optional[ArchDiskCacheUpdater] = None): super(ArchManager, self).__init__(context=context) self.aur_cache = context.cache_factory.new() # context.disk_loader_factory.map(ArchPackage, self.aur_cache) TODO - + self.configman = ArchConfigManager() self.aur_mapper = AURDataMapper(http_client=context.http_client, i18n=context.i18n, logger=context.logger) self.i18n = context.i18n self.aur_client = AURClient(http_client=context.http_client, logger=context.logger, x86_64=context.is_system_x86_64()) @@ -309,7 +309,7 @@ class ArchManager(SoftwareManager): type_=MessageType.ERROR) return False - sort_limit = read_config()['mirrors_sort_limit'] + sort_limit = self.configman.get_config()['mirrors_sort_limit'] if sort_limit is not None and isinstance(sort_limit, int) and sort_limit >= 0: watcher.change_substatus(self.i18n['arch.custom_action.refresh_mirrors.status.sorting']) @@ -419,7 +419,7 @@ class ArchManager(SoftwareManager): if is_url: return SearchResult([], [], 0) - arch_config = read_config() + arch_config = self.configman.get_config() aur_supported = aur.is_supported(arch_config) if not any([arch_config['repositories'], aur_supported]): @@ -563,7 +563,7 @@ class ArchManager(SoftwareManager): def read_installed(self, disk_loader: Optional[DiskCacheLoader], limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None, names: Iterable[str] = None, wait_disk_cache: bool = True) -> SearchResult: self.aur_client.clean_caches() - arch_config = read_config() + arch_config = self.configman.get_config() aur_supported = aur.is_supported(arch_config) @@ -781,7 +781,7 @@ class ArchManager(SoftwareManager): if self._is_database_locked(handler, root_password): return False - arch_config = read_config() + arch_config = self.configman.get_config() aur_supported = pkg.repository == 'aur' or aur.is_supported(arch_config) context = TransactionContext(name=pkg.name, base=pkg.get_base_name(), skip_opt_deps=True, change_progress=True, dependency=False, repository=pkg.repository, pkg=pkg, @@ -1105,7 +1105,7 @@ class ArchManager(SoftwareManager): if aur_pkgs and not self._check_action_allowed(aur_pkgs[0], watcher): return False - arch_config = read_config() + arch_config = self.configman.get_config() aur_supported = bool(aur_pkgs) or aur.is_supported(arch_config) self._sync_databases(arch_config=arch_config, aur_supported=aur_supported, @@ -1392,7 +1392,7 @@ class ArchManager(SoftwareManager): return TransactionResult.fail() removed = {} - arch_config = read_config() + arch_config = self.configman.get_config() success = self._uninstall(TransactionContext(change_progress=True, arch_config=arch_config, watcher=watcher, @@ -1520,7 +1520,7 @@ class ArchManager(SoftwareManager): else: self.logger.warning("Package '{}' has no commit associated with it. Current history status may not be correct.".format(pkg.name)) - arch_config = read_config() + arch_config = self.configman.get_config() temp_dir = '{}/build_{}'.format(get_build_dir(arch_config), int(time.time())) try: @@ -1938,7 +1938,7 @@ class ArchManager(SoftwareManager): watcher.change_substatus(self.i18n['arch.task.aur.index.status']) idx_updater = AURIndexUpdater(context=self.context, taskman=TaskManager()) # null task manager - idx_updater.run() + idx_updater.update_index() else: self.logger.warning("Could not update the AUR index: no internet connection detected") @@ -2488,7 +2488,7 @@ class ArchManager(SoftwareManager): def _optimize_makepkg(self, arch_config: dict, watcher: Optional[ProcessWatcher]): if arch_config['optimize'] and not os.path.exists(CUSTOM_MAKEPKG_FILE): watcher.change_substatus(self.i18n['arch.makepkg.optimizing']) - ArchCompilationOptimizer(arch_config=arch_config, i18n=self.i18n, logger=self.context.logger).optimize() + ArchCompilationOptimizer(i18n=self.i18n, logger=self.context.logger, taskman=TaskManager()).optimize() def install(self, pkg: ArchPackage, root_password: str, disk_loader: Optional[DiskCacheLoader], watcher: ProcessWatcher, context: TransactionContext = None) -> TransactionResult: self.aur_client.clean_caches() @@ -2504,7 +2504,7 @@ class ArchManager(SoftwareManager): if context: install_context = context else: - install_context = TransactionContext.gen_context_from(pkg=pkg, handler=handler, arch_config=read_config(), + install_context = TransactionContext.gen_context_from(pkg=pkg, handler=handler, arch_config=self.configman.get_config(), root_password=root_password) install_context.skip_opt_deps = False install_context.disk_loader = disk_loader @@ -2612,68 +2612,68 @@ class ArchManager(SoftwareManager): def requires_root(self, action: SoftwareAction, pkg: ArchPackage) -> bool: if action == SoftwareAction.PREPARE: - arch_config = read_config() + arch_config = self.configman.get_config() + aur_supported = (pkg and pkg.repository == 'aur') or aur.is_supported(arch_config) - if arch_config['refresh_mirrors_startup'] and mirrors.should_sync(self.logger): + if RefreshMirrors.should_synchronize(arch_config, aur_supported, self.logger): return True - aur_supported = (pkg and pkg.repository == 'aur') or aur.is_supported(arch_config) - return arch_config['sync_databases_startup'] and database.should_sync(arch_config, aur_supported, None, self.logger) + return SyncDatabases.should_sync(mirrors_refreshed=False, arch_config=arch_config, + aur_supported=aur_supported, logger=self.logger) return action != SoftwareAction.SEARCH - def _start_category_task(self, task_man: TaskManager): - task_man.register_task('arch_aur_cats', self.i18n['task.download_categories'], get_icon_path()) - task_man.update_progress('arch_aur_cats', 50, None) + def _start_category_task(self, taskman: TaskManager, create_config: CreateConfigFile, downloader: CategoriesDownloader): + taskman.update_progress('arch_aur_cats', 0, self.i18n['task.waiting_task'].format(bold(create_config.task_name))) + create_config.join() + arch_config = create_config.config - def _finish_category_task(self, task_man: TaskManager): - task_man.update_progress('arch_aur_cats', 100, None) - task_man.finish_task('arch_aur_cats') + downloader.expiration = arch_config['categories_exp'] if isinstance(arch_config['categories_exp'], int) else None + taskman.update_progress('arch_aur_cats', 50, None) + + def _finish_category_task(self, taskman: TaskManager): + taskman.update_progress('arch_aur_cats', 100, None) + taskman.finish_task('arch_aur_cats') def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): - arch_config = read_config(update_file=True) + create_config = CreateConfigFile(taskman=task_manager, configman=self.configman, i18n=self.i18n, + task_icon_path=get_icon_path(), logger=self.logger) + create_config.start() - if internet_available and AURIndexUpdater.should_update(arch_config): - self.index_aur = AURIndexUpdater(context=self.context, taskman=task_manager) # must all execute to properly determine the installed packages (even that AUR is disabled) + if internet_available: + self.index_aur = AURIndexUpdater(context=self.context, taskman=task_manager, create_config=create_config) # must always execute to properly determine the installed packages (even that AUR is disabled) self.index_aur.start() - aur_supported = aur.is_supported(arch_config) - - if aur_supported: - ArchCompilationOptimizer(arch_config, self.i18n, self.context.logger, task_manager).start() - - if aur_supported or arch_config['repositories']: - self.disk_cache_updater = ArchDiskCacheUpdater(task_man=task_manager, - arch_config=arch_config, - i18n=self.i18n, - logger=self.context.logger, - controller=self, - internet_available=internet_available, - aur_indexer=self.index_aur, - aur_supported=aur_supported) - self.disk_cache_updater.start() - - CategoriesDownloader(id_='Arch', http_client=self.context.http_client, logger=self.context.logger, - manager=self, url_categories_file=URL_CATEGORIES_FILE, - categories_path=CATEGORIES_FILE_PATH, - expiration=arch_config['categories_exp'] if isinstance(arch_config['categories_exp'], int) else '', - internet_connection=internet_available, - internet_checker=self.context.internet_checker, - before=lambda: self._start_category_task(task_manager), - after=lambda: self._finish_category_task(task_manager)).start() - - refresh_mirrors = None - if internet_available and arch_config['repositories'] and arch_config['refresh_mirrors_startup'] \ - and pacman.is_mirrors_available() and mirrors.should_sync(self.logger): - - refresh_mirrors = RefreshMirrors(taskman=task_manager, i18n=self.i18n, - root_password=root_password, logger=self.logger, - sort_limit=arch_config['mirrors_sort_limit']) + refresh_mirrors = RefreshMirrors(taskman=task_manager, i18n=self.i18n, root_password=root_password, + logger=self.logger, create_config=create_config) refresh_mirrors.start() - if internet_available and (refresh_mirrors or (arch_config['sync_databases_startup'] and database.should_sync(arch_config, aur_supported, None, self.logger))): SyncDatabases(taskman=task_manager, root_password=root_password, i18n=self.i18n, - logger=self.logger, refresh_mirrors=refresh_mirrors).start() + logger=self.logger, refresh_mirrors=refresh_mirrors, create_config=create_config).start() + + ArchCompilationOptimizer(i18n=self.i18n, logger=self.context.logger, + taskman=task_manager, create_config=create_config).start() + + self.disk_cache_updater = ArchDiskCacheUpdater(taskman=task_manager, + i18n=self.i18n, + logger=self.context.logger, + controller=self, + internet_available=internet_available, + aur_indexer=self.index_aur, + create_config=create_config) + self.disk_cache_updater.start() + + task_manager.register_task('arch_aur_cats', self.i18n['task.download_categories'], get_icon_path()) + cat_download = CategoriesDownloader(id_='Arch', http_client=self.context.http_client, + logger=self.context.logger, + manager=self, url_categories_file=URL_CATEGORIES_FILE, + categories_path=CATEGORIES_FILE_PATH, + internet_connection=internet_available, + internet_checker=self.context.internet_checker, + after=lambda: self._finish_category_task(task_manager)) + cat_download.before = lambda: self._start_category_task(taskman=task_manager, create_config=create_config, + downloader=cat_download) + cat_download.start() def list_updates(self, internet_available: bool) -> List[PackageUpdate]: installed = self.read_installed(disk_loader=None, internet_available=internet_available).installed @@ -2763,90 +2763,92 @@ class ArchManager(SoftwareManager): capitalize_label=capitalize_label) def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: - local_config = read_config() + arch_config = self.configman.get_config() max_width = floor(screen_width * 0.25) db_sync_start = self._gen_bool_selector(id_='sync_dbs_start', label_key='arch.config.sync_dbs', tooltip_key='arch.config.sync_dbs_start.tip', - value=bool(local_config['sync_databases_startup']), + value=bool(arch_config['sync_databases_startup']), max_width=max_width) - db_sync_start.label += ' ( {} )'.format(self.i18n['initialization'].capitalize()) + db_sync_start.label += ' ({})'.format(self.i18n['initialization'].capitalize()) fields = [ self._gen_bool_selector(id_='repos', label_key='arch.config.repos', tooltip_key='arch.config.repos.tip', - value=bool(local_config['repositories']), + value=bool(arch_config['repositories']), max_width=max_width), self._gen_bool_selector(id_='aur', label_key='arch.config.aur', tooltip_key='arch.config.aur.tip', - value=local_config['aur'], + value=arch_config['aur'], max_width=max_width, capitalize_label=False), self._gen_bool_selector(id_='opts', label_key='arch.config.optimize', tooltip_key='arch.config.optimize.tip', - value=bool(local_config['optimize']), + value=bool(arch_config['optimize']), + label_params=['(AUR)'], + capitalize_label=False, max_width=max_width), self._gen_bool_selector(id_='autoprovs', label_key='arch.config.automatch_providers', tooltip_key='arch.config.automatch_providers.tip', - value=bool(local_config['automatch_providers']), + value=bool(arch_config['automatch_providers']), max_width=max_width), self._gen_bool_selector(id_='check_dependency_breakage', label_key='arch.config.check_dependency_breakage', tooltip_key='arch.config.check_dependency_breakage.tip', - value=bool(local_config['check_dependency_breakage']), + value=bool(arch_config['check_dependency_breakage']), max_width=max_width), self._gen_bool_selector(id_='mthread_download', label_key='arch.config.pacman_mthread_download', tooltip_key='arch.config.pacman_mthread_download.tip', - value=local_config['repositories_mthread_download'], + value=arch_config['repositories_mthread_download'], max_width=max_width, capitalize_label=True), self._gen_bool_selector(id_='sync_dbs', label_key='arch.config.sync_dbs', tooltip_key='arch.config.sync_dbs.tip', - value=bool(local_config['sync_databases']), + value=bool(arch_config['sync_databases']), max_width=max_width), db_sync_start, self._gen_bool_selector(id_='clean_cached', label_key='arch.config.clean_cache', tooltip_key='arch.config.clean_cache.tip', - value=bool(local_config['clean_cached']), + value=bool(arch_config['clean_cached']), max_width=max_width), self._gen_bool_selector(id_='suggest_unneeded_uninstall', label_key='arch.config.suggest_unneeded_uninstall', tooltip_params=['"{}"'.format(self.i18n['arch.config.suggest_optdep_uninstall'])], tooltip_key='arch.config.suggest_unneeded_uninstall.tip', - value=bool(local_config['suggest_unneeded_uninstall']), + value=bool(arch_config['suggest_unneeded_uninstall']), max_width=max_width), self._gen_bool_selector(id_='suggest_optdep_uninstall', label_key='arch.config.suggest_optdep_uninstall', tooltip_key='arch.config.suggest_optdep_uninstall.tip', - value=bool(local_config['suggest_optdep_uninstall']), + value=bool(arch_config['suggest_optdep_uninstall']), max_width=max_width), self._gen_bool_selector(id_='ref_mirs', label_key='arch.config.refresh_mirrors', tooltip_key='arch.config.refresh_mirrors.tip', - value=bool(local_config['refresh_mirrors_startup']), + value=bool(arch_config['refresh_mirrors_startup']), max_width=max_width), TextInputComponent(id_='mirrors_sort_limit', label=self.i18n['arch.config.mirrors_sort_limit'], tooltip=self.i18n['arch.config.mirrors_sort_limit.tip'], only_int=True, max_width=max_width, - value=local_config['mirrors_sort_limit'] if isinstance(local_config['mirrors_sort_limit'], int) else ''), + value=arch_config['mirrors_sort_limit'] if isinstance(arch_config['mirrors_sort_limit'], int) else ''), TextInputComponent(id_='aur_idx_exp', label=self.i18n['arch.config.aur_idx_exp'] + ' (AUR)', tooltip=self.i18n['arch.config.aur_idx_exp.tip'], max_width=max_width, only_int=True, capitalize_label=False, - value=local_config['aur_idx_exp'] if isinstance(local_config['aur_idx_exp'], int) else ''), + value=arch_config['aur_idx_exp'] if isinstance(arch_config['aur_idx_exp'], int) else ''), new_select(id_='aur_build_only_chosen', label=self.i18n['arch.config.aur_build_only_chosen'], tip=self.i18n['arch.config.aur_build_only_chosen.tip'], @@ -2854,7 +2856,7 @@ class ArchManager(SoftwareManager): (self.i18n['no'].capitalize(), False, None), (self.i18n['ask'].capitalize(), None, None), ], - value=local_config['aur_build_only_chosen'], + value=arch_config['aur_build_only_chosen'], max_width=max_width, type_=SelectViewType.RADIO, capitalize_label=False), @@ -2865,21 +2867,21 @@ class ArchManager(SoftwareManager): (self.i18n['no'].capitalize(), False, None), (self.i18n['ask'].capitalize(), None, None), ], - value=local_config['edit_aur_pkgbuild'], + value=arch_config['edit_aur_pkgbuild'], max_width=max_width, type_=SelectViewType.RADIO, capitalize_label=False), self._gen_bool_selector(id_='aur_remove_build_dir', label_key='arch.config.aur_remove_build_dir', tooltip_key='arch.config.aur_remove_build_dir.tip', - value=bool(local_config['aur_remove_build_dir']), + value=bool(arch_config['aur_remove_build_dir']), max_width=max_width, capitalize_label=False), FileChooserComponent(id_='aur_build_dir', label=self.i18n['arch.config.aur_build_dir'], tooltip=self.i18n['arch.config.aur_build_dir.tip'].format(BUILD_DIR), max_width=max_width, - file_path=local_config['aur_build_dir'], + file_path=arch_config['aur_build_dir'], capitalize_label=False, directory=True), TextInputComponent(id_='arch_cats_exp', @@ -2888,47 +2890,47 @@ class ArchManager(SoftwareManager): max_width=max_width, only_int=True, capitalize_label=False, - value=local_config['categories_exp'] if isinstance(local_config['categories_exp'], int) else ''), + value=arch_config['categories_exp'] if isinstance(arch_config['categories_exp'], int) else ''), ] return PanelComponent([FormComponent(fields, spaces=False)]) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: - config = read_config() + arch_config = self.configman.get_config() panel = component.components[0] - config['repositories'] = panel.get_component('repos').get_selected() - config['aur'] = panel.get_component('aur').get_selected() - config['optimize'] = panel.get_component('opts').get_selected() - config['sync_databases'] = panel.get_component('sync_dbs').get_selected() - config['sync_databases_startup'] = panel.get_component('sync_dbs_start').get_selected() - config['clean_cached'] = panel.get_component('clean_cached').get_selected() - config['refresh_mirrors_startup'] = panel.get_component('ref_mirs').get_selected() - config['mirrors_sort_limit'] = panel.get_component('mirrors_sort_limit').get_int_value() - config['repositories_mthread_download'] = panel.get_component('mthread_download').get_selected() - config['automatch_providers'] = panel.get_component('autoprovs').get_selected() - config['edit_aur_pkgbuild'] = panel.get_component('edit_aur_pkgbuild').get_selected() - config['aur_remove_build_dir'] = panel.get_component('aur_remove_build_dir').get_selected() - config['aur_build_dir'] = panel.get_component('aur_build_dir').file_path - config['aur_build_only_chosen'] = panel.get_component('aur_build_only_chosen').get_selected() - config['aur_idx_exp'] = panel.get_component('aur_idx_exp').get_int_value() - config['check_dependency_breakage'] = panel.get_component('check_dependency_breakage').get_selected() - config['suggest_optdep_uninstall'] = panel.get_component('suggest_optdep_uninstall').get_selected() - config['suggest_unneeded_uninstall'] = panel.get_component('suggest_unneeded_uninstall').get_selected() - config['categories_exp'] = panel.get_component('arch_cats_exp').get_int_value() + arch_config['repositories'] = panel.get_component('repos').get_selected() + arch_config['aur'] = panel.get_component('aur').get_selected() + arch_config['optimize'] = panel.get_component('opts').get_selected() + arch_config['sync_databases'] = panel.get_component('sync_dbs').get_selected() + arch_config['sync_databases_startup'] = panel.get_component('sync_dbs_start').get_selected() + arch_config['clean_cached'] = panel.get_component('clean_cached').get_selected() + arch_config['refresh_mirrors_startup'] = panel.get_component('ref_mirs').get_selected() + arch_config['mirrors_sort_limit'] = panel.get_component('mirrors_sort_limit').get_int_value() + arch_config['repositories_mthread_download'] = panel.get_component('mthread_download').get_selected() + arch_config['automatch_providers'] = panel.get_component('autoprovs').get_selected() + arch_config['edit_aur_pkgbuild'] = panel.get_component('edit_aur_pkgbuild').get_selected() + arch_config['aur_remove_build_dir'] = panel.get_component('aur_remove_build_dir').get_selected() + arch_config['aur_build_dir'] = panel.get_component('aur_build_dir').file_path + arch_config['aur_build_only_chosen'] = panel.get_component('aur_build_only_chosen').get_selected() + arch_config['aur_idx_exp'] = panel.get_component('aur_idx_exp').get_int_value() + arch_config['check_dependency_breakage'] = panel.get_component('check_dependency_breakage').get_selected() + arch_config['suggest_optdep_uninstall'] = panel.get_component('suggest_optdep_uninstall').get_selected() + arch_config['suggest_unneeded_uninstall'] = panel.get_component('suggest_unneeded_uninstall').get_selected() + arch_config['categories_exp'] = panel.get_component('arch_cats_exp').get_int_value() - if not config['aur_build_dir']: - config['aur_build_dir'] = None + if not arch_config['aur_build_dir']: + arch_config['aur_build_dir'] = None try: - save_config(config, CONFIG_FILE) + self.configman.save_config(arch_config) return True, None except: return False, [traceback.format_exc()] def get_upgrade_requirements(self, pkgs: List[ArchPackage], root_password: str, watcher: ProcessWatcher) -> UpgradeRequirements: self.aur_client.clean_caches() - arch_config = read_config() + arch_config = self.configman.get_config() aur_supported = aur.is_supported(arch_config) @@ -2949,7 +2951,7 @@ class ArchManager(SoftwareManager): def get_custom_actions(self) -> List[CustomSoftwareAction]: actions = [] - arch_config = read_config() + arch_config = self.configman.get_config() if pacman.is_mirrors_available(): actions.append(self.custom_actions['ref_mirrors']) diff --git a/bauh/gems/arch/pacman.py b/bauh/gems/arch/pacman.py index b929f266..13cadd25 100644 --- a/bauh/gems/arch/pacman.py +++ b/bauh/gems/arch/pacman.py @@ -497,7 +497,8 @@ def get_current_mirror_countries() -> List[str]: def is_mirrors_available() -> bool: - return bool(run_cmd('which pacman-mirrors', print_error=False)) + code, _ = system.execute(cmd='which pacman-mirrors', output=False) + return code == 0 def map_update_sizes(pkgs: List[str]) -> Dict[str, int]: # bytes: diff --git a/bauh/gems/arch/resources/locale/ca b/bauh/gems/arch/resources/locale/ca index c7fb64e3..4860325d 100644 --- a/bauh/gems/arch/resources/locale/ca +++ b/bauh/gems/arch/resources/locale/ca @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. -arch.config.optimize=optimize +arch.config.optimize=Optimize {} arch.config.optimize.tip=La configuració optimitzada s'utilitzarà per fer més ràpida la instal·lació, actualització i reversió dels paquets, en cas contrari s'utilitzarà la configuració del sistema. arch.config.pacman_mthread_download=Multithreaded download (repositories) arch.config.pacman_mthread_download.tip=Whether the repository packages should be downloaded with a tool that works with threads (it may be faster). pacman-mirrors must be installed. arch.config.refresh_mirrors=Refresh mirrors on startup -arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup ( or after a device reboot ) +arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup arch.config.repos=Repositories packages arch.config.repos.tip=It allows to manage packages from the repositories set arch.config.suggest_optdep_uninstall=Uninstall optional dependencies @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Running pre-transaction hooks arch.sync.dep_breakage.reason={} requires {} arch.sync_databases.substatus=Synchronizing package databases arch.sync_databases.substatus.error=It was not possible to synchronize the package database +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexing packages data arch.task.disk_cache.checking=Checking index arch.task.disk_cache.indexed=Indexed @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexing arch.task.disk_cache.reading_files=Reading files arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Refreshing mirrors +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Optimizing {} -arch.task.sync_databases.waiting=Waiting for {} arch.task.sync_sb.status=Actualitzen {} arch.uninstall.clean_cached.error=No s'ha pogut eliminar {} versions antigues que es troba al disc arch.uninstall.clean_cached.substatus=Eliminació de versions antigues del disc diff --git a/bauh/gems/arch/resources/locale/de b/bauh/gems/arch/resources/locale/de index e0804d17..6c0168c5 100644 --- a/bauh/gems/arch/resources/locale/de +++ b/bauh/gems/arch/resources/locale/de @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. -arch.config.optimize=optimize +arch.config.optimize=Optimize {} arch.config.optimize.tip=Optimized settings will be used in order to make the packages installation, upgrading and downgrading faster, otherwise the system settings will be used arch.config.pacman_mthread_download=Multithreaded download (repositories) arch.config.pacman_mthread_download.tip=Whether the repository packages should be downloaded with a tool that works with threads (it may be faster). pacman-mirrors must be installed. arch.config.refresh_mirrors=Refresh mirrors on startup -arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup ( or after a device reboot ) +arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup arch.config.repos=Repositories packages arch.config.repos.tip=It allows to manage packages from the repositories set arch.config.suggest_optdep_uninstall=Uninstall optional dependencies @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Running pre-transaction hooks arch.sync.dep_breakage.reason={} requires {} arch.sync_databases.substatus=Synchronizing package databases arch.sync_databases.substatus.error=It was not possible to synchronize the package database +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexing packages data arch.task.disk_cache.checking=Checking index arch.task.disk_cache.indexed=Indexed @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexing arch.task.disk_cache.reading_files=Reading files arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Refreshing mirrors +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Optimizing {} -arch.task.sync_databases.waiting=Waiting for {} arch.task.sync_sb.status=Updating {} arch.uninstall.clean_cached.error=It was not possible to remove old {} versions found on disk arch.uninstall.clean_cached.substatus=Removing old versions from disk diff --git a/bauh/gems/arch/resources/locale/en b/bauh/gems/arch/resources/locale/en index 5de9022e..ef81ee91 100644 --- a/bauh/gems/arch/resources/locale/en +++ b/bauh/gems/arch/resources/locale/en @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. -arch.config.optimize=optimize +arch.config.optimize=Optimize {} arch.config.optimize.tip=Optimized settings will be used in order to make the packages installation, upgrading and downgrading faster, otherwise the system settings will be used arch.config.pacman_mthread_download=Multi-threaded download (repositories) arch.config.pacman_mthread_download.tip=Whether the repository packages should be downloaded with a tool that works with threads (it may be faster). pacman-mirrors must be installed. arch.config.refresh_mirrors=Refresh mirrors on startup -arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup ( or after a device reboot ) +arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup arch.config.repos=Repositories packages arch.config.repos.tip=It allows to manage packages from the repositories set arch.config.suggest_optdep_uninstall=Uninstall optional dependencies @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Running pre-transaction hooks arch.sync.dep_breakage.reason={} requires {} arch.sync_databases.substatus=Synchronizing package databases arch.sync_databases.substatus.error=It was not possible to synchronize the package database +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexing packages data arch.task.disk_cache.checking=Checking index arch.task.disk_cache.indexed=Indexed @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexing arch.task.disk_cache.reading_files=Reading files arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Refreshing mirrors +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Optimizing {} -arch.task.sync_databases.waiting=Waiting for {} arch.task.sync_sb.status=Updating {} arch.uninstall.clean_cached.error=It was not possible to remove old {} versions found on disk arch.uninstall.clean_cached.substatus=Removing old versions from disk diff --git a/bauh/gems/arch/resources/locale/es b/bauh/gems/arch/resources/locale/es index 8d79a7dc..2d500d3e 100644 --- a/bauh/gems/arch/resources/locale/es +++ b/bauh/gems/arch/resources/locale/es @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Expiración del índice arch.config.aur_idx_exp.tip=Define el período (en minutos) para que el índice AUR almacenado en el disco se considere válido durante el proceso de inicialización. Utilice 0 para que esté siempre actualizado. arch.config.mirrors_sort_limit=Límite de ordenación de espejos arch.config.mirrors_sort_limit.tip=Define el número máximo de espejos que se utilizarán para la ordenación por velocidad. Use 0 para no limitar o déjelo en blanco para deshabilitar la clasificación. -arch.config.optimize=optimizar +arch.config.optimize=Optimizar {} arch.config.optimize.tip=Se usará la configuración optimizada para que la instalación, actualización y reversión de los paquetes sean más rápidas, de lo contrario se usará la configuración del sistema arch.config.pacman_mthread_download=Descarga segmentada (repositorios) arch.config.pacman_mthread_download.tip=Si los paquetes de los repositorios deben descargarse con una herramienta que usa segmentación/threads (puede ser más rápido). pacman-mirrors necesita estar instalado. arch.config.refresh_mirrors=Actualizar espejos al iniciar -arch.config.refresh_mirrors.tip=Actualiza los espejos de paquetes una vez al día al iniciar ( o después de reiniciar el dispositivo ) +arch.config.refresh_mirrors.tip=Actualiza los espejos de paquetes una vez al día al iniciar arch.config.repos=Paquetes de repositorios arch.config.repos.tip=Permite gestionar paquetes de los repositorios configurados arch.config.suggest_optdep_uninstall=Desinstalar dependencias opcionales @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Ejecutando ganchos pre-transacción arch.sync.dep_breakage.reason={} necesita de {} arch.sync_databases.substatus=Sincronizando bases de paquetes arch.sync_databases.substatus.error=No fue posible sincronizar la base de paquetes +arch.sync_databases.substatus.synchronized=Sincronizado arch.task.aur.index.status=Generando índice local del AUR +arch.task.aur.index.substatus.checking=Buscando actualizaciones arch.task.aur.index.substatus.download=Descargando el índice arch.task.aur.index.substatus.error.download=Error de conexión al descargar el índice arch.task.aur.index.substatus.error.no_data=Error: índice vacío arch.task.aur.index.substatus.gen_index=Generando índice +arch.task.checking_settings=Verificando configuraciones +arch.task.disabled=Deshabilitado arch.task.disk_cache=Indexando datos de paquetes arch.task.disk_cache.checking=Comprobando índice arch.task.disk_cache.indexed=Indexados @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexando arch.task.disk_cache.reading_files=Lendo archivos arch.task.disk_cache.waiting_aur_index=Esperando {} arch.task.mirrors=Actualizando espejos +arch.task.mirrors.cached=Actualizados arch.task.optimizing=Optimizando {} -arch.task.sync_databases.waiting=Esperando por {} arch.task.sync_sb.status=Actualizando {} arch.uninstall.clean_cached.error=No fue posible eliminar versiones antiguas de {} encontradas en disco arch.uninstall.clean_cached.substatus=Eliminando versiones antiguas del disco diff --git a/bauh/gems/arch/resources/locale/fr b/bauh/gems/arch/resources/locale/fr index c9a3c609..80e8581b 100644 --- a/bauh/gems/arch/resources/locale/fr +++ b/bauh/gems/arch/resources/locale/fr @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Limite de tri des miroirs arch.config.mirrors_sort_limit.tip=Définit le nombre maximal de miroirs utilisés pour trier vite. 0 pour aucune limite ou vide pour désactiver le tri. -arch.config.optimize=optimizer +arch.config.optimize=Optimizer {} arch.config.optimize.tip=Utiliser des paramètres optimisés pour rendre l'installation, mise à jour et downgrade des paquets plus rapide. À défaut, les paramètres systèmes seront utilisés. arch.config.pacman_mthread_download=Téléchargement parallèle (repos) arch.config.pacman_mthread_download.tip=Si il faut utiliser un outil qui télécharge les paquets du répos en parallèle (plus rapide). pacman-mirrors doit être installé. arch.config.refresh_mirrors=Actualiser les miroirs au démarrage -arch.config.refresh_mirrors.tip=Actualiser les miroirs du paquet une fois par jour au démarrage ( ou après redémarrage système ) +arch.config.refresh_mirrors.tip=Actualiser les miroirs du paquet une fois par jour au démarrage arch.config.repos=Repos des paquets arch.config.repos.tip=Permet de gerer les paquets du repo arch.config.suggest_optdep_uninstall=Désinstaller les dépendances optionnelles @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Éxécution des pre-transaction hooks arch.sync.dep_breakage.reason={} requiert {} arch.sync_databases.substatus=Synchronisation des bases de données du paquet arch.sync_databases.substatus.error=Impossible de synchroniser les bases de données du paquet +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexation des données des paquets arch.task.disk_cache.checking=Verification de l'index arch.task.disk_cache.indexed=Indexé @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexation arch.task.disk_cache.reading_files=Lecture des fichiers arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Mise à jour des miroirs +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Optimisation {} -arch.task.sync_databases.waiting=Attente de {} arch.task.sync_sb.status=Mise à jour {} arch.uninstall.clean_cached.error=Impossible de supprimer les anciennes version de {} trouvées sur le disque arch.uninstall.clean_cached.substatus=Suppression des anciennes versions diff --git a/bauh/gems/arch/resources/locale/it b/bauh/gems/arch/resources/locale/it index 020f5c48..43567720 100644 --- a/bauh/gems/arch/resources/locale/it +++ b/bauh/gems/arch/resources/locale/it @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Mirrors sort limit arch.config.mirrors_sort_limit.tip=Defines the maximum number of mirrors that will be used for speed sorting. Use 0 for no limit or leave it blank to disable sorting. -arch.config.optimize=optimize +arch.config.optimize=Optimize {} arch.config.optimize.tip=Verranno utilizzate le impostazioni ottimizzate per velocizzare l'installazione, l'aggiornamento e l'inversione dei pacchetti, altrimenti verranno utilizzate le impostazioni di sistema arch.config.pacman_mthread_download=Multithreaded download (repositories) arch.config.pacman_mthread_download.tip=Whether the repository packages should be downloaded with a tool that works with threads (it may be faster). pacman-mirrors must be installed. arch.config.refresh_mirrors=Refresh mirrors on startup -arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup ( or after a device reboot ) +arch.config.refresh_mirrors.tip=Refresh the package mirrors once a day on startup arch.config.repos=Repositories packages arch.config.repos.tip=It allows to manage packages from the repositories set arch.config.suggest_optdep_uninstall=Uninstall optional dependencies @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Running pre-transaction hooks arch.sync.dep_breakage.reason={} requires {} arch.sync_databases.substatus=Synchronizing package databases arch.sync_databases.substatus.error=It was not possible to synchronize the package database +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexing packages data arch.task.disk_cache.checking=Checking index arch.task.disk_cache.indexed=Indexed @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexing arch.task.disk_cache.reading_files=Reading files arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Aggiornando i mirror +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Ottimizzando {} -arch.task.sync_databases.waiting=Waiting for {} arch.task.sync_sb.status=Aggiornando {} arch.uninstall.clean_cached.error=Non è stato possibile rimuovere le vecchie {} versioni trovate sul disco arch.uninstall.clean_cached.substatus=Rimozione di versioni precedenti dal disco diff --git a/bauh/gems/arch/resources/locale/pt b/bauh/gems/arch/resources/locale/pt index 2d586fe2..e127c31b 100644 --- a/bauh/gems/arch/resources/locale/pt +++ b/bauh/gems/arch/resources/locale/pt @@ -53,7 +53,7 @@ arch.config.aur_idx_exp=Expiração do índice arch.config.aur_idx_exp.tip=Define o período (em minutos) em que o índice do AUR armazenado em disco é considerado válido durante a inicialização. Utilize 0 para que ele sempre seja atualizado. arch.config.mirrors_sort_limit=Limite de ordenação de espelhos arch.config.mirrors_sort_limit.tip=Define o número máximo de espelhos que serão utilizados para a ordenação por velocidade. Utilize 0 para não limitar ou deixe em branco para desabilitar a ordenação. -arch.config.optimize=Otimizar +arch.config.optimize=Otimizar {} arch.config.pacman_mthread_download=Download segmentado (repositórios) arch.config.pacman_mthread_download.tip=Se os pacotes dos repositórios devem baixados através de uma ferramenta que trabalha com segmentação/threads (pode ser mais rápido). pacman-mirrors precisa estar instalado. arch.config.refresh_mirrors=Atualizar espelhos ao iniciar @@ -209,11 +209,15 @@ arch.substatus.pre_hooks=Executando ganchos pré-transação arch.sync.dep_breakage.reason={} precisa de {} arch.sync_databases.substatus=Sincronizando bases de pacotes arch.sync_databases.substatus.error=Não foi possível sincronizar as bases de pacotes +arch.sync_databases.substatus.synchronized=Sincronizado arch.task.aur.index.status=Gerando índice local do AUR +arch.task.aur.index.substatus.checking=Checando atualizações arch.task.aur.index.substatus.download=Baixando o índice arch.task.aur.index.substatus.error.download=Erro de conexão ao baixar o índice arch.task.aur.index.substatus.error.no_data=Error: índice vazio arch.task.aur.index.substatus.gen_index=Gerando índice +arch.task.checking_settings=Verificando configurações +arch.task.disabled=Desabilitado arch.task.disk_cache=Indexando dados de pacotes arch.task.disk_cache.checking=Verificando índice arch.task.disk_cache.indexed=Indexados @@ -221,8 +225,8 @@ arch.task.disk_cache.indexing=Indexando arch.task.disk_cache.reading_files=Lendo arquivos arch.task.disk_cache.waiting_aur_index=Aguardando {} arch.task.mirrors=Atualizando espelhos +arch.task.mirrors.cached=Atualizados arch.task.optimizing=Otimizando {} -arch.task.sync_databases.waiting=Aguardando por {} arch.task.sync_sb.status=Atualizando {} arch.uninstall.clean_cached.error=Não foi possível remover versões antigas de {} encontradas em disco arch.uninstall.clean_cached.substatus=Removendo versões antigas do disco diff --git a/bauh/gems/arch/resources/locale/ru b/bauh/gems/arch/resources/locale/ru index 50279fb3..9a5a58f9 100644 --- a/bauh/gems/arch/resources/locale/ru +++ b/bauh/gems/arch/resources/locale/ru @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Ограничение сортировки зеркал arch.config.mirrors_sort_limit.tip=Определяет максимальное количество зеркал, которые будут использоваться для сортировки по скорости. Используйте 0 для No limit или оставьте его пустым, чтобы отключить сортировку. -arch.config.optimize=Оптимизация +arch.config.optimize=Оптимизация {} arch.config.optimize.tip=Оптимизированные настройки будут использоваться для ускорения установки пакетов, в противном случае будут использоваться системные настройки arch.config.pacman_mthread_download=Multithreaded download (repositories) arch.config.pacman_mthread_download.tip=Whether the repository packages should be downloaded with a tool that works with threads (it may be faster). pacman-mirrors must be installed. arch.config.refresh_mirrors=Обновить зеркала при запуске -arch.config.refresh_mirrors.tip=Обновляйте зеркала пакета один раз в день при запуске (или после перезагрузки устройства) +arch.config.refresh_mirrors.tip=Обновляйте зеркала пакета один раз в день при запуске arch.config.repos=Пакеты репозиториев arch.config.repos.tip=Он позволяет управлять пакетами из набора репозиториев arch.config.suggest_optdep_uninstall=Uninstall optional dependencies @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Running pre-transaction hooks arch.sync.dep_breakage.reason={} requires {} arch.sync_databases.substatus=Синхронизация баз данных пакетов arch.sync_databases.substatus.error=Синхронизировать базу данных пакета не удалось +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexing packages data arch.task.disk_cache.checking=Checking index arch.task.disk_cache.indexed=Indexed @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexing arch.task.disk_cache.reading_files=Reading files arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Обновление зеркал +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Optimizing {} -arch.task.sync_databases.waiting=Waiting for {} arch.task.sync_sb.status=Updating {} arch.uninstall.clean_cached.error=It was not possible to remove old {} versions found on disk arch.uninstall.clean_cached.substatus=Removing old versions from disk diff --git a/bauh/gems/arch/resources/locale/tr b/bauh/gems/arch/resources/locale/tr index 865603af..13d3b726 100644 --- a/bauh/gems/arch/resources/locale/tr +++ b/bauh/gems/arch/resources/locale/tr @@ -53,12 +53,12 @@ arch.config.aur_idx_exp=Index expiration arch.config.aur_idx_exp.tip=It defines the period (in minutes) for the AUR index stored in disc to be considered valid during the initialization process. Use 0 so that it is always updated. arch.config.mirrors_sort_limit=Yansı sıralama sınırı arch.config.mirrors_sort_limit.tip=Hız sıralama için kullanılacak maksimum yansı sayısını tanımlar. Sınırsız olması için 0 kullanın veya sıralamayı devre dışı bırakmak için boş bırakın. -arch.config.optimize=optimize +arch.config.optimize=Optimize {} arch.config.optimize.tip=Paketlerin kurulumunu, yükseltilmesini ve indirilmesini hızlandırmak için optimize edilmiş ayarlar kullanılacak, aksi takdirde sistem ayarları kullanılacak arch.config.pacman_mthread_download=Multithreaded download (repositories) arch.config.pacman_mthread_download.tip=Whether the repository packages should be downloaded with a tool that works with threads (it may be faster). pacman-mirrors must be installed. arch.config.refresh_mirrors=Başlangıçta yansıları yenile -arch.config.refresh_mirrors.tip=Paket yansılarını başlangıçta günde bir kez (veya cihaz yeniden başlatıldıktan sonra) yenileyin +arch.config.refresh_mirrors.tip=Paket yansılarını başlangıçta günde bir kez yenileyin arch.config.repos=Depo paketleri arch.config.repos.tip=Paketleri depolardan yönetmeye izin verir arch.config.suggest_optdep_uninstall=Uninstall optional dependencies @@ -210,11 +210,15 @@ arch.substatus.pre_hooks=Running pre-transaction hooks arch.sync.dep_breakage.reason={} requires {} arch.sync_databases.substatus=Paket veritabanı eşitleniyor arch.sync_databases.substatus.error=Paket veritabanı eşitlenemedi +arch.sync_databases.substatus.synchronized=Synchronized arch.task.aur.index.status=Generating local AUR index +arch.task.aur.index.substatus.checking=Checking for updates arch.task.aur.index.substatus.download=Downloading the AUR index arch.task.aur.index.substatus.error.download=Connection error while downloading the index arch.task.aur.index.substatus.error.no_data=Error: empty index arch.task.aur.index.substatus.gen_index=Generating index +arch.task.checking_settings=Checking settings +arch.task.disabled=Disabled arch.task.disk_cache=Indexing packages data arch.task.disk_cache.checking=Checking index arch.task.disk_cache.indexed=Indexed @@ -222,8 +226,8 @@ arch.task.disk_cache.indexing=Indexing arch.task.disk_cache.reading_files=Reading files arch.task.disk_cache.waiting_aur_index=Waiting {} arch.task.mirrors=Yansılar yenileniyor +arch.task.mirrors.cached=Refreshed arch.task.optimizing=Uygun hale getiriliyor {} -arch.task.sync_databases.waiting=Waiting for {} arch.task.sync_sb.status={} güncelleniyor arch.uninstall.clean_cached.error=Diskte bulunan eski {} sürümleri kaldırılamadı arch.uninstall.clean_cached.substatus=Eski sürümler diskten kaldırılıyor diff --git a/bauh/gems/arch/worker.py b/bauh/gems/arch/worker.py index 63f4ecdc..3fbcd82f 100644 --- a/bauh/gems/arch/worker.py +++ b/bauh/gems/arch/worker.py @@ -13,11 +13,12 @@ import requests from bauh.api.abstract.context import ApplicationContext from bauh.api.abstract.handler import TaskManager +from bauh.commons import system +from bauh.commons.boot import CreateConfigFile from bauh.commons.html import bold -from bauh.commons.system import run_cmd, new_root_subprocess, ProcessHandler -from bauh.commons.util import datetime_as_milis +from bauh.commons.system import new_root_subprocess, ProcessHandler from bauh.gems.arch import pacman, disk, CUSTOM_MAKEPKG_FILE, CONFIG_DIR, AUR_INDEX_FILE, get_icon_path, database, \ - mirrors, ARCH_CACHE_PATH, BUILD_DIR, AUR_INDEX_TS_FILE + mirrors, ARCH_CACHE_PATH, AUR_INDEX_TS_FILE, aur from bauh.gems.arch.aur import URL_INDEX from bauh.view.util.translation import I18n @@ -31,18 +32,20 @@ RE_CLEAR_REPLACE = re.compile(r'[\-_.]') class AURIndexUpdater(Thread): - def __init__(self, context: ApplicationContext, taskman: TaskManager): + def __init__(self, context: ApplicationContext, taskman: TaskManager, create_config: Optional[CreateConfigFile] = None, arch_config: Optional[dict] = None): super(AURIndexUpdater, self).__init__(daemon=True) self.http_client = context.http_client self.i18n = context.i18n self.logger = context.logger self.taskman = taskman self.task_id = 'index_aur' + self.create_config = create_config + self.config = arch_config + self.taskman.register_task(self.task_id, self.i18n['arch.task.aur.index.status'], get_icon_path()) - @staticmethod - def should_update(arch_config: dict) -> bool: + def should_update(self) -> bool: try: - exp_minutes = int(arch_config['aur_idx_exp']) + exp_minutes = int(self.config['aur_idx_exp']) except: traceback.print_exc() return True @@ -66,11 +69,9 @@ class AURIndexUpdater(Thread): traceback.print_exc() return True - def run(self): - ti = time.time() + def update_index(self): self.logger.info('Indexing AUR packages') - self.taskman.register_task(self.task_id, self.i18n['arch.task.aur.index.status'], get_icon_path()) - self.taskman.update_progress(self.task_id, 1, self.i18n['arch.task.aur.index.substatus.download']) + self.taskman.update_progress(self.task_id, 5, self.i18n['arch.task.aur.index.substatus.download']) try: index_ts = datetime.utcnow().timestamp() res = self.http_client.get(URL_INDEX) @@ -115,6 +116,22 @@ class AURIndexUpdater(Thread): self.logger.warning('No internet connection: could not pre-index packages') self.taskman.update_progress(self.task_id, 100, self.i18n['arch.task.aur.index.substatus.error.download']) + def run(self): + ti = time.time() + + if self.create_config: + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(self.create_config.task_name)) + self.create_config.join() + self.config = self.create_config.config + + self.taskman.update_progress(self.task_id, 1, self.i18n['arch.task.aur.index.substatus.checking']) + + if self.should_update(): + self.update_index() + else: + self.logger.info("AUR index is up to date. Aborting...") + self.taskman.update_progress(self.task_id, 100, None) + tf = time.time() self.taskman.finish_task(self.task_id) self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti)) @@ -122,48 +139,56 @@ class AURIndexUpdater(Thread): class ArchDiskCacheUpdater(Thread): - def __init__(self, task_man: TaskManager, arch_config: dict, i18n: I18n, logger: logging.Logger, controller: "ArchManager", internet_available: bool, - aur_supported: bool, aur_indexer: Thread): + def __init__(self, taskman: TaskManager, i18n: I18n, logger: logging.Logger, + controller: "ArchManager", internet_available: bool, aur_indexer: Thread, + create_config: CreateConfigFile): super(ArchDiskCacheUpdater, self).__init__(daemon=True) self.logger = logger - self.task_man = task_man + self.taskman = taskman self.task_id = 'arch_cache_up' self.i18n = i18n self.indexed = 0 self.indexed_template = self.i18n['arch.task.disk_cache.indexed'] + ': {}/ {}' self.to_index = 0 self.progress = 0 # progress is defined by the number of packages prepared and indexed - self.repositories = arch_config['repositories'] - self.aur = aur_supported self.controller = controller self.internet_available = internet_available self.installed_hash_path = '{}/installed.sha1'.format(ARCH_CACHE_PATH) self.installed_cache_dir = '{}/installed'.format(ARCH_CACHE_PATH) self.aur_indexer = aur_indexer + self.create_config = create_config + self.taskman.register_task(self.task_id, self.i18n['arch.task.disk_cache'], get_icon_path()) def update_indexed(self, pkgname: str): self.indexed += 1 sub = self.indexed_template.format(self.indexed, self.to_index) progress = self.progress + (self.indexed / self.to_index) * 50 - self.task_man.update_progress(self.task_id, progress, sub) + self.taskman.update_progress(self.task_id, progress, sub) def _update_progress(self, progress: float, msg: str): self.progress = progress - self.task_man.update_progress(self.task_id, self.progress, msg) + self.taskman.update_progress(self.task_id, self.progress, msg) def _notify_reading_files(self): self._update_progress(50, self.i18n['arch.task.disk_cache.indexing']) def run(self): - if not any([self.aur, self.repositories]): + ti = time.time() + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(self.create_config.task_name)) + self.create_config.join() + + config = self.create_config.config + aur_supported, repositories = aur.is_supported(config), config['repositories'] + + self.taskman.update_progress(self.task_id, 1, None) + if not any([aur_supported, repositories]): + self.taskman.update_progress(self.task_id, 100, self.i18n['arch.task.disabled']) + self.taskman.finish_task(self.task_id) return - ti = time.time() - self.task_man.register_task(self.task_id, self.i18n['arch.task.disk_cache'], get_icon_path()) - self.logger.info("Checking already cached package data") - self._update_progress(1, self.i18n['arch.task.disk_cache.checking']) + cache_dirs = [fpath for fpath in glob.glob('{}/*'.format(self.installed_cache_dir)) if os.path.isdir(fpath)] not_cached_names = None @@ -177,8 +202,8 @@ class ArchDiskCacheUpdater(Thread): self._update_progress(20, self.i18n['arch.task.disk_cache.checking']) if not not_cached_names: - self.task_man.update_progress(self.task_id, 100, '') - self.task_man.finish_task(self.task_id) + self.taskman.update_progress(self.task_id, 100, '') + self.taskman.finish_task(self.task_id) tf = time.time() time_msg = '{0:.2f} seconds'.format(tf - ti) self.logger.info('Finished: no package data to cache ({})'.format(time_msg)) @@ -186,8 +211,8 @@ class ArchDiskCacheUpdater(Thread): self.logger.info('Pre-caching installed Arch packages data to disk') - if self.aur and self.aur_indexer: - self.task_man.update_progress(self.task_id, 20, self.i18n['arch.task.disk_cache.waiting_aur_index'].format(bold(self.i18n['arch.task.aur.index.status']))) + if aur_supported and self.aur_indexer: + self.taskman.update_progress(self.task_id, 20, self.i18n['arch.task.disk_cache.waiting_aur_index'].format(bold(self.i18n['arch.task.aur.index.status']))) self.aur_indexer.join() self._update_progress(21, self.i18n['arch.task.disk_cache.checking']) @@ -199,7 +224,7 @@ class ArchDiskCacheUpdater(Thread): saved = 0 - pkgs = {p.name: p for p in installed if ((self.aur and p.repository == 'aur') or (self.repositories and p.repository != 'aur')) and not os.path.exists(p.get_disk_cache_path())} + pkgs = {p.name: p for p in installed if ((aur_supported and p.repository == 'aur') or (repositories and p.repository != 'aur')) and not os.path.exists(p.get_disk_cache_path())} self.to_index = len(pkgs) # overwrite == True because the verification already happened @@ -207,8 +232,8 @@ class ArchDiskCacheUpdater(Thread): saved += disk.write_several(pkgs=pkgs, after_desktop_files=self._notify_reading_files, after_written=self.update_indexed, overwrite=True) - self.task_man.update_progress(self.task_id, 100, None) - self.task_man.finish_task(self.task_id) + self.taskman.update_progress(self.task_id, 100, None) + self.taskman.finish_task(self.task_id) tf = time.time() time_msg = '{0:.2f} seconds'.format(tf - ti) @@ -217,7 +242,7 @@ class ArchDiskCacheUpdater(Thread): class ArchCompilationOptimizer(Thread): - def __init__(self, arch_config: dict, i18n: I18n, logger: logging.Logger, taskman: Optional[TaskManager] = None): + def __init__(self, i18n: I18n, logger: logging.Logger, taskman: TaskManager, create_config: Optional[CreateConfigFile] = None): super(ArchCompilationOptimizer, self).__init__(daemon=True) self.logger = logger self.i18n = i18n @@ -227,17 +252,12 @@ class ArchCompilationOptimizer(Thread): self.re_ccache = re.compile(r'!?ccache') self.taskman = taskman self.task_id = 'arch_make_optm' - self.optimizations = bool(arch_config['optimize']) + self.create_config = create_config + self.taskman.register_task(self.task_id, self.i18n['arch.task.optimizing'].format(bold('makepkg.conf')), get_icon_path()) def _is_ccache_installed(self) -> bool: - return bool(run_cmd('which ccache', print_error=False)) - - def _update_progress(self, progress: float, substatus: str = None): - if self.taskman: - self.taskman.update_progress(self.task_id, progress, substatus) - - if progress == 100: - self.taskman.finish_task(self.task_id) + code, _ = system.execute(cmd='which ccache', output=False) + return code == 0 def optimize(self): ti = time.time() @@ -271,7 +291,7 @@ class ArchCompilationOptimizer(Thread): else: optimizations.append('MAKEFLAGS="-j$(nproc)"') - self._update_progress(20) + self.taskman.update_progress(self.task_id, 20, None) compress_xz = self.re_compress_xz.findall(custom_makepkg or global_makepkg) @@ -286,7 +306,7 @@ class ArchCompilationOptimizer(Thread): else: optimizations.append('COMPRESSXZ=(xz -c -z - --threads=0)') - self._update_progress(40) + self.taskman.update_progress(self.task_id, 40, None) compress_zst = self.re_compress_zst.findall(custom_makepkg or global_makepkg) @@ -301,7 +321,7 @@ class ArchCompilationOptimizer(Thread): else: optimizations.append('COMPRESSZST=(zstd -c -z -q - --threads=0)') - self._update_progress(60) + self.taskman.update_progress(self.task_id, 60, None) build_envs = self.re_build_env.findall(custom_makepkg or global_makepkg) @@ -331,7 +351,7 @@ class ArchCompilationOptimizer(Thread): self.logger.info('Adding a BUILDENV declaration') optimizations.append('BUILDENV=(ccache)') - self._update_progress(80) + self.taskman.update_progress(self.task_id, 80, None) if custom_makepkg and optimizations: generated_by = '# \n' @@ -348,43 +368,91 @@ class ArchCompilationOptimizer(Thread): self.logger.info("Removing old optimized 'makepkg.conf' at '{}'".format(CUSTOM_MAKEPKG_FILE)) os.remove(CUSTOM_MAKEPKG_FILE) + self.taskman.update_progress(self.task_id, 100, None) tf = time.time() - self._update_progress(100) - self.logger.info("Optimizations took {0:.2f} seconds".format(tf - ti)) - self.logger.info('Finished') + self.logger.info('Finished. {0:.2f} seconds'.format(tf - ti)) def run(self): - if not self.optimizations: - self.logger.info("Arch packages compilation optimizations are disabled") + ti = time.time() + if self.create_config: + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(bold(self.create_config.task_name))) + self.create_config.join() - if os.path.exists(CUSTOM_MAKEPKG_FILE): - self.logger.info("Removing custom 'makepkg.conf' -> '{}'".format(CUSTOM_MAKEPKG_FILE)) - os.remove(CUSTOM_MAKEPKG_FILE) + self.taskman.update_progress(self.task_id, 1, None) - self.logger.info('Finished') - else: - if self.taskman: - self.taskman.register_task(self.task_id, self.i18n['arch.task.optimizing'].format(bold('makepkg.conf')), get_icon_path()) + if self.create_config.config['optimize'] and aur.is_supported(self.create_config.config): + try: + self.optimize() + except: + self.logger.error("Unexpected exception") + traceback.print_exc() + self.taskman.update_progress(self.task_id, 100, None) + else: + self.logger.info("AUR packages compilation optimizations are disabled") - self.optimize() + if os.path.exists(CUSTOM_MAKEPKG_FILE): + try: + self.logger.info("Removing custom 'makepkg.conf' -> '{}'".format(CUSTOM_MAKEPKG_FILE)) + os.remove(CUSTOM_MAKEPKG_FILE) + except: + self.logger.error("Unexpected exception") + traceback.print_exc() + + self.taskman.update_progress(self.task_id, 100, self.i18n['arch.task.disabled']) + + tf = time.time() + self.taskman.finish_task(self.task_id) + self.logger.info('Finished. Took {0:.2f} seconds'.format(tf - ti)) class RefreshMirrors(Thread): - def __init__(self, taskman: TaskManager, root_password: str, i18n: I18n, sort_limit: int, logger: logging.Logger): + def __init__(self, taskman: TaskManager, root_password: str, i18n: I18n, logger: logging.Logger, + create_config: CreateConfigFile): super(RefreshMirrors, self).__init__(daemon=True) self.taskman = taskman self.i18n = i18n self.logger = logger self.root_password = root_password self.task_id = "arch_mirrors" - self.sort_limit = sort_limit + self.create_config = create_config + self.refreshed = False + self.task_name = self.i18n['arch.task.mirrors'] + self.taskman.register_task(self.task_id, self.task_name, get_icon_path()) def _notify_output(self, output: str): self.taskman.update_output(self.task_id, output) + @staticmethod + def is_enabled(arch_config: dict, aur_supported: bool) -> bool: + return (arch_config['repositories'] or aur_supported) \ + and arch_config['refresh_mirrors_startup'] and pacman.is_mirrors_available() + + @classmethod + def should_synchronize(cls, arch_config: dict, aur_supported: bool, logger: logging.Logger) -> bool: + return cls.is_enabled(arch_config, aur_supported) and mirrors.should_sync(logger) + def run(self): - self.taskman.register_task(self.task_id, self.i18n['arch.task.mirrors'], get_icon_path()) + ti = time.time() + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(bold(self.create_config.task_name))) + self.create_config.join() + + arch_config = self.create_config.config + aur_supported = aur.is_supported(arch_config) + + self.taskman.update_progress(self.task_id, 1, self.i18n['arch.task.checking_settings']) + + if not self.is_enabled(arch_config, aur_supported): + self.taskman.update_progress(self.task_id, 100, self.i18n['arch.task.disabled']) + self.taskman.finish_task(self.task_id) + return + + if not mirrors.should_sync(self.logger): + self.taskman.update_progress(self.task_id, 100, self.i18n['arch.task.mirrors.cached']) + self.taskman.finish_task(self.task_id) + return + + sort_limit = arch_config['mirrors_sort_limit'] self.logger.info("Refreshing mirrors") handler = ProcessHandler() @@ -394,15 +462,16 @@ class RefreshMirrors(Thread): if success: - if self.sort_limit is not None and self.sort_limit >= 0: + if sort_limit is not None and sort_limit >= 0: self.taskman.update_progress(self.task_id, 50, self.i18n['arch.custom_action.refresh_mirrors.status.updating']) try: - handler.handle_simple(pacman.sort_fastest_mirrors(self.root_password, self.sort_limit), output_handler=self._notify_output) + handler.handle_simple(pacman.sort_fastest_mirrors(self.root_password, sort_limit), output_handler=self._notify_output) except: self.logger.error("Could not sort mirrors by speed") traceback.print_exc() mirrors.register_sync(self.logger) + self.refreshed = True else: self.logger.error("It was not possible to refresh mirrors") except: @@ -411,12 +480,14 @@ class RefreshMirrors(Thread): self.taskman.update_progress(self.task_id, 100, None) self.taskman.finish_task(self.task_id) - self.logger.info("Finished") + tf = time.time() + self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti)) class SyncDatabases(Thread): - def __init__(self, taskman: TaskManager, root_password: str, i18n: I18n, logger: logging.Logger, refresh_mirrors: RefreshMirrors = None): + def __init__(self, taskman: TaskManager, root_password: str, i18n: I18n, logger: logging.Logger, + refresh_mirrors: RefreshMirrors, create_config: CreateConfigFile): super(SyncDatabases, self).__init__(daemon=True) self.task_man = taskman self.i18n = i18n @@ -425,15 +496,46 @@ class SyncDatabases(Thread): self.root_password = root_password self.refresh_mirrors = refresh_mirrors self.logger = logger + self.create_config = create_config + self.task_name = self.i18n['arch.sync_databases.substatus'] + self.taskman.register_task(self.task_id, self.task_name, get_icon_path()) + self.synchronized = False + + @staticmethod + def is_enabled(arch_config: dict, aur_supported: bool) -> bool: + return arch_config['sync_databases_startup'] and (aur_supported or arch_config['repositories']) + + @classmethod + def should_sync(cls, mirrors_refreshed: bool, arch_config: dict, aur_supported: bool, logger: logging.Logger): + return mirrors_refreshed or (cls.is_enabled(arch_config, aur_supported) and database.should_sync(arch_config, aur_supported, None, logger)) def run(self) -> None: + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(bold(self.create_config.task_name))) + self.create_config.join() + + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(bold(self.refresh_mirrors.task_name))) + self.refresh_mirrors.join() + + self.taskman.update_progress(self.task_id, 1, self.i18n['arch.task.checking_settings']) + + arch_config = self.create_config.config + aur_supported = aur.is_supported(arch_config) + if not self.is_enabled(arch_config, aur_supported): + self.taskman.update_progress(self.task_id, 100, self.i18n['arch.task.disabled']) + self.taskman.finish_task(self.task_id) + return + + shoud_sync = self.refresh_mirrors.refreshed or (database.should_sync(arch_config, aur_supported, None, self.logger)) + + if not shoud_sync: + self.taskman.update_progress(self.task_id, 100, self.i18n['arch.sync_databases.substatus.synchronized']) + self.taskman.finish_task(self.task_id) + self.synchronized = True + return + self.logger.info("Synchronizing databases") self.taskman.register_task(self.task_id, self.i18n['arch.sync_databases.substatus'], get_icon_path()) - if self.refresh_mirrors and self.refresh_mirrors.is_alive(): - self.taskman.update_progress(self.task_id, 0, self.i18n['arch.task.sync_databases.waiting'].format('"{}"'.format(self.i18n['arch.task.mirrors']))) - self.refresh_mirrors.join() - progress = 10 dbs = pacman.get_databases() self.taskman.update_progress(self.task_id, progress, None) @@ -472,6 +574,7 @@ class SyncDatabases(Thread): if p.returncode == 0: database.register_sync(self.logger) + self.synchronized = True else: self.logger.error("Could not synchronize database") diff --git a/bauh/gems/flatpak/__init__.py b/bauh/gems/flatpak/__init__.py index 69e08aae..e6a7328b 100644 --- a/bauh/gems/flatpak/__init__.py +++ b/bauh/gems/flatpak/__init__.py @@ -2,6 +2,7 @@ import os from pathlib import Path from bauh.api.constants import CONFIG_PATH +from bauh.commons import resource ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) SUGGESTIONS_FILE = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/flatpak/suggestions.txt' @@ -9,3 +10,7 @@ CONFIG_FILE = '{}/flatpak.yml'.format(CONFIG_PATH) CONFIG_DIR = '{}/flatpak'.format(CONFIG_PATH) UPDATES_IGNORED_FILE = '{}/updates_ignored.txt'.format(CONFIG_DIR) EXPORTS_PATH = '{}/.local/share/flatpak/exports/share'.format(str(Path.home())) + + +def get_icon_path() -> str: + return resource.get_path('img/flatpak.svg', ROOT_DIR) diff --git a/bauh/gems/flatpak/config.py b/bauh/gems/flatpak/config.py index c6c9dcff..fbc2046a 100644 --- a/bauh/gems/flatpak/config.py +++ b/bauh/gems/flatpak/config.py @@ -1,7 +1,11 @@ -from bauh.commons.config import read_config as read +from bauh.commons.config import YAMLConfigManager from bauh.gems.flatpak import CONFIG_FILE -def read_config(update_file: bool = False) -> dict: - template = {'installation_level': None} - return read(CONFIG_FILE, template, update_file=update_file) +class FlatpakConfigManager(YAMLConfigManager): + + def __init__(self): + super(FlatpakConfigManager, self).__init__(config_file_path=CONFIG_FILE) + + def get_default_config(self) -> dict: + return {'installation_level': None} diff --git a/bauh/gems/flatpak/controller.py b/bauh/gems/flatpak/controller.py index de5e7d6a..16465450 100644 --- a/bauh/gems/flatpak/controller.py +++ b/bauh/gems/flatpak/controller.py @@ -16,11 +16,12 @@ from bauh.api.abstract.model import PackageHistory, PackageUpdate, SoftwarePacka from bauh.api.abstract.view import MessageType, FormComponent, SingleSelectComponent, InputOption, SelectViewType, \ ViewComponent, PanelComponent from bauh.commons import user -from bauh.commons.config import save_config +from bauh.commons.boot import CreateConfigFile from bauh.commons.html import strip_html, bold from bauh.commons.system import ProcessHandler -from bauh.gems.flatpak import flatpak, SUGGESTIONS_FILE, CONFIG_FILE, UPDATES_IGNORED_FILE, CONFIG_DIR, EXPORTS_PATH -from bauh.gems.flatpak.config import read_config +from bauh.gems.flatpak import flatpak, SUGGESTIONS_FILE, CONFIG_FILE, UPDATES_IGNORED_FILE, CONFIG_DIR, EXPORTS_PATH, \ + get_icon_path +from bauh.gems.flatpak.config import FlatpakConfigManager from bauh.gems.flatpak.constants import FLATHUB_API_URL from bauh.gems.flatpak.model import FlatpakApplication from bauh.gems.flatpak.worker import FlatpakAsyncDataLoader, FlatpakUpdateLoader @@ -41,6 +42,7 @@ class FlatpakManager(SoftwareManager): self.http_client = context.http_client self.suggestions_cache = context.cache_factory.new() self.logger = context.logger + self.configman = FlatpakConfigManager() def get_managed_types(self) -> Set["type"]: return {FlatpakApplication} @@ -337,9 +339,9 @@ class FlatpakManager(SoftwareManager): return True def install(self, pkg: FlatpakApplication, root_password: str, disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult: - config = read_config() + flatpak_config = self.configman.get_config() - install_level = config['installation_level'] + install_level = flatpak_config['installation_level'] if install_level is not None: self.logger.info("Default Flaptak installation level defined: {}".format(install_level)) @@ -453,7 +455,8 @@ class FlatpakManager(SoftwareManager): return action == SoftwareAction.DOWNGRADE and pkg.installation == 'system' def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): - Thread(target=read_config, args=(True,), daemon=True).start() + CreateConfigFile(taskman=task_manager, configman=self.configman, i18n=self.i18n, + task_icon_path=get_icon_path(), logger=self.logger).start() def list_updates(self, internet_available: bool) -> List[PackageUpdate]: updates = [] @@ -555,7 +558,7 @@ class FlatpakManager(SoftwareManager): def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: fields = [] - config = read_config() + flatpak_config = self.configman.get_config() install_opts = [InputOption(label=self.i18n['flatpak.config.install_level.system'].capitalize(), value='system', @@ -568,7 +571,7 @@ class FlatpakManager(SoftwareManager): tooltip=self.i18n['flatpak.config.install_level.ask.tip'].format(app=self.context.app_name))] fields.append(SingleSelectComponent(label=self.i18n['flatpak.config.install_level'], options=install_opts, - default_option=[o for o in install_opts if o.value == config['installation_level']][0], + default_option=[o for o in install_opts if o.value == flatpak_config['installation_level']][0], max_per_line=len(install_opts), max_width=floor(screen_width * 0.22), type_=SelectViewType.RADIO)) @@ -576,11 +579,11 @@ class FlatpakManager(SoftwareManager): return PanelComponent([FormComponent(fields, self.i18n['installation'].capitalize())]) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: - config = read_config() - config['installation_level'] = component.components[0].components[0].get_selected() + flatpak_config = self.configman.get_config() + flatpak_config['installation_level'] = component.components[0].components[0].get_selected() try: - save_config(config, CONFIG_FILE) + self.configman.save_config(flatpak_config) return True, None except: return False, [traceback.format_exc()] diff --git a/bauh/gems/snap/config.py b/bauh/gems/snap/config.py index 1dc7c398..5a44d04d 100644 --- a/bauh/gems/snap/config.py +++ b/bauh/gems/snap/config.py @@ -1,7 +1,11 @@ -from bauh.commons.config import read_config as read +from bauh.commons.config import YAMLConfigManager from bauh.gems.snap import CONFIG_FILE -def read_config(update_file: bool = False) -> dict: - template = {'install_channel': False, 'categories_exp': 24} - return read(CONFIG_FILE, template, update_file=update_file) +class SnapConfigManager(YAMLConfigManager): + + def __init__(self): + super(SnapConfigManager, self).__init__(config_file_path=CONFIG_FILE) + + def get_default_config(self) -> dict: + return {'install_channel': False, 'categories_exp': 24} diff --git a/bauh/gems/snap/controller.py b/bauh/gems/snap/controller.py index 241a4e64..db8dcb53 100644 --- a/bauh/gems/snap/controller.py +++ b/bauh/gems/snap/controller.py @@ -14,14 +14,14 @@ from bauh.api.abstract.view import SingleSelectComponent, SelectViewType, InputO FormComponent, TextInputComponent from bauh.api.exception import NoInternetException from bauh.commons import resource +from bauh.commons.boot import CreateConfigFile from bauh.commons.category import CategoriesDownloader -from bauh.commons.config import save_config from bauh.commons.html import bold from bauh.commons.system import SystemProcess, ProcessHandler, new_root_subprocess, get_human_size_str from bauh.commons.view_utils import new_select from bauh.gems.snap import snap, URL_CATEGORIES_FILE, CATEGORIES_FILE_PATH, SUGGESTIONS_FILE, \ - get_icon_path, snapd, CONFIG_FILE, ROOT_DIR -from bauh.gems.snap.config import read_config + get_icon_path, snapd, ROOT_DIR +from bauh.gems.snap.config import SnapConfigManager from bauh.gems.snap.model import SnapApplication from bauh.gems.snap.snapd import SnapdClient @@ -42,6 +42,7 @@ class SnapManager(SoftwareManager): self.categories = {} self.suggestions_cache = context.cache_factory.new() self.info_path = None + self.configman = SnapConfigManager() self.custom_actions = [ CustomSoftwareAction(i18n_status_key='snap.action.refresh.status', i18n_label_key='snap.action.refresh.label', @@ -187,7 +188,7 @@ class SnapManager(SoftwareManager): installed_names = {s['name'] for s in SnapdClient(self.logger).list_all_snaps()} client = SnapdClient(self.logger) - snap_config = read_config() + snap_config = self.configman.get_config() try: channel = self._request_channel_installation(pkg=pkg, snap_config=snap_config, snapd_client=client, watcher=watcher) @@ -283,27 +284,35 @@ class SnapManager(SoftwareManager): except: return False - def _start_category_task(self, task_man: TaskManager): - if task_man: - task_man.register_task('snap_cats', self.i18n['task.download_categories'], get_icon_path()) - task_man.update_progress('snap_cats', 50, None) + def _start_category_task(self, taskman: TaskManager, create_config: CreateConfigFile, downloader: CategoriesDownloader): + if taskman: + taskman.update_progress('snap_cats', 0, self.i18n['task.waiting_task'].format(bold(create_config.task_name))) + create_config.join() - def _finish_category_task(self, task_man: TaskManager): - if task_man: - task_man.update_progress('snap_cats', 100, None) - task_man.finish_task('snap_cats') + categories_exp = create_config.config['categories_exp'] + downloader.expiration = categories_exp if isinstance(categories_exp, int) else None + taskman.update_progress('snap_cats', 1, None) + + def _finish_category_task(self, taskman: TaskManager): + if taskman: + taskman.update_progress('snap_cats', 100, None) + taskman.finish_task('snap_cats') def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): - snap_config = read_config() + create_config = CreateConfigFile(taskman=task_manager, configman=self.configman, i18n=self.i18n, + task_icon_path=get_icon_path(), logger=self.logger) + create_config.start() - CategoriesDownloader(id_='snap', manager=self, http_client=self.http_client, logger=self.logger, - url_categories_file=URL_CATEGORIES_FILE, - categories_path=CATEGORIES_FILE_PATH, - expiration=snap_config['categories_exp'] if isinstance(snap_config['categories_exp'], int) else 0, - internet_connection=internet_available, - internet_checker=self.context.internet_checker, - before=lambda: self._start_category_task(task_manager), - after=lambda: self._finish_category_task(task_manager)).start() + task_manager.register_task('snap_cats', self.i18n['task.download_categories'], get_icon_path()) + category_downloader = CategoriesDownloader(id_='snap', manager=self, http_client=self.http_client, + logger=self.logger, + url_categories_file=URL_CATEGORIES_FILE, + categories_path=CATEGORIES_FILE_PATH, + internet_connection=internet_available, + internet_checker=self.context.internet_checker, + after=lambda: self._finish_category_task(task_manager)) + category_downloader.before = lambda: self._start_category_task(task_manager, create_config, category_downloader) + category_downloader.start() def list_updates(self, internet_available: bool) -> List[PackageUpdate]: pass @@ -436,7 +445,7 @@ class SnapManager(SoftwareManager): return pkg.screenshots if pkg.has_screenshots() else [] def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: - snap_config = read_config() + snap_config = self.configman.get_config() max_width = 200 install_channel = new_select(label=self.i18n['snap.config.install_channel'], @@ -457,14 +466,14 @@ class SnapManager(SoftwareManager): return PanelComponent([FormComponent([install_channel, categories_exp], self.i18n['installation'].capitalize())]) def save_settings(self, component: ViewComponent) -> Tuple[bool, Optional[List[str]]]: - config = read_config() + snap_config = self.configman.get_config() panel = component.components[0] - config['install_channel'] = panel.get_component('snap_install_channel').get_selected() - config['categories_exp'] = panel.get_component('snap_cat_exp').get_int_value() + snap_config['install_channel'] = panel.get_component('snap_install_channel').get_selected() + snap_config['categories_exp'] = panel.get_component('snap_cat_exp').get_int_value() try: - save_config(config, CONFIG_FILE) + self.configman.save_config(snap_config) return True, None except: return False, [traceback.format_exc()] diff --git a/bauh/gems/web/config.py b/bauh/gems/web/config.py index dde44cd6..82ace5d1 100644 --- a/bauh/gems/web/config.py +++ b/bauh/gems/web/config.py @@ -1,16 +1,17 @@ -from bauh.commons.config import read_config as read +from bauh.commons.config import YAMLConfigManager from bauh.gems.web import CONFIG_FILE -def read_config(update_file: bool = False) -> dict: - default_config = { - 'environment': { - 'system': False, - 'electron': {'version': None}, - 'cache_exp': 1440 +class WebConfigManager(YAMLConfigManager): + + def __init__(self): + super(WebConfigManager, self).__init__(config_file_path=CONFIG_FILE) + + def get_default_config(self) -> dict: + return { + 'environment': { + 'system': False, + 'electron': {'version': None}, + 'cache_exp': 1440 + } } - } - - return read(CONFIG_FILE, default_config, update_file) - - diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py index 484d2051..db2ff42a 100644 --- a/bauh/gems/web/controller.py +++ b/bauh/gems/web/controller.py @@ -28,15 +28,17 @@ from bauh.api.abstract.view import MessageType, MultipleSelectComponent, InputOp SelectViewType, TextInputComponent, FormComponent, FileChooserComponent, ViewComponent, PanelComponent from bauh.api.constants import DESKTOP_ENTRIES_DIR from bauh.commons import resource -from bauh.commons.config import save_config +from bauh.commons.boot import CreateConfigFile from bauh.commons.html import bold from bauh.commons.system import ProcessHandler, get_dir_size, get_human_size_str, SimpleProcess from bauh.gems.web import INSTALLED_PATH, nativefier, DESKTOP_ENTRY_PATH_PATTERN, URL_FIX_PATTERN, ENV_PATH, UA_CHROME, \ - SEARCH_INDEX_FILE, SUGGESTIONS_CACHE_FILE, ROOT_DIR, CONFIG_FILE, TEMP_PATH, FIXES_PATH, ELECTRON_PATH -from bauh.gems.web.config import read_config + SEARCH_INDEX_FILE, SUGGESTIONS_CACHE_FILE, ROOT_DIR, TEMP_PATH, FIXES_PATH, ELECTRON_PATH, \ + get_icon_path +from bauh.gems.web.config import WebConfigManager from bauh.gems.web.environment import EnvironmentUpdater, EnvironmentComponent from bauh.gems.web.model import WebApplication -from bauh.gems.web.worker import SuggestionsDownloader, SearchIndexGenerator +from bauh.gems.web.worker import SuggestionsManager, UpdateEnvironmentSettings, \ + SuggestionsLoader, SearchIndexGenerator try: from bs4 import BeautifulSoup, SoupStrainer @@ -58,7 +60,7 @@ RE_SYMBOLS_SPLIT = re.compile(r'[\-|_\s:.]') class WebApplicationManager(SoftwareManager): - def __init__(self, context: ApplicationContext, suggestions_downloader: Thread = None): + def __init__(self, context: ApplicationContext, suggestions_loader: Optional[SuggestionsLoader] = None): super(WebApplicationManager, self).__init__(context=context) self.http_client = context.http_client self.env_updater = EnvironmentUpdater(logger=context.logger, http_client=context.http_client, @@ -67,8 +69,9 @@ class WebApplicationManager(SoftwareManager): self.i18n = context.i18n self.logger = context.logger self.env_thread = None - self.suggestions_downloader = suggestions_downloader + self.suggestions_loader = suggestions_loader self.suggestions = {} + self.configman = WebConfigManager() self.custom_actions = [CustomSoftwareAction(i18n_label_key='web.custom_action.clean_env', i18n_status_key='web.custom_action.clean_env.status', manager=self, @@ -254,8 +257,8 @@ class WebApplicationManager(SoftwareManager): index = self._read_search_index() - if not index and self.suggestions_downloader and self.suggestions_downloader.is_alive(): - self.suggestions_downloader.join() + if not index and self.suggestions_loader and self.suggestions_loader.is_alive(): + self.suggestions_loader.join() index = self._read_search_index() if index: @@ -634,7 +637,7 @@ class WebApplicationManager(SoftwareManager): watcher.change_substatus(self.i18n['web.env.checking']) handler = ProcessHandler(watcher) - web_config = read_config() + web_config = self.configman.get_config() env_settings = self.env_updater.read_settings(web_config=web_config) if web_config['environment']['system'] and not nativefier.is_available(): @@ -796,7 +799,7 @@ class WebApplicationManager(SoftwareManager): def can_work(self) -> bool: if BS4_AVAILABLE and LXML_AVAILABLE: - config = read_config(update_file=True) + config = self.configman.get_config() use_system_env = config['environment']['system'] if not use_system_env: @@ -809,28 +812,37 @@ class WebApplicationManager(SoftwareManager): def requires_root(self, action: SoftwareAction, pkg: SoftwarePackage) -> bool: return False - def _download_suggestions(self, downloader: SuggestionsDownloader): - self.suggestions = downloader.download() - - if self.suggestions: - index_gen = SearchIndexGenerator(logger=self.logger) - Thread(target=index_gen.generate_index, args=(self.suggestions,), daemon=True).start() + def _assign_suggestions(self, suggestions: dict): + self.suggestions = suggestions def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): + create_config = CreateConfigFile(taskman=task_manager, configman=self.configman, i18n=self.i18n, + task_icon_path=get_icon_path(), logger=self.logger) + create_config.start() + if internet_available: - downloader = SuggestionsDownloader(logger=self.logger, http_client=self.http_client, - i18n=self.i18n, taskman=task_manager) - downloader.register_task() + UpdateEnvironmentSettings(env_updater=EnvironmentUpdater(logger=self.context.logger, + i18n=self.i18n, + http_client=self.http_client, + file_downloader=self.context.file_downloader, + taskman=task_manager), + taskman=task_manager, + create_config=create_config, + i18n=self.i18n).start() - self.suggestions_downloader = Thread(target=self._download_suggestions, args=(downloader,), daemon=True) - self.suggestions_downloader.start() + self.suggestions_loader = SuggestionsLoader(manager=SuggestionsManager(logger=self.logger, + http_client=self.http_client, + i18n=self.i18n), + logger=self.logger, + i18n=self.i18n, + taskman=task_manager, + suggestions_callback=self._assign_suggestions) + self.suggestions_loader.start() - web_config = read_config() - - if self.env_updater.should_download_settings(web_config): - self.env_updater.register_task_read_settings(taskman=task_manager) - self.env_thread = Thread(target=self.env_updater.read_settings, args=(web_config, False, task_manager), daemon=True) - self.env_thread.start() + SearchIndexGenerator(taskman=task_manager, + suggestions_loader=self.suggestions_loader, + i18n=self.i18n, + logger=self.logger).start() def list_updates(self, internet_available: bool) -> List[PackageUpdate]: pass @@ -893,7 +905,7 @@ class WebApplicationManager(SoftwareManager): return PackageSuggestion(priority=SuggestionPriority(suggestion['priority']), package=app) def _fill_config_async(self, output: dict): - output.update(read_config()) + output.update(self.configman.get_config()) def list_suggestions(self, limit: int, filter_installed: bool) -> List[PackageSuggestion]: web_config = {} @@ -903,14 +915,14 @@ class WebApplicationManager(SoftwareManager): if self.suggestions: suggestions = self.suggestions - elif self.suggestions_downloader: - self.suggestions_downloader.join(5) + elif self.suggestions_loader: + self.suggestions_loader.join(5) suggestions = self.suggestions else: - suggestions = SuggestionsDownloader(logger=self.logger, http_client=self.http_client, i18n=self.i18n).download() + suggestions = SuggestionsManager(logger=self.logger, http_client=self.http_client, i18n=self.i18n).download() # cleaning memory - self.suggestions_downloader = None + self.suggestions_loader = None self.suggestions = None if suggestions: @@ -981,7 +993,7 @@ class WebApplicationManager(SoftwareManager): traceback.print_exc() def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: - web_config = read_config() + web_config = self.configman.get_config() max_width = floor(screen_width * 0.15) input_electron = TextInputComponent(label=self.i18n['web.settings.electron.version.label'], @@ -1018,7 +1030,7 @@ class WebApplicationManager(SoftwareManager): return PanelComponent([form_env]) def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]: - web_config = read_config() + web_config = self.configman.get_config() form_env = component.components[0] @@ -1036,7 +1048,7 @@ class WebApplicationManager(SoftwareManager): web_config['environment']['cache_exp'] = form_env.get_component('web_cache_exp').get_int_value() try: - save_config(web_config, CONFIG_FILE) + self.configman.save_config(web_config) return True, None except: return False, [traceback.format_exc()] diff --git a/bauh/gems/web/environment.py b/bauh/gems/web/environment.py index e8d077fa..fd0303c1 100644 --- a/bauh/gems/web/environment.py +++ b/bauh/gems/web/environment.py @@ -41,12 +41,13 @@ class EnvironmentComponent: class EnvironmentUpdater: - def __init__(self, logger: logging.Logger, http_client: HttpClient, file_downloader: FileDownloader, i18n: I18n): + def __init__(self, logger: logging.Logger, http_client: HttpClient, file_downloader: FileDownloader, i18n: I18n, taskman: Optional[TaskManager] = None): self.logger = logger self.file_downloader = file_downloader self.i18n = i18n self.http_client = http_client self.task_read_settings_id = 'web_read_settings' + self.taskman = taskman def _download_and_install(self, version: str, version_url: str, watcher: ProcessWatcher) -> bool: self.logger.info("Downloading NodeJS {}: {}".format(version, version_url)) @@ -267,10 +268,10 @@ class EnvironmentUpdater: return res - def _finish_task_download_settings(self, task_man: TaskManager): - if task_man: - task_man.update_progress(self.task_read_settings_id, 100, None) - task_man.finish_task(self.task_read_settings_id) + def _finish_task_download_settings(self): + if self.taskman: + self.taskman.update_progress(self.task_read_settings_id, 100, None) + self.taskman.finish_task(self.task_read_settings_id) def should_download_settings(self, web_config: dict) -> bool: try: @@ -302,7 +303,14 @@ class EnvironmentUpdater: self.logger.error("Could not parse environment settings file timestamp: {}".format(env_ts_str)) return True - return env_timestamp + timedelta(minutes=settings_exp) <= datetime.utcnow() + expired = env_timestamp + timedelta(minutes=settings_exp) <= datetime.utcnow() + + if expired: + self.logger.info("Environment settings file has expired. It should be re-downloaded") + return True + else: + self.logger.info("Cached environment settings file is up to date") + return False def read_cached_settings(self, web_config: dict) -> Optional[dict]: if not self.should_download_settings(web_config): @@ -314,32 +322,33 @@ class EnvironmentUpdater: except yaml.YAMLError: self.logger.error('Could not parse the cache environment settings file: {}'.format(cached_settings_str)) - def register_task_read_settings(self, taskman: TaskManager): - taskman.register_task(self.task_read_settings_id, self.i18n['web.task.download_settings'], get_icon_path()) + def read_settings(self, web_config: dict, cache: bool = True) -> Optional[dict]: + if self.taskman: + self.taskman.register_task(self.task_read_settings_id, self.i18n['web.task.download_settings'], get_icon_path()) + self.taskman.update_progress(self.task_read_settings_id, 1, None) - def read_settings(self, web_config: dict, cache: bool = True, taskman: Optional[TaskManager] = None) -> Optional[dict]: cached_settings = self.read_cached_settings(web_config) if cache else None if cached_settings: return cached_settings try: - if taskman: - taskman.update_progress(self.task_read_settings_id, 10, None) + if self.taskman: + self.taskman.update_progress(self.task_read_settings_id, 10, None) self.logger.info("Downloading environment settings") res = self.http_client.get(URL_ENVIRONMENT_SETTINGS) if not res: self.logger.warning('Could not retrieve the environments settings from the cloud') - self._finish_task_download_settings(taskman) + self._finish_task_download_settings() return try: settings = yaml.safe_load(res.content) except yaml.YAMLError: self.logger.error('Could not parse environment settings: {}'.format(res.text)) - self._finish_task_download_settings(taskman) + self._finish_task_download_settings() return self.logger.info("Caching environment settings to disk") @@ -350,7 +359,7 @@ class EnvironmentUpdater: except OSError: self.logger.error("Could not create Web cache directory: {}".format(cache_dir)) self.logger.info('Finished') - self._finish_task_download_settings(taskman) + self._finish_task_download_settings() return cache_timestamp = datetime.utcnow().timestamp() @@ -360,12 +369,12 @@ class EnvironmentUpdater: with open(ENVIRONMENT_SETTINGS_TS_FILE, 'w+') as f: f.write(str(cache_timestamp)) - self._finish_task_download_settings(taskman) + self._finish_task_download_settings() self.logger.info("Finished") return settings except requests.exceptions.ConnectionError: - self._finish_task_download_settings(taskman) + self._finish_task_download_settings() return def _check_and_fill_electron(self, pkg: WebApplication, env: dict, local_config: dict, x86_x64: bool, widevine: bool, output: List[EnvironmentComponent]): diff --git a/bauh/gems/web/resources/locale/ca b/bauh/gems/web/resources/locale/ca index eb8fc2f2..0365d8be 100644 --- a/bauh/gems/web/resources/locale/ca +++ b/bauh/gems/web/resources/locale/ca @@ -73,7 +73,9 @@ web.settings.nativefier.system.tooltip=The Nativefier version installed on your web.settings.nativefier.system=system web.settings.nativefier.tip=Defines which Nativefier version should be used to generate the Web applications web.task.download_settings=Updating environment settings -web.task.suggestions=Downloading applications suggestions list +web.task.search_index=Indexing suggestions +web.task.suggestions=Downloading suggestions +web.task.suggestions.saving=Saving to disk web.uninstall.error.install_dir.not_found=The installation directory {} was not found web.uninstall.error.remove_dir=It was not possible to remove {} web.waiting.env_updater=Updating environment \ No newline at end of file diff --git a/bauh/gems/web/resources/locale/en b/bauh/gems/web/resources/locale/en index eb8fc2f2..0365d8be 100644 --- a/bauh/gems/web/resources/locale/en +++ b/bauh/gems/web/resources/locale/en @@ -73,7 +73,9 @@ web.settings.nativefier.system.tooltip=The Nativefier version installed on your web.settings.nativefier.system=system web.settings.nativefier.tip=Defines which Nativefier version should be used to generate the Web applications web.task.download_settings=Updating environment settings -web.task.suggestions=Downloading applications suggestions list +web.task.search_index=Indexing suggestions +web.task.suggestions=Downloading suggestions +web.task.suggestions.saving=Saving to disk web.uninstall.error.install_dir.not_found=The installation directory {} was not found web.uninstall.error.remove_dir=It was not possible to remove {} web.waiting.env_updater=Updating environment \ No newline at end of file diff --git a/bauh/gems/web/resources/locale/es b/bauh/gems/web/resources/locale/es index ee60e4b1..03e99768 100644 --- a/bauh/gems/web/resources/locale/es +++ b/bauh/gems/web/resources/locale/es @@ -73,7 +73,9 @@ web.settings.nativefier.system.tooltip=Se utilizará la versión de Nativefier i web.settings.nativefier.system=sistema web.settings.nativefier.tip=Define qué versión de Nativefier debe usarse para generar las aplicaciones Web web.task.download_settings=Actualizando configuraciones de ambiente -web.task.suggestions=Descargando la lista de sugerencias de aplicaciones +web.task.search_index=Indexando sugerencias +web.task.suggestions=Descargando sugerencias +web.task.suggestions.saving=Guardando en disco web.uninstall.error.install_dir.not_found=No se encontró el directorio de instalación {} web.uninstall.error.remove_dir=No fue posible eliminar {} web.waiting.env_updater=Actualizando el ambiente \ No newline at end of file diff --git a/bauh/gems/web/resources/locale/fr b/bauh/gems/web/resources/locale/fr index f9b225a0..5e0ea4d3 100644 --- a/bauh/gems/web/resources/locale/fr +++ b/bauh/gems/web/resources/locale/fr @@ -73,7 +73,9 @@ web.settings.nativefier.system.tooltip=La version de Nativefier installée sur v web.settings.nativefier.system=système web.settings.nativefier.tip=Definit quelle version de Nativefier doit être utilisée pour géner les applications Web web.task.download_settings=Mise à jour des paramètres d'environnement -web.task.suggestions=Téléchargement de la liste de suggestions d'applications +web.task.search_index=Indexing suggestions +web.task.suggestions=Téléchargement de suggestions +web.task.suggestions.saving=Saving to disk web.uninstall.error.install_dir.not_found=Impossible de trouver le dossier d'installation {} web.uninstall.error.remove_dir=Impossible d'enlever {} web.waiting.env_updater=Mise à jour de l'environnement diff --git a/bauh/gems/web/resources/locale/it b/bauh/gems/web/resources/locale/it index eb8fc2f2..0365d8be 100644 --- a/bauh/gems/web/resources/locale/it +++ b/bauh/gems/web/resources/locale/it @@ -73,7 +73,9 @@ web.settings.nativefier.system.tooltip=The Nativefier version installed on your web.settings.nativefier.system=system web.settings.nativefier.tip=Defines which Nativefier version should be used to generate the Web applications web.task.download_settings=Updating environment settings -web.task.suggestions=Downloading applications suggestions list +web.task.search_index=Indexing suggestions +web.task.suggestions=Downloading suggestions +web.task.suggestions.saving=Saving to disk web.uninstall.error.install_dir.not_found=The installation directory {} was not found web.uninstall.error.remove_dir=It was not possible to remove {} web.waiting.env_updater=Updating environment \ No newline at end of file diff --git a/bauh/gems/web/resources/locale/pt b/bauh/gems/web/resources/locale/pt index 5c5c5ae6..3310e350 100644 --- a/bauh/gems/web/resources/locale/pt +++ b/bauh/gems/web/resources/locale/pt @@ -72,7 +72,9 @@ web.settings.nativefier.system.tooltip=A versão do Nativefier instalada no seu web.settings.nativefier.system=sistema web.settings.nativefier.tip=Define qual versão do Nativefier será utilizada para gerar os aplicativos Web web.task.download_settings=Atualizando configurações de ambiente -web.task.suggestions=Baixando lista de sugestões de aplicativos +web.task.search_index=Indexando sugestões +web.task.suggestions=Baixando sugestões +web.task.suggestions.saving=Salvando em disco web.uninstall.error.install_dir.not_found=O diretório de instalação {} não foi encontrado web.uninstall.error.remove=Não foi possível remover {} web.waiting.env_updater=Atualizando ambiente diff --git a/bauh/gems/web/resources/locale/ru b/bauh/gems/web/resources/locale/ru index 94fcd970..d14ec477 100644 --- a/bauh/gems/web/resources/locale/ru +++ b/bauh/gems/web/resources/locale/ru @@ -73,7 +73,9 @@ web.settings.nativefier.system=Из системы web.settings.nativefier.system.tooltip=Будет использоваться версия Nativefier установленная в Вашей системе web.settings.nativefier.tip=Определяет, какие Nativefier версии должны быдут использованы для установки веб-приложений web.task.download_settings=Updating environment settings -web.task.suggestions=Downloading applications suggestions list +web.task.search_index=Indexing suggestions +web.task.suggestions=Downloading suggestions +web.task.suggestions.saving=Saving to disk web.uninstall.error.install_dir.not_found=Каталог установки {} не найден web.uninstall.error.remove_dir=Не удалось удалить {} web.waiting.env_updater=Обнавляется Веб-среда \ No newline at end of file diff --git a/bauh/gems/web/resources/locale/tr b/bauh/gems/web/resources/locale/tr index 60e3eafb..62a4d4ad 100644 --- a/bauh/gems/web/resources/locale/tr +++ b/bauh/gems/web/resources/locale/tr @@ -73,7 +73,9 @@ web.settings.nativefier.system.tooltip=Sisteminizde yüklü olan Nativefier sür web.settings.nativefier.system=sistem web.settings.nativefier.tip=Web uygulamalarını oluşturmak için hangi Nativefier sürümünün kullanılması gerektiğini tanımlar web.task.download_settings=Ortam ayarları güncelleniyor -web.task.suggestions=Uygulama öneri listesi indiriliyor +web.task.search_index=Indexing suggestions +web.task.suggestions=Downloading suggestions +web.task.suggestions.saving=Saving to disk web.uninstall.error.install_dir.not_found={} kurulum dizini bulunamadı web.uninstall.error.remove_dir={} kaldırmak mümkün olmadı web.waiting.env_updater=Ortam güncelleniyor diff --git a/bauh/gems/web/suggestions.py b/bauh/gems/web/suggestions.py new file mode 100644 index 00000000..ecd0bf7f --- /dev/null +++ b/bauh/gems/web/suggestions.py @@ -0,0 +1,59 @@ +import os +import traceback +from logging import Logger +from pathlib import Path + +import requests +import yaml + +from bauh.api.http import HttpClient +from bauh.gems.web import URL_SUGGESTIONS, SUGGESTIONS_CACHE_FILE +from bauh.view.util.translation import I18n + + +class SuggestionsManager: + + def __init__(self, http_client: HttpClient, logger: Logger, i18n: I18n): + self.http_client = http_client + self.logger = logger + self.i18n = i18n + + def download(self) -> dict: + self.logger.info("Reading suggestions from {}".format(URL_SUGGESTIONS)) + try: + suggestions = self.http_client.get_yaml(URL_SUGGESTIONS, session=False) + + if suggestions: + self.logger.info("{} suggestions successfully read".format(len(suggestions))) + else: + self.logger.warning("Could not read suggestions from {}".format(URL_SUGGESTIONS)) + + except (requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout): + self.logger.warning("Internet seems to be off: it was not possible to retrieve the suggestions") + suggestions = {} + + self.logger.info("Finished") + return suggestions + + def save_to_disk(self, suggestions: dict): + if not suggestions: + return + + self.logger.info('Caching {} suggestions to the disk'.format(len(suggestions))) + suggestions_file_dir = os.path.dirname(SUGGESTIONS_CACHE_FILE) + try: + Path(suggestions_file_dir).mkdir(parents=True, exist_ok=True) + except OSError: + self.logger.error("Could not generate the directory {}".format(suggestions_file_dir)) + traceback.print_exc() + return + + try: + with open(SUGGESTIONS_CACHE_FILE, 'w+') as f: + f.write(yaml.safe_dump(suggestions)) + + self.logger.info("{} suggestions successfully cached to file '{}'".format(len(suggestions), SUGGESTIONS_CACHE_FILE)) + except: + self.logger.error("Could write to {}".format(SUGGESTIONS_CACHE_FILE)) + traceback.print_exc() + return diff --git a/bauh/gems/web/worker.py b/bauh/gems/web/worker.py index 3d3628fd..a96c9cea 100644 --- a/bauh/gems/web/worker.py +++ b/bauh/gems/web/worker.py @@ -1,87 +1,85 @@ import logging +import time import traceback -from pathlib import Path +from threading import Thread +from typing import Optional -import requests import yaml from bauh.api.abstract.handler import TaskManager -from bauh.api.http import HttpClient -from bauh.gems.web import URL_SUGGESTIONS, TEMP_PATH, SEARCH_INDEX_FILE, SUGGESTIONS_CACHE_FILE, get_icon_path +from bauh.commons.boot import CreateConfigFile +from bauh.commons.html import bold +from bauh.gems.web import SEARCH_INDEX_FILE, get_icon_path +from bauh.gems.web.environment import EnvironmentUpdater +from bauh.gems.web.suggestions import SuggestionsManager from bauh.view.util.translation import I18n -class SuggestionsDownloader: +class SuggestionsLoader(Thread): - def __init__(self, http_client: HttpClient, logger: logging.Logger, i18n: I18n, taskman: TaskManager = None): - super(SuggestionsDownloader, self).__init__() - self.http_client = http_client - self.logger = logger + def __init__(self, taskman: TaskManager, manager: SuggestionsManager, + i18n: I18n, logger: logging.Logger, suggestions_callback, suggestions: Optional[dict] = None): + super(SuggestionsLoader, self).__init__(daemon=True) self.taskman = taskman - self.i18n = i18n self.task_id = 'web_sugs' - self._task_registered = False - self._task_finished = False + self.manager = manager + self.suggestions_callback = suggestions_callback + self.i18n = i18n + self.logger = logger + self.suggestions = suggestions + self.task_name = self.i18n['web.task.suggestions'] + self.taskman.register_task(self.task_id, self.task_name, get_icon_path()) - def _finish_task(self): - if self.taskman: + def run(self): + ti = time.time() + self.taskman.update_progress(self.task_id, 10, None) + try: + self.suggestions = self.manager.download() + + if self.suggestions_callback: + self.suggestions_callback(self.suggestions) + + if self.suggestions: + self.taskman.update_progress(self.task_id, 50, self.i18n['web.task.suggestions.saving']) + self.manager.save_to_disk(self.suggestions) + + except: + self.logger.error("Unexpected exception") + traceback.print_exc() + finally: self.taskman.update_progress(self.task_id, 100, None) self.taskman.finish_task(self.task_id) - self._task_finished = True - self.logger.info("Finished") - - def register_task(self): - if self.taskman and not self._task_registered and not self._task_finished: - self.taskman.register_task(self.task_id, self.i18n['web.task.suggestions'], get_icon_path()) - self._task_registered = True - - def download(self) -> dict: - if self.taskman: - self.taskman.update_progress(self.task_id, 10, None) - - self.logger.info("Reading suggestions from {}".format(URL_SUGGESTIONS)) - try: - suggestions = self.http_client.get_yaml(URL_SUGGESTIONS, session=False) - - if suggestions: - self.logger.info("{} suggestions successfully read".format(len(suggestions))) - else: - self.logger.warning("Could not read suggestions from {}".format(URL_SUGGESTIONS)) - - except (requests.exceptions.ConnectionError, requests.exceptions.ConnectTimeout): - self.logger.warning("Internet seems to be off: it was not possible to retrieve the suggestions") - self._finish_task() - return {} - - self._finish_task() - return suggestions + tf = time.time() + self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti)) -class SearchIndexGenerator: +class SearchIndexGenerator(Thread): - def __init__(self, logger: logging.Logger): + def __init__(self, taskman: TaskManager, suggestions_loader: SuggestionsLoader, i18n: I18n, logger: logging.Logger): + super(SearchIndexGenerator, self).__init__(daemon=True) + self.taskman = taskman + self.i18n = i18n self.logger = logger + self.suggestions_loader = suggestions_loader + self.task_id = 'web_idx_gen' + self.taskman.register_task(self.task_id, self.i18n['web.task.search_index'], get_icon_path()) + + def run(self): + ti = time.time() + self.taskman.update_progress(self.task_id, 0, self.i18n['task.waiting_task'].format(bold(self.suggestions_loader.task_name))) + self.suggestions_loader.join() + + if self.suggestions_loader.suggestions: + self.generate_index(self.suggestions_loader.suggestions) + else: + self.taskman.update_progress(self.task_id, 100, None) + self.taskman.finish_task(self.task_id) + + tf = time.time() + self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti)) def generate_index(self, suggestions: dict): - self.logger.info('Caching {} suggestions to the disk'.format(len(suggestions))) - - try: - Path(TEMP_PATH).mkdir(parents=True, exist_ok=True) - except: - self.logger.error("Could not generate the directory {}".format(TEMP_PATH)) - traceback.print_exc() - return - - try: - with open(SUGGESTIONS_CACHE_FILE, 'w+') as f: - f.write(yaml.safe_dump(suggestions)) - - self.logger.info('{} successfully cached to the disk as {}'.format(len(suggestions), SUGGESTIONS_CACHE_FILE)) - except: - self.logger.error("Could write to {}".format(SUGGESTIONS_CACHE_FILE)) - traceback.print_exc() - return - + self.taskman.update_progress(self.task_id, 1, None) self.logger.info('Indexing suggestions') index = {} @@ -102,6 +100,7 @@ class SearchIndexGenerator: mapped.add(key) if index: + self.taskman.update_progress(self.task_id, 50, self.i18n['web.task.suggestions.saving']) self.logger.info('Preparing search index for writing') for key in index.keys(): @@ -115,3 +114,29 @@ class SearchIndexGenerator: except: self.logger.error("Could not write the seach index to {}".format(SEARCH_INDEX_FILE)) traceback.print_exc() + + self.taskman.update_progress(self.task_id, 100, None) + self.taskman.finish_task(self.task_id) + + +class UpdateEnvironmentSettings(Thread): + + def __init__(self, env_updater: EnvironmentUpdater, taskman: TaskManager, i18n: I18n, create_config: CreateConfigFile): + super(UpdateEnvironmentSettings, self).__init__(daemon=True) + self.env_updater = env_updater + self.taskman = taskman + self.create_config = create_config + self.task_id = env_updater.task_read_settings_id + self.i18n = i18n + + def run(self): + self.taskman.register_task(self.task_id, self.i18n['web.task.download_settings'], get_icon_path()) + self.taskman.update_progress(self.task_id, 1, self.i18n['task.waiting_task'].format(bold(self.create_config.task_name))) + self.create_config.join() + + web_config = self.create_config.config + if self.env_updater.should_download_settings(web_config): + self.env_updater.read_settings(web_config=web_config, cache=False) + else: + self.taskman.update_progress(self.task_id, 100, None) + self.taskman.finish_task(self.task_id) diff --git a/bauh/view/core/config.py b/bauh/view/core/config.py index ce6fe43f..9737bc5c 100644 --- a/bauh/view/core/config.py +++ b/bauh/view/core/config.py @@ -1,80 +1,73 @@ from pathlib import Path -import yaml - from bauh import __app_name__ -from bauh.commons.config import read_config as read +from bauh.commons.config import YAMLConfigManager -CONFIG_PATH = '{}/.config/{}'.format(Path.home(), __app_name__) -FILE_PATH = '{}/config.yml'.format(CONFIG_PATH) +FILE_PATH = '{}/.config/{}/config.yml'.format(str(Path.home()), __app_name__) -def read_config(update_file: bool = False) -> dict: - default = { - 'gems': None, - 'memory_cache': { - 'data_expiration': 60 * 60, - 'icon_expiration': 60 * 5 - }, - 'locale': None, - 'updates': { - 'check_interval': 30, - 'ask_for_reboot': True - }, - 'system': { - 'notifications': True, - 'single_dependency_checking': False - }, - 'suggestions': { - 'enabled': True, - 'by_type': 10 - }, - 'ui': { - 'table': { - 'max_displayed': 50 +class CoreConfigManager(YAMLConfigManager): + + def __init__(self): + super(CoreConfigManager, self).__init__(config_file_path=FILE_PATH) + + def get_default_config(self) -> dict: + return { + 'gems': None, + 'memory_cache': { + 'data_expiration': 60 * 60, + 'icon_expiration': 60 * 5 }, - 'tray': { - 'default_icon': None, - 'updates_icon': None + 'locale': None, + 'updates': { + 'check_interval': 30, + 'ask_for_reboot': True }, - 'qt_style': 'fusion', - 'hdpi': True, - "auto_scale": False, - "scale_factor": 1.0, - 'theme': 'light', - 'system_theme': False + 'system': { + 'notifications': True, + 'single_dependency_checking': False + }, + 'suggestions': { + 'enabled': True, + 'by_type': 10 + }, + 'ui': { + 'table': { + 'max_displayed': 50 + }, + 'tray': { + 'default_icon': None, + 'updates_icon': None + }, + 'qt_style': 'fusion', + 'hdpi': True, + "auto_scale": False, + "scale_factor": 1.0, + 'theme': 'light', + 'system_theme': False - }, - 'download': { - 'multithreaded': True, - 'multithreaded_client': None, - 'icons': True - }, - 'store_root_password': True, - 'disk': { - 'trim': { - 'after_upgrade': False + }, + 'download': { + 'multithreaded': True, + 'multithreaded_client': None, + 'icons': True + }, + 'store_root_password': True, + 'disk': { + 'trim': { + 'after_upgrade': False + } + }, + 'backup': { + 'enabled': True, + 'install': None, + 'uninstall': None, + 'downgrade': None, + 'upgrade': None, + 'mode': 'incremental', + 'type': 'rsync' + }, + 'boot': { + 'load_apps': True } - }, - 'backup': { - 'enabled': True, - 'install': None, - 'uninstall': None, - 'downgrade': None, - 'upgrade': None, - 'mode': 'incremental', - 'type': 'rsync' - }, - 'boot': { - 'load_apps': True } - - } - return read(FILE_PATH, default, update_file=update_file, update_async=True) - - -def save(config: dict): - Path(CONFIG_PATH).mkdir(parents=True, exist_ok=True) - - with open(FILE_PATH, 'w+') as f: - f.write(yaml.safe_dump(config)) diff --git a/bauh/view/core/controller.py b/bauh/view/core/controller.py index 21f5eb70..baca76d9 100755 --- a/bauh/view/core/controller.py +++ b/bauh/view/core/controller.py @@ -14,12 +14,14 @@ from bauh.api.abstract.model import SoftwarePackage, PackageUpdate, PackageHisto CustomSoftwareAction from bauh.api.abstract.view import ViewComponent, TabGroupComponent, MessageType from bauh.api.exception import NoInternetException +from bauh.commons.boot import CreateConfigFile from bauh.commons.html import bold from bauh.commons.system import run_cmd -from bauh.view.core.config import read_config +from bauh.view.core.config import CoreConfigManager from bauh.view.core.settings import GenericSettingsManager from bauh.view.core.update import check_for_update from bauh.view.util import resource +from bauh.view.util.resource import get_path from bauh.view.util.util import clean_app_files, restart_app RE_IS_URL = re.compile(r'^https?://.+') @@ -52,6 +54,7 @@ class GenericSoftwareManager(SoftwareManager): self.config = config self.settings_manager = settings_manager self.http_client = context.http_client + self.configman = CoreConfigManager() self.extra_actions = [CustomSoftwareAction(i18n_label_key='action.reset', i18n_status_key='action.reset.status', manager_method='reset', @@ -402,14 +405,25 @@ class GenericSoftwareManager(SoftwareManager): def prepare(self, task_manager: TaskManager, root_password: str, internet_available: bool): ti = time.time() self.logger.info("Initializing") + taskman = task_manager if task_manager else TaskManager() # empty task manager to prevent null pointers + + create_config = CreateConfigFile(taskman=taskman, configman=self.configman, i18n=self.i18n, + task_icon_path=get_path('img/logo.svg'), logger=self.logger) + create_config.start() + if self.managers: internet_on = self.context.is_internet_available() - taskman = task_manager if task_manager else TaskManager() # empty task manager to prevent null pointers + prepare_tasks = [] for man in self.managers: if man not in self._already_prepared and self._can_work(man): - man.prepare(taskman, root_password, internet_on) + t = Thread(target=man.prepare, args=(taskman, root_password, internet_on), daemon=True) + t.start() + prepare_tasks.append(t) self._already_prepared.append(man) + for t in prepare_tasks: + t.join() + tf = time.time() self.logger.info("Finished. Took {0:.2f} seconds".format(tf - ti)) @@ -525,7 +539,8 @@ class GenericSoftwareManager(SoftwareManager): working_managers=self.working_managers, logger=self.logger, i18n=self.i18n, - file_downloader=self.context.file_downloader) + file_downloader=self.context.file_downloader, + configman=self.configman) else: self.settings_manager.managers = self.managers self.settings_manager.working_managers = self.working_managers @@ -618,7 +633,7 @@ class GenericSoftwareManager(SoftwareManager): if man_actions: actions.extend(man_actions) - app_config = read_config() + app_config = self.configman.get_config() for action, available in self.dynamic_extra_actions.items(): if available(app_config): diff --git a/bauh/view/core/settings.py b/bauh/view/core/settings.py index 99a196fd..253da8d0 100644 --- a/bauh/view/core/settings.py +++ b/bauh/view/core/settings.py @@ -13,8 +13,8 @@ from bauh.api.abstract.view import ViewComponent, TabComponent, InputOption, Tex PanelComponent, FormComponent, TabGroupComponent, SingleSelectComponent, SelectViewType, TextInputComponent, \ FileChooserComponent, RangeInputComponent from bauh.commons.view_utils import new_select -from bauh.view.core import config, timeshift -from bauh.view.core.config import read_config +from bauh.view.core import timeshift +from bauh.view.core.config import CoreConfigManager from bauh.view.core.downloader import AdaptableFileDownloader from bauh.view.util import translation from bauh.view.util.translation import I18n @@ -23,12 +23,13 @@ from bauh.view.util.translation import I18n class GenericSettingsManager: def __init__(self, managers: List[SoftwareManager], working_managers: List[SoftwareManager], - logger: logging.Logger, i18n: I18n, file_downloader: FileDownloader): + logger: logging.Logger, i18n: I18n, file_downloader: FileDownloader, configman: CoreConfigManager): self.i18n = i18n self.managers = managers self.working_managers = working_managers self.logger = logger self.file_downloader = file_downloader + self.configman = configman def get_settings(self, screen_width: int, screen_height: int) -> ViewComponent: tabs = list() @@ -54,7 +55,7 @@ class GenericSettingsManager: if man.is_enabled() and man in self.working_managers: def_gem_opts.add(opt) - core_config = read_config() + core_config = self.configman.get_config() if gem_opts: type_help = TextComponent(html=self.i18n['core.config.types.tip']) @@ -340,7 +341,7 @@ class GenericSettingsManager: ui: PanelComponent, tray: PanelComponent, gems_panel: PanelComponent) -> Tuple[bool, Optional[List[str]]]: - core_config = config.read_config() + core_config = self.configman.get_config() # general general_form = general.components[0] @@ -443,7 +444,7 @@ class GenericSettingsManager: core_config['gems'] = None if core_config['gems'] is None and len(checked_gems) == len(self.managers) else checked_gems try: - config.save(core_config) + self.configman.save_config(core_config) return True, None except: return False, [traceback.format_exc()] diff --git a/bauh/view/qt/prepare.py b/bauh/view/qt/prepare.py index 9df7d1fa..65a7f930 100644 --- a/bauh/view/qt/prepare.py +++ b/bauh/view/qt/prepare.py @@ -1,6 +1,8 @@ import datetime import operator +import time from functools import reduce +from threading import Lock from typing import Tuple, Optional from PyQt5.QtCore import QSize, Qt, QThread, pyqtSignal, QCoreApplication @@ -34,7 +36,10 @@ class Prepare(QThread, TaskManager): self.context = context self.waiting_password = False self.password_response = None - self._registered = 0 + self._tasks_added = set() + self._tasks_finished = set() + self._add_lock = Lock() + self._finish_lock = Lock() def ask_password(self) -> Tuple[bool, Optional[str]]: self.waiting_password = True @@ -58,20 +63,42 @@ class Prepare(QThread, TaskManager): QCoreApplication.exit(1) self.manager.prepare(self, root_pwd, None) - self.signal_started.emit(self._registered) + self._add_lock.acquire() + self.signal_started.emit(len(self._tasks_added)) + self._add_lock.release() def update_progress(self, task_id: str, progress: float, substatus: str): - self.signal_update.emit(task_id, progress, substatus) + if task_id in self._tasks_added: + self.signal_update.emit(task_id, progress, substatus) def update_output(self, task_id: str, output: str): - self.signal_output.emit(task_id, output) + if task_id in self._tasks_added: + self.signal_output.emit(task_id, output) def register_task(self, id_: str, label: str, icon_path: str): - self._registered += 1 - self.signal_register.emit(id_, label, icon_path) + self._add_lock.acquire() + + if id_ not in self._tasks_added: + self._tasks_added.add(id_) + self.signal_register.emit(id_, label, icon_path) + + self._add_lock.release() def finish_task(self, task_id: str): - self.signal_finished.emit(task_id) + self._add_lock.acquire() + task_registered = task_id in self._tasks_added + self._add_lock.release() + + if not task_registered: + return + + self._finish_lock.acquire() + + if task_id not in self._tasks_finished: + self._tasks_finished.add(task_id) + self.signal_finished.emit(task_id) + + self._finish_lock.release() class CheckFinished(QThread): @@ -84,7 +111,7 @@ class CheckFinished(QThread): def run(self): while True: - if self.total == self.finished: + if self.finished == self.total: break self.msleep(5) @@ -134,6 +161,7 @@ class PreparePanel(QWidget, TaskManager): self.output = {} self.added_tasks = 0 self.ftasks = 0 + self.started_at = None self.self_close = False self.prepare_thread = Prepare(self.context, manager, self.i18n) @@ -263,6 +291,7 @@ class PreparePanel(QWidget, TaskManager): centralize(self) def start(self, tasks: int): + self.started_at = time.time() self.check_thread.total = tasks self.check_thread.start() self.skip_thread.start() @@ -387,9 +416,8 @@ class PreparePanel(QWidget, TaskManager): def finish_task(self, task_id: str): task = self.tasks[task_id] - task['lb_sub'].setText('') - for key in ('lb_prog', 'lb_status'): + for key in ('lb_prog', 'lb_status', 'lb_sub'): label = task[key] label.setProperty('status', 'done') label.style().unpolish(label) @@ -402,10 +430,9 @@ class PreparePanel(QWidget, TaskManager): self.ftasks += 1 self.signal_status.emit(self.ftasks) - if self.table.rowCount() == self.ftasks: - self.label_top.setText(self.i18n['ready'].capitalize()) - def finish(self): + now = time.time() + self.context.logger.info("{0} tasks finished in {1:.9f} seconds".format(self.ftasks, (now - self.started_at))) if self.isVisible(): self.manage_window.show() diff --git a/bauh/view/qt/root.py b/bauh/view/qt/root.py index 7c260da0..fd541d32 100644 --- a/bauh/view/qt/root.py +++ b/bauh/view/qt/root.py @@ -9,7 +9,7 @@ from PyQt5.QtWidgets import QLineEdit, QApplication, QDialog, QPushButton, QVBox from bauh.api.abstract.context import ApplicationContext from bauh.commons.system import new_subprocess -from bauh.view.core.config import read_config +from bauh.view.core.config import CoreConfigManager from bauh.view.qt.components import QtComponentsManager, new_spacer from bauh.view.util import util from bauh.view.util.translation import I18n @@ -131,7 +131,7 @@ class RootDialog(QDialog): def ask_password(context: ApplicationContext, i18n: I18n, app_config: Optional[dict] = None, comp_manager: Optional[QtComponentsManager] = None, tries: int = 3) -> Tuple[bool, Optional[str]]: - current_config = read_config() if not app_config else app_config + current_config = CoreConfigManager().get_config() if not app_config else app_config store_password = bool(current_config['store_root_password']) diff --git a/bauh/view/qt/thread.py b/bauh/view/qt/thread.py index b2c75af5..e386138b 100644 --- a/bauh/view/qt/thread.py +++ b/bauh/view/qt/thread.py @@ -23,8 +23,8 @@ from bauh.api.exception import NoInternetException from bauh.commons import user from bauh.commons.html import bold from bauh.commons.system import get_human_size_str, ProcessHandler, SimpleProcess -from bauh.view.core import timeshift, config -from bauh.view.core.config import read_config +from bauh.view.core import timeshift +from bauh.view.core.config import CoreConfigManager from bauh.view.qt import commons from bauh.view.qt.view_model import PackageView, PackageViewStatus from bauh.view.util.translation import I18n @@ -466,7 +466,7 @@ class UpgradeSelected(AsyncAction): self.change_substatus('') - app_config = read_config() + app_config = CoreConfigManager().get_config() # backup dialog ( if enabled, supported and accepted ) should_backup = bkp_supported @@ -572,7 +572,7 @@ class UninstallPackage(AsyncAction): pkg=self.pkg, i18n=self.i18n, root_password=self.root_pwd, - app_config=read_config()) + app_config=CoreConfigManager().get_config()) if not proceed: self.notify_finished({'success': False, 'removed': None, 'pkg': self.pkg}) self.pkg = None @@ -615,7 +615,7 @@ class DowngradePackage(AsyncAction): pkg=self.pkg, i18n=self.i18n, root_password=self.root_pwd, - app_config=read_config()) + app_config=CoreConfigManager().get_config()) if not proceed: self.notify_finished({'app': self.pkg, 'success': success}) @@ -707,7 +707,7 @@ class InstallPackage(AsyncAction): pkg=self.pkg, i18n=self.i18n, root_password=self.root_pwd, - app_config=read_config()) + app_config=CoreConfigManager().get_config()) if not proceed: self.signal_finished.emit(res) @@ -1007,7 +1007,7 @@ class CustomAction(AsyncAction): res = {'success': False, 'pkg': self.pkg, 'action': self.custom_action, 'error': None, 'error_type': MessageType.ERROR} if self.custom_action.backup: - proceed, _ = self.request_backup(app_config=read_config(), + proceed, _ = self.request_backup(app_config=CoreConfigManager.get_config(), action_key=None, i18n=self.i18n, root_password=self.root_pwd, @@ -1081,10 +1081,11 @@ class SaveTheme(QThread): def run(self): if self.theme_key: - core_config = read_config() + configman = CoreConfigManager() + core_config = configman.get_config() core_config['ui']['theme'] = self.theme_key try: - config.save(core_config) + configman.save_config(core_config) except: traceback.print_exc() diff --git a/bauh/view/qt/window.py b/bauh/view/qt/window.py index bbad74e2..d40d5a89 100755 --- a/bauh/view/qt/window.py +++ b/bauh/view/qt/window.py @@ -22,7 +22,7 @@ from bauh.commons import user from bauh.commons.html import bold from bauh.context import set_theme from bauh.stylesheet import read_all_themes_metadata, ThemeMetadata -from bauh.view.core.config import read_config +from bauh.view.core.config import CoreConfigManager from bauh.view.core.tray_client import notify_tray from bauh.view.qt import dialog, commons, qt_utils from bauh.view.qt.about import AboutDialog @@ -1586,7 +1586,7 @@ class ManageWindow(QWidget): menu_row.exec_() def _map_theme_actions(self, menu: QMenu) -> List[QCustomMenuAction]: - core_config = read_config() + core_config = CoreConfigManager().get_config() current_theme_key, current_action = core_config['ui']['theme'], None diff --git a/bauh/view/resources/locale/ca b/bauh/view/resources/locale/ca index 31ae55ba..95c5812c 100644 --- a/bauh/view/resources/locale/ca +++ b/bauh/view/resources/locale/ca @@ -391,6 +391,8 @@ status.caching_data=S’estan emmagatzemant {} dades a la memòria cau al disc style=estil success=success summary=resum +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Download de categories task.waiting_task=Waiting for {} type=tipus diff --git a/bauh/view/resources/locale/de b/bauh/view/resources/locale/de index 440d4e17..8b555c43 100644 --- a/bauh/view/resources/locale/de +++ b/bauh/view/resources/locale/de @@ -391,6 +391,8 @@ status.caching_data={} Daten auf der Festplatte zwischenspeichern style=Stil success=success summary=Zusammenfassung +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Downloading categories task.waiting_task=Waiting for {} type=Typ diff --git a/bauh/view/resources/locale/en b/bauh/view/resources/locale/en index e6593837..acd65ae9 100644 --- a/bauh/view/resources/locale/en +++ b/bauh/view/resources/locale/en @@ -392,6 +392,8 @@ status.caching_data=Caching {} data to disk style=style success=success summary=summary +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Downloading categories task.waiting_task=Waiting for {} type=type diff --git a/bauh/view/resources/locale/es b/bauh/view/resources/locale/es index 4928aa86..72d12dd0 100644 --- a/bauh/view/resources/locale/es +++ b/bauh/view/resources/locale/es @@ -394,6 +394,8 @@ status.caching_data=Almacenando en antememoria los datos de {} para el disco style=estilo success=éxito summary=resumen +task.checking_config=Verificando archivo de configuración +task.checking_config.saving=Creando archivo task.download_categories=Descargando categorías task.waiting_task=Esperando por {} type=tipo diff --git a/bauh/view/resources/locale/fr b/bauh/view/resources/locale/fr index 6ec77d62..30e3bd23 100644 --- a/bauh/view/resources/locale/fr +++ b/bauh/view/resources/locale/fr @@ -388,6 +388,8 @@ status.caching_data=Mise en cache des données de {} sur disque style=style success=succès summary=résumé +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Téléchargement des catégories task.waiting_task=Waiting for {} type=type diff --git a/bauh/view/resources/locale/it b/bauh/view/resources/locale/it index f5ad626e..2e9e5d6c 100644 --- a/bauh/view/resources/locale/it +++ b/bauh/view/resources/locale/it @@ -394,6 +394,8 @@ status.caching_data=Memorizza {} dati sul disco style=stile success=success summary=riepilogo +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Download delle categorie task.waiting_task=Waiting for {} type=tipe diff --git a/bauh/view/resources/locale/pt b/bauh/view/resources/locale/pt index 82b80356..58e7b48f 100644 --- a/bauh/view/resources/locale/pt +++ b/bauh/view/resources/locale/pt @@ -392,6 +392,8 @@ status.caching_data=Cacheando dados de {} para o disco style=estilo success=sucesso summary=resumo +task.checking_config=Verificando arquivo de configuração +task.checking_config.saving=Criando arquivo task.download_categories=Baixando categorias task.waiting_task=Aguardando {} type=tipo diff --git a/bauh/view/resources/locale/ru b/bauh/view/resources/locale/ru index 93c97421..3777c7ac 100644 --- a/bauh/view/resources/locale/ru +++ b/bauh/view/resources/locale/ru @@ -391,6 +391,8 @@ status.caching_data=Кэширование данных {} на диск style=Стиль success=success summary=Суммарно +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Downloading categories task.waiting_task=Waiting for {} type=Тип diff --git a/bauh/view/resources/locale/tr b/bauh/view/resources/locale/tr index cf27b801..84db32dc 100644 --- a/bauh/view/resources/locale/tr +++ b/bauh/view/resources/locale/tr @@ -391,6 +391,8 @@ status.caching_data={} verilerini diske önbellekle style=Görünüm biçimi success=başarılı summary=özet +task.checking_config=Checking configuration file +task.checking_config.saving=Creating file task.download_categories=Kategoriler indiriliyor task.waiting_task=Waiting for {} type=tür diff --git a/bauh/view/resources/style/darcula/darcula.qss b/bauh/view/resources/style/darcula/darcula.qss index 84e96707..c662c20f 100644 --- a/bauh/view/resources/style/darcula/darcula.qss +++ b/bauh/view/resources/style/darcula/darcula.qss @@ -238,7 +238,7 @@ PreparePanel QTableWidget#tasks { background-color: @inner_widget.background.color; } -PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] { +PreparePanel QLabel#task_status[status = "done"], PreparePanel QLabel#task_progress[status = "done"], PreparePanel QLabel#task_substatus[status = "done"] { color: @task.done.color; } diff --git a/bauh/view/resources/style/default/default.qss b/bauh/view/resources/style/default/default.qss index 2ca2a7b6..e00e2cfd 100644 --- a/bauh/view/resources/style/default/default.qss +++ b/bauh/view/resources/style/default/default.qss @@ -219,7 +219,7 @@ PreparePanel QPushButton#bt_hide_details { background: none; } -PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] { +PreparePanel QLabel#task_status[status = "done"], PreparePanel QLabel#task_progress[status = "done"], PreparePanel QLabel#task_substatus[status = "done"] { text-decoration: line-through; } diff --git a/bauh/view/resources/style/light/light.qss b/bauh/view/resources/style/light/light.qss index d1e28538..b0db2bac 100644 --- a/bauh/view/resources/style/light/light.qss +++ b/bauh/view/resources/style/light/light.qss @@ -145,11 +145,11 @@ PreparePanel QTableWidget#tasks { background-color: @task.background.color; } -PreparePanel QLabel#task_status[status = "running"], QLabel#task_progress[status = "running"] { +PreparePanel QLabel#task_status[status = "running"], PreparePanel QLabel#task_progress[status = "running"] { font-weight: 525; } -PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] { +PreparePanel QLabel#task_status[status = "done"], PreparePanel QLabel#task_progress[status = "done"], PreparePanel QLabel#task_substatus[status = "done"] { color: @task.done.color; } diff --git a/bauh/view/resources/style/sublime/sublime.qss b/bauh/view/resources/style/sublime/sublime.qss index c67ab5f6..019bdb56 100644 --- a/bauh/view/resources/style/sublime/sublime.qss +++ b/bauh/view/resources/style/sublime/sublime.qss @@ -195,7 +195,7 @@ PreparePanel QLabel#task_status[status = "running"], QLabel#task_progress[status color: @task.running.color; } -PreparePanel QLabel#task_status[status = "done"], QLabel#task_progress[status = "done"] { +PreparePanel QLabel#task_status[status = "done"], PreparePanel QLabel#task_progress[status = "done"], PreparePanel QLabel#task_substatus[status = "done"]{ color: @task.done.color; }