diff --git a/src/vorta/application.py b/src/vorta/application.py index 8857a51eb..40714d5b0 100644 --- a/src/vorta/application.py +++ b/src/vorta/application.py @@ -40,7 +40,7 @@ class VortaApp(QtSingleApplication): backup_finished_event = QtCore.pyqtSignal(dict) backup_cancelled_event = QtCore.pyqtSignal() backup_log_event = QtCore.pyqtSignal(str, dict) - backup_progress_event = QtCore.pyqtSignal(str) + backup_progress_event = QtCore.pyqtSignal(int, str) check_failed_event = QtCore.pyqtSignal(dict) def __init__(self, args_raw, single_app=False): @@ -121,7 +121,7 @@ def create_backup_action(self, profile_id=None): translate('messages', msg['message']), level='error', ) - self.backup_progress_event.emit(f"[{profile.name}] {translate('messages', msg['message'])}") + self.backup_progress_event.emit(profile.id, f"[{profile.name}] {translate('messages', msg['message'])}") return None def open_main_window_action(self): @@ -257,7 +257,7 @@ def react_to_log(self, mgs, context): def break_lock(self, profile): params = BorgBreakJob.prepare(profile) if not params['ok']: - self.backup_progress_event.emit(f"[{profile.name}] {params['message']}") + self.backup_progress_event.emit(profile.id, f"[{profile.name}] {params['message']}") return job = BorgBreakJob(params['cmd'], params) self.jobs_manager.add_job(job) diff --git a/src/vorta/borg/borg_job.py b/src/vorta/borg/borg_job.py index 9cf6a1551..d0af78ec2 100644 --- a/src/vorta/borg/borg_job.py +++ b/src/vorta/borg/borg_job.py @@ -296,7 +296,9 @@ def read_async(fd): # f"{translate('BorgJob','Compressed')}: {pretty_bytes(parsed['compressed_size'])}, " f"{translate('BorgJob','Deduplicated')}: {pretty_bytes(parsed['deduplicated_size'])}" # noqa: E501 ) - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {msg}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {msg}" + ) except json.decoder.JSONDecodeError: msg = line.strip() if msg: # Log only if there is something to log. diff --git a/src/vorta/borg/break_lock.py b/src/vorta/borg/break_lock.py index 0184b6558..91a20ec4b 100644 --- a/src/vorta/borg/break_lock.py +++ b/src/vorta/borg/break_lock.py @@ -4,12 +4,15 @@ class BorgBreakJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Breaking repository lock…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Breaking repository lock…')}" + ) def finished_event(self, result): self.app.backup_finished_event.emit(result) self.app.backup_progress_event.emit( - f"[{self.params['profile_name']}] {self.tr('Repository lock broken. Please redo your last action.')}" + self.params['profile_id'], + f"[{self.params['profile_name']}] {self.tr('Repository lock broken. Please redo your last action.')}", ) self.result.emit(result) diff --git a/src/vorta/borg/check.py b/src/vorta/borg/check.py index e25443fdf..cf67db630 100644 --- a/src/vorta/borg/check.py +++ b/src/vorta/borg/check.py @@ -10,7 +10,9 @@ class BorgCheckJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Starting consistency check…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Starting consistency check…')}" + ) def finished_event(self, result: Dict[str, Any]): """ @@ -25,14 +27,17 @@ def finished_event(self, result: Dict[str, Any]): self.result.emit(result) if result['returncode'] != 0: self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] " + translate('RepoCheckJob', 'Repo check failed. See the logs for details.').format( config.LOG_DIR.as_uri() - ) + ), ) self.app.check_failed_event.emit(result) else: - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Check completed.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Check completed.')}" + ) @classmethod def prepare(cls, profile): diff --git a/src/vorta/borg/compact.py b/src/vorta/borg/compact.py index 12bc160c4..75b174977 100644 --- a/src/vorta/borg/compact.py +++ b/src/vorta/borg/compact.py @@ -11,7 +11,7 @@ class BorgCompactJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() self.app.backup_progress_event.emit( - f"[{self.params['profile_name']} {self.tr('Starting repository compaction...')}]" + self.params['profile_id'], f"[{self.params['profile_name']} {self.tr('Starting repository compaction...')}]" ) def finished_event(self, result: Dict[str, Any]): @@ -27,13 +27,16 @@ def finished_event(self, result: Dict[str, Any]): self.result.emit(result) if result['returncode'] != 0: self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] " + translate( 'BorgCompactJob', 'Errors during compaction. See the logs for details.' - ).format(config.LOG_DIR.as_uri()) + ).format(config.LOG_DIR.as_uri()), ) else: - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Compaction completed.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Compaction completed.')}" + ) @classmethod def prepare(cls, profile): diff --git a/src/vorta/borg/create.py b/src/vorta/borg/create.py index 4b2e8b84b..a9f0f0009 100644 --- a/src/vorta/borg/create.py +++ b/src/vorta/borg/create.py @@ -43,22 +43,26 @@ def process_result(self, result): if result['returncode'] == 1: self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] " + translate( 'BorgCreateJob', 'Backup finished with warnings. See the logs for details.', - ).format(config.LOG_DIR.as_uri()) + ).format(config.LOG_DIR.as_uri()), ) else: - self.app.backup_log_event.emit('', {}) - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Backup finished.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Backup finished.')}" + ) def progress_event(self, fmt): - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {fmt}") + self.app.backup_progress_event.emit(self.params['profile_id'], f"[{self.params['profile_name']}] {fmt}") def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Backup started.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Backup started.')}" + ) def finished_event(self, result): self.app.backup_finished_event.emit(result) diff --git a/src/vorta/borg/delete.py b/src/vorta/borg/delete.py index 5b1fe72f0..b7690be56 100644 --- a/src/vorta/borg/delete.py +++ b/src/vorta/borg/delete.py @@ -9,7 +9,9 @@ class BorgDeleteJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Deleting archive…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Deleting archive…')}" + ) def finished_event(self, result): # set repo stats to N/A @@ -22,7 +24,9 @@ def finished_event(self, result): self.app.backup_finished_event.emit(result) self.result.emit(result) - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Archive deleted.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Archive deleted.')}" + ) @classmethod def prepare(cls, profile, archives: List[str]): diff --git a/src/vorta/borg/diff.py b/src/vorta/borg/diff.py index cd09ff815..6a34c525b 100644 --- a/src/vorta/borg/diff.py +++ b/src/vorta/borg/diff.py @@ -7,13 +7,15 @@ class BorgDiffJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() self.app.backup_progress_event.emit( - f"[{self.params['profile_name']}] {self.tr('Requesting differences between archives…')}" + self.params['profile_id'], + f"[{self.params['profile_name']}] {self.tr('Requesting differences between archives…')}", ) def finished_event(self, result): self.app.backup_finished_event.emit(result) self.app.backup_progress_event.emit( - f"[{self.params['profile_name']}] {self.tr('Obtained differences between archives.')}" + self.params['profile_id'], + f"[{self.params['profile_name']}] {self.tr('Obtained differences between archives.')}", ) self.result.emit(result) diff --git a/src/vorta/borg/extract.py b/src/vorta/borg/extract.py index 41108721f..d2cd7e15d 100644 --- a/src/vorta/borg/extract.py +++ b/src/vorta/borg/extract.py @@ -13,14 +13,14 @@ class BorgExtractJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() self.app.backup_progress_event.emit( - f"[{self.params['profile_name']}] {self.tr('Downloading files from archive…')}" + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Downloading files from archive…')}" ) def finished_event(self, result): self.app.backup_finished_event.emit(result) self.result.emit(result) self.app.backup_progress_event.emit( - f"[{self.params['profile_name']}] {self.tr('Restored files from archive.')}" + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Restored files from archive.')}" ) @classmethod diff --git a/src/vorta/borg/info_archive.py b/src/vorta/borg/info_archive.py index afb94b2f3..9b124c37f 100644 --- a/src/vorta/borg/info_archive.py +++ b/src/vorta/borg/info_archive.py @@ -7,12 +7,16 @@ class BorgInfoArchiveJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Refreshing archive…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Refreshing archive…')}" + ) def finished_event(self, result): self.app.backup_finished_event.emit(result) self.result.emit(result) - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Refreshing archive done.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Refreshing archive done.')}" + ) @classmethod def prepare(cls, profile, archive_name): diff --git a/src/vorta/borg/list_archive.py b/src/vorta/borg/list_archive.py index ef00a0134..a544592bc 100644 --- a/src/vorta/borg/list_archive.py +++ b/src/vorta/borg/list_archive.py @@ -6,12 +6,14 @@ class BorgListArchiveJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Getting archive content…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Getting archive content…')}" + ) def finished_event(self, result): self.app.backup_finished_event.emit(result) self.app.backup_progress_event.emit( - f"[{self.params['profile_name']}] {self.tr('Done getting archive content.')}" + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Done getting archive content.')}" ) self.result.emit(result) diff --git a/src/vorta/borg/list_repo.py b/src/vorta/borg/list_repo.py index 664778d64..90a9c48bc 100644 --- a/src/vorta/borg/list_repo.py +++ b/src/vorta/borg/list_repo.py @@ -9,12 +9,16 @@ class BorgListRepoJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Refreshing archives…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Refreshing archives…')}" + ) def finished_event(self, result): self.app.backup_finished_event.emit(result) self.result.emit(result) - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Refreshing archives done.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Refreshing archives done.')}" + ) @classmethod def prepare(cls, profile): diff --git a/src/vorta/borg/prune.py b/src/vorta/borg/prune.py index aba888fb3..47bafbd95 100644 --- a/src/vorta/borg/prune.py +++ b/src/vorta/borg/prune.py @@ -7,7 +7,9 @@ class BorgPruneJob(BorgJob): def started_event(self): self.app.backup_started_event.emit() - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Pruning old archives…')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Pruning old archives…')}" + ) def finished_event(self, result): # set repo stats to N/A @@ -20,7 +22,9 @@ def finished_event(self, result): self.app.backup_finished_event.emit(result) self.result.emit(result) - self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Pruning done.')}") + self.app.backup_progress_event.emit( + self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Pruning done.')}" + ) @classmethod def prepare(cls, profile): diff --git a/src/vorta/store/models.py b/src/vorta/store/models.py index 400299fb2..214887e55 100644 --- a/src/vorta/store/models.py +++ b/src/vorta/store/models.py @@ -102,6 +102,7 @@ class BackupProfileModel(BaseModel): pre_backup_cmd = pw.CharField(default='') post_backup_cmd = pw.CharField(default='') dont_run_on_metered_networks = pw.BooleanField(default=True) + last_status = pw.CharField(default='', null=True) def refresh(self): return type(self).get(self._pk_expr()) diff --git a/src/vorta/views/main_window.py b/src/vorta/views/main_window.py index 78cf40f47..39a1dc49b 100644 --- a/src/vorta/views/main_window.py +++ b/src/vorta/views/main_window.py @@ -22,7 +22,7 @@ is_system_tray_available, ) from vorta.views.partials.loading_button import LoadingButton -from vorta.views.utils import get_colored_icon +from vorta.views.utils import extract_profile_name, get_colored_icon from .about_tab import AboutTab from .archive_tab import ArchiveTab @@ -135,13 +135,22 @@ def set_icons(self): self.profileDeleteButton.setIcon(get_colored_icon('minus')) self.miscButton.setIcon(get_colored_icon('settings_wheel')) - def set_progress(self, text=''): - self.progressText.setText(text) - self.progressText.repaint() + def set_progress(self, profile_id, text=''): + profile = BackupProfileModel.get_by_id(profile_id) + profile.last_status = text + profile.save() + if profile.name == self.current_profile.name: + self.progressText.setText(text) + self.progressText.repaint() def set_log(self, text=''): - self.logText.setText(text) - self.logText.repaint() + profile = extract_profile_name(text) + if profile == self.current_profile.name: + self.logText.setText(text) + self.logText.repaint() + else: + self.logText.setText('') + self.logText.repaint() def _toggle_buttons(self, create_enabled=True): if create_enabled: @@ -188,6 +197,8 @@ def profile_selection_changed_action(self, index): SettingsModel.key == 'previous_profile_id' ).execute() self.archiveTab.toggle_compact_button_visibility() + self.app.backup_progress_event.emit(self.current_profile.id, self.current_profile.last_status) + self.app.backup_log_event.emit("", {}) def profile_clicked_action(self): if self.miscWidget.isVisible(): diff --git a/src/vorta/views/utils.py b/src/vorta/views/utils.py index 5a9697a73..d3d7365c0 100644 --- a/src/vorta/views/utils.py +++ b/src/vorta/views/utils.py @@ -1,5 +1,6 @@ import json import os +import re import sys from PyQt6.QtGui import QIcon, QImage, QPixmap @@ -49,3 +50,14 @@ def get_exclusion_presets(): 'tags': preset['tags'], } return allPresets + + +def extract_profile_name(text): + pattern = r'\[([^]]+)\]' + + match = re.search(pattern, text) + + if match: + return match.group(1) + else: + return None