Compare commits

...

4 Commits

Author SHA1 Message Date
Aryaman Sharma ed88504d47
Merge 42549de806 into 9cabbbd193 2024-04-28 04:02:47 +05:30
Manu 9cabbbd193 Input to change macOS version for building 2024-04-08 16:24:31 +01:00
Aryaman 42549de806 storing last status for BackupProfiles 2024-03-13 22:40:16 +05:30
Aryaman e843fff945 show progress and logs only for currently selected profile 2024-03-08 16:39:03 +05:30
17 changed files with 105 additions and 40 deletions

View File

@ -3,17 +3,21 @@ on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
branch: branch:
description: 'Branch to use for building macOS release' description: 'Branch to use for building release'
required: true required: true
default: 'master' default: 'master'
borg_version: borg_version:
description: 'Borg version to package' description: 'Borg version to package'
required: true required: true
default: '1.2.1' default: '1.2.8'
macos_version:
description: 'macOS version for building'
required: true
default: 'macos-11'
jobs: jobs:
build: build:
runs-on: macos-11 runs-on: ${{ github.event.inputs.macos_version }}
steps: steps:
- name: Check out selected branch - name: Check out selected branch

View File

@ -40,7 +40,7 @@ class VortaApp(QtSingleApplication):
backup_finished_event = QtCore.pyqtSignal(dict) backup_finished_event = QtCore.pyqtSignal(dict)
backup_cancelled_event = QtCore.pyqtSignal() backup_cancelled_event = QtCore.pyqtSignal()
backup_log_event = QtCore.pyqtSignal(str, dict) 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) check_failed_event = QtCore.pyqtSignal(dict)
def __init__(self, args_raw, single_app=False): def __init__(self, args_raw, single_app=False):
@ -121,7 +121,7 @@ class VortaApp(QtSingleApplication):
translate('messages', msg['message']), translate('messages', msg['message']),
level='error', 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 return None
def open_main_window_action(self): def open_main_window_action(self):
@ -257,7 +257,7 @@ class VortaApp(QtSingleApplication):
def break_lock(self, profile): def break_lock(self, profile):
params = BorgBreakJob.prepare(profile) params = BorgBreakJob.prepare(profile)
if not params['ok']: 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 return
job = BorgBreakJob(params['cmd'], params) job = BorgBreakJob(params['cmd'], params)
self.jobs_manager.add_job(job) self.jobs_manager.add_job(job)

View File

@ -296,7 +296,9 @@ class BorgJob(JobInterface, BackupProfileMixin):
# f"{translate('BorgJob','Compressed')}: {pretty_bytes(parsed['compressed_size'])}, " # f"{translate('BorgJob','Compressed')}: {pretty_bytes(parsed['compressed_size'])}, "
f"{translate('BorgJob','Deduplicated')}: {pretty_bytes(parsed['deduplicated_size'])}" # noqa: E501 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: except json.decoder.JSONDecodeError:
msg = line.strip() msg = line.strip()
if msg: # Log only if there is something to log. if msg: # Log only if there is something to log.

View File

@ -4,12 +4,15 @@ from .borg_job import BorgJob
class BorgBreakJob(BorgJob): class BorgBreakJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.app.backup_progress_event.emit( 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) self.result.emit(result)

View File

@ -10,7 +10,9 @@ from .borg_job import BorgJob
class BorgCheckJob(BorgJob): class BorgCheckJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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]): def finished_event(self, result: Dict[str, Any]):
""" """
@ -25,14 +27,17 @@ class BorgCheckJob(BorgJob):
self.result.emit(result) self.result.emit(result)
if result['returncode'] != 0: if result['returncode'] != 0:
self.app.backup_progress_event.emit( self.app.backup_progress_event.emit(
self.params['profile_id'],
f"[{self.params['profile_name']}] " f"[{self.params['profile_name']}] "
+ translate('RepoCheckJob', 'Repo check failed. See the <a href="{0}">logs</a> for details.').format( + translate('RepoCheckJob', 'Repo check failed. See the <a href="{0}">logs</a> for details.').format(
config.LOG_DIR.as_uri() config.LOG_DIR.as_uri()
) ),
) )
self.app.check_failed_event.emit(result) self.app.check_failed_event.emit(result)
else: 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 @classmethod
def prepare(cls, profile): def prepare(cls, profile):

View File

@ -11,7 +11,7 @@ class BorgCompactJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() self.app.backup_started_event.emit()
self.app.backup_progress_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]): def finished_event(self, result: Dict[str, Any]):
@ -27,13 +27,16 @@ class BorgCompactJob(BorgJob):
self.result.emit(result) self.result.emit(result)
if result['returncode'] != 0: if result['returncode'] != 0:
self.app.backup_progress_event.emit( self.app.backup_progress_event.emit(
self.params['profile_id'],
f"[{self.params['profile_name']}] " f"[{self.params['profile_name']}] "
+ translate( + translate(
'BorgCompactJob', 'Errors during compaction. See the <a href="{0}">logs</a> for details.' 'BorgCompactJob', 'Errors during compaction. See the <a href="{0}">logs</a> for details.'
).format(config.LOG_DIR.as_uri()) ).format(config.LOG_DIR.as_uri()),
) )
else: 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 @classmethod
def prepare(cls, profile): def prepare(cls, profile):

View File

@ -43,22 +43,26 @@ class BorgCreateJob(BorgJob):
if result['returncode'] == 1: if result['returncode'] == 1:
self.app.backup_progress_event.emit( self.app.backup_progress_event.emit(
self.params['profile_id'],
f"[{self.params['profile_name']}] " f"[{self.params['profile_name']}] "
+ translate( + translate(
'BorgCreateJob', 'BorgCreateJob',
'Backup finished with warnings. See the <a href="{0}">logs</a> for details.', 'Backup finished with warnings. See the <a href="{0}">logs</a> for details.',
).format(config.LOG_DIR.as_uri()) ).format(config.LOG_DIR.as_uri()),
) )
else: else:
self.app.backup_log_event.emit('', {}) self.app.backup_progress_event.emit(
self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Backup finished.')}") self.params['profile_id'], f"[{self.params['profile_name']}] {self.tr('Backup finished.')}"
)
def progress_event(self, fmt): 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): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)

View File

@ -9,7 +9,9 @@ from .borg_job import BorgJob
class BorgDeleteJob(BorgJob): class BorgDeleteJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
# set repo stats to N/A # set repo stats to N/A
@ -22,7 +24,9 @@ class BorgDeleteJob(BorgJob):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.result.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 @classmethod
def prepare(cls, profile, archives: List[str]): def prepare(cls, profile, archives: List[str]):

View File

@ -7,13 +7,15 @@ class BorgDiffJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() self.app.backup_started_event.emit()
self.app.backup_progress_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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.app.backup_progress_event.emit( 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) self.result.emit(result)

View File

@ -13,14 +13,14 @@ class BorgExtractJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() self.app.backup_started_event.emit()
self.app.backup_progress_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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.result.emit(result) self.result.emit(result)
self.app.backup_progress_event.emit( 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 @classmethod

View File

@ -7,12 +7,16 @@ from .borg_job import BorgJob
class BorgInfoArchiveJob(BorgJob): class BorgInfoArchiveJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.result.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 @classmethod
def prepare(cls, profile, archive_name): def prepare(cls, profile, archive_name):

View File

@ -6,12 +6,14 @@ from .borg_job import BorgJob
class BorgListArchiveJob(BorgJob): class BorgListArchiveJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.app.backup_progress_event.emit( 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) self.result.emit(result)

View File

@ -9,12 +9,16 @@ from .borg_job import BorgJob
class BorgListRepoJob(BorgJob): class BorgListRepoJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.result.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 @classmethod
def prepare(cls, profile): def prepare(cls, profile):

View File

@ -7,7 +7,9 @@ from .borg_job import BorgJob
class BorgPruneJob(BorgJob): class BorgPruneJob(BorgJob):
def started_event(self): def started_event(self):
self.app.backup_started_event.emit() 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): def finished_event(self, result):
# set repo stats to N/A # set repo stats to N/A
@ -20,7 +22,9 @@ class BorgPruneJob(BorgJob):
self.app.backup_finished_event.emit(result) self.app.backup_finished_event.emit(result)
self.result.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 @classmethod
def prepare(cls, profile): def prepare(cls, profile):

View File

@ -102,6 +102,7 @@ class BackupProfileModel(BaseModel):
pre_backup_cmd = pw.CharField(default='') pre_backup_cmd = pw.CharField(default='')
post_backup_cmd = pw.CharField(default='') post_backup_cmd = pw.CharField(default='')
dont_run_on_metered_networks = pw.BooleanField(default=True) dont_run_on_metered_networks = pw.BooleanField(default=True)
last_status = pw.CharField(default='', null=True)
def refresh(self): def refresh(self):
return type(self).get(self._pk_expr()) return type(self).get(self._pk_expr())

View File

@ -22,7 +22,7 @@ from vorta.utils import (
is_system_tray_available, is_system_tray_available,
) )
from vorta.views.partials.loading_button import LoadingButton 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 .about_tab import AboutTab
from .archive_tab import ArchiveTab from .archive_tab import ArchiveTab
@ -135,13 +135,22 @@ class MainWindow(MainWindowBase, MainWindowUI):
self.profileDeleteButton.setIcon(get_colored_icon('minus')) self.profileDeleteButton.setIcon(get_colored_icon('minus'))
self.miscButton.setIcon(get_colored_icon('settings_wheel')) self.miscButton.setIcon(get_colored_icon('settings_wheel'))
def set_progress(self, text=''): def set_progress(self, profile_id, text=''):
self.progressText.setText(text) profile = BackupProfileModel.get_by_id(profile_id)
self.progressText.repaint() 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=''): def set_log(self, text=''):
self.logText.setText(text) profile = extract_profile_name(text)
self.logText.repaint() 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): def _toggle_buttons(self, create_enabled=True):
if create_enabled: if create_enabled:
@ -188,6 +197,8 @@ class MainWindow(MainWindowBase, MainWindowUI):
SettingsModel.key == 'previous_profile_id' SettingsModel.key == 'previous_profile_id'
).execute() ).execute()
self.archiveTab.toggle_compact_button_visibility() 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): def profile_clicked_action(self):
if self.miscWidget.isVisible(): if self.miscWidget.isVisible():

View File

@ -1,5 +1,6 @@
import json import json
import os import os
import re
import sys import sys
from PyQt6.QtGui import QIcon, QImage, QPixmap from PyQt6.QtGui import QIcon, QImage, QPixmap
@ -49,3 +50,14 @@ def get_exclusion_presets():
'tags': preset['tags'], 'tags': preset['tags'],
} }
return allPresets return allPresets
def extract_profile_name(text):
pattern = r'\[([^]]+)\]'
match = re.search(pattern, text)
if match:
return match.group(1)
else:
return None