diff --git a/web/transfer-time b/web/transfer-time index 1883228..dc6368d 100755 --- a/web/transfer-time +++ b/web/transfer-time @@ -1,9 +1,9 @@ #!/usr/bin/env python3 import argparse -import sys -import os import datetime +import os +import sys def is_digit(d): @@ -56,24 +56,39 @@ def get_numerical(num_string): def parse_options(): parser = argparse.ArgumentParser( - description="Transfer time calculator", + description="Transfer time calculator. Two arguments only! The third is calculated.", epilog="You may omit the 'b' to time events. ex. '20 minutes / task, 7 tasks to do': %(prog)s 3/h 7", ) parser.add_argument( + "--rate", "-r", - dest="rate", - help="Rate: inverse of speed. Instead of 10b/s, write 0.1s/b", - action="store_true", - default=False, + help="Rate: inverse of speed. ex: 0.1s/b", + action="store", + default=None, ) parser.add_argument( - "speed", help="Speed of transfer (ex. 3.2Mb/s). Time units: s, m, h, d, w" + "--speed", "-v", help="Speed of transfer (ex. 3.2Mb/s). Time units: s, m, h, d, w", default=None ) parser.add_argument( - "size", + "--size", + "-s", help="Data size (ex. 4.55GB), or folder/file path. Units: b, kb, mb, gb, tb, pb or kib, mib ... ", + default=None, ) - return parser.parse_args() + parser.add_argument( + "--time", "-t", help="Time used to transfer Units: s,m,h,d or [dd]:[hh]:[mm]:[ss].[ss]... ", default=None + ) + parsed = parser.parse_args() + if parsed.rate and parsed.speed: + parser.error("Can't use both rate and speed") + + if ( + sum((int(parsed.speed is None and parsed.rate is None), int(parsed.time is None), int(parsed.size is None))) + != 1 + ): + parser.error("Exactly two measures required") + + return parsed def parse_rate(rate_string): @@ -124,24 +139,24 @@ def parse_size(size_string): if multiplier_part == "kb": return 1024 * numerical_part if multiplier_part == "mb": - return 1024 ** 2 * numerical_part + return 1024**2 * numerical_part if multiplier_part == "gb": - return 1024 ** 3 * numerical_part + return 1024**3 * numerical_part if multiplier_part == "tb": - return 1024 ** 4 * numerical_part + return 1024**4 * numerical_part if multiplier_part == "pb": - return 1024 ** 5 * numerical_part + return 1024**5 * numerical_part if multiplier_part == "kib": return 1000 * numerical_part if multiplier_part == "mib": - return 1000 ** 2 * numerical_part + return 1000**2 * numerical_part if multiplier_part == "gib": - return 1000 ** 3 * numerical_part + return 1000**3 * numerical_part if multiplier_part == "tib": - return 1000 ** 4 * numerical_part + return 1000**4 * numerical_part if multiplier_part == "pib": - return 1000 ** 5 * numerical_part + return 1000**5 * numerical_part return None @@ -160,7 +175,6 @@ def parse_speed(speed_string): if divider_string == "s": return divisor - if divider_string == "m": return divisor / 60 if divider_string == "h": @@ -171,41 +185,97 @@ def parse_speed(speed_string): return None +def parse_time(time_string): + """Return in seconds""" + if ":" in time_string: + split_time = time_string.split(":") + if len(split_time) > 0: + s = float(split_time.pop()) + if len(split_time) > 0: + s += 60 * float(split_time.pop()) + if len(split_time) > 0: + s += 60 * 60 * float(split_time.pop()) + if len(split_time) > 0: + s += 24 * 60 * 60 * float(split_time.pop()) + if len(split_time) > 0: + raise ValueError("Too many digits in time string") + return s + + if is_numerical(time_string): + return float(time_string) + + value = float(time_string[0:-1]) + unit = time_string[-1] + + if unit == "s": + return value + if unit == "m": + return 60 * value + if unit == "h": + return 60 * 60 * value + if unit == "d": + return 24 * 60 * 60 * value + if unit == "w": + return 7 * 24 * 60 * 60 * value + if unit == "y": + return 365 * 24 * 60 * 60 * value + + raise ValueError("Can't parse time unit") + + def print_err(s): sys.stderr.write(str(s) + "\n") sys.stderr.flush() -def time_human(num): - return str(datetime.timedelta(seconds=num)) +def time_human(seconds): + return str(datetime.timedelta(seconds=seconds)) + + +def size_human(size, precision=1): + if size == None: + return "nan" + suffixes = ["B", "KB", "MB", "GB", "TB", "PB"] + suffixIndex = 0 + defPrecision = 0 + while size > 1024: + suffixIndex += 1 # increment the index of the suffix + size = float(size / 1024.0) # apply the division + defPrecision = precision + return "%.*f%s" % (defPrecision, size, suffixes[suffixIndex]) if __name__ == "__main__": opts = parse_options() + speed, size, time = (None, None, None) - if opts.rate: - speed = parse_rate(opts.speed.lower()) - if speed == None: - print_err("Cannot parse rate ( ex. 3.5s/kb )") - print_err("Rate: %s" % (opts.speed,)) - sys.exit(1) - else: + if opts.rate is not None: + speed = parse_rate(opts.rate.lower()) + if speed is None: + raise ValueError("Cannot parse rate ( ex. 3.5s/kb ), Rate: %s" % (opts.speed,)) + + if opts.speed is not None: speed = parse_speed(opts.speed.lower()) - if speed == None: - print_err("Cannot parse speed ( ex. 3.5Mb/s )") - print_err("Speed: %s" % (opts.speed,)) - sys.exit(1) + if speed is None: + raise ValueError("Cannot parse speed ( ex. 3.5Mb/s ), Speed: %s" % (opts.speed,)) - if os.path.isfile(opts.size): - size = get_files_size(opts.size) - else: - size = parse_size(opts.size.lower()) + if opts.size is not None: + if os.path.exists(opts.size): + size = get_files_size(opts.size) + else: + size = parse_size(opts.size.lower()) - if size == None: - print_err( - "Cannot parse size, and it's not a path either ( ex. 11Gb / file.name )" - ) - print_err("Size: %s" % (opts.size,)) - sys.exit(1) + if size is None: + raise ValueError( + "Cannot parse size, and it's not a path either ( ex. 11Gb / file.name ), Size: %s" % (opts.size,) + ) - print(time_human(round(size / speed))) + if opts.time is not None: + time = parse_time(opts.time.lower()) + + if time is None: + print(f"Transfer time: {time_human(round(size / speed))}") + if size is None: + print(f"Transferred size: {size_human(speed * time)}") + if speed is None: + print(f"Transfer speed: {size_human(size/time)}/s")