diff --git a/catcli/catalog.py b/catcli/catalog.py index eee2704..67afe28 100644 --- a/catcli/catalog.py +++ b/catcli/catalog.py @@ -6,9 +6,10 @@ Class that represents the catcli catalog """ import os -from typing import Optional -from anytree.exporter import JsonExporter # type: ignore +from typing import Optional, List, Dict, Tuple, Union, Any +from anytree.exporter import JsonExporter, DictExporter # type: ignore from anytree.importer import JsonImporter # type: ignore +from anytree import AnyNode # type: ignore # local imports from catcli import nodes @@ -29,7 +30,7 @@ class Catalog: @debug: debug mode @force: force overwrite if exists """ - self.path = path + self.path = os.path.expanduser(path) self.debug = debug self.force = force self.metanode: Optional[NodeMeta] = None @@ -85,7 +86,8 @@ class Catalog: def _save_json(self, top: NodeTop) -> bool: """export the catalog in json""" self._debug(f'saving {top} to json...') - exp = JsonExporter(indent=2, sort_keys=True) + dexporter = DictExporter(attriter=attriter) + exp = JsonExporter(dictexporter=dexporter, indent=2, sort_keys=True) with open(self.path, 'w', encoding='UTF-8') as file: exp.write(top, file) self._debug(f'Catalog saved to json \"{self.path}\"') @@ -93,7 +95,7 @@ class Catalog: def _restore_json(self, string: str) -> Optional[NodeTop]: """restore the tree from json""" - imp = JsonImporter() + imp = JsonImporter(dictimporter=_DictImporter(debug=self.debug)) self._debug('import from string...') root = imp.import_(string) self._debug(f'Catalog imported from json \"{self.path}\"') @@ -103,3 +105,54 @@ class Catalog: top = NodeTop(root.name, children=root.children) self._debug(f'top imported: {top}') return top + + +class _DictImporter(): + + def __init__(self, + nodecls: AnyNode = AnyNode, + debug: bool = False): + self.nodecls = nodecls + self.debug = debug + + def import_(self, data: Dict[str, str]) -> AnyNode: + """Import tree from `data`.""" + return self.__import(data) + + def __import(self, data: Union[str, Any], + parent: AnyNode = None) -> AnyNode: + """overwrite parent imoprt""" + assert isinstance(data, dict) + assert "parent" not in data + attrs = dict(data) + # replace attr + attrs = back_attriter(attrs, debug=self.debug) + children: Union[str, Any] = attrs.pop("children", []) + node = self.nodecls(parent=parent, **attrs) + for child in children: + self.__import(child, parent=node) + return node + + +def back_attriter(adict: Dict[str, str], + debug: bool = False) -> Dict[str, str]: + """replace attribute on json restore""" + attrs = {} + for k, val in adict.items(): + if k == 'size': + if debug: + Logger.debug(f'changing {k}={val}') + k = 'nodesize' + attrs[k] = val + return attrs + + +def attriter(attrs: List[Tuple[str, Any]]) -> List[Tuple[str, Any]]: + """replace attribute on json save""" + newattr = [] + for attr in attrs: + k, val = attr + if k == 'nodesize': + k = 'size' + newattr.append((k, val)) + return newattr diff --git a/catcli/fuser.py b/catcli/fuser.py index 935a663..50cf6be 100644 --- a/catcli/fuser.py +++ b/catcli/fuser.py @@ -74,21 +74,21 @@ class CatcliFilesystem(fuse.LoggingMixIn, fuse.Operations): # type: ignore maccess = time() mode: Any = S_IFREG - size: int = 0 + nodesize: int = 0 if entry.type == nodes.TYPE_ARCHIVED: mode = S_IFREG - size = entry.size + nodesize = entry.nodesize elif entry.type == nodes.TYPE_DIR: mode = S_IFDIR - size = entry.size + nodesize = entry.nodesize maccess = entry.maccess elif entry.type == nodes.TYPE_FILE: mode = S_IFREG - size = entry.size + nodesize = entry.nodesize maccess = entry.maccess elif entry.type == nodes.TYPE_STORAGE: mode = S_IFDIR - size = entry.size + nodesize = entry.nodesize maccess = entry.ts elif entry.type == nodes.TYPE_META: mode = S_IFREG @@ -98,7 +98,7 @@ class CatcliFilesystem(fuse.LoggingMixIn, fuse.Operations): # type: ignore return { 'st_mode': (mode), # file type 'st_nlink': 1, # count hard link - 'st_size': size, + 'st_size': nodesize, 'st_ctime': maccess, # attr last modified 'st_mtime': maccess, # content last modified 'st_atime': maccess, # access time diff --git a/catcli/noder.py b/catcli/noder.py index b53e7ee..d0ab9d4 100644 --- a/catcli/noder.py +++ b/catcli/noder.py @@ -138,29 +138,31 @@ class Noder: @store: store the size in the node """ if node.type == nodes.TYPE_FILE: - self._debug(f'size of {node.type} \"{node.name}\": {node.size}') - return node.size + node.__class__ = NodeFile + msg = f'size of {node.type} \"{node.name}\": {node.nodesize}' + self._debug(msg) + return node.nodesize msg = f'getting node size recursively for \"{node.name}\"' self._debug(msg) - size: int = 0 + fullsize: int = 0 for i in node.children: if node.type == nodes.TYPE_DIR: sub_size = self.rec_size(i, store=store) if store: - i.size = sub_size - size += sub_size + i.nodesize = sub_size + fullsize += sub_size continue if node.type == nodes.TYPE_STORAGE: sub_size = self.rec_size(i, store=store) if store: - i.size = sub_size - size += sub_size + i.nodesize = sub_size + fullsize += sub_size continue self._debug(f'skipping {node.name}') if store: - node.size = size - self._debug(f'size of {node.type} \"{node.name}\": {size}') - return size + node.nodesize = fullsize + self._debug(f'size of {node.type} \"{node.name}\": {fullsize}') + return fullsize ############################################################### # public helpers @@ -261,7 +263,7 @@ class Noder: parent: str, archive: str) -> NodeArchived: """create a new node for archive data""" return NodeArchived(name=name, relpath=path, - parent=parent, size=0, md5='', + parent=parent, nodesize=0, md5='', archive=archive) ############################################################### @@ -351,7 +353,7 @@ class Noder: fullpath = os.path.join(storage.name, parents) out.append(fullpath.replace('"', '""')) # full path - out.append(size_to_str(node.size, raw=raw)) # size + out.append(size_to_str(node.nodesize, raw=raw)) # size out.append(epoch_to_str(storage.ts)) # indexed_at if self._has_attr(node, 'maccess'): out.append(epoch_to_str(node.maccess)) # maccess @@ -392,9 +394,11 @@ class Noder: """ if node.type == nodes.TYPE_TOP: # top node + node.__class__ = NodeTop Logger.stdout_nocolor(f'{pre}{node.name}') elif node.type == nodes.TYPE_FILE: # node of type file + node.__class__ = NodeFile name = node.name if withpath: if recalcparent: @@ -407,7 +411,7 @@ class Noder: attr_str = '' if node.md5: attr_str = f', md5:{node.md5}' - size = size_to_str(node.size, raw=raw) + size = size_to_str(node.nodesize, raw=raw) compl = f'size:{size}{attr_str}' if withstorage: content = Logger.get_bold_text(storage.name) @@ -415,6 +419,7 @@ class Noder: NodePrinter.print_file_native(pre, name, compl) elif node.type == nodes.TYPE_DIR: # node of type directory + node.__class__ = NodeDir name = node.name if withpath: if recalcparent: @@ -428,13 +433,14 @@ class Noder: if withstorage: storage = self._get_storage(node) attr: List[Tuple[str, str]] = [] - if node.size: - attr.append(('totsize', size_to_str(node.size, raw=raw))) + if node.nodesize: + attr.append(('totsize', size_to_str(node.nodesize, raw=raw))) if withstorage: attr.append(('storage', Logger.get_bold_text(storage.name))) NodePrinter.print_dir_native(pre, name, depth=depth, attr=attr) elif node.type == nodes.TYPE_STORAGE: # node of type storage + node.__class__ = NodeStorage sztotal = size_to_str(node.total, raw=raw) szused = size_to_str(node.total - node.free, raw=raw) nbchildren = len(node.children) @@ -467,6 +473,7 @@ class Noder: node.attr) elif node.type == nodes.TYPE_ARCHIVED: # archive node + node.__class__ = NodeArchived if self.arc: NodePrinter.print_archive_native(pre, node.name, node.archive) else: @@ -775,9 +782,9 @@ class Noder: def _sort_size(node: NodeAny) -> float: """sorting nodes by size""" try: - if not node.size: + if not node.nodesize: return 0 - return float(node.size) + return float(node.nodesize) except AttributeError: return 0 diff --git a/catcli/nodes.py b/catcli/nodes.py index 0f96c45..eb42e81 100644 --- a/catcli/nodes.py +++ b/catcli/nodes.py @@ -84,7 +84,7 @@ class NodeFile(NodeAny): def __init__(self, # type: ignore[no-untyped-def] name: str, relpath: str, - size: int, + nodesize: int, md5: str, maccess: float, parent=None, @@ -94,7 +94,7 @@ class NodeFile(NodeAny): self.name = name self.type = TYPE_FILE self.relpath = relpath - self.size = size + self.nodesize = nodesize self.md5 = md5 self.maccess = maccess self.parent = parent @@ -111,7 +111,7 @@ class NodeDir(NodeAny): def __init__(self, # type: ignore[no-untyped-def] name: str, relpath: str, - size: int, + nodesize: int, maccess: float, parent=None, children=None): @@ -120,7 +120,7 @@ class NodeDir(NodeAny): self.name = name self.type = TYPE_DIR self.relpath = relpath - self.size = size + self.nodesize = nodesize self.maccess = maccess self.parent = parent if children: @@ -136,7 +136,7 @@ class NodeArchived(NodeAny): def __init__(self, # type: ignore[no-untyped-def] name: str, relpath: str, - size: int, + nodesize: int, md5: str, archive: str, parent=None, @@ -146,7 +146,7 @@ class NodeArchived(NodeAny): self.name = name self.type = TYPE_ARCHIVED self.relpath = relpath - self.size = size + self.nodesize = nodesize self.md5 = md5 self.archive = archive self.parent = parent @@ -164,7 +164,7 @@ class NodeStorage(NodeAny): name: str, free: int, total: int, - size: int, + nodesize: int, ts: float, attr: str, parent=None, @@ -176,7 +176,7 @@ class NodeStorage(NodeAny): self.free = free self.total = total self.attr = attr - self.size = size + self.nodesize = nodesize self.ts = ts # pylint: disable=C0103 self.parent = parent if children: