Skip to content

Commit 8e390a3

Browse files
committed
Merge branch 'master' into windows
2 parents a801cb8 + 79994dd commit 8e390a3

3 files changed

Lines changed: 40 additions & 198 deletions

File tree

README.md

Lines changed: 1 addition & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -49,201 +49,9 @@ export QFIELDCLOUD_TOKEN=017478ee2464440cb8d3e98080df5e5a
4949
qfieldcloud-cli --json list-projects
5050
```
5151

52-
### Global options overview
5352

54-
```
55-
-U, --url TEXT URL to the QFieldCloud API endpoint. If not
56-
passed, gets the value from QFIELDCLOUD_URL
57-
environment variable. Default:
58-
https://app.qfield.cloud/api/v1/
59-
-u, --username TEXT Username or email.
60-
-p, --password TEXT
61-
-t, --token TEXT Session token.
62-
--json / --human Output the result as newline formatted json. Default: False
63-
--verify-ssl / --no-verify-ssl Verify SSL. Default: True
64-
--help Show this message and exit.
65-
```
66-
67-
Environment variables can be used instead of passing some common global options.
68-
69-
- `QFIELDCLOUD_API` - QFieldCloud API endpoint URL
70-
- `QFIELDCLOUD_USERNAME` - QFieldCloud username or email. Requires `QFIELDCLOUD_PASSWORD` to be set.
71-
- `QFIELDCLOUD_PASSWORD` - Password. Requires `QFIELDCLOUD_USERNAME` to be set.
72-
- `QFIELDCLOUD_TOKEN` - Token that can be used instead of passing username and password. It can be obtained by running `qfieldcloud-cli login`.
73-
- `QFIELDCLOUD_VERIFY_SSL` - When set to `0` has the same effect as passing `--no-verify-ssl`.
74-
75-
### Commands overview
76-
77-
```
78-
login Login to QFieldCloud.
79-
logout Logout and expire the token.
80-
list-projects List QFieldCloud projects.
81-
list-files List QFieldCloud project files.
82-
create-project Creates a new empty QFieldCloud project.
83-
delete-project Deletes a QFieldCloud project.
84-
upload-files Upload files to a QFieldCloud project.
85-
download-files Download QFieldCloud project files.
86-
delete-files Delete QFieldCloud project files.
87-
list-jobs List project jobs.
88-
job-trigger Triggers a new job.
89-
job-status Get job status.
90-
package-latest Check project packaging status.
91-
package-download Download packaged QFieldCloud project files.
92-
```
93-
94-
#### login
95-
96-
Login to QFieldCloud.
97-
98-
```
99-
qfieldcloud-cli login [OPTIONS] USERNAME PASSWORD
100-
```
101-
102-
#### logout
103-
104-
Logout from QFieldCloud.
105-
106-
```
107-
qfieldcloud-cli logout
108-
```
109-
110-
#### list-projects
111-
112-
List QFieldCloud projects.
113-
114-
```
115-
qfieldcloud-cli list-projects [OPTIONS]
116-
117-
Options:
118-
--include-public / --no-public Includes the public project in the list. Default: False
119-
```
120-
121-
#### list-files
122-
123-
List QFieldCloud project files.
124-
125-
```
126-
qfieldcloud-cli list-files [OPTIONS] PROJECT_ID
127-
```
128-
129-
#### create-project
130-
131-
Creates a new empty QFieldCloud project.
132-
133-
```
134-
qfieldcloud-cli create-project [OPTIONS] NAME
135-
136-
Options:
137-
--owner TEXT Owner of the project. If omitted, the current
138-
user is the owner.
139-
--description TEXT Description of the project.
140-
--is-public / --is-private Mark the project as public.
141-
```
142-
143-
#### delete-project
144-
145-
Deletes a QFieldCloud project.
146-
147-
```
148-
qfieldcloud-cli delete-project [OPTIONS] PROJECT_ID
149-
```
150-
151-
#### upload-files
53+
More detailed documentation can be found [here](https://docs.qfield.org/reference/qfieldcloud/sdk/)
15254

153-
Upload files to a QFieldCloud project.
154-
155-
```
156-
qfieldcloud-cli upload-files [OPTIONS] PROJECT_ID PROJECT_PATH
157-
158-
Options:
159-
--filter TEXT Do not upload the whole project, but only
160-
the files which match the glob.
161-
--throw-on-error / --no-throw-on-error
162-
If any project file upload fails stop
163-
uploading the rest. Default: False
164-
```
165-
166-
#### download-files
167-
168-
Download QFieldCloud project files.
169-
170-
```
171-
qfieldcloud-cli download-files [OPTIONS] PROJECT_ID LOCAL_DIR
172-
173-
Options:
174-
--filter TEXT Do not download the whole project, but only
175-
the files which match the glob.
176-
--throw-on-error / --no-throw-on-error
177-
If any project file downloads fails stop
178-
downloading the rest. Default: False
179-
```
180-
181-
#### delete-files
182-
183-
Delete QFieldCloud project files.
184-
185-
```
186-
qfieldcloud-cli delete-files [OPTIONS] PROJECT_ID PATHS...
187-
188-
Options:
189-
--throw-on-error / --no-throw-on-error
190-
If any project file delete operations fails
191-
stop, stop deleting the rest. Default: False
192-
```
193-
194-
#### job-list
195-
196-
List project jobs.
197-
198-
```
199-
qfieldcloud-cli list-jobs [OPTIONS] PROJECT_ID
200-
201-
Options:
202-
--type JOBTYPES Job type. One of package, delta_apply or
203-
process_projectfile.
204-
```
205-
206-
#### job-trigger
207-
208-
Triggers a new job.
209-
210-
```
211-
qfieldcloud-cli job-trigger [OPTIONS] PROJECT_ID JOB_TYPE
212-
213-
Options:
214-
--force / --no-force Should force creating a new job. Default: False
215-
```
216-
217-
#### job-status
218-
219-
Get job status.
220-
221-
```
222-
qfieldcloud-cli job-status [OPTIONS] JOB_ID
223-
```
224-
225-
#### package-latest
226-
227-
Check project packaging status.
228-
229-
```
230-
qfieldcloud-cli package-latest [OPTIONS] PROJECT_ID
231-
```
232-
233-
#### package-download
234-
235-
Download packaged QFieldCloud project files.
236-
237-
```
238-
qfieldcloud-cli package-download [OPTIONS] PROJECT_ID LOCAL_DIR
239-
240-
Options:
241-
--filter TEXT Do not download the whole packaged project,
242-
but only the files which match the glob.
243-
--throw-on-error / --no-throw-on-error
244-
If any packaged file downloads fails stop
245-
downloading the rest. Default: False
246-
```
24755

24856
## Development
24957

src/bin/qfieldcloud_cli.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,14 +303,18 @@ def upload_files(ctx, project_id, project_path, filter_glob, throw_on_error):
303303
"--throw-on-error/--no-throw-on-error",
304304
help="If any project file downloads fails stop downloading the rest. Default: False",
305305
)
306+
@click.option(
307+
"--force-download/--no-force-download",
308+
help="Download file even if it already exists locally. Default: False",
309+
)
306310
@click.pass_context
307-
def download_files(ctx, project_id, local_dir, filter_glob, throw_on_error):
311+
def download_files(ctx, project_id, local_dir, filter_glob, throw_on_error, force_download):
308312
"""Download QFieldCloud project files."""
309313

310314
log(f'Downloading project "{project_id}" files to {local_dir}…')
311315

312316
files = ctx.obj["client"].download_project(
313-
project_id, local_dir, filter_glob, throw_on_error, show_progress=True
317+
project_id, local_dir, filter_glob, throw_on_error, show_progress=True, force_download=force_download,
314318
)
315319

316320
if ctx.obj["format_json"]:
@@ -459,14 +463,18 @@ def package_latest(ctx, project_id):
459463
"--throw-on-error/--no-throw-on-error",
460464
help="If any packaged file downloads fails stop downloading the rest. Default: False",
461465
)
466+
@click.option(
467+
"--force-download/--no-force-download",
468+
help="Download file even if it already exists locally. Default: False",
469+
)
462470
@click.pass_context
463-
def package_download(ctx, project_id, local_dir, filter_glob, throw_on_error):
471+
def package_download(ctx, project_id, local_dir, filter_glob, throw_on_error, force_download):
464472
"""Download packaged QFieldCloud project files."""
465473

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

468476
files = ctx.obj["client"].package_download(
469-
project_id, local_dir, filter_glob, throw_on_error, show_progress=True
477+
project_id, local_dir, filter_glob, throw_on_error, show_progress=True, force_download=force_download,
470478
)
471479

472480
if ctx.obj["format_json"]:

src/qfieldcloud_sdk/sdk.py

Lines changed: 27 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
@@ -508,6 +515,9 @@ def download_files(
508515

509516
for file in files_to_download:
510517
local_filename = Path(f'{local_dir}/{file["name"]}')
518+
md5sum = None
519+
if not force_download:
520+
md5sum = file.get("md5sum", None)
511521

512522
try:
513523
self.download_file(
@@ -516,6 +526,7 @@ def download_files(
516526
local_filename,
517527
file["name"],
518528
show_progress,
529+
md5sum,
519530
)
520531
file["status"] = FileTransferStatus.SUCCESS
521532
except QfcRequestException as err:
@@ -542,6 +553,7 @@ def download_file(
542553
local_filename: Path,
543554
remote_filename: Path,
544555
show_progress: bool,
556+
remote_md5sum: str = None,
545557
) -> requests.Response:
546558
"""Download a single project file.
547559
@@ -551,13 +563,27 @@ def download_file(
551563
local_filename (Path): Local filename
552564
remote_filename (Path): Remote filename
553565
show_progress (bool): Show progressbar in the console
566+
remote_md5sum (str, optional): The md5sum of the remote file. If is None, the download of the file happens even if it already exists locally. Defaults to None.
554567
555568
Raises:
556569
NotImplementedError: Raised if unknown `download_type` is passed
557570
558571
Returns:
559572
requests.Response: the response object
560573
"""
574+
575+
if remote_md5sum and local_filename.exists():
576+
if self._get_md5sum(str(local_filename)) == remote_md5sum:
577+
if show_progress:
578+
print(
579+
f"{remote_filename}: Already present locally. Download skipped."
580+
)
581+
else:
582+
logging.info(
583+
f'Skipping download of "{remote_filename}" because it is already present locally'
584+
)
585+
return
586+
561587
if download_type == FileTransferType.PROJECT:
562588
url = f"files/{project_id}/{remote_filename}"
563589
elif download_type == FileTransferType.PACKAGE:

0 commit comments

Comments
 (0)