eodag 3.10.1__py3-none-any.whl → 4.0.0a2__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/__init__.py +6 -1
- eodag/api/collection.py +353 -0
- eodag/api/core.py +606 -641
- eodag/api/product/__init__.py +3 -3
- eodag/api/product/_product.py +74 -56
- eodag/api/product/drivers/__init__.py +4 -46
- eodag/api/product/drivers/base.py +0 -28
- eodag/api/product/metadata_mapping.py +178 -216
- eodag/api/search_result.py +156 -15
- eodag/cli.py +83 -403
- eodag/config.py +81 -51
- eodag/plugins/apis/base.py +2 -2
- eodag/plugins/apis/ecmwf.py +36 -25
- eodag/plugins/apis/usgs.py +55 -40
- eodag/plugins/authentication/base.py +1 -3
- eodag/plugins/crunch/filter_date.py +3 -3
- eodag/plugins/crunch/filter_latest_intersect.py +2 -2
- eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
- eodag/plugins/download/aws.py +46 -42
- eodag/plugins/download/base.py +13 -14
- eodag/plugins/download/http.py +65 -65
- eodag/plugins/manager.py +28 -29
- eodag/plugins/search/__init__.py +6 -4
- eodag/plugins/search/base.py +131 -80
- eodag/plugins/search/build_search_result.py +245 -173
- eodag/plugins/search/cop_marine.py +87 -56
- eodag/plugins/search/csw.py +47 -37
- eodag/plugins/search/qssearch.py +653 -429
- eodag/plugins/search/stac_list_assets.py +1 -1
- eodag/plugins/search/static_stac_search.py +43 -44
- eodag/resources/{product_types.yml → collections.yml} +2594 -2453
- eodag/resources/ext_collections.json +1 -1
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/providers.yml +2706 -2733
- eodag/resources/stac_provider.yml +50 -92
- eodag/resources/user_conf_template.yml +9 -0
- eodag/types/__init__.py +2 -0
- eodag/types/queryables.py +70 -91
- eodag/types/search_args.py +1 -1
- eodag/utils/__init__.py +97 -21
- eodag/utils/dates.py +0 -12
- eodag/utils/exceptions.py +6 -6
- eodag/utils/free_text_search.py +3 -3
- eodag/utils/repr.py +2 -0
- {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/METADATA +13 -99
- eodag-4.0.0a2.dist-info/RECORD +93 -0
- {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/entry_points.txt +0 -4
- eodag/plugins/authentication/oauth.py +0 -60
- eodag/plugins/download/creodias_s3.py +0 -71
- eodag/plugins/download/s3rest.py +0 -351
- eodag/plugins/search/data_request_search.py +0 -565
- eodag/resources/stac.yml +0 -294
- eodag/resources/stac_api.yml +0 -2105
- eodag/rest/__init__.py +0 -24
- eodag/rest/cache.py +0 -70
- eodag/rest/config.py +0 -67
- eodag/rest/constants.py +0 -26
- eodag/rest/core.py +0 -764
- eodag/rest/errors.py +0 -210
- eodag/rest/server.py +0 -604
- eodag/rest/server.wsgi +0 -6
- eodag/rest/stac.py +0 -1032
- eodag/rest/templates/README +0 -1
- eodag/rest/types/__init__.py +0 -18
- eodag/rest/types/collections_search.py +0 -44
- eodag/rest/types/eodag_search.py +0 -386
- eodag/rest/types/queryables.py +0 -174
- eodag/rest/types/stac_search.py +0 -272
- eodag/rest/utils/__init__.py +0 -207
- eodag/rest/utils/cql_evaluate.py +0 -119
- eodag/rest/utils/rfc3339.py +0 -64
- eodag-3.10.1.dist-info/RECORD +0 -116
- {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/WHEEL +0 -0
- {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/licenses/LICENSE +0 -0
- {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/top_level.txt +0 -0
|
@@ -34,11 +34,12 @@ from dateutil.utils import today
|
|
|
34
34
|
|
|
35
35
|
from eodag import EOProduct
|
|
36
36
|
from eodag.api.product import AssetsDict
|
|
37
|
+
from eodag.api.search_result import SearchResult
|
|
37
38
|
from eodag.config import PluginConfig
|
|
38
39
|
from eodag.plugins.search import PreparedSearch
|
|
39
40
|
from eodag.plugins.search.static_stac_search import StaticStacSearch
|
|
40
41
|
from eodag.utils import get_bucket_name_and_prefix, get_geometry_from_various
|
|
41
|
-
from eodag.utils.exceptions import RequestError,
|
|
42
|
+
from eodag.utils.exceptions import RequestError, UnsupportedCollection, ValidationError
|
|
42
43
|
|
|
43
44
|
if TYPE_CHECKING:
|
|
44
45
|
from mypy_boto3_s3 import S3Client
|
|
@@ -112,7 +113,7 @@ def _check_int_values_properties(properties: dict[str, Any]):
|
|
|
112
113
|
class CopMarineSearch(StaticStacSearch):
|
|
113
114
|
"""class that implements search for the Copernicus Marine provider
|
|
114
115
|
|
|
115
|
-
It calls :meth:`~eodag.plugins.search.static_stac_search.StaticStacSearch.
|
|
116
|
+
It calls :meth:`~eodag.plugins.search.static_stac_search.StaticStacSearch.discover_collections`
|
|
116
117
|
inherited from :class:`~eodag.plugins.search.static_stac_search.StaticStacSearch`
|
|
117
118
|
but for the actual search a special method which fetches the urls of the available products from an S3 storage and
|
|
118
119
|
filters them has been written.
|
|
@@ -132,40 +133,40 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
132
133
|
# reset to original metadata mapping from config (changed in super class init)
|
|
133
134
|
self.config.metadata_mapping = original_metadata_mapping
|
|
134
135
|
|
|
135
|
-
def
|
|
136
|
-
self,
|
|
136
|
+
def _get_collection_info(
|
|
137
|
+
self, collection: str
|
|
137
138
|
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
|
|
138
|
-
"""Fetch
|
|
139
|
+
"""Fetch collection and associated datasets info"""
|
|
139
140
|
|
|
140
|
-
fetch_url = cast(str, self.config.
|
|
141
|
+
fetch_url = cast(str, self.config.discover_collections["fetch_url"]).format(
|
|
141
142
|
**self.config.__dict__
|
|
142
143
|
)
|
|
143
144
|
|
|
144
|
-
logger.debug("fetch data for collection %s",
|
|
145
|
-
|
|
146
|
-
"
|
|
145
|
+
logger.debug("fetch data for collection %s", collection)
|
|
146
|
+
provider_collection = self.config.products.get(collection, {}).get(
|
|
147
|
+
"_collection", None
|
|
147
148
|
)
|
|
148
|
-
if not
|
|
149
|
-
|
|
149
|
+
if not provider_collection:
|
|
150
|
+
provider_collection = collection
|
|
150
151
|
collection_url = (
|
|
151
|
-
fetch_url.replace("catalog.stac.json",
|
|
152
|
+
fetch_url.replace("catalog.stac.json", provider_collection)
|
|
152
153
|
+ "/product.stac.json"
|
|
153
154
|
)
|
|
154
155
|
try:
|
|
155
156
|
collection_data = requests.get(collection_url).json()
|
|
156
157
|
except requests.RequestException as exc:
|
|
157
158
|
if exc.errno == 404:
|
|
158
|
-
logger.error("product %s not found",
|
|
159
|
-
raise
|
|
160
|
-
logger.error("data for product %s could not be fetched",
|
|
159
|
+
logger.error("product %s not found", collection)
|
|
160
|
+
raise UnsupportedCollection(collection)
|
|
161
|
+
logger.error("data for product %s could not be fetched", collection)
|
|
161
162
|
raise RequestError.from_error(
|
|
162
|
-
exc, f"data for product {
|
|
163
|
+
exc, f"data for product {collection} could not be fetched"
|
|
163
164
|
) from exc
|
|
164
165
|
|
|
165
166
|
datasets = []
|
|
166
167
|
for link in [li for li in collection_data["links"] if li["rel"] == "item"]:
|
|
167
168
|
dataset_url = (
|
|
168
|
-
fetch_url.replace("catalog.stac.json",
|
|
169
|
+
fetch_url.replace("catalog.stac.json", provider_collection)
|
|
169
170
|
+ "/"
|
|
170
171
|
+ link["href"]
|
|
171
172
|
)
|
|
@@ -182,7 +183,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
182
183
|
collection_objects: ListObjectsOutputTypeDef,
|
|
183
184
|
product_id: str,
|
|
184
185
|
s3_url: str,
|
|
185
|
-
|
|
186
|
+
collection: str,
|
|
186
187
|
dataset_item: dict[str, Any],
|
|
187
188
|
collection_dict: dict[str, Any],
|
|
188
189
|
):
|
|
@@ -194,7 +195,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
194
195
|
for obj in collection_objects["Contents"]:
|
|
195
196
|
if product_id in obj["Key"]:
|
|
196
197
|
return self._create_product(
|
|
197
|
-
|
|
198
|
+
collection,
|
|
198
199
|
obj["Key"],
|
|
199
200
|
s3_url,
|
|
200
201
|
dataset_item,
|
|
@@ -205,7 +206,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
205
206
|
|
|
206
207
|
def _create_product(
|
|
207
208
|
self,
|
|
208
|
-
|
|
209
|
+
collection: str,
|
|
209
210
|
item_key: str,
|
|
210
211
|
s3_url: str,
|
|
211
212
|
dataset_item: dict[str, Any],
|
|
@@ -220,21 +221,21 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
220
221
|
download_url = s3_url + "/" + item_key
|
|
221
222
|
geometry = (
|
|
222
223
|
get_geometry_from_various(**dataset_item)
|
|
223
|
-
or self.config.metadata_mapping["
|
|
224
|
+
or self.config.metadata_mapping["eodag:default_geometry"]
|
|
224
225
|
)
|
|
225
226
|
properties = {
|
|
226
227
|
"id": item_id,
|
|
227
228
|
"title": item_id,
|
|
228
229
|
"geometry": geometry,
|
|
229
|
-
"
|
|
230
|
+
"eodag:download_link": download_url,
|
|
230
231
|
"dataset": dataset_item["id"],
|
|
231
232
|
}
|
|
232
233
|
if use_dataset_dates:
|
|
233
234
|
dates = _get_dates_from_dataset_data(dataset_item)
|
|
234
235
|
if not dates:
|
|
235
236
|
return None
|
|
236
|
-
properties["
|
|
237
|
-
properties["
|
|
237
|
+
properties["start_datetime"] = dates["start"]
|
|
238
|
+
properties["end_datetime"] = dates["end"]
|
|
238
239
|
else:
|
|
239
240
|
item_dates = re.findall(r"(\d{4})(0[1-9]|1[0-2])([0-3]\d)", item_id)
|
|
240
241
|
if not item_dates:
|
|
@@ -247,12 +248,10 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
247
248
|
item_end = _get_date_from_yyyymmdd(item_dates[1], item_key)
|
|
248
249
|
else: # only date and created_at timestamps
|
|
249
250
|
item_end = item_start
|
|
250
|
-
properties["
|
|
251
|
+
properties["start_datetime"] = item_start.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
252
|
+
properties["end_datetime"] = (item_end or item_start).strftime(
|
|
251
253
|
"%Y-%m-%dT%H:%M:%SZ"
|
|
252
254
|
)
|
|
253
|
-
properties["completionTimeFromAscendingNode"] = (
|
|
254
|
-
item_end or item_start
|
|
255
|
-
).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
256
255
|
|
|
257
256
|
for key, value in collection_dict["properties"].items():
|
|
258
257
|
if key not in ["id", "title", "start_datetime", "end_datetime", "datetime"]:
|
|
@@ -261,7 +260,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
261
260
|
if key not in ["id", "title", "start_datetime", "end_datetime", "datetime"]:
|
|
262
261
|
properties[key] = value
|
|
263
262
|
|
|
264
|
-
code_mapping = self.config.products.get(
|
|
263
|
+
code_mapping = self.config.products.get(collection, {}).get(
|
|
265
264
|
"code_mapping", None
|
|
266
265
|
)
|
|
267
266
|
if code_mapping:
|
|
@@ -277,9 +276,11 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
277
276
|
|
|
278
277
|
_check_int_values_properties(properties)
|
|
279
278
|
|
|
280
|
-
properties["thumbnail"] = collection_dict["assets"]["thumbnail"]["href"]
|
|
279
|
+
properties["eodag:thumbnail"] = collection_dict["assets"]["thumbnail"]["href"]
|
|
281
280
|
if "omiFigure" in collection_dict["assets"]:
|
|
282
|
-
properties["quicklook"] = collection_dict["assets"]["omiFigure"][
|
|
281
|
+
properties["eodag:quicklook"] = collection_dict["assets"]["omiFigure"][
|
|
282
|
+
"href"
|
|
283
|
+
]
|
|
283
284
|
assets = {
|
|
284
285
|
"native": {
|
|
285
286
|
"title": "native",
|
|
@@ -289,10 +290,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
289
290
|
}
|
|
290
291
|
additional_assets = self.get_assets_from_mapping(dataset_item)
|
|
291
292
|
assets.update(additional_assets)
|
|
292
|
-
product = EOProduct(self.provider, properties,
|
|
293
|
-
# use product_type_config as default properties
|
|
294
|
-
product_type_config = getattr(self.config, "product_type_config", {})
|
|
295
|
-
product.properties = dict(product_type_config, **product.properties)
|
|
293
|
+
product = EOProduct(self.provider, properties, collection=collection)
|
|
296
294
|
product.assets = AssetsDict(product, assets)
|
|
297
295
|
return product
|
|
298
296
|
|
|
@@ -300,29 +298,39 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
300
298
|
self,
|
|
301
299
|
prep: PreparedSearch = PreparedSearch(),
|
|
302
300
|
**kwargs: Any,
|
|
303
|
-
) ->
|
|
301
|
+
) -> SearchResult:
|
|
304
302
|
"""
|
|
305
303
|
Implementation of search for the Copernicus Marine provider
|
|
306
304
|
:param prep: object containing search parameterds
|
|
307
305
|
:param kwargs: additional search arguments
|
|
308
306
|
:returns: list of products and total number of products
|
|
309
307
|
"""
|
|
310
|
-
page = prep.page
|
|
311
308
|
items_per_page = prep.items_per_page
|
|
309
|
+
token_value = getattr(prep, "next_page_token") or prep.page
|
|
312
310
|
|
|
313
311
|
# only return 1 page if pagination is disabled
|
|
314
|
-
if
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
312
|
+
if (
|
|
313
|
+
token_value is None
|
|
314
|
+
or items_per_page is None
|
|
315
|
+
or int(token_value) > 1
|
|
316
|
+
and items_per_page <= 0
|
|
317
|
+
):
|
|
318
|
+
result = SearchResult([])
|
|
319
|
+
if prep.count:
|
|
320
|
+
result.number_matched = 0
|
|
321
|
+
return result
|
|
322
|
+
|
|
323
|
+
token = int(token_value)
|
|
324
|
+
|
|
325
|
+
collection = kwargs.get("collection", prep.collection)
|
|
326
|
+
if not collection:
|
|
319
327
|
raise ValidationError(
|
|
320
|
-
"parameter
|
|
328
|
+
"parameter collection is required for search with cop_marine provider"
|
|
321
329
|
)
|
|
322
|
-
collection_dict, datasets_items_list = self.
|
|
330
|
+
collection_dict, datasets_items_list = self._get_collection_info(collection)
|
|
323
331
|
geometry = kwargs.pop("geometry", None)
|
|
324
332
|
products: list[EOProduct] = []
|
|
325
|
-
start_index = items_per_page * (
|
|
333
|
+
start_index = items_per_page * (token - 1) + 1
|
|
326
334
|
num_total = 0
|
|
327
335
|
for i, dataset_item in enumerate(datasets_items_list):
|
|
328
336
|
# Filter by geometry
|
|
@@ -334,16 +342,16 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
334
342
|
logger.debug("searching data for dataset %s", dataset_item["id"])
|
|
335
343
|
|
|
336
344
|
# date bounds
|
|
337
|
-
if "
|
|
338
|
-
start_date = isoparse(kwargs["
|
|
345
|
+
if "start_datetime" in kwargs:
|
|
346
|
+
start_date = isoparse(kwargs["start_datetime"])
|
|
339
347
|
elif "start_datetime" in dataset_item["properties"]:
|
|
340
348
|
start_date = isoparse(dataset_item["properties"]["start_datetime"])
|
|
341
349
|
else:
|
|
342
350
|
start_date = isoparse(dataset_item["properties"]["datetime"])
|
|
343
351
|
if not start_date.tzinfo:
|
|
344
352
|
start_date = start_date.replace(tzinfo=tzutc())
|
|
345
|
-
if "
|
|
346
|
-
end_date = isoparse(kwargs["
|
|
353
|
+
if "end_datetime" in kwargs:
|
|
354
|
+
end_date = isoparse(kwargs["end_datetime"])
|
|
347
355
|
elif "end_datetime" in dataset_item["properties"]:
|
|
348
356
|
end_date = isoparse(dataset_item["properties"]["end_datetime"])
|
|
349
357
|
else:
|
|
@@ -355,7 +363,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
355
363
|
s3_url = dataset_item["assets"]["native"]["href"]
|
|
356
364
|
except KeyError as e:
|
|
357
365
|
logger.warning(
|
|
358
|
-
f"Unable to extract info from {
|
|
366
|
+
f"Unable to extract info from {collection} item #{i}: {str(e)}"
|
|
359
367
|
)
|
|
360
368
|
continue
|
|
361
369
|
|
|
@@ -374,7 +382,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
374
382
|
continue
|
|
375
383
|
if len(products) < items_per_page or items_per_page < 0:
|
|
376
384
|
product = self._create_product(
|
|
377
|
-
|
|
385
|
+
collection,
|
|
378
386
|
collection_path,
|
|
379
387
|
endpoint_url + "/" + bucket,
|
|
380
388
|
dataset_item,
|
|
@@ -403,7 +411,10 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
403
411
|
)
|
|
404
412
|
if "Contents" not in s3_objects:
|
|
405
413
|
if len(products) == 0 and i == len(datasets_items_list) - 1:
|
|
406
|
-
|
|
414
|
+
result = SearchResult([])
|
|
415
|
+
if prep.count:
|
|
416
|
+
result.number_matched = 0
|
|
417
|
+
return result
|
|
407
418
|
else:
|
|
408
419
|
break
|
|
409
420
|
|
|
@@ -412,12 +423,16 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
412
423
|
s3_objects,
|
|
413
424
|
kwargs["id"],
|
|
414
425
|
endpoint_url + "/" + bucket,
|
|
415
|
-
|
|
426
|
+
collection,
|
|
416
427
|
dataset_item,
|
|
417
428
|
collection_dict,
|
|
418
429
|
)
|
|
419
430
|
if product:
|
|
420
|
-
|
|
431
|
+
formated_result = SearchResult(
|
|
432
|
+
[product],
|
|
433
|
+
1,
|
|
434
|
+
)
|
|
435
|
+
return formated_result
|
|
421
436
|
current_object = s3_objects["Contents"][-1]["Key"]
|
|
422
437
|
continue
|
|
423
438
|
|
|
@@ -476,7 +491,7 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
476
491
|
continue
|
|
477
492
|
if len(products) < items_per_page or items_per_page < 0:
|
|
478
493
|
product = self._create_product(
|
|
479
|
-
|
|
494
|
+
collection,
|
|
480
495
|
item_key,
|
|
481
496
|
endpoint_url + "/" + bucket,
|
|
482
497
|
dataset_item,
|
|
@@ -487,4 +502,20 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
487
502
|
products.append(product)
|
|
488
503
|
current_object = item_key
|
|
489
504
|
|
|
490
|
-
|
|
505
|
+
search_params = (
|
|
506
|
+
kwargs
|
|
507
|
+
| {"items_per_page": prep.items_per_page}
|
|
508
|
+
| {"collection": collection}
|
|
509
|
+
| {"provider": self.provider}
|
|
510
|
+
| {"geometry": geometry}
|
|
511
|
+
if geometry
|
|
512
|
+
else {}
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
formated_result = SearchResult(
|
|
516
|
+
products,
|
|
517
|
+
num_total,
|
|
518
|
+
search_params=search_params,
|
|
519
|
+
next_page_token=str(start_index + 1),
|
|
520
|
+
)
|
|
521
|
+
return formated_result
|
eodag/plugins/search/csw.py
CHANGED
|
@@ -35,6 +35,7 @@ from shapely import geometry, wkt
|
|
|
35
35
|
|
|
36
36
|
from eodag.api.product import EOProduct
|
|
37
37
|
from eodag.api.product.metadata_mapping import properties_from_xml
|
|
38
|
+
from eodag.api.search_result import SearchResult
|
|
38
39
|
from eodag.plugins.search import PreparedSearch
|
|
39
40
|
from eodag.plugins.search.base import Search
|
|
40
41
|
from eodag.utils import DEFAULT_PROJ
|
|
@@ -62,7 +63,7 @@ class CSWSearch(Search):
|
|
|
62
63
|
* :attr:`~eodag.config.PluginConfig.version` (``str``): OGC Catalogue Service version; default: ``2.0.2``
|
|
63
64
|
* :attr:`~eodag.config.PluginConfig.search_definition` (``dict[str, Any]``) (**mandatory**):
|
|
64
65
|
|
|
65
|
-
* **
|
|
66
|
+
* **collection_tags** (``list[dict[str, Any]``): dict of collection tags
|
|
66
67
|
* **resource_location_filter** (``str``): regex string
|
|
67
68
|
* **date_tags** (``dict[str, Any]``): tags for start and end
|
|
68
69
|
|
|
@@ -74,15 +75,15 @@ class CSWSearch(Search):
|
|
|
74
75
|
specification of Python string formatting, with a special behaviour added to it. For example,
|
|
75
76
|
an entry in the metadata mapping of this kind::
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
- 'f=acquisition.endViewingDate:lte:{
|
|
78
|
+
end_datetime:
|
|
79
|
+
- 'f=acquisition.endViewingDate:lte:{end_datetime#timestamp}'
|
|
79
80
|
- '$.properties.acquisition.endViewingDate'
|
|
80
81
|
|
|
81
82
|
means that the search url will have a query string parameter named ``f`` with a value of
|
|
82
83
|
``acquisition.endViewingDate:lte:1543922280.0`` if the search was done with the value
|
|
83
|
-
of ``
|
|
84
|
-
``{
|
|
85
|
-
of ``
|
|
84
|
+
of ``end_datetime`` being ``2018-12-04T12:18:00``. What happened is that
|
|
85
|
+
``{end_datetime#timestamp}`` was replaced with the timestamp of the value
|
|
86
|
+
of ``end_datetime``. This example shows all there is to know about the
|
|
86
87
|
semantics of the query string formatting introduced by this plugin: any eodag search parameter
|
|
87
88
|
can be referenced in the query string with an additional optional conversion function that
|
|
88
89
|
is separated from it by a ``#`` (see :func:`~eodag.api.product.metadata_mapping.format_metadata` for further
|
|
@@ -107,11 +108,14 @@ class CSWSearch(Search):
|
|
|
107
108
|
self,
|
|
108
109
|
prep: PreparedSearch = PreparedSearch(),
|
|
109
110
|
**kwargs: Any,
|
|
110
|
-
) ->
|
|
111
|
+
) -> SearchResult:
|
|
111
112
|
"""Perform a search on a OGC/CSW-like interface"""
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
113
|
+
collection = kwargs.get("collection")
|
|
114
|
+
if collection is None:
|
|
115
|
+
result = SearchResult([])
|
|
116
|
+
if prep.count:
|
|
117
|
+
result.number_matched = 0
|
|
118
|
+
return result
|
|
115
119
|
auth = kwargs.get("auth")
|
|
116
120
|
if auth:
|
|
117
121
|
self.__init_catalog(**getattr(auth.config, "credentials", {}))
|
|
@@ -119,16 +123,16 @@ class CSWSearch(Search):
|
|
|
119
123
|
self.__init_catalog()
|
|
120
124
|
results: list[EOProduct] = []
|
|
121
125
|
if self.catalog:
|
|
122
|
-
|
|
123
|
-
for
|
|
124
|
-
|
|
126
|
+
provider_collection = self.config.products[collection]["collection"]
|
|
127
|
+
for collection_def in self.config.search_definition["collection_tags"]:
|
|
128
|
+
collection_search_tag = collection_def["name"]
|
|
125
129
|
logger.debug(
|
|
126
|
-
"Querying <%s> tag for
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
"Querying <%s> tag for collection %s",
|
|
131
|
+
collection_search_tag,
|
|
132
|
+
provider_collection,
|
|
129
133
|
)
|
|
130
134
|
constraints = self.__convert_query_params(
|
|
131
|
-
|
|
135
|
+
collection_def, provider_collection, kwargs
|
|
132
136
|
)
|
|
133
137
|
with patch_owslib_requests(verify=True):
|
|
134
138
|
try:
|
|
@@ -139,25 +143,31 @@ class CSWSearch(Search):
|
|
|
139
143
|
import traceback as tb
|
|
140
144
|
|
|
141
145
|
logger.warning(
|
|
142
|
-
"Failed to query %s for
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
"Failed to query %s for collection %s : %s",
|
|
147
|
+
collection_search_tag,
|
|
148
|
+
collection,
|
|
145
149
|
tb.format_exc(),
|
|
146
150
|
)
|
|
147
151
|
continue
|
|
148
152
|
partial_results = [
|
|
149
|
-
self.__build_product(record,
|
|
153
|
+
self.__build_product(record, collection, **kwargs)
|
|
150
154
|
for record in self.catalog.records.values()
|
|
151
155
|
]
|
|
152
156
|
logger.info(
|
|
153
157
|
"Found %s results querying %s",
|
|
154
158
|
len(partial_results),
|
|
155
|
-
|
|
159
|
+
collection_search_tag,
|
|
156
160
|
)
|
|
157
161
|
results.extend(partial_results)
|
|
158
162
|
logger.info("Found %s overall results", len(results))
|
|
159
163
|
total_results = len(results) if prep.count else None
|
|
160
|
-
|
|
164
|
+
if not prep.count and "number_matched" in kwargs:
|
|
165
|
+
total_results = kwargs["number_matched"]
|
|
166
|
+
formated_result = SearchResult(
|
|
167
|
+
results,
|
|
168
|
+
total_results,
|
|
169
|
+
)
|
|
170
|
+
return formated_result
|
|
161
171
|
|
|
162
172
|
def __init_catalog(
|
|
163
173
|
self, username: Optional[str] = None, password: Optional[str] = None
|
|
@@ -182,7 +192,7 @@ class CSWSearch(Search):
|
|
|
182
192
|
e,
|
|
183
193
|
)
|
|
184
194
|
|
|
185
|
-
def __build_product(self, rec: Any,
|
|
195
|
+
def __build_product(self, rec: Any, collection: str, **kwargs: Any) -> EOProduct:
|
|
186
196
|
"""Enable search results to be handled by http download plugin"""
|
|
187
197
|
download_url = ""
|
|
188
198
|
resource_filter = re.compile(
|
|
@@ -217,11 +227,11 @@ class CSWSearch(Search):
|
|
|
217
227
|
else:
|
|
218
228
|
properties["geometry"] = wkt.loads(properties["geometry"])
|
|
219
229
|
return EOProduct(
|
|
220
|
-
|
|
230
|
+
collection,
|
|
221
231
|
self.provider,
|
|
222
232
|
# TODO: EOProduct has no more *args in its __init__ (search_args attribute removed)
|
|
223
233
|
# Not sure why download_url was here in the first place, needs to be updated,
|
|
224
|
-
# possibly by having instead '
|
|
234
|
+
# possibly by having instead 'eodag:download_link' in the properties
|
|
225
235
|
# download_url,
|
|
226
236
|
properties,
|
|
227
237
|
searched_bbox=kwargs.get("footprints"),
|
|
@@ -229,26 +239,26 @@ class CSWSearch(Search):
|
|
|
229
239
|
|
|
230
240
|
def __convert_query_params(
|
|
231
241
|
self,
|
|
232
|
-
|
|
233
|
-
|
|
242
|
+
collection_def: dict[str, Any],
|
|
243
|
+
collection: str,
|
|
234
244
|
params: dict[str, Any],
|
|
235
245
|
) -> Union[list[OgcExpression], list[list[OgcExpression]]]:
|
|
236
246
|
"""Translates eodag search to CSW constraints using owslib constraint classes"""
|
|
237
247
|
constraints: list[OgcExpression] = []
|
|
238
248
|
# How the match should be performed (fuzzy, prefix, postfix or exact).
|
|
239
249
|
# defaults to fuzzy
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
250
|
+
col_tag, matching = (
|
|
251
|
+
collection_def["name"],
|
|
252
|
+
collection_def.get("matching", "fuzzy"),
|
|
243
253
|
)
|
|
244
254
|
if matching == "prefix":
|
|
245
|
-
constraints.append(PropertyIsLike(
|
|
255
|
+
constraints.append(PropertyIsLike(col_tag, "{}%".format(collection)))
|
|
246
256
|
elif matching == "postfix":
|
|
247
|
-
constraints.append(PropertyIsLike(
|
|
257
|
+
constraints.append(PropertyIsLike(col_tag, "%{}".format(collection)))
|
|
248
258
|
elif matching == "exact":
|
|
249
|
-
constraints.append(PropertyIsEqualTo(
|
|
259
|
+
constraints.append(PropertyIsEqualTo(col_tag, collection))
|
|
250
260
|
else: # unknown matching is considered to be equal to 'fuzzy'
|
|
251
|
-
constraints.append(PropertyIsLike(
|
|
261
|
+
constraints.append(PropertyIsLike(col_tag, "%{}%".format(collection)))
|
|
252
262
|
|
|
253
263
|
# `footprint`
|
|
254
264
|
fp = params.get("geometry")
|
|
@@ -259,8 +269,8 @@ class CSWSearch(Search):
|
|
|
259
269
|
|
|
260
270
|
# dates
|
|
261
271
|
start, end = (
|
|
262
|
-
params.get("
|
|
263
|
-
params.get("
|
|
272
|
+
params.get("start_datetime"),
|
|
273
|
+
params.get("end_datetime"),
|
|
264
274
|
)
|
|
265
275
|
if start and "date_tags" in self.config.search_definition:
|
|
266
276
|
constraints.append(
|