Skip to content

Commit 5336f2d

Browse files
committed
Add option to skip downloading locally existing files
1 parent 6ebe7a7 commit 5336f2d

2 files changed

Lines changed: 40 additions & 5 deletions

File tree

src/bin/qfieldcloud-cli

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,14 +293,18 @@ def upload_files(ctx, project_id, project_path, filter_glob, throw_on_error):
293293
"--throw-on-error/--no-throw-on-error",
294294
help="If any project file downloads fails stop downloading the rest. Default: False",
295295
)
296+
@click.option(
297+
"--force-download/--no-force-download",
298+
help="Download file even if it already exists locally. Default: False",
299+
)
296300
@click.pass_context
297-
def download_files(ctx, project_id, local_dir, filter_glob, throw_on_error):
301+
def download_files(ctx, project_id, local_dir, filter_glob, throw_on_error, force_download):
298302
"""Download QFieldCloud project files."""
299303

300304
log(f'Downloading project "{project_id}" files to {local_dir}…')
301305

302306
files = ctx.obj["client"].download_project(
303-
project_id, local_dir, filter_glob, throw_on_error, show_progress=True
307+
project_id, local_dir, filter_glob, throw_on_error, show_progress=True, force_download=force_download,
304308
)
305309

306310
if ctx.obj["format_json"]:
@@ -441,14 +445,18 @@ def package_latest(ctx, project_id):
441445
"--throw-on-error/--no-throw-on-error",
442446
help="If any packaged file downloads fails stop downloading the rest. Default: False",
443447
)
448+
@click.option(
449+
"--force-download/--no-force-download",
450+
help="Download file even if it already exists locally. Default: False",
451+
)
444452
@click.pass_context
445-
def package_download(ctx, project_id, local_dir, filter_glob, throw_on_error):
453+
def package_download(ctx, project_id, local_dir, filter_glob, throw_on_error, force_download):
446454
"""Download packaged QFieldCloud project files."""
447455

448456
log(f'Downloading the latest project "{project_id}" package files to {local_dir}…')
449457

450458
files = ctx.obj["client"].package_download(
451-
project_id, local_dir, filter_glob, throw_on_error, show_progress=True
459+
project_id, local_dir, filter_glob, throw_on_error, show_progress=True, force_download=force_download,
452460
)
453461

454462
if ctx.obj["format_json"]:

src/qfieldcloud_sdk/sdk.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,15 @@ def download_project(
279279
filter_glob: str = None,
280280
throw_on_error: bool = False,
281281
show_progress: bool = False,
282+
force_download: bool = False,
282283
) -> List[Dict]:
283284
"""Download the specified project files into the destination dir.
284285
285286
Args:
286287
project_id: id of the project to be downloaded
287288
local_dir: destination directory where the files will be downloaded
288289
filter_glob: if specified, download only the files which match the glob, otherwise download all
290+
force_download (bool, optional): Download file even if it already exists locally. Defaults to False.
289291
"""
290292

291293
files = self.list_remote_files(project_id)
@@ -298,6 +300,7 @@ def download_project(
298300
filter_glob,
299301
throw_on_error,
300302
show_progress,
303+
force_download,
301304
)
302305

303306
def list_jobs(self, project_id: str, job_type: JobTypes = None) -> Dict[str, Any]:
@@ -442,13 +445,15 @@ def package_download(
442445
filter_glob: str = None,
443446
throw_on_error: bool = False,
444447
show_progress: bool = False,
448+
force_download: bool = False,
445449
) -> List[Dict]:
446450
"""Download the specified project packaged files into the destination dir.
447451
448452
Args:
449453
project_id: id of the project to be downloaded
450454
local_dir: destination directory where the files will be downloaded
451455
filter_glob: if specified, download only packaged files which match the glob, otherwise download all
456+
force_download (bool, optional): Download file even if it already exists locally. Defaults to False.
452457
"""
453458
project_status = self.package_latest(project_id)
454459

@@ -467,6 +472,7 @@ def package_download(
467472
filter_glob,
468473
throw_on_error,
469474
show_progress,
475+
force_download,
470476
)
471477

472478
def download_files(
@@ -478,6 +484,7 @@ def download_files(
478484
filter_glob: str = None,
479485
throw_on_error: bool = False,
480486
show_progress: bool = False,
487+
force_download: bool = False,
481488
) -> List[Dict]:
482489
"""Download project files.
483490
@@ -489,7 +496,7 @@ def download_files(
489496
filter_glob (str, optional): Download only files matching the glob pattern. If None download all. Defaults to None.
490497
throw_on_error (bool, optional): Throw if download error occurres. Defaults to False.
491498
show_progress (bool, optional): Show progress bar in the console. Defaults to False.
492-
499+
force_download (bool, optional): Download file even if it already exists locally. Defaults to False.
493500
Raises:
494501
QFieldCloudException: if throw_on_error is True, throw an error if a download request fails.
495502
@@ -515,7 +522,9 @@ def download_files(
515522
download_type,
516523
local_filename,
517524
file["name"],
525+
file.get("md5sum", None),
518526
show_progress,
527+
force_download,
519528
)
520529
file["status"] = FileTransferStatus.SUCCESS
521530
except QfcRequestException as err:
@@ -541,7 +550,9 @@ def download_file(
541550
download_type: FileTransferType,
542551
local_filename: Path,
543552
remote_filename: Path,
553+
remote_md5sum: str,
544554
show_progress: bool,
555+
force_download: bool = False,
545556
) -> requests.Response:
546557
"""Download a single project file.
547558
@@ -551,13 +562,29 @@ def download_file(
551562
local_filename (Path): Local filename
552563
remote_filename (Path): Remote filename
553564
show_progress (bool): Show progressbar in the console
565+
force_download (bool, optional): Download file even if it already exists locally. Defaults to False.
554566
555567
Raises:
556568
NotImplementedError: Raised if unknown `download_type` is passed
557569
558570
Returns:
559571
requests.Response: the response object
560572
"""
573+
574+
if (not force_download) and local_filename.exists() and remote_md5sum:
575+
with open(local_filename, "rb") as f:
576+
file_hash = hashlib.md5()
577+
chunk = f.read(8192)
578+
while chunk:
579+
file_hash.update(chunk)
580+
chunk = f.read(8192)
581+
if file_hash.hexdigest() == remote_md5sum:
582+
if show_progress:
583+
print(f"{remote_filename}: Already present locally. Download skipped.")
584+
else:
585+
logging.info(f'Skipping downloading file "{remote_filename}" because it is already present locally')
586+
return
587+
561588
if download_type == FileTransferType.PROJECT:
562589
url = f"files/{project_id}/{remote_filename}"
563590
elif download_type == FileTransferType.PACKAGE:

0 commit comments

Comments
 (0)