Last active
November 1, 2018 07:21
-
-
Save itsthejb/7f0b64eb6a7e9eee1767483ed1c2ebce to your computer and use it in GitHub Desktop.
Pack dirs to maximise fill space (Packer problem)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import shutil | |
from functools import reduce | |
from bisect import insort | |
try: | |
from os import scandir, walk | |
except ImportError: | |
from scandir import scandir, walk | |
class DirPacker(object): | |
targetSize = 0 | |
candidates = [] | |
dirMap = None | |
results = None | |
bestFit = None | |
def __init__(self, candidates, targetSize): | |
self.targetSize = targetSize | |
self.candidates = candidates | |
print("Target size {}, {} candiate directories".format(targetSize, len(candidates))) | |
def calculate(self): | |
dirMap = self.DirMap(self.candidates, self.targetSize) | |
results = [] | |
for i in range(dirMap.size): | |
total = 0 | |
current = [] | |
for j in range(i, dirMap.size): | |
t = dirMap.sortedTuples[j] | |
if total + t.size <= self.targetSize: | |
current.append(t) | |
total += t.size | |
if current: | |
insort(results, current) | |
results.reverse() | |
self.dirMap = dirMap | |
self.results = results | |
self.bestFit = DirPacker.Result(results[0], self.targetSize) if results else None | |
class Result(object): | |
paths = None | |
total = None | |
remaining = None | |
targetSize = None | |
def __init__(self, dirs, targetSize): | |
paths = [] | |
total = 0 | |
for d in dirs: | |
paths.append(d.path) | |
total += d.size | |
self.paths = paths | |
self.total = total | |
self.targetSize = targetSize | |
self.remaining = targetSize - total | |
assert self.remaining >= 0, "Remaining size is -ve {remaining}".format(remaining=str(self.remaining)) | |
def __repr__(self): | |
return "Available: {target}, Max Pack: {actual}, Remaining: {diff}\nPaths: {paths}".format(target=str(self.targetSize), | |
actual=str(self.total), | |
diff=str(self.remaining), | |
paths=str(self.paths)) | |
def output(self, verbose): | |
if verbose: | |
return self.__repr__ | |
else: | |
return "\n".join(self.paths) | |
class DirMap(object): | |
sortedTuples = [] | |
size = 0 | |
def __init__(self, dirs, space): | |
sortedTuples = [] | |
for d in dirs: | |
t = DirPacker.DirTuple(d) | |
if t.size <= space: | |
insort(sortedTuples, t) | |
sortedTuples.reverse() | |
self.sortedTuples = sortedTuples | |
self.size = len(self.sortedTuples) | |
class DirTuple(object): | |
path = "" | |
size = 0 | |
def __init__(self, dir): | |
self.path = dir | |
self.size = self.sizeOf(dir) | |
def __lt__(self, other): | |
return self.size < other.size | |
def __repr__(self): | |
return "{path} <{size}>".format(path=self.path, size=str(self.size)) | |
def sizeOf(self, dir): | |
# l = lambda t,f: f.stat().st_size + t if f.is_file else t | |
# return reduce(l, os.scandir(dir)) | |
# TODO use reduce | |
c = 0 | |
for f in scandir(dir): | |
c += f.stat().st_size | |
# if f.is_file: | |
return c | |
def stdInDirs(): | |
res = [] | |
print("Reading STDIN...") | |
for line in sys.stdin: | |
res.append(line.strip()) | |
return res | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
print("Usage: " + sys.argv[0] + " <-v> <target parent directory>") | |
print("Example: find /mnt/1/Media/Movies/* -type d | dir-packer /mnt/2/Media/Movies") | |
exit(1) | |
verbose = "-v" in sys.argv | |
usage = shutil.disk_usage(sys.argv[-1]) | |
res = DirPacker(stdInDirs(), usage.free) | |
res.calculate() | |
if res.bestFit: | |
print("Fill the destination with:") | |
print(res.bestFit.output(verbose)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment