diff --git a/src/vorta/borg/info_archive.py b/src/vorta/borg/info_archive.py new file mode 100644 index 00000000..2396ece3 --- /dev/null +++ b/src/vorta/borg/info_archive.py @@ -0,0 +1,57 @@ +from .borg_thread import BorgThread +from vorta.models import ArchiveModel, RepoModel + + +class BorgInfoArchiveThread(BorgThread): + + def started_event(self): + self.app.backup_started_event.emit() + self.app.backup_progress_event.emit(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(self.tr('Refreshing archive done.')) + + @classmethod + def prepare(cls, profile, archive_name): + ret = super().prepare(profile) + if not ret['ok']: + return ret + + ret['ok'] = True + ret['cmd'] = [ + 'borg', 'info', '--log-json', '--json', + f'{profile.repo.url}::{archive_name}'] + ret['archive_name'] = archive_name + + return ret + + def process_result(self, result): + if result['returncode'] == 0: + remote_archives = result['data'].get('archives', []) + + # get info stored during BorgThread.prepare() + # repo_id = self.params['repo_id'] + repo_id = result['params']['repo_id'] + + # Update remote archives. + for remote_archive in remote_archives: + archive = ArchiveModel.get_or_none( + snapshot_id=remote_archive['id'], + repo=repo_id) + archive.name = remote_archive['name'] # incase name changed + # archive.time = parser.parse(remote_archive['time']) + archive.duration = remote_archive['duration'] + archive.size = remote_archive['stats']['deduplicated_size'] + + archive.save() + + if 'cache' in result['data']: + stats = result['data']['cache']['stats'] + repo = RepoModel.get(id=result['params']['repo_id']) + repo.total_size = stats['total_size'] + repo.unique_csize = stats['unique_csize'] + repo.unique_size = stats['unique_size'] + repo.total_unique_chunks = stats['total_unique_chunks'] + repo.save() diff --git a/src/vorta/borg/info.py b/src/vorta/borg/info_repo.py similarity index 98% rename from src/vorta/borg/info.py rename to src/vorta/borg/info_repo.py index e1aacdc0..f216ff33 100644 --- a/src/vorta/borg/info.py +++ b/src/vorta/borg/info_repo.py @@ -3,7 +3,7 @@ from vorta.i18n import trans_late from vorta.models import RepoModel -class BorgInfoThread(BorgThread): +class BorgInfoRepoThread(BorgThread): def started_event(self): self.updated.emit(self.tr('Validating existing repo...')) diff --git a/src/vorta/views/archive_tab.py b/src/vorta/views/archive_tab.py index 201c0d08..c81601b2 100644 --- a/src/vorta/views/archive_tab.py +++ b/src/vorta/views/archive_tab.py @@ -14,6 +14,7 @@ from vorta.borg.diff import BorgDiffThread from vorta.borg.extract import BorgExtractThread from vorta.borg.list_archive import BorgListArchiveThread from vorta.borg.list_repo import BorgListRepoThread +from vorta.borg.info_archive import BorgInfoArchiveThread from vorta.borg.mount import BorgMountThread from vorta.borg.prune import BorgPruneThread from vorta.borg.umount import BorgUmountThread @@ -117,10 +118,6 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): action.setEnabled(False) return menu - extractAction = menu.addAction("Extract", self.list_archive_action) - deleteAction = menu.addAction("Delete", self.delete_action) - renameAction = menu.addAction("Rename", self.rename_action) - if archive_name in self.mount_points: unmountAction = menu.addAction("Unmount", self.umount_action) unmountAction.setIcon(get_colored_icon('eject')) @@ -128,9 +125,15 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): mountAction = menu.addAction("Mount", self.mount_action) mountAction.setIcon(get_colored_icon('folder-open')) + extractAction = menu.addAction("Extract", self.list_archive_action) + refreshAction = menu.addAction("Refresh", self.refresh_archive_action) + renameAction = menu.addAction("Rename", self.rename_action) + deleteAction = menu.addAction("Delete", self.delete_action) + extractAction.setIcon(get_colored_icon('cloud-download')) - deleteAction.setIcon(get_colored_icon('trash')) + refreshAction.setIcon(get_colored_icon('refresh')) renameAction.setIcon(get_colored_icon('edit')) + deleteAction.setIcon(get_colored_icon('trash')) return menu def populate_from_profile(self): @@ -258,6 +261,23 @@ class ArchiveTab(ArchiveTabBase, ArchiveTabUI, BackupProfileMixin): self._set_status(self.tr('Refreshed archives.')) self.populate_from_profile() + def refresh_archive_action(self): + archive_name = self.selected_archive_name() + if archive_name is not None: + params = BorgInfoArchiveThread.prepare(self.profile(), archive_name) + if params['ok']: + thread = BorgInfoArchiveThread(params['cmd'], params, parent=self.app) + thread.updated.connect(self._set_status) + thread.result.connect(self.refresh_archive_result) + self._toggle_all_buttons(False) + thread.start() + + def refresh_archive_result(self, result): + self._toggle_all_buttons(True) + if result['returncode'] == 0: + self._set_status(self.tr('Refreshed archive.')) + self.populate_from_profile() + def selected_archive_name(self): row_selected = self.archiveTable.selectionModel().selectedRows() if row_selected: diff --git a/src/vorta/views/repo_add_dialog.py b/src/vorta/views/repo_add_dialog.py index 42c135c6..4c97acaa 100644 --- a/src/vorta/views/repo_add_dialog.py +++ b/src/vorta/views/repo_add_dialog.py @@ -6,7 +6,7 @@ from vorta.utils import get_private_keys, get_asset, choose_file_dialog, \ borg_compat, validate_passwords, display_password_backend from vorta.keyring.abc import VortaKeyring from vorta.borg.init import BorgInitThread -from vorta.borg.info import BorgInfoThread +from vorta.borg.info_repo import BorgInfoRepoThread from vorta.i18n import translate from vorta.views.utils import get_colored_icon from vorta.models import RepoModel @@ -208,10 +208,10 @@ class ExistingRepoWindow(AddRepoWindow): def run(self): if self.validate(): - params = BorgInfoThread.prepare(self.values) + params = BorgInfoRepoThread.prepare(self.values) if params['ok']: self.saveButton.setEnabled(False) - thread = BorgInfoThread(params['cmd'], params, parent=self) + thread = BorgInfoRepoThread(params['cmd'], params, parent=self) thread.updated.connect(self._set_status) thread.result.connect(self.run_result) self.thread = thread # Needs to be connected to self for tests to work.