Skip to content

Commit

Permalink
Merge pull request #93 from vesellov/master
Browse files Browse the repository at this point in the history
improvements in file management
  • Loading branch information
vesellov authored Sep 6, 2024
2 parents e7cf271 + 642706e commit 6234a4e
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 14 deletions.
31 changes: 29 additions & 2 deletions src/components/file_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,18 @@ def __init__(self, **kwargs):
self.index_by_global_id = {}
self.file_clicked_callback = None
self.file_system_type = None
self.filesize_units = ('B', 'KB', 'MB', 'GB', 'TB')

def init(self, file_system_type, key_id=None, file_clicked_callback=None):
if _Debug:
print('DistributedFileChooserListView.init file_system_type=%r key_id=%r' % (file_system_type, key_id, ))
self.file_system_type = file_system_type
self.file_clicked_callback = file_clicked_callback
self.file_system.key_id = key_id
api_client.add_model_listener('remote_version', listener_cb=self.on_remote_version)
if self.file_system_type == 'private':
api_client.add_model_listener('private_file', listener_cb=self.on_private_file)
elif self.file_system_type == 'shared':
self.file_system.key_id = key_id
api_client.add_model_listener('shared_file', listener_cb=self.on_shared_file)

def shutdown(self):
Expand All @@ -277,8 +278,15 @@ def shutdown(self):
self.close()

def open(self):
if _Debug:
print('DistributedFileChooserListView.open')
self.opened = True
self._trigger_update()
if self.file_system_type == 'private':
api_client.request_model_data('private_file')
elif self.file_system_type == 'shared':
api_client.request_model_data('shared_file', query_details={'key_id': self.file_system.key_id, })
api_client.request_model_data('remote_version', query_details={'key_id': self.file_system.key_id, })

def close(self):
if _Debug:
Expand Down Expand Up @@ -341,10 +349,15 @@ def on_remote_version(self, payload):
if _Debug:
print('DistributedFileChooserListView.on_remote_version SKIP', payload)
return
remote_path = payload['data']['remote_path']
global_id = payload['data']['global_id']
if _Debug:
print('DistributedFileChooserListView.on_remote_version', global_id, payload)
if payload.get('deleted'):
if _Debug:
print(' updating files versions')
self._update_files()
return
remote_path = payload['data'].get('remote_path')
if self.file_system_type == 'shared':
if not global_id.startswith(self.file_system.key_id):
return
Expand Down Expand Up @@ -405,6 +418,20 @@ def get_condition(self, fn):
return ''
return self.file_system.get_condition(fn)

def get_nice_size(self, fn):
if self.file_system.is_dir(fn):
return ''
try:
size = self.file_system.getsize(fn)
except OSError:
return '--'
if not size:
return ''
for unit in self.filesize_units:
if size < 1024.0:
return '%1.0f %s' % (size, unit)
size /= 1024.0

def _trigger_update(self, *args):
if not self.opened:
return
Expand Down
7 changes: 6 additions & 1 deletion src/lib/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,12 @@ def make_nice_size(sz):


def make_nice_file_condition(file_info):
return '{}/{}'.format(file_info.get('delivered', '0%'), file_info.get('reliable', '0%'))
versions = file_info.get('versions', 1)
return '{}{}/{}'.format(
'' if versions <= 1 else 'X{} '.format(versions),
file_info.get('delivered', '0%'),
file_info.get('reliable', '0%'),
)

#------------------------------------------------------------------------------

Expand Down
9 changes: 9 additions & 0 deletions src/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ def IDUrlToGlobalID(idurl):
if port:
host = '%s_%s' % (host, port)
return '%s@%s' % (username, host)


def clean_remote_path(file_path):
"""
Full remote path have such format: [email protected]:myfiles/animals/cat.png
The last part of the remote_path (myfiles/animals/cat.png) must not have any special characters in order to support such format.
That method replaces special chararts of the file_path.
"""
return file_path.replace('$', '%24').replace('@', '%40').replace(':', '%3a')
31 changes: 30 additions & 1 deletion src/screens/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def __init__(self, app):
self.shared_files_index = {}
self.remote_versions_index = {}
self.remote_files_details = {}
self.my_global_id = None
self.my_idurl = None

def mw(self):
return self.app.main_window
Expand Down Expand Up @@ -303,6 +305,8 @@ def on_identity_get_result(self, resp):
self.mw().state_identity_get = -1
return
self.identity_get_latest = time.time()
self.my_global_id = api_client.result(resp).get('global_id')
self.my_idurl = api_client.result(resp).get('idurl')
self.mw().state_identity_get = 1 if api_client.is_ok(resp) else -1
self.run()

Expand Down Expand Up @@ -466,8 +470,32 @@ def on_model_update(self, json_data):
elif model_name == 'shared_file':
self.shared_files_index.pop(d['remote_path'], None)
elif model_name == 'remote_version':
self.remote_versions_index.pop(d['global_id'], None)
global_id = d['global_id']
_, _, version_id = d['backup_id'].rpartition('/')
if global_id in self.remote_versions_index:
self.remote_versions_index[global_id].pop(version_id, None)
self.remote_files_details.pop(d['global_id'], None)
sz = 0
delivered = 0.0
reliable = 0.0
total_file_versions = 0
for one_snap_id in self.remote_versions_index[global_id].values():
version_details = self.model_data['remote_version'].get(one_snap_id)
if version_details:
sz += version_details['data']['size']
delivered += float(version_details['data']['delivered'].replace('%', ''))
reliable += float(version_details['data']['reliable'].replace('%', ''))
total_file_versions += 1
if total_file_versions:
self.remote_files_details[global_id] = dict(
size=sz,
delivered=system.percent2string(delivered / total_file_versions),
reliable=system.percent2string(reliable / total_file_versions),
count=total_file_versions,
versions=total_file_versions,
)
else:
self.remote_files_details.pop(global_id, None)
else:
if snap_id not in self.model_data[model_name]:
self.model_data[model_name][snap_id] = {}
Expand Down Expand Up @@ -514,6 +542,7 @@ def _st(d):
delivered=system.percent2string(delivered / total_file_versions),
reliable=system.percent2string(reliable / total_file_versions),
count=total_file_versions,
versions=total_file_versions,
)
else:
self.remote_files_details.pop(global_id, None)
Expand Down
26 changes: 20 additions & 6 deletions src/screens/screen_private_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from lib import api_client
from lib import system
from lib import util

from components import screen
from components import snackbar
Expand Down Expand Up @@ -68,10 +69,14 @@ def get_statuses(self):

def populate(self, *args, **kwargs):
pass
# api_client.request_model_data('private_file')
# if self.control().my_global_id:
# api_client.request_model_data('remote_version', query_details={'key_id': 'master${}'.format(self.control().my_global_id), })

def on_created(self):
self.ids.files_list_view.init(
file_system_type='private',
key_id='master${}'.format(self.control().my_global_id) if self.control().my_global_id else None,
file_clicked_callback=self.on_file_clicked,
)

Expand All @@ -80,6 +85,7 @@ def on_destroying(self):

def on_enter(self, *args):
self.ids.state_panel.attach(automat_id='service_my_data', callback_start=self.on_state_panel_attach)
self.populate()

def on_leave(self, *args):
self.ids.files_list_view.close()
Expand Down Expand Up @@ -136,29 +142,37 @@ def on_upload_file_selected(self, *args, **kwargs):
pass
file_path = args[0][0]
file_name = os.path.basename(file_path)
remote_path = util.clean_remote_path(file_name)
if not system.is_android():
if not os.path.isfile(file_path):
if _Debug:
print('PrivateFilesScreen.on_upload_file_selected file do not exist', file_path)
snackbar.error(text='file path not found: %r' % file_path)
return
api_client.file_create(
remote_path=file_name,
remote_path=remote_path,
as_folder=False,
exist_ok=True,
cb=lambda resp: self.on_file_created(resp, file_path),
cb=lambda resp: self.on_file_created(resp, file_path, remote_path),
)

def on_file_created(self, resp, file_path):
def on_file_created(self, resp, file_path, remote_path):
if _Debug:
print('PrivateFilesScreen.on_file_created', file_path, resp)
print('PrivateFilesScreen.on_file_created', file_path, remote_path, resp)
if not api_client.is_ok(resp):
snackbar.error(text=api_client.response_err(resp))
return
file_name = os.path.basename(file_path)
api_client.file_upload_start(
local_path=file_path,
remote_path=file_name,
remote_path=remote_path,
cb=self.on_upload_file_started,
)

def on_upload_file_started(self, resp):
if _Debug:
print('PrivateFilesScreen.on_upload_file_started', resp)
if not api_client.is_ok(resp):
snackbar.error(text=api_client.response_err(resp))

def on_file_clicked(self, *args, **kwargs):
if _Debug:
Expand Down
10 changes: 9 additions & 1 deletion src/screens/screen_shared_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from lib import api_client
from lib import system
from lib import util

from components import screen
from components import snackbar
Expand Down Expand Up @@ -97,6 +98,7 @@ def on_destroying(self):

def on_enter(self, *args):
self.ids.state_panel.attach(automat_id=self.automat_id, callback_start=self.on_state_panel_attach)
self.populate()

def on_leave(self, *args):
self.ids.files_list_view.close()
Expand Down Expand Up @@ -161,7 +163,13 @@ def on_upload_file_selected(self, *args, **kwargs):
pass
file_path = args[0][0]
file_name = os.path.basename(file_path)
remote_path = '{}:{}'.format(self.key_id, file_name)
if not system.is_android():
if not os.path.isfile(file_path):
if _Debug:
print('SharedLocationScreen.on_upload_file_selected file do not exist', file_path)
snackbar.error(text='file path not found: %r' % file_path)
return
remote_path = '{}:{}'.format(self.key_id, util.clean_remote_path(file_name))
if _Debug:
print('SharedLocationScreen.on_upload_file_selected', args, kwargs, remote_path)
api_client.file_create(
Expand Down
1 change: 1 addition & 0 deletions src/screens/screen_single_private_file.kv
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
height: self.texture_size[1]
color: app.color_black
text: ''
on_ref_press: root.on_private_file_details_ref_pressed(*args)

FillRoundFlatButton:
id: download_file_button
Expand Down
45 changes: 44 additions & 1 deletion src/screens/screen_single_private_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
"""

version_info_text = """
[size={header_text_size}]{label}[/size]
[size={header_text_size}]{label} [u][color=#0000ff][ref=download_{backup_id}]download[/ref][/color][/u][/size]
[color=#909090] ID:[/color] {backup_id}
[color=#909090] size:[/color] {size}
[color=#909090] fragments:[/color] {fragments}
[color=#909090] delivered:[/color] {delivered}
[color=#909090] reliable:[/color] {reliable}
Expand Down Expand Up @@ -86,6 +88,7 @@ def populate(self, **kwargs):
versions_text = ''
for v in ctx['versions']:
v['header_text_size'] = '{}sp'.format(self.app().font_size_medium_absolute)
v['size'] = system.get_nice_size(v.get('size', 0))
versions_text += version_info_text.format(**v)
ctx['versions_text'] = versions_text
self.ids.private_file_details.text = private_file_info_text.format(**ctx)
Expand All @@ -96,9 +99,11 @@ def populate(self, **kwargs):

def on_enter(self, *args):
self.ids.state_panel.attach(automat_id='service_my_data')
api_client.add_model_listener('remote_version', listener_cb=self.on_remote_version)
self.populate()

def on_leave(self, *args):
api_client.remove_model_listener('remote_version', listener_cb=self.on_remote_version)
self.ids.state_panel.release()

def on_download_file_button_clicked(self):
Expand Down Expand Up @@ -160,3 +165,41 @@ def on_open_file_button_clicked(self):
else:
if self.local_path and os.path.exists(self.local_path):
system.open_path_in_os(self.local_path)

def on_remote_version(self, payload):
if _Debug:
print('SinglePrivateFileScreen.on_remote_version', payload)
remote_path = payload['data']['remote_path']
global_id = payload['data']['global_id']
if remote_path == self.remote_path and global_id == self.global_id:
api_client.file_info(
remote_path=remote_path,
cb=lambda resp: self.on_private_file_info_result(resp, remote_path, global_id),
)

def on_private_file_info_result(self, resp, remote_path, global_id):
if _Debug:
print('SinglePrivateFileScreen.on_private_file_info_result', remote_path, global_id)
if not api_client.is_ok(resp):
snackbar.error(text=api_client.response_err(resp))
return
self.details = api_client.response_result(resp)
self.populate()

def on_private_file_details_ref_pressed(self, *args):
if _Debug:
print('SinglePrivateFileScreen.on_private_file_details_ref_pressed', args)
if args[1].startswith('download_'):
backup_id = args[1][9:]
destination_path = None
if system.is_android():
from android.storage import app_storage_path # @UnresolvedImport
destination_path = tempfile.mkdtemp(dir=app_storage_path())
if not os.path.exists(destination_path):
os.makedirs(destination_path)
api_client.file_download_start(
remote_path=backup_id,
destination_path=destination_path,
wait_result=True,
cb=lambda resp: self.on_file_download_result(resp, destination_path),
)
1 change: 1 addition & 0 deletions src/screens/screen_single_shared_file.kv
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
height: self.texture_size[1]
color: app.color_black
text: ''
on_ref_press: root.on_shared_file_details_ref_pressed(*args)

FillRoundFlatButton:
id: download_file_button
Expand Down
Loading

0 comments on commit 6234a4e

Please sign in to comment.