-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSyncDirs.py
242 lines (209 loc) · 7.36 KB
/
SyncDirs.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
#!/usr/bin/env python3
import os
from os import sep
import shutil
from time import time
from datetime import datetime
class SyncDirs:
"""
Synchronizes source directory tree and contents to target
with identical structure and contents. Uses size comparison
and existence of source in target to decide whether to copy
or not.
File or folder not in source is removed from target.
Returns:
obj: instance of SyncDirs
"""
jobs = set()
def __init__(self, name, paths):
"""
Initialize an instance of SyncDirs.
Args:
name (str): name for SyncDirs instance.
src (str): Source directory.
tgt (str): Target directory.
"""
self.__name = name
self.srcPath, self.tgtPath = paths
self.isFile = False
if os.path.isfile(self.srcPath):
self.isFile = True
self.start = time() - 360
self.end = None
while True:
if os.path.exists(self.srcPath):
break
else:
print('Invalid source directory: ', self.srcPath)
self.srcPath = input('Re-enter path: ')
if self.srcPath == 'cancel':
exit()
self.addJob(self)
def __str__(self):
return f"""
<{self.__name}>
Source: {self.srcPath}
Target: {self.tgtPath}
"""
def sync(self):
"""
use os.walk() to find differences in source
and target directory and then synchronize two
paths.
"""
if self.isFile:
self.makeTgtDirs(self.tgtPath)
file = os.path.basename(self.srcPath)
self.copyFile(file)
self.clean()
else:
for folder, _, files in os.walk(self.srcPath):
tail = self.getTail(folder, src=True)
tgt = f'{self.tgtPath}{tail}'
self.makeTgtDirs(tgt)
for file in files:
self.copy(folder, tgt, file)
self.clean()
def getTail(self, folder, src=False, tgt=False):
"""
Returns tail path of source path or target path.
example.srcPath = '/home/user/Desktop/buffer'
example.tgtPath = '/home/user/Desktop/foo/bar'
folder = '/home/user/Desktop/buffer/another'
example.getTail(folder, src=True) --> '/another'
example.tgtPath + '/another'
= 'home/user/Desktop/foo/bar/another'
Args:
folder (str): current folder in os.walk() recursion.
src (bool, optional): set to True for source path.
tgt (bool, optional): set to True for target path.
Returns:
str: tail path of current folder in os.walk() recursion.
"""
if src:
return folder.replace(self.srcPath, '')
elif tgt:
return folder.replace(self.tgtPath, '')
else:
return None
def clean(self):
"""
Removes files and/or directories from target directory
that no longer exist in source directory.
"""
if self.isFile:
file = os.path.basename(self.srcPath)
if not os.path.exists(self.srcPath):
os.remove(self.tgtPath+sep+file)
else:
for folder, _, files in os.walk(self.tgtPath):
tail = self.getTail(folder, tgt=True)
if not os.path.exists(self.srcPath+tail):
print(f'\nremoving "{folder}"')
shutil.rmtree(folder)
else:
for file in files:
if not os.path.exists(self.srcPath+tail+sep+file):
tgtFile = folder+sep+file
print(f'\nremoving "{tgtFile}"')
os.remove(tgtFile)
def copyFile(self, file):
src = self.srcPath
tgt = self.tgtPath + sep + file
if os.path.exists(self.tgtPath+sep+file):
if not self.compareSize(src, tgt):
shutil.copy(src, tgt)
print(f'\ncopying "{file}"')
else:
shutil.copy(src, tgt)
print(f'\ncopying "{file}"')
def copy(self, folder, tgt, file):
"""uses self.compareSize() and os.path.exists()
to decide whether to copy source file/directory
to target.
Args:
folder (str): current folder in os.walk() recursion.
tgt (str): path to copy to.
file (str): current file in os.walk() recursion.
"""
src = f'{folder}{sep}{file}'
tgt = f'{tgt}{sep}{file}'
if os.path.exists(tgt):
if not self.compareSize(src, tgt):
print(f'\ncopying "{file}"')
shutil.copy(src, tgt)
else:
print(f'\ncopying "{file}"')
shutil.copy(src, tgt)
def backUp(self):
"""
Makes clones of target directory at set interval in
its parent directory.
example.tgtPath = 'home/user/Desktop/buffer/another'
backup = 'home/user/Desktop/buffer/another_backup'
in os.listdir(backup) = [
'another backup 2020-08-09 17:01:13.731190',
'another backup 2020-08-09 17:11:13.731190'
]
"""
basename = os.path.basename(self.tgtPath)
backup = self.tgtPath+sep+'..'+sep+f'.{basename}_backup'
self.makeTgtDirs(backup)
while len(os.listdir(backup)) > 12:
a = sorted(os.listdir(backup)).pop(0)
print('\ndeleting oldest backup')
shutil.rmtree(backup+sep+a)
self.end = time()
if int(self.end - self.start) > 360:
print(f'\ncreating backup for <{self.__name}>')
shutil.copytree(self.tgtPath, backup + sep +
basename + ' ' + str(datetime.now()))
self.start = time()
def compareSize(self, src, tgt):
"""
Compares parallel files or source and target directories
of their size.
Args:
src (str): absolute path of file or directory in source directory.
tgt (str): absolute path of file or directory in target directory.
Returns:
bool: size comparison of src and tgt.
"""
if os.path.getsize(src) == os.path.getsize(tgt):
return True
else:
return False
@classmethod
def run(cls):
"""
Execute synchronizing process for each SyncDirs instance
in jobs set.
"""
count = 0
while True:
try:
for job in cls.jobs:
print(job)
job.sync()
job.backUp()
except FileNotFoundError:
pass
@classmethod
def addJob(cls, instance):
print(f'{instance}')
cls.jobs.add(instance)
@staticmethod
def makeTgtDirs(tgt):
"""
Creates target directory (and intermediary directories).
Args:
tgt (str): absolute path of target directory.
"""
try:
os.makedirs(tgt)
print(f'\ncreating "{tgt}" because it does not exist.')
except FileExistsError:
pass
SyncDirs('example', ('/SOURCE/DIRECTORY', '/TARGET/DIRECTORY'))
while True:
SyncDirs.run()