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.
This commit is contained in:
2025-09-01 02:02:15 +02:00
parent 7c765019ff
commit fbfc6a7224
7 changed files with 58 additions and 17 deletions

View File

@@ -112,6 +112,33 @@ class BackupManager:
else: else:
self.logger.log(f"Failed to delete path: {path}") 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): def cancel_backup(self):
if self.process and self.process.poll() is None: # Check if process is still running if self.process and self.process.poll() is None: # Check if process is still running
self.logger.log("Attempting to cancel backup...") self.logger.log("Attempting to cancel backup...")
@@ -528,9 +555,9 @@ set -e
if not os.path.isdir(pybackup_path): if not os.path.isdir(pybackup_path):
return system_backups 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( 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): for item in os.listdir(pybackup_path):
# Skip info files # Skip info files
@@ -543,8 +570,9 @@ set -e
full_path = os.path.join(pybackup_path, item) full_path = os.path.join(pybackup_path, item)
date_str = match.group(1) date_str = match.group(1)
backup_type_base = match.group(2).capitalize() # time_str = match.group(2) # Not currently used in UI, but available
is_compressed = match.group(3) is not None backup_type_base = match.group(3).capitalize()
is_compressed = match.group(4) is not None
backup_type = backup_type_base backup_type = backup_type_base
if is_compressed: if is_compressed:

View File

@@ -242,6 +242,10 @@ class DataProcessing:
self.app.drawing.update_target_projection() 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 --- # --- Handle Accurate Calculation Completion ---
if calc_type == 'accurate_incremental': if calc_type == 'accurate_incremental':
self.app.source_size_bytes = folder_size # Update the source size self.app.source_size_bytes = folder_size # Update the source size

View File

@@ -312,6 +312,7 @@ class Msg:
"final_warning_system_restore_title": _("FINAL WARNING"), "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?"), "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"), "btn_continue": _("PROCEED"),
"deleting_backup_in_progress": _("Deletion in progress... Please wait."),
"select_restore_source_title": _("Select Restore Source"), "select_restore_source_title": _("Select Restore Source"),
"select_restore_destination_title": _("Select Restore Destination"), "select_restore_destination_title": _("Select Restore Destination"),
@@ -337,4 +338,4 @@ class Msg:
"force_compression": _("Always compress backup"), "force_compression": _("Always compress backup"),
"force_encryption": _("Always encrypt 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."), "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."),
} }

View File

@@ -376,7 +376,6 @@ class Actions:
"backup_destination_path", path) "backup_destination_path", path)
self.app.drawing.redraw_right_canvas() self.app.drawing.redraw_right_canvas()
self.app.drawing.update_target_projection() self.app.drawing.update_target_projection()
self.app.start_pause_button.config(state="normal")
current_source = self.app.left_canvas_data.get('folder') current_source = self.app.left_canvas_data.get('folder')
if current_source: if current_source:

View File

@@ -7,9 +7,10 @@ from pyimage_ui.comment_editor_dialog import CommentEditorDialog
class SystemBackupContentFrame(ttk.Frame): class SystemBackupContentFrame(ttk.Frame):
def __init__(self, master, backup_manager, **kwargs): def __init__(self, master, backup_manager, actions, **kwargs):
super().__init__(master, **kwargs) super().__init__(master, **kwargs)
self.backup_manager = backup_manager self.backup_manager = backup_manager
self.actions = actions
self.system_backups_list = [] self.system_backups_list = []
self.backup_path = None self.backup_path = None
@@ -121,29 +122,30 @@ class SystemBackupContentFrame(ttk.Frame):
selected_item = self.content_tree.focus() selected_item = self.content_tree.focus()
if not selected_item: if not selected_item:
return return
item_values = self.content_tree.item(selected_item)["values"] 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 selected_backup = None
for backup in self.system_backups_list: for backup in self.system_backups_list:
if backup.get("folder_name") == folder_name: if backup.get("folder_name") == folder_name:
selected_backup = backup selected_backup = backup
break break
if not selected_backup: if not selected_backup:
print(f"Error: Could not find backup info for {folder_name}") print(f"Error: Could not find backup info for {folder_name}")
return return
# We need to get the restore destination from the main app # 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. # 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. # Let's get it from the config manager, which should be accessible via the backup_manager's app instance.
try: try:
# Accessing the app instance through the master hierarchy # Accessing the app instance through the master hierarchy
main_app = self.winfo_toplevel() 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: if not restore_dest_path:
print("Error: Restore destination not set.") print("Error: Restore destination not set.")
# Optionally, show a message box to the user # 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") pybackup_path = os.path.join(self.backup_path, "pybackup")
folder_to_delete = os.path.join(pybackup_path, folder_name) folder_to_delete = os.path.join(pybackup_path, folder_name)
self.backup_manager.delete_privileged_path(folder_to_delete) # Lock UI and show status
self._load_backup_content() 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)

View File

@@ -7,11 +7,12 @@ from pyimage_ui.comment_editor_dialog import CommentEditorDialog
class UserBackupContentFrame(ttk.Frame): class UserBackupContentFrame(ttk.Frame):
def __init__(self, master, backup_manager, **kwargs): def __init__(self, master, backup_manager, actions, **kwargs):
super().__init__(master, **kwargs) super().__init__(master, **kwargs)
self.backup_manager = backup_manager
self.backup_manager = backup_manager
self.backup_path = None self.backup_path = None
self.actions = actions # Store actions object
# --- Backup Content List View --- # --- Backup Content List View ---
self.content_frame = ttk.LabelFrame( self.content_frame = ttk.LabelFrame(