-
Notifications
You must be signed in to change notification settings - Fork 0
/
backup_version.py
146 lines (112 loc) · 5.29 KB
/
backup_version.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import os
import sys
import argparse
import shutil
import tarfile
from datetime import datetime
def parse_args():
parser = argparse.ArgumentParser(description='Backup Projects')
parser.add_argument('-d', '--destination', required=True, help='Backup destination directory')
parser.add_argument('-s', '--source', required=True, help='Backup source file/folder')
parser.add_argument('-i', '--include', nargs='+', help='Files to transfer')
parser.add_argument('-e', '--exclude', nargs='+', help='Files to ignore')
parser.add_argument('-n', '--name', default=None, help='Destination base folder Name')
parser.add_argument('-c', '--compress', action='store_true', default=False, help='Should compress source')
parser.add_argument('-f', '--force', action='store_true', default=False, help='Skip checking for changes')
if len(sys.argv) == 1:
parser.print_help()
sys.exit()
return parser.parse_args()
def get_last_modified(directory, include, exclude):
list_of_files = get_file_tree(directory, '', include, exclude)
latest_file = max(list_of_files, key=lambda fn: os.path.getmtime(os.path.join(directory, fn)), default=0)
return os.stat(os.path.join(directory, latest_file)).st_mtime
def is_newer(source_time, destination_time):
return source_time - destination_time > 2
def has_version_changed(source, destination, name=None, include=None, exclude=None):
if not (os.path.exists(source) and os.path.isdir(source)):
return False
if not (os.path.exists(destination) and os.path.isdir(destination)):
return True
source_basename = name if name else os.path.basename(source)
list_of_backups = list(filter(lambda fn: os.path.basename(fn).startswith(source_basename), os.listdir(destination)))
if len(list_of_backups) == 0:
return True
latest_backup = max(list_of_backups, key=lambda file: os.path.getctime(os.path.join(destination, file)))
return is_newer(get_last_modified(source, include, exclude), os.path.getctime(os.path.join(destination, latest_backup)))
def transfer_file(source, destination):
try:
if os.path.isdir(source):
if not os.path.exists(destination):
os.mkdir(destination)
shutil.copystat(source, destination)
else:
shutil.copy2(source, destination)
except FileNotFoundError:
os.makedirs(os.path.dirname(destination))
transfer_file(source, destination)
def should_copy(file_name, include=None, exclude=None):
if include:
for include_item in include:
if file_name.startswith(include_item):
return True
return False
if exclude:
for exclude_item in exclude:
if file_name.startswith(exclude_item):
return False
return True
return True
def get_file_tree(directory, base, include=None, exclude=None):
for item in os.listdir(directory):
s = os.path.join(base, item)
if should_copy(s, include, exclude):
yield s
if os.path.isdir(os.path.join(directory, item)):
yield from get_file_tree(os.path.join(directory, item), s, include, exclude)
def print_progress(header, status, is_end=False):
w, _ = shutil.get_terminal_size((80, 20))
print('\r' + header + ' ' * (w-len(status)-len(header)) + status, end='\n' if is_end else '')
def print_backup_state(file, status, is_end=False):
print_progress('Backing up ' + '\'' + file + '\'', status, is_end)
def backup(source, destination, name=None, include=None, exclude=None, compress=True, force=False):
print_backup_state(source, 'WORKING')
if not os.path.exists(source):
print_backup_state(source, 'NOT FOUND', True)
return
if not os.path.exists(destination):
os.makedirs(destination)
backup_file = None
if force or has_version_changed(source, destination, name, include, exclude):
date = datetime.now().strftime('%Y_%m_%d_%H%M%S')
if name:
basename = name
else:
basename = os.path.basename(os.path.normpath(source))
destination = os.path.join(destination, basename + '_' + date)
file_list = get_file_tree(source, '', include, exclude)
if compress:
backup_file = destination + '.tgz'
tar = tarfile.open(backup_file, 'w')
for file_item in file_list:
tar.add(os.path.join(source, file_item), arcname=file_item, recursive=False)
tar.close()
else:
backup_file = destination
for file_item in file_list:
s = os.path.join(source, file_item)
d = os.path.join(destination, file_item)
if os.path.isdir(s):
os.makedirs(d)
else:
transfer_file(s, d)
shutil.copystat(s, d)
print_backup_state(source, 'DONE', True)
else:
print_backup_state(source, 'UP TO DATE', True)
return backup_file
def main():
args = parse_args()
backup(args.source, args.destination, args.name, args.include, args.exclude, args.compress, args.force)
if __name__ == '__main__':
main()