Skip to content

Commit

Permalink
Merge pull request #45 from sat-utils/develop
Browse files Browse the repository at this point in the history
release 0.2
  • Loading branch information
matthewhanson authored Jul 16, 2019
2 parents 8101782 + 64b977c commit 88c07de
Show file tree
Hide file tree
Showing 19 changed files with 251 additions and 304 deletions.
69 changes: 7 additions & 62 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,55 +25,6 @@ references:

jobs:

build_and_test_35:
docker:
- image: circleci/python:3.5
steps:
- *restore_repo
- checkout
- *save_repo
- restore_cache:
keys:
- v1-dependencies35-{{ checksum "requirements.txt"}}
- v1-dependencies35
- run: |
pip install virtualenv
virtualenv ~/venv35
. ~/venv35/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt
pip install .
cd test
pytest -v --cov satstac --cov-report term-missing
- save_cache:
key: v1-dependencies35-{{ checksum "requirements.txt"}}
paths:
- ~/venv35

build_and_test_36:
docker:
- image: circleci/python:3.6
steps:
- *restore_repo
- checkout
- *save_repo
- restore_cache:
keys:
- v1-dependencies36-{{ checksum "requirements.txt"}}
- v1-dependencies36
- run: |
python3 -m venv ~/venv36
. ~/venv36/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt
pip install .
cd test
pytest -v --cov satstac --cov-report term-missing
- save_cache:
key: v1-dependencies36-{{ checksum "requirements.txt"}}
paths:
- ~/venv36

build_and_test_37:
docker:
- image: circleci/python:3.7
Expand Down Expand Up @@ -101,17 +52,17 @@ jobs:

deploy:
docker:
- image: circleci/python:3.6
- image: circleci/python:3.7
steps:
- *restore_repo
- restore_cache:
keys:
- v1-dependencies36-{{ checksum "requirements.txt"}}
- v1-dependencies36
- v1-dependencies37-{{ checksum "requirements.txt"}}
- v1-dependencies37
- run:
name: Deploy
command: |
. ~/venv36/bin/activate
. ~/venv37/bin/activate
mkdir -p ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
pip install twine
Expand All @@ -124,18 +75,12 @@ jobs:
workflows:
version: 2
build_test_35:
jobs:
- build_and_test_35
build_test_36:
build_test_37:
jobs:
- build_and_test_36
- build_and_test_37
- deploy:
requires:
- build_and_test_36
- build_and_test_37
filters:
branches:
only: master
build_test_37:
jobs:
- build_and_test_37
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Changed
- Thing.publish() removed. Self links are not used at all (and not recommended for static catalogs)
- Thing.root() and Thing.parent() functions now return `None` if no root or parent (rather than an empty list). If more than one root or parent then an error will now be thrown.
- Internal JSON data now stored in variable called `_data` rather than `data`

## [v0.1.3] - 2019-05-04

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The table below shows the corresponding versions between sat-stac and STAC:
| sat-stac | STAC |
| -------- | ---- |
| 0.1.x | 0.6.x |
| 0.2.x | 0.7.x |

## Tutorials

Expand Down
62 changes: 17 additions & 45 deletions satstac/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,25 @@ class Catalog(Thing):
def __init__(self, data, root=None, **kwargs):
""" Initialize a catalog with a catalog file """
super(Catalog, self).__init__(data, **kwargs)
self._root = root

@property
def stac_version(self):
""" Get the STAC version of this catalog """
return self.data['stac_version']
return self._data['stac_version']

@property
def description(self):
""" Get catalog description """
return self.data.get('description', '')
return self._data.get('description', '')

@classmethod
def create(cls, id='stac-catalog', description='A STAC Catalog', root=None, **kwargs):
def create(cls, id='stac-catalog', title='A STAC Catalog',
description='A STAC Catalog', root=None, **kwargs):
""" Create new catalog """
kwargs.update({
'id': id,
'stac_version': STAC_VERSION,
'title': title,
'description': description,
'links': []
})
Expand All @@ -45,78 +46,49 @@ def catalogs(self):
for cat in self.children():
for subcat in cat.children():
yield subcat
# Python 2
for x in subcat.catalogs():
yield x
# Python 3.3+
# yield from subcat.catalogs()
yield from subcat.catalogs()
yield cat

def collections(self):
""" Recursively get all collections within this Catalog """
for cat in self.children():
if 'extent' in cat.data.keys():
if 'extent' in cat._data.keys():
yield Collection.open(cat.filename)
# TODO - keep going? if other Collections can appear below a Collection
else:
# Python 2
for x in cat.collections():
yield x
# Python 3.3+
# yield from cat.collections()
yield from cat.collections()

def items(self):
""" Recursively get all items within this Catalog """
for item in self.links('item'):
yield Item.open(item)
for child in self.children():
# Python 2
for x in child.items():
yield x
# Python 3.3+
# yield from child.items()
yield from child.items()

def add_catalog(self, catalog):
def add_catalog(self, catalog, basename='catalog'):
""" Add a catalog to this catalog """
if self.filename is None:
raise STACError('Save catalog before adding sub-catalogs')
# add new catalog child link
child_link = '%s/catalog.json' % catalog.id
child_link = '%s/%s.json' % (catalog.id, basename)
child_fname = os.path.join(self.path, child_link)
child_path = os.path.dirname(child_fname)
root_link = self.links('root')[0]
root_links = self.links('root')
root_link = root_links[0] if len(root_links) > 0 else self.filename
root_path = os.path.dirname(root_link)
self.add_link('child', child_link)
self.save()
# strip self, parent, child links from catalog and add new links
catalog.clean_hierarchy()
catalog.add_link('self', os.path.join(self.endpoint(), os.path.relpath(child_fname, root_path)))
catalog.add_link('root', os.path.relpath(root_link, child_path))
catalog.add_link('parent', os.path.relpath(self.filename, child_path))
# create catalog file
catalog.save_as(child_fname)
catalog.save(filename=child_fname)
return self

def endpoint(self):
""" Get endpoint URL to the root catalog """
return os.path.dirname(self.root().links('self')[0])

def publish(self, endpoint, root=None):
""" Update all self links throughout catalog to use new endpoint """
# we don't use the catalogs and items functions as we'd have to go
# through the tree twice, once for catalogs and once for items
# update myself
if root is None:
root = self.filename
super(Catalog, self).publish(endpoint, root=root)
# update direct items
for link in self.links('item'):
item = Item.open(link)
item.publish(endpoint, root=root)
# follow children
for cat in self.children():
cat.publish(endpoint, root=root)

def add_collection(self, catalog, basename='collection'):
""" Add a collection to this catalog """
return self.add_catalog(catalog, basename=basename)


# import and end of module prevents problems with circular dependencies.
Expand Down
19 changes: 3 additions & 16 deletions satstac/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,7 @@ def parse_args(args):
parser.add_argument('id', help='ID of the new catalog')
parser.add_argument('description', help='Description of new catalog')
parser.add_argument('--filename', help='Filename of catalog', default='catalog.json')
group = parser.add_argument_group('root catalog options (mutually exclusive)')
group = group.add_mutually_exclusive_group(required=True)
group.add_argument('--root', help='Filename to existing root catalog', default=None)
group.add_argument('--endpoint', help='Endpoint for this new root catalog', default=None)

# command 2
h = 'Update entire catalog with a new endpoint (update self links)'
parser = subparsers.add_parser('publish', parents=[pparser], help=h, formatter_class=dhf)
parser.add_argument('root', help='Filename to existing root catalog')
parser.add_argument('endpoint', help='New endpoint')
# parser.add_argument()
parser.add_argument('--root', help='Filename to existing root catalog', default=None)

# turn Namespace into dictinary
parsed_args = vars(parser0.parse_args(args))
Expand All @@ -57,11 +47,8 @@ def cli():
cat = Catalog.create(id=args['id'], description=args['description'])
root.add_catalog(cat)
else:
cat = Catalog.create(id=args['id'], description=args['description'], root=args['endpoint'])
cat.save_as(args['filename'])
elif cmd == 'publish':
cat = Catalog.open(args['root'])
cat.publish(args['endpoint'])
cat = Catalog.create(id=args['id'], description=args['description'])
cat.save(filename=args['filename'])


if __name__ == "__main__":
Expand Down
19 changes: 9 additions & 10 deletions satstac/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,32 @@ def __init__(self, *args, **kwargs):

@property
def title(self):
return self.data.get('title', '')
return self._data.get('title', '')

@property
def keywords(self):
return self.data.get('keywords', [])
return self._data.get('keywords', [])

@property
def version(self):
return self.data.get('version', '')
return self._data.get('version', '')

@property
def license(self):
return self.data.get('license')
return self._data.get('license')

@property
def providers(self):
return self.data.get('providers', [])
return self._data.get('providers', [])

@property
def extent(self):
return self.data.get('extent')
return self._data.get('extent')

@property
def properties(self):
""" Get dictionary of properties """
return self.data.get('properties', {})
return self._data.get('properties', {})

@functools.lru_cache()
def parent_catalog(self, path):
Expand All @@ -63,7 +63,7 @@ def parent_catalog(self, path):
except STACError as err:
# create a new sub-catalog
subcat = self.create(id=d, description='%s catalog' % var_names[i])
subcat.save_as(fname)
subcat.save(filename=fname)
# add the sub-catalog to this catalog
cat.add_catalog(subcat)
cat = subcat
Expand All @@ -88,13 +88,12 @@ def add_item(self, item, path='', filename='${id}'):

# create links from item
item.clean_hierarchy()
item.add_link('self', os.path.join(self.endpoint(), os.path.relpath(item_fname, root_path)))
item.add_link('root', os.path.relpath(root_link, item_path))
item.add_link('parent', os.path.relpath(parent.filename, item_path))
# this assumes the item has been added to a Collection, not a Catalog
item.add_link('collection', os.path.relpath(self.filename, item_path))

# save item
item.save_as(item_fname)
item.save(filename=item_fname)
logger.debug('Added %s in %s seconds' % (item.filename, datetime.now()-start))
return self
10 changes: 5 additions & 5 deletions satstac/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def eobands(self):
@property
def properties(self):
""" Get dictionary of properties """
return self.data.get('properties', {})
return self._data.get('properties', {})

def __getitem__(self, key):
""" Get key from properties """
Expand All @@ -68,17 +68,17 @@ def datetime(self):

@property
def geometry(self):
return self.data['geometry']
return self._data['geometry']

@property
def bbox(self):
""" Get bounding box of scene """
return self.data['bbox']
return self._data['bbox']

@property
def assets(self):
""" Return dictionary of assets """
return self.data.get('assets', {})
return self._data.get('assets', {})

@property
def assets_by_common_name(self):
Expand Down Expand Up @@ -126,7 +126,7 @@ def substitute(self, string):
def download_assets(self, keys=None, **kwargs):
""" Download multiple assets """
if keys is None:
keys = self.data['assets'].keys()
keys = self._data['assets'].keys()
filenames = []
for key in keys:
filenames.append(self.download(key, **kwargs))
Expand Down
4 changes: 2 additions & 2 deletions satstac/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,11 @@ def save(self, filename):

def geojson(self):
""" Get Items as GeoJSON FeatureCollection """
features = [s.data for s in self._items]
features = [s._data for s in self._items]
geoj = {
'type': 'FeatureCollection',
'features': features,
'collections': [c.data for c in self._collections],
'collections': [c._data for c in self._collections],
}
if self._search is not None:
geoj['search'] = self._search
Expand Down
Loading

0 comments on commit 88c07de

Please sign in to comment.