eodag 3.10.0__py3-none-any.whl → 4.0.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. eodag/api/core.py +378 -419
  2. eodag/api/product/__init__.py +3 -3
  3. eodag/api/product/_product.py +68 -40
  4. eodag/api/product/drivers/__init__.py +3 -5
  5. eodag/api/product/drivers/base.py +1 -18
  6. eodag/api/product/metadata_mapping.py +151 -215
  7. eodag/api/search_result.py +13 -7
  8. eodag/cli.py +72 -395
  9. eodag/config.py +46 -50
  10. eodag/plugins/apis/base.py +2 -2
  11. eodag/plugins/apis/ecmwf.py +20 -21
  12. eodag/plugins/apis/usgs.py +37 -33
  13. eodag/plugins/authentication/base.py +1 -3
  14. eodag/plugins/crunch/filter_date.py +3 -3
  15. eodag/plugins/crunch/filter_latest_intersect.py +2 -2
  16. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  17. eodag/plugins/download/aws.py +45 -41
  18. eodag/plugins/download/base.py +13 -14
  19. eodag/plugins/download/http.py +65 -65
  20. eodag/plugins/manager.py +28 -29
  21. eodag/plugins/search/__init__.py +3 -4
  22. eodag/plugins/search/base.py +128 -77
  23. eodag/plugins/search/build_search_result.py +105 -107
  24. eodag/plugins/search/cop_marine.py +44 -47
  25. eodag/plugins/search/csw.py +33 -33
  26. eodag/plugins/search/qssearch.py +335 -354
  27. eodag/plugins/search/stac_list_assets.py +1 -1
  28. eodag/plugins/search/static_stac_search.py +31 -31
  29. eodag/resources/{product_types.yml → collections.yml} +2353 -2429
  30. eodag/resources/ext_collections.json +1 -1
  31. eodag/resources/providers.yml +2427 -2719
  32. eodag/resources/stac_provider.yml +46 -90
  33. eodag/types/queryables.py +55 -91
  34. eodag/types/search_args.py +1 -1
  35. eodag/utils/__init__.py +94 -21
  36. eodag/utils/exceptions.py +6 -6
  37. eodag/utils/free_text_search.py +3 -3
  38. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/METADATA +10 -87
  39. eodag-4.0.0a1.dist-info/RECORD +92 -0
  40. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/entry_points.txt +0 -4
  41. eodag/plugins/authentication/oauth.py +0 -60
  42. eodag/plugins/download/creodias_s3.py +0 -71
  43. eodag/plugins/download/s3rest.py +0 -351
  44. eodag/plugins/search/data_request_search.py +0 -565
  45. eodag/resources/stac.yml +0 -294
  46. eodag/resources/stac_api.yml +0 -2105
  47. eodag/rest/__init__.py +0 -24
  48. eodag/rest/cache.py +0 -70
  49. eodag/rest/config.py +0 -67
  50. eodag/rest/constants.py +0 -26
  51. eodag/rest/core.py +0 -764
  52. eodag/rest/errors.py +0 -210
  53. eodag/rest/server.py +0 -604
  54. eodag/rest/server.wsgi +0 -6
  55. eodag/rest/stac.py +0 -1032
  56. eodag/rest/templates/README +0 -1
  57. eodag/rest/types/__init__.py +0 -18
  58. eodag/rest/types/collections_search.py +0 -44
  59. eodag/rest/types/eodag_search.py +0 -386
  60. eodag/rest/types/queryables.py +0 -174
  61. eodag/rest/types/stac_search.py +0 -272
  62. eodag/rest/utils/__init__.py +0 -207
  63. eodag/rest/utils/cql_evaluate.py +0 -119
  64. eodag/rest/utils/rfc3339.py +0 -64
  65. eodag-3.10.0.dist-info/RECORD +0 -116
  66. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/WHEEL +0 -0
  67. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
  68. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/top_level.txt +0 -0
eodag/config.py CHANGED
@@ -64,8 +64,8 @@ from eodag.utils.exceptions import ValidationError
64
64
 
65
65
  logger = logging.getLogger("eodag.config")
66
66
 
67
- EXT_PRODUCT_TYPES_CONF_URI = (
68
- "https://cs-si.github.io/eodag/eodag/resources/ext_product_types.json"
67
+ EXT_COLLECTIONS_CONF_URI = (
68
+ "https://cs-si.github.io/eodag/eodag/resources/ext_collections.json"
69
69
  )
70
70
  AUTH_TOPIC_KEYS = ("auth", "search_auth", "download_auth")
71
71
  PLUGINS_TOPICS_KEYS = ("api", "search", "download") + AUTH_TOPIC_KEYS
@@ -114,7 +114,7 @@ class ProviderConfig(yaml.YAMLObject):
114
114
  Lower value means lower priority. (Default: 0)
115
115
  :param api: (optional) The configuration of a plugin of type Api
116
116
  :param search: (optional) The configuration of a plugin of type Search
117
- :param products: (optional) The products types supported by the provider
117
+ :param products: (optional) The collections supported by the provider
118
118
  :param download: (optional) The configuration of a plugin of type Download
119
119
  :param auth: (optional) The configuration of a plugin of type Authentication
120
120
  :param search_auth: (optional) The configuration of a plugin of type Authentication for search
@@ -135,7 +135,7 @@ class ProviderConfig(yaml.YAMLObject):
135
135
  auth: PluginConfig
136
136
  search_auth: PluginConfig
137
137
  download_auth: PluginConfig
138
- product_types_fetched: bool # set in core.update_product_types_list
138
+ collections_fetched: bool # set in core.update_collections_list
139
139
 
140
140
  yaml_loader = yaml.Loader
141
141
  yaml_dumper = yaml.SafeDumper
@@ -274,17 +274,19 @@ class PluginConfig(yaml.YAMLObject):
274
274
  metadata_path: str
275
275
  #: list search parameters to send as is to the provider
276
276
  search_param_unparsed: list[str]
277
+ #: Use as STAC extension prefix if it does not have one already
278
+ metadata_prefix: str
277
279
  #: Whether an error must be raised when using a search parameter which is not queryable or not
278
280
  raise_mtd_discovery_error: bool
279
281
 
280
- class DiscoverProductTypes(TypedDict, total=False):
281
- """Configuration for product types discovery"""
282
+ class DiscoverCollections(TypedDict, total=False):
283
+ """Configuration for collections discovery"""
282
284
 
283
- #: URL from which the product types can be fetched
285
+ #: URL from which the collections can be fetched
284
286
  fetch_url: Optional[str]
285
- #: HTTP method used to fetch product types
287
+ #: HTTP method used to fetch collections
286
288
  fetch_method: str
287
- #: Request body to fetch product types using POST method
289
+ #: Request body to fetch collections using POST method
288
290
  fetch_body: dict[str, Any]
289
291
  #: Maximum number of connections for concurrent HTTP requests
290
292
  max_connections: int
@@ -294,32 +296,32 @@ class PluginConfig(yaml.YAMLObject):
294
296
  start_page: int
295
297
  #: Type of the provider result
296
298
  result_type: str
297
- #: JsonPath to the list of product types
299
+ #: JsonPath to the list of collections
298
300
  results_entry: Union[JSONPath, str]
299
- #: Mapping for the product type id
300
- generic_product_type_id: str
301
- #: Mapping for product type metadata (e.g. ``abstract``, ``licence``) which can be parsed from the provider
301
+ #: Mapping for the collection id
302
+ generic_collection_id: str
303
+ #: Mapping for collection metadata (e.g. ``abstract``, ``licence``) which can be parsed from the provider
302
304
  #: result
303
- generic_product_type_parsable_metadata: dict[str, str]
304
- #: Mapping for product type properties which can be parsed from the result and are not product type metadata
305
- generic_product_type_parsable_properties: dict[str, str]
306
- #: Mapping for product type properties which cannot be parsed from the result and are not product type metadata
307
- generic_product_type_unparsable_properties: dict[str, str]
305
+ generic_collection_parsable_metadata: dict[str, str]
306
+ #: Mapping for collection properties which can be parsed from the result and are not collection metadata
307
+ generic_collection_parsable_properties: dict[str, str]
308
+ #: Mapping for collection properties which cannot be parsed from the result and are not collection metadata
309
+ generic_collection_unparsable_properties: dict[str, str]
308
310
  #: URL to fetch data for a single collection
309
311
  single_collection_fetch_url: str
310
312
  #: Query string to be added to the fetch_url to filter for a collection
311
313
  single_collection_fetch_qs: str
312
- #: Mapping for product type metadata returned by the endpoint given in single_collection_fetch_url. If ``ID``
313
- #: is redefined in this mapping, it will replace ``generic_product_type_id`` value
314
- single_product_type_parsable_metadata: dict[str, str]
314
+ #: Mapping for collection metadata returned by the endpoint given in single_collection_fetch_url. If ``ID``
315
+ #: is redefined in this mapping, it will replace ``generic_collection_id`` value
316
+ single_collection_parsable_metadata: dict[str, str]
315
317
 
316
318
  class DiscoverQueryables(TypedDict, total=False):
317
319
  """Configuration for queryables discovery"""
318
320
 
319
- #: URL to fetch the queryables valid for all product types
321
+ #: URL to fetch the queryables valid for all collections
320
322
  fetch_url: Optional[str]
321
- #: URL to fetch the queryables for a specific product type
322
- product_type_fetch_url: Optional[str]
323
+ #: URL to fetch the queryables for a specific collection
324
+ collection_fetch_url: Optional[str]
323
325
  #: Type of the result
324
326
  result_type: str
325
327
  #: JsonPath to retrieve the queryables from the provider result
@@ -431,8 +433,8 @@ class PluginConfig(yaml.YAMLObject):
431
433
  # search & api -----------------------------------------------------------------------------------------------------
432
434
  # copied from ProviderConfig in PluginManager.get_search_plugins()
433
435
  priority: int
434
- # per product type metadata-mapping, set in core._prepare_search
435
- product_type_config: dict[str, Any]
436
+ # per collection metadata-mapping, set in core._prepare_search
437
+ collection_config: dict[str, Any]
436
438
 
437
439
  #: :class:`~eodag.plugins.search.base.Search` Plugin API endpoint
438
440
  api_endpoint: str
@@ -449,14 +451,14 @@ class PluginConfig(yaml.YAMLObject):
449
451
  sort: PluginConfig.Sort
450
452
  #: :class:`~eodag.plugins.search.base.Search` Configuration for the metadata auto-discovery
451
453
  discover_metadata: PluginConfig.DiscoverMetadata
452
- #: :class:`~eodag.plugins.search.base.Search` Configuration for the product types auto-discovery
453
- discover_product_types: PluginConfig.DiscoverProductTypes
454
+ #: :class:`~eodag.plugins.search.base.Search` Configuration for the collections auto-discovery
455
+ discover_collections: PluginConfig.DiscoverCollections
454
456
  #: :class:`~eodag.plugins.search.base.Search` Configuration for the queryables auto-discovery
455
457
  discover_queryables: PluginConfig.DiscoverQueryables
456
458
  #: :class:`~eodag.plugins.search.base.Search` The mapping between eodag metadata and the plugin specific metadata
457
459
  metadata_mapping: dict[str, Union[str, list[str]]]
458
460
  #: :class:`~eodag.plugins.search.base.Search` :attr:`~eodag.config.PluginConfig.metadata_mapping` got from the given
459
- #: product type
461
+ #: collection
460
462
  metadata_mapping_from_product: str
461
463
  #: :class:`~eodag.plugins.search.base.Search` A mapping for the metadata of individual assets
462
464
  assets_mapping: dict[str, dict[str, Any]]
@@ -476,26 +478,19 @@ class PluginConfig(yaml.YAMLObject):
476
478
  per_product_metadata_query: bool
477
479
  #: :class:`~eodag.plugins.search.qssearch.ODataV4Search` Dict used to simplify further metadata extraction
478
480
  metadata_pre_mapping: PluginConfig.MetadataPreMapping
479
- #: :class:`~eodag.plugins.search.data_request_search.DataRequestSearch` URL to which the data request shall be sent
480
- data_request_url: str
481
- #: :class:`~eodag.plugins.search.data_request_search.DataRequestSearch` URL to fetch the status of the data request
482
- status_url: str
483
- #: :class:`~eodag.plugins.search.data_request_search.DataRequestSearch`
484
- #: URL to fetch the search result when the data request is done
485
- result_url: str
486
- #: :class:`~eodag.plugins.search.data_request_search.DataRequestSearch`
487
- #: if date parameters are mandatory in the request
488
- dates_required: bool
489
481
  #: :class:`~eodag.plugins.search.csw.CSWSearch` Search definition dictionary
490
482
  search_definition: dict[str, Any]
491
483
  #: :class:`~eodag.plugins.search.qssearch.PostJsonSearch` Whether to merge responses or not (`aws_eos` specific)
492
484
  merge_responses: bool
493
485
  #: :class:`~eodag.plugins.search.qssearch.PostJsonSearch` Collections names (`aws_eos` specific)
494
- collection: list[str]
486
+ _collection: list[str]
495
487
  #: :class:`~eodag.plugins.search.static_stac_search.StaticStacSearch`
496
488
  #: Maximum number of connections for concurrent HTTP requests
497
489
  max_connections: int
498
490
  #: :class:`~eodag.plugins.search.build_search_result.ECMWFSearch`
491
+ #: if date parameters are mandatory in the request
492
+ dates_required: bool
493
+ #: :class:`~eodag.plugins.search.build_search_result.ECMWFSearch`
499
494
  #: Whether end date should be excluded from search request or not
500
495
  end_date_excluded: bool
501
496
  #: :class:`~eodag.plugins.search.build_search_result.ECMWFSearch`
@@ -521,9 +516,10 @@ class PluginConfig(yaml.YAMLObject):
521
516
  flatten_top_dirs: bool
522
517
  #: :class:`~eodag.plugins.download.base.Download` Level in extracted path tree where to find data
523
518
  archive_depth: int
524
- #: :class:`~eodag.plugins.download.base.Download` Whether ignore assets and download using ``downloadLink`` or not
519
+ #: :class:`~eodag.plugins.download.base.Download` Whether ignore assets and download using ``eodag:download_link``
520
+ #: or not
525
521
  ignore_assets: bool
526
- #: :class:`~eodag.plugins.download.base.Download` Product type specific configuration
522
+ #: :class:`~eodag.plugins.download.base.Download` Collection specific configuration
527
523
  products: dict[str, dict[str, Any]]
528
524
  #: :class:`~eodag.plugins.download.http.HTTPDownload` Whether the product has to be ordered to download it or not
529
525
  order_enabled: bool
@@ -542,7 +538,7 @@ class PluginConfig(yaml.YAMLObject):
542
538
  no_auth_download: bool
543
539
  #: :class:`~eodag.plugins.download.http.HTTPDownload` Parameters to be added to the query params of the request
544
540
  dl_url_params: dict[str, str]
545
- #: :class:`~eodag.plugins.download.s3rest.S3RestDownload`
541
+ #: :class:`~eodag.plugins.download.aws.AwsDownload`
546
542
  #: At which level of the path part of the url the bucket can be found
547
543
  bucket_path_level: int
548
544
  #: :class:`~eodag.plugins.download.aws.AwsDownload` S3 endpoint
@@ -1069,15 +1065,15 @@ def load_stac_provider_config() -> dict[str, Any]:
1069
1065
  ).source
1070
1066
 
1071
1067
 
1072
- def get_ext_product_types_conf(
1073
- conf_uri: str = EXT_PRODUCT_TYPES_CONF_URI,
1068
+ def get_ext_collections_conf(
1069
+ conf_uri: str = EXT_COLLECTIONS_CONF_URI,
1074
1070
  ) -> dict[str, Any]:
1075
- """Read external product types conf
1071
+ """Read external collections conf
1076
1072
 
1077
1073
  :param conf_uri: URI to local or remote configuration file
1078
- :returns: The external product types configuration
1074
+ :returns: The external collections configuration
1079
1075
  """
1080
- logger.info("Fetching external product types from %s", conf_uri)
1076
+ logger.info("Fetching external collections from %s", conf_uri)
1081
1077
  if conf_uri.lower().startswith("http"):
1082
1078
  # read from remote
1083
1079
  try:
@@ -1089,7 +1085,7 @@ def get_ext_product_types_conf(
1089
1085
  except requests.RequestException as e:
1090
1086
  logger.debug(e)
1091
1087
  logger.warning(
1092
- "Could not read remote external product types conf from %s", conf_uri
1088
+ "Could not read remote external collections conf from %s", conf_uri
1093
1089
  )
1094
1090
  return {}
1095
1091
  elif conf_uri.lower().startswith("file"):
@@ -1102,6 +1098,6 @@ def get_ext_product_types_conf(
1102
1098
  except (orjson.JSONDecodeError, FileNotFoundError) as e:
1103
1099
  logger.debug(e)
1104
1100
  logger.warning(
1105
- "Could not read local external product types conf from %s", conf_uri
1101
+ "Could not read local external collections conf from %s", conf_uri
1106
1102
  )
1107
1103
  return {}
@@ -45,8 +45,8 @@ class Api(Search, Download):
45
45
  (e.g. 'file:///tmp/product_folder' on Linux or
46
46
  'file:///C:/Users/username/AppData/Local/Temp' on Windows)
47
47
  - save a *record* file in the directory ``output_dir/.downloaded`` whose name
48
- is built on the MD5 hash of the product's ``product_type`` and ``properties['id']``
49
- attributes (``hashlib.md5((product.product_type+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
48
+ is built on the MD5 hash of the product's ``collection`` and ``properties['id']``
49
+ attributes (``hashlib.md5((product.collection+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
50
50
  and whose content is the product's ``remote_location`` attribute itself.
51
51
  - not try to download a product whose ``location`` attribute already points to an
52
52
  existing file/directory
@@ -35,6 +35,7 @@ from eodag.utils import (
35
35
  DEFAULT_DOWNLOAD_TIMEOUT,
36
36
  DEFAULT_DOWNLOAD_WAIT,
37
37
  DEFAULT_MISSION_START_DATE,
38
+ get_collection_dates,
38
39
  get_geometry_from_various,
39
40
  path_to_uri,
40
41
  sanitize,
@@ -52,7 +53,6 @@ if TYPE_CHECKING:
52
53
  from eodag.api.product import EOProduct
53
54
  from eodag.api.search_result import SearchResult
54
55
  from eodag.config import PluginConfig
55
- from eodag.types import S3SessionKwargs
56
56
  from eodag.types.download_args import DownloadConf
57
57
  from eodag.utils import DownloadedCallback, ProgressCallback, Unpack
58
58
 
@@ -114,25 +114,24 @@ class EcmwfApi(Api, ECMWFSearch):
114
114
  ) -> tuple[list[EOProduct], Optional[int]]:
115
115
  """Build ready-to-download SearchResult"""
116
116
 
117
- # check productType, dates, geometry, use defaults if not specified
118
- # productType
119
- if not kwargs.get("productType"):
120
- kwargs["productType"] = "%s_%s_%s" % (
117
+ # check collection, dates, geometry, use defaults if not specified
118
+ # collection
119
+ if not kwargs.get("collection"):
120
+ kwargs["collection"] = "%s_%s_%s" % (
121
121
  kwargs.get("ecmwf:dataset", "mars"),
122
122
  kwargs.get("ecmwf:type", ""),
123
123
  kwargs.get("ecmwf:levtype", ""),
124
124
  )
125
+
126
+ col_start, col_end = get_collection_dates(
127
+ getattr(self.config, "collection_config", {})
128
+ )
125
129
  # start date
126
- if "startTimeFromAscendingNode" not in kwargs:
127
- kwargs["startTimeFromAscendingNode"] = (
128
- getattr(self.config, "product_type_config", {}).get("missionStartDate")
129
- or DEFAULT_MISSION_START_DATE
130
- )
130
+ if "start_datetime" not in kwargs:
131
+ kwargs["start_datetime"] = col_start or DEFAULT_MISSION_START_DATE
131
132
  # end date
132
- if "completionTimeFromAscendingNode" not in kwargs:
133
- kwargs["completionTimeFromAscendingNode"] = getattr(
134
- self.config, "product_type_config", {}
135
- ).get("missionEndDate") or datetime.now(timezone.utc).isoformat(
133
+ if "end_datetime" not in kwargs:
134
+ kwargs["end_datetime"] = col_end or datetime.now(timezone.utc).isoformat(
136
135
  timespec="seconds"
137
136
  )
138
137
 
@@ -173,7 +172,7 @@ class EcmwfApi(Api, ECMWFSearch):
173
172
  def download(
174
173
  self,
175
174
  product: EOProduct,
176
- auth: Optional[Union[AuthBase, S3SessionKwargs, S3ServiceResource]] = None,
175
+ auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
177
176
  progress_callback: Optional[ProgressCallback] = None,
178
177
  wait: float = DEFAULT_DOWNLOAD_WAIT,
179
178
  timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
@@ -205,7 +204,7 @@ class EcmwfApi(Api, ECMWFSearch):
205
204
  os.makedirs(new_fs_path)
206
205
  fs_path = os.path.join(new_fs_path, os.path.basename(fs_path))
207
206
 
208
- # get download request dict from product.location/downloadLink url query string
207
+ # get download request dict from product.location/eodag:download_link url query string
209
208
  # separate url & parameters
210
209
  download_request = geojson.loads(urlsplit(product.location).query)
211
210
 
@@ -245,7 +244,7 @@ class EcmwfApi(Api, ECMWFSearch):
245
244
  raise DownloadError(e)
246
245
 
247
246
  with open(record_filename, "w") as fh:
248
- fh.write(product.properties["downloadLink"])
247
+ fh.write(product.properties["eodag:download_link"])
249
248
  logger.debug("Download recorded in %s", record_filename)
250
249
 
251
250
  # do not try to extract a directory
@@ -262,7 +261,7 @@ class EcmwfApi(Api, ECMWFSearch):
262
261
  def download_all(
263
262
  self,
264
263
  products: SearchResult,
265
- auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
264
+ auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
266
265
  downloaded_callback: Optional[DownloadedCallback] = None,
267
266
  progress_callback: Optional[ProgressCallback] = None,
268
267
  wait: float = DEFAULT_DOWNLOAD_WAIT,
@@ -291,9 +290,9 @@ class EcmwfApi(Api, ECMWFSearch):
291
290
  ) -> Optional[dict[str, Annotated[Any, FieldInfo]]]:
292
291
  """Fetch queryables list from provider using metadata mapping
293
292
 
294
- :param kwargs: additional filters for queryables (`productType` and other search
293
+ :param kwargs: additional filters for queryables (`collection` and other search
295
294
  arguments)
296
295
  :returns: fetched queryable parameters dict
297
296
  """
298
- product_type = kwargs.get("productType")
299
- return self.queryables_from_metadata_mapping(product_type)
297
+ collection = kwargs.get("collection")
298
+ return self.queryables_from_metadata_mapping(collection)
@@ -31,7 +31,6 @@ from usgs import USGSAuthExpiredError, USGSError, api
31
31
 
32
32
  from eodag.api.product import EOProduct
33
33
  from eodag.api.product.metadata_mapping import (
34
- DEFAULT_METADATA_MAPPING,
35
34
  mtd_cfg_as_conversion_and_querypath,
36
35
  properties_from_json,
37
36
  )
@@ -42,7 +41,7 @@ from eodag.utils import (
42
41
  DEFAULT_DOWNLOAD_WAIT,
43
42
  DEFAULT_ITEMS_PER_PAGE,
44
43
  DEFAULT_PAGE,
45
- GENERIC_PRODUCT_TYPE,
44
+ GENERIC_COLLECTION,
46
45
  USER_AGENT,
47
46
  ProgressCallback,
48
47
  format_dict_items,
@@ -50,7 +49,7 @@ from eodag.utils import (
50
49
  )
51
50
  from eodag.utils.exceptions import (
52
51
  AuthenticationError,
53
- NoMatchingProductType,
52
+ NoMatchingCollection,
54
53
  NotAvailableError,
55
54
  RequestError,
56
55
  ValidationError,
@@ -62,7 +61,6 @@ if TYPE_CHECKING:
62
61
 
63
62
  from eodag.api.search_result import SearchResult
64
63
  from eodag.config import PluginConfig
65
- from eodag.types import S3SessionKwargs
66
64
  from eodag.types.download_args import DownloadConf
67
65
  from eodag.utils import DownloadedCallback, Unpack
68
66
 
@@ -101,7 +99,7 @@ class UsgsApi(Api):
101
99
  # Same method as in base.py, Search.__init__()
102
100
  # Prepare the metadata mapping
103
101
  # Do a shallow copy, the structure is flat enough for this to be sufficient
104
- metas: dict[str, Any] = DEFAULT_METADATA_MAPPING.copy()
102
+ metas: dict[str, Any] = {}
105
103
  # Update the defaults with the mapping value. This will add any new key
106
104
  # added by the provider mapping that is not in the default metadata.
107
105
  metas.update(self.config.metadata_mapping)
@@ -148,23 +146,25 @@ class UsgsApi(Api):
148
146
  if prep.items_per_page is not None
149
147
  else DEFAULT_ITEMS_PER_PAGE
150
148
  )
151
- product_type = kwargs.get("productType")
152
- if product_type is None:
153
- raise NoMatchingProductType(
154
- "Cannot search on USGS without productType specified"
149
+ collection = kwargs.get("collection")
150
+ if collection is None:
151
+ raise NoMatchingCollection(
152
+ "Cannot search on USGS without collection specified"
155
153
  )
156
154
  if kwargs.get("sort_by"):
157
155
  raise ValidationError("USGS does not support sorting feature")
158
156
 
159
157
  self.authenticate()
160
158
 
161
- product_type_def_params = self.config.products.get( # type: ignore
162
- product_type,
163
- self.config.products[GENERIC_PRODUCT_TYPE], # type: ignore
159
+ collection_def_params = self.config.products.get( # type: ignore
160
+ collection,
161
+ self.config.products[GENERIC_COLLECTION], # type: ignore
164
162
  )
165
- usgs_dataset = format_dict_items(product_type_def_params, **kwargs)["dataset"]
166
- start_date = kwargs.pop("startTimeFromAscendingNode", None)
167
- end_date = kwargs.pop("completionTimeFromAscendingNode", None)
163
+ usgs_collection = format_dict_items(collection_def_params, **kwargs)[
164
+ "_collection"
165
+ ]
166
+ start_date = kwargs.pop("start_datetime", None)
167
+ end_date = kwargs.pop("end_datetime", None)
168
168
  geom = kwargs.pop("geometry", None)
169
169
  footprint: dict[str, str] = {}
170
170
  if hasattr(geom, "bounds"):
@@ -201,14 +201,16 @@ class UsgsApi(Api):
201
201
 
202
202
  # search by id
203
203
  if searched_id := kwargs.get("id"):
204
- dataset_filters = api.dataset_filters(usgs_dataset)
204
+ dataset_filters = api.dataset_filters(usgs_collection)
205
205
  # ip pattern set as parameter queryable (first element of param conf list)
206
206
  id_pattern = self.config.metadata_mapping["id"][0]
207
207
  # loop on matching dataset_filters until one returns expected results
208
208
  for dataset_filter in dataset_filters["data"]:
209
209
  if id_pattern in dataset_filter["searchSql"]:
210
210
  logger.debug(
211
- f"Try using {dataset_filter['searchSql']} dataset filter to search by id on {usgs_dataset}"
211
+ "Try using %s dataset filter to search by id on %s",
212
+ dataset_filter["searchSql"],
213
+ usgs_collection,
212
214
  )
213
215
  full_api_search_kwargs = {
214
216
  "where": {
@@ -218,19 +220,19 @@ class UsgsApi(Api):
218
220
  **api_search_kwargs,
219
221
  }
220
222
  logger.info(
221
- f"Sending search request for {usgs_dataset} with {full_api_search_kwargs}"
223
+ f"Sending search request for {usgs_collection} with {full_api_search_kwargs}"
222
224
  )
223
225
  results = api.scene_search(
224
- usgs_dataset, **full_api_search_kwargs
226
+ usgs_collection, **full_api_search_kwargs
225
227
  )
226
228
  if len(results["data"]["results"]) == 1:
227
229
  # search by id using this dataset_filter succeeded
228
230
  break
229
231
  else:
230
232
  logger.info(
231
- f"Sending search request for {usgs_dataset} with {api_search_kwargs}"
233
+ f"Sending search request for {usgs_collection} with {api_search_kwargs}"
232
234
  )
233
- results = api.scene_search(usgs_dataset, **api_search_kwargs)
235
+ results = api.scene_search(usgs_collection, **api_search_kwargs)
234
236
 
235
237
  # update results with storage info from download_options()
236
238
  results_by_entity_id = {
@@ -240,7 +242,7 @@ class UsgsApi(Api):
240
242
  f"Adapting {len(results_by_entity_id)} plugin results to eodag product representation"
241
243
  )
242
244
  download_options = api.download_options(
243
- usgs_dataset, list(results_by_entity_id.keys())
245
+ usgs_collection, list(results_by_entity_id.keys())
244
246
  )
245
247
  if download_options.get("data") is not None:
246
248
  for download_option in download_options["data"]:
@@ -262,7 +264,7 @@ class UsgsApi(Api):
262
264
  results["data"]["results"] = list(results_by_entity_id.values())
263
265
 
264
266
  for result in results["data"]["results"]:
265
- result["productType"] = usgs_dataset
267
+ result["collection"] = usgs_collection
266
268
 
267
269
  product_properties = properties_from_json(
268
270
  result, self.config.metadata_mapping
@@ -270,7 +272,7 @@ class UsgsApi(Api):
270
272
 
271
273
  final.append(
272
274
  EOProduct(
273
- productType=product_type,
275
+ collection=collection,
274
276
  provider=self.provider,
275
277
  properties=product_properties,
276
278
  geometry=footprint,
@@ -278,7 +280,7 @@ class UsgsApi(Api):
278
280
  )
279
281
  except USGSError as e:
280
282
  logger.warning(
281
- f"Product type {usgs_dataset} may not exist on USGS EE catalog"
283
+ f"Collection {usgs_collection} may not exist on USGS EE catalog"
282
284
  )
283
285
  api.logout()
284
286
  raise RequestError.from_error(e) from e
@@ -297,7 +299,7 @@ class UsgsApi(Api):
297
299
  def download(
298
300
  self,
299
301
  product: EOProduct,
300
- auth: Optional[Union[AuthBase, S3SessionKwargs, S3ServiceResource]] = None,
302
+ auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
301
303
  progress_callback: Optional[ProgressCallback] = None,
302
304
  wait: float = DEFAULT_DOWNLOAD_WAIT,
303
305
  timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
@@ -314,7 +316,7 @@ class UsgsApi(Api):
314
316
  output_extension = cast(
315
317
  str,
316
318
  self.config.products.get( # type: ignore
317
- product.product_type, self.config.products[GENERIC_PRODUCT_TYPE] # type: ignore
319
+ product.collection, self.config.products[GENERIC_COLLECTION] # type: ignore
318
320
  ).get("output_extension", ".tar.gz"),
319
321
  )
320
322
  kwargs["output_extension"] = kwargs.get("output_extension", output_extension)
@@ -335,11 +337,13 @@ class UsgsApi(Api):
335
337
  raise NotAvailableError(
336
338
  f"No USGS products found for {product.properties['id']}"
337
339
  )
338
-
340
+ usgs_dataset = self.config.products.get(product.collection, {}).get(
341
+ "_collection", GENERIC_COLLECTION
342
+ )
339
343
  download_request_results = api.download_request(
340
- product.properties["productType"],
341
- product.properties["entityId"],
342
- product.properties["productId"],
344
+ usgs_dataset,
345
+ product.properties["usgs:entityId"],
346
+ product.properties["usgs:productId"],
343
347
  )
344
348
 
345
349
  req_urls: list[str] = []
@@ -422,7 +426,7 @@ class UsgsApi(Api):
422
426
  download_request(product, fs_path, progress_callback, **kwargs)
423
427
 
424
428
  with open(record_filename, "w") as fh:
425
- fh.write(product.properties["downloadLink"])
429
+ fh.write(product.properties["eodag:download_link"])
426
430
  logger.debug(f"Download recorded in {record_filename}")
427
431
 
428
432
  api.logout()
@@ -466,7 +470,7 @@ class UsgsApi(Api):
466
470
  def download_all(
467
471
  self,
468
472
  products: SearchResult,
469
- auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
473
+ auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
470
474
  downloaded_callback: Optional[DownloadedCallback] = None,
471
475
  progress_callback: Optional[ProgressCallback] = None,
472
476
  wait: float = DEFAULT_DOWNLOAD_WAIT,
@@ -27,8 +27,6 @@ if TYPE_CHECKING:
27
27
  from mypy_boto3_s3 import S3ServiceResource
28
28
  from requests.auth import AuthBase
29
29
 
30
- from eodag.types import S3SessionKwargs
31
-
32
30
 
33
31
  class Authentication(PluginTopic):
34
32
  """Plugins authentication Base plugin
@@ -42,7 +40,7 @@ class Authentication(PluginTopic):
42
40
  configuration that needs authentication and helps identifying it
43
41
  """
44
42
 
45
- def authenticate(self) -> Union[AuthBase, S3SessionKwargs, S3ServiceResource]:
43
+ def authenticate(self) -> Union[AuthBase, S3ServiceResource]:
46
44
  """Authenticate"""
47
45
  raise NotImplementedError
48
46
 
@@ -49,7 +49,7 @@ class FilterDate(Crunch):
49
49
  @staticmethod
50
50
  def sort_product_by_start_date(product: EOProduct) -> dt:
51
51
  """Get product start date"""
52
- start_date = product.properties.get("startTimeFromAscendingNode")
52
+ start_date = product.properties.get("start_datetime")
53
53
  if not start_date:
54
54
  # Retrieve year, month, day, hour, minute, second of EPOCH start
55
55
  epoch = time.gmtime(0)[:-3]
@@ -93,7 +93,7 @@ class FilterDate(Crunch):
93
93
  for product in products:
94
94
 
95
95
  # product start date
96
- product_start_str = product.properties.get("startTimeFromAscendingNode")
96
+ product_start_str = product.properties.get("start_datetime")
97
97
  if product_start_str:
98
98
  product_start = dateutil.parser.parse(product_start_str)
99
99
  if not product_start.tzinfo:
@@ -102,7 +102,7 @@ class FilterDate(Crunch):
102
102
  product_start = None
103
103
 
104
104
  # product end date
105
- product_end_str = product.properties.get("completionTimeFromAscendingNode")
105
+ product_end_str = product.properties.get("end_datetime")
106
106
  if product_end_str:
107
107
  product_end = dateutil.parser.parse(product_end_str)
108
108
  if not product_end.tzinfo:
@@ -46,7 +46,7 @@ class FilterLatestIntersect(Crunch):
46
46
  @staticmethod
47
47
  def sort_product_by_start_date(product: EOProduct) -> dt:
48
48
  """Get product start date"""
49
- start_date = product.properties.get("startTimeFromAscendingNode")
49
+ start_date = product.properties.get("start_datetime")
50
50
  if not start_date:
51
51
  # Retrieve year, month, day, hour, minute, second of EPOCH start
52
52
  epoch = time.gmtime(0)[:-3]
@@ -67,7 +67,7 @@ class FilterLatestIntersect(Crunch):
67
67
  logger.debug("Start filtering for latest products")
68
68
  if not products:
69
69
  return []
70
- # Warning: May crash if startTimeFromAscendingNode is not in the appropriate format
70
+ # Warning: May crash if start_datetime is not in the appropriate format
71
71
  products.sort(key=self.sort_product_by_start_date, reverse=True)
72
72
  filtered: list[EOProduct] = []
73
73
  add_to_filtered = filtered.append
@@ -75,7 +75,7 @@ class FilterLatestByName(Crunch):
75
75
  logger.debug(
76
76
  "Latest product found for tileid=%s: date=%s",
77
77
  tileid,
78
- product.properties["startTimeFromAscendingNode"],
78
+ product.properties["start_datetime"],
79
79
  )
80
80
  filtered.append(product)
81
81
  processed.append(tileid)