Skip to content

Commit

Permalink
Remove 'href_type' in move_assets commands.
Browse files Browse the repository at this point in the history
In PySTAC 0.5.4, the asset HREFs are either relative or absolute
depending on their catalog type. This removes the need for users to
set the href_type for the assets themselves.
  • Loading branch information
lossyrob committed Jan 15, 2021
1 parent 4082e86 commit bb00e73
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 68 deletions.
35 changes: 22 additions & 13 deletions stactools_cli/stactools/cli/commands/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,34 @@ def create_move_assets_command(cli):
'move-assets',
short_help='Move or copy assets in a STAC to the Item locations.')
@click.argument('catalog_path')
@click.option('-c', '--copy', help='Copy assets instead of moving.', is_flag=True)
@click.option('-c',
'--copy',
help='Copy assets instead of moving.',
is_flag=True)
@click.option('-s',
'--asset-subdirectory',
help=('Subdirectory to place assets '
'inside of the directory containing '
'their items'))
@click.option(
'-h',
'--href-type',
type=click.Choice([pystac.LinkType.ABSOLUTE, pystac.LinkType.RELATIVE],
case_sensitive=False),
help=('If supplied, forces asset HREFs to be either absolute or '
'relative HREFS'))
def move_assets_command(catalog_path, copy, asset_subdirectory, href_type):
def move_assets_command(catalog_path, copy, asset_subdirectory):
"""Move or copy assets in a STAC Catalog.
For all assets in the catalog at CATALOG_PATH, move or copy
those assets into the directory of the item for which they belong.
If --asset-subdirectory is used, moves them instead into a directory
called 'assets' next to the item for which they belong. If -c is used,
assets are copied; otherwise they are moved.
Note: If the catalog is an ABSOLUTE_PUBLISHED catalog, the assets will have
an absolute HREF after this operation. Otherwise, it will have a relative HREF.
"""
catalog = pystac.read_file(catalog_path)
move_all_assets(catalog,
asset_subdirectory=asset_subdirectory,
href_type=href_type,
copy=copy)

processed = move_all_assets(catalog,
asset_subdirectory=asset_subdirectory,
copy=copy)

processed.save()

return move_assets_command

Expand Down
59 changes: 18 additions & 41 deletions stactools_core/stactools/core/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
import fsspec
from fsspec.core import split_protocol
from fsspec.registry import get_filesystem_class
import pystac
from pystac.utils import (is_absolute_href, make_absolute_href,
make_relative_href)

from pystac.utils import (is_absolute_href, make_absolute_href)

logger = logging.getLogger(__name__)


def move_asset_file_to_item(item,
asset_href,
asset_subdirectory=None,
href_type=None,
copy=False,
ignore_conflicts=False):
"""Moves an asset file to be alongside that item.
Expand All @@ -26,10 +24,6 @@ def move_asset_file_to_item(item,
asset_subdirectory (str or None): A subdirectory that will be used
to store the assets. If not supplied, the assets will be moved
or copied to the same directory as their item.
href_type (str or None): Either 'relative' or 'absolute' (one of the values
in PySTAC.LinkType), or None. If None, the asset HREF will remain
in its current state, either absolute or relative. If supplied,
the asset HREF will be made either absolute or relative.
copy (bool): If False this function will move the asset file; if True,
the asset file will be copied.
ignore_conflicts (bool): If the asset destination file already exists,
Expand All @@ -54,17 +48,14 @@ def move_asset_file_to_item(item,
target_dir = item_dir
else:
target_dir = os.path.join(item_dir, asset_subdirectory)
new_abs_href = os.path.join(target_dir, fname)
new_asset_href = new_abs_href
if href_type == pystac.LinkType.RELATIVE:
new_asset_href = make_relative_href(new_abs_href, item_href)
new_asset_href = os.path.join(target_dir, fname)

if asset_href != new_abs_href:
dest_protocol = split_protocol(new_abs_href)[0]
if asset_href != new_asset_href:
dest_protocol = split_protocol(new_asset_href)[0]
fs_dest = get_filesystem_class(dest_protocol)()
op = None

if fs_dest.exists(new_abs_href):
if fs_dest.exists(new_asset_href):
if not ignore_conflicts:
raise FileExistsError(
'{} already exists'.format(new_asset_href))
Expand All @@ -73,12 +64,12 @@ def move_asset_file_to_item(item,

def _op(dry_run=False):
logger.info("Copying {} to {}...".format(
asset_href, new_abs_href))
asset_href, new_asset_href))
if not dry_run:
fs_dest.makedirs(os.path.dirname(new_abs_href),
fs_dest.makedirs(os.path.dirname(new_asset_href),
exist_ok=True)
with fsspec.open(asset_href, 'rb') as f_src:
with fsspec.open(new_abs_href, 'wb') as f_dst:
with fsspec.open(new_asset_href, 'wb') as f_dst:
f_dst.write(f_src.read())

op = _op
Expand All @@ -89,24 +80,25 @@ def _op(dry_run=False):

def _op(dry_run=False):
logger.info("Moving {} to {}...".format(
asset_href, new_abs_href))
asset_href, new_asset_href))
if not dry_run:
fs_dest.makedirs(os.path.dirname(new_abs_href),
fs_dest.makedirs(os.path.dirname(new_asset_href),
exist_ok=True)
fs_dest.move(asset_href, new_abs_href)
fs_dest.move(asset_href, new_asset_href)

op = _op
else:

def _op(dry_run=False):
logger.info("Moving {} to {}...".format(
asset_href, new_abs_href))
asset_href, new_asset_href))
if not dry_run:
fs_source = get_filesystem_class(source_protocol)()
fs_dest.makedirs(os.path.dirname(new_abs_href),
fs_dest.makedirs(os.path.dirname(new_asset_href),
exist_ok=True)
with fsspec.open(asset_href, 'rb') as f_src:
with fsspec.open(new_abs_href, 'wb') as f_dst:
with fsspec.open(new_asset_href,
'wb') as f_dst:
f_dst.write(f_src.read())
fs_source.delete(asset_href)

Expand All @@ -120,7 +112,6 @@ def _op(dry_run=False):

def move_assets(item,
asset_subdirectory=None,
href_type=None,
copy=False,
ignore_conflicts=False):
"""Moves assets for an item to be alongside that item.
Expand All @@ -131,10 +122,6 @@ def move_assets(item,
asset_subdirectory (str or None): A subdirectory that will be used
to store the assets. If not supplied, the assets will be moved
or copied to the same directory as their item.
href_type (str or None): Either 'relative' or 'absolute' (one of the values
in PySTAC.LinkType), or None. If None, the asset HREFs will remain
in their current state, either absolute or relative. If supplied,
all asset HREFs will be made either absolute or relative.
copy (bool): If False this function will move the asset file; if True,
the asset file will be copied.
ignore_conflicts (bool): If the asset destination file already exists,
Expand All @@ -151,17 +138,12 @@ def move_assets(item,
'requires that the Item HREFs are available.')

for asset in item.assets.values():
original_asset_href = asset.href

is_absolute = is_absolute_href(original_asset_href)
abs_asset_href = asset.get_absolute_href()

new_asset_href = move_asset_file_to_item(
item,
abs_asset_href,
asset_subdirectory=asset_subdirectory,
href_type=href_type or (pystac.LinkType.ABSOLUTE if is_absolute
else pystac.LinkType.RELATIVE),
copy=copy,
ignore_conflicts=ignore_conflicts)

Expand All @@ -172,7 +154,6 @@ def move_assets(item,

def move_all_assets(catalog,
asset_subdirectory=None,
href_type=None,
copy=False,
ignore_conflicts=False):
"""Moves assets in a catalog to be alongside the items that own them.
Expand All @@ -183,10 +164,6 @@ def move_all_assets(catalog,
asset_subdirectory (str or None): A subdirectory that will be used
to store the assets. If not supplied, the assets will be moved
or copied to the same directory as their item.
href_type (str or None): Either 'relative' or 'absolute' (one of the values
in PySTAC.LinkType), or None. If None, the asset HREFs will remain
in their current state, either absolute or relative. If supplied,
all asset HREFs will be made either absolute or relative.
copy (bool): If False this function will move the asset file; if True,
the asset file will be copied.
ignore_conflicts (bool): If the asset destination file already exists,
Expand All @@ -196,9 +173,9 @@ def move_all_assets(catalog,
[Catalog or Collection]: Returns the updated catalog.
This operation mutates the catalog.
"""

for item in catalog.get_all_items():
move_assets(item, asset_subdirectory, href_type, copy,
ignore_conflicts)
move_assets(item, asset_subdirectory, copy, ignore_conflicts)

return catalog

Expand Down
5 changes: 1 addition & 4 deletions stactools_core/stactools/core/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def merge_items(source_item,
new_asset_href = move_asset_file_to_item(
target_item,
asset_href,
href_type=pystac.LinkType.RELATIVE,
ignore_conflicts=ignore_conflicts)
else:
asset_href = asset.get_absolute_href()
Expand Down Expand Up @@ -114,9 +113,7 @@ def merge_all_items(source_catalog,
item_copy.set_collection(None)

if move_assets:
do_move_assets(item_copy,
href_type=pystac.LinkType.RELATIVE,
copy=False)
do_move_assets(item_copy, copy=False)

if target_catalog.STAC_OBJECT_TYPE == pystac.STACObjectType.COLLECTION:
target_catalog.update_extent_from_items()
Expand Down
34 changes: 24 additions & 10 deletions tests/cli/commands/test_copy.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import os
from tempfile import TemporaryDirectory
from typing import cast

import pystac
from pystac.utils import is_absolute_href, make_absolute_href

from stactools.cli.commands.copy import create_copy_command
from stactools.cli.commands.copy import create_copy_command, create_move_assets_command
from tests.utils import (TestCases, CliTestCase)


class CopyTest(CliTestCase):
def create_subcommand_functions(self):
return [create_copy_command]
return [create_copy_command, create_move_assets_command]

def test_copy(self):
cat = TestCases.planet_disaster()
Expand All @@ -36,11 +35,8 @@ def test_copy_to_relative(self):
cat2_dir = os.path.join(tmp_dir, 'second')

command = [
'copy',
'-t', 'SELF_CONTAINED',
'-a',
cat.get_self_href(),
cat2_dir
'copy', '-t', 'SELF_CONTAINED', '-a',
cat.get_self_href(), cat2_dir
]
self.run_command(command)
cat2 = pystac.read_file(os.path.join(cat2_dir, 'collection.json'))
Expand All @@ -55,8 +51,26 @@ def test_copy_to_relative(self):
])
self.assertTrue(common_path, os.path.dirname(item_href))

def test_move_assets(self):
cat = TestCases.planet_disaster()

with TemporaryDirectory() as tmp_dir:
cat.normalize_hrefs(tmp_dir)
cat.save(catalog_type=pystac.CatalogType.RELATIVE_PUBLISHED)
cat_href = cat.get_self_href()

command = ['move-assets', '-c', cat_href]
self.assertEqual(self.run_command(command).exit_code, 0)
cat2 = pystac.read_file(cat_href)
for item in cat2.get_all_items():
item_href = item.get_self_href()
for asset in item.assets.values():
href = asset.href
print(href)
self.assertFalse(is_absolute_href(href))
common_path = os.path.commonpath([
os.path.dirname(item_href),
make_absolute_href(href, item_href)
])



self.assertEqual(common_path, os.path.dirname(item_href))

0 comments on commit bb00e73

Please sign in to comment.