eodag 3.0.1__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.
- eodag/api/core.py +174 -138
- eodag/api/product/_assets.py +44 -15
- eodag/api/product/_product.py +58 -47
- 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 +117 -90
- eodag/api/search_result.py +13 -23
- eodag/cli.py +26 -5
- eodag/config.py +86 -92
- eodag/plugins/apis/base.py +1 -1
- eodag/plugins/apis/ecmwf.py +42 -22
- eodag/plugins/apis/usgs.py +17 -16
- 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 +22 -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 +146 -87
- 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 +90 -46
- eodag/plugins/search/build_search_result.py +1048 -361
- 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 +19 -18
- eodag/plugins/search/qssearch.py +99 -258
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +4 -4
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1134 -325
- eodag/resources/providers.yml +906 -2006
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +10 -9
- 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 +41 -38
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +29 -23
- eodag/rest/types/queryables.py +42 -31
- 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 +141 -32
- 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 +153 -51
- 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 +231 -0
- eodag/utils/stac_reader.py +10 -10
- {eodag-3.0.1.dist-info → eodag-3.1.0.dist-info}/METADATA +77 -76
- eodag-3.1.0.dist-info/RECORD +113 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0.dist-info}/WHEEL +1 -1
- {eodag-3.0.1.dist-info → eodag-3.1.0.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.0.dist-info}/LICENSE +0 -0
- {eodag-3.0.1.dist-info → eodag-3.1.0.dist-info}/top_level.txt +0 -0
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
|
-
from typing import TYPE_CHECKING, Any,
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
22
22
|
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
|
23
23
|
|
|
24
24
|
import requests
|
|
@@ -47,7 +47,12 @@ logger = logging.getLogger("eodag.authentication.token")
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class TokenAuth(Authentication):
|
|
50
|
-
"""TokenAuth authentication plugin - fetches a token which is added to search/download requests
|
|
50
|
+
"""TokenAuth authentication plugin - fetches a token which is added to search/download requests.
|
|
51
|
+
|
|
52
|
+
When using headers, if only :attr:`~eodag.config.PluginConfig.headers` is given, it will be used for both token
|
|
53
|
+
retrieve and authentication. If :attr:`~eodag.config.PluginConfig.retrieve_headers` is given, it will be used for
|
|
54
|
+
token retrieve only. If both are given, :attr:`~eodag.config.PluginConfig.retrieve_headers` will be used for token
|
|
55
|
+
retrieve and :attr:`~eodag.config.PluginConfig.headers` for authentication.
|
|
51
56
|
|
|
52
57
|
:param provider: provider name
|
|
53
58
|
:param config: Authentication plugin configuration:
|
|
@@ -55,6 +60,10 @@ class TokenAuth(Authentication):
|
|
|
55
60
|
* :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): TokenAuth
|
|
56
61
|
* :attr:`~eodag.config.PluginConfig.auth_uri` (``str``) (**mandatory**): url used to fetch
|
|
57
62
|
the access token with user/password
|
|
63
|
+
* :attr:`~eodag.config.PluginConfig.headers` (``dict[str, str]``): Dictionary containing all
|
|
64
|
+
keys/value pairs that should be added to the headers
|
|
65
|
+
* :attr:`~eodag.config.PluginConfig.retrieve_headers` (``dict[str, str]``): Dictionary containing all
|
|
66
|
+
keys/value pairs that should be added to the headers for token retrieve only
|
|
58
67
|
* :attr:`~eodag.config.PluginConfig.refresh_uri` (``str``) : url used to fetch the
|
|
59
68
|
access token with a refresh token
|
|
60
69
|
* :attr:`~eodag.config.PluginConfig.token_type` (``str``): type of the token (``json``
|
|
@@ -67,13 +76,13 @@ class TokenAuth(Authentication):
|
|
|
67
76
|
should be verified in the requests; default: ``True``
|
|
68
77
|
* :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``): which error code is
|
|
69
78
|
returned in case of an authentication error
|
|
70
|
-
* :attr:`~eodag.config.PluginConfig.req_data` (``
|
|
79
|
+
* :attr:`~eodag.config.PluginConfig.req_data` (``dict[str, Any]``): if the credentials
|
|
71
80
|
should be sent as data in the post request, the json structure can be given in this parameter
|
|
72
81
|
* :attr:`~eodag.config.PluginConfig.retry_total` (``int``): :class:`urllib3.util.Retry` ``total`` parameter,
|
|
73
82
|
total number of retries to allow; default: ``3``
|
|
74
83
|
* :attr:`~eodag.config.PluginConfig.retry_backoff_factor` (``int``): :class:`urllib3.util.Retry`
|
|
75
84
|
``backoff_factor`` parameter, backoff factor to apply between attempts after the second try; default: ``2``
|
|
76
|
-
* :attr:`~eodag.config.PluginConfig.retry_status_forcelist` (``
|
|
85
|
+
* :attr:`~eodag.config.PluginConfig.retry_status_forcelist` (``list[int]``): :class:`urllib3.util.Retry`
|
|
77
86
|
``status_forcelist`` parameter, list of integer HTTP status codes that we should force a retry on; default:
|
|
78
87
|
``[401, 429, 500, 502, 503, 504]``
|
|
79
88
|
"""
|
|
@@ -91,11 +100,29 @@ class TokenAuth(Authentication):
|
|
|
91
100
|
self.config.auth_uri = self.config.auth_uri.format(
|
|
92
101
|
**self.config.credentials
|
|
93
102
|
)
|
|
94
|
-
|
|
103
|
+
|
|
104
|
+
# Format headers if needed with values from the credentials. Note:
|
|
105
|
+
# if only 'headers' is given, it will be used for both token retrieve and authentication.
|
|
106
|
+
# if 'retrieve_headers' is given, it will be used for token retrieve only.
|
|
107
|
+
# if both are given, 'retrieve_headers' will be used for token retrieve and 'headers' for authentication.
|
|
108
|
+
|
|
109
|
+
# If the authentication headers are undefined or None: use an empty dict.
|
|
110
|
+
# And don't format '{token}' now, it will be done later.
|
|
111
|
+
raw_headers = getattr(self.config, "headers", None) or {}
|
|
95
112
|
self.config.headers = {
|
|
96
113
|
header: value.format(**{"token": "{token}", **self.config.credentials})
|
|
97
|
-
for header, value in
|
|
114
|
+
for header, value in raw_headers.items()
|
|
98
115
|
}
|
|
116
|
+
|
|
117
|
+
# If the retrieve headers are undefined, their attribute must not be set in self.config.
|
|
118
|
+
# If they are defined but empty, use an empty dict instead of None.
|
|
119
|
+
if hasattr(self.config, "retrieve_headers"):
|
|
120
|
+
raw_retrieve_headers = self.config.retrieve_headers or {}
|
|
121
|
+
self.config.retrieve_headers = {
|
|
122
|
+
header: value.format(**self.config.credentials)
|
|
123
|
+
for header, value in raw_retrieve_headers.items()
|
|
124
|
+
}
|
|
125
|
+
|
|
99
126
|
except KeyError as e:
|
|
100
127
|
raise MisconfiguredError(
|
|
101
128
|
f"Missing credentials inputs for provider {self.provider}: {e}"
|
|
@@ -166,10 +193,14 @@ class TokenAuth(Authentication):
|
|
|
166
193
|
status_forcelist=retry_status_forcelist,
|
|
167
194
|
)
|
|
168
195
|
|
|
196
|
+
# Use the headers for retrieval if defined, else the headers for authentication
|
|
197
|
+
try:
|
|
198
|
+
headers = self.config.retrieve_headers
|
|
199
|
+
except AttributeError:
|
|
200
|
+
headers = self.config.headers
|
|
201
|
+
|
|
169
202
|
# append headers to req if some are specified in config
|
|
170
|
-
req_kwargs:
|
|
171
|
-
"headers": dict(self.config.headers, **USER_AGENT)
|
|
172
|
-
}
|
|
203
|
+
req_kwargs: dict[str, Any] = {"headers": dict(headers, **USER_AGENT)}
|
|
173
204
|
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
174
205
|
|
|
175
206
|
if self.refresh_token:
|
|
@@ -229,7 +260,7 @@ class RequestsTokenAuth(AuthBase):
|
|
|
229
260
|
token: str,
|
|
230
261
|
where: str,
|
|
231
262
|
qs_key: Optional[str] = None,
|
|
232
|
-
headers: Optional[
|
|
263
|
+
headers: Optional[dict[str, str]] = None,
|
|
233
264
|
) -> None:
|
|
234
265
|
self.token = token
|
|
235
266
|
self.where = where
|
|
@@ -41,7 +41,7 @@ class OIDCTokenExchangeAuth(Authentication):
|
|
|
41
41
|
:param provider: provider name
|
|
42
42
|
:param config: Authentication plugin configuration:
|
|
43
43
|
|
|
44
|
-
* :attr:`~eodag.config.PluginConfig.subject` (``
|
|
44
|
+
* :attr:`~eodag.config.PluginConfig.subject` (``dict[str, Any]``) (**mandatory**):
|
|
45
45
|
The full :class:`~eodag.plugins.authentication.openid_connect.OIDCAuthorizationCodeFlowAuth` plugin
|
|
46
46
|
configuration used to retrieve subject token
|
|
47
47
|
* :attr:`~eodag.config.PluginConfig.subject_issuer` (``str``) (**mandatory**): Identifies
|
eodag/plugins/base.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from typing import TYPE_CHECKING, Any
|
|
20
|
+
from typing import TYPE_CHECKING, Any
|
|
21
21
|
|
|
22
22
|
from eodag.utils.exceptions import PluginNotFoundError
|
|
23
23
|
|
|
@@ -29,21 +29,21 @@ class EODAGPluginMount(type):
|
|
|
29
29
|
"""Plugin mount"""
|
|
30
30
|
|
|
31
31
|
def __init__(
|
|
32
|
-
cls, name: str, bases:
|
|
32
|
+
cls, name: str, bases: tuple[type, ...], attrs: dict[str, Any]
|
|
33
33
|
) -> None:
|
|
34
34
|
if not hasattr(cls, "plugins"):
|
|
35
35
|
# This branch only executes when processing the mount point itself.
|
|
36
36
|
# So, since this is a new plugin type, not an implementation, this
|
|
37
37
|
# class shouldn't be registered as a plugin. Instead, it sets up a
|
|
38
38
|
# list where plugins can be registered later.
|
|
39
|
-
cls.plugins:
|
|
39
|
+
cls.plugins: list[EODAGPluginMount] = []
|
|
40
40
|
else:
|
|
41
41
|
# This must be a plugin implementation, which should be registered.
|
|
42
42
|
# Simply appending it to the list is all that's needed to keep
|
|
43
43
|
# track of it later.
|
|
44
44
|
cls.plugins.append(cls)
|
|
45
45
|
|
|
46
|
-
def get_plugins(cls, *args: Any, **kwargs: Any) ->
|
|
46
|
+
def get_plugins(cls, *args: Any, **kwargs: Any) -> list[EODAGPluginMount]:
|
|
47
47
|
"""Get plugins"""
|
|
48
48
|
return [plugin(*args, **kwargs) for plugin in cls.plugins]
|
|
49
49
|
|
eodag/plugins/crunch/base.py
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
# limitations under the License
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
-
from typing import TYPE_CHECKING, Any,
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
21
21
|
|
|
22
22
|
from eodag.config import PluginConfig
|
|
23
23
|
from eodag.plugins.base import PluginTopic
|
|
@@ -32,12 +32,12 @@ class Crunch(PluginTopic):
|
|
|
32
32
|
:param config: Crunch configuration
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
def __init__(self, config: Optional[
|
|
35
|
+
def __init__(self, config: Optional[dict[str, Any]]) -> None:
|
|
36
36
|
self.config = PluginConfig()
|
|
37
37
|
self.config.__dict__ = config if config is not None else {}
|
|
38
38
|
|
|
39
39
|
def proceed(
|
|
40
|
-
self, products:
|
|
41
|
-
) ->
|
|
40
|
+
self, products: list[EOProduct], **search_params: Any
|
|
41
|
+
) -> list[EOProduct]:
|
|
42
42
|
"""Implementation of how the results must be crunched"""
|
|
43
43
|
raise NotImplementedError
|
|
@@ -21,7 +21,7 @@ import datetime
|
|
|
21
21
|
import logging
|
|
22
22
|
import time
|
|
23
23
|
from datetime import datetime as dt
|
|
24
|
-
from typing import TYPE_CHECKING, Any
|
|
24
|
+
from typing import TYPE_CHECKING, Any
|
|
25
25
|
|
|
26
26
|
import dateutil.parser
|
|
27
27
|
from dateutil import tz
|
|
@@ -57,8 +57,8 @@ class FilterDate(Crunch):
|
|
|
57
57
|
return dateutil.parser.parse(start_date)
|
|
58
58
|
|
|
59
59
|
def proceed(
|
|
60
|
-
self, products:
|
|
61
|
-
) ->
|
|
60
|
+
self, products: list[EOProduct], **search_params: Any
|
|
61
|
+
) -> list[EOProduct]:
|
|
62
62
|
"""Execute crunch: Filter products between start and end dates.
|
|
63
63
|
|
|
64
64
|
:param products: A list of products resulting from a search
|
|
@@ -89,7 +89,7 @@ class FilterDate(Crunch):
|
|
|
89
89
|
if not filter_start and not filter_end:
|
|
90
90
|
return products
|
|
91
91
|
|
|
92
|
-
filtered:
|
|
92
|
+
filtered: list[EOProduct] = []
|
|
93
93
|
for product in products:
|
|
94
94
|
|
|
95
95
|
# product start date
|
|
@@ -20,7 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
import datetime
|
|
21
21
|
import logging
|
|
22
22
|
import time
|
|
23
|
-
from typing import TYPE_CHECKING, Any,
|
|
23
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
24
24
|
|
|
25
25
|
import dateutil.parser
|
|
26
26
|
from shapely import geometry
|
|
@@ -54,14 +54,14 @@ class FilterLatestIntersect(Crunch):
|
|
|
54
54
|
return dateutil.parser.parse(start_date)
|
|
55
55
|
|
|
56
56
|
def proceed(
|
|
57
|
-
self, products:
|
|
58
|
-
) ->
|
|
57
|
+
self, products: list[EOProduct], **search_params: dict[str, Any]
|
|
58
|
+
) -> list[EOProduct]:
|
|
59
59
|
"""Execute crunch:
|
|
60
60
|
Filter latest products (the ones with a the highest start date) that intersect search extent.
|
|
61
61
|
|
|
62
62
|
:param products: A list of products resulting from a search
|
|
63
63
|
:param search_params: Search criteria that must contain ``geometry`` or ``geom`` parameters having value of
|
|
64
|
-
type :class:`shapely.geometry.base.BaseGeometry` or ``
|
|
64
|
+
type :class:`shapely.geometry.base.BaseGeometry` or ``dict[str, Any]``
|
|
65
65
|
:returns: The filtered products
|
|
66
66
|
"""
|
|
67
67
|
logger.debug("Start filtering for latest products")
|
|
@@ -69,9 +69,9 @@ class FilterLatestIntersect(Crunch):
|
|
|
69
69
|
return []
|
|
70
70
|
# Warning: May crash if startTimeFromAscendingNode is not in the appropriate format
|
|
71
71
|
products.sort(key=self.sort_product_by_start_date, reverse=True)
|
|
72
|
-
filtered:
|
|
72
|
+
filtered: list[EOProduct] = []
|
|
73
73
|
add_to_filtered = filtered.append
|
|
74
|
-
footprint: Union[
|
|
74
|
+
footprint: Union[dict[str, Any], BaseGeometry, Any] = search_params.get(
|
|
75
75
|
"geometry"
|
|
76
76
|
) or search_params.get("geom")
|
|
77
77
|
if not footprint:
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
import re
|
|
22
|
-
from typing import TYPE_CHECKING, Any,
|
|
22
|
+
from typing import TYPE_CHECKING, Any, Optional, cast
|
|
23
23
|
|
|
24
24
|
from eodag.plugins.crunch.base import Crunch
|
|
25
25
|
from eodag.utils.exceptions import ValidationError
|
|
@@ -42,7 +42,7 @@ class FilterLatestByName(Crunch):
|
|
|
42
42
|
|
|
43
43
|
NAME_PATTERN_CONSTRAINT = re.compile(r"\(\?P<tileid>\\d\{6\}\)")
|
|
44
44
|
|
|
45
|
-
def __init__(self, config:
|
|
45
|
+
def __init__(self, config: dict[str, Any]) -> None:
|
|
46
46
|
super(FilterLatestByName, self).__init__(config)
|
|
47
47
|
name_pattern = config.pop("name_pattern")
|
|
48
48
|
if not self.NAME_PATTERN_CONSTRAINT.search(name_pattern):
|
|
@@ -54,19 +54,19 @@ class FilterLatestByName(Crunch):
|
|
|
54
54
|
self.name_pattern = re.compile(name_pattern)
|
|
55
55
|
|
|
56
56
|
def proceed(
|
|
57
|
-
self, products:
|
|
58
|
-
) ->
|
|
57
|
+
self, products: list[EOProduct], **search_params: Any
|
|
58
|
+
) -> list[EOProduct]:
|
|
59
59
|
"""Execute crunch: Filter Search results to get only the latest product, based on the name of the product
|
|
60
60
|
|
|
61
61
|
:param products: A list of products resulting from a search
|
|
62
62
|
:returns: The filtered products
|
|
63
63
|
"""
|
|
64
64
|
logger.debug("Starting products filtering")
|
|
65
|
-
processed:
|
|
66
|
-
filtered:
|
|
65
|
+
processed: list[str] = []
|
|
66
|
+
filtered: list[EOProduct] = []
|
|
67
67
|
for product in products:
|
|
68
68
|
match = cast(
|
|
69
|
-
Optional[Match[Any]],
|
|
69
|
+
Optional[re.Match[Any]],
|
|
70
70
|
self.name_pattern.match(product.properties["title"]),
|
|
71
71
|
)
|
|
72
72
|
if match:
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
|
-
from typing import TYPE_CHECKING, Any
|
|
21
|
+
from typing import TYPE_CHECKING, Any
|
|
22
22
|
|
|
23
23
|
from eodag.plugins.crunch.base import Crunch
|
|
24
24
|
from eodag.utils import get_geometry_from_various
|
|
@@ -49,8 +49,8 @@ class FilterOverlap(Crunch):
|
|
|
49
49
|
"""
|
|
50
50
|
|
|
51
51
|
def proceed(
|
|
52
|
-
self, products:
|
|
53
|
-
) ->
|
|
52
|
+
self, products: list[EOProduct], **search_params: Any
|
|
53
|
+
) -> list[EOProduct]:
|
|
54
54
|
"""Execute crunch: Filter products, retaining only those that are overlapping with the search_extent
|
|
55
55
|
|
|
56
56
|
:param products: A list of products resulting from a search
|
|
@@ -58,7 +58,7 @@ class FilterOverlap(Crunch):
|
|
|
58
58
|
:returns: The filtered products
|
|
59
59
|
"""
|
|
60
60
|
logger.debug("Start filtering for overlapping products")
|
|
61
|
-
filtered:
|
|
61
|
+
filtered: list[EOProduct] = []
|
|
62
62
|
add_to_filtered = filtered.append
|
|
63
63
|
|
|
64
64
|
search_geom = get_geometry_from_various(**search_params)
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
import operator
|
|
22
|
-
from typing import TYPE_CHECKING, Any
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
23
|
|
|
24
24
|
from eodag.plugins.crunch.base import Crunch
|
|
25
25
|
|
|
@@ -42,8 +42,8 @@ class FilterProperty(Crunch):
|
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
def proceed(
|
|
45
|
-
self, products:
|
|
46
|
-
) ->
|
|
45
|
+
self, products: list[EOProduct], **search_params: Any
|
|
46
|
+
) -> list[EOProduct]:
|
|
47
47
|
"""Execute crunch: Filter products, retaining only those that match property filtering
|
|
48
48
|
|
|
49
49
|
:param products: A list of products resulting from a search
|
|
@@ -72,16 +72,15 @@ class FilterProperty(Crunch):
|
|
|
72
72
|
property_key,
|
|
73
73
|
property_value,
|
|
74
74
|
)
|
|
75
|
-
filtered:
|
|
75
|
+
filtered: list[EOProduct] = []
|
|
76
76
|
add_to_filtered = filtered.append
|
|
77
77
|
|
|
78
78
|
for product in products:
|
|
79
79
|
if property_key not in product.properties.keys():
|
|
80
80
|
logger.warning(
|
|
81
|
-
"
|
|
82
|
-
property_key,
|
|
81
|
+
f"{property_key} not found in {product}.properties, product skipped",
|
|
83
82
|
)
|
|
84
|
-
|
|
83
|
+
continue
|
|
85
84
|
if operator_method(product.properties[property_key], property_value):
|
|
86
85
|
add_to_filtered(product)
|
|
87
86
|
|