node as Class

This commit is contained in:
Amber 2024-12-29 16:56:06 +01:00
parent b552ccd298
commit bdba95d9c2
2 changed files with 117 additions and 96 deletions

View File

@ -4,6 +4,7 @@ import os
import subprocess
from pathlib import Path
from lib.repr.node import node_factory
from lib.snapshot.generate import local as _genlocal
from lib.snapshot import dump as _dump
# from lib.snapshot.generate.remote import RHAgent
@ -18,6 +19,7 @@ from lib.utils import node as _node_utils
from tools import colors
from iface import snap
# from iface import Node
DEFAULT_MANAGER_CONF = './conf/manager.conf'
@ -154,20 +156,6 @@ class Manager():
unmerged_file_path = unmerged_local_file_path.replace('.%s' % (os.sep), self.get_unmerged_path())
return unmerged_file_path
def get_absolute_local_path(self, node):
node_path = self.normalize_path(node['rel_path'])
node_name = node['name']
local_file_path = '%s%s' % (node_path, node_name)
local_file_path = local_file_path.replace('.%s' % (os.sep), self.local_path)
return local_file_path
def get_absolute_remote_path(self, node):
node_path = self.normalize_path(node['rel_path'])
node_name = node['name']
remote_file_path = '%s%s' % (node_path, node_name)
remote_file_path = remote_file_path.replace('.%s' % (os.sep), self.remote_path)
return remote_file_path
def get_remote_mount_point(self, node):
node_path = node['rel_path']
remote_mount_point = node_path.replace('.%s' % (os.sep), self.remote_path)
@ -229,7 +217,8 @@ class Manager():
store in file diff path `.masy/diff` the tree with diffs
file is containing the diff between local and remote
'''
node_local_path = self.get_absolute_local_path(node)
LNode = node_factory(self.local_path, node)
node_local_path = LNode.absolute_path()
filea_block_tag = '%s %s (local)' % (node['name'], node['cur_hash'])
fileb_block_tag = '%s %s (remote)' % (node['name'], node['remote_hash'])
# rfile_buf = ''
@ -284,11 +273,15 @@ class Manager():
def get_rnode_status(self, node):
'''
we start from a local node
gived a node check the remote status
- node exists in remote
- hash changed
'''
absolute_remote_path = self.get_absolute_remote_path(node)
LNode = node_factory(self.local_path, node)
RNode = node_factory(self.remote_path, node)
absolute_remote_path = RNode.absolute_path()
agent = self.get_agent()
## if node not exist in the local tree there is not last_type
## we use cur_type (is this correct?)
@ -303,8 +296,12 @@ class Manager():
use put of sftp client
put(localpath, remotepath, callback=None, confirm=True)
'''
absolute_local_path = self.get_absolute_local_path(node)
absolute_remote_path = self.get_absolute_remote_path(node)
LNode = node_factory(self.local_path, node)
RNode = node_factory(self.remote_path, node)
absolute_local_path = LNode.absolute_path()
absolute_remote_path = RNode.absolute_path()
agent = self.get_agent()
if node['cur_type'] == 'f':
## simply put
@ -316,86 +313,39 @@ class Manager():
return agent.copy_dir(absolute_local_path, remote_mount_point)
def remove_node_remotely(self, node):
absolute_remote_path = self.get_absolute_remote_path(node)
RNode = node_factory(self.remote_path, node)
absolute_remote_path = RNode.absolute_path()
agent = self.get_agent()
if node['last_type'] == 'f':
if RNode.is_file():
## simply put
return agent.remove_file(absolute_remote_path)
elif node['last_type'] == 'd':
elif RNode.is_dir():
#cur_hash contains the entire subtree to recreate on remote
## it is better to remove the root node if already exists on remote
# remote_mount_point = self.get_remote_mount_point(node)
return agent.remove_dir(absolute_remote_path)
#maybe useless
# def precheck_remote_unode(self, node):
# '''
# Check the unmerged node on remote before solve conflict
# Consideration about unode:
# 1. We have generated `cur_hash` starting from `last_hash` version of node on local tree
# 2. We are aware on remote the hash is changed at the moment we `sync`, the value is stored in `remote_hash`
#
# we expect
# `remote_hash` is unchanged, then we can proceed with solving the conflict
# `remote_hash` is changed, then we can generate a new conflict with `remote_hash` equals to the new remote hash
#
# '''
# rstatus = self.get_rnode_status(node)
# rfile_buf, rhash, rexists = map(rstatus.get, ['iobuffer', 'hash', 'exists'])
#
# remote_hash = node['remote_hash']
#
# if not (remote_hash == rhash):
# unmerged = self.load_unmerged_diff()
# name = node['name']
# path = node['rel_path']
#
# unmerged = self.load_unmerged_diff()
#
# found = {}
# for n, unode in enumerate(unmerged):
# if unode['name'] == name and unode['rel_path'] == path:
# found = unode
# break
# else:
# n = -1
#
# if n == -1:
# raise Exception('No node conflicting found!!')
#
# print(f'remote hash changed you...generate a new conflict')
# ## update the conflicting node by index
# # (cur_hash, cur_buf) = _genlocal.generate_file_hash(self.get_absolute_local_path(found))
# found['cur_hash'] = cur_hash
# found['last_hash'] = found['remote_hash']
# found['remote_hash'] = rhash
# found['remote_file_buf'] = rfile_buf
# self.store_unmerged_diff(unmerged)
# ## store diff in diffs path
# self.store_fdiff(found)
# return False, 'Generate conflict'
#
# return True, 'You can proceed'
def show_conflicts(self):
unmerged = self.load_unmerged_diff()
for n, unode in enumerate(unmerged):
i = n+1
absolute_local_path = colors.CYAN(self.get_absolute_local_path(unode))
name = unode['name']
remote_hash = unode['remote_hash']
last_type = unode.get('last_type')
local_hash = unode['cur_hash']
cur_type = unode['cur_type']
last_hash = unode['last_hash']
if remote_hash is None:
remote_hash = colors.RED('file not present in remote')
elif local_hash is None:
local_hash = colors.RED('file not present local')
LNode = node_factory(self.local_path, unode)
absolute_local_path = colors.CYAN(LNode.absolute_path())
name = LNode.name
remote_hash = LNode.remote_hash
node_type = LNode.get_type()
node_action = LNode.get_action()
local_hash = LNode.cur_hash
last_hash = LNode.last_hash if LNode.last_hash is not None else 'File was not present'
print(f'{i}- path: \'{absolute_local_path}\' ({cur_type}) \n local_hash: \'{last_hash}\' \u21E8 \'{local_hash}\', \n remote_hash: \'{last_hash}\' \u21E8 \'{remote_hash}\'')
if remote_hash is None:
remote_hash = colors.RED('File not present in remote')
elif local_hash is None:
local_hash = colors.RED('File not present local')
print(f'{i}- path: \'{absolute_local_path}\' ({node_type} - {node_action}) \n local: \'{last_hash}\' \u21E8 \'{local_hash}\',\
\n remote: \'{last_hash}\' \u21E8 \'{remote_hash}\'')
def manual_solve_conflict(self, n, version_kept):
i = n-1
@ -414,8 +364,10 @@ class Manager():
self.store_unmerged_diff(unmerged)
return
absolute_local_path = colors.CYAN(self.get_absolute_local_path(node))
absolute_remote_path = colors.CYAN(self.get_absolute_remote_path(node))
LNode = node_factory(self.local_path, node)
RNode = node_factory(self.remote_path, node)
absolute_local_path = colors.CYAN(LNode.absolute_path())
absolute_remote_path = colors.CYAN(RNode.absolute_path())
if version_kept == 'keep_local':
# before copy check the remote
@ -628,24 +580,28 @@ class Manager():
def compute_local_node_hash(self, n):
## we must compute n['cur_hash']
path = self.get_absolute_local_path(n)
LNode = node_factory(self.local_path, n)
absolute_local_path = LNode.absolute_path()
if n['last_type'] == 'd':
cur_hash = _genlocal.generate_tree_hash(path)
cur_hash = _genlocal.generate_tree_hash(absolute_local_path)
else:
cur_hash = _genlocal.generate_file_hash(path)
cur_hash = _genlocal.generate_file_hash(absolute_local_path)
return cur_hash
def copy_node_from_remote(self, n):
a = self.get_agent()
last_type = n['last_type']
absolute_remote_path = self.get_absolute_remote_path(n)
# absolute_remote_path = self.get_absolute_remote_path(n)
RNode = node_factory(self.remote_path, n)
absolute_remote_path = RNode.absolute_path()
if last_type == 'd':
local_mount_point = self.get_local_mount_point(n)
return a.get_dir(absolute_remote_path, local_mount_point)
absolute_local_path = self.get_absolute_local_path(n)
LNode = node_factory(self.local_path, n)
absolute_local_path = LNode.absolute_path()
return a.get_file(absolute_remote_path, absolute_local_path)

View File

@ -1,18 +1,25 @@
import os
from dataclasses import dataclass
from lib.utils import path_utils as _path_utils
@dataclass
class Node:
base_path: str
name: str
rel_path: str
last_type: str
cur_type:str
last_hash: str
cur_hash: str
last_type: str = None
cur_type:str = None
last_hash: str = None
cur_hash: str = None
remote_hash: str = None
'''
A node is a representation of a node within the tree
that we wish to keep synchronised with remote source
@param base_path base_path of node: NOTE can be local or remote
@param name name of node
@param rel_path relative path of node respect of root of shared folder
@param last_type is the last type received from server
@ -23,3 +30,61 @@ class Node:
no conflict -> `remote_hash` == `last_hash`
conflict -> `remote_hash` != `last_hash` this means we have lost a version of node
'''
'''
compute the action actually binded to node
can be -> deletion | addition | change
'''
def is_node_added(self):
return self.last_hash is None and self.cur_hash is not None
def is_node_deleted(self):
return self.cur_hash is None and self.last_hash is not None
def is_node_changed(self):
return self.cur_hash is not None and self.last_hash is not None
def get_action(self):
if self.is_node_added():
return 'addition'
elif self.is_node_deleted():
return 'deletion'
elif self.is_node_changed():
return 'changed'
def absolute_path(self):
rel_path = self.rel_path
name = self.name
file_path = '%s%s' % (rel_path, name)
file_path = file_path.replace('.%s' % (os.sep), self.base_path)
return file_path
def is_file(self):
if self.is_node_deleted():
return self.last_type == 'f'
## if is_node_added or is_node_changed
return self.cur_type == 'f'
def is_dir(self):
if self.is_node_deleted():
return self.last_type == 'd'
## if is_node_added or is_node_changed
return self.cur_type == 'd'
def get_type(self):
if self.is_file: return 'f'
return 'd'
def node_factory(base_path, d):
base_path = _path_utils.normalize_path(base_path)
name = d['name']
rel_path = _path_utils.normalize_path(d['rel_path'])
last_type = d.get('last_type')
cur_type = d.get('cur_type')
last_hash = d.get('last_hash')
cur_hash = d.get('cur_hash')
remote_hash = d.get('remote_hash')
# action = d.get('action')
return Node(base_path, name, rel_path, last_type, cur_type, last_hash, cur_hash, remote_hash)