From fbfc6a7224483ed07eb1cdbdc761e86163f11d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A9sir=C3=A9=20Werner=20Menrath?= Date: Mon, 1 Sep 2025 02:02:15 +0200 Subject: [PATCH] Refactor: Update various modules and add deletion functionality This commit includes updates across several modules, including: - backup_manager.py: Enhancements related to backup deletion and regex for backup naming. - core/data_processing.py: Adjustments to UI state handling. - pbp_app_config.py: Addition of new UI messages. - pyimage_ui/actions.py: Refinements in UI actions. - pyimage_ui/system_backup_content_frame.py: Integration of new deletion logic. - pyimage_ui/user_backup_content_frame.py: Minor adjustments. These changes collectively improve backup management, UI responsiveness, and prepare for new deletion features. --- backup_manager.py | 36 ++++++++++++++++--- core/data_processing.py | 4 +++ pbp_app_config.py | 3 +- .../01-September-2025_000000_system_full.txt | 0 pyimage_ui/actions.py | 1 - pyimage_ui/system_backup_content_frame.py | 26 +++++++++----- pyimage_ui/user_backup_content_frame.py | 5 +-- 7 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 pybackup/01-September-2025_000000_system_full.txt diff --git a/backup_manager.py b/backup_manager.py index 64ad4c7..a39f9ad 100644 --- a/backup_manager.py +++ b/backup_manager.py @@ -112,6 +112,33 @@ class BackupManager: else: self.logger.log(f"Failed to delete path: {path}") + def start_delete_system_backup(self, path: str, queue): + """Starts a threaded system backup deletion.""" + thread = threading.Thread(target=self._run_delete, args=(path, queue)) + thread.daemon = True + thread.start() + + def _run_delete(self, path: str, queue): + """Runs the deletion and puts a message on the queue when done.""" + try: + info_file = f"{path}.txt" + # Build a script to remove both the folder and the info file in one go. + # Use -f to avoid errors if the info file doesn't exist. + script_content = f""" +rm -rf '{path}' +rm -f '{info_file}' +""" + if self._execute_as_root(script_content): + self.logger.log(f"Successfully deleted {path} and {info_file}") + queue.put(('deletion_complete', True)) + else: + self.logger.log(f"Failed to delete {path}") + queue.put(('deletion_complete', False)) + + except Exception as e: + self.logger.log(f"Error during threaded deletion: {e}") + queue.put(('deletion_complete', False)) + def cancel_backup(self): if self.process and self.process.poll() is None: # Check if process is still running self.logger.log("Attempting to cancel backup...") @@ -528,9 +555,9 @@ set -e if not os.path.isdir(pybackup_path): return system_backups - # Regex to parse folder names like '6-März-2024_system_full' or '6-März-2024_system_full.tar.gz' + # Regex to parse folder names like '6-März-2024_143000_system_full' or '6-März-2024_143000_system_full.tar.gz' name_regex = re.compile( - r"^(\d{1,2}-\w+-\d{4})_system_(full|incremental)(\.tar\.gz)?$", re.IGNORECASE) + r"^(\d{1,2}-\w+-\d{4})_(\d{6})_system_(full|incremental)(\.tar\.gz)?$", re.IGNORECASE) for item in os.listdir(pybackup_path): # Skip info files @@ -543,8 +570,9 @@ set -e full_path = os.path.join(pybackup_path, item) date_str = match.group(1) - backup_type_base = match.group(2).capitalize() - is_compressed = match.group(3) is not None + # time_str = match.group(2) # Not currently used in UI, but available + backup_type_base = match.group(3).capitalize() + is_compressed = match.group(4) is not None backup_type = backup_type_base if is_compressed: diff --git a/core/data_processing.py b/core/data_processing.py index d1ae75d..437a59e 100644 --- a/core/data_processing.py +++ b/core/data_processing.py @@ -242,6 +242,10 @@ class DataProcessing: self.app.drawing.update_target_projection() + # --- Enable Start Button Logic --- + if self.app.mode == 'backup' and self.app.destination_path: + self.app.start_pause_button.config(state="normal") + # --- Handle Accurate Calculation Completion --- if calc_type == 'accurate_incremental': self.app.source_size_bytes = folder_size # Update the source size diff --git a/pbp_app_config.py b/pbp_app_config.py index eddd048..9c4321e 100644 --- a/pbp_app_config.py +++ b/pbp_app_config.py @@ -312,6 +312,7 @@ class Msg: "final_warning_system_restore_title": _("FINAL WARNING"), "final_warning_system_restore_msg": _("ATTENTION: You are about to restore the system. This process cannot be safely interrupted. All changes since the backup will be lost. \n\nThe computer will automatically restart upon completion. \n\nREALLY PROCEED?"), "btn_continue": _("PROCEED"), + "deleting_backup_in_progress": _("Deletion in progress... Please wait."), "select_restore_source_title": _("Select Restore Source"), "select_restore_destination_title": _("Select Restore Destination"), @@ -337,4 +338,4 @@ class Msg: "force_compression": _("Always compress backup"), "force_encryption": _("Always encrypt backup"), "encryption_note_system_backup": _("Note: For system backups, encryption only applies to files directly within the /home directory. Folders are not automatically encrypted unless explicitly included in the backup."), - } + } \ No newline at end of file diff --git a/pybackup/01-September-2025_000000_system_full.txt b/pybackup/01-September-2025_000000_system_full.txt new file mode 100644 index 0000000..e69de29 diff --git a/pyimage_ui/actions.py b/pyimage_ui/actions.py index a0cfb50..fb88d3d 100644 --- a/pyimage_ui/actions.py +++ b/pyimage_ui/actions.py @@ -376,7 +376,6 @@ class Actions: "backup_destination_path", path) self.app.drawing.redraw_right_canvas() self.app.drawing.update_target_projection() - self.app.start_pause_button.config(state="normal") current_source = self.app.left_canvas_data.get('folder') if current_source: diff --git a/pyimage_ui/system_backup_content_frame.py b/pyimage_ui/system_backup_content_frame.py index 7db84d5..12cd7fe 100644 --- a/pyimage_ui/system_backup_content_frame.py +++ b/pyimage_ui/system_backup_content_frame.py @@ -7,9 +7,10 @@ from pyimage_ui.comment_editor_dialog import CommentEditorDialog class SystemBackupContentFrame(ttk.Frame): - def __init__(self, master, backup_manager, **kwargs): + def __init__(self, master, backup_manager, actions, **kwargs): super().__init__(master, **kwargs) self.backup_manager = backup_manager + self.actions = actions self.system_backups_list = [] self.backup_path = None @@ -121,29 +122,30 @@ class SystemBackupContentFrame(ttk.Frame): selected_item = self.content_tree.focus() if not selected_item: return - + item_values = self.content_tree.item(selected_item)["values"] - folder_name = item_values[4] # 5th column is folder_name + folder_name = item_values[4] # 5th column is folder_name selected_backup = None for backup in self.system_backups_list: if backup.get("folder_name") == folder_name: selected_backup = backup break - + if not selected_backup: print(f"Error: Could not find backup info for {folder_name}") return # We need to get the restore destination from the main app - # This is a bit tricky as this frame is isolated. + # This is a bit tricky as this frame is isolated. # We assume the main app has a way to provide this. # Let's get it from the config manager, which should be accessible via the backup_manager's app instance. try: # Accessing the app instance through the master hierarchy main_app = self.winfo_toplevel() - restore_dest_path = main_app.config_manager.get_setting("restore_destination_path", "/") - + restore_dest_path = main_app.config_manager.get_setting( + "restore_destination_path", "/") + if not restore_dest_path: print("Error: Restore destination not set.") # Optionally, show a message box to the user @@ -169,5 +171,11 @@ class SystemBackupContentFrame(ttk.Frame): pybackup_path = os.path.join(self.backup_path, "pybackup") folder_to_delete = os.path.join(pybackup_path, folder_name) - self.backup_manager.delete_privileged_path(folder_to_delete) - self._load_backup_content() + # Lock UI and show status + self.actions._set_ui_state(False) # Lock UI + self.master.show_deletion_status( + Msg.STR["deleting_backup_in_progress"]) + + # Start deletion in background + self.backup_manager.start_delete_system_backup( + folder_to_delete, self.winfo_toplevel().queue) diff --git a/pyimage_ui/user_backup_content_frame.py b/pyimage_ui/user_backup_content_frame.py index d63d1ee..bc821c9 100644 --- a/pyimage_ui/user_backup_content_frame.py +++ b/pyimage_ui/user_backup_content_frame.py @@ -7,11 +7,12 @@ from pyimage_ui.comment_editor_dialog import CommentEditorDialog class UserBackupContentFrame(ttk.Frame): - def __init__(self, master, backup_manager, **kwargs): + def __init__(self, master, backup_manager, actions, **kwargs): super().__init__(master, **kwargs) - self.backup_manager = backup_manager + self.backup_manager = backup_manager self.backup_path = None + self.actions = actions # Store actions object # --- Backup Content List View --- self.content_frame = ttk.LabelFrame(