#!/usr/bin/python3 from datetime import datetime, timedelta import argparse import os import sys import random import shutil import string from urllib.parse import quote DEL_TIME = '.delete_in' DESCRIPTION = '.description' def parse_opts(): class _HelpAction(argparse._HelpAction): def __call__(self, parser, namespace, values, option_string=None): parser.print_help() print("") # retrieve subparsers from parser subparsers_actions = [ action for action in parser._actions if isinstance(action, argparse._SubParsersAction) ] for subparsers_action in subparsers_actions: # get all subparsers and print help for choice, subparser in subparsers_action.choices.items(): print("Command: {}".format(choice)) print(subparser.format_help()) parser.exit() parser = argparse.ArgumentParser(add_help=False) parser.add_argument( '--help', '-h', action = _HelpAction, help = 'Show this help message and exit' ) parser.add_argument( '--verbose', '-v', action = "store_true", dest = "verbose", default = False, help = 'Increase verbosity' ) parser.add_argument( '--root', '-r', action = "store", dest = "root", type = str, default = "", help = 'Root address for printing URLS' ) subparsers = parser.add_subparsers( dest="command", help = "Command defaults to add" ) add_parser = subparsers.add_parser('add', add_help=False) add_parser.add_argument( "-d", action = "store", type = int, help = "Days to keep files", default = 30, dest = 'days' ) add_parser.add_argument( "-m", action = "store", type = str, help = "Describe share", default = "-", dest = 'description' ) add_parser.add_argument( "files", action = "store", type = str, help = "Copy files/folders under the new share", default = [], nargs = '*' ) list_parser = subparsers.add_parser('list', add_help=False) list_parser.add_argument( '--verbose', '-v', action = "store_true", dest = "verbose", default = False, help = 'Print individual files too' ) del_parser = subparsers.add_parser('del', add_help=False) return parser.parse_args() def random_char(): return random.choice(string.ascii_uppercase + string.digits) def random_name(): while True: existing_names = get_folder_names() index = 0 for existing in existing_names: try: e_index = int(existing[0:3]) index = max(e_index + 1, index) except: pass name = "{:03d}-{}-{}".format( index, "".join([random_char() for x in range(3)]), "".join([random_char() for x in range(3)]) ) if not os.path.exists(name): break return name def create_new(p, days, description): os.mkdir(p) with open(os.path.join(p, DEL_TIME), 'wt') as fp: fp.write(str(days)) with open(os.path.join(p, DESCRIPTION), 'wt') as fp: fp.write(description) def copy_files(cwd, new_name, files): target = os.path.abspath(new_name) for f in opts.files: print(f"Copying: {f}") source = os.path.join(cwd, f) if os.path.isfile(source): shutil.copy2( source, target ) else: shutil.copytree( source, os.path.join(target,f), symlinks = True, ) def get_description(f): try: with open(f) as fp: return "".join(filter(lambda x: x in string.printable, fp.read(32).split("\n")[0])) except: return "-" def get_stats(p): # TODO: named tuple try: with open(os.path.join(p, DEL_TIME), 'rt') as fp: do_del = int(fp.read(64)) desc = get_description(os.path.join(p, DESCRIPTION)) now = datetime.now() mtime = datetime.fromtimestamp( os.path.getmtime(os.path.join(p, DEL_TIME)) ) del_delta = timedelta(days = do_del) del_time = mtime + del_delta to_del_time = del_time - now is_due = now > del_time return mtime, del_time, to_del_time, is_due, desc except Exception as e: print(e) return (None, None, False, False, "-") def get_sub_files(p): file_list = sorted([x for x in os.listdir(p) if not x.startswith(".")]) dir_list = [x + "/" for x in file_list if os.path.isdir(os.path.join(p, x))] file_list = [x for x in file_list if os.path.isfile(os.path.join(p, x))] return dir_list + file_list def get_folder_names(): dir_list = [(p,os.path.getmtime(os.path.join(p, DEL_TIME))) for p in os.listdir(".") if os.path.exists(os.path.join(p, DEL_TIME))] dir_list.sort(key = lambda t: -t[1]) return [p[0] for p in dir_list] def list_folders(root_folder, verbose = False, filter_name = None): names = get_folder_names() print("Folder Created ToDelete InDays") for p in names: if filter_name is not None: if filter_name != p: continue stats = get_stats(p) sub_files = get_sub_files(p) due = "*" if stats[3] else " " print("{}/{}/ {} {} {: 4d}d{} {}".format( root_folder, p, stats[0].isoformat()[0:10], stats[1].isoformat()[0:10], stats[2].days, due, stats[4] )) if verbose: for sp in sub_files: print(" {}/{}/{}".format(root_folder,p,quote(sp,"/"))) #print(p, stats, sub_files) def del_due_folders(): names = get_folder_names() for p in names: stats = get_stats(p) due = stats[3] if due: print("Deleting {}".format(p)) shutil.rmtree(p) if __name__ == '__main__': opts = parse_opts() cwd = os.getcwd() os.chdir(os.path.dirname(__file__)) if opts.command == 'add': new_name = random_name() create_new(new_name, opts.days, opts.description) print(os.path.abspath(new_name)) print("") copy_files(cwd, new_name, opts.files) list_folders(opts.root, verbose = True, filter_name = new_name) if opts.command == "list" or opts.command is None: list_folders(opts.root, verbose = opts.verbose) if opts.command == "del": del_due_folders()