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:
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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."),
|
||||||
}
|
}
|
||||||
0
pybackup/01-September-2025_000000_system_full.txt
Normal file
0
pybackup/01-September-2025_000000_system_full.txt
Normal 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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user