eodag 3.0.0b3__py3-none-any.whl → 3.0.1__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 (71) hide show
  1. eodag/api/core.py +189 -125
  2. eodag/api/product/metadata_mapping.py +12 -3
  3. eodag/api/search_result.py +29 -3
  4. eodag/cli.py +35 -19
  5. eodag/config.py +412 -116
  6. eodag/plugins/apis/base.py +10 -4
  7. eodag/plugins/apis/ecmwf.py +14 -4
  8. eodag/plugins/apis/usgs.py +25 -2
  9. eodag/plugins/authentication/aws_auth.py +14 -5
  10. eodag/plugins/authentication/base.py +10 -1
  11. eodag/plugins/authentication/generic.py +14 -3
  12. eodag/plugins/authentication/header.py +12 -4
  13. eodag/plugins/authentication/keycloak.py +41 -22
  14. eodag/plugins/authentication/oauth.py +11 -1
  15. eodag/plugins/authentication/openid_connect.py +178 -163
  16. eodag/plugins/authentication/qsauth.py +12 -4
  17. eodag/plugins/authentication/sas_auth.py +19 -2
  18. eodag/plugins/authentication/token.py +57 -10
  19. eodag/plugins/authentication/token_exchange.py +19 -19
  20. eodag/plugins/crunch/base.py +4 -1
  21. eodag/plugins/crunch/filter_date.py +5 -2
  22. eodag/plugins/crunch/filter_latest_intersect.py +5 -4
  23. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  24. eodag/plugins/crunch/filter_overlap.py +5 -7
  25. eodag/plugins/crunch/filter_property.py +4 -3
  26. eodag/plugins/download/aws.py +39 -22
  27. eodag/plugins/download/base.py +11 -11
  28. eodag/plugins/download/creodias_s3.py +11 -2
  29. eodag/plugins/download/http.py +86 -52
  30. eodag/plugins/download/s3rest.py +20 -18
  31. eodag/plugins/manager.py +168 -23
  32. eodag/plugins/search/base.py +33 -14
  33. eodag/plugins/search/build_search_result.py +55 -51
  34. eodag/plugins/search/cop_marine.py +112 -29
  35. eodag/plugins/search/creodias_s3.py +20 -5
  36. eodag/plugins/search/csw.py +41 -1
  37. eodag/plugins/search/data_request_search.py +109 -9
  38. eodag/plugins/search/qssearch.py +532 -152
  39. eodag/plugins/search/static_stac_search.py +20 -21
  40. eodag/resources/ext_product_types.json +1 -1
  41. eodag/resources/product_types.yml +187 -56
  42. eodag/resources/providers.yml +1610 -1701
  43. eodag/resources/stac.yml +3 -163
  44. eodag/resources/user_conf_template.yml +112 -97
  45. eodag/rest/config.py +1 -2
  46. eodag/rest/constants.py +0 -1
  47. eodag/rest/core.py +61 -51
  48. eodag/rest/errors.py +181 -0
  49. eodag/rest/server.py +24 -325
  50. eodag/rest/stac.py +93 -544
  51. eodag/rest/types/eodag_search.py +13 -8
  52. eodag/rest/types/queryables.py +1 -2
  53. eodag/rest/types/stac_search.py +11 -2
  54. eodag/types/__init__.py +15 -3
  55. eodag/types/download_args.py +1 -1
  56. eodag/types/queryables.py +1 -2
  57. eodag/types/search_args.py +3 -3
  58. eodag/utils/__init__.py +77 -57
  59. eodag/utils/exceptions.py +23 -9
  60. eodag/utils/logging.py +37 -77
  61. eodag/utils/requests.py +1 -3
  62. eodag/utils/stac_reader.py +1 -1
  63. {eodag-3.0.0b3.dist-info → eodag-3.0.1.dist-info}/METADATA +11 -12
  64. eodag-3.0.1.dist-info/RECORD +109 -0
  65. {eodag-3.0.0b3.dist-info → eodag-3.0.1.dist-info}/WHEEL +1 -1
  66. {eodag-3.0.0b3.dist-info → eodag-3.0.1.dist-info}/entry_points.txt +1 -0
  67. eodag/resources/constraints/climate-dt.json +0 -13
  68. eodag/resources/constraints/extremes-dt.json +0 -8
  69. eodag-3.0.0b3.dist-info/RECORD +0 -110
  70. {eodag-3.0.0b3.dist-info → eodag-3.0.1.dist-info}/LICENSE +0 -0
  71. {eodag-3.0.0b3.dist-info → eodag-3.0.1.dist-info}/top_level.txt +0 -0
@@ -41,6 +41,7 @@ import geojson
41
41
  import orjson
42
42
  import pyproj
43
43
  from dateutil.parser import isoparse
44
+ from dateutil.relativedelta import relativedelta
44
45
  from dateutil.tz import UTC, tzutc
45
46
  from jsonpath_ng.jsonpath import Child, JSONPath
46
47
  from lxml import etree
@@ -152,7 +153,7 @@ def get_search_param(map_value: List[str]) -> str:
152
153
 
153
154
 
154
155
  def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
155
- """Format a string of form {<field_name>#<conversion_function>}
156
+ """Format a string of form ``{<field_name>#<conversion_function>}``
156
157
 
157
158
  The currently understood converters are:
158
159
  - ``datetime_to_timestamp_milliseconds``: converts a utc date string to a timestamp in
@@ -831,7 +832,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
831
832
  def convert_get_hydrological_year(date: str):
832
833
  utc_date = MetadataFormatter.convert_to_iso_utc_datetime(date)
833
834
  date_object = datetime.strptime(utc_date, "%Y-%m-%dT%H:%M:%S.%fZ")
834
- date_object_second_year = date_object + timedelta(days=365)
835
+ date_object_second_year = date_object + relativedelta(years=1)
835
836
  return [
836
837
  f'{date_object.strftime("%Y")}_{date_object_second_year.strftime("%y")}'
837
838
  ]
@@ -1208,7 +1209,7 @@ def mtd_cfg_as_conversion_and_querypath(
1208
1209
  dest_dict: Dict[str, Any] = {},
1209
1210
  result_type: str = "json",
1210
1211
  ) -> Dict[str, Any]:
1211
- """Metadata configuration dictionary to querypath with conversion dictionnary
1212
+ """Metadata configuration dictionary to querypath with conversion dictionary
1212
1213
  Transform every src_dict value from jsonpath_str to tuple `(conversion, jsonpath_object)`
1213
1214
  or from xpath_str to tuple `(conversion, xpath_str)`
1214
1215
 
@@ -1462,6 +1463,14 @@ def get_queryable_from_provider(
1462
1463
  :returns: EODAG configured queryable parameter or None
1463
1464
  """
1464
1465
  pattern = rf"\b{provider_queryable}\b"
1466
+ # if 1:1 mapping exists privilege this one instead of other mapping
1467
+ # e.g. provider queryable = year -> use year and not date in which year also appears
1468
+ mapping_values = [
1469
+ v[0] if isinstance(v, list) else "" for v in metadata_mapping.values()
1470
+ ]
1471
+ if provider_queryable in mapping_values:
1472
+ ind = mapping_values.index(provider_queryable)
1473
+ return Queryables.get_queryable_from_alias(list(metadata_mapping.keys())[ind])
1465
1474
  for param, param_conf in metadata_mapping.items():
1466
1475
  if isinstance(param_conf, list) and re.search(pattern, param_conf[0]):
1467
1476
  return Queryables.get_queryable_from_alias(param)
@@ -18,9 +18,20 @@
18
18
  from __future__ import annotations
19
19
 
20
20
  from collections import UserList
21
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
21
+ from typing import (
22
+ TYPE_CHECKING,
23
+ Annotated,
24
+ Any,
25
+ Dict,
26
+ Iterable,
27
+ List,
28
+ Optional,
29
+ Tuple,
30
+ Union,
31
+ )
22
32
 
23
33
  from shapely.geometry import GeometryCollection, shape
34
+ from typing_extensions import Doc
24
35
 
25
36
  from eodag.api.product import EOProduct
26
37
  from eodag.plugins.crunch.filter_date import FilterDate
@@ -47,11 +58,19 @@ class SearchResult(UserList):
47
58
 
48
59
  data: List[EOProduct]
49
60
 
61
+ errors: Annotated[
62
+ List[Tuple[str, Exception]], Doc("Tuple of provider name, exception")
63
+ ]
64
+
50
65
  def __init__(
51
- self, products: List[EOProduct], number_matched: Optional[int] = None
66
+ self,
67
+ products: List[EOProduct],
68
+ number_matched: Optional[int] = None,
69
+ errors: List[Tuple[str, Exception]] = [],
52
70
  ) -> None:
53
- super(SearchResult, self).__init__(products)
71
+ super().__init__(products)
54
72
  self.number_matched = number_matched
73
+ self.errors = errors
55
74
 
56
75
  def crunch(self, cruncher: Crunch, **search_params: Any) -> SearchResult:
57
76
  """Do some crunching with the underlying EO products.
@@ -197,6 +216,13 @@ class SearchResult(UserList):
197
216
  + "</table>"
198
217
  )
199
218
 
219
+ def extend(self, other: Iterable) -> None:
220
+ """override extend method to include errors"""
221
+ if isinstance(other, SearchResult):
222
+ self.errors.extend(other.errors)
223
+
224
+ return super().extend(other)
225
+
200
226
 
201
227
  class RawSearchResult(UserList):
202
228
  """An object representing a collection of raw/unparsed search results obtained from a provider.
eodag/cli.py CHANGED
@@ -39,6 +39,7 @@ Commands:
39
39
 
40
40
  noqa: D103
41
41
  """
42
+
42
43
  from __future__ import annotations
43
44
 
44
45
  import json
@@ -553,12 +554,13 @@ def download(ctx: Context, **kwargs: Any) -> None:
553
554
 
554
555
  for idx, product in enumerate(search_results):
555
556
  if product.downloader is None:
557
+ downloader = satim_api._plugins_manager.get_download_plugin(product)
556
558
  auth = product.downloader_auth
557
559
  if auth is None:
558
- auth = satim_api._plugins_manager.get_auth_plugin(product.provider)
559
- search_results[idx].register_downloader(
560
- satim_api._plugins_manager.get_download_plugin(product), auth
561
- )
560
+ auth = satim_api._plugins_manager.get_auth_plugin(
561
+ downloader, product
562
+ )
563
+ search_results[idx].register_downloader(downloader, auth)
562
564
 
563
565
  downloaded_file = product.get_quicklook()
564
566
  if not downloaded_file:
@@ -573,12 +575,13 @@ def download(ctx: Context, **kwargs: Any) -> None:
573
575
  # register downloader
574
576
  for idx, product in enumerate(search_results):
575
577
  if product.downloader is None:
578
+ downloader = satim_api._plugins_manager.get_download_plugin(product)
576
579
  auth = product.downloader_auth
577
580
  if auth is None:
578
- auth = satim_api._plugins_manager.get_auth_plugin(product.provider)
579
- search_results[idx].register_downloader(
580
- satim_api._plugins_manager.get_download_plugin(product), auth
581
- )
581
+ auth = satim_api._plugins_manager.get_auth_plugin(
582
+ downloader, product
583
+ )
584
+ search_results[idx].register_downloader(downloader, auth)
582
585
 
583
586
  downloaded_files = satim_api.download_all(search_results)
584
587
  if downloaded_files and len(downloaded_files) > 0:
@@ -684,19 +687,32 @@ def serve_rest(
684
687
  else:
685
688
  sys.exit(0)
686
689
  else:
690
+ import logging
691
+
687
692
  logging_config = uvicorn.config.LOGGING_CONFIG
688
- if debug:
689
- logging_config["loggers"]["uvicorn"]["level"] = "DEBUG"
690
- logging_config["loggers"]["uvicorn.error"]["level"] = "DEBUG"
691
- logging_config["loggers"]["uvicorn.access"]["level"] = "DEBUG"
692
- logging_config["formatters"]["default"][
693
- "fmt"
694
- ] = "%(asctime)-15s %(name)-32s [%(levelname)-8s] (%(module)-17s) %(message)s"
695
- logging_config["loggers"]["eodag"] = {
696
- "handlers": ["default"],
697
- "level": "DEBUG" if debug else "INFO",
698
- "propagate": False,
693
+ uvicorn_fmt = "%(asctime)-15s %(name)-32s [%(levelname)-8s] %(message)s"
694
+ logging_config["formatters"]["access"]["fmt"] = uvicorn_fmt
695
+ logging_config["formatters"]["default"]["fmt"] = uvicorn_fmt
696
+
697
+ eodag_formatter = logging.Formatter(
698
+ "%(asctime)-15s %(name)-32s [%(levelname)-8s] (tid=%(thread)d) %(message)s"
699
+ )
700
+ logging.getLogger("eodag").handlers[0].setFormatter(eodag_formatter)
701
+
702
+ if ctx.obj["verbosity"] <= 1:
703
+ logging_config["handlers"]["null"] = {
704
+ "level": "DEBUG",
705
+ "class": "logging.NullHandler",
699
706
  }
707
+ logging_config["loggers"]["uvicorn"]["handlers"] = ["null"]
708
+ logging_config["loggers"]["uvicorn.error"]["handlers"] = ["null"]
709
+ logging_config["loggers"]["uvicorn.access"]["handlers"] = ["null"]
710
+ else:
711
+ log_level = "INFO" if ctx.obj["verbosity"] == 2 else "DEBUG"
712
+ logging_config["loggers"]["uvicorn"]["level"] = log_level
713
+ logging_config["loggers"]["uvicorn.error"]["level"] = log_level
714
+ logging_config["loggers"]["uvicorn.access"]["level"] = log_level
715
+
700
716
  uvicorn.run(
701
717
  "eodag.rest.server:app",
702
718
  host=bind_host,