starting rewrite

This commit is contained in:
Amber 2024-09-29 21:51:19 +02:00
parent 20ad01d4c0
commit 01a1b1a258
21 changed files with 184 additions and 62 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
*swp *swp
*.pyc
__pycache__

View File

@ -133,6 +133,13 @@ class Manager():
snapshot = _dump.load_snapshot(snapshot_dump_path) snapshot = _dump.load_snapshot(snapshot_dump_path)
return snapshot return snapshot
def get_unmerged_local_path(self, node):
node_path = node['path']
node_name = node['name']
unmerged_local_file_path = '%s%s' % (node_path, node_name)
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): def get_absolute_local_path(self, node):
node_path = node['path'] node_path = node['path']
node_name = node['name'] node_name = node['name']
@ -189,13 +196,31 @@ class Manager():
def get_remote_snap_diff(self): def get_remote_snap_diff(self):
pass pass
def store_fdiff(self, node, only_print=False):
'''
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)
filea_block_tag = '%s %s (local)' % (node['name'], node['cur_hash'])
fileb_block_tag = '%s %s (remote)' % (node['name'], node['remote_hash'])
rfile_buf = node.pop('remote_file_buf')
node_diff_path = self.get_tree_diff_path(node)
try:
os.makedirs(node_diff_path)
except FileExistsError:
print(f'{node_diff_path} already exists skip creation')
outfile = ''
if not only_print:
outfile = '%s%s' % (self.normalize_path(node_diff_path), node['name'])
fdiff.print_diff(node_local_path, rfile_buf, remove_diff_letters_code=False, outfile=outfile, filea_block_tag=filea_block_tag, fileb_block_tag=fileb_block_tag)
return node
def store_unmerged_diff(self, nodes): def store_unmerged_diff(self, nodes):
''' '''
node_path is the relative path node_path is the relative path
''' '''
# path = node['path']
# name = node['name']
unmerged_path = self.get_unmerged_path() unmerged_path = self.get_unmerged_path()
# unmerged_path = Path(self.local_path) # unmerged_path = Path(self.local_path)
# if not local_path.exists(): # if not local_path.exists():
@ -209,18 +234,8 @@ class Manager():
todump = [] todump = []
for node in nodes: for node in nodes:
node_local_path = self.get_absolute_local_path(node) n = self.store_fdiff(node)
filea_block_tag = '%s %s (local)' % (node['name'], node['cur_hash']) todump.append(n)
fileb_block_tag = '%s %s (remote)' % (node['name'], node['remote_hash'])
rfile_buf = node.pop('remote_file')
node_diff_path = self.get_tree_diff_path(node)
try:
os.makedirs(node_diff_path)
except FileExistsError:
print(f'{node_diff_path} already exists skip creation')
outfile = '%s%s' % (self.normalize_path(node_diff_path), node['name'])
fdiff.print_diff(node_local_path, rfile_buf, remove_diff_letters_code=False, outfile=outfile, filea_block_tag=filea_block_tag, fileb_block_tag=fileb_block_tag)
todump.append(node)
_dump.dump_snapshot(todump, path=unmerged_path, dump_file_name='.unmerged.json.gz') _dump.dump_snapshot(todump, path=unmerged_path, dump_file_name='.unmerged.json.gz')
@ -245,8 +260,6 @@ class Manager():
## node is a folder ## node is a folder
return agent.check_rfolder_status(absolute_remote_path) return agent.check_rfolder_status(absolute_remote_path)
# if node_type == 'f':
def copy_node(self, node): def copy_node(self, node):
pass pass
# node_type = node['type'] # node_type = node['type']
@ -258,6 +271,57 @@ class Manager():
# if node_type == 'd': # if node_type == 'd':
# pass # pass
def show_conflicts(self):
unmerged = self.load_unmerged_diff()
for n, unode in enumerate(unmerged):
i = n+1
path = unode['path']
name = unode['name']
remote_hash = unode['remote_hash']
last_type = unode['last_type']
print(f'{i}- path: \'{path}\', name: \'{name}\', last_type: \'{last_type}\' remote_hash: \'{remote_hash}\'')
def mark_conflicting_node_as_solved(self, node):
name = node['name']
path = node['path']
unmerged = self.load_unmerged_diff()
found = {}
for n, unode in enumerate(unmerged):
if unode['name'] == name and unode['path'] == path:
found = unode
break
else:
n = -1
if n == -1:
return 'No node conflicting'
remote_hash = found['remote_hash']
## if you mark the node as conflict solved
## you have incorporated the `remote_hash`
## you must verify this is the actual hash in the server side
rstatus = self.get_rnode_status(node)
rfile_buf, rhash, rexists = map(rstatus.get, ['iobuffer', 'hash', 'exists'])
if not (remote_hash == rhash):
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'] = self.get_absolute_local_path(found)
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
## found the node in unmerged path
unmerged_local_path = self.get_unmerged_local_path(node)
absolute_remote_path = self.get_absolute_remote_path(node)
print(f'force local node: {unmerged_local_path} to remote path: {absolute_remote_path}')
def sync(self): def sync(self):
@ -281,51 +345,73 @@ class Manager():
for node in changes: for node in changes:
node_name = node['name'] node_name = node['name']
node_path = node['path'] last_hash = node['last_hash']
node_current_type = node['current_type'] print(f'Checking local {node_name}')
node_last_type = node['last_type']
node_current_hash = node['cur_hash']
node_last_hash = node['last_hash']
if not (node_current_type == node_last_type): rstatus = self.get_rnode_status(node)
print(f'node {node_name} change type in local tree') rfile_buf, rhash, rexists = map(rstatus.get, ['iobuffer', 'hash', 'exists'])
if node_last_type == 'f':
# remote_file_path = '%s%s' % (node_path, node_name)
# remote_file_path = remote_file_path.replace('.%s' % (os.sep), self.remote_path)
remote_file_path = self.get_absolute_remote_path(node)
rfile_buf, rhash = agent.generate_file_hash_oversftp(remote_file_path, return_also_buffer=True)
if node_last_hash == rhash:
print(f'You can proceed to push {node_name} file it is not changed from the last version')
# local_file_path = '%s%s' % (node_path, node_name)
# local_file_path = local_file_path.replace('.%s' % (os.sep), self.local_path)
local_file_path = self.get_absolute_local_path(node)
agent.put(local_file_path, remote_file_path, lambda x,y: print(f'{local_file_path} copied correctly to remote'))
else:
print(f'{node_path} file it changed local hash: {node_last_hash}, remote hash {rhash}, you can\'t push it directly')
# self.store_unmerged_diff(node)
node['remote_hash'] = rhash node['remote_hash'] = rhash
node['remote_file'] = rfile_buf node['remote_file_buf'] = rfile_buf
unmerged.append(node)
if node_last_type == 'd': if not rexists or last_hash != rhash:
# remote_path = '%s%s' % (node_path, node_name) print(f'Put node {node_name} in unmerged, node not exists in remote or is changed from the last local snapshot')
# remote_path = remote_file_path.replace('.%s' % (os.sep), self.remote_path) unmerged.append(node)
remote_path = self.get_absolute_local_path(node)
rhash = agent.generate_tree_hash_oversftp(remote_path)
if node_last_hash == rhash:
print(f'You can proceed to push {node_name} folder it is not changed from the last version')
else: else:
print(f'{node_name} folder it changed from the last version, you can\'t push it directly') print(f'You can proceed to push {node_name} file it is not changed from the last version')
if unmerged: if unmerged:
self.store_unmerged_diff(unmerged) self.store_unmerged_diff(unmerged)
## managing added return
added = local_snap_diff.get('added') or []
for node in added:
## if node not exists in remote copy it recursively in remote
remote_file_path = self.get_absolute_remote_path(node)
agent.copy(node)
return local_snap_diff
# for node in changes:
# node_name = node['name']
# node_path = node['path']
# node_current_type = node['current_type']
# node_last_type = node['last_type']
# node_current_hash = node['cur_hash']
# node_last_hash = node['last_hash']
#
# if not (node_current_type == node_last_type):
# print(f'node {node_name} change type in local tree')
# if node_last_type == 'f':
# # remote_file_path = '%s%s' % (node_path, node_name)
# # remote_file_path = remote_file_path.replace('.%s' % (os.sep), self.remote_path)
# remote_file_path = self.get_absolute_remote_path(node)
# rfile_buf, rhash = agent.generate_file_hash_oversftp(remote_file_path, return_also_buffer=True)
# if node_last_hash == rhash:
# print(f'You can proceed to push {node_name} file it is not changed from the last version')
# # local_file_path = '%s%s' % (node_path, node_name)
# # local_file_path = local_file_path.replace('.%s' % (os.sep), self.local_path)
# local_file_path = self.get_absolute_local_path(node)
# agent.put(local_file_path, remote_file_path, lambda x,y: print(f'{local_file_path} copied correctly to remote'))
# else:
# print(f'{node_path} file it changed local hash: {node_last_hash}, remote hash {rhash}, you can\'t push it directly')
# # self.store_unmerged_diff(node)
# node['remote_hash'] = rhash
# node['remote_file'] = rfile_buf
# unmerged.append(node)
#
# if node_last_type == 'd':
# # remote_path = '%s%s' % (node_path, node_name)
# # remote_path = remote_file_path.replace('.%s' % (os.sep), self.remote_path)
# remote_path = self.get_absolute_local_path(node)
# rhash = agent.generate_tree_hash_oversftp(remote_path)
# if node_last_hash == rhash:
# print(f'You can proceed to push {node_name} folder it is not changed from the last version')
# else:
# print(f'{node_name} folder it changed from the last version, you can\'t push it directly')
#
# if unmerged:
# self.store_unmerged_diff(unmerged)
#
# ## managing added
# added = local_snap_diff.get('added') or []
# for node in added:
# ## if node not exists in remote copy it recursively in remote
# remote_file_path = self.get_absolute_remote_path(node)
# agent.copy(node)
#
# return local_snap_diff

23
src/lib/repr/node.py Normal file
View File

@ -0,0 +1,23 @@
from dataclasses import dataclass
@dataclass
class Node:
name: str
rel_path: str
last_type: str
cur_type:str
last_hash: str
cur_hash: str
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 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
@param cur_type is the current type in local tree
@param last_hash is the last hash received from server (a directory can have or empty hash or a subtree)
@param cur_hash is the current hash in local tree
@param remote_hash is the remote hash (usually computed with a remote call)
'''

View File

@ -1,15 +1,20 @@
import os import os
import io
import hashlib import hashlib
import json import json
import gzip import gzip
def generate_file_hash(file_path, hexdigest=True): def generate_file_hash(file_path, hexdigest=True, return_also_buffer=False):
with open(file_path, "rb") as f: with open(file_path, "rb") as f:
buf = f.read() buf = f.read()
if hexdigest: return hashlib.md5(buf).hexdigest() if hexdigest:
return hashlib.md5(buf).digest() if not return_also_buffer: return hashlib.md5(buf).hexdigest()
return (hashlib.md5(buf).hexdigest(), io.BytesIO(buf))
if not return_also_buffer: return hashlib.md5(buf).digest()
return (hashlib.md5(buf).digest(), io.BytesIO(buf))
def check_isdir(path: str): def check_isdir(path: str):
if not os.path.isdir(path): if not os.path.isdir(path):

View File

@ -67,17 +67,19 @@ class RHAgent():
dig = self.generate_tree_hash_oversftp(path) dig = self.generate_tree_hash_oversftp(path)
return { return {
'exists' : True, 'exists' : True,
# 'iobuffer' : iobuf, 'iobuffer' : None,
'hash': dig, 'hash': dig,
} }
except IOError as e: except IOError as e:
if e.errno == errno.ENOENT: return { if e.errno == errno.ENOENT: return {
'exists' : False, 'exists' : False,
# 'iobuffer' : None, 'iobuffer' : None,
'hash': None 'hash': None
} }
raise Exception(f'Something went wrong and strange {path}') raise Exception(f'Something went wrong and strange {path}')
def copy_node(self):
pass
def generate_tree_hash_oversftp(self, root_path :str): def generate_tree_hash_oversftp(self, root_path :str):

View File

@ -1,5 +1,9 @@
from iface import sync_manager from iface import sync_manager
def get_test_manager():
m = sync_manager.Manager(local_path='/home/luca/sharednotes_dev', remote_path='notanamber@myvps:/home/notanamber/notes_dev')
return m
def test_init_sync(): def test_init_sync():
m = sync_manager.Manager(local_path='/home/luca/sharednotes_dev', remote_path='notanamber@myvps:/home/notanamber/notes_dev') m = sync_manager.Manager(local_path='/home/luca/sharednotes_dev', remote_path='notanamber@myvps:/home/notanamber/notes_dev')
m.init_sync() m.init_sync()