eodag 3.0.0b3__py3-none-any.whl → 3.1.0__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 (94) hide show
  1. eodag/api/core.py +347 -247
  2. eodag/api/product/_assets.py +44 -15
  3. eodag/api/product/_product.py +58 -47
  4. eodag/api/product/drivers/__init__.py +81 -4
  5. eodag/api/product/drivers/base.py +65 -4
  6. eodag/api/product/drivers/generic.py +65 -0
  7. eodag/api/product/drivers/sentinel1.py +97 -0
  8. eodag/api/product/drivers/sentinel2.py +95 -0
  9. eodag/api/product/metadata_mapping.py +129 -93
  10. eodag/api/search_result.py +28 -12
  11. eodag/cli.py +61 -24
  12. eodag/config.py +457 -167
  13. eodag/plugins/apis/base.py +10 -4
  14. eodag/plugins/apis/ecmwf.py +53 -23
  15. eodag/plugins/apis/usgs.py +41 -17
  16. eodag/plugins/authentication/aws_auth.py +30 -18
  17. eodag/plugins/authentication/base.py +14 -3
  18. eodag/plugins/authentication/generic.py +14 -3
  19. eodag/plugins/authentication/header.py +14 -6
  20. eodag/plugins/authentication/keycloak.py +44 -25
  21. eodag/plugins/authentication/oauth.py +18 -4
  22. eodag/plugins/authentication/openid_connect.py +192 -171
  23. eodag/plugins/authentication/qsauth.py +12 -4
  24. eodag/plugins/authentication/sas_auth.py +22 -5
  25. eodag/plugins/authentication/token.py +95 -17
  26. eodag/plugins/authentication/token_exchange.py +19 -19
  27. eodag/plugins/base.py +4 -4
  28. eodag/plugins/crunch/base.py +8 -5
  29. eodag/plugins/crunch/filter_date.py +9 -6
  30. eodag/plugins/crunch/filter_latest_intersect.py +9 -8
  31. eodag/plugins/crunch/filter_latest_tpl_name.py +8 -8
  32. eodag/plugins/crunch/filter_overlap.py +9 -11
  33. eodag/plugins/crunch/filter_property.py +10 -10
  34. eodag/plugins/download/aws.py +181 -105
  35. eodag/plugins/download/base.py +49 -67
  36. eodag/plugins/download/creodias_s3.py +40 -2
  37. eodag/plugins/download/http.py +247 -223
  38. eodag/plugins/download/s3rest.py +29 -28
  39. eodag/plugins/manager.py +176 -41
  40. eodag/plugins/search/__init__.py +6 -5
  41. eodag/plugins/search/base.py +123 -60
  42. eodag/plugins/search/build_search_result.py +1046 -355
  43. eodag/plugins/search/cop_marine.py +132 -39
  44. eodag/plugins/search/creodias_s3.py +19 -68
  45. eodag/plugins/search/csw.py +48 -8
  46. eodag/plugins/search/data_request_search.py +124 -23
  47. eodag/plugins/search/qssearch.py +531 -310
  48. eodag/plugins/search/stac_list_assets.py +85 -0
  49. eodag/plugins/search/static_stac_search.py +23 -24
  50. eodag/resources/ext_product_types.json +1 -1
  51. eodag/resources/product_types.yml +1295 -355
  52. eodag/resources/providers.yml +1819 -3010
  53. eodag/resources/stac.yml +3 -163
  54. eodag/resources/stac_api.yml +2 -2
  55. eodag/resources/user_conf_template.yml +115 -99
  56. eodag/rest/cache.py +2 -2
  57. eodag/rest/config.py +3 -4
  58. eodag/rest/constants.py +0 -1
  59. eodag/rest/core.py +157 -117
  60. eodag/rest/errors.py +181 -0
  61. eodag/rest/server.py +57 -339
  62. eodag/rest/stac.py +133 -581
  63. eodag/rest/types/collections_search.py +3 -3
  64. eodag/rest/types/eodag_search.py +41 -30
  65. eodag/rest/types/queryables.py +42 -32
  66. eodag/rest/types/stac_search.py +15 -16
  67. eodag/rest/utils/__init__.py +14 -21
  68. eodag/rest/utils/cql_evaluate.py +6 -6
  69. eodag/rest/utils/rfc3339.py +2 -2
  70. eodag/types/__init__.py +153 -32
  71. eodag/types/bbox.py +2 -2
  72. eodag/types/download_args.py +4 -4
  73. eodag/types/queryables.py +183 -73
  74. eodag/types/search_args.py +6 -6
  75. eodag/types/whoosh.py +127 -3
  76. eodag/utils/__init__.py +228 -106
  77. eodag/utils/exceptions.py +47 -26
  78. eodag/utils/import_system.py +2 -2
  79. eodag/utils/logging.py +37 -77
  80. eodag/utils/repr.py +65 -6
  81. eodag/utils/requests.py +13 -15
  82. eodag/utils/rest.py +2 -2
  83. eodag/utils/s3.py +231 -0
  84. eodag/utils/stac_reader.py +11 -11
  85. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/METADATA +81 -81
  86. eodag-3.1.0.dist-info/RECORD +113 -0
  87. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/WHEEL +1 -1
  88. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/entry_points.txt +5 -2
  89. eodag/resources/constraints/climate-dt.json +0 -13
  90. eodag/resources/constraints/extremes-dt.json +0 -8
  91. eodag/utils/constraints.py +0 -244
  92. eodag-3.0.0b3.dist-info/RECORD +0 -110
  93. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/LICENSE +0 -0
  94. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/top_level.txt +0 -0
@@ -20,7 +20,7 @@ from __future__ import annotations
20
20
  import logging
21
21
  import time
22
22
  from datetime import datetime, timedelta, timezone
23
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, cast
23
+ from typing import TYPE_CHECKING, Any, Optional, cast
24
24
 
25
25
  import requests
26
26
 
@@ -36,6 +36,7 @@ from eodag.utils import (
36
36
  DEFAULT_ITEMS_PER_PAGE,
37
37
  DEFAULT_MISSION_START_DATE,
38
38
  DEFAULT_PAGE,
39
+ DEFAULT_SEARCH_TIMEOUT,
39
40
  GENERIC_PRODUCT_TYPE,
40
41
  HTTP_REQ_TIMEOUT,
41
42
  USER_AGENT,
@@ -58,9 +59,107 @@ logger = logging.getLogger("eodag.search.data_request_search")
58
59
  class DataRequestSearch(Search):
59
60
  """
60
61
  Plugin to execute search requests composed of several steps:
61
- - do a data request which defines which data shall be searched
62
- - check the status of the request job
63
- - if finished - fetch the result of the job
62
+
63
+ #. do a data request which defines which data shall be searched
64
+ #. check the status of the request job
65
+ #. if finished - fetch the result of the job
66
+
67
+ :param provider: provider name
68
+ :param config: Search plugin configuration:
69
+
70
+ * :attr:`~eodag.config.PluginConfig.api_endpoint` (``str``) (**mandatory**): The endpoint of the
71
+ provider's search interface
72
+ * :attr:`~eodag.config.PluginConfig.results_entry` (``str``) (**mandatory**): The name of
73
+ the key in the provider search result that gives access to the result entries
74
+ * :attr:`~eodag.config.PluginConfig.data_request_url` (``str``) (**mandatory**): url
75
+ to which the data request shall be sent
76
+ * :attr:`~eodag.config.PluginConfig.status_url` (``str``) (**mandatory**): url to fetch
77
+ the status of the data request
78
+ * :attr:`~eodag.config.PluginConfig.result_url` (``str``) (**mandatory**): url to fetch
79
+ the search result when the data request is done
80
+ * :attr:`~eodag.config.PluginConfig.need_auth` (``bool``): if authentication is needed for
81
+ the search request; default: ``False``
82
+ * :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``): which error code is returned in case of an
83
+ authentication error; only used if ``need_auth=true``
84
+ * :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates should be
85
+ verified in requests; default: ``True``
86
+ * :attr:`~eodag.config.PluginConfig.timeout` (``int``): time to wait until request timeout in seconds;
87
+ default: ``5``
88
+ * :attr:`~eodag.config.PluginConfig.dates_required` (``bool``): if date parameters are mandatory
89
+ in the request; default: ``True``
90
+ * :attr:`~eodag.config.PluginConfig.pagination` (:class:`~eodag.config.PluginConfig.Pagination`)
91
+ (**mandatory**): The configuration of how the pagination is done on the provider. It is a tree with the
92
+ following nodes:
93
+
94
+ * :attr:`~eodag.config.PluginConfig.Pagination.total_items_nb_key_path` (``str``): An XPath or JsonPath
95
+ leading to the total number of results satisfying a request. This is used for providers which provides the
96
+ total results metadata along with the result of the query and don't have an endpoint for querying
97
+ the number of items satisfying a request, or for providers for which the count endpoint
98
+ returns a json or xml document
99
+ * :attr:`~eodag.config.PluginConfig.Pagination.max_items_per_page` (``int``): The maximum
100
+ number of items per page that the provider can handle; default: ``50``
101
+ * :attr:`~eodag.config.PluginConfig.Pagination.start_page` (``int``): number of the
102
+ first page; default: ``1``
103
+
104
+ * :attr:`~eodag.config.PluginConfig.discover_product_types`
105
+ (:class:`~eodag.config.PluginConfig.DiscoverProductTypes`): configuration for product type discovery based on
106
+ information from the provider; It contains the keys:
107
+
108
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.fetch_url` (``str``) (**mandatory**): url from which
109
+ the product types can be fetched
110
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.result_type` (``str``): type of the provider result;
111
+ currently only ``json`` is supported (other types could be used in an extension of this plugin)
112
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.results_entry` (``str``) (**mandatory**): json path
113
+ to the list of product types
114
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_id` (``str``): mapping for the
115
+ product type id
116
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_parsable_metadata`
117
+ (``dict[str, str]``): mapping for product type metadata (e.g. ``abstract``, ``licence``) which can be parsed
118
+ from the provider result
119
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_parsable_properties`
120
+ (``dict[str, str]``): mapping for product type properties which can be parsed from the result and are not
121
+ product type metadata
122
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_collection_fetch_url` (``str``): url to fetch
123
+ data for a single collection; used if product type metadata is not available from the endpoint given in
124
+ :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.fetch_url`
125
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_collection_fetch_qs` (``str``): query string
126
+ to be added to the :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.fetch_url` to filter for a
127
+ collection
128
+ * :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_product_type_parsable_metadata`
129
+ (``dict[str, str]``): mapping for product type metadata returned by the endpoint given in
130
+ :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_collection_fetch_url`.
131
+
132
+ * :attr:`~eodag.config.PluginConfig.constraints_file_url` (``str``): url to fetch the constraints for a specific
133
+ product type, can be an http url or a path to a file; the constraints are used to build queryables
134
+ * :attr:`~eodag.config.PluginConfig.constraints_entry` (``str``): key in the json result where the constraints
135
+ can be found; if not given, it is assumed that the constraints are on top level of the result, i.e.
136
+ the result is an array of constraints
137
+ * :attr:`~eodag.config.PluginConfig.metadata_mapping` (``dict[str, Any]``): The search plugins of this kind can
138
+ detect when a metadata mapping is "query-able", and get the semantics of how to format the query string
139
+ parameter that enables to make a query on the corresponding metadata. To make a metadata query-able,
140
+ just configure it in the metadata mapping to be a list of 2 items, the first one being the
141
+ specification of the query string search formatting. The later is a string following the
142
+ specification of Python string formatting, with a special behaviour added to it. For example,
143
+ an entry in the metadata mapping of this kind::
144
+
145
+ completionTimeFromAscendingNode:
146
+ - 'f=acquisition.endViewingDate:lte:{completionTimeFromAscendingNode#timestamp}'
147
+ - '$.properties.acquisition.endViewingDate'
148
+
149
+ means that the search url will have a query string parameter named ``f`` with a value of
150
+ ``acquisition.endViewingDate:lte:1543922280.0`` if the search was done with the value
151
+ of ``completionTimeFromAscendingNode`` being ``2018-12-04T12:18:00``. What happened is that
152
+ ``{completionTimeFromAscendingNode#timestamp}`` was replaced with the timestamp of the value
153
+ of ``completionTimeFromAscendingNode``. This example shows all there is to know about the
154
+ semantics of the query string formatting introduced by this plugin: any eodag search parameter
155
+ can be referenced in the query string with an additional optional conversion function that
156
+ is separated from it by a ``#`` (see :func:`~eodag.api.product.metadata_mapping.format_metadata` for further
157
+ details on the available converters). Note that for the values in the
158
+ :attr:`~eodag.config.PluginConfig.free_text_search_operations` configuration parameter follow the same rule.
159
+ If the metadata_mapping is not a list but only a string, this means that the parameters is not queryable but
160
+ it is included in the result obtained from the provider. The string indicates how the provider result should
161
+ be mapped to the eodag parameter.
162
+
64
163
  """
65
164
 
66
165
  data_request_id: Optional[str]
@@ -88,7 +187,7 @@ class DataRequestSearch(Search):
88
187
  )
89
188
  if other_product_for_mapping:
90
189
  other_product_type_def_params = self.get_product_type_def_params(
91
- other_product_for_mapping, # **kwargs
190
+ other_product_for_mapping,
92
191
  )
93
192
  product_type_metadata_mapping.update(
94
193
  other_product_type_def_params.get("metadata_mapping", {})
@@ -109,10 +208,10 @@ class DataRequestSearch(Search):
109
208
  self.config.pagination["next_page_url_key_path"] = string_to_jsonpath(
110
209
  self.config.pagination.get("next_page_url_key_path", None)
111
210
  )
112
- self.download_info: Dict[str, Any] = {}
211
+ self.download_info: dict[str, Any] = {}
113
212
  self.data_request_id = None
114
213
 
115
- def discover_product_types(self, **kwargs: Any) -> Optional[Dict[str, Any]]:
214
+ def discover_product_types(self, **kwargs: Any) -> Optional[dict[str, Any]]:
116
215
  """Fetch product types is disabled for `DataRequestSearch`
117
216
 
118
217
  :returns: empty dict
@@ -128,7 +227,7 @@ class DataRequestSearch(Search):
128
227
  self,
129
228
  prep: PreparedSearch = PreparedSearch(),
130
229
  **kwargs: Any,
131
- ) -> Tuple[List[EOProduct], Optional[int]]:
230
+ ) -> tuple[list[EOProduct], Optional[int]]:
132
231
  """
133
232
  performs the search for a provider where several steps are required to fetch the data
134
233
  """
@@ -154,7 +253,7 @@ class DataRequestSearch(Search):
154
253
 
155
254
  # provider product type specific conf
156
255
  self.product_type_def_params = self.get_product_type_def_params(
157
- product_type, **kwargs
256
+ product_type, format_variables=kwargs
158
257
  )
159
258
 
160
259
  # update config using provider product type definition metadata_mapping
@@ -164,7 +263,7 @@ class DataRequestSearch(Search):
164
263
  )
165
264
  if other_product_for_mapping:
166
265
  other_product_type_def_params = self.get_product_type_def_params(
167
- other_product_for_mapping, **kwargs
266
+ other_product_for_mapping, format_variables=kwargs
168
267
  )
169
268
  self.config.metadata_mapping.update(
170
269
  other_product_type_def_params.get("metadata_mapping", {})
@@ -210,7 +309,7 @@ class DataRequestSearch(Search):
210
309
  request_finished = True
211
310
 
212
311
  # loop to check search job status
213
- search_timeout = int(getattr(self.config, "timeout", HTTP_REQ_TIMEOUT))
312
+ search_timeout = int(getattr(self.config, "timeout", DEFAULT_SEARCH_TIMEOUT))
214
313
  logger.info(
215
314
  f"checking status of request job {data_request_id} (timeout={search_timeout}s)"
216
315
  )
@@ -275,9 +374,9 @@ class DataRequestSearch(Search):
275
374
  except requests.exceptions.Timeout as exc:
276
375
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
277
376
  except requests.RequestException as e:
278
- raise RequestError(
279
- f"search job for product_type {product_type} could not be created: {str(e)}, {request_job.text}"
280
- )
377
+ raise RequestError.from_error(
378
+ e, f"search job for product_type {product_type} could not be created"
379
+ ) from e
281
380
  else:
282
381
  logger.info("search job for product_type %s created", product_type)
283
382
  return request_job.json()["jobId"]
@@ -294,7 +393,7 @@ class DataRequestSearch(Search):
294
393
  except requests.exceptions.Timeout as exc:
295
394
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
296
395
  except requests.RequestException as e:
297
- raise RequestError(f"_cancel_request failed: {str(e)}")
396
+ raise RequestError.from_error(e, "_cancel_request failed") from e
298
397
 
299
398
  def _check_request_status(self, data_request_id: str) -> bool:
300
399
  logger.debug("checking status of request job %s", data_request_id)
@@ -313,7 +412,7 @@ class DataRequestSearch(Search):
313
412
  except requests.exceptions.Timeout as exc:
314
413
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
315
414
  except requests.RequestException as e:
316
- raise RequestError(f"_check_request_status failed: {str(e)}")
415
+ raise RequestError.from_error(e, "_check_request_status failed") from e
317
416
  else:
318
417
  status_data = status_resp.json()
319
418
  if "status_code" in status_data and status_data["status_code"] in [
@@ -321,7 +420,9 @@ class DataRequestSearch(Search):
321
420
  404,
322
421
  ]:
323
422
  logger.error(f"_check_request_status failed: {status_data}")
324
- raise RequestError("authentication token expired during request")
423
+ error = RequestError("authentication token expired during request")
424
+ error.status_code = status_data["status_code"]
425
+ raise error
325
426
  if status_data["status"] == "failed":
326
427
  logger.error(f"_check_request_status failed: {status_data}")
327
428
  raise RequestError(
@@ -331,7 +432,7 @@ class DataRequestSearch(Search):
331
432
 
332
433
  def _get_result_data(
333
434
  self, data_request_id: str, items_per_page: int, page: int
334
- ) -> Dict[str, Any]:
435
+ ) -> dict[str, Any]:
335
436
  page = page - 1 + self.config.pagination.get("start_page", 1)
336
437
  url = self.config.result_url.format(
337
438
  jobId=data_request_id, items_per_page=items_per_page, page=page
@@ -350,18 +451,18 @@ class DataRequestSearch(Search):
350
451
 
351
452
  def _convert_result_data(
352
453
  self,
353
- result_data: Dict[str, Any],
454
+ result_data: dict[str, Any],
354
455
  data_request_id: str,
355
456
  product_type: str,
356
457
  **kwargs: Any,
357
- ) -> Tuple[List[EOProduct], int]:
458
+ ) -> tuple[list[EOProduct], int]:
358
459
  """Build EOProducts from provider results"""
359
460
  results_entry = self.config.results_entry
360
461
  results = result_data[results_entry]
361
462
  logger.debug(
362
463
  "Adapting %s plugin results to eodag product representation" % len(results)
363
464
  )
364
- products: List[EOProduct] = []
465
+ products: list[EOProduct] = []
365
466
  for result in results:
366
467
  product = EOProduct(
367
468
  self.provider,
@@ -417,8 +518,8 @@ class DataRequestSearch(Search):
417
518
  return False
418
519
 
419
520
  def _apply_additional_filters(
420
- self, result: Dict[str, Any], custom_filters: Dict[str, str]
421
- ) -> Dict[str, Any]:
521
+ self, result: dict[str, Any], custom_filters: dict[str, str]
522
+ ) -> dict[str, Any]:
422
523
  filtered_result = []
423
524
  results_entry = self.config.results_entry
424
525
  results = result[results_entry]