Source code for descarteslabs.catalog.product

# Copyright 2018-2024 Descartes Labs.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ..common.collection import Collection
from ..common.property_filtering import Properties
from .attributes import (
    BooleanAttribute,
    ListAttribute,
    Resolution,
    Timestamp,
    TypedAttribute,
)
from .catalog_base import (
    AuthCatalogObject,
    CatalogClient,
    check_deleted,
)
from .task import TaskStatus


properties = Properties()


[docs]class Product(AuthCatalogObject): """A raster product that connects band information to imagery. Instantiating a product indicates that you want to create a *new* Descartes Labs catalog product. If you instead want to retrieve an existing catalog product use `Product.get() <descarteslabs.catalog.Product.get>`, or if you're not sure use `Product.get_or_create() <descarteslabs.catalog.Product.get_or_create>`. You can also use `Product.search() <descarteslabs.catalog.Product.search>`. Also see the example for :py:meth:`~descarteslabs.catalog.Product.save`. Parameters ---------- client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. kwargs : dict With the exception of readonly attributes (`created`, `modified`, `resolution_min`, and `resolution_max`) and with the exception of properties (`ATTRIBUTES`, `is_modified`, and `state`), any attribute listed below can also be used as a keyword argument. Also see `~Product.ATTRIBUTES`. """ _doc_type = "product" _url = "/products" # _collection_type set below due to circular problems # Product Attributes name = TypedAttribute( str, doc="""str: The name of this product. This should not be confused with a band name or image name. Unlike the band name and image name, this name is not unique and purely for display purposes and is used by :py:meth:`Search.find_text`. It can contain a string with up to 2000 arbitrary characters. *Searchable, sortable*. """, ) description = TypedAttribute( str, doc="""str, optional: A description with further details on this product. The description can be up to 80,000 characters and is used by :py:meth:`Search.find_text`. *Searchable* """, ) is_core = BooleanAttribute( doc="""bool, optional: Whether this is a Descartes Labs catalog core product. A core product is a product that is fully supported by Descartes Labs. By default this value is ``False`` and you must have a special permission (``descarteslabs:core:create``) to set it to ``True``. *Filterable, sortable*. """ ) revisit_period_minutes_min = TypedAttribute( float, coerce=True, doc="""float, optional: Minimum length of the time interval between observations. The minimum length of the time interval between observations of any given area in minutes. *Filterable, sortable*. """, ) revisit_period_minutes_max = TypedAttribute( float, coerce=True, doc="""float, optional: Maximum length of the time interval between observations. The maximum length of the time interval between observations of any given area in minutes. *Filterable, sortable*. """, ) start_datetime = Timestamp( doc="""str or datetime, optional: The beginning of the mission for this product. *Filterable, sortable*. """ ) end_datetime = Timestamp( doc="""str or datetime, optional: The end of the mission for this product. *Filterable, sortable*. """ ) resolution_min = Resolution( readonly=True, doc="""Resolution, readonly: Minimum resolution of the bands for this product. If applying a filter with a plain unitless number the value is assumed to be in meters. *Filterable, sortable*. """, ) resolution_max = Resolution( readonly=True, doc="""Resolution, readonly: Maximum resolution of the bands for this product. If applying a filter with a plain unitless number the value is assumed to be in meters. *Filterable, sortable*. """, ) default_display_bands = ListAttribute( TypedAttribute(str), doc="""list(str) or iterable: Which bands to use for RGBA display. This field defines the default bands that are used for display purposes. There are four supported formats: ``["greyscale-or-class"]``, ``["greyscale-or-class", "alpha"]``, ``["red", "green", "blue"]``, and ``["red", "green", "blue", "alpha"]``. """, ) image_index_name = TypedAttribute( str, doc="""str: The name of the image index for this product. This is an internal field, accessible to privileged users only. *Filterable, sortable*. """, ) product_tier = TypedAttribute( str, doc="""str: Product tier for this product. This field can be set by privileged users only. *Filterable, sortable*. """, )
[docs] def named_id(self, name): """Return the ~descarteslabs.catalog.NamedCatalogObject.id` for the given named catalog object. Parameters ---------- name : str The name of the catalog object within this product, see :py:attr:`~descarteslabs.catalog.NamedCatalogObject.name`. Returns ------- str The named catalog object id within this product. """ return "{}:{}".format(self.id, name)
[docs] @check_deleted def get_band(self, name, client=None, request_params=None, headers=None): """Retrieve the request band associated with this product by name. Parameters ---------- name : str The name of the band to retrieve. client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. Returns ------- Band or None A derived class of `Band` that represents the requested band object if found, ``None`` if not found. """ from .band import Band return Band.get( self.named_id(name), client=client, request_params=request_params, headers=headers, )
[docs] @check_deleted def get_image(self, name, client=None, request_params=None, headers=None): """Retrieve the request image associated with this product by name. Parameters ---------- name : str The name of the image to retrieve. client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. Returns ------- ~descarteslabs.catalog.Image or None The requested image if found, or ``None`` if not found. """ from .image import Image return Image.get( self.named_id(name), client=client, request_params=request_params, headers=headers, )
[docs] @check_deleted def get_delete_status(self): """Fetches the status of a deletion task. Fetches the status of a deletion task started using :py:meth:`delete_related_objects`. Returns ------- DeletionTaskStatus Raises ------ DeletedObjectError If this product was deleted. ~descarteslabs.exceptions.ClientError or ~descarteslabs.exceptions.ServerError :ref:`Spurious exception <network_exceptions>` that can occur during a network request. """ r = self._client.session.get( "/products/{}/delete_related_objects".format(self.id) ) response = r.json() return DeletionTaskStatus( id=self.id, _client=self._client, **response["data"]["attributes"] )
[docs] @check_deleted def bands(self, request_params=None, headers=None): """A search query for all bands for this product, sorted by default band ``sort_order``. Returns ------- :py:class:`~descarteslabs.catalog.Search` A :py:class:`~descarteslabs.catalog.Search` instance configured to find all bands for this product. Raises ------ DeletedObjectError If this product was deleted. """ from .band import Band return ( Band.search( client=self._client, request_params=request_params, headers=headers ) .filter(properties.product_id == self.id) .sort("sort_order") )
[docs] @check_deleted def images(self, request_params=None, headers=None): """A search query for all images in this product. Returns ------- :py:class:`~descarteslabs.catalog.Search` A :py:class:`~descarteslabs.catalog.Search` instance configured to find all images in this product. Raises ------ DeletedObjectError If this product was deleted. """ from .image import Image return Image.search( client=self._client, request_params=request_params, headers=headers ).filter(properties.product_id == self.id)
[docs] @check_deleted def image_uploads(self): """A search query for all uploads in this product created by this user. Returns ------- :py:class:`~descarteslabs.catalog.Search` A :py:class:`~descarteslabs.catalog.Search` instance configured to find all uploads in this product. Raises ------ DeletedObjectError If this product was deleted. """ from .image_upload import ImageUpload return ImageUpload.search(client=self._client).filter( properties.product_id == self.id )
[docs] @classmethod def namespace_id(cls, id_, client=None): """Generate a fully namespaced id. Parameters ---------- id_ : str The unprefixed part of the id that you want prefixed. client : CatalogClient, optional A `CatalogClient` instance to use for requests to the Descartes Labs catalog. The :py:meth:`~descarteslabs.catalog.CatalogClient.get_default_client` will be used if not set. Returns ------- str The fully namespaced id. Example ------- >>> product_id = Product.namespace_id("my-product") """ if client is None: client = CatalogClient.get_default_client() org = client.auth.payload.get("org") if org is None: org = client.auth.namespace # defaults to the user namespace prefix = "{}:".format(org) if id_.startswith(prefix): return id_ return "{}{}".format(prefix, id_)
[docs]class ProductCollection(Collection): _item_type = Product
# handle circular references Product._collection_type = ProductCollection
[docs]class DeletionTaskStatus(TaskStatus): """The asynchronous deletion task's status Attributes ---------- id : str The id of the object for which this task is running. status : TaskState The state of the task as explained in `TaskState`. start_datetime : datetime The date and time at which the task started running. duration_in_seconds : float The duration of the task. objects_deleted : int The number of objects (a combination of bands or images) that were deleted. errors: list In case the status is ``FAILED`` this will contain a list of errors that were encountered. In all other states this will not be set. """ _task_name = "delete task" _url = "/products/{}/delete_related_objects" def __init__(self, objects_deleted=None, **kwargs): super(DeletionTaskStatus, self).__init__(**kwargs) self.objects_deleted = objects_deleted def __repr__(self): text = super(DeletionTaskStatus, self).__repr__() if self.objects_deleted: text += "\n - {:,} objects deleted".format(self.objects_deleted) return text