eodag 3.9.1__py3-none-any.whl → 4.0.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. eodag/api/core.py +378 -419
  2. eodag/api/product/__init__.py +3 -3
  3. eodag/api/product/_product.py +68 -40
  4. eodag/api/product/drivers/__init__.py +3 -5
  5. eodag/api/product/drivers/base.py +1 -18
  6. eodag/api/product/metadata_mapping.py +151 -215
  7. eodag/api/search_result.py +13 -7
  8. eodag/cli.py +72 -395
  9. eodag/config.py +46 -50
  10. eodag/plugins/apis/base.py +2 -2
  11. eodag/plugins/apis/ecmwf.py +20 -21
  12. eodag/plugins/apis/usgs.py +37 -33
  13. eodag/plugins/authentication/aws_auth.py +36 -1
  14. eodag/plugins/authentication/base.py +18 -3
  15. eodag/plugins/authentication/sas_auth.py +15 -0
  16. eodag/plugins/crunch/filter_date.py +3 -3
  17. eodag/plugins/crunch/filter_latest_intersect.py +2 -2
  18. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  19. eodag/plugins/download/aws.py +45 -41
  20. eodag/plugins/download/base.py +13 -14
  21. eodag/plugins/download/http.py +65 -65
  22. eodag/plugins/manager.py +28 -29
  23. eodag/plugins/search/__init__.py +3 -4
  24. eodag/plugins/search/base.py +128 -77
  25. eodag/plugins/search/build_search_result.py +105 -107
  26. eodag/plugins/search/cop_marine.py +44 -47
  27. eodag/plugins/search/csw.py +33 -33
  28. eodag/plugins/search/qssearch.py +335 -354
  29. eodag/plugins/search/stac_list_assets.py +1 -1
  30. eodag/plugins/search/static_stac_search.py +31 -31
  31. eodag/resources/{product_types.yml → collections.yml} +2353 -2429
  32. eodag/resources/ext_collections.json +1 -0
  33. eodag/resources/ext_product_types.json +1 -1
  34. eodag/resources/providers.yml +2432 -2714
  35. eodag/resources/stac_provider.yml +46 -90
  36. eodag/types/queryables.py +55 -91
  37. eodag/types/search_args.py +1 -1
  38. eodag/utils/__init__.py +94 -21
  39. eodag/utils/exceptions.py +6 -6
  40. eodag/utils/free_text_search.py +3 -3
  41. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/METADATA +11 -88
  42. eodag-4.0.0a1.dist-info/RECORD +92 -0
  43. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/entry_points.txt +0 -4
  44. eodag/plugins/authentication/oauth.py +0 -60
  45. eodag/plugins/download/creodias_s3.py +0 -64
  46. eodag/plugins/download/s3rest.py +0 -351
  47. eodag/plugins/search/data_request_search.py +0 -565
  48. eodag/resources/stac.yml +0 -294
  49. eodag/resources/stac_api.yml +0 -2105
  50. eodag/rest/__init__.py +0 -24
  51. eodag/rest/cache.py +0 -70
  52. eodag/rest/config.py +0 -67
  53. eodag/rest/constants.py +0 -26
  54. eodag/rest/core.py +0 -764
  55. eodag/rest/errors.py +0 -210
  56. eodag/rest/server.py +0 -604
  57. eodag/rest/server.wsgi +0 -6
  58. eodag/rest/stac.py +0 -1032
  59. eodag/rest/templates/README +0 -1
  60. eodag/rest/types/__init__.py +0 -18
  61. eodag/rest/types/collections_search.py +0 -44
  62. eodag/rest/types/eodag_search.py +0 -386
  63. eodag/rest/types/queryables.py +0 -174
  64. eodag/rest/types/stac_search.py +0 -272
  65. eodag/rest/utils/__init__.py +0 -207
  66. eodag/rest/utils/cql_evaluate.py +0 -119
  67. eodag/rest/utils/rfc3339.py +0 -64
  68. eodag-3.9.1.dist-info/RECORD +0 -115
  69. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/WHEEL +0 -0
  70. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
  71. {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/top_level.txt +0 -0
eodag/rest/core.py DELETED
@@ -1,764 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # Copyright 2023, CS GROUP - France, https://www.csgroup.eu/
3
- #
4
- # This file is part of EODAG project
5
- # https://www.github.com/CS-SI/EODAG
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
- from __future__ import annotations
19
-
20
- import datetime
21
- import logging
22
- import os
23
- import re
24
- import warnings
25
- from typing import TYPE_CHECKING, cast
26
- from unittest.mock import Mock
27
- from urllib.parse import urlencode
28
-
29
- import dateutil
30
- from cachetools.func import lru_cache
31
- from fastapi.responses import ORJSONResponse, StreamingResponse
32
- from pydantic import ValidationError as pydanticValidationError
33
- from requests.models import Response as RequestsResponse
34
-
35
- import eodag
36
- from eodag import EOProduct
37
- from eodag.api.product.metadata_mapping import (
38
- NOT_AVAILABLE,
39
- OFFLINE_STATUS,
40
- ONLINE_STATUS,
41
- OSEO_METADATA_MAPPING,
42
- STAGING_STATUS,
43
- )
44
- from eodag.api.search_result import SearchResult
45
- from eodag.config import load_stac_config
46
- from eodag.plugins.crunch.filter_latest_intersect import FilterLatestIntersect
47
- from eodag.plugins.crunch.filter_latest_tpl_name import FilterLatestByName
48
- from eodag.plugins.crunch.filter_overlap import FilterOverlap
49
- from eodag.rest.cache import cached
50
- from eodag.rest.constants import (
51
- CACHE_KEY_COLLECTION,
52
- CACHE_KEY_COLLECTIONS,
53
- CACHE_KEY_QUERYABLES,
54
- )
55
- from eodag.rest.errors import ResponseSearchError
56
- from eodag.rest.stac import StacCatalog, StacCollection, StacCommon, StacItem
57
- from eodag.rest.types.eodag_search import EODAGSearch
58
- from eodag.rest.types.queryables import QueryablesGetParams, StacQueryables
59
- from eodag.rest.types.stac_search import SearchPostRequest
60
- from eodag.rest.utils import (
61
- Cruncher,
62
- file_to_stream,
63
- format_pydantic_error,
64
- get_next_link,
65
- )
66
- from eodag.rest.utils.rfc3339 import rfc3339_str_to_datetime
67
- from eodag.utils import (
68
- _deprecated,
69
- deepcopy,
70
- dict_items_recursive_apply,
71
- format_dict_items,
72
- )
73
- from eodag.utils.exceptions import (
74
- MisconfiguredError,
75
- NotAvailableError,
76
- ValidationError,
77
- )
78
-
79
- if TYPE_CHECKING:
80
- from typing import Any, Optional, Union
81
-
82
- from fastapi import Request
83
- from requests.auth import AuthBase
84
- from starlette.responses import Response
85
-
86
-
87
- warnings.warn(
88
- "The module `eodag.rest.core` is deprecated since v3.9.0 and will be removed in a future version. "
89
- "The STAC server has moved to https://github.com/CS-SI/stac-fastapi-eodag",
90
- category=DeprecationWarning,
91
- stacklevel=2,
92
- )
93
-
94
- eodag_api = eodag.EODataAccessGateway()
95
-
96
- logger = logging.getLogger("eodag.rest.core")
97
-
98
- stac_config = load_stac_config()
99
-
100
- crunchers = {
101
- "filterLatestIntersect": Cruncher(FilterLatestIntersect, []),
102
- "filterLatestByName": Cruncher(FilterLatestByName, ["name_pattern"]),
103
- "filterOverlap": Cruncher(FilterOverlap, ["minimum_overlap"]),
104
- }
105
-
106
-
107
- @_deprecated(reason="No more needed with STAC API + Swagger", version="2.6.1")
108
- def get_home_page_content(base_url: str, ipp: Optional[int] = None) -> str:
109
- """Compute eodag service home page content
110
-
111
- :param base_url: The service root URL
112
- :param ipp: (optional) Items per page number
113
- """
114
- base_url = base_url.rstrip("/") + "/"
115
- content = f"""<h1>EODAG Server</h1><br />
116
- <a href='{base_url}'>root</a><br />
117
- <a href='{base_url}service-doc'>service-doc</a><br />
118
- """
119
- return content
120
-
121
-
122
- @_deprecated(
123
- reason="Function internally used by get_home_page_content, also deprecated",
124
- version="2.6.1",
125
- )
126
- def format_product_types(product_types: list[dict[str, Any]]) -> str:
127
- """Format product_types
128
-
129
- :param product_types: A list of EODAG product types as returned by the core api
130
- """
131
- result: list[str] = []
132
- for pt in product_types:
133
- result.append(f"* *__{pt['ID']}__*: {pt['abstract']}")
134
- return "\n".join(sorted(result))
135
-
136
-
137
- def search_stac_items(
138
- request: Request,
139
- search_request: SearchPostRequest,
140
- ) -> dict[str, Any]:
141
- """
142
- Search and retrieve STAC items based on the given search request.
143
-
144
- This function takes a search request, performs a search using EODAG API, and returns a
145
- dictionary of STAC items.
146
-
147
- :param request: The incoming HTTP request with state information.
148
- :param search_request: The search criteria for STAC items
149
- :returns: A dictionary containing the STAC items and related metadata.
150
-
151
- The function handles the conversion of search criteria into STAC and EODAG compatible formats, validates the input
152
- using pydantic, and constructs the appropriate URLs for querying the STAC API. It also manages pagination and the
153
- construction of the 'next' link for the response.
154
-
155
- If specific item IDs are provided, it retrieves the corresponding products. Otherwise, it performs a search based on
156
- the provided criteria and time interval overlap checks.
157
-
158
- The results are then formatted into STAC items and returned as part of the response dictionary, which includes the
159
- items themselves, total count, and the next link if applicable.
160
- """
161
-
162
- stac_args = search_request.model_dump(exclude_none=True)
163
- if search_request.start_date:
164
- stac_args["start_datetime"] = search_request.start_date
165
- if search_request.end_date:
166
- stac_args["end_datetime"] = search_request.end_date
167
- if search_request.spatial_filter:
168
- stac_args["geometry"] = search_request.spatial_filter
169
- try:
170
- eodag_args = EODAGSearch.model_validate(stac_args)
171
- except pydanticValidationError as e:
172
- raise ValidationError(format_pydantic_error(e)) from e
173
-
174
- catalog_url = re.sub("/items.*", "", request.state.url)
175
- catalog = StacCatalog(
176
- url=catalog_url.replace("/search", f"/collections/{eodag_args.productType}"),
177
- stac_config=stac_config,
178
- root=request.state.url_root,
179
- provider=eodag_args.provider,
180
- eodag_api=eodag_api,
181
- collection=eodag_args.productType, # type: ignore
182
- )
183
-
184
- # get products by ids
185
- if eodag_args.ids:
186
- results = SearchResult([])
187
- for item_id in eodag_args.ids:
188
- results.extend(
189
- eodag_api.search(
190
- id=item_id,
191
- productType=eodag_args.productType,
192
- provider=eodag_args.provider,
193
- )
194
- )
195
- results.number_matched = len(results)
196
- total = len(results)
197
-
198
- else:
199
- criteria = eodag_args.model_dump(exclude_none=True)
200
- # remove provider prefixes
201
- # quickfix for ecmwf fake extension to not impact items creation
202
- stac_extensions = list(stac_config["extensions"].keys()) + ["ecmwf"]
203
- for key in list(criteria):
204
- if ":" in key and key.split(":")[0] not in stac_extensions:
205
- new_key = key.split(":")[1]
206
- criteria[new_key] = criteria.pop(key)
207
-
208
- results = eodag_api.search(count=True, **criteria)
209
- total = results.number_matched or 0
210
-
211
- if len(results) == 0 and results.errors:
212
- raise ResponseSearchError(results.errors)
213
-
214
- if search_request.crunch:
215
- results = crunch_products(results, search_request.crunch, **criteria)
216
-
217
- for record in results:
218
- record.product_type = eodag_api.get_alias_from_product_type(record.product_type)
219
-
220
- items = StacItem(
221
- url=request.state.url,
222
- stac_config=stac_config,
223
- provider=eodag_args.provider,
224
- eodag_api=eodag_api,
225
- root=request.state.url_root,
226
- ).get_stac_items(
227
- search_results=results,
228
- total=total,
229
- next_link=get_next_link(
230
- request, search_request, total, eodag_args.items_per_page
231
- ),
232
- catalog={
233
- **catalog.data,
234
- **{"url": catalog.url, "root": catalog.root},
235
- },
236
- )
237
- return items
238
-
239
-
240
- def download_stac_item(
241
- request: Request,
242
- collection_id: str,
243
- item_id: str,
244
- provider: Optional[str] = None,
245
- asset: Optional[str] = None,
246
- **kwargs: Any,
247
- ) -> Response:
248
- """Download item
249
-
250
- :param collection_id: id of the product type
251
- :param item_id: Product ID
252
- :param provider: (optional) Chosen provider
253
- :param kwargs: additional download parameters
254
- :returns: a stream of the downloaded data (zip file)
255
- """
256
- product_type = collection_id
257
-
258
- search_results = eodag_api.search(
259
- id=item_id, productType=product_type, provider=provider, **kwargs
260
- )
261
- if len(search_results) > 0:
262
- product = cast(EOProduct, search_results[0])
263
-
264
- else:
265
- raise NotAvailableError(
266
- f"Could not find {item_id} item in {product_type} collection"
267
- + (f" for provider {provider}" if provider else "")
268
- )
269
- auth = product.downloader_auth.authenticate() if product.downloader_auth else None
270
-
271
- try:
272
- if product.properties.get("orderLink"):
273
- _order_and_update(product, auth, kwargs)
274
-
275
- download_stream = product.downloader._stream_download_dict(
276
- product, auth=auth, asset=asset, wait=-1, timeout=-1
277
- )
278
- except NotImplementedError:
279
- logger.warning(
280
- "Download streaming not supported for %s: downloading locally then delete",
281
- product.downloader,
282
- )
283
- download_stream = file_to_stream(
284
- eodag_api.download(product, extract=False, asset=asset)
285
- )
286
- except NotAvailableError:
287
- if product.properties.get("storageStatus") != ONLINE_STATUS:
288
- kwargs["orderId"] = kwargs.get("orderId") or product.properties.get(
289
- "orderId"
290
- )
291
- kwargs["provider"] = provider
292
- qs = urlencode(kwargs, doseq=True)
293
- download_link = f"{request.state.url}?{qs}"
294
- return ORJSONResponse(
295
- status_code=202,
296
- headers={"Location": download_link},
297
- content={
298
- "description": "Product is not available yet, please try again using given updated location",
299
- "status": product.properties.get("orderStatus"),
300
- "location": download_link,
301
- },
302
- )
303
- else:
304
- raise
305
-
306
- return StreamingResponse(
307
- content=download_stream.content,
308
- headers=download_stream.headers,
309
- media_type=download_stream.media_type,
310
- )
311
-
312
-
313
- def _order_and_update(
314
- product: EOProduct,
315
- auth: Union[AuthBase, dict[str, str], None],
316
- query_args: dict[str, Any],
317
- ) -> None:
318
- """Order product if needed and update given kwargs with order-status-dict"""
319
- if product.properties.get("storageStatus") != ONLINE_STATUS and hasattr(
320
- product.downloader, "order_response_process"
321
- ):
322
- # update product (including orderStatusLink) if product was previously ordered
323
- logger.debug("Use given download query arguments to parse order link")
324
- response = Mock(spec=RequestsResponse)
325
- response.status_code = 200
326
- response.json.return_value = query_args
327
- response.headers = {}
328
- product.downloader.order_response_process(response, product)
329
-
330
- if (
331
- product.properties.get("storageStatus") != ONLINE_STATUS
332
- and NOT_AVAILABLE in product.properties.get("orderStatusLink", "")
333
- and hasattr(product.downloader, "_order")
334
- ):
335
- # first order
336
- logger.debug("Order product")
337
- order_status_dict = product.downloader._order(product=product, auth=auth)
338
- query_args.update(order_status_dict or {})
339
-
340
- if (
341
- product.properties.get("storageStatus") == OFFLINE_STATUS
342
- and product.properties.get("orderStatusLink")
343
- and NOT_AVAILABLE not in product.properties.get("orderStatusLink", "")
344
- ):
345
- product.properties["storageStatus"] = STAGING_STATUS
346
-
347
- if product.properties.get("storageStatus") == STAGING_STATUS and hasattr(
348
- product.downloader, "_order_status"
349
- ):
350
- # check order status if needed
351
- logger.debug("Checking product order status")
352
- product.downloader._order_status(product=product, auth=auth)
353
-
354
- if product.properties.get("storageStatus") != ONLINE_STATUS:
355
- raise NotAvailableError("Product is not available yet")
356
-
357
-
358
- @lru_cache(maxsize=1)
359
- def get_detailled_collections_list() -> list[dict[str, Any]]:
360
- """Returns detailled collections / product_types list as a list of
361
- config dicts
362
-
363
- :returns: List of config dicts
364
- """
365
- return eodag_api.list_product_types(fetch_providers=False)
366
-
367
-
368
- async def all_collections(
369
- request: Request,
370
- provider: Optional[str] = None,
371
- q: Optional[str] = None,
372
- platform: Optional[str] = None,
373
- instrument: Optional[str] = None,
374
- constellation: Optional[str] = None,
375
- datetime: Optional[str] = None,
376
- bbox: Optional[str] = None,
377
- ) -> dict[str, Any]:
378
- """Build STAC collections
379
-
380
- :param url: Requested URL
381
- :param root: The API root
382
- :param filters: Search collections filters
383
- :param provider: (optional) Chosen provider
384
- :returns: Collections dictionary
385
- """
386
-
387
- async def _fetch() -> dict[str, Any]:
388
- stac_collection = StacCollection(
389
- url=request.state.url,
390
- stac_config=stac_config,
391
- provider=provider,
392
- eodag_api=eodag_api,
393
- root=request.state.url_root,
394
- )
395
- collections = deepcopy(stac_config["collections"])
396
- collections["collections"] = stac_collection.get_collection_list(
397
- q=q,
398
- platform=platform,
399
- instrument=instrument,
400
- constellation=constellation,
401
- datetime=datetime,
402
- bbox=bbox,
403
- )
404
-
405
- # # parse f-strings
406
- format_args = deepcopy(stac_config)
407
- format_args["collections"].update(
408
- {
409
- "url": stac_collection.url,
410
- "root": stac_collection.root,
411
- }
412
- )
413
-
414
- collections["links"] = [
415
- format_dict_items(link, **format_args) for link in collections["links"]
416
- ]
417
-
418
- collections = format_dict_items(collections, **format_args)
419
- return collections
420
-
421
- hashed_collections = hash(
422
- f"{provider}:{q}:{platform}:{instrument}:{constellation}:{datetime}:{bbox}"
423
- )
424
- cache_key = f"{CACHE_KEY_COLLECTIONS}:{hashed_collections}"
425
- return await cached(_fetch, cache_key, request)
426
-
427
-
428
- async def get_collection(
429
- request: Request, collection_id: str, provider: Optional[str] = None
430
- ) -> dict[str, Any]:
431
- """Build STAC collection by id
432
-
433
- :param url: Requested URL
434
- :param root: API root
435
- :param collection_id: Product_type as ID of the collection
436
- :param provider: (optional) Chosen provider
437
- :returns: Collection dictionary
438
- """
439
-
440
- async def _fetch() -> dict[str, Any]:
441
- stac_collection = StacCollection(
442
- url=request.state.url,
443
- stac_config=stac_config,
444
- provider=provider,
445
- eodag_api=eodag_api,
446
- root=request.state.url_root,
447
- )
448
- collection_list = stac_collection.get_collection_list(collection=collection_id)
449
-
450
- if not collection_list:
451
- raise NotAvailableError(f"Collection {collection_id} does not exist.")
452
-
453
- return collection_list[0]
454
-
455
- cache_key = f"{CACHE_KEY_COLLECTION}:{provider}:{collection_id}"
456
- return await cached(_fetch, cache_key, request)
457
-
458
-
459
- async def get_stac_catalogs(
460
- request: Request,
461
- url: str,
462
- provider: Optional[str] = None,
463
- ) -> dict[str, Any]:
464
- """Build STAC catalog
465
-
466
- :param url: Requested URL
467
- :param root: (optional) API root
468
- :param provider: (optional) Chosen provider
469
- :returns: Catalog dictionary
470
- """
471
-
472
- async def _fetch() -> dict[str, Any]:
473
- return StacCatalog(
474
- url=url,
475
- stac_config=stac_config,
476
- root=request.state.url_root,
477
- provider=provider,
478
- eodag_api=eodag_api,
479
- ).data
480
-
481
- return await cached(_fetch, f"{CACHE_KEY_COLLECTION}:{provider}", request)
482
-
483
-
484
- def time_interval_overlap(eodag_args: EODAGSearch, catalog: StacCatalog) -> bool:
485
- """fix search date filter based on catalog date range"""
486
- # check if time filtering appears both in search arguments and catalog
487
- # (for catalogs built by date: i.e. `year/2020/month/05`)
488
- if not set(["start", "end"]) <= set(eodag_args.model_dump().keys()) or not set(
489
- [
490
- "start",
491
- "end",
492
- ]
493
- ) <= set(catalog.search_args.keys()):
494
- return True
495
-
496
- search_date_min = cast(
497
- datetime.datetime,
498
- (
499
- dateutil.parser.parse(eodag_args.start) # type: ignore
500
- if eodag_args.start
501
- else datetime.datetime.min.replace(tzinfo=datetime.timezone.utc)
502
- ),
503
- )
504
- search_date_max = cast(
505
- datetime.datetime,
506
- (
507
- dateutil.parser.parse(eodag_args.end) # type: ignore
508
- if eodag_args.end
509
- else datetime.datetime.now(tz=datetime.timezone.utc)
510
- ),
511
- )
512
-
513
- catalog_date_min = rfc3339_str_to_datetime(catalog.search_args["start"])
514
- catalog_date_max = rfc3339_str_to_datetime(catalog.search_args["end"])
515
- # check if date intervals overlap
516
- if (search_date_min <= catalog_date_max) and (search_date_max >= catalog_date_min):
517
- # use intersection
518
- eodag_args.start = (
519
- max(search_date_min, catalog_date_min).isoformat().replace("+00:00", "Z")
520
- )
521
- eodag_args.end = (
522
- min(search_date_max, catalog_date_max).isoformat().replace("+00:00", "Z")
523
- )
524
- return True
525
-
526
- logger.warning("Time intervals do not overlap")
527
- return False
528
-
529
-
530
- @lru_cache(maxsize=1)
531
- def get_stac_conformance() -> dict[str, str]:
532
- """Build STAC conformance
533
-
534
- :returns: conformance dictionary
535
- """
536
- return stac_config["conformance"]
537
-
538
-
539
- def get_stac_api_version() -> str:
540
- """Get STAC API version
541
-
542
- :returns: STAC API version
543
- """
544
- return stac_config["stac_api_version"]
545
-
546
-
547
- @lru_cache(maxsize=1)
548
- def get_stac_extension_oseo(url: str) -> dict[str, str]:
549
- """Build STAC OGC / OpenSearch Extension for EO
550
-
551
- :param url: Requested URL
552
- :returns: Catalog dictionary
553
- """
554
-
555
- def apply_method(_: str, x: str) -> str:
556
- return str(x).replace("$.product.", "$.")
557
-
558
- item_mapping = dict_items_recursive_apply(stac_config["item"], apply_method)
559
-
560
- # all properties as string type by default
561
- oseo_properties = {
562
- "oseo:{}".format(k): {
563
- "type": "string",
564
- "title": k[0].upper() + re.sub(r"([A-Z][a-z]+)", r" \1", k[1:]),
565
- }
566
- for k, v in OSEO_METADATA_MAPPING.items()
567
- if v not in str(item_mapping)
568
- }
569
-
570
- return StacCommon.get_stac_extension(
571
- url=url, stac_config=stac_config, extension="oseo", properties=oseo_properties
572
- )
573
-
574
-
575
- async def get_queryables(
576
- request: Request,
577
- params: QueryablesGetParams,
578
- provider: Optional[str] = None,
579
- ) -> dict[str, Any]:
580
- """Fetch the queryable properties for a collection.
581
-
582
- :param collection_id: The ID of the collection.
583
- :returns: A set containing the STAC standardized queryable properties for a collection.
584
- """
585
-
586
- async def _fetch() -> dict[str, Any]:
587
- python_queryables = eodag_api.list_queryables(
588
- provider=provider,
589
- fetch_providers=False,
590
- **params.model_dump(exclude_none=True, by_alias=True),
591
- )
592
-
593
- python_queryables_json = python_queryables.get_model().model_json_schema(
594
- by_alias=True
595
- )
596
-
597
- properties: dict[str, Any] = python_queryables_json["properties"]
598
- required: list[str] = python_queryables_json.get("required") or []
599
-
600
- # productType is either simply removed or replaced by collection later.
601
- if "productType" in properties:
602
- properties.pop("productType")
603
- if "productType" in required:
604
- required.remove("productType")
605
-
606
- stac_properties: dict[str, Any] = {}
607
-
608
- # get stac default properties to set prefixes
609
- stac_item_properties = list(stac_config["item"]["properties"].values())
610
- stac_item_properties.extend(stac_config["metadata_ignore"])
611
- for param, queryable in properties.items():
612
- # convert key to STAC format
613
- if param in OSEO_METADATA_MAPPING.keys() and not any(
614
- param in str(prop) for prop in stac_item_properties
615
- ):
616
- param = f"oseo:{param}"
617
- stac_param = EODAGSearch.to_stac(param, stac_item_properties, provider)
618
-
619
- queryable["title"] = stac_param.split(":")[-1]
620
-
621
- # remove null default values
622
- if not queryable.get("default"):
623
- queryable.pop("default", None)
624
-
625
- stac_properties[stac_param] = queryable
626
- required = list(map(lambda x: x.replace(param, stac_param), required))
627
-
628
- # due to certain metadata mappings we might only get end_datetime but we can
629
- # assume that start_datetime is also available
630
- if (
631
- "end_datetime" in stac_properties
632
- and "start_datetime" not in stac_properties
633
- ):
634
- stac_properties["start_datetime"] = deepcopy(
635
- stac_properties["end_datetime"]
636
- )
637
- stac_properties["start_datetime"]["title"] = "start_datetime"
638
- # if we can search by start_datetime we can search by datetime
639
- if "start_datetime" in stac_properties:
640
- stac_properties["datetime"] = StacQueryables.possible_properties[
641
- "datetime"
642
- ].model_dump()
643
-
644
- # format spatial extend properties to STAC format.
645
- if "geometry" in stac_properties:
646
- stac_properties["bbox"] = StacQueryables.possible_properties[
647
- "bbox"
648
- ].model_dump()
649
- stac_properties["geometry"] = StacQueryables.possible_properties[
650
- "geometry"
651
- ].model_dump()
652
-
653
- if not params.collection:
654
- stac_properties["collection"] = StacQueryables.default_properties[
655
- "collection"
656
- ].model_dump()
657
-
658
- additional_properties = python_queryables.additional_properties
659
- description = "Queryable names for the EODAG STAC API Item Search filter. "
660
- description += python_queryables.additional_information
661
-
662
- return StacQueryables(
663
- q_id=request.state.url,
664
- additional_properties=additional_properties,
665
- properties=stac_properties,
666
- required=required or None,
667
- description=description,
668
- ).model_dump(mode="json", by_alias=True, exclude_none=True)
669
-
670
- hashed_queryables = hash(params.model_dump_json())
671
- return await cached(
672
- _fetch, f"{CACHE_KEY_QUERYABLES}:{provider}:{hashed_queryables}", request
673
- )
674
-
675
-
676
- @_deprecated(
677
- reason="Used to format output from deprecated function get_home_page_content",
678
- version="2.6.1",
679
- )
680
- def get_templates_path() -> str:
681
- """Returns Jinja templates path"""
682
- return os.path.join(os.path.dirname(__file__), "templates")
683
-
684
-
685
- def crunch_products(
686
- products: SearchResult, cruncher_name: str, **kwargs: Any
687
- ) -> SearchResult:
688
- """Apply an eodag cruncher to filter products"""
689
- cruncher = crunchers.get(cruncher_name)
690
- if not cruncher:
691
- raise ValidationError(
692
- f"Unknown crunch name. Use one of: {', '.join(crunchers.keys())}"
693
- )
694
-
695
- cruncher_config: dict[str, Any] = {}
696
- for config_param in cruncher.config_params:
697
- config_param_value = kwargs.get(config_param)
698
- if not config_param_value:
699
- raise ValidationError(
700
- (
701
- f"cruncher {cruncher} require additional parameters:"
702
- f" {', '.join(cruncher.config_params)}"
703
- )
704
- )
705
- cruncher_config[config_param] = config_param_value
706
-
707
- try:
708
- products = products.crunch(cruncher.clazz(cruncher_config), **kwargs)
709
- except MisconfiguredError as e:
710
- raise ValidationError(str(e)) from e
711
-
712
- return products
713
-
714
-
715
- def eodag_api_init() -> None:
716
- """Init EODataAccessGateway server instance, pre-running all time consuming tasks"""
717
- eodag_api.fetch_product_types_list()
718
- StacCollection.fetch_external_stac_collections(eodag_api)
719
-
720
- # update eodag product_types config form external stac collections
721
- for p, p_f in eodag_api.product_types_config.source.items():
722
- for key in (p, p_f.get("alias")):
723
- if key is None:
724
- continue
725
- ext_col = StacCollection.ext_stac_collections.get(key)
726
- if not ext_col:
727
- continue
728
- platform: Union[str, list[str]] = ext_col.get("summaries", {}).get(
729
- "platform"
730
- )
731
- constellation: Union[str, list[str]] = ext_col.get("summaries", {}).get(
732
- "constellation"
733
- )
734
- processing_level: Union[str, list[str]] = ext_col.get("summaries", {}).get(
735
- "processing:level"
736
- )
737
- # Check if platform or constellation are lists and join them into a string if they are
738
- if isinstance(platform, list):
739
- platform = ",".join(platform)
740
- if isinstance(constellation, list):
741
- constellation = ",".join(constellation)
742
- if isinstance(processing_level, list):
743
- processing_level = ",".join(processing_level)
744
-
745
- update_fields = {
746
- "title": ext_col.get("title"),
747
- "abstract": ext_col["description"],
748
- "keywords": ext_col.get("keywords"),
749
- "instrument": ",".join(
750
- ext_col.get("summaries", {}).get("instruments", [])
751
- ),
752
- "platform": constellation,
753
- "platformSerialIdentifier": platform,
754
- "processingLevel": processing_level,
755
- "license": ext_col["license"],
756
- "missionStartDate": ext_col["extent"]["temporal"]["interval"][0][0],
757
- "missionEndDate": ext_col["extent"]["temporal"]["interval"][-1][1],
758
- }
759
- clean = {k: v for k, v in update_fields.items() if v}
760
- p_f.update(clean)
761
-
762
- # pre-build search plugins
763
- for provider in eodag_api.available_providers():
764
- next(eodag_api._plugins_manager.get_search_plugins(provider=provider))