Skip to content

Commit

Permalink
feat: add option for templated paths
Browse files Browse the repository at this point in the history
  • Loading branch information
drnextgis committed Jun 7, 2024
1 parent cbeb940 commit 3fb8e6e
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

- More `HttpClient` attributes to `Config` ([#177](https://github.com/stac-utils/stac-asset/pull/177))
- `derived_from` link ([#178](https://github.com/stac-utils/stac-asset/pull/178))
- Option for templated paths when downloading item collections ([#181](https://github.com/stac-utils/stac-asset/pull/181))

### Removed

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ stac-client search https://planetarycomputer.microsoft.com/api/stac/v1 \
stac-asset download -i rendered_preview -q
```

By default, all assets are stored in a folder named after the item ID. To change this, you can use the `-p` flag and specify a path template using PySTAC layout template [variables](https://pystac.readthedocs.io/en/latest/api/layout.html#pystac.layout.LayoutTemplate):

```
stac-client search https://planetarycomputer.microsoft.com/api/stac/v1 \
-c landsat-c2-l2 \
--max-items 1 | \
stac-asset download -i rendered_preview -p '${collection}'
```

See [the documentation](https://stac-asset.readthedocs.io/en/latest/index.html) for more examples and complete API and CLI documentation.

### Clients
Expand Down
9 changes: 9 additions & 0 deletions src/stac_asset/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ def cli() -> None:
@cli.command()
@click.argument("href", required=False)
@click.argument("directory", required=False)
@click.option(
"-p",
"--path-template",
help="String to be interpolated to specify where to store downloaded files",
)
@click.option(
"-a",
"--alternate-assets",
Expand Down Expand Up @@ -125,6 +130,7 @@ def cli() -> None:
def download(
href: Optional[str],
directory: Optional[str],
path_template: Optional[str],
alternate_assets: List[str],
include: List[str],
exclude: List[str],
Expand Down Expand Up @@ -160,6 +166,7 @@ def download(
download_async(
href,
directory,
path_template,
alternate_assets,
include,
exclude,
Expand All @@ -178,6 +185,7 @@ def download(
async def download_async(
href: Optional[str],
directory: Optional[str],
path_template: Optional[str],
alternate_assets: List[str],
include: List[str],
exclude: List[str],
Expand Down Expand Up @@ -241,6 +249,7 @@ async def download() -> Union[Item, ItemCollection]:
return await _functions.download_item_collection(
item_collection,
directory_str,
path_template,
file_name=file_name,
config=config,
messages=messages,
Expand Down
8 changes: 7 additions & 1 deletion src/stac_asset/_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import pystac.utils
from pystac import Asset, Collection, Item, ItemCollection, Link, STACError
from pystac.layout import LayoutTemplate
from yarl import URL

from .client import Client, Clients
Expand Down Expand Up @@ -307,6 +308,7 @@ async def download_collection(
async def download_item_collection(
item_collection: ItemCollection,
directory: PathLikeObject,
path_template: Optional[str] = None,
file_name: Optional[str] = "item-collection.json",
config: Optional[Config] = None,
messages: Optional[MessageQueue] = None,
Expand All @@ -318,6 +320,8 @@ async def download_item_collection(
Args:
item_collection: The item collection to download
directory: The destination directory
path_template: String to be interpolated to specify where to store
downloaded files.
file_name: The name of the item collection file to save. If not
provided, will not be saved.
config: The download configuration
Expand All @@ -335,7 +339,9 @@ async def download_item_collection(
async with Downloads(config=config or Config(), clients=clients) as downloads:
for item in item_collection.items:
item.set_self_href(None)
root = Path(directory) / item.id
root = Path(directory) / LayoutTemplate(
path_template if path_template is not None else "${id}"
).substitute(item)
await downloads.add(item, root, None, keep_non_downloaded)
await downloads.download(messages)
if file_name:
Expand Down
4 changes: 4 additions & 0 deletions src/stac_asset/blocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def download_collection(
def download_item_collection(
item_collection: ItemCollection,
directory: PathLikeObject,
path_template: Optional[str] = None,
file_name: Optional[str] = "item-collection.json",
config: Optional[Config] = None,
messages: Optional[MessageQueue] = None,
Expand All @@ -119,6 +120,8 @@ def download_item_collection(
Args:
item_collection: The item collection to download
directory: The destination directory
path_template: String to be interpolated to specify where to store
downloaded files.
file_name: The name of the item collection file to save. If not
provided, will not be saved.
config: The download configuration
Expand All @@ -142,6 +145,7 @@ def download_item_collection(
messages=messages,
clients=clients,
keep_non_downloaded=keep_non_downloaded,
path_template=path_template,
)
)

Expand Down
9 changes: 7 additions & 2 deletions tests/data/item.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
{
"href": "./catalog.json",
"rel": "parent"
},
{
"href": "./collection.json",
"rel": "collection"
}
],
"assets": {
"data": {
"href": "./20201211_223832_CS2.jpg"
}
}
}
},
"collection": "test-collection"
}
16 changes: 16 additions & 0 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ async def test_download_item_collection_with_file_name_and_cd(
)


async def test_download_item_collection_with_path_template(
tmp_path: Path, item_collection: ItemCollection
) -> None:
await stac_asset.download_item_collection(
item_collection,
tmp_path,
file_name="item-collection.json",
path_template="${collection}",
)
item_collection = ItemCollection.from_file(str(tmp_path / "item-collection.json"))
assert isinstance(item_collection.items[0].collection_id, str)
assert item_collection.items[0].assets["data"].href == str(
tmp_path / "test-collection/20201211_223832_CS2.jpg"
)


async def test_download_collection(tmp_path: Path, collection: Collection) -> None:
collection = await stac_asset.download_collection(
collection, tmp_path, file_name="collection.json"
Expand Down

0 comments on commit 3fb8e6e

Please sign in to comment.