From 8453468459b3977aceabb7e6ff14add704f74f2d Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Thu, 13 Jan 2022 22:47:25 +0100 Subject: [PATCH] add csv export for #16 --- catcli/catcli.py | 22 +++++++++++++++++++- catcli/logger.py | 23 +++++++++++++++++---- catcli/noder.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/catcli/catcli.py b/catcli/catcli.py index 431c195..05451d6 100755 --- a/catcli/catcli.py +++ b/catcli/catcli.py @@ -45,6 +45,7 @@ Usage: {1} rename [--catalog=] [-BCfV] {1} edit [--catalog=] [-BCfV] {1} graph [--catalog=] [-BCV] [] + {1} export [--catalog=] [-BH] [--format=] [] {1} help {1} --help {1} --version @@ -57,7 +58,9 @@ Options: -b --script Output script to manage found file(s) [default: False]. -C --no-color Do not output colors [default: False]. -c --hash Calculate md5 hash [default: False]. - -d --directory Only directory (default: False). + -d --directory Only directory [default: False]. + -F --format= Export format [default: csv]. + -H --header Export with header [default: False]. -f --force Do not ask when updating the catalog [default: False]. -l --lpath= Path where changes are logged [default: ] -n --no-subsize Do not store size of directories [default: False]. @@ -202,6 +205,21 @@ def cmd_rename(args, noder, catalog, top): return top +def cmd_export(args, noder, catalog, top): + path = args[''] + node = top + if path: + node = noder.get_node(top, path) + + header = args['--header'] + + fmt = args['--format'] + if fmt == 'csv': + noder.to_csv(node, with_header=header) + else: + Logger.err('Format not supported: {}'.format(fmt)) + + def cmd_edit(args, noder, catalog, top): storage = args[''] storages = list(x.name for x in top.children) @@ -276,6 +294,8 @@ def main(): cmd_rename(args, noder, catalog, top) elif args['edit']: cmd_edit(args, noder, catalog, top) + elif args['export']: + cmd_export(args, noder, catalog, top) return True diff --git a/catcli/logger.py b/catcli/logger.py index 8500a97..1f1dbef 100644 --- a/catcli/logger.py +++ b/catcli/logger.py @@ -48,13 +48,15 @@ class Logger: if attr: end = ' {}({}){}'.format(Logger.GRAY, attr, Logger.RESET) s = '{}{}{}{}:'.format(pre, Logger.UND, Logger.STORAGE, Logger.RESET) - s += ' {}{}{}{}\n'.format(Logger.PURPLE, name, Logger.RESET, end) + s += ' {}{}{}{}\n'.format(Logger.PURPLE, + _fix_badchars(name), + Logger.RESET, end) s += ' {}{}{}'.format(Logger.GRAY, args, Logger.RESET) sys.stdout.write('{}\n'.format(s)) def file(pre, name, attr): '''print a file node''' - s = '{}{}'.format(pre, name) + s = '{}{}'.format(pre, _fix_badchars(name)) s += ' {}[{}]{}'.format(Logger.GRAY, attr, Logger.RESET) sys.stdout.write('{}\n'.format(s)) @@ -67,12 +69,14 @@ class Logger: end.append(' '.join(['{}:{}'.format(x, y) for x, y in attr])) if end: end = ' [{}]'.format(', '.join(end)) - s = '{}{}{}{}'.format(pre, Logger.BLUE, name, Logger.RESET) + s = '{}{}{}{}'.format(pre, Logger.BLUE, + _fix_badchars(name), Logger.RESET) s += '{}{}{}'.format(Logger.GRAY, end, Logger.RESET) sys.stdout.write('{}\n'.format(s)) def arc(pre, name, archive): - s = '{}{}{}{}'.format(pre, Logger.YELLOW, name, Logger.RESET) + s = '{}{}{}{}'.format(pre, Logger.YELLOW, + _fix_badchars(name), Logger.RESET) s += ' {}[{}:{}]{}'.format(Logger.GRAY, Logger.ARCHIVE, archive, Logger.RESET) sys.stdout.write('{}\n'.format(s)) @@ -82,34 +86,45 @@ class Logger: ###################################################################### def out(string): '''to stdout no color''' + string = _fix_badchars(string) sys.stdout.write('{}\n'.format(string)) def debug(string): '''to stderr no color''' + string = _fix_badchars(string) sys.stderr.write('[DBG] {}\n'.format(string)) def info(string): '''to stdout in color''' + string = _fix_badchars(string) s = '{}{}{}'.format(Logger.MAGENTA, string, Logger.RESET) sys.stdout.write('{}\n'.format(s)) def err(string): '''to stderr in RED''' + string = _fix_badchars(string) s = '{}{}{}'.format(Logger.RED, string, Logger.RESET) sys.stderr.write('{}\n'.format(s)) def progr(string): '''print progress''' + string = _fix_badchars(string) sys.stderr.write('{}\r'.format(string)) sys.stderr.flush() def bold(string): '''make it bold''' + string = _fix_badchars(string) return '{}{}{}'.format(Logger.BOLD, string, Logger.RESET) def flog(path, string, append=True): + string = _fix_badchars(string) mode = 'w' if append: mode = 'a' with open(path, mode) as f: f.write(string) + + +def _fix_badchars(line): + return line.encode('utf-8', 'ignore').decode('utf-8') diff --git a/catcli/noder.py b/catcli/noder.py index 8b793b4..396c77d 100644 --- a/catcli/noder.py +++ b/catcli/noder.py @@ -35,6 +35,7 @@ class Noder: TYPE_ARC = 'arc' TYPE_STORAGE = 'storage' TYPE_META = 'meta' + CSV_HEADER = 'name,type,path,size,md5' def __init__(self, debug=False, sortsize=False, arc=False): ''' @@ -280,6 +281,46 @@ class Noder: ############################################################### # printing ############################################################### + def _node_to_csv(self, node, sep=','): + ''' + print a node to csv + @node: the node to consider + ''' + if not node: + return '' + if node.type == self.TYPE_TOP: + return '' + if node.type == self.TYPE_STORAGE: + return '' + + out = [] + + # node name + out.append(node.name) + + # node type + out.append(node.type) + + # node full path + parents = self._get_parents(node) + storage = self._get_storage(node) + fullpath = os.path.join(storage.name, parents) + out.append(fullpath) + + # size + if node.size: + out.append(utils.human(node.size)) + else: + out.append('') + + # md5 if any + if node.md5: + out.append(node.md5) + else: + out.append('') + + return sep.join(['"' + o + '"' for o in out]) + def _print_node(self, node, pre='', withpath=False, withdepth=False, withstorage=False, recalcparent=False): @@ -376,6 +417,17 @@ class Noder: for pre, fill, node in rend: self._print_node(node, pre=pre, withdepth=True) + def to_csv(self, node, with_header=False): + '''print the tree to csv''' + if with_header: + Logger.out(self.CSV_HEADER) + + rend = anytree.RenderTree(node, childiter=self._sort_tree) + for _, _, node in rend: + line = self._node_to_csv(node) + if len(line) > 0: + Logger.out(line) + def to_dot(self, node, path='tree.dot'): '''export to dot for graphing''' anytree.exporter.DotExporter(node).to_dotfile(path)