diff --git a/CHANGES.rst b/CHANGES.rst index 95d0a75..04a7619 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,6 @@ .. This file is part of BDC-Collectors. - Copyright (C) 2023 INPE. + Copyright (C) 2025 INPE. BDC-Collectors is a free software; you can redistribute it and/or modify it under the terms of the MIT License; see LICENSE file for more details. @@ -10,6 +10,14 @@ Changes ======= +Version 1.0.3 (2025-02-21) +-------------------------- + +- Update USGS Connector API to support login-token (#103) +- Remove warnings from docs buiding +- Review Dockerfile version + + Version 1.0.2 (2024-12-30) -------------------------- diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4f7ee3f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +# +# This file is part of Brazil Data Cube BDC-Collectors. +# Copyright (C) 2023 INPE. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +ARG GIT_COMMIT +ARG BASE_IMAGE=python:3.11-bullseye +FROM ${BASE_IMAGE} + +ARG GIT_COMMIT + +LABEL "org.repo.maintainer"="Brazil Data Cube " +LABEL "org.repo.title"="Docker image for BDC Collectors" +LABEL "org.repo.description"="Docker image to collect data from multiple providers." +LABEL "org.repo.git_commit"="${GIT_COMMIT}" + +# Build arguments +ARG APP_INSTALL_PATH="/opt/bdc-collectors" + +ADD . ${APP_INSTALL_PATH} + +WORKDIR ${APP_INSTALL_PATH} + +RUN python3 -m pip install pip --upgrade setuptools wheel --no-cache && \ + python3 -m pip install --no-cache -e .[docs,tests,raster] + +CMD ["bdc-collector"] \ No newline at end of file diff --git a/USAGE.rst b/USAGE.rst index cd7089d..15f16ca 100644 --- a/USAGE.rst +++ b/USAGE.rst @@ -291,3 +291,80 @@ The following code is an example of an ``entry_points`` in ``setup.py`` file: 'mycatalog = my_app.mycatalog' ] } + + +Creating a new provider based on existent provider +++++++++++++++++++++++++++++++++++++++++++++++++++ + +This example shows how to create your own provider based on :class:`bdc_collectors.dataspace.DataspaceProvider`. +You can also use this if you would like to implement a different provider from scratch. + + +This example assume you have a minimal package named ``my-app`` with the following structure:: + + my-app/ + ├── setup.py + └── my_app/ + ├── __init__.py + └── api.py + + +Create a file named ``my_app/api.py`` and use the following snippet for a minimal set up for inheriting from :class:`bdc_collectors.dataspace.DataspaceProvider`:: + + from typing import Type + + from ..base import BaseCollection + from ..dataspace import DataspaceProvider + + def init_provider(): + return dict(MyProvider=MyProviderAPI) + + + class MyProviderAPI(DataspaceProvider): + def __init__(self, *args, **kwargs): + kwargs.setdefault("api_url", "https://my-url-provider-hub") + super(MyProviderAPI, self).__init__(*args, **kwargs) + def get_collector(self, collection: str) -> Type[BaseCollection]: + # ... logic for custom data collection + return super().get_collector(collection) # optional + def search(self, *args, **kwargs): + # ... logic for search collection + return super().search(*args, **kwargs) # optional + def download(self, *args, **kwargs): + # ... logic for search collection + return super().download(*args, **kwargs) # optional + + +The file ``my_app/__init__.py`` should export the ``MyProviderAPI`` as following:: + + from .api import MyProviderAPI, init_provider + + __all__ = ( + "MyProviderAPI", + "init_provider" + ) + + +Edit ``setup.py`` and register your module with the following statement:: + + + entry_points={ + 'bdc_collectors.providers': [ + 'myprovider = my_app' + ] + } + +After that, you can install the module and it will be loaded whenever you call the extension like as following:: + + from bdc_collectors.ext import CollectorExtension + from flask import Flask + + app = Flask(__name__) + ext = CollectorExtension(app) # flask_app + catalog = ext.get_provider("MyProvider")(parameter1="x", parameter2="b", ...) + catalog.search(...) + + # Or use directly + # from my_app import MyProviderAPI + # catalog = MyProviderAPI(parameter1="x", parameter2="b", ...) + # catalog.search(...) diff --git a/bdc_collectors/usgs/__init__.py b/bdc_collectors/usgs/__init__.py index ecd699d..164547d 100644 --- a/bdc_collectors/usgs/__init__.py +++ b/bdc_collectors/usgs/__init__.py @@ -83,11 +83,10 @@ def __init__(self, **kwargs): self.kwargs = kwargs - if lazy: - self.api = None - else: - self.api = LandsatApi(self.kwargs['username'], self.kwargs['password']) - self.ee = EarthExplorer(self.kwargs['username'], self.kwargs['password']) + self.api = None + + if not lazy: + self._api() def _set_default_collections(self, datasets, data_type): for dataset in datasets: @@ -97,7 +96,7 @@ def _set_default_collections(self, datasets, data_type): def _api(self): """Lazy API instance.""" if self.api is None: - self.api = LandsatApi(self.kwargs['username'], self.kwargs['password']) + self.api = LandsatApi(self.kwargs['username'], self.kwargs['token']) self.ee = EarthExplorer(self.kwargs['username'], self.kwargs['password']) def get_collector(self, collection: str) -> Type[BaseCollection]: diff --git a/bdc_collectors/usgs/api.py b/bdc_collectors/usgs/api.py index b1e72c3..ec13571 100644 --- a/bdc_collectors/usgs/api.py +++ b/bdc_collectors/usgs/api.py @@ -38,6 +38,10 @@ class LandsatApi: This interface follows the `JSON API 1.5 `_ stable spec. Use this API directly only if you really need. Otherwise use `bdc_collectors.usgs.USGS` instead. + + Note: + This interface is already according to M2M Login Tokens. + Consider ``password`` as ``token``. """ api_url: str = 'https://m2m.cr.usgs.gov/api/api/json/{version}' @@ -46,7 +50,7 @@ class LandsatApi: _filters: Dict[str, Any] - def __init__(self, username: str, password: str, version: str = 'stable', + def __init__(self, username: str, token: str, version: str = 'stable', lazy: bool = False, progress: bool = False, **kwargs): """Build a API instance.""" self.api_url = self.api_url.format(version=version) @@ -54,7 +58,7 @@ def __init__(self, username: str, password: str, version: str = 'stable', self.session.headers.update(**{'Content-Type': 'application/json'}) self._credentials = dict( username=username, - password=password, + token=token, **kwargs ) self.progress = progress @@ -71,7 +75,7 @@ def login(self): Raises: RuntimeError for any error occurred. """ - response = self.session.post(f'{self.api_url}/login', json=self._credentials, + response = self.session.post(f'{self.api_url}/login-token', json=self._credentials, headers={'Content-Type': 'application/json'}) # TODO: validate content type diff --git a/bdc_collectors/version.py b/bdc_collectors/version.py index 0bd7858..4ee027b 100644 --- a/bdc_collectors/version.py +++ b/bdc_collectors/version.py @@ -18,4 +18,4 @@ """Version information for BDC-Collectors.""" -__version__ = '1.0.2' +__version__ = '1.0.3'