#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (c) 2013 Sven Erik Knop, Perforce Software Ltd # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE # SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # User contributed content on the Perforce Public Depot is not supported by Perforce, # although it may be supported by its author. This applies to all contributions # even those submitted by Perforce employees. # # Maintainer: Sven Erik Knop, sknop@perforce.com import P4 import sys import argparse class Directory: def __init__(self, path): self.path = path self.localSize = 0 self.totalSize = 0 def __repr__(self): if self.totalSize == 0: return self.path else: return "{} : {}".format(self.path, self.totalSize) class DirectorySizes: def __init__(self, path, depth, revisions): self.range = revisions self.depth = depth self.p4 = P4.P4() self.p4.prog = "DirectorySizes" self.p4.connect() if path.endswith("/"): path = path[:-1] self.directories = [] pattern = path for d in range(depth): pattern = pattern + "/*" dirs = self.find_directories(pattern) self.directories.append(dirs) self.dir_sizes = self.find_sizes(revisions) def find_directories(self, pattern): dirs = [ Directory(x['dir']) for x in self.p4.run_dirs(pattern) ] return dirs def run_sizes(self, directory, wildcard, range): cmd = "{dir}/{wild}{range}".format(dir=directory, wild=wildcard, range=range) return int(self.p4.run_sizes('-sa', cmd)[0]['fileSize']) def find_sizes(self, range): result = {} for level in self.directories: print("level : ", level) for d in level: d.localSize = self.run_sizes(d.path, "*", range) #size = int(self.p4.run_sizes('-sa', "{dir}/...{range}".format(dir=d, range=range))[0]['fileSize']) #result[d] = size #self.print_size(d, size) return result def print_size(self, directory, size): print("{dir:50} : {size:16} ({human})".format(dir=directory, size=size, human=self.human(size))) def human(self, size): units = [" B", " KB", " MB", " GB", " TB"] factor = 1024 current = size for u in units: if current < factor: return str(current) + u else: current = round(current / factor, 2) return str(current) + " PB" if __name__ == "__main__": parser = argparse.ArgumentParser(description="Directory sizes") parser.add_argument("-d","--depth", default=1, type=int) parser.add_argument("-p","--path", required=True) parser.add_argument("-r","--range", default="") args = parser.parse_args() tool = DirectorySizes(args.path, args.depth, args.range)