eodag 3.0.1__py3-none-any.whl → 3.1.0b2__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.
- eodag/api/core.py +164 -127
- eodag/api/product/_assets.py +11 -11
- eodag/api/product/_product.py +45 -30
- eodag/api/product/drivers/__init__.py +81 -4
- eodag/api/product/drivers/base.py +65 -4
- eodag/api/product/drivers/generic.py +65 -0
- eodag/api/product/drivers/sentinel1.py +97 -0
- eodag/api/product/drivers/sentinel2.py +95 -0
- eodag/api/product/metadata_mapping.py +101 -85
- eodag/api/search_result.py +13 -23
- eodag/cli.py +26 -5
- eodag/config.py +78 -81
- eodag/plugins/apis/base.py +1 -1
- eodag/plugins/apis/ecmwf.py +46 -22
- eodag/plugins/apis/usgs.py +16 -15
- eodag/plugins/authentication/aws_auth.py +16 -13
- eodag/plugins/authentication/base.py +5 -3
- eodag/plugins/authentication/header.py +3 -3
- eodag/plugins/authentication/keycloak.py +4 -4
- eodag/plugins/authentication/oauth.py +7 -3
- eodag/plugins/authentication/openid_connect.py +16 -16
- eodag/plugins/authentication/sas_auth.py +4 -4
- eodag/plugins/authentication/token.py +41 -10
- eodag/plugins/authentication/token_exchange.py +1 -1
- eodag/plugins/base.py +4 -4
- eodag/plugins/crunch/base.py +4 -4
- eodag/plugins/crunch/filter_date.py +4 -4
- eodag/plugins/crunch/filter_latest_intersect.py +6 -6
- eodag/plugins/crunch/filter_latest_tpl_name.py +7 -7
- eodag/plugins/crunch/filter_overlap.py +4 -4
- eodag/plugins/crunch/filter_property.py +6 -7
- eodag/plugins/download/aws.py +58 -78
- eodag/plugins/download/base.py +38 -56
- eodag/plugins/download/creodias_s3.py +29 -0
- eodag/plugins/download/http.py +173 -183
- eodag/plugins/download/s3rest.py +10 -11
- eodag/plugins/manager.py +10 -20
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +87 -44
- eodag/plugins/search/build_search_result.py +1067 -329
- eodag/plugins/search/cop_marine.py +22 -12
- eodag/plugins/search/creodias_s3.py +9 -73
- eodag/plugins/search/csw.py +11 -11
- eodag/plugins/search/data_request_search.py +16 -15
- eodag/plugins/search/qssearch.py +103 -187
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +3 -3
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +663 -304
- eodag/resources/providers.yml +823 -1749
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +11 -0
- eodag/rest/cache.py +2 -2
- eodag/rest/config.py +3 -3
- eodag/rest/core.py +112 -82
- eodag/rest/errors.py +5 -5
- eodag/rest/server.py +33 -14
- eodag/rest/stac.py +40 -38
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +29 -23
- eodag/rest/types/queryables.py +15 -16
- eodag/rest/types/stac_search.py +15 -25
- eodag/rest/utils/__init__.py +14 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +75 -28
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +3 -3
- eodag/types/queryables.py +183 -72
- eodag/types/search_args.py +4 -4
- eodag/types/whoosh.py +127 -3
- eodag/utils/__init__.py +152 -50
- eodag/utils/exceptions.py +28 -21
- eodag/utils/import_system.py +2 -2
- eodag/utils/repr.py +65 -6
- eodag/utils/requests.py +13 -13
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +208 -0
- eodag/utils/stac_reader.py +10 -10
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/METADATA +77 -76
- eodag-3.1.0b2.dist-info/RECORD +113 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/WHEEL +1 -1
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/entry_points.txt +4 -2
- eodag/utils/constraints.py +0 -244
- eodag-3.0.1.dist-info/RECORD +0 -109
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/LICENSE +0 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0b2.dist-info}/top_level.txt +0 -0
eodag/plugins/download/s3rest.py
CHANGED
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
22
|
import os.path
|
|
23
|
-
from typing import TYPE_CHECKING,
|
|
23
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
24
24
|
from xml.dom import minidom
|
|
25
25
|
from xml.parsers.expat import ExpatError
|
|
26
26
|
|
|
@@ -54,6 +54,7 @@ from eodag.utils.exceptions import (
|
|
|
54
54
|
if TYPE_CHECKING:
|
|
55
55
|
from eodag.api.product import EOProduct
|
|
56
56
|
from eodag.config import PluginConfig
|
|
57
|
+
from eodag.types import S3SessionKwargs
|
|
57
58
|
from eodag.types.download_args import DownloadConf
|
|
58
59
|
from eodag.utils import Unpack
|
|
59
60
|
|
|
@@ -78,7 +79,7 @@ class S3RestDownload(Download):
|
|
|
78
79
|
* :attr:`~eodag.config.PluginConfig.order_enabled` (``bool``): whether order is enabled
|
|
79
80
|
or not if product is `OFFLINE`
|
|
80
81
|
* :attr:`~eodag.config.PluginConfig.order_method` (``str``) HTTP request method, ``GET`` (default) or ``POST``
|
|
81
|
-
* :attr:`~eodag.config.PluginConfig.order_headers` (``[
|
|
82
|
+
* :attr:`~eodag.config.PluginConfig.order_headers` (``[dict[str, str]]``): order request headers
|
|
82
83
|
* :attr:`~eodag.config.PluginConfig.order_on_response` (:class:`~eodag.config.PluginConfig.OrderOnResponse`):
|
|
83
84
|
a typed dictionary containing the key :attr:`~eodag.config.PluginConfig.OrderOnResponse.metadata_mapping`
|
|
84
85
|
which can be used to add new product properties based on the data in response to the order request
|
|
@@ -93,10 +94,10 @@ class S3RestDownload(Download):
|
|
|
93
94
|
def download(
|
|
94
95
|
self,
|
|
95
96
|
product: EOProduct,
|
|
96
|
-
auth: Optional[Union[AuthBase,
|
|
97
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None,
|
|
97
98
|
progress_callback: Optional[ProgressCallback] = None,
|
|
98
|
-
wait:
|
|
99
|
-
timeout:
|
|
99
|
+
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
100
|
+
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
100
101
|
**kwargs: Unpack[DownloadConf],
|
|
101
102
|
) -> Optional[str]:
|
|
102
103
|
"""Download method for S3 REST API.
|
|
@@ -130,9 +131,9 @@ class S3RestDownload(Download):
|
|
|
130
131
|
and "storageStatus" in product.properties
|
|
131
132
|
and product.properties["storageStatus"] != ONLINE_STATUS
|
|
132
133
|
):
|
|
133
|
-
self.http_download_plugin.
|
|
134
|
+
self.http_download_plugin._order(product=product, auth=auth)
|
|
134
135
|
|
|
135
|
-
@self.
|
|
136
|
+
@self._order_download_retry(product, wait, timeout)
|
|
136
137
|
def download_request(
|
|
137
138
|
product: EOProduct,
|
|
138
139
|
auth: AuthBase,
|
|
@@ -142,9 +143,7 @@ class S3RestDownload(Download):
|
|
|
142
143
|
):
|
|
143
144
|
# check order status
|
|
144
145
|
if product.properties.get("orderStatusLink", None):
|
|
145
|
-
self.http_download_plugin.
|
|
146
|
-
product=product, auth=auth
|
|
147
|
-
)
|
|
146
|
+
self.http_download_plugin._order_status(product=product, auth=auth)
|
|
148
147
|
|
|
149
148
|
# get bucket urls
|
|
150
149
|
bucket_name, prefix = get_bucket_name_and_prefix(
|
|
@@ -272,7 +271,7 @@ class S3RestDownload(Download):
|
|
|
272
271
|
os.remove(record_filename)
|
|
273
272
|
|
|
274
273
|
# total size for progress_callback
|
|
275
|
-
size_list:
|
|
274
|
+
size_list: list[int] = [
|
|
276
275
|
int(node.firstChild.nodeValue) # type: ignore[attr-defined]
|
|
277
276
|
for node in xmldoc.getElementsByTagName("Size")
|
|
278
277
|
if node.firstChild is not None
|
eodag/plugins/manager.py
CHANGED
|
@@ -21,18 +21,7 @@ import logging
|
|
|
21
21
|
import re
|
|
22
22
|
from operator import attrgetter
|
|
23
23
|
from pathlib import Path
|
|
24
|
-
from typing import
|
|
25
|
-
TYPE_CHECKING,
|
|
26
|
-
Any,
|
|
27
|
-
Dict,
|
|
28
|
-
Iterator,
|
|
29
|
-
List,
|
|
30
|
-
Optional,
|
|
31
|
-
Tuple,
|
|
32
|
-
Type,
|
|
33
|
-
Union,
|
|
34
|
-
cast,
|
|
35
|
-
)
|
|
24
|
+
from typing import TYPE_CHECKING, Any, Iterator, Optional, Union, cast
|
|
36
25
|
|
|
37
26
|
import pkg_resources
|
|
38
27
|
|
|
@@ -61,6 +50,7 @@ if TYPE_CHECKING:
|
|
|
61
50
|
from eodag.api.product import EOProduct
|
|
62
51
|
from eodag.config import PluginConfig, ProviderConfig
|
|
63
52
|
from eodag.plugins.base import PluginTopic
|
|
53
|
+
from eodag.types import S3SessionKwargs
|
|
64
54
|
|
|
65
55
|
|
|
66
56
|
logger = logging.getLogger("eodag.plugins.manager")
|
|
@@ -84,11 +74,11 @@ class PluginManager:
|
|
|
84
74
|
|
|
85
75
|
supported_topics = set(PLUGINS_TOPICS_KEYS)
|
|
86
76
|
|
|
87
|
-
product_type_to_provider_config_map:
|
|
77
|
+
product_type_to_provider_config_map: dict[str, list[ProviderConfig]]
|
|
88
78
|
|
|
89
|
-
skipped_plugins:
|
|
79
|
+
skipped_plugins: list[str]
|
|
90
80
|
|
|
91
|
-
def __init__(self, providers_config:
|
|
81
|
+
def __init__(self, providers_config: dict[str, ProviderConfig]) -> None:
|
|
92
82
|
self.skipped_plugins = []
|
|
93
83
|
self.providers_config = providers_config
|
|
94
84
|
# Load all the plugins. This will make all plugin classes of a particular
|
|
@@ -144,14 +134,14 @@ class PluginManager:
|
|
|
144
134
|
self.rebuild()
|
|
145
135
|
|
|
146
136
|
def rebuild(
|
|
147
|
-
self, providers_config: Optional[
|
|
137
|
+
self, providers_config: Optional[dict[str, ProviderConfig]] = None
|
|
148
138
|
) -> None:
|
|
149
139
|
"""(Re)Build plugin manager mapping and cache"""
|
|
150
140
|
if providers_config is not None:
|
|
151
141
|
self.providers_config = providers_config
|
|
152
142
|
|
|
153
143
|
self.build_product_type_to_provider_config_map()
|
|
154
|
-
self._built_plugins_cache:
|
|
144
|
+
self._built_plugins_cache: dict[tuple[str, str, str], Any] = {}
|
|
155
145
|
|
|
156
146
|
def build_product_type_to_provider_config_map(self) -> None:
|
|
157
147
|
"""Build mapping conf between product types and providers"""
|
|
@@ -211,7 +201,7 @@ class PluginManager:
|
|
|
211
201
|
)
|
|
212
202
|
return plugin
|
|
213
203
|
|
|
214
|
-
configs: Optional[
|
|
204
|
+
configs: Optional[list[ProviderConfig]]
|
|
215
205
|
if product_type:
|
|
216
206
|
configs = self.product_type_to_provider_config_map.get(product_type)
|
|
217
207
|
if not configs:
|
|
@@ -378,7 +368,7 @@ class PluginManager:
|
|
|
378
368
|
provider: str,
|
|
379
369
|
matching_url: Optional[str] = None,
|
|
380
370
|
matching_conf: Optional[PluginConfig] = None,
|
|
381
|
-
) -> Optional[Union[AuthBase,
|
|
371
|
+
) -> Optional[Union[AuthBase, S3SessionKwargs]]:
|
|
382
372
|
"""Authenticate and return the authenticated object for the first matching
|
|
383
373
|
authentication plugin
|
|
384
374
|
|
|
@@ -447,7 +437,7 @@ class PluginManager:
|
|
|
447
437
|
self,
|
|
448
438
|
provider: str,
|
|
449
439
|
plugin_conf: PluginConfig,
|
|
450
|
-
topic_class:
|
|
440
|
+
topic_class: type[PluginTopic],
|
|
451
441
|
) -> Union[Api, Search, Download, Authentication, Crunch]:
|
|
452
442
|
"""Build the plugin of the given topic with the given plugin configuration and
|
|
453
443
|
registered as the given provider
|
eodag/plugins/search/__init__.py
CHANGED
|
@@ -24,11 +24,12 @@ from typing import TYPE_CHECKING
|
|
|
24
24
|
from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
from typing import Any,
|
|
27
|
+
from typing import Any, Optional, Union
|
|
28
28
|
|
|
29
29
|
from requests.auth import AuthBase
|
|
30
30
|
|
|
31
31
|
from eodag.plugins.authentication.base import Authentication
|
|
32
|
+
from eodag.types import S3SessionKwargs
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
@dataclass
|
|
@@ -38,7 +39,7 @@ class PreparedSearch:
|
|
|
38
39
|
product_type: Optional[str] = None
|
|
39
40
|
page: Optional[int] = DEFAULT_PAGE
|
|
40
41
|
items_per_page: Optional[int] = DEFAULT_ITEMS_PER_PAGE
|
|
41
|
-
auth: Optional[Union[AuthBase,
|
|
42
|
+
auth: Optional[Union[AuthBase, S3SessionKwargs]] = None
|
|
42
43
|
auth_plugin: Optional[Authentication] = None
|
|
43
44
|
count: bool = True
|
|
44
45
|
url: Optional[str] = None
|
|
@@ -46,9 +47,9 @@ class PreparedSearch:
|
|
|
46
47
|
exception_message: Optional[str] = None
|
|
47
48
|
|
|
48
49
|
need_count: bool = field(init=False, repr=False)
|
|
49
|
-
query_params:
|
|
50
|
+
query_params: dict[str, Any] = field(init=False, repr=False)
|
|
50
51
|
query_string: str = field(init=False, repr=False)
|
|
51
|
-
search_urls:
|
|
52
|
-
product_type_def_params:
|
|
52
|
+
search_urls: list[str] = field(init=False, repr=False)
|
|
53
|
+
product_type_def_params: dict[str, Any] = field(init=False, repr=False)
|
|
53
54
|
total_items_nb: int = field(init=False, repr=False)
|
|
54
55
|
sort_by_qs: str = field(init=False, repr=False)
|
eodag/plugins/search/base.py
CHANGED
|
@@ -31,7 +31,7 @@ from eodag.api.product.metadata_mapping import (
|
|
|
31
31
|
from eodag.plugins.base import PluginTopic
|
|
32
32
|
from eodag.plugins.search import PreparedSearch
|
|
33
33
|
from eodag.types import model_fields_to_annotated
|
|
34
|
-
from eodag.types.queryables import Queryables
|
|
34
|
+
from eodag.types.queryables import Queryables, QueryablesDict
|
|
35
35
|
from eodag.types.search_args import SortByList
|
|
36
36
|
from eodag.utils import (
|
|
37
37
|
GENERIC_PRODUCT_TYPE,
|
|
@@ -43,12 +43,13 @@ from eodag.utils import (
|
|
|
43
43
|
from eodag.utils.exceptions import ValidationError
|
|
44
44
|
|
|
45
45
|
if TYPE_CHECKING:
|
|
46
|
-
from typing import Any,
|
|
46
|
+
from typing import Any, Optional, Union
|
|
47
47
|
|
|
48
48
|
from requests.auth import AuthBase
|
|
49
49
|
|
|
50
50
|
from eodag.api.product import EOProduct
|
|
51
51
|
from eodag.config import PluginConfig
|
|
52
|
+
from eodag.types import S3SessionKwargs
|
|
52
53
|
|
|
53
54
|
logger = logging.getLogger("eodag.search.base")
|
|
54
55
|
|
|
@@ -60,9 +61,9 @@ class Search(PluginTopic):
|
|
|
60
61
|
:param config: An EODAG plugin configuration
|
|
61
62
|
"""
|
|
62
63
|
|
|
63
|
-
auth: Union[AuthBase,
|
|
64
|
+
auth: Union[AuthBase, S3SessionKwargs]
|
|
64
65
|
next_page_url: Optional[str]
|
|
65
|
-
next_page_query_obj: Optional[
|
|
66
|
+
next_page_query_obj: Optional[dict[str, Any]]
|
|
66
67
|
total_items_nb: int
|
|
67
68
|
need_count: bool
|
|
68
69
|
_request: Any # needed by deprecated load_stac_items
|
|
@@ -71,7 +72,7 @@ class Search(PluginTopic):
|
|
|
71
72
|
super(Search, self).__init__(provider, config)
|
|
72
73
|
# Prepare the metadata mapping
|
|
73
74
|
# Do a shallow copy, the structure is flat enough for this to be sufficient
|
|
74
|
-
metas:
|
|
75
|
+
metas: dict[str, Any] = DEFAULT_METADATA_MAPPING.copy()
|
|
75
76
|
# Update the defaults with the mapping value. This will add any new key
|
|
76
77
|
# added by the provider mapping that is not in the default metadata
|
|
77
78
|
if self.config.metadata_mapping:
|
|
@@ -90,7 +91,7 @@ class Search(PluginTopic):
|
|
|
90
91
|
self,
|
|
91
92
|
prep: PreparedSearch = PreparedSearch(),
|
|
92
93
|
**kwargs: Any,
|
|
93
|
-
) ->
|
|
94
|
+
) -> tuple[list[EOProduct], Optional[int]]:
|
|
94
95
|
"""Implementation of how the products must be searched goes here.
|
|
95
96
|
|
|
96
97
|
This method must return a tuple with (1) a list of :class:`~eodag.api.product._product.EOProduct` instances
|
|
@@ -99,13 +100,13 @@ class Search(PluginTopic):
|
|
|
99
100
|
"""
|
|
100
101
|
raise NotImplementedError("A Search plugin must implement a method named query")
|
|
101
102
|
|
|
102
|
-
def discover_product_types(self, **kwargs: Any) -> Optional[
|
|
103
|
+
def discover_product_types(self, **kwargs: Any) -> Optional[dict[str, Any]]:
|
|
103
104
|
"""Fetch product types list from provider using `discover_product_types` conf"""
|
|
104
105
|
return None
|
|
105
106
|
|
|
106
107
|
def discover_queryables(
|
|
107
108
|
self, **kwargs: Any
|
|
108
|
-
) -> Optional[
|
|
109
|
+
) -> Optional[dict[str, Annotated[Any, FieldInfo]]]:
|
|
109
110
|
"""Fetch queryables list from provider using :attr:`~eodag.config.PluginConfig.discover_queryables` conf
|
|
110
111
|
|
|
111
112
|
:param kwargs: additional filters for queryables (``productType`` and other search
|
|
@@ -118,7 +119,7 @@ class Search(PluginTopic):
|
|
|
118
119
|
|
|
119
120
|
def _get_defaults_as_queryables(
|
|
120
121
|
self, product_type: str
|
|
121
|
-
) ->
|
|
122
|
+
) -> dict[str, Annotated[Any, FieldInfo]]:
|
|
122
123
|
"""
|
|
123
124
|
Return given product type default settings as queryables
|
|
124
125
|
|
|
@@ -128,7 +129,7 @@ class Search(PluginTopic):
|
|
|
128
129
|
defaults = deepcopy(self.config.products.get(product_type, {}))
|
|
129
130
|
defaults.pop("metadata_mapping", None)
|
|
130
131
|
|
|
131
|
-
queryables:
|
|
132
|
+
queryables: dict[str, Annotated[Any, FieldInfo]] = {}
|
|
132
133
|
for parameter, value in defaults.items():
|
|
133
134
|
queryables[parameter] = Annotated[type(value), Field(default=value)]
|
|
134
135
|
return queryables
|
|
@@ -150,7 +151,7 @@ class Search(PluginTopic):
|
|
|
150
151
|
|
|
151
152
|
def get_product_type_def_params(
|
|
152
153
|
self, product_type: str, **kwargs: Any
|
|
153
|
-
) ->
|
|
154
|
+
) -> dict[str, Any]:
|
|
154
155
|
"""Get the provider product type definition parameters and specific settings
|
|
155
156
|
|
|
156
157
|
:param product_type: the desired product type
|
|
@@ -200,7 +201,7 @@ class Search(PluginTopic):
|
|
|
200
201
|
|
|
201
202
|
def get_metadata_mapping(
|
|
202
203
|
self, product_type: Optional[str] = None
|
|
203
|
-
) ->
|
|
204
|
+
) -> dict[str, Union[str, list[str]]]:
|
|
204
205
|
"""Get the plugin metadata mapping configuration (product type specific if exists)
|
|
205
206
|
|
|
206
207
|
:param product_type: the desired product type
|
|
@@ -212,7 +213,7 @@ class Search(PluginTopic):
|
|
|
212
213
|
)
|
|
213
214
|
return self.config.metadata_mapping
|
|
214
215
|
|
|
215
|
-
def get_sort_by_arg(self, kwargs:
|
|
216
|
+
def get_sort_by_arg(self, kwargs: dict[str, Any]) -> Optional[SortByList]:
|
|
216
217
|
"""Extract the ``sort_by`` argument from the kwargs or the provider default sort configuration
|
|
217
218
|
|
|
218
219
|
:param kwargs: Search arguments
|
|
@@ -233,7 +234,7 @@ class Search(PluginTopic):
|
|
|
233
234
|
|
|
234
235
|
def build_sort_by(
|
|
235
236
|
self, sort_by_arg: SortByList
|
|
236
|
-
) ->
|
|
237
|
+
) -> tuple[str, dict[str, list[dict[str, str]]]]:
|
|
237
238
|
"""Build the sorting part of the query string or body by transforming
|
|
238
239
|
the ``sort_by`` argument into a provider-specific string or dictionary
|
|
239
240
|
|
|
@@ -247,9 +248,9 @@ class Search(PluginTopic):
|
|
|
247
248
|
sort_by_arg = list(dict.fromkeys(sort_by_arg))
|
|
248
249
|
|
|
249
250
|
sort_by_qs: str = ""
|
|
250
|
-
sort_by_qp:
|
|
251
|
+
sort_by_qp: dict[str, Any] = {}
|
|
251
252
|
|
|
252
|
-
provider_sort_by_tuples_used:
|
|
253
|
+
provider_sort_by_tuples_used: list[tuple[str, str]] = []
|
|
253
254
|
for eodag_sort_by_tuple in sort_by_arg:
|
|
254
255
|
eodag_sort_param = eodag_sort_by_tuple[0]
|
|
255
256
|
provider_sort_param = self.config.sort["sort_param_mapping"].get(
|
|
@@ -282,7 +283,7 @@ class Search(PluginTopic):
|
|
|
282
283
|
if eodag_sort_order == "ASC"
|
|
283
284
|
else self.config.sort["sort_order_mapping"]["descending"]
|
|
284
285
|
)
|
|
285
|
-
provider_sort_by_tuple:
|
|
286
|
+
provider_sort_by_tuple: tuple[str, str] = (
|
|
286
287
|
provider_sort_param,
|
|
287
288
|
provider_sort_order,
|
|
288
289
|
)
|
|
@@ -315,7 +316,7 @@ class Search(PluginTopic):
|
|
|
315
316
|
sort_order=provider_sort_by_tuple[1],
|
|
316
317
|
)
|
|
317
318
|
try:
|
|
318
|
-
parsed_sort_by_tpl_dict:
|
|
319
|
+
parsed_sort_by_tpl_dict: dict[str, Any] = orjson.loads(
|
|
319
320
|
parsed_sort_by_tpl
|
|
320
321
|
)
|
|
321
322
|
sort_by_qp = update_nested_dict(
|
|
@@ -325,35 +326,87 @@ class Search(PluginTopic):
|
|
|
325
326
|
sort_by_qs += parsed_sort_by_tpl
|
|
326
327
|
return (sort_by_qs, sort_by_qp)
|
|
327
328
|
|
|
329
|
+
def _get_product_type_queryables(
|
|
330
|
+
self, product_type: Optional[str], alias: Optional[str], filters: dict[str, Any]
|
|
331
|
+
) -> QueryablesDict:
|
|
332
|
+
default_values: dict[str, Any] = deepcopy(
|
|
333
|
+
getattr(self.config, "products", {}).get(product_type, {})
|
|
334
|
+
)
|
|
335
|
+
default_values.pop("metadata_mapping", None)
|
|
336
|
+
try:
|
|
337
|
+
filters["productType"] = product_type
|
|
338
|
+
queryables = self.discover_queryables(**{**default_values, **filters}) or {}
|
|
339
|
+
except NotImplementedError:
|
|
340
|
+
queryables = self.queryables_from_metadata_mapping(product_type, alias)
|
|
341
|
+
|
|
342
|
+
return QueryablesDict(**queryables)
|
|
343
|
+
|
|
328
344
|
def list_queryables(
|
|
329
345
|
self,
|
|
330
|
-
filters:
|
|
346
|
+
filters: dict[str, Any],
|
|
347
|
+
available_product_types: list[Any],
|
|
348
|
+
product_type_configs: dict[str, dict[str, Any]],
|
|
331
349
|
product_type: Optional[str] = None,
|
|
332
|
-
|
|
350
|
+
alias: Optional[str] = None,
|
|
351
|
+
) -> QueryablesDict:
|
|
333
352
|
"""
|
|
334
353
|
Get queryables
|
|
335
354
|
|
|
336
355
|
:param filters: Additional filters for queryables.
|
|
356
|
+
:param available_product_types: list of available product types
|
|
357
|
+
:param product_type_configs: dict containing the product type information for all used product types
|
|
337
358
|
:param product_type: (optional) The product type.
|
|
359
|
+
:param alias: (optional) alias of the product type
|
|
338
360
|
|
|
339
361
|
:return: A dictionary containing the queryable properties, associating parameters to their
|
|
340
362
|
annotated type.
|
|
341
363
|
"""
|
|
342
|
-
|
|
343
|
-
|
|
364
|
+
additional_info = (
|
|
365
|
+
"Please select a product type to get the possible values of the parameters!"
|
|
366
|
+
if not product_type
|
|
367
|
+
else ""
|
|
344
368
|
)
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
369
|
+
if product_type or getattr(self.config, "discover_queryables", {}).get(
|
|
370
|
+
"fetch_url", ""
|
|
371
|
+
):
|
|
372
|
+
if product_type:
|
|
373
|
+
self.config.product_type_config = product_type_configs[product_type]
|
|
374
|
+
queryables = self._get_product_type_queryables(product_type, alias, filters)
|
|
375
|
+
queryables.additional_information = additional_info
|
|
376
|
+
|
|
377
|
+
return queryables
|
|
378
|
+
else:
|
|
379
|
+
all_queryables: dict[str, Any] = {}
|
|
380
|
+
for pt in available_product_types:
|
|
381
|
+
self.config.product_type_config = product_type_configs[pt]
|
|
382
|
+
pt_queryables = self._get_product_type_queryables(pt, None, filters)
|
|
383
|
+
# only use key and type because values and defaults will vary between product types
|
|
384
|
+
pt_queryables_neutral = {
|
|
385
|
+
k: Annotated[v.__args__[0], Field(default=None)]
|
|
386
|
+
for k, v in pt_queryables.items()
|
|
387
|
+
}
|
|
388
|
+
all_queryables.update(pt_queryables_neutral)
|
|
389
|
+
return QueryablesDict(
|
|
390
|
+
additional_properties=True,
|
|
391
|
+
additional_information=additional_info,
|
|
392
|
+
**all_queryables,
|
|
393
|
+
)
|
|
352
394
|
|
|
353
|
-
|
|
395
|
+
def queryables_from_metadata_mapping(
|
|
396
|
+
self, product_type: Optional[str] = None, alias: Optional[str] = None
|
|
397
|
+
) -> dict[str, Annotated[Any, FieldInfo]]:
|
|
398
|
+
"""
|
|
399
|
+
Extract queryable parameters from product type metadata mapping.
|
|
400
|
+
:param product_type: product type id (optional)
|
|
401
|
+
:param alias: (optional) alias of the product type
|
|
402
|
+
:returns: dict of annotated queryables
|
|
403
|
+
"""
|
|
404
|
+
metadata_mapping: dict[str, Any] = deepcopy(
|
|
354
405
|
self.get_metadata_mapping(product_type)
|
|
355
406
|
)
|
|
356
407
|
|
|
408
|
+
queryables: dict[str, Annotated[Any, FieldInfo]] = {}
|
|
409
|
+
|
|
357
410
|
for param in list(metadata_mapping.keys()):
|
|
358
411
|
if NOT_MAPPED in metadata_mapping[param] or not isinstance(
|
|
359
412
|
metadata_mapping[param], list
|
|
@@ -363,28 +416,18 @@ class Search(PluginTopic):
|
|
|
363
416
|
eodag_queryables = copy_deepcopy(
|
|
364
417
|
model_fields_to_annotated(Queryables.model_fields)
|
|
365
418
|
)
|
|
419
|
+
# add default value for product type
|
|
420
|
+
if alias:
|
|
421
|
+
eodag_queryables.pop("productType")
|
|
422
|
+
eodag_queryables["productType"] = Annotated[str, Field(default=alias)]
|
|
366
423
|
for k, v in eodag_queryables.items():
|
|
367
424
|
eodag_queryable_field_info = (
|
|
368
425
|
get_args(v)[1] if len(get_args(v)) > 1 else None
|
|
369
426
|
)
|
|
370
427
|
if not isinstance(eodag_queryable_field_info, FieldInfo):
|
|
371
428
|
continue
|
|
372
|
-
# keep default field info of eodag queryables
|
|
373
|
-
if k in filters and k in queryables:
|
|
374
|
-
queryable_field_info = (
|
|
375
|
-
get_args(queryables[k])[1]
|
|
376
|
-
if len(get_args(queryables[k])) > 1
|
|
377
|
-
else None
|
|
378
|
-
)
|
|
379
|
-
if not isinstance(queryable_field_info, FieldInfo):
|
|
380
|
-
continue
|
|
381
|
-
queryable_field_info.default = filters[k]
|
|
382
|
-
continue
|
|
383
|
-
if k in queryables:
|
|
384
|
-
continue
|
|
385
429
|
if eodag_queryable_field_info.is_required() or (
|
|
386
430
|
(eodag_queryable_field_info.alias or k) in metadata_mapping
|
|
387
431
|
):
|
|
388
432
|
queryables[k] = v
|
|
389
|
-
|
|
390
433
|
return queryables
|