Source code for kvalikirstu2.folder_backup

"""A module for creating backups of folders for reverting changes to the study folder."""

import os
import shutil
from kvalikirstu2 import utils


[docs]class FolderTimestamp: """ Folder timestamp for a backup. :var int index: The index of the timestamp. :var float timestamp: The unix timestamp. """ def __init__(self, index, timestamp): """FolderTimestamp contructor. :param index: The index of the folder. :param timestamp: Unix timestamp. """ self.index = index self.timestamp = timestamp
[docs]def get_timestamps(folder, max_backups): """Get time stamps for a folder. :param str folder: Folder path to get backups for. :param max_backups: Maximum number of backups. """ backup_dir = _get_backup_folder(folder) timestamps = [] for index in range(max_backups): folder_path = _get_folder_name(index, backup_dir) if os.path.exists(folder_path): timestamp = utils.folder_modified_timestamp(folder_path) timestamps.append(FolderTimestamp(index, timestamp)) return timestamps
[docs]def backup_folder(folder, max_folder_size, max_backups): """Create a backup of the folder. :param str folder: The folder path. :param max_folder_size: The maximum size of the folder. :param max_backups: The maximum number of backups. """ if not should_backup_folder(folder, max_folder_size, max_backups): return new_folder = _get_new_folder_name(folder, max_backups) if os.path.exists(new_folder): shutil.rmtree(new_folder) os.makedirs(new_folder) for file in os.listdir(folder): path = os.path.join(folder, file) if os.path.isfile(path): shutil.copy(path, os.path.join(new_folder, file)) elif file != _backup_folder_name(): shutil.copytree(path, os.path.join(new_folder, file))
[docs]def clear_backups(folder): """Clear old backups for the folder. :param folder: The path of the folder to clear backups for. """ backup_dir = _get_backup_folder(folder) if os.path.exists(backup_dir): shutil.rmtree(backup_dir)
[docs]def restore_backup(folder, index): """Restore backup with index. :param str folder: The folder to get a backup for. :param int index: The index that chooses the timestamp to restore from. """ backup_dir = _get_backup_folder(folder) curr_backup_folder = _get_folder_name(index, backup_dir) old_folders = [] for file in os.listdir(folder): path = os.path.join(folder, file) if file != _backup_folder_name(): if os.path.isfile(path): os.unlink(path) else: new_name = utils.get_temp_path(folder, "") old_folders.append(new_name) os.rename(path, new_name) for file in os.listdir(curr_backup_folder): path = os.path.join(folder, file) backup_path = os.path.join(curr_backup_folder, file) if os.path.isfile(backup_path): shutil.copy2(backup_path, path) else: shutil.copytree(backup_path, path) for path in old_folders: shutil.rmtree(path)
def _calculate_folders(folder): """Calculates the number of folders inside a folder. :param folder: The path of the folder. """ count = 0 for file in os.listdir(folder): if os.path.isdir(os.path.join(folder, file)): count += 1 return count def _get_backup_folder(folder): """Gets the path for the backup folder. :param folder: The path of the folder. """ return os.path.join(folder, _backup_folder_name()) def _backup_folder_name(): """Gets the name for the backup folder. """ return 'backup' def _get_next_index(folder, backup_path, max_backups): """Gets the next index to use for the next backup. :param folder: The path of the folder. :param backup_path: That path of the backup folder. :param max_backups: The maximum number of backups. """ folder_count = _calculate_folders(backup_path) if folder_count < max_backups: return folder_count timestamps = get_timestamps(folder, max_backups) lowest_ts = timestamps[0].timestamp lowest_index = 0 for index, timestamp in enumerate(timestamps): if timestamp.timestamp < lowest_ts: lowest_index = index lowest_ts = timestamp.timestamp return lowest_index def _get_folder_name(index, backup_dir): """Get backup folder name for index. :param index: The index of the folder. :param backup_dir: The path of the directory used for storing backups. """ return os.path.join(backup_dir, str(index)) def _get_new_folder_name(folder, max_backups): """Get the backup folder name for a given folder. :param folder: The path of the folder that is being backed up. :param max_backups: The maximum number of backups. """ backup_path = _get_backup_folder(folder) _init_backup_folder(backup_path) index = _get_next_index(folder, backup_path, max_backups) return _get_folder_name(index, backup_path) def _init_backup_folder(folder): """Initializes the backup folder. :param folder: The path of the backup folder. """ if not os.path.exists(folder): os.makedirs(folder)
[docs]def folder_too_large(folder, max_folder_size): """Is the folder too large to be backed up? :param folder: The path of the folder. :param max_folder_size: The maximum size of the folder in bytes. :return: True/False based on whether or not the folder is too large to be backed up. """ return _get_folder_size_wo_backup(folder) > max_folder_size
[docs]def should_backup_folder(folder, max_folder_size, max_backups): """Should the folder be backed up? :param folder: The path of the folder. :param max_folder_size: The maximum size of the folder being backed up. :param max_backups: The maximum number of backups to store. """ if folder_too_large(folder, max_folder_size): return False timestamp = utils.folder_modified_timestamp(folder) timestamps = get_timestamps(folder, max_backups) filtered = [ts for ts in timestamps if ts.timestamp == timestamp] return not filtered
def _get_folder_size_wo_backup(folder_path): """Gets the size of a folder, excluding the folder used for backup. :param folder_path: The path of the folder to be examined. """ size = 0 for file in os.listdir(folder_path): path = os.path.join(folder_path, file) if os.path.isfile(path): size += os.path.getsize(path) elif os.path.isdir(path) and file != _backup_folder_name(): size += utils.get_folder_size(path) return size