diff --git a/npbackup/common.py b/npbackup/common.py index a8931c2..fd1041a 100644 --- a/npbackup/common.py +++ b/npbackup/common.py @@ -1,3 +1,17 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# +# This file is part of npbackup + +__intname__ = "npbackup.common" +__author__ = "Orsiris de Jong" +__site__ = "https://www.netperfect.fr/npbackup" +__description__ = "NetPerfect Backup Client" +__copyright__ = "Copyright (C) 2023 NetInvent" +__license__ = "GPL-3.0-only" +__build__ = "2023121801" + + from datetime import datetime from logging import getLogger import ofunctions.logger_utils @@ -6,9 +20,6 @@ import ofunctions.logger_utils logger = getLogger() -EXIT_CODE = 0 - - def execution_logs(start_time: datetime) -> None: """ Try to know if logger.warning or worse has been called @@ -23,7 +34,6 @@ def execution_logs(start_time: datetime) -> None: So using logger = getLogger("anotherinstance") will create a separate instance from the one we can inspect Makes sense ;) """ - global EXIT_CODE end_time = datetime.utcnow() @@ -33,7 +43,6 @@ def execution_logs(start_time: datetime) -> None: logger_worst_level = flt.worst_level log_level_reached = "success" - EXIT_CODE = logger_worst_level try: if logger_worst_level >= 40: log_level_reached = "errors" @@ -46,4 +55,5 @@ def execution_logs(start_time: datetime) -> None: end_time - start_time, log_level_reached ) ) - # using sys.exit(code) in a atexit function will swallow the exitcode and render 0 \ No newline at end of file + # using sys.exit(code) in a atexit function will swallow the exitcode and render 0 + # Using sys.exit(logger.get_worst_logger_level()) is the way to go, when using ofunctions.logger_utils >= 2.4.1 \ No newline at end of file diff --git a/npbackup/core/runner.py b/npbackup/core/runner.py index 1bd1049..40cb388 100644 --- a/npbackup/core/runner.py +++ b/npbackup/core/runner.py @@ -652,7 +652,8 @@ class NPBackupRunner: return result def group_runner( - self, operations_config: dict, result_queue: Optional[queue.Queue] + self, repo_list: list, operation: str, result_queue: Optional[queue.Queue] ) -> bool: - print(operations_config) + for repo in repo_list: + print(f"Running {operation} for repo {repo}") print("run to the hills") diff --git a/npbackup/gui/__main__.py b/npbackup/gui/__main__.py index 0cf2c08..ce4cfb5 100644 --- a/npbackup/gui/__main__.py +++ b/npbackup/gui/__main__.py @@ -14,7 +14,7 @@ from typing import List, Optional, Tuple import sys import os from pathlib import Path -from logging import getLogger +import ofunctions.logger_utils from datetime import datetime import dateutil import queue @@ -57,10 +57,15 @@ from npbackup.customization import ( OEM_ICON, ) + + +LOG_FILE = os.path.join(CURRENT_DIR, "{}.log".format(__intname__)) +logger = ofunctions.logger_utils.logger_get_logger(LOG_FILE) + + sg.theme(PYSIMPLEGUI_THEME) sg.SetOptions(icon=OEM_ICON) -logger = getLogger() # Let's use mutable to get a cheap way of transfering data from thread to main program # There are no possible race conditions since we don't modifiy the data from anywhere outside the thread @@ -199,21 +204,21 @@ def _gui_update_state( window, current_state: bool, backup_tz: Optional[datetime], snapshot_list: List[str] ) -> None: if current_state: - window["state-button"].Update( + window["--STATE-BUTTON--"].Update( "{}: {}".format(_t("generic.up_to_date"), backup_tz), button_color=GUI_STATE_OK_BUTTON, ) elif current_state is False and backup_tz == datetime(1, 1, 1, 0, 0): - window["state-button"].Update( + window["--STATE-BUTTON--"].Update( _t("generic.no_snapshots"), button_color=GUI_STATE_OLD_BUTTON ) elif current_state is False: - window["state-button"].Update( + window["--STATE-BUTTON--"].Update( "{}: {}".format(_t("generic.too_old"), backup_tz.replace(microsecond=0)), button_color=GUI_STATE_OLD_BUTTON, ) elif current_state is None: - window["state-button"].Update( + window["--STATE-BUTTON--"].Update( _t("generic.not_connected_yet"), button_color=GUI_STATE_UNKNOWN_BUTTON ) @@ -619,7 +624,7 @@ def _main_gui(): [ sg.Button( _t("generic.unknown"), - key="state-button", + key="--STATE-BUTTON--", button_color=("white", "grey"), ) ], @@ -650,13 +655,13 @@ def _main_gui(): ) ], [ - sg.Button(_t("main_gui.launch_backup"), key="launch-backup"), - sg.Button(_t("main_gui.see_content"), key="see-content"), - sg.Button(_t("generic.forget"), key="forget"), - sg.Button(_t("main_gui.operations"), key="operations"), - sg.Button(_t("generic.configure"), key="configure"), - sg.Button(_t("generic.about"), key="about"), - sg.Button(_t("generic.quit"), key="-EXIT-"), + sg.Button(_t("main_gui.launch_backup"), key="--LAUNCH-BACKUP--"), + sg.Button(_t("main_gui.see_content"), key="--SEE-CONTENT--"), + sg.Button(_t("generic.forget"), key="--FORGET--"), + sg.Button(_t("main_gui.operations"), key="--OPERATIONS--"), + sg.Button(_t("generic.configure"), key="--CONFIGURE--"), + sg.Button(_t("generic.about"), key="--ABOUT--"), + sg.Button(_t("generic.quit"), key="--EXIT--"), ], ], element_justification="C", @@ -694,7 +699,7 @@ def _main_gui(): while True: event, values = window.read(timeout=60000) - if event in (sg.WIN_X_EVENT, sg.WIN_CLOSED, "-EXIT-"): + if event in (sg.WIN_X_EVENT, sg.WIN_CLOSED, "--EXIT--"): break if event == "-active_repo-": active_repo = values["-active_repo-"] @@ -708,7 +713,7 @@ def _main_gui(): else: sg.PopupError("Repo not existent in config") continue - if event == "launch-backup": + if event == "--LAUNCH-BACKUP--": progress_windows_layout = [ [ sg.Multiline( @@ -762,17 +767,16 @@ def _main_gui(): ) progress_window.close() continue - if event == "see-content": + if event == "--SEE-CONTENT--": if not values["snapshot-list"]: sg.Popup(_t("main_gui.select_backup"), keep_on_top=True) continue - print(values["snapshot-list"]) if len(values["snapshot-list"]) > 1: sg.Popup(_t("main_gui.select_only_one_snapshot")) continue snapshot_to_see = snapshot_list[values["snapshot-list"][0]][0] ls_window(repo_config, snapshot_to_see) - if event == "forget": + if event == "--FORGET--": if not values["snapshot-list"]: sg.Popup(_t("main_gui.select_backup"), keep_on_top=True) continue @@ -781,14 +785,14 @@ def _main_gui(): snapshots_to_forget.append(snapshot_list[row][0]) forget_snapshot(repo_config, snapshots_to_forget) # Make sure we trigger a GUI refresh after forgetting snapshots - event = "state-button" - if event == "operations": - full_config = operations_gui(full_config, config_file) - event = "state-button" - if event == "configure": + event = "--STATE-BUTTON--" + if event == "--OPERATIONS--": + full_config = operations_gui(full_config) + event = "--STATE-BUTTON--" + if event == "--CONFIGURE--": full_config = config_gui(full_config, config_file) # Make sure we trigger a GUI refresh when configuration is changed - event = "state-button" + event = "--STATE-BUTTON--" if event == _t("generic.destination"): try: if backend_type: @@ -799,9 +803,9 @@ def _main_gui(): sg.PopupNoFrame(destination_string) except (TypeError, KeyError): sg.PopupNoFrame(_t("main_gui.unknown_repo")) - if event == "about": + if event == "--ABOUT--": _about_gui(version_string, full_config) - if event == "state-button": + if event == "--STATE-BUTTON--": current_state, backup_tz, snapshot_list = get_gui_data(repo_config) _gui_update_state(window, current_state, backup_tz, snapshot_list) if current_state is None: @@ -815,7 +819,7 @@ def main_gui(): ) try: _main_gui() - sys.exit(npbackup.common.EXIT_CODE) + sys.exit(logger.get_worst_logger_level()) except _tkinter.TclError as exc: logger.critical(f'Tkinter error: "{exc}". Is this a headless server ?') sys.exit(250) diff --git a/npbackup/gui/operations.py b/npbackup/gui/operations.py index a6a254c..388123e 100644 --- a/npbackup/gui/operations.py +++ b/npbackup/gui/operations.py @@ -38,34 +38,28 @@ from npbackup.customization import ( logger = getLogger(__intname__) -def add_repo(config_dict: dict) -> dict: - pass - - -def gui_update_state(window, config_dict: dict) -> list: +def gui_update_state(window, full_config: dict) -> list: repo_list = [] try: - for repo_name in config_dict["repos"]: - if ( - config_dict["repos"][repo_name]["repository"] - and config_dict["repos"][repo_name]["password"] + for repo_name in full_config.g("repos"): + repo_config, _ = configuration.get_repo_config(full_config, repo_name) + if repo_config.g(f"repo_uri") and ( + repo_config.g(f"repo_opts.repo_password") + or repo_config.g(f"repo_opts.repo_password_command") ): backend_type, repo_uri = get_anon_repo_uri( - config_dict["repos"][repo_name]["repository"] + repo_config.g(f"repo_uri") ) repo_list.append([backend_type, repo_uri]) else: logger.warning("Incomplete operations repo {}".format(repo_name)) except KeyError: logger.info("No operations repos configured") - if config_dict["repo"]["repository"] and config_dict["repo"]["password"]: - backend_type, repo_uri = get_anon_repo_uri(config_dict["repo"]["repository"]) - repo_list.append("[{}] {}".format(backend_type, repo_uri)) window["repo-list"].update(repo_list) return repo_list -def operations_gui(config_dict: dict, config_file: str) -> dict: +def operations_gui(full_config: dict) -> dict: """ Operate on one or multiple repositories """ @@ -102,13 +96,8 @@ def operations_gui(config_dict: dict, config_file: str) -> dict: ) ], [ - sg.Button(_t("operations_gui.add_repo"), key="add-repo"), - sg.Button(_t("operations_gui.edit_repo"), key="edit-repo"), - sg.Button(_t("operations_gui.remove_repo"), key="remove-repo"), - ], - [ - sg.Button(_t("operations_gui.quick_check"), key="quick-check"), - sg.Button(_t("operations_gui.full_check"), key="full-check"), + sg.Button(_t("operations_gui.quick_check"), key="--QUICK-CHECK--"), + sg.Button(_t("operations_gui.full_check"), key="--FULL-CHECK--"), ], [ sg.Button( @@ -118,11 +107,11 @@ def operations_gui(config_dict: dict, config_file: str) -> dict: ], [ sg.Button( - _t("operations_gui.standard_prune"), key="standard-prune" + _t("operations_gui.standard_prune"), key="--STANDARD-PRUNE--" ), - sg.Button(_t("operations_gui.max_prune"), key="max-prune"), + sg.Button(_t("operations_gui.max_prune"), key="--MAX-PRUNE--"), ], - [sg.Button(_t("generic.quit"), key="exit")], + [sg.Button(_t("generic.quit"), key="--EXIT--")], ], element_justification="C", ) @@ -144,7 +133,7 @@ def operations_gui(config_dict: dict, config_file: str) -> dict: finalize=True, ) - full_repo_list = gui_update_state(window, config_dict) + complete_repo_list = gui_update_state(window, full_config) # Auto reisze table to window size window["repo-list"].expand(True, True) @@ -152,34 +141,14 @@ def operations_gui(config_dict: dict, config_file: str) -> dict: while True: event, values = window.read(timeout=60000) - if event in (sg.WIN_CLOSED, "exit"): + if event in (sg.WIN_CLOSED, sg.WIN_X_EVENT, '--EXIT--'): break - if event == "add-repo": - pass - if event in ["add-repo", "remove-repo"]: - if not values["repo-list"]: - sg.Popup(_t("main_gui.select_backup"), keep_on_top=True) - continue - if event == "add-repo": - config_dict = add_repo(config_dict) - # Save to config here #TODO #WIP - event == "state-update" - elif event == "remove-repo": - result = sg.popup( - _t("generic.are_you_sure"), - custom_text=(_t("generic.yes"), _t("generic.no")), - ) - if result == _t("generic.yes"): - # Save to config here #TODO #WIP - event == "state-update" - if event == "forget": - pass if event in [ - "forget", - "quick-check", - "full-check", - "standard-prune", - "max-prune", + "--FORGET--", + "--QUICK-CHECK--", + "--FULL-CHECK--", + "--STANDARD-PRUNE--", + "--MAX-PRUNE--", ]: if not values["repo-list"]: result = sg.popup( @@ -188,14 +157,29 @@ def operations_gui(config_dict: dict, config_file: str) -> dict: ) if not result == _t("generic.yes"): continue - repos = full_repo_list + repos = complete_repo_list else: repos = values["repo-list"] + result_queue = queue.Queue() runner = NPBackupRunner() - runner.group_runner(repos, result_queue) - if event == "state-update": - full_repo_list = gui_update_state(window, config_dict) + print(repos) + group_runner_repo_list = [repo_name for backend_type, repo_name in repos] + + if event == '--FORGET--': + operation = 'forget' + if event == '--QUICK-CHECK--': + operation = 'quick_check' + if event == '--FULL-CHECK--': + operation = 'full_check' + if event == '--STANDARD-PRUNE--': + operation = 'standard_prune' + if event == '--MAX-PRUNE--': + operation = 'max_prune' + runner.group_runner(group_runner_repo_list, operation, result_queue) + event = '---STATE-UPDATE---' + if event == "---STATE-UPDATE---": + complete_repo_list = gui_update_state(window, full_config) window.close() - return config_dict + return full_config diff --git a/npbackup/translations/main_gui.en.yml b/npbackup/translations/main_gui.en.yml index 60f7b46..3983617 100644 --- a/npbackup/translations/main_gui.en.yml +++ b/npbackup/translations/main_gui.en.yml @@ -14,7 +14,7 @@ en: only_include: Only include destination_folder: Destination folder backup_state: Backup state - backup_list_to: List of backups to + backup_list_to: List of backups to repo local_folder: Local folder external_server: external server launch_backup: Launch backup diff --git a/npbackup/translations/main_gui.fr.yml b/npbackup/translations/main_gui.fr.yml index f1ca8fe..4e1025a 100644 --- a/npbackup/translations/main_gui.fr.yml +++ b/npbackup/translations/main_gui.fr.yml @@ -14,7 +14,7 @@ fr: only_include: Inclure seulement destination_folder: Dossier de destination backup_state: Etat de sauvegarde - backup_list_to: Liste des sauvegardes vers + backup_list_to: Liste des sauvegardes vers le dépot local_folder: Dossier local external_server: serveur externalisé launch_backup: Sauvegarder