Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stac copy --copy-assets should copy collection assets #437

Merged
merged 4 commits into from
Aug 4, 2023
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Update pystac dependency to 0.7 and shapely to 2.0 ([#441](https://github.com/stac-utils/stactools/pull/441))
- Make `stac copy --copy-assets` copy assets to collections ([#437](https://github.com/stac-utils/stactools/pull/437))

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
"lxml>=4.9.2",
"numpy>=1.23.0",
"pyproj>=3.3",
"pystac[validation]>=1.7.0",
"pystac[validation]>=1.8.2",
"rasterio>=1.3.2",
"shapely>=2.0.1",
"stac-check>=1.3.2",
Expand Down
26 changes: 13 additions & 13 deletions src/stactools/cli/commands/add_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import click
import pystac
import pystac.utils
from stactools.core import add_asset_to_item
from stactools.core import add_asset


def _add_asset(
item_path: str,
owner_path: str,
asset_key: str,
asset_path: str,
title: Optional[str] = None,
Expand All @@ -17,23 +17,23 @@ def _add_asset(
move_assets: bool = False,
ignore_conflicts: bool = False,
) -> None:
item = pystac.read_file(item_path)
if not isinstance(item, pystac.Item):
raise click.BadArgumentUsage(f"{item_path} is not a STAC Item")
owner = pystac.read_file(owner_path)
if not isinstance(owner, (pystac.Item, pystac.Collection)):
raise click.BadArgumentUsage(f"{owner_path} is not a STAC Item or Collection")
asset = pystac.Asset(asset_path, title, description, media_type, roles)
item = add_asset_to_item(
item,
owner = add_asset(
owner,
asset_key,
asset,
move_assets=move_assets,
ignore_conflicts=ignore_conflicts,
)
item.save_object()
owner.save_object()


def create_add_asset_command(cli: click.Group) -> click.Command:
@cli.command("add-asset", short_help="Add an asset to an item.")
@click.argument("item_path")
@cli.command("add-asset", short_help="Add an asset to an item or collection.")
@click.argument("owner_path")
@click.argument("asset_key")
@click.argument("asset_path")
@click.option("--title", help="Optional title of the asset")
Expand All @@ -55,7 +55,7 @@ def create_add_asset_command(cli: click.Group) -> click.Command:
@click.option(
"--move-assets",
is_flag=True,
help="Move asset to the target Item's location.",
help="Move asset to the target Item or Collection's location.",
)
@click.option(
"--ignore-conflicts",
Expand All @@ -67,7 +67,7 @@ def create_add_asset_command(cli: click.Group) -> click.Command:
),
)
def add_asset_command(
item_path: str,
owner_path: str,
asset_key: str,
asset_path: str,
title: Optional[str] = None,
Expand All @@ -78,7 +78,7 @@ def add_asset_command(
ignore_conflicts: bool = False,
) -> None:
_add_asset(
pystac.utils.make_absolute_href(item_path),
pystac.utils.make_absolute_href(owner_path),
asset_key,
pystac.utils.make_absolute_href(asset_path),
title,
Expand Down
8 changes: 6 additions & 2 deletions src/stactools/cli/commands/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@

def create_move_assets_command(cli: click.Group) -> click.Command:
@cli.command(
"move-assets", short_help="Move or copy assets in a STAC to the Item locations."
"move-assets",
help=(
"Move or copy assets in a STAC catalog to the locations of the "
"items or collections that own them."
),
)
@click.argument("catalog_path")
@click.option("-c", "--copy", help="Copy assets instead of moving.", is_flag=True)
Expand Down Expand Up @@ -68,7 +72,7 @@ def create_copy_command(cli: click.Group) -> click.Command:
"--copy-assets",
"-a",
is_flag=True,
help="Copy all item assets to be alongside the new item location.",
help="Copy all asset files to be alongside the new location.",
)
@click.option(
"-l",
Expand Down
5 changes: 4 additions & 1 deletion src/stactools/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from stactools.core.add import add_item
from stactools.core.add_asset import add_asset_to_item
from stactools.core.add_asset import add_asset, add_asset_to_item
from stactools.core.add_raster import add_raster_to_item
from stactools.core.copy import (
copy_catalog,
move_all_assets,
move_asset_file,
move_asset_file_to_item,
move_assets,
)
Expand All @@ -13,12 +14,14 @@

__all__ = [
"add_item",
"add_asset",
"add_asset_to_item",
"add_raster_to_item",
"copy_catalog",
"layout_catalog",
"merge_all_items",
"merge_items",
"move_asset_file",
"move_asset_file_to_item",
"move_assets",
"move_all_assets",
Expand Down
84 changes: 59 additions & 25 deletions src/stactools/core/add_asset.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@
import logging
import warnings
from typing import Union, cast

from pystac import Asset, Item
from pystac import Asset, Collection, Item
from pystac.utils import is_absolute_href, make_relative_href
from stactools.core.copy import move_asset_file_to_item
from stactools.core.copy import move_asset_file

logger = logging.getLogger(__name__)


def add_asset_to_item(
item: Item,
def add_asset(
owner: Union[Collection, Item],
key: str,
asset: Asset,
move_assets: bool = False,
ignore_conflicts: bool = False,
) -> Item:
"""Adds an asset to an item.
) -> Union[Collection, Item]:
"""Adds an asset to an item or collection.

Args:
item (Item): The PySTAC Item to which the asset will be added.
owner (Item or Collection): The PySTAC Item or Collecitonto which the asset
will be added.
key (str): The unique key of the asset.
asset (Asset): The PySTAC Asset to add.
move_assets (bool): If True, move the asset file alongside the target item.
move_assets (bool): If True, move the asset file alongside the target owner.
ignore_conflicts (bool): If True, asset with the same key will not be added,
and asset file that would overwrite an existing file will not be moved.
If False, either of these situations will throw an error.

Returns:
Item: Returns an updated Item with the added Asset.
This operation mutates the Item.
owner: Returns an updated Item or Collection with the added Asset.
This operation mutates the owner.
"""
item_href = item.get_self_href()
owner_href = owner.get_self_href()
asset_href = asset.get_absolute_href()
if key in item.assets:
if key in owner.assets:
if not ignore_conflicts:
raise Exception(
f"Target item {item} already has an asset with key {key}, "
f"Target {owner} already has an asset with key {key}, "
"cannot add asset in from {asset_href}"
)
else:
Expand All @@ -43,23 +46,54 @@ def add_asset_to_item(
f"Asset {asset} must have an href to be added. The href "
"value should be an absolute path or URL."
)
if not item_href and move_assets:
if not owner_href and move_assets:
raise ValueError(f"Target {owner} must have an href to move an asset to it")
if not owner_href and not is_absolute_href(asset.href):
raise ValueError(
f"Target Item {item} must have an href to move an asset to the item"
)
if not item_href and not is_absolute_href(asset.href):
raise ValueError(
f"Target Item {item} must have an href to add "
f"Target {owner} must have an href to add "
"an asset with a relative href"
)
if move_assets:
new_asset_href = move_asset_file_to_item(
item, asset_href, ignore_conflicts=ignore_conflicts
new_asset_href = move_asset_file(
owner, asset_href, ignore_conflicts=ignore_conflicts
)
else:
if not is_absolute_href(asset.href) and item_href is not None:
asset_href = make_relative_href(asset_href, item_href)
if not is_absolute_href(asset.href) and owner_href is not None:
asset_href = make_relative_href(asset_href, owner_href)
new_asset_href = asset_href
asset.href = new_asset_href
item.add_asset(key, asset)
return item
owner.add_asset(key, asset)
return owner


def add_asset_to_item(
item: Item,
key: str,
asset: Asset,
move_assets: bool = False,
ignore_conflicts: bool = False,
) -> Item:
"""Adds an asset to an item.

Args:
item (Item): The PySTAC Item to which the asset will be added.
key (str): The unique key of the asset.
asset (Asset): The PySTAC Asset to add.
move_assets (bool): If True, move the asset file alongside the target item.
ignore_conflicts (bool): If True, asset with the same key will not be added,
and asset file that would overwrite an existing file will not be moved.
If False, either of these situations will throw an error.

Returns:
Item: Returns an updated Item with the added Asset.
This operation mutates the Item.
"""
warnings.warn(
"'add_asset_to_item' is deprecated. Use 'add_asset' instead", DeprecationWarning
)
return cast(
Item,
add_asset(
item, key, asset, move_assets=move_assets, ignore_conflicts=ignore_conflicts
),
)
Loading