Skip to content

Commit 4d00171

Browse files
authored
Merge pull request #91 from opengisch/QF-7220-seed
feat: add support for project seed and seed xlsform APIs in the SDK
2 parents 105a10d + 3628fef commit 4d00171

2 files changed

Lines changed: 125 additions & 2 deletions

File tree

qfieldcloud_sdk/cli.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,57 @@ def get_project(ctx: Context, project_id: str) -> None:
245245
log("User does not have access to projects yet.")
246246

247247

248+
@cli.command()
249+
@click.argument("project_id")
250+
@click.pass_context
251+
def get_project_seed(ctx: Context, project_id: str) -> None:
252+
"""Get QFieldCloud project seed data."""
253+
254+
project_seed: Dict[str, Any] = ctx.obj["client"].get_project_seed(project_id)
255+
256+
if ctx.obj["format_json"]:
257+
print_json(project_seed)
258+
else:
259+
if project_seed:
260+
log("Project name: {}".format(project_seed["name"]))
261+
log("Project CRS: {}".format(project_seed["crs"]))
262+
log(
263+
"Project extent: {}".format(
264+
", ".join(map(lambda n: str(n), project_seed["extent"]))
265+
)
266+
)
267+
log(
268+
"Project basemaps: {}".format(len(project_seed["settings"]["basemaps"]))
269+
)
270+
log("Project XLSForm: {}".format(bool(project_seed["settings"]["xlsform"])))
271+
else:
272+
log("User does not have access to projects yet.")
273+
274+
275+
@cli.command()
276+
@click.argument("project_id")
277+
@click.argument("destination_dir")
278+
@click.pass_context
279+
def get_project_seed_xlsform(
280+
ctx: Context,
281+
project_id: str,
282+
destination_dir: str,
283+
) -> None:
284+
"""Get QFieldCloud project seed XLSForm file."""
285+
286+
xlsform_filename = ctx.obj["client"].get_project_seed_xlsform(
287+
project_id, destination_dir
288+
)
289+
290+
if ctx.obj["format_json"]:
291+
print_json(xlsform_filename)
292+
else:
293+
if xlsform_filename:
294+
log(f"XLSForm seed file downloaded to: {xlsform_filename}")
295+
else:
296+
log("No XLSForm seed file found for the project.")
297+
298+
248299
@cli.command()
249300
@click.argument("project_id")
250301
@click.option(

qfieldcloud_sdk/sdk.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import logging
44
import os
55
import sys
6+
import requests
7+
import urllib3
8+
import cgi
9+
610
from enum import Enum
711
from pathlib import Path
812
from typing import Any, Callable, Dict, List, Optional, TypedDict, Union, cast
913
from urllib import parse as urlparse
1014

11-
import requests
12-
import urllib3
1315
from requests.adapters import HTTPAdapter, Retry
1416
from requests_toolbelt.multipart.encoder import MultipartEncoderMonitor
1517

@@ -76,6 +78,7 @@ class JobTypes(str, Enum):
7678
PACKAGE = "package"
7779
APPLY_DELTAS = "delta_apply"
7880
PROCESS_PROJECTFILE = "process_projectfile"
81+
CREATE_PROJECT = "create_project"
7982

8083

8184
class ProjectCollaboratorRole(str, Enum):
@@ -399,6 +402,75 @@ def get_project(
399402

400403
return cast(Dict, payload)
401404

405+
def get_project_seed(
406+
self,
407+
project_id: str,
408+
) -> Dict[str, Any]:
409+
"""Get project seed data.
410+
411+
Args:
412+
project_id: the project data to get seed data for.
413+
414+
Returns:
415+
A dictionary containing project seed.
416+
417+
Example:
418+
```python
419+
client.get_project_seed(project_id)
420+
```
421+
"""
422+
payload = self._request_json("GET", f"projects/{project_id}/seed")
423+
424+
return cast(Dict, payload)
425+
426+
def get_project_seed_xlsform(
427+
self,
428+
project_id: str,
429+
destination_dir: str,
430+
) -> str | None:
431+
"""Get project seed XLSForm file content.
432+
433+
Args:
434+
project_id: the project data to get seed XLSForm for.
435+
436+
Returns:
437+
The name of the downloaded XLSForm file.
438+
439+
Example:
440+
```python
441+
client.get_project_seed_xlsform(project_id)
442+
```
443+
"""
444+
445+
resp = self._request("GET", f"projects/{project_id}/seed/xlsform")
446+
447+
if resp.status_code != 200:
448+
return None
449+
450+
content_disposition = resp.headers.get("Content-Disposition", "")
451+
452+
if not content_disposition:
453+
logger.warning(
454+
"Response has no `Content-Disposition` header. Skip download of XLSForm file!"
455+
)
456+
457+
return None
458+
459+
_value, params = cgi.parse_header(content_disposition)
460+
filename = params.get("filename")
461+
462+
if not filename:
463+
logger.warning(
464+
"Response has no filename in `Content-Disposition` header. Skip download of XLSForm file!"
465+
)
466+
467+
return None
468+
469+
path = Path(destination_dir).joinpath(filename)
470+
path.write_bytes(resp.content)
471+
472+
return str(path)
473+
402474
def list_remote_files(
403475
self, project_id: str, skip_metadata: bool = True
404476
) -> List[Dict[str, Any]]:

0 commit comments

Comments
 (0)