def bootstrap_profile(self, bootstrap_file=PROFILE_BOOTSTRAP_FILE): """ Make sure there is at least one profile when first starting Vorta. Will either import a profile placed in ~/.vorta-init.json or add an empty "Default" profile. """ if bootstrap_file.is_file(): try: profile_export = ProfileExport.from_json(bootstrap_file) profile = profile_export.to_db(overwrite_profile=True, overwrite_settings=True) except Exception as exception: double_newline = os.linesep + os.linesep QMessageBox.critical( None, self.tr('Failed to import profile'), "{}{}\"{}\"{}{}".format( self.tr('Failed to import a profile from {}:').format( bootstrap_file), double_newline, str(exception), double_newline, self.tr('Consider removing or repairing this file to ' 'get rid of this message.'), )) return bootstrap_file.unlink() notifier = VortaNotifications.pick() notifier.deliver(self.tr('Profile import successful!'), self.tr('Profile {} imported.').format( profile.name), level='info') logger.info('Profile {} imported.'.format(profile.name)) if BackupProfileModel.select().count() == 0: default_profile = BackupProfileModel(name='Default') default_profile.save()
def test_interval(clockmock, passed_time, scheduled, now, unit, count, added_time): """Test scheduling in interval mode.""" # setup scheduler = VortaScheduler() time = dt(2020, 5, 4, 0, 0) + now clockmock.now.return_value = time profile = BackupProfileModel.get(name=PROFILE_NAME) profile.schedule_make_up_missed = False profile.schedule_mode = INTERVAL_SCHEDULE profile.schedule_interval_unit = unit profile.schedule_interval_count = count profile.save() event = EventLogModel(subcommand='create', profile=profile.id, returncode=0, category='scheduled' if scheduled else '', start_time=time - passed_time, end_time=time - passed_time) event.save() # run test scheduler.set_timer_for_profile(profile.id) assert scheduler.timers[profile.id]['dt'] == time + added_time
def profile_delete_action(self): if self.profileSelector.count() > 1: to_delete_id = self.profileSelector.currentData() to_delete = BackupProfileModel.get(id=to_delete_id) msg = self.tr( "Are you sure you want to delete profile '{}'?".format( to_delete.name)) reply = QMessageBox.question(self, self.tr("Confirm deletion"), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: to_delete.delete_instance(recursive=True) self.app.scheduler.remove_job( to_delete_id) # Remove pending jobs self.profileSelector.removeItem( self.profileSelector.currentIndex()) self.profile_select_action(0) else: warn = self.tr("Can't delete the last profile.") point = QPoint(0, self.profileDeleteButton.size().height() / 2) QToolTip.showText(self.profileDeleteButton.mapToGlobal(point), warn)
def post_backup_tasks(self, profile_id): """ Pruning and checking after successful backup. """ profile = BackupProfileModel.get(id=profile_id) logger.info('Doing post-backup jobs for %s', profile.name) if profile.prune_on: msg = BorgPruneJob.prepare(profile) if msg['ok']: job = BorgPruneJob(msg['cmd'], msg, profile.repo.id) self.app.jobs_manager.add_job(job) # Refresh archives msg = BorgListRepoJob.prepare(profile) if msg['ok']: job = BorgListRepoJob(msg['cmd'], msg, profile.repo.id) self.app.jobs_manager.add_job(job) validation_cutoff = dt.now() - timedelta(days=7 * profile.validation_weeks) recent_validations = EventLogModel.select().where( (EventLogModel.subcommand == 'check') & (EventLogModel.start_time > validation_cutoff) & (EventLogModel.repo_url == profile.repo.url)).count() if profile.validation_on and recent_validations == 0: msg = BorgCheckJob.prepare(profile) if msg['ok']: job = BorgCheckJob(msg['cmd'], msg, profile.repo.id) self.app.jobs_manager.add_job(job) logger.info('Finished background task for profile %s', profile.name)
def test_fixed(clockmock, passed_time, scheduled, now, hour, minute): """Test scheduling in fixed mode.""" # setup scheduler = VortaScheduler() time = dt(2020, 5, 4, 0, 0) + now clockmock.now.return_value = time profile = BackupProfileModel.get(name=PROFILE_NAME) profile.schedule_make_up_missed = False profile.schedule_mode = FIXED_SCHEDULE profile.schedule_fixed_hour = hour profile.schedule_fixed_minute = minute profile.save() last_time = time - passed_time event = EventLogModel(subcommand='create', profile=profile.id, returncode=0, category='scheduled' if scheduled else '', start_time=last_time, end_time=last_time) event.save() # run test expected = time.replace(hour=hour, minute=minute) if time >= expected or last_time.date() == expected.date(): expected += td(days=1) scheduler.set_timer_for_profile(profile.id) assert scheduler.timers[profile.id]['dt'] == expected
def test_simple_schedule(clockmock): """Test a simple scheduling including `next_job` and `remove_job`.""" scheduler = VortaScheduler() # setup clockmock.now.return_value = dt(2020, 5, 6, 4, 30) profile = BackupProfileModel.get(name=PROFILE_NAME) profile.schedule_make_up_missed = False profile.schedule_mode = INTERVAL_SCHEDULE profile.schedule_interval_unit = 'hours' profile.schedule_interval_count = 3 profile.save() # test set timer and next_job scheduler.set_timer_for_profile(profile.id) assert len(scheduler.timers) == 1 assert scheduler.next_job() == '07:30 ({})'.format(PROFILE_NAME) assert scheduler.next_job_for_profile(profile.id) == '2020-05-06 07:30' # test remove_job and next_job scheduler.remove_job(profile.id) assert len(scheduler.timers) == 0 assert scheduler.next_job() == 'None scheduled' assert scheduler.next_job_for_profile(profile.id) == 'None scheduled'
def test_profile_edit(qapp, qtbot): main = qapp.main_window qtbot.mouseClick(main.profileRenameButton, QtCore.Qt.LeftButton) edit_profile_window = main.window qtbot.addWidget(edit_profile_window) edit_profile_window.profileNameField.setText("") qtbot.keyClicks(edit_profile_window.profileNameField, 'Test Profile') qtbot.mouseClick( edit_profile_window.buttonBox.button(QDialogButtonBox.Save), QtCore.Qt.LeftButton) assert BackupProfileModel.get_or_none(name='Default') is None assert BackupProfileModel.get_or_none(name='Test Profile') is not None assert main.profileSelector.currentText() == 'Test Profile'
def create_backup(self, profile_id): notifier = VortaNotifications.pick() profile = BackupProfileModel.get_or_none(id=profile_id) if profile is None: logger.info('Profile not found. Maybe deleted?') return # Skip if a job for this profile (repo) is already in progress if self.app.jobs_manager.is_worker_running(site=profile.repo.id): logger.debug('A job for repo %s is already active.', profile.repo.id) return self.lock.acquire() logger.info('Starting background backup for %s', profile.name) notifier.deliver(self.tr('Vorta Backup'), self.tr('Starting background backup for %s.') % profile.name, level='info') msg = BorgCreateJob.prepare(profile) if msg['ok']: logger.info('Preparation for backup successful.') msg['category'] = 'scheduled' job = BorgCreateJob(msg['cmd'], msg, profile.repo.id) job.result.connect(self.notify) self.app.jobs_manager.add_job(job) else: logger.error('Conditions for backup not met. Aborting.') logger.error(msg['message']) notifier.deliver(self.tr('Vorta Backup'), translate('messages', msg['message']), level='error') self.lock.release()
def test_import_bootstrap_success(qapp, mocker): mocked_unlink = mocker.MagicMock() mocker.patch.object(Path, 'unlink', mocked_unlink) qapp.bootstrap_profile(Path(VALID_IMPORT_FILE)) assert mocked_unlink.called restored_profile = BackupProfileModel.get_or_none( name="Test Profile Restoration") assert restored_profile is not None restored_repo = restored_profile.repo assert restored_repo is not None assert len(SourceFileModel.select().where( SourceFileModel.profile == restored_profile)) == 3 assert BackupProfileModel.select().count() == 2
def populate_profile_selector(self): self.profileSelector.clear() for profile in BackupProfileModel.select().order_by( BackupProfileModel.name): self.profileSelector.addItem(profile.name, profile.id) current_profile_index = self.profileSelector.findData( self.current_profile.id) self.profileSelector.setCurrentIndex(current_profile_index)
def create_backups_cmdline(self, profile_name): profile = BackupProfileModel.get_or_none(name=profile_name) if profile is not None: if profile.repo is None: logger.warning(f"Add a repository to {profile_name}") self.create_backup_action(profile_id=profile.id) else: logger.warning(f"Invalid profile name {profile_name}")
def test_prune_intervals(qapp, qtbot): prune_intervals = ['hour', 'day', 'week', 'month', 'year'] main = qapp.main_window tab = main.archiveTab profile = BackupProfileModel.get(id=1) for i in prune_intervals: getattr(tab, f'prune_{i}').setValue(9) tab.save_prune_setting(None) profile = profile.refresh() assert getattr(profile, f'prune_{i}') == 9
def profile_select_action(self, index): backup_profile_id = self.profileSelector.currentData() if not backup_profile_id: return self.current_profile = BackupProfileModel.get(id=backup_profile_id) self.archiveTab.populate_from_profile() self.repoTab.populate_from_profile() self.sourceTab.populate_from_profile() self.scheduleTab.populate_from_profile() SettingsModel.update({SettingsModel.str_value: self.current_profile.id}) \ .where(SettingsModel.key == 'previous_profile_id') \ .execute()
def init_overwrite_profile_checkbox(self): """Disable the overwrite profile checkbox if no profile with that name currently exists.""" existing_backup_profile = BackupProfileModel.get_or_none( BackupProfileModel.name == self.profile_export.name ) if not existing_backup_profile: self.overwriteExistingProfile.setChecked(False) self.overwriteExistingProfile.setEnabled(False) self.overwriteExistingProfile.setToolTip( self.tr( 'A profile with the name {} does not exist. Nothing to overwrite.'.format(self.profile_export.name) ) )
def test_manual_mode(): """Test scheduling in manual mode.""" scheduler = VortaScheduler() # setup model profile = BackupProfileModel.get(name=PROFILE_NAME) profile.schedule_make_up_missed = False profile.schedule_mode = MANUAL_SCHEDULE profile.save() # test scheduler.set_timer_for_profile(profile.id) assert len(scheduler.timers) == 0
def validate(self): name = self.profileNameField.text() # A name was entered? if len(name) == 0: self._set_status(translate('AddProfileWindow', self.name_blank)) return False # Profile with this name already exists? exists = BackupProfileModel.select().where( BackupProfileModel.name == name).count() if exists > 0: self._set_status(translate('AddProfileWindow', self.name_exists)) return False self._set_status(self.tr('')) return True
def create_backup_action(self, profile_id=None): if not profile_id: profile_id = self.main_window.current_profile.id profile = BackupProfileModel.get(id=profile_id) msg = BorgCreateJob.prepare(profile) if msg['ok']: job = BorgCreateJob(msg['cmd'], msg, profile.repo.id) self.jobs_manager.add_job(job) else: notifier = VortaNotifications.pick() notifier.deliver(self.tr('Vorta Backup'), translate('messages', msg['message']), level='error') self.backup_progress_event.emit( translate('messages', msg['message'])) return None
def test_first_interval(clockmock, now, unit, count, added_time): """Test scheduling in interval mode without a previous backup.""" # setup scheduler = VortaScheduler() time = dt(2020, 5, 4, 0, 0) + now clockmock.now.return_value = time profile = BackupProfileModel.get(name=PROFILE_NAME) profile.schedule_make_up_missed = False profile.schedule_mode = INTERVAL_SCHEDULE profile.schedule_interval_unit = unit profile.schedule_interval_count = count profile.save() # run test scheduler.set_timer_for_profile(profile.id) assert scheduler.timers[profile.id]['dt'] == time + added_time
def test_first_fixed(clockmock, now, hour, minute, added_time): """Test scheduling in fixed mode without a previous backup.""" # setup scheduler = VortaScheduler() time = dt(2020, 5, 4, 0, 0) + now clockmock.now.return_value = time profile = BackupProfileModel.get(name=PROFILE_NAME) profile.schedule_make_up_missed = False profile.schedule_mode = FIXED_SCHEDULE profile.schedule_fixed_hour = hour profile.schedule_fixed_minute = minute profile.save() # run test scheduler.set_timer_for_profile(profile.id) assert scheduler.timers[profile.id]['dt'] == time + added_time
def next_job(self): next_job = now = dt.now() next_profile = None for profile_id, timer in self.timers.items(): if next_job == now and timer['dt'] > next_job and timer[ 'qtt'].isActive(): next_job = timer['dt'] next_profile = profile_id elif next_job > now and timer['dt'] < next_job and timer[ 'qtt'].isActive(): next_job = timer['dt'] next_profile = profile_id if next_profile is not None: profile = BackupProfileModel.get_or_none(id=next_profile) return f"{next_job.strftime('%H:%M')} ({profile.name})" else: return self.tr('None scheduled')
def init_db(qapp, qtbot, tmpdir_factory): tmp_db = tmpdir_factory.mktemp('Vorta').join('settings.sqlite') mock_db = SqliteDatabase(str(tmp_db), pragmas={'journal_mode': 'wal', }) vorta.store.connection.init_db(mock_db) default_profile = BackupProfileModel(name='Default') default_profile.save() new_repo = RepoModel(url='[email protected]:repo') new_repo.encryption = 'none' new_repo.save() default_profile.repo = new_repo.id default_profile.dont_run_on_metered_networks = False default_profile.validation_on = False default_profile.save() test_archive = ArchiveModel(snapshot_id='99999', name='test-archive', time=dt(2000, 1, 1, 0, 0), repo=1) test_archive.save() test_archive1 = ArchiveModel(snapshot_id='99998', name='test-archive1', time=dt(2000, 1, 1, 0, 0), repo=1) test_archive1.save() source_dir = SourceFileModel(dir='/tmp/another', repo=new_repo, dir_size=100, dir_files_count=18, path_isdir=True) source_dir.save() qapp.main_window.deleteLater() del qapp.main_window qapp.main_window = MainWindow(qapp) # Re-open main window to apply mock data in UI yield qapp.jobs_manager.cancel_all_jobs() qtbot.waitUntil(lambda: not qapp.jobs_manager.is_worker_running(), **pytest._wait_defaults) mock_db.close()
def test_import_success(qapp, qtbot, rootdir, monkeypatch): monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args: [VALID_IMPORT_FILE]) monkeypatch.setattr(QMessageBox, 'information', lambda *args: None) main = qapp.main_window main.profile_import_action() import_dialog: ImportWindow = main.window import_dialog.overwriteExistingSettings.setChecked(True) qtbot.mouseClick(import_dialog.buttonBox.button(QDialogButtonBox.Ok), QtCore.Qt.LeftButton) qtbot.waitSignal(import_dialog.profile_imported, **pytest._wait_defaults) restored_profile = BackupProfileModel.get_or_none( name="Test Profile Restoration") assert restored_profile is not None restored_repo = restored_profile.repo assert restored_repo is not None assert len(SourceFileModel.select().where( SourceFileModel.profile == restored_profile)) == 3
def react_to_log(self, mgs, context): """ Trigger Vorta actions based on Borg logs. E.g. repo lock. """ msgid = context.get('msgid') if msgid == 'LockTimeout': profile = BackupProfileModel.get(name=context['profile_name']) repo_url = context.get('repo_url') msg = QMessageBox() msg.setWindowTitle(self.tr("Repository In Use")) msg.setIcon(QMessageBox.Critical) abortButton = msg.addButton(self.tr("Abort"), QMessageBox.RejectRole) msg.addButton(self.tr("Continue"), QMessageBox.AcceptRole) msg.setDefaultButton(abortButton) msg.setText( self. tr(f"The repository at {repo_url} might be in use elsewhere.")) msg.setInformativeText( self. tr("Only break the lock if you are certain no other Borg process " "on any machine is accessing the repository. Abort or break the lock?" )) msg.accepted.connect(lambda: self.break_lock(profile)) self._msg = msg msg.show() elif msgid == 'LockFailed': repo_url = context.get('repo_url') msg = QMessageBox() msg.setText( self. tr(f"You do not have permission to access the repository at {repo_url}. Gain access and try again." )) # noqa: E501 msg.setWindowTitle(self.tr("No Repository Permissions")) self._msg = msg msg.show()
def on_user_click(self): """Build system tray menu based on current state.""" menu = self.contextMenu() menu.clear() open_action = menu.addAction(self.tr('Vorta for Borg Backup')) open_action.triggered.connect(self.app.open_main_window_action) menu.addSeparator() next_task_time = self.app.scheduler.next_job() status = menu.addAction(next_task_time) status.setEnabled(False) if self.app.jobs_manager.is_worker_running(): status.setText(self.tr('Task in progress')) cancel_action = menu.addAction(self.tr('Cancel Backup')) cancel_action.triggered.connect(self.app.backup_cancelled_event.emit) else: status.setText(self.tr('Next Task: %s') % next_task_time) profiles = BackupProfileModel.select() if profiles.count() > 1: profile_menu = menu.addMenu(self.tr('Backup Now')) for profile in profiles: new_item = profile_menu.addAction(profile.name) new_item.triggered.connect(lambda state, i=profile.id: self.app.create_backup_action(i)) else: profile = profiles.first() profile_menu = menu.addAction(self.tr('Backup Now')) profile_menu.triggered.connect(lambda state, i=profile.id: self.app.create_backup_action(i)) menu.addSeparator() exit_action = menu.addAction(self.tr('Quit')) exit_action.triggered.connect(self.app.quit)
def __init__(self, parent=None): super().__init__() self.setupUi(self) self.setWindowTitle('Vorta for Borg Backup') self.app = parent self.setWindowIcon(get_colored_icon("icon")) self.setWindowFlags(QtCore.Qt.WindowCloseButtonHint | QtCore.Qt.WindowMinimizeButtonHint) self.createStartBtn = LoadingButton(self.tr("Start Backup")) self.gridLayout.addWidget(self.createStartBtn, 0, 0, 1, 1) self.createStartBtn.setGif(get_asset("icons/loading")) # Use previous window state previous_window_width = SettingsModel.get(key='previous_window_width') previous_window_height = SettingsModel.get( key='previous_window_height') self.resize(int(previous_window_width.str_value), int(previous_window_height.str_value)) # Select previously used profile, if available prev_profile_id = SettingsModel.get(key='previous_profile_id') self.current_profile = BackupProfileModel.get_or_none( id=prev_profile_id.str_value) if self.current_profile is None: self.current_profile = BackupProfileModel.select().order_by( 'name').first() # Load tab models self.repoTab = RepoTab(self.repoTabSlot) self.sourceTab = SourceTab(self.sourceTabSlot) self.archiveTab = ArchiveTab(self.archiveTabSlot, app=self.app) self.scheduleTab = ScheduleTab(self.scheduleTabSlot) self.miscTab = MiscTab(self.miscTabSlot) self.miscTab.set_borg_details(borg_compat.version, borg_compat.path) self.tabWidget.setCurrentIndex(0) self.repoTab.repo_changed.connect( self.archiveTab.populate_from_profile) self.repoTab.repo_changed.connect( self.scheduleTab.populate_from_profile) self.repoTab.repo_added.connect(self.archiveTab.list_action) self.createStartBtn.clicked.connect(self.app.create_backup_action) self.cancelButton.clicked.connect(self.app.backup_cancelled_event.emit) QShortcut(QKeySequence("Ctrl+W"), self).activated.connect(self.on_close_window) QShortcut(QKeySequence("Ctrl+Q"), self).activated.connect(self.on_close_window) self.app.backup_started_event.connect(self.backup_started_event) self.app.backup_finished_event.connect(self.backup_finished_event) self.app.backup_log_event.connect(self.set_log) self.app.backup_progress_event.connect(self.set_progress) self.app.backup_cancelled_event.connect(self.backup_cancelled_event) # Init profile list self.populate_profile_selector() self.profileSelector.currentIndexChanged.connect( self.profile_select_action) self.profileRenameButton.clicked.connect(self.profile_rename_action) self.profileExportButton.clicked.connect(self.profile_export_action) self.profileDeleteButton.clicked.connect(self.profile_delete_action) profile_add_menu = QMenu() profile_add_menu.addAction(self.tr('Import from file...'), self.profile_import_action) self.profileAddButton.setMenu(profile_add_menu) self.profileAddButton.clicked.connect(self.profile_add_action) # OS-specific startup options: if not get_network_status_monitor().is_network_status_available(): # Hide Wifi-rule section in schedule tab. self.scheduleTab.wifiListLabel.hide() self.scheduleTab.wifiListWidget.hide() self.scheduleTab.page_2.hide() self.scheduleTab.toolBox.removeItem(1) # Connect to existing thread. if self.app.jobs_manager.is_worker_running(): self.createStartBtn.setEnabled(False) self.createStartBtn.start() self.cancelButton.setEnabled(True) self.set_icons()
def set_timer_for_profile(self, profile_id: int): """ Set a timer for next scheduled backup run of this profile. Removes existing jobs if set to manual only or no repo is assigned. Else will look for previous scheduled backups and catch up if schedule_make_up_missed is enabled. Or, if catch-up is not enabled, will add interval to last run to find next suitable backup time. """ profile = BackupProfileModel.get_or_none(id=profile_id) if profile is None: # profile doesn't exist any more. return with self.lock: # Acquire lock if profile_id in self.timers: self.remove_job(profile_id) # reset schedule if profile.repo is None: # No backups without repo set logger.debug( 'Nothing scheduled for profile %s because of unset repo.', profile_id) if profile.schedule_mode == 'off': logger.debug('Scheduler for profile %s is disabled.', profile_id) return logger.info('Setting timer for profile %s', profile_id) # determine last backup time last_run_log = EventLogModel.select().where( EventLogModel.subcommand == 'create', EventLogModel.category == 'scheduled', EventLogModel.profile == profile.id, 0 <= EventLogModel.returncode <= 1, ).order_by(EventLogModel.end_time.desc()).first() if last_run_log is None: # look for non scheduled (manual) backup runs last_run_log = EventLogModel.select().where( EventLogModel.subcommand == 'create', EventLogModel.profile == profile.id, 0 <= EventLogModel.returncode <= 1, ).order_by(EventLogModel.end_time.desc()).first() # calculate next scheduled time if profile.schedule_mode == 'interval': if last_run_log is None: last_time = dt.now() else: last_time = last_run_log.end_time interval = { profile.schedule_interval_unit: profile.schedule_interval_count } next_time = last_time + timedelta(**interval) elif profile.schedule_mode == 'fixed': if last_run_log is None: last_time = dt.now() else: last_time = last_run_log.end_time + timedelta(days=1) next_time = last_time.replace( hour=profile.schedule_fixed_hour, minute=profile.schedule_fixed_minute) else: # unknown schedule mode raise ValueError("Unknown schedule mode '{}'".format( profile.schedule_mode)) # handle missing of a scheduled time if next_time <= dt.now(): if profile.schedule_make_up_missed: self.lock.release() try: logger.debug('Catching up by running job for %s (%s)', profile.name, profile_id) self.create_backup(profile_id) finally: self.lock.acquire( ) # with-statement will try to release return # create_backup will lead to a call to this method # calculate next time from now if profile.schedule_mode == 'interval': # next_time % interval should be 0 # while next_time > now delta = dt.now() - last_time next_time = dt.now() - delta % timedelta(**interval) next_time += timedelta(**interval) elif profile.schedule_mode == 'fixed': if next_time.date() == dt.now().date(): # time for today has passed, schedule for tomorrow next_time += timedelta(days=1) else: # schedule for today next_time = dt.now().replace( hour=profile.schedule_fixed_hour, minute=profile.schedule_fixed_minute) # start QTimer logger.debug('Scheduling next run for %s', next_time) timer_ms = (next_time - dt.now()).total_seconds() * 1000 timer = QtCore.QTimer() timer.setSingleShot(True) timer.setInterval(int(timer_ms)) timer.timeout.connect(lambda: self.create_backup(profile_id)) timer.start() self.timers[profile_id] = {'qtt': timer, 'dt': next_time} # Emit signal so that e.g. the GUI can react to the new schedule self.schedule_changed.emit(profile_id)
def to_db(self, overwrite_profile=False, overwrite_settings=True): profile_schema = self._profile_dict['SchemaVersion']['version'] keyring = VortaKeyring.get_keyring() if SCHEMA_VERSION < profile_schema: raise VersionException() elif SCHEMA_VERSION > profile_schema: # Add model upgrading code here, only needed if not adding columns if profile_schema < 16: for sourcedir in self._profile_dict['SourceFileModel']: sourcedir['dir_files_count'] = -1 sourcedir['dir_size'] = -1 sourcedir['path_isdir'] = False existing_profile = None if overwrite_profile: existing_profile = BackupProfileModel.get_or_none( BackupProfileModel.name == self.name) if existing_profile: self._profile_dict['id'] = existing_profile.id if not overwrite_profile or not existing_profile: # Guarantee uniqueness of ids while BackupProfileModel.get_or_none( BackupProfileModel.id == self.id) is not None: self._profile_dict['id'] += 1 # Add suffix incase names are the same if BackupProfileModel.get_or_none( BackupProfileModel.name == self.name) is not None: suffix = 1 while BackupProfileModel.get_or_none( BackupProfileModel.name == f"{self.name}-{suffix}") is not None: suffix += 1 self._profile_dict['name'] = f"{self.name}-{suffix}" # Load existing repo or restore it if self._profile_dict['repo']: repo = RepoModel.get_or_none(RepoModel.url == self.repo_url) if repo is None: # Load repo from export repo = dict_to_model(RepoModel, self._profile_dict['repo']) repo.save(force_insert=True) self._profile_dict['repo'] = model_to_dict(repo) if self.repo_password: keyring.set_password('vorta-repo', self.repo_url, self.repo_password) del self._profile_dict['password'] # Delete and recreate the tables to clear them if overwrite_settings: DB.drop_tables([SettingsModel, WifiSettingModel]) DB.create_tables([SettingsModel, WifiSettingModel]) SettingsModel.insert_many( self._profile_dict['SettingsModel']).execute() WifiSettingModel.insert_many( self._profile_dict['WifiSettingModel']).execute() # Set the profile ids to be match new profile for source in self._profile_dict['SourceFileModel']: source['profile'] = self.id SourceFileModel.insert_many( self._profile_dict['SourceFileModel']).execute() # Delete added dictionaries to make it match BackupProfileModel del self._profile_dict['SettingsModel'] del self._profile_dict['SourceFileModel'] del self._profile_dict['WifiSettingModel'] del self._profile_dict['SchemaVersion'] # dict to profile new_profile = dict_to_model(BackupProfileModel, self._profile_dict) if overwrite_profile and existing_profile: force_insert = False else: force_insert = True new_profile.save(force_insert=force_insert) init_db( ) # rerun db init code to perform the same operations on the new as as on application boot return new_profile
def save(self): renamed_profile = BackupProfileModel.get(id=self.existing_id) renamed_profile.name = self.profileNameField.text() renamed_profile.save() self.profile_changed.emit(renamed_profile.name, renamed_profile.id) self.accept()
def __init__(self, parent=None, rename_existing_id=None): super().__init__(parent) existing_profile = BackupProfileModel.get(id=rename_existing_id) self.profileNameField.setText(existing_profile.name) self.existing_id = rename_existing_id self.modalTitle.setText(self.tr('Rename Profile'))
def save(self): new_profile = BackupProfileModel(name=self.profileNameField.text()) new_profile.save() self.profile_changed.emit(new_profile.name, new_profile.id) self.accept()