Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions mergin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ def list_projects(flag):
@cli.command()
@click.argument('project')
@click.argument('directory', type=click.Path(), required=False)
def download(project, directory):
@click.option('--version', default=None, help='Version of project to download')
def download(project, directory, version):
"""Download last version of mergin project"""

c = _init_client()
Expand All @@ -156,7 +157,7 @@ def download(project, directory):

click.echo('Downloading into {}'.format(directory))
try:
job = download_project_async(c, project, directory)
job = download_project_async(c, project, directory, version)

import time
with click.progressbar(length=job.total_size) as bar:
Expand Down
16 changes: 12 additions & 4 deletions mergin/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,17 +318,22 @@ def projects_list(self, tags=None, user=None, flag=None, q=None):
projects = json.load(resp)
return projects

def project_info(self, project_path, since=None):
def project_info(self, project_path, since=None, version=None):
"""
Fetch info about project.

:param project_path: Project's full name (<namespace>/<name>)
:type project_path: String
:param since: Version to track history of geodiff files from
:type since: String
:param version: Project version to get details for (particularly list of files)
:type version: String
:rtype: Dict
"""
params = {'since': since} if since else {}
# since and version are mutually exclusive
if version:
params = {'version': version}
resp = self.get("/v1/project/{}".format(project_path), params)
return json.load(resp)

Expand All @@ -344,17 +349,20 @@ def project_versions(self, project_path):
resp = self.get("/v1/project/version/{}".format(project_path))
return json.load(resp)

def download_project(self, project_path, directory):
def download_project(self, project_path, directory, version=None):
"""
Download latest version of project into given directory.
Download project into given directory. If version is not specified, latest version is downloaded

:param project_path: Project's full name (<namespace>/<name>)
:type project_path: String

:param version: Project version to download, e.g. v42
:type version: String

:param directory: Target directory
:type directory: String
"""
job = download_project_async(self, project_path, directory)
job = download_project_async(self, project_path, directory, version)
download_project_wait(job)
download_project_finalize(job)

Expand Down
4 changes: 2 additions & 2 deletions mergin/client_pull.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def _cleanup_failed_download(directory):
shutil.rmtree(directory)


def download_project_async(mc, project_path, directory):
def download_project_async(mc, project_path, directory, project_version=None):
"""
Starts project download in background and returns handle to the pending project download.
Using that object it is possible to watch progress or cancel the ongoing work.
Expand All @@ -114,7 +114,7 @@ def download_project_async(mc, project_path, directory):
mp.log.info(f"--- start download {project_path}")

try:
project_info = mc.project_info(project_path)
project_info = mc.project_info(project_path, version=project_version)
except ClientError:
_cleanup_failed_download(directory)
raise
Expand Down
36 changes: 36 additions & 0 deletions mergin/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,39 @@ def test_get_projects_by_name(mc):
assert full_name in resp
assert resp[full_name]["name"] == name
assert resp[full_name]["version"] == 'v0'


def test_download_versions(mc):
test_project = 'test_download'
project = API_USER + '/' + test_project
project_dir = os.path.join(TMP_DIR, test_project)
# download dirs
project_dir_v1 = os.path.join(TMP_DIR, test_project + '_v1')
project_dir_v2 = os.path.join(TMP_DIR, test_project + '_v2')
project_dir_v3 = os.path.join(TMP_DIR, test_project + '_v3')

cleanup(mc, project, [project_dir, project_dir_v1, project_dir_v2, project_dir_v3])
# create remote project
shutil.copytree(TEST_DATA_DIR, project_dir)
mc.create_project_and_push(test_project, project_dir)

# create new version - v2
f_added = 'new.txt'
with open(os.path.join(project_dir, f_added), 'w') as f:
f.write('new file')

mc.push_project(project_dir)
project_info = mc.project_info(project)
assert project_info['version'] == 'v2'

mc.download_project(project, project_dir_v1, 'v1')
assert os.path.exists(os.path.join(project_dir_v1, 'base.gpkg'))
assert not os.path.exists(os.path.join(project_dir_v2, f_added)) # added only in v2

mc.download_project(project, project_dir_v2, 'v2')
assert os.path.exists(os.path.join(project_dir_v2, f_added))
assert os.path.exists(os.path.join(project_dir_v1, 'base.gpkg')) # added in v1 but still present in v2

# try to download not-existing version
with pytest.raises(ClientError):
mc.download_project(project, project_dir_v3, 'v3')