Example #1
0
    def restore_button_clicked(self):
        class WaitingThread(QThread):
            completed = pyqtSignal()

            def __init__(self, wthread):
                super(WaitingThread, self).__init__()

                self.wthread = wthread

            def __del__(self):
                self.wait()

            def run(self):
                self.wthread.wait()
                self.completed.emit()

        if self.backup_searching:
            if (self.compressing_timer is not None and
                self.compressing_timer.isActive()):
                self.compressing_timer.stop()

            self.backup_searching = False

            self.finish_backup_saves()

            main_window = self.get_main_window()
            status_bar = main_window.statusBar()

            status_bar.showMessage(_('Restore backup cancelled'))

            self.restore_button.setText(_('Restore backup'))

        elif self.backup_compressing:
            if self.compress_thread is not None:
                self.backup_current_button.setEnabled(False)
                self.compress_thread.quit()

                def completed():
                    self.finish_backup_saves()
                    delete_path(self.backup_path)
                    self.compress_thread = None

                waiting_thread = WaitingThread(self.compress_thread)
                waiting_thread.completed.connect(completed)
                self.waiting_thread = waiting_thread

                waiting_thread.start()
            else:
                self.finish_backup_saves()
                delete_path(self.backup_path)
                self.compress_thread = None

            self.backup_compressing = False

            main_window = self.get_main_window()
            status_bar = main_window.statusBar()

            status_bar.showMessage(_('Restore backup cancelled'))

            self.restore_button.setText(_('Restore backup'))
        elif self.extracting_backup:
            if self.extracting_thread is not None:
                self.restore_button.setEnabled(False)
                self.extracting_thread.quit()

                def completed():
                    save_dir = os.path.join(self.game_dir, 'save')
                    delete_path(save_dir)
                    if self.temp_save_dir is not None:
                        retry_rename(self.temp_save_dir, save_dir)
                    self.temp_save_dir = None

                    self.finish_restore_backup()
                    self.extracting_thread = None

                waiting_thread = WaitingThread(self.extracting_thread)
                waiting_thread.completed.connect(completed)
                self.waiting_thread = waiting_thread

                waiting_thread.start()
            else:
                self.finish_restore_backup()
                self.extracting_thread = None

            self.extracting_backup = False

            main_window = self.get_main_window()
            status_bar = main_window.statusBar()

            status_bar.showMessage(_('Restore backup cancelled'))
        else:
            selection_model = self.backups_table.selectionModel()
            if selection_model is None or not selection_model.hasSelection():
                return

            selected = selection_model.currentIndex()
            table_item = self.backups_table.item(selected.row(), 0)
            selected_info = self.backups[table_item]

            if not os.path.isfile(selected_info['path']):
                return

            backup_previous = not config_true(get_config_value(
                'do_not_backup_previous', 'False'))

            if backup_previous:
                '''
                If restoring the before_last_restore, we rename it to make sure
                we make a proper backup first.
                '''
                model = selection_model.model()
                backup_name = model.data(model.index(selected.row(), 0))

                before_last_restore_name = _('before_last_restore')

                if backup_name.lower() == before_last_restore_name.lower():
                    backup_dir = os.path.join(self.game_dir, 'save_backups')

                    name_lower = backup_name.lower()
                    name_key = alphanum_key(name_lower)
                    max_counter = 1

                    for entry in scandir(backup_dir):
                        filename, ext = os.path.splitext(entry.name)
                        if ext.lower() == '.zip':
                            filename_lower = filename.lower()

                            filename_key = alphanum_key(filename_lower)

                            counter = filename_key[-1:][0]
                            if len(filename_key) > 1 and isinstance(counter,
                                int):
                                filename_key = filename_key[:-1]

                                if name_key == filename_key:
                                    max_counter = max(max_counter, counter)

                    new_backup_name = (before_last_restore_name +
                        str(max_counter + 1))
                    new_backup_path = os.path.join(backup_dir,
                        new_backup_name + '.zip')

                    if not retry_rename(selected_info['path'], new_backup_path):
                        return

                    selected_info['path'] = new_backup_path

                def next_step():
                    self.restore_backup()

                self.after_backup = next_step

                self.backup_saves(before_last_restore_name, True)

                self.restore_button.setEnabled(True)
                self.restore_button.setText(_('Cancel restore backup'))
            else:
                self.restore_backup()
Example #2
0
    def backup_saves(self, name, single=False):
        main_window = self.get_main_window()
        status_bar = main_window.statusBar()

        if self.game_dir is None:
            status_bar.showMessage(_('Game directory not found'))
            if self.after_backup is not None:
                self.after_backup()
                self.after_backup = None
            return

        save_dir = os.path.join(self.game_dir, 'save')
        if not os.path.isdir(save_dir):
            status_bar.showMessage(_('Save directory not found'))
            if self.after_backup is not None:
                self.after_backup()
                self.after_backup = None
            return
        self.save_dir = save_dir

        backup_dir = os.path.join(self.game_dir, 'save_backups')
        if not os.path.isdir(backup_dir):
            if os.path.isfile(backup_dir):
                os.remove(backup_dir)

            os.makedirs(backup_dir)

        if single:
            backup_filename = name + '.zip'
            self.backup_path = os.path.join(backup_dir, backup_filename)
            if os.path.isfile(self.backup_path):
                if not delete_path(self.backup_path):
                    status_bar.showMessage(_('Could not delete previous '
                        'backup archive'))
                    return
        else:
            '''
            Finding a backup filename which does not already exists or is the
            next backup name based on an incremental counter placed at the end
            of the filename without the extension.
            '''

            name_lower = name.lower()
            name_key = alphanum_key(name_lower)
            if len(name_key) > 1 and isinstance(name_key[-1:][0], int):
                name_key = name_key[:-1]

            duplicate_name = False
            duplicate_basename = False
            max_counter = 0

            for entry in scandir(backup_dir):
                filename, ext = os.path.splitext(entry.name)
                if entry.is_file() and ext.lower() == '.zip':
                    filename_lower = filename.lower()

                    if filename_lower == name_lower:
                        duplicate_name = True
                    else:
                        filename_key = alphanum_key(filename_lower)

                        counter = filename_key[-1:][0]
                        if len(filename_key) > 1 and isinstance(counter, int):
                            filename_key = filename_key[:-1]

                            if name_key == filename_key:
                                duplicate_basename = True
                                max_counter = max(max_counter, counter)

            if duplicate_basename:
                name_key = alphanum_key(name)
                if len(name_key) > 1 and isinstance(name_key[-1:][0], int):
                    name_key = name_key[:-1]

                name_key.append(max_counter + 1)
                backup_filename = ''.join(map(lambda x: str(x), name_key))
            elif duplicate_name:
                backup_filename = name + '2'
            else:
                backup_filename = name

            backup_filename = backup_filename + '.zip'

            self.backup_path = os.path.join(backup_dir, backup_filename)

        self.backup_file = None

        status_bar.clearMessage()
        status_bar.busy += 1

        compressing_label = QLabel()
        status_bar.addWidget(compressing_label, 100)
        self.compressing_label = compressing_label

        self.compressing_progress_bar = None
        self.compressing_speed_label = None
        self.compressing_size_label = None

        timer = QTimer(self)
        self.compressing_timer = timer

        self.backup_searching = True
        self.backup_compressing = False

        self.backup_files = deque()
        self.backup_file_sizes = {}

        self.backup_scan = None
        self.next_backup_scans = deque()

        self.total_backup_size = 0
        self.total_files = 0

        self.disable_tab()
        self.get_main_tab().disable_tab()
        self.get_soundpacks_tab().disable_tab()
        self.get_settings_tab().disable_tab()
        self.get_mods_tab().disable_tab()
        self.get_backups_tab().disable_tab()

        if self.manual_backup:
            self.backup_current_button.setText(_('Cancel backup'))
            self.backup_current_button.setEnabled(True)

        compressing_label.setText(_('Searching for save files'))

        def timeout():
            if self.backup_scan is None:
                self.backup_scan = scandir(self.save_dir)
            else:
                try:
                    entry = next(self.backup_scan)

                    if entry.is_file():
                        self.compressing_label.setText(
                            _('Found {filename} in {path}').format(
                                filename=entry.name,
                                path=os.path.dirname(entry.path)))
                        self.backup_files.append(entry.path)
                        self.total_backup_size += entry.stat().st_size
                        self.backup_file_sizes[entry.path
                            ] = entry.stat().st_size
                        self.total_files += 1
                    elif entry.is_dir():
                        self.next_backup_scans.append(entry.path)
                except StopIteration:
                    try:
                        self.backup_scan = scandir(
                            self.next_backup_scans.popleft())
                    except IndexError:
                        self.backup_searching = False
                        self.backup_compressing = True

                        self.compressing_label.setText(
                            _('Compressing save files'))

                        compressing_speed_label = QLabel()
                        compressing_speed_label.setText(_('{bytes_sec}/s'
                            ).format(bytes_sec=sizeof_fmt(0)))
                        status_bar.addWidget(compressing_speed_label)
                        self.compressing_speed_label = (
                            compressing_speed_label)

                        compressing_size_label = QLabel()
                        compressing_size_label.setText(
                            '{bytes_read}/{total_bytes}'
                            .format(bytes_read=sizeof_fmt(0),
                                    total_bytes=sizeof_fmt(self.total_backup_size))
                        )
                        status_bar.addWidget(compressing_size_label)
                        self.compressing_size_label = (
                            compressing_size_label)

                        progress_bar = QProgressBar()
                        progress_bar.setRange(0, self.total_backup_size)
                        progress_bar.setValue(0)
                        status_bar.addWidget(progress_bar)
                        self.compressing_progress_bar = progress_bar

                        self.comp_size = 0
                        self.comp_files = 0
                        self.last_comp_bytes = 0
                        self.last_comp = datetime.utcnow()
                        self.next_backup_file = None

                        if self.compressing_timer is not None:
                            self.compressing_timer.stop()
                            self.compressing_timer = None

                        self.backup_saves_step2()

        timer.timeout.connect(timeout)
        timer.start(0)
Example #3
0
        def timeout():
            try:
                entry = next(self.backups_scan)
                filename, ext = os.path.splitext(entry.name)
                if ext.lower() == '.zip':
                    uncompressed_size = 0
                    character_count = 0
                    worlds_set = set()
                    try:
                        with zipfile.ZipFile(entry.path) as zfile:
                            for info in zfile.infolist():
                                if not info.filename.startswith('save/'):
                                    return

                                uncompressed_size += info.file_size

                                path_items = info.filename.split('/')

                                if len(path_items) == 3:
                                    save_file = path_items[-1]
                                    if save_file.endswith('.sav'):
                                        character_count += 1
                                    if save_file in cons.WORLD_FILES:
                                        worlds_set.add(path_items[1])
                    except zipfile.BadZipFile:
                        pass

                    # We found a valid backup

                    compressed_size = entry.stat().st_size
                    modified_date = datetime.fromtimestamp(
                        entry.stat().st_mtime)
                    formated_date = format_datetime(modified_date,
                        format='short', locale=self.app_locale)
                    arrow_date = arrow.get(entry.stat().st_mtime)
                    human_delta = arrow_date.humanize(arrow.utcnow(),
                        locale=self.app_locale)

                    row_index = self.backups_table.rowCount()
                    self.backups_table.insertRow(row_index)

                    flags = (Qt.ItemIsSelectable | Qt.ItemIsEnabled)

                    if uncompressed_size == 0:
                        compression_ratio = 0
                    else:
                        compression_ratio = 1.0 - (compressed_size /
                            uncompressed_size)
                    rounded_ratio = round(compression_ratio, 4)
                    ratio_percent = format_percent(rounded_ratio,
                        format='#.##%', locale=self.app_locale)

                    if self.previous_selection is not None:
                        if entry.path == self.previous_selection:
                            self.previous_selection_index = row_index

                    fields = (
                        (filename, alphanum_key(filename)),
                        (human_delta, modified_date),
                        (str(len(worlds_set)), len(worlds_set)),
                        (str(character_count), character_count),
                        (sizeof_fmt(uncompressed_size), uncompressed_size),
                        (sizeof_fmt(compressed_size), compressed_size),
                        (ratio_percent, compression_ratio),
                        (formated_date, modified_date)
                        )

                    for index, value in enumerate(fields):
                        item = SortEnabledTableWidgetItem(value[0], value[1])
                        item.setFlags(flags)
                        self.backups_table.setItem(row_index, index, item)

                        if index == 0:
                            self.backups[item] = {
                                'path': entry.path,
                                'actual_size': uncompressed_size
                            }

            except StopIteration:
                self.update_backups_timer.stop()

                if self.previous_selection_index is not None:
                    selection_model = self.backups_table.selectionModel()
                    model = selection_model.model()

                    first_index = model.index(self.previous_selection_index, 0)
                    last_index = model.index(self.previous_selection_index,
                        self.backups_table.columnCount() - 1)
                    row_selection = QItemSelection(first_index, last_index)

                    selection_model.select(row_selection,
                        QItemSelectionModel.Select)
                    selection_model.setCurrentIndex(first_index,
                        QItemSelectionModel.Select)

                self.backups_table.sortItems(1, Qt.DescendingOrder)
                self.backups_table.horizontalHeader().setSortIndicatorShown(
                    True)

                if self.after_update_backups is not None:
                    self.after_update_backups()
                    self.after_update_backups = None