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
eodag/plugins/search/qssearch.py
CHANGED
|
@@ -19,19 +19,15 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
21
|
import re
|
|
22
|
+
import socket
|
|
22
23
|
from copy import copy as copy_copy
|
|
23
|
-
from datetime import datetime
|
|
24
24
|
from typing import (
|
|
25
25
|
TYPE_CHECKING,
|
|
26
26
|
Annotated,
|
|
27
27
|
Any,
|
|
28
28
|
Callable,
|
|
29
|
-
Dict,
|
|
30
|
-
List,
|
|
31
29
|
Optional,
|
|
32
30
|
Sequence,
|
|
33
|
-
Set,
|
|
34
|
-
Tuple,
|
|
35
31
|
TypedDict,
|
|
36
32
|
cast,
|
|
37
33
|
get_args,
|
|
@@ -52,7 +48,6 @@ import geojson
|
|
|
52
48
|
import orjson
|
|
53
49
|
import requests
|
|
54
50
|
import yaml
|
|
55
|
-
from dateutil.utils import today
|
|
56
51
|
from jsonpath_ng import JSONPath
|
|
57
52
|
from lxml import etree
|
|
58
53
|
from pydantic import create_model
|
|
@@ -75,9 +70,9 @@ from eodag.api.search_result import RawSearchResult
|
|
|
75
70
|
from eodag.plugins.search import PreparedSearch
|
|
76
71
|
from eodag.plugins.search.base import Search
|
|
77
72
|
from eodag.types import json_field_definition_to_python, model_fields_to_annotated
|
|
78
|
-
from eodag.types.queryables import CommonQueryables
|
|
79
73
|
from eodag.types.search_args import SortByList
|
|
80
74
|
from eodag.utils import (
|
|
75
|
+
DEFAULT_SEARCH_TIMEOUT,
|
|
81
76
|
GENERIC_PRODUCT_TYPE,
|
|
82
77
|
HTTP_REQ_TIMEOUT,
|
|
83
78
|
REQ_RETRY_BACKOFF_FACTOR,
|
|
@@ -94,10 +89,6 @@ from eodag.utils import (
|
|
|
94
89
|
update_nested_dict,
|
|
95
90
|
urlencode,
|
|
96
91
|
)
|
|
97
|
-
from eodag.utils.constraints import (
|
|
98
|
-
fetch_constraints,
|
|
99
|
-
get_constraint_queryables_with_additional_params,
|
|
100
|
-
)
|
|
101
92
|
from eodag.utils.exceptions import (
|
|
102
93
|
AuthenticationError,
|
|
103
94
|
MisconfiguredError,
|
|
@@ -132,7 +123,7 @@ class QueryStringSearch(Search):
|
|
|
132
123
|
authentication error; only used if ``need_auth=true``
|
|
133
124
|
* :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates should be verified in
|
|
134
125
|
requests; default: ``True``
|
|
135
|
-
* :attr:`~eodag.config.PluginConfig.dont_quote` (``
|
|
126
|
+
* :attr:`~eodag.config.PluginConfig.dont_quote` (``list[str]``): characters that should not be quoted in the
|
|
136
127
|
url params
|
|
137
128
|
* :attr:`~eodag.config.PluginConfig.timeout` (``int``): time to wait until request timeout in seconds;
|
|
138
129
|
default: ``5``
|
|
@@ -140,10 +131,10 @@ class QueryStringSearch(Search):
|
|
|
140
131
|
total number of retries to allow; default: ``3``
|
|
141
132
|
* :attr:`~eodag.config.PluginConfig.retry_backoff_factor` (``int``): :class:`urllib3.util.Retry`
|
|
142
133
|
``backoff_factor`` parameter, backoff factor to apply between attempts after the second try; default: ``2``
|
|
143
|
-
* :attr:`~eodag.config.PluginConfig.retry_status_forcelist` (``
|
|
134
|
+
* :attr:`~eodag.config.PluginConfig.retry_status_forcelist` (``list[int]``): :class:`urllib3.util.Retry`
|
|
144
135
|
``status_forcelist`` parameter, list of integer HTTP status codes that we should force a retry on; default:
|
|
145
136
|
``[401, 429, 500, 502, 503, 504]``
|
|
146
|
-
* :attr:`~eodag.config.PluginConfig.literal_search_params` (``
|
|
137
|
+
* :attr:`~eodag.config.PluginConfig.literal_search_params` (``dict[str, str]``): A mapping of (search_param =>
|
|
147
138
|
search_value) pairs giving search parameters to be passed as is in the search url query string. This is useful
|
|
148
139
|
for example in situations where the user wants to add a fixed search query parameter exactly
|
|
149
140
|
as it is done on the provider interface.
|
|
@@ -187,10 +178,13 @@ class QueryStringSearch(Search):
|
|
|
187
178
|
* :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_id` (``str``): mapping for the
|
|
188
179
|
product type id
|
|
189
180
|
* :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_parsable_metadata`
|
|
190
|
-
(``
|
|
181
|
+
(``dict[str, str]``): mapping for product type metadata (e.g. ``abstract``, ``licence``) which can be parsed
|
|
191
182
|
from the provider result
|
|
192
183
|
* :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_parsable_properties`
|
|
193
|
-
(``
|
|
184
|
+
(``dict[str, str]``): mapping for product type properties which can be parsed from the result and are not
|
|
185
|
+
product type metadata
|
|
186
|
+
* :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.generic_product_type_unparsable_properties`
|
|
187
|
+
(``dict[str, str]``): mapping for product type properties which cannot be parsed from the result and are not
|
|
194
188
|
product type metadata
|
|
195
189
|
* :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_collection_fetch_url` (``str``): url to fetch
|
|
196
190
|
data for a single collection; used if product type metadata is not available from the endpoint given in
|
|
@@ -199,13 +193,13 @@ class QueryStringSearch(Search):
|
|
|
199
193
|
to be added to the :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.fetch_url` to filter for a
|
|
200
194
|
collection
|
|
201
195
|
* :attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_product_type_parsable_metadata`
|
|
202
|
-
(``
|
|
196
|
+
(``dict[str, str]``): mapping for product type metadata returned by the endpoint given in
|
|
203
197
|
:attr:`~eodag.config.PluginConfig.DiscoverProductTypes.single_collection_fetch_url`.
|
|
204
198
|
|
|
205
199
|
* :attr:`~eodag.config.PluginConfig.sort` (:class:`~eodag.config.PluginConfig.Sort`): configuration for sorting
|
|
206
200
|
the results. It contains the keys:
|
|
207
201
|
|
|
208
|
-
* :attr:`~eodag.config.PluginConfig.Sort.sort_by_default` (``
|
|
202
|
+
* :attr:`~eodag.config.PluginConfig.Sort.sort_by_default` (``list[Tuple(str, Literal["ASC", "DESC"])]``):
|
|
209
203
|
parameter and sort order by which the result will be sorted by default (if the user does not enter a
|
|
210
204
|
``sort_by`` parameter); if not given the result will use the default sorting of the provider; Attention:
|
|
211
205
|
for some providers sorting might cause a timeout if no filters are used. In that case no default
|
|
@@ -221,12 +215,12 @@ class QueryStringSearch(Search):
|
|
|
221
215
|
* :attr:`~eodag.config.PluginConfig.Sort.sort_param_mapping` (``Dict [str, str]``): mapping for the parameters
|
|
222
216
|
available for sorting
|
|
223
217
|
* :attr:`~eodag.config.PluginConfig.Sort.sort_order_mapping`
|
|
224
|
-
(``
|
|
218
|
+
(``dict[Literal["ascending", "descending"], str]``): mapping for the sort order
|
|
225
219
|
* :attr:`~eodag.config.PluginConfig.Sort.max_sort_params` (``int``): maximum number of sort parameters
|
|
226
220
|
supported by the provider; used to validate the user input to avoid failed requests or unexpected behaviour
|
|
227
221
|
(not all parameters are used in the request)
|
|
228
222
|
|
|
229
|
-
* :attr:`~eodag.config.PluginConfig.metadata_mapping` (``
|
|
223
|
+
* :attr:`~eodag.config.PluginConfig.metadata_mapping` (``dict[str, Any]``): The search plugins of this kind can
|
|
230
224
|
detect when a metadata mapping is "query-able", and get the semantics of how to format the query string
|
|
231
225
|
parameter that enables to make a query on the corresponding metadata. To make a metadata query-able,
|
|
232
226
|
just configure it in the metadata mapping to be a list of 2 items, the first one being the
|
|
@@ -259,7 +253,7 @@ class QueryStringSearch(Search):
|
|
|
259
253
|
metadata is activated; default: ``False``; if false, the other parameters are not used;
|
|
260
254
|
* :attr:`~eodag.config.PluginConfig.DiscoverMetadata.metadata_pattern` (``str``): regex string a parameter in
|
|
261
255
|
the result should match so that is used
|
|
262
|
-
* :attr:`~eodag.config.PluginConfig.DiscoverMetadata.search_param` (``Union [str,
|
|
256
|
+
* :attr:`~eodag.config.PluginConfig.DiscoverMetadata.search_param` (``Union [str, dict[str, Any]]``): format
|
|
263
257
|
to add a query param given by the user and not in the metadata mapping to the requests, 'metadata' will be
|
|
264
258
|
replaced by the search param; can be a string or a dict containing
|
|
265
259
|
:attr:`~eodag.config.PluginConfig.free_text_search_operations`
|
|
@@ -282,16 +276,12 @@ class QueryStringSearch(Search):
|
|
|
282
276
|
|
|
283
277
|
* :attr:`~eodag.config.PluginConfig.constraints_file_url` (``str``): url to fetch the constraints for a specific
|
|
284
278
|
product type, can be an http url or a path to a file; the constraints are used to build queryables
|
|
285
|
-
* :attr:`~eodag.config.PluginConfig.constraints_file_dataset_key` (``str``): key which is used in the eodag
|
|
286
|
-
configuration to map the eodag product type to the provider product type; default: ``dataset``
|
|
287
279
|
* :attr:`~eodag.config.PluginConfig.constraints_entry` (``str``): key in the json result where the constraints
|
|
288
280
|
can be found; if not given, it is assumed that the constraints are on top level of the result, i.e.
|
|
289
281
|
the result is an array of constraints
|
|
290
|
-
* :attr:`~eodag.config.PluginConfig.stop_without_constraints_entry_key` (``bool``): if true only a provider
|
|
291
|
-
result containing `constraints_entry` is accepted as valid and used to create constraints; default: ``False``
|
|
292
282
|
"""
|
|
293
283
|
|
|
294
|
-
extract_properties:
|
|
284
|
+
extract_properties: dict[str, Callable[..., dict[str, Any]]] = {
|
|
295
285
|
"xml": properties_from_xml,
|
|
296
286
|
"json": properties_from_json,
|
|
297
287
|
}
|
|
@@ -302,8 +292,8 @@ class QueryStringSearch(Search):
|
|
|
302
292
|
self.config.__dict__.setdefault("results_entry", "features")
|
|
303
293
|
self.config.__dict__.setdefault("pagination", {})
|
|
304
294
|
self.config.__dict__.setdefault("free_text_search_operations", {})
|
|
305
|
-
self.search_urls:
|
|
306
|
-
self.query_params:
|
|
295
|
+
self.search_urls: list[str] = []
|
|
296
|
+
self.query_params: dict[str, str] = dict()
|
|
307
297
|
self.query_string = ""
|
|
308
298
|
self.next_page_url = None
|
|
309
299
|
self.next_page_query_obj = None
|
|
@@ -448,7 +438,7 @@ class QueryStringSearch(Search):
|
|
|
448
438
|
self.next_page_query_obj = None
|
|
449
439
|
self.next_page_merge = None
|
|
450
440
|
|
|
451
|
-
def discover_product_types(self, **kwargs: Any) -> Optional[
|
|
441
|
+
def discover_product_types(self, **kwargs: Any) -> Optional[dict[str, Any]]:
|
|
452
442
|
"""Fetch product types list from provider using `discover_product_types` conf
|
|
453
443
|
|
|
454
444
|
:returns: configuration dict containing fetched product types information
|
|
@@ -465,7 +455,7 @@ class QueryStringSearch(Search):
|
|
|
465
455
|
# no pagination
|
|
466
456
|
return self.discover_product_types_per_page(**kwargs)
|
|
467
457
|
|
|
468
|
-
conf_update_dict:
|
|
458
|
+
conf_update_dict: dict[str, Any] = {
|
|
469
459
|
"providers_config": {},
|
|
470
460
|
"product_types_config": {},
|
|
471
461
|
}
|
|
@@ -498,7 +488,7 @@ class QueryStringSearch(Search):
|
|
|
498
488
|
|
|
499
489
|
def discover_product_types_per_page(
|
|
500
490
|
self, **kwargs: Any
|
|
501
|
-
) -> Optional[
|
|
491
|
+
) -> Optional[dict[str, Any]]:
|
|
502
492
|
"""Fetch product types list from provider using `discover_product_types` conf
|
|
503
493
|
using paginated ``kwargs["fetch_url"]``
|
|
504
494
|
|
|
@@ -541,7 +531,7 @@ class QueryStringSearch(Search):
|
|
|
541
531
|
|
|
542
532
|
prep.info_message = "Fetching product types: {}".format(prep.url)
|
|
543
533
|
prep.exception_message = (
|
|
544
|
-
"Skipping error while fetching product types for
|
|
534
|
+
"Skipping error while fetching product types for {} {} instance:"
|
|
545
535
|
).format(self.provider, self.__class__.__name__)
|
|
546
536
|
|
|
547
537
|
# Query using appropriate method
|
|
@@ -556,7 +546,7 @@ class QueryStringSearch(Search):
|
|
|
556
546
|
return None
|
|
557
547
|
else:
|
|
558
548
|
try:
|
|
559
|
-
conf_update_dict:
|
|
549
|
+
conf_update_dict: dict[str, Any] = {
|
|
560
550
|
"providers_config": {},
|
|
561
551
|
"product_types_config": {},
|
|
562
552
|
}
|
|
@@ -575,7 +565,7 @@ class QueryStringSearch(Search):
|
|
|
575
565
|
result = result[0]
|
|
576
566
|
|
|
577
567
|
def conf_update_from_product_type_result(
|
|
578
|
-
product_type_result:
|
|
568
|
+
product_type_result: dict[str, Any],
|
|
579
569
|
) -> None:
|
|
580
570
|
"""Update ``conf_update_dict`` using given product type json response"""
|
|
581
571
|
# providers_config extraction
|
|
@@ -641,7 +631,11 @@ class QueryStringSearch(Search):
|
|
|
641
631
|
][kf]
|
|
642
632
|
)
|
|
643
633
|
for kf in keywords_fields
|
|
644
|
-
if
|
|
634
|
+
if kf
|
|
635
|
+
in conf_update_dict["product_types_config"][
|
|
636
|
+
generic_product_type_id
|
|
637
|
+
]
|
|
638
|
+
and conf_update_dict["product_types_config"][
|
|
645
639
|
generic_product_type_id
|
|
646
640
|
][kf]
|
|
647
641
|
!= NOT_AVAILABLE
|
|
@@ -699,7 +693,7 @@ class QueryStringSearch(Search):
|
|
|
699
693
|
|
|
700
694
|
def _get_product_type_metadata_from_single_collection_endpoint(
|
|
701
695
|
self, product_type: str
|
|
702
|
-
) ->
|
|
696
|
+
) -> dict[str, Any]:
|
|
703
697
|
"""
|
|
704
698
|
retrieves additional product type information from an endpoint returning data for a single collection
|
|
705
699
|
:param product_type: product type
|
|
@@ -723,107 +717,11 @@ class QueryStringSearch(Search):
|
|
|
723
717
|
self.config.discover_product_types["single_product_type_parsable_metadata"],
|
|
724
718
|
)
|
|
725
719
|
|
|
726
|
-
def discover_queryables(
|
|
727
|
-
self, **kwargs: Any
|
|
728
|
-
) -> Optional[Dict[str, Annotated[Any, FieldInfo]]]:
|
|
729
|
-
"""Fetch queryables list from provider using its constraints file
|
|
730
|
-
|
|
731
|
-
:param kwargs: additional filters for queryables (`productType` and other search
|
|
732
|
-
arguments)
|
|
733
|
-
:returns: fetched queryable parameters dict
|
|
734
|
-
"""
|
|
735
|
-
product_type = kwargs.pop("productType", None)
|
|
736
|
-
if not product_type:
|
|
737
|
-
return {}
|
|
738
|
-
constraints_file_url = getattr(self.config, "constraints_file_url", "")
|
|
739
|
-
if not constraints_file_url:
|
|
740
|
-
return {}
|
|
741
|
-
|
|
742
|
-
constraints_file_dataset_key = getattr(
|
|
743
|
-
self.config, "constraints_file_dataset_key", "dataset"
|
|
744
|
-
)
|
|
745
|
-
provider_product_type = self.config.products.get(product_type, {}).get(
|
|
746
|
-
constraints_file_dataset_key, None
|
|
747
|
-
)
|
|
748
|
-
|
|
749
|
-
# defaults
|
|
750
|
-
default_queryables = self._get_defaults_as_queryables(product_type)
|
|
751
|
-
# remove unwanted queryables
|
|
752
|
-
for param in getattr(self.config, "remove_from_queryables", []):
|
|
753
|
-
default_queryables.pop(param, None)
|
|
754
|
-
|
|
755
|
-
non_empty_kwargs = {k: v for k, v in kwargs.items() if v}
|
|
756
|
-
|
|
757
|
-
if "{" in constraints_file_url:
|
|
758
|
-
constraints_file_url = constraints_file_url.format(
|
|
759
|
-
dataset=provider_product_type
|
|
760
|
-
)
|
|
761
|
-
constraints = fetch_constraints(constraints_file_url, self)
|
|
762
|
-
if not constraints:
|
|
763
|
-
return default_queryables
|
|
764
|
-
|
|
765
|
-
constraint_params: Dict[str, Dict[str, Set[Any]]] = {}
|
|
766
|
-
if len(kwargs) == 0:
|
|
767
|
-
# get values from constraints without additional filters
|
|
768
|
-
for constraint in constraints:
|
|
769
|
-
for key in constraint.keys():
|
|
770
|
-
if key in constraint_params:
|
|
771
|
-
constraint_params[key]["enum"].update(constraint[key])
|
|
772
|
-
else:
|
|
773
|
-
constraint_params[key] = {"enum": set(constraint[key])}
|
|
774
|
-
else:
|
|
775
|
-
# get values from constraints with additional filters
|
|
776
|
-
constraints_input_params = {k: v for k, v in non_empty_kwargs.items()}
|
|
777
|
-
constraint_params = get_constraint_queryables_with_additional_params(
|
|
778
|
-
constraints, constraints_input_params, self, product_type
|
|
779
|
-
)
|
|
780
|
-
# query params that are not in constraints but might be default queryables
|
|
781
|
-
if len(constraint_params) == 1 and "not_available" in constraint_params:
|
|
782
|
-
not_queryables = set()
|
|
783
|
-
for constraint_param in constraint_params["not_available"]["enum"]:
|
|
784
|
-
param = CommonQueryables.get_queryable_from_alias(constraint_param)
|
|
785
|
-
if param in dict(
|
|
786
|
-
CommonQueryables.model_fields, **default_queryables
|
|
787
|
-
):
|
|
788
|
-
non_empty_kwargs.pop(constraint_param)
|
|
789
|
-
else:
|
|
790
|
-
not_queryables.add(constraint_param)
|
|
791
|
-
if not_queryables:
|
|
792
|
-
raise ValidationError(
|
|
793
|
-
f"parameter(s) {str(not_queryables)} not queryable"
|
|
794
|
-
)
|
|
795
|
-
else:
|
|
796
|
-
# get constraints again without common queryables
|
|
797
|
-
constraint_params = (
|
|
798
|
-
get_constraint_queryables_with_additional_params(
|
|
799
|
-
constraints, non_empty_kwargs, self, product_type
|
|
800
|
-
)
|
|
801
|
-
)
|
|
802
|
-
|
|
803
|
-
field_definitions: Dict[str, Any] = dict()
|
|
804
|
-
for json_param, json_mtd in constraint_params.items():
|
|
805
|
-
param = (
|
|
806
|
-
get_queryable_from_provider(
|
|
807
|
-
json_param, self.get_metadata_mapping(product_type)
|
|
808
|
-
)
|
|
809
|
-
or json_param
|
|
810
|
-
)
|
|
811
|
-
default = kwargs.get(param, None) or self.config.products.get(
|
|
812
|
-
product_type, {}
|
|
813
|
-
).get(param, None)
|
|
814
|
-
annotated_def = json_field_definition_to_python(
|
|
815
|
-
json_mtd, default_value=default, required=True
|
|
816
|
-
)
|
|
817
|
-
field_definitions[param] = get_args(annotated_def)
|
|
818
|
-
|
|
819
|
-
python_queryables = create_model("m", **field_definitions).model_fields
|
|
820
|
-
return dict(default_queryables, **model_fields_to_annotated(python_queryables))
|
|
821
|
-
|
|
822
720
|
def query(
|
|
823
721
|
self,
|
|
824
722
|
prep: PreparedSearch = PreparedSearch(),
|
|
825
723
|
**kwargs: Any,
|
|
826
|
-
) ->
|
|
724
|
+
) -> tuple[list[EOProduct], Optional[int]]:
|
|
827
725
|
"""Perform a search on an OpenSearch-like interface
|
|
828
726
|
|
|
829
727
|
:param prep: Object collecting needed information for search.
|
|
@@ -851,7 +749,7 @@ class QueryStringSearch(Search):
|
|
|
851
749
|
|
|
852
750
|
# provider product type specific conf
|
|
853
751
|
prep.product_type_def_params = (
|
|
854
|
-
self.get_product_type_def_params(product_type,
|
|
752
|
+
self.get_product_type_def_params(product_type, format_variables=kwargs)
|
|
855
753
|
if product_type is not None
|
|
856
754
|
else {}
|
|
857
755
|
)
|
|
@@ -875,7 +773,7 @@ class QueryStringSearch(Search):
|
|
|
875
773
|
}
|
|
876
774
|
)
|
|
877
775
|
|
|
878
|
-
qp, qs = self.build_query_string(product_type,
|
|
776
|
+
qp, qs = self.build_query_string(product_type, keywords)
|
|
879
777
|
|
|
880
778
|
prep.query_params = qp
|
|
881
779
|
prep.query_string = qs
|
|
@@ -903,21 +801,21 @@ class QueryStringSearch(Search):
|
|
|
903
801
|
reason="Simply run `self.config.metadata_mapping.update(metadata_mapping)` instead",
|
|
904
802
|
version="2.10.0",
|
|
905
803
|
)
|
|
906
|
-
def update_metadata_mapping(self, metadata_mapping:
|
|
804
|
+
def update_metadata_mapping(self, metadata_mapping: dict[str, Any]) -> None:
|
|
907
805
|
"""Update plugin metadata_mapping with input metadata_mapping configuration"""
|
|
908
806
|
if self.config.metadata_mapping:
|
|
909
807
|
self.config.metadata_mapping.update(metadata_mapping)
|
|
910
808
|
|
|
911
809
|
def build_query_string(
|
|
912
|
-
self, product_type: str,
|
|
913
|
-
) ->
|
|
810
|
+
self, product_type: str, query_dict: dict[str, Any]
|
|
811
|
+
) -> tuple[dict[str, Any], str]:
|
|
914
812
|
"""Build The query string using the search parameters"""
|
|
915
813
|
logger.debug("Building the query string that will be used for search")
|
|
916
|
-
query_params = format_query_params(product_type, self.config,
|
|
814
|
+
query_params = format_query_params(product_type, self.config, query_dict)
|
|
917
815
|
|
|
918
816
|
# Build the final query string, in one go without quoting it
|
|
919
817
|
# (some providers do not operate well with urlencoded and quoted query strings)
|
|
920
|
-
def quote_via(x: Any, *_args, **_kwargs) -> str:
|
|
818
|
+
def quote_via(x: Any, *_args: Any, **_kwargs: Any) -> str:
|
|
921
819
|
return x
|
|
922
820
|
|
|
923
821
|
return (
|
|
@@ -929,7 +827,7 @@ class QueryStringSearch(Search):
|
|
|
929
827
|
self,
|
|
930
828
|
prep: PreparedSearch = PreparedSearch(page=None, items_per_page=None),
|
|
931
829
|
**kwargs: Any,
|
|
932
|
-
) ->
|
|
830
|
+
) -> tuple[list[str], Optional[int]]:
|
|
933
831
|
"""Build paginated urls"""
|
|
934
832
|
page = prep.page
|
|
935
833
|
items_per_page = prep.items_per_page
|
|
@@ -998,7 +896,7 @@ class QueryStringSearch(Search):
|
|
|
998
896
|
|
|
999
897
|
def do_search(
|
|
1000
898
|
self, prep: PreparedSearch = PreparedSearch(items_per_page=None), **kwargs: Any
|
|
1001
|
-
) ->
|
|
899
|
+
) -> list[Any]:
|
|
1002
900
|
"""Perform the actual search request.
|
|
1003
901
|
|
|
1004
902
|
If there is a specified number of items per page, return the results as soon
|
|
@@ -1015,7 +913,7 @@ class QueryStringSearch(Search):
|
|
|
1015
913
|
"total_items_nb_key_path"
|
|
1016
914
|
]
|
|
1017
915
|
|
|
1018
|
-
results:
|
|
916
|
+
results: list[Any] = []
|
|
1019
917
|
for search_url in prep.search_urls:
|
|
1020
918
|
single_search_prep = copy_copy(prep)
|
|
1021
919
|
single_search_prep.url = search_url
|
|
@@ -1138,9 +1036,13 @@ class QueryStringSearch(Search):
|
|
|
1138
1036
|
logger.debug(
|
|
1139
1037
|
"Could not extract total_items_nb from search results"
|
|
1140
1038
|
)
|
|
1141
|
-
if
|
|
1039
|
+
if (
|
|
1040
|
+
getattr(self.config, "merge_responses", False)
|
|
1041
|
+
and self.config.result_type == "json"
|
|
1042
|
+
):
|
|
1043
|
+
json_result = cast(list[dict[str, Any]], result)
|
|
1142
1044
|
results = (
|
|
1143
|
-
[dict(r, **
|
|
1045
|
+
[dict(r, **json_result[i]) for i, r in enumerate(results)]
|
|
1144
1046
|
if results
|
|
1145
1047
|
else result
|
|
1146
1048
|
)
|
|
@@ -1162,14 +1064,14 @@ class QueryStringSearch(Search):
|
|
|
1162
1064
|
|
|
1163
1065
|
def normalize_results(
|
|
1164
1066
|
self, results: RawSearchResult, **kwargs: Any
|
|
1165
|
-
) ->
|
|
1067
|
+
) -> list[EOProduct]:
|
|
1166
1068
|
"""Build EOProducts from provider results"""
|
|
1167
1069
|
normalize_remaining_count = len(results)
|
|
1168
1070
|
logger.debug(
|
|
1169
1071
|
"Adapting %s plugin results to eodag product representation"
|
|
1170
1072
|
% normalize_remaining_count
|
|
1171
1073
|
)
|
|
1172
|
-
products:
|
|
1074
|
+
products: list[EOProduct] = []
|
|
1173
1075
|
for result in results:
|
|
1174
1076
|
product = EOProduct(
|
|
1175
1077
|
self.provider,
|
|
@@ -1184,8 +1086,15 @@ class QueryStringSearch(Search):
|
|
|
1184
1086
|
product.properties = dict(
|
|
1185
1087
|
getattr(self.config, "product_type_config", {}), **product.properties
|
|
1186
1088
|
)
|
|
1187
|
-
# move assets from properties to product's attr
|
|
1188
|
-
product.
|
|
1089
|
+
# move assets from properties to product's attr, normalize keys & roles
|
|
1090
|
+
for key, asset in product.properties.pop("assets", {}).items():
|
|
1091
|
+
norm_key, asset["roles"] = product.driver.guess_asset_key_and_roles(
|
|
1092
|
+
asset.get("href", ""), product
|
|
1093
|
+
)
|
|
1094
|
+
if norm_key:
|
|
1095
|
+
product.assets[norm_key] = asset
|
|
1096
|
+
# sort assets
|
|
1097
|
+
product.assets.data = dict(sorted(product.assets.data.items()))
|
|
1189
1098
|
products.append(product)
|
|
1190
1099
|
return products
|
|
1191
1100
|
|
|
@@ -1227,7 +1136,7 @@ class QueryStringSearch(Search):
|
|
|
1227
1136
|
total_results = int(count_results)
|
|
1228
1137
|
return total_results
|
|
1229
1138
|
|
|
1230
|
-
def get_collections(self, prep: PreparedSearch, **kwargs: Any) ->
|
|
1139
|
+
def get_collections(self, prep: PreparedSearch, **kwargs: Any) -> tuple[str, ...]:
|
|
1231
1140
|
"""Get the collection to which the product belongs"""
|
|
1232
1141
|
# See https://earth.esa.int/web/sentinel/missions/sentinel-2/news/-
|
|
1233
1142
|
# /asset_publisher/Ac0d/content/change-of
|
|
@@ -1238,7 +1147,7 @@ class QueryStringSearch(Search):
|
|
|
1238
1147
|
not hasattr(prep, "product_type_def_params")
|
|
1239
1148
|
or not prep.product_type_def_params
|
|
1240
1149
|
):
|
|
1241
|
-
collections:
|
|
1150
|
+
collections: set[str] = set()
|
|
1242
1151
|
collection = getattr(self.config, "collection", None)
|
|
1243
1152
|
if collection is None:
|
|
1244
1153
|
try:
|
|
@@ -1280,7 +1189,7 @@ class QueryStringSearch(Search):
|
|
|
1280
1189
|
info_message = prep.info_message
|
|
1281
1190
|
exception_message = prep.exception_message
|
|
1282
1191
|
try:
|
|
1283
|
-
timeout = getattr(self.config, "timeout",
|
|
1192
|
+
timeout = getattr(self.config, "timeout", DEFAULT_SEARCH_TIMEOUT)
|
|
1284
1193
|
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
1285
1194
|
|
|
1286
1195
|
retry_total = getattr(self.config, "retry_total", REQ_RETRY_TOTAL)
|
|
@@ -1293,7 +1202,7 @@ class QueryStringSearch(Search):
|
|
|
1293
1202
|
|
|
1294
1203
|
ssl_ctx = get_ssl_context(ssl_verify)
|
|
1295
1204
|
# auth if needed
|
|
1296
|
-
kwargs:
|
|
1205
|
+
kwargs: dict[str, Any] = {}
|
|
1297
1206
|
if (
|
|
1298
1207
|
getattr(self.config, "need_auth", False)
|
|
1299
1208
|
and hasattr(prep, "auth")
|
|
@@ -1348,6 +1257,9 @@ class QueryStringSearch(Search):
|
|
|
1348
1257
|
response.raise_for_status()
|
|
1349
1258
|
except requests.exceptions.Timeout as exc:
|
|
1350
1259
|
raise TimeOutError(exc, timeout=timeout) from exc
|
|
1260
|
+
except socket.timeout:
|
|
1261
|
+
err = requests.exceptions.Timeout(request=requests.Request(url=url))
|
|
1262
|
+
raise TimeOutError(err, timeout=timeout)
|
|
1351
1263
|
except (requests.RequestException, URLError) as err:
|
|
1352
1264
|
err_msg = err.readlines() if hasattr(err, "readlines") else ""
|
|
1353
1265
|
if exception_message:
|
|
@@ -1424,7 +1336,7 @@ class ODataV4Search(QueryStringSearch):
|
|
|
1424
1336
|
|
|
1425
1337
|
def do_search(
|
|
1426
1338
|
self, prep: PreparedSearch = PreparedSearch(), **kwargs: Any
|
|
1427
|
-
) ->
|
|
1339
|
+
) -> list[Any]:
|
|
1428
1340
|
"""A two step search can be performed if the metadata are not given into the search result"""
|
|
1429
1341
|
|
|
1430
1342
|
if getattr(self.config, "per_product_metadata_query", False):
|
|
@@ -1459,7 +1371,7 @@ class ODataV4Search(QueryStringSearch):
|
|
|
1459
1371
|
else:
|
|
1460
1372
|
return super(ODataV4Search, self).do_search(prep, **kwargs)
|
|
1461
1373
|
|
|
1462
|
-
def get_metadata_search_url(self, entity:
|
|
1374
|
+
def get_metadata_search_url(self, entity: dict[str, Any]) -> str:
|
|
1463
1375
|
"""Build the metadata link for the given entity"""
|
|
1464
1376
|
return "{}({})/Metadata".format(
|
|
1465
1377
|
self.config.api_endpoint.rstrip("/"), entity["id"]
|
|
@@ -1467,7 +1379,7 @@ class ODataV4Search(QueryStringSearch):
|
|
|
1467
1379
|
|
|
1468
1380
|
def normalize_results(
|
|
1469
1381
|
self, results: RawSearchResult, **kwargs: Any
|
|
1470
|
-
) ->
|
|
1382
|
+
) -> list[EOProduct]:
|
|
1471
1383
|
"""Build EOProducts from provider results
|
|
1472
1384
|
|
|
1473
1385
|
If configured, a metadata pre-mapping can be applied to simplify further metadata extraction.
|
|
@@ -1523,89 +1435,13 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1523
1435
|
|
|
1524
1436
|
"""
|
|
1525
1437
|
|
|
1526
|
-
def _get_default_end_date_from_start_date(
|
|
1527
|
-
self, start_datetime: str, product_type: str
|
|
1528
|
-
) -> str:
|
|
1529
|
-
default_end_date = self.config.products.get(product_type, {}).get(
|
|
1530
|
-
"_default_end_date", None
|
|
1531
|
-
)
|
|
1532
|
-
if default_end_date:
|
|
1533
|
-
return default_end_date
|
|
1534
|
-
try:
|
|
1535
|
-
start_date = datetime.fromisoformat(start_datetime)
|
|
1536
|
-
except ValueError:
|
|
1537
|
-
start_date = datetime.strptime(start_datetime, "%Y-%m-%dT%H:%M:%SZ")
|
|
1538
|
-
product_type_conf = self.config.products[product_type]
|
|
1539
|
-
if (
|
|
1540
|
-
"metadata_mapping" in product_type_conf
|
|
1541
|
-
and "startTimeFromAscendingNode" in product_type_conf["metadata_mapping"]
|
|
1542
|
-
):
|
|
1543
|
-
mapping = product_type_conf["metadata_mapping"][
|
|
1544
|
-
"startTimeFromAscendingNode"
|
|
1545
|
-
]
|
|
1546
|
-
if isinstance(mapping, list) and "year" in mapping[0]:
|
|
1547
|
-
# if date is mapped to year/month/(day), use end_date = start_date to avoid large requests
|
|
1548
|
-
end_date = start_date
|
|
1549
|
-
return end_date.isoformat()
|
|
1550
|
-
return self.get_product_type_cfg_value("missionEndDate", today().isoformat())
|
|
1551
|
-
|
|
1552
|
-
def _check_date_params(self, keywords: Dict[str, Any], product_type: str) -> None:
|
|
1553
|
-
"""checks if start and end date are present in the keywords and adds them if not"""
|
|
1554
|
-
if (
|
|
1555
|
-
"startTimeFromAscendingNode"
|
|
1556
|
-
and "completionTimeFromAscendingNode" in keywords
|
|
1557
|
-
):
|
|
1558
|
-
return
|
|
1559
|
-
# start time given, end time missing
|
|
1560
|
-
if "startTimeFromAscendingNode" in keywords:
|
|
1561
|
-
keywords[
|
|
1562
|
-
"completionTimeFromAscendingNode"
|
|
1563
|
-
] = self._get_default_end_date_from_start_date(
|
|
1564
|
-
keywords["startTimeFromAscendingNode"], product_type
|
|
1565
|
-
)
|
|
1566
|
-
return
|
|
1567
|
-
product_type_conf = self.config.products[product_type]
|
|
1568
|
-
if (
|
|
1569
|
-
"metadata_mapping" in product_type_conf
|
|
1570
|
-
and "startTimeFromAscendingNode" in product_type_conf["metadata_mapping"]
|
|
1571
|
-
):
|
|
1572
|
-
mapping = product_type_conf["metadata_mapping"][
|
|
1573
|
-
"startTimeFromAscendingNode"
|
|
1574
|
-
]
|
|
1575
|
-
if isinstance(mapping, list):
|
|
1576
|
-
# get time parameters (date, year, month, ...) from metadata mapping
|
|
1577
|
-
input_mapping = mapping[0].replace("{{", "").replace("}}", "")
|
|
1578
|
-
time_params = [
|
|
1579
|
-
values.split(":")[0].strip() for values in input_mapping.split(",")
|
|
1580
|
-
]
|
|
1581
|
-
time_params = [
|
|
1582
|
-
tp.replace('"', "").replace("'", "") for tp in time_params
|
|
1583
|
-
]
|
|
1584
|
-
# if startTime is not given but other time params (e.g. year/month/(day)) are given,
|
|
1585
|
-
# no default date is required
|
|
1586
|
-
in_keywords = True
|
|
1587
|
-
for tp in time_params:
|
|
1588
|
-
if tp not in keywords:
|
|
1589
|
-
in_keywords = False
|
|
1590
|
-
if not in_keywords:
|
|
1591
|
-
keywords[
|
|
1592
|
-
"startTimeFromAscendingNode"
|
|
1593
|
-
] = self.get_product_type_cfg_value(
|
|
1594
|
-
"missionStartDate", today().isoformat()
|
|
1595
|
-
)
|
|
1596
|
-
keywords[
|
|
1597
|
-
"completionTimeFromAscendingNode"
|
|
1598
|
-
] = self._get_default_end_date_from_start_date(
|
|
1599
|
-
keywords["startTimeFromAscendingNode"], product_type
|
|
1600
|
-
)
|
|
1601
|
-
|
|
1602
1438
|
def query(
|
|
1603
1439
|
self,
|
|
1604
1440
|
prep: PreparedSearch = PreparedSearch(),
|
|
1605
1441
|
**kwargs: Any,
|
|
1606
|
-
) ->
|
|
1442
|
+
) -> tuple[list[EOProduct], Optional[int]]:
|
|
1607
1443
|
"""Perform a search on an OpenSearch-like interface"""
|
|
1608
|
-
product_type = kwargs.get("productType",
|
|
1444
|
+
product_type = kwargs.get("productType", "")
|
|
1609
1445
|
count = prep.count
|
|
1610
1446
|
# remove "product_type" from search args if exists for compatibility with QueryStringSearch methods
|
|
1611
1447
|
kwargs.pop("product_type", None)
|
|
@@ -1621,7 +1457,7 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1621
1457
|
|
|
1622
1458
|
# provider product type specific conf
|
|
1623
1459
|
prep.product_type_def_params = self.get_product_type_def_params(
|
|
1624
|
-
product_type,
|
|
1460
|
+
product_type, format_variables=kwargs
|
|
1625
1461
|
)
|
|
1626
1462
|
else:
|
|
1627
1463
|
keywords = {
|
|
@@ -1635,7 +1471,7 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1635
1471
|
|
|
1636
1472
|
# provider product type specific conf
|
|
1637
1473
|
prep.product_type_def_params = self.get_product_type_def_params(
|
|
1638
|
-
product_type,
|
|
1474
|
+
product_type, format_variables=kwargs
|
|
1639
1475
|
)
|
|
1640
1476
|
|
|
1641
1477
|
# Add to the query, the queryable parameters set in the provider product type definition
|
|
@@ -1648,10 +1484,8 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1648
1484
|
and isinstance(self.config.metadata_mapping[k], list)
|
|
1649
1485
|
}
|
|
1650
1486
|
)
|
|
1651
|
-
if getattr(self.config, "dates_required", False):
|
|
1652
|
-
self._check_date_params(keywords, product_type)
|
|
1653
1487
|
|
|
1654
|
-
qp, _ = self.build_query_string(product_type,
|
|
1488
|
+
qp, _ = self.build_query_string(product_type, keywords)
|
|
1655
1489
|
|
|
1656
1490
|
for query_param, query_value in qp.items():
|
|
1657
1491
|
if (
|
|
@@ -1720,6 +1554,7 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1720
1554
|
# do not try to extract total_items from search results if count is False
|
|
1721
1555
|
del prep.total_items_nb
|
|
1722
1556
|
del prep.need_count
|
|
1557
|
+
|
|
1723
1558
|
provider_results = self.do_search(prep, **kwargs)
|
|
1724
1559
|
if count and total_items is None and hasattr(prep, "total_items_nb"):
|
|
1725
1560
|
total_items = prep.total_items_nb
|
|
@@ -1733,7 +1568,7 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1733
1568
|
|
|
1734
1569
|
def normalize_results(
|
|
1735
1570
|
self, results: RawSearchResult, **kwargs: Any
|
|
1736
|
-
) ->
|
|
1571
|
+
) -> list[EOProduct]:
|
|
1737
1572
|
"""Build EOProducts from provider results"""
|
|
1738
1573
|
normalized = super().normalize_results(results, **kwargs)
|
|
1739
1574
|
for product in normalized:
|
|
@@ -1768,12 +1603,12 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1768
1603
|
self,
|
|
1769
1604
|
prep: PreparedSearch = PreparedSearch(),
|
|
1770
1605
|
**kwargs: Any,
|
|
1771
|
-
) ->
|
|
1606
|
+
) -> tuple[list[str], Optional[int]]:
|
|
1772
1607
|
"""Adds pagination to query parameters, and auth to url"""
|
|
1773
1608
|
page = prep.page
|
|
1774
1609
|
items_per_page = prep.items_per_page
|
|
1775
1610
|
count = prep.count
|
|
1776
|
-
urls:
|
|
1611
|
+
urls: list[str] = []
|
|
1777
1612
|
total_results = 0 if count else None
|
|
1778
1613
|
|
|
1779
1614
|
if "count_endpoint" not in self.config.pagination:
|
|
@@ -1842,7 +1677,7 @@ class PostJsonSearch(QueryStringSearch):
|
|
|
1842
1677
|
raise ValidationError("Cannot request empty URL")
|
|
1843
1678
|
info_message = prep.info_message
|
|
1844
1679
|
exception_message = prep.exception_message
|
|
1845
|
-
timeout = getattr(self.config, "timeout",
|
|
1680
|
+
timeout = getattr(self.config, "timeout", DEFAULT_SEARCH_TIMEOUT)
|
|
1846
1681
|
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
1847
1682
|
try:
|
|
1848
1683
|
# auth if needed
|
|
@@ -1933,24 +1768,24 @@ class StacSearch(PostJsonSearch):
|
|
|
1933
1768
|
self.config.results_entry = results_entry
|
|
1934
1769
|
|
|
1935
1770
|
def build_query_string(
|
|
1936
|
-
self, product_type: str,
|
|
1937
|
-
) ->
|
|
1771
|
+
self, product_type: str, query_dict: dict[str, Any]
|
|
1772
|
+
) -> tuple[dict[str, Any], str]:
|
|
1938
1773
|
"""Build The query string using the search parameters"""
|
|
1939
1774
|
logger.debug("Building the query string that will be used for search")
|
|
1940
1775
|
|
|
1941
1776
|
# handle opened time intervals
|
|
1942
1777
|
if any(
|
|
1943
|
-
|
|
1944
|
-
for
|
|
1778
|
+
q in query_dict
|
|
1779
|
+
for q in ("startTimeFromAscendingNode", "completionTimeFromAscendingNode")
|
|
1945
1780
|
):
|
|
1946
|
-
|
|
1947
|
-
|
|
1781
|
+
query_dict.setdefault("startTimeFromAscendingNode", "..")
|
|
1782
|
+
query_dict.setdefault("completionTimeFromAscendingNode", "..")
|
|
1948
1783
|
|
|
1949
|
-
query_params = format_query_params(product_type, self.config,
|
|
1784
|
+
query_params = format_query_params(product_type, self.config, query_dict)
|
|
1950
1785
|
|
|
1951
1786
|
# Build the final query string, in one go without quoting it
|
|
1952
1787
|
# (some providers do not operate well with urlencoded and quoted query strings)
|
|
1953
|
-
def quote_via(x: Any, *_args, **_kwargs) -> str:
|
|
1788
|
+
def quote_via(x: Any, *_args: Any, **_kwargs: Any) -> str:
|
|
1954
1789
|
return x
|
|
1955
1790
|
|
|
1956
1791
|
return (
|
|
@@ -1960,7 +1795,7 @@ class StacSearch(PostJsonSearch):
|
|
|
1960
1795
|
|
|
1961
1796
|
def discover_queryables(
|
|
1962
1797
|
self, **kwargs: Any
|
|
1963
|
-
) -> Optional[
|
|
1798
|
+
) -> Optional[dict[str, Annotated[Any, FieldInfo]]]:
|
|
1964
1799
|
"""Fetch queryables list from provider using `discover_queryables` conf
|
|
1965
1800
|
|
|
1966
1801
|
:param kwargs: additional filters for queryables (`productType` and other search
|
|
@@ -2007,7 +1842,8 @@ class StacSearch(PostJsonSearch):
|
|
|
2007
1842
|
return None
|
|
2008
1843
|
|
|
2009
1844
|
fetch_url = unparsed_fetch_url.format(
|
|
2010
|
-
provider_product_type=provider_product_type,
|
|
1845
|
+
provider_product_type=provider_product_type,
|
|
1846
|
+
**self.config.__dict__,
|
|
2011
1847
|
)
|
|
2012
1848
|
auth = (
|
|
2013
1849
|
self.auth
|
|
@@ -2024,7 +1860,8 @@ class StacSearch(PostJsonSearch):
|
|
|
2024
1860
|
"{} {} instance:".format(self.provider, self.__class__.__name__),
|
|
2025
1861
|
),
|
|
2026
1862
|
)
|
|
2027
|
-
except (RequestError, KeyError, AttributeError):
|
|
1863
|
+
except (RequestError, KeyError, AttributeError) as e:
|
|
1864
|
+
logger.warning("failure in queryables discovery: %s", e)
|
|
2028
1865
|
return None
|
|
2029
1866
|
else:
|
|
2030
1867
|
json_queryables = dict()
|
|
@@ -2056,7 +1893,7 @@ class StacSearch(PostJsonSearch):
|
|
|
2056
1893
|
return None
|
|
2057
1894
|
|
|
2058
1895
|
# convert json results to pydantic model fields
|
|
2059
|
-
field_definitions:
|
|
1896
|
+
field_definitions: dict[str, Any] = dict()
|
|
2060
1897
|
for json_param, json_mtd in json_queryables.items():
|
|
2061
1898
|
param = (
|
|
2062
1899
|
get_queryable_from_provider(
|
|
@@ -2072,6 +1909,10 @@ class StacSearch(PostJsonSearch):
|
|
|
2072
1909
|
field_definitions[param] = get_args(annotated_def)
|
|
2073
1910
|
|
|
2074
1911
|
python_queryables = create_model("m", **field_definitions).model_fields
|
|
1912
|
+
# replace geometry by geom
|
|
1913
|
+
geom_queryable = python_queryables.pop("geometry", None)
|
|
1914
|
+
if geom_queryable:
|
|
1915
|
+
python_queryables["geom"] = geom_queryable
|
|
2075
1916
|
|
|
2076
1917
|
return model_fields_to_annotated(python_queryables)
|
|
2077
1918
|
|
|
@@ -2085,7 +1926,7 @@ class PostJsonSearchWithStacQueryables(StacSearch, PostJsonSearch):
|
|
|
2085
1926
|
PostJsonSearch.__init__(self, provider, config)
|
|
2086
1927
|
|
|
2087
1928
|
def build_query_string(
|
|
2088
|
-
self, product_type: str,
|
|
2089
|
-
) ->
|
|
1929
|
+
self, product_type: str, query_dict: dict[str, Any]
|
|
1930
|
+
) -> tuple[dict[str, Any], str]:
|
|
2090
1931
|
"""Build The query string using the search parameters"""
|
|
2091
|
-
return PostJsonSearch.build_query_string(self, product_type,
|
|
1932
|
+
return PostJsonSearch.build_query_string(self, product_type, query_dict)
|