Skip to content

Commit baf1e32

Browse files
committed
sync methods
1 parent 3fa4545 commit baf1e32

File tree

5 files changed

+94
-120
lines changed

5 files changed

+94
-120
lines changed

mergin/cli.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@
3333
download_project_is_running,
3434
)
3535
from mergin.client_pull import pull_project_async, pull_project_is_running, pull_project_finalize, pull_project_cancel
36-
from mergin.client_push import push_next_change, push_project_is_running, push_project_finalize, push_project_cancel, push_project_async
37-
from mergin.client_sync import Syncer, calculate_uploads_size
36+
from mergin.client_push import push_project_is_running, push_project_finalize, push_project_cancel, push_project_async, total_upload_size
3837

3938
from pygeodiff import GeoDiff
4039

@@ -411,8 +410,8 @@ def sync(ctx):
411410
if mc is None:
412411
return
413412
directory = os.getcwd()
414-
syncer = Syncer(mc, directory)
415-
size = syncer.estimate_total_upload()
413+
414+
size = total_upload_size(directory)
416415
if size == 0:
417416
click.secho("Already up to date.", fg="green")
418417
return
@@ -428,8 +427,8 @@ def on_progress(last, now):
428427
uploaded_so_far += delta
429428
bar.update(delta)
430429

431-
# run pull push cycles
432-
syncer.sync_loop(progress_callback=on_progress)
430+
# run pull & push cycles
431+
mc.sync_project_with_callback(directory, progress_callback=on_progress)
433432

434433
click.secho("Sync complete.", fg="green")
435434

@@ -440,7 +439,8 @@ def on_progress(last, now):
440439
return
441440
except KeyboardInterrupt:
442441
click.secho("Cancelling...")
443-
syncer.cancel()
442+
push_project_cancel(mc.upload_job)
443+
pull_project_cancel(mc.download_job)
444444
except Exception as e:
445445
_print_unhandled_exception()
446446

mergin/client.py

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import logging
2+
from time import sleep
3+
24
import math
35
import os
46
import json
@@ -19,7 +21,6 @@
1921

2022
from typing import List
2123

22-
from .client_sync import Syncer
2324
from .common import ClientError, LoginError, WorkspaceRole, ProjectRole, LOG_FILE_SIZE_TO_SEND, MERGIN_DEFAULT_LOGS_URL
2425
from .merginproject import MerginProject
2526
from .client_pull import (
@@ -32,9 +33,13 @@
3233
download_project_finalize,
3334
download_project_wait,
3435
download_diffs_finalize,
36+
pull_project_is_running,
37+
pull_project_async,
38+
pull_project_wait,
39+
pull_project_finalize
3540
)
36-
from .client_pull import pull_project_async, pull_project_wait, pull_project_finalize
37-
from .client_push import push_next_change, push_project_wait, push_project_finalize, push_project_async
41+
from .client_push import push_project_wait, push_project_finalize, push_project_async, push_project_is_running, \
42+
ChangesHandler
3843
from .utils import DateTimeEncoder, get_versions_with_file_changes, int_version, is_version_acceptable
3944
from .version import __version__
4045

@@ -104,6 +109,8 @@ def __init__(
104109
if plugin_version is not None: # this could be e.g. "Plugin/2020.1 QGIS/3.14"
105110
self.client_version += " " + plugin_version
106111
self.setup_logging()
112+
self.pull_job = None
113+
self.push_job = None
107114
if auth_token:
108115
try:
109116
token_data = decode_token_data(auth_token)
@@ -891,11 +898,6 @@ def project_user_permissions(self, project_path):
891898
result["readers"] = access.get("readersnames", [])
892899
return result
893900

894-
895-
896-
897-
898-
899901
def push_project(self, directory):
900902
"""
901903
Upload local changes to the repository.
@@ -923,23 +925,56 @@ def pull_project(self, directory):
923925
pull_project_wait(job)
924926
return pull_project_finalize(job)
925927

928+
def sync_project(self, project_dir):
929+
"""
930+
Syncs project by loop with these steps:
931+
1. Pull server version
932+
2. Get local changes
933+
3. Push first change batch
934+
Repeat if there are more changes pending.
935+
"""
936+
has_more_changes = True
937+
while has_more_changes:
938+
self.pull_job = pull_project_async(self, project_dir)
939+
pull_project_wait(self.pull_job)
940+
pull_project_finalize(self.pull_job)
941+
942+
changes_handler = ChangesHandler(self, project_dir)
943+
changes_batch, has_more_changes = changes_handler.get_change_batch()
944+
945+
self.push_job = push_project_async(project_dir, changes_batch)
946+
push_project_wait(self.push_job)
947+
push_project_finalize(self.push_job)
948+
949+
def sync_project_with_callback(self, project_dir, progress_callback=None, sleep_time=100):
950+
"""
951+
Syncs project while sending push progress info as callback.
952+
Sync is done in this loop:
953+
Pending changes? -> Pull -> Push change batch -> repeat
954+
"""
955+
has_more_changes = True
956+
while has_more_changes:
957+
changes_handler = ChangesHandler(self, project_dir)
958+
959+
self.pull_job = pull_project_async(self, project_dir)
960+
if self.pull_job is None:
961+
return
962+
pull_project_wait(self.pull_job)
963+
pull_project_finalize(self.pull_job)
926964

927-
def sync_project(self, directory):
965+
changes_batch, has_more_changes = changes_handler.get_change_batch()
928966

929-
syncer = Syncer(self, directory)
930-
syncer.sync_loop()
967+
self.push_job = push_project_async(self, project_dir, changes_batch)
968+
if not self.push_job:
969+
return
931970

932-
# self.pull_project(directory)
933-
#
934-
# mp = MerginProject(directory)
935-
# changes = mp.get_push_changes()
936-
#
937-
# if not changes:
938-
# return
939-
#
940-
# push_changes(self, mp, changes)
941-
#
942-
# self.sync_project(directory)
971+
last = 0
972+
while push_project_is_running(self.push_job):
973+
sleep(sleep_time)
974+
now = self.push_job.transferred_size
975+
progress_callback(last, now)
976+
last = now
977+
push_project_finalize(self.push_job)
943978

944979

945980
def clone_project(self, source_project_path, cloned_project_name, cloned_project_namespace=None):

mergin/client_push.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ class ChangesHandler:
9696
- Generating upload-ready change groups for asynchronous job creation.
9797
"""
9898

99-
def __init__(self, client, project_info, changes):
99+
def __init__(self, client, mp):
100100
self.client = client
101-
self.project_info = project_info
102-
self._raw_changes = changes
101+
self.mp = MerginProject(mp)
102+
self._raw_changes = self.mp.get_push_changes()
103103

104104
@staticmethod
105105
def is_blocking_file(file):
@@ -117,7 +117,12 @@ def _filter_changes(self, changes: Dict[str, List[dict]]) -> Dict[str, List[dict
117117
Returns:
118118
dict[str, list[dict]]: The filtered changes dictionary.
119119
"""
120-
if not is_editor_enabled(self.client, self.project_info):
120+
project_name = self.mp.project_full_name()
121+
try:
122+
project_info = self.client.project_info(project_name)
123+
except Exception as e:
124+
self.mp.log.error(f"Failed to get project info for project {project_name}: {e}")
125+
if not is_editor_enabled(self.client, project_info):
121126
return changes
122127
return _apply_editor_filters(changes)
123128

@@ -159,8 +164,17 @@ def split(self) -> List[Dict[str, List[dict]]]:
159164
# TODO: apply limits; changes = self._limit_by_file_count(changes)
160165
return changes_list
161166

167+
def get_change_batch(self) -> Tuple[Optional[Dict[str, List[dict]]], bool]:
168+
"""
169+
Return the next changes dictionary and flag if there are more changes
170+
"""
171+
changes_list = self.split()
172+
if not changes_list:
173+
return None, False
174+
return changes_list[0], len(changes_list) > 1
175+
162176

163-
def push_project_async(mc, directory) -> Optional[UploadJob]:
177+
def push_project_async(mc, directory, change_batch=None) -> Optional[UploadJob]:
164178
"""Starts push of a change of a project and returns pending upload job"""
165179

166180
mp = MerginProject(directory)
@@ -413,9 +427,15 @@ def remove_diff_files(job) -> None:
413427
if os.path.exists(diff_file):
414428
os.remove(diff_file)
415429

416-
def get_next_batch(project_dir) -> Tuple[Dict[str], bool]:
430+
431+
def total_upload_size(directory) -> int:
417432
"""
418-
Return the next dictionary with changes, similar to changes[0] in push_project_async.
433+
Total up the number of bytes that need to be uploaded.
419434
"""
420-
# TODO
421-
return {"added": [], "updated": [], "removed": []}, True
435+
mp = MerginProject(directory)
436+
changes = mp.get_push_changes()
437+
files = changes.get("added", []) + changes.get("updated", [])
438+
return sum(
439+
f.get("diff", {}).get("size", f.get("size", 0))
440+
for f in files
441+
)

mergin/client_sync.py

Lines changed: 0 additions & 81 deletions
This file was deleted.

mergin/merginproject.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import uuid
1010
import tempfile
1111
from datetime import datetime
12-
from typing import List, Dict, Tuple
12+
from typing import List, Dict
1313
from dateutil.tz import tzlocal
1414

1515
from .editor import prevent_conflicted_copy

0 commit comments

Comments
 (0)