eodag 2.12.0__py3-none-any.whl → 3.0.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/__init__.py +6 -8
- eodag/api/core.py +654 -538
- eodag/api/product/__init__.py +12 -2
- eodag/api/product/_assets.py +59 -16
- eodag/api/product/_product.py +100 -93
- eodag/api/product/drivers/__init__.py +7 -2
- eodag/api/product/drivers/base.py +0 -3
- eodag/api/product/metadata_mapping.py +192 -96
- eodag/api/search_result.py +69 -10
- eodag/cli.py +55 -25
- eodag/config.py +391 -116
- eodag/plugins/apis/base.py +11 -165
- eodag/plugins/apis/ecmwf.py +36 -25
- eodag/plugins/apis/usgs.py +80 -35
- eodag/plugins/authentication/aws_auth.py +13 -4
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +2 -2
- eodag/plugins/authentication/header.py +31 -6
- eodag/plugins/authentication/keycloak.py +17 -84
- eodag/plugins/authentication/oauth.py +3 -3
- eodag/plugins/authentication/openid_connect.py +268 -49
- eodag/plugins/authentication/qsauth.py +4 -1
- eodag/plugins/authentication/sas_auth.py +9 -2
- eodag/plugins/authentication/token.py +98 -47
- eodag/plugins/authentication/token_exchange.py +122 -0
- eodag/plugins/crunch/base.py +3 -1
- eodag/plugins/crunch/filter_date.py +3 -9
- eodag/plugins/crunch/filter_latest_intersect.py +0 -3
- eodag/plugins/crunch/filter_latest_tpl_name.py +1 -4
- eodag/plugins/crunch/filter_overlap.py +4 -8
- eodag/plugins/crunch/filter_property.py +5 -11
- eodag/plugins/download/aws.py +149 -185
- eodag/plugins/download/base.py +88 -97
- eodag/plugins/download/creodias_s3.py +1 -1
- eodag/plugins/download/http.py +638 -310
- eodag/plugins/download/s3rest.py +47 -45
- eodag/plugins/manager.py +228 -88
- eodag/plugins/search/__init__.py +36 -0
- eodag/plugins/search/base.py +239 -30
- eodag/plugins/search/build_search_result.py +382 -37
- eodag/plugins/search/cop_marine.py +441 -0
- eodag/plugins/search/creodias_s3.py +25 -20
- eodag/plugins/search/csw.py +5 -7
- eodag/plugins/search/data_request_search.py +61 -30
- eodag/plugins/search/qssearch.py +713 -255
- eodag/plugins/search/static_stac_search.py +106 -40
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +1921 -34
- eodag/resources/providers.yml +4091 -3655
- eodag/resources/stac.yml +50 -216
- eodag/resources/stac_api.yml +71 -25
- eodag/resources/stac_provider.yml +5 -0
- eodag/resources/user_conf_template.yml +89 -32
- eodag/rest/__init__.py +6 -0
- eodag/rest/cache.py +70 -0
- eodag/rest/config.py +68 -0
- eodag/rest/constants.py +26 -0
- eodag/rest/core.py +735 -0
- eodag/rest/errors.py +178 -0
- eodag/rest/server.py +264 -431
- eodag/rest/stac.py +442 -836
- eodag/rest/types/collections_search.py +44 -0
- eodag/rest/types/eodag_search.py +238 -47
- eodag/rest/types/queryables.py +164 -0
- eodag/rest/types/stac_search.py +273 -0
- eodag/rest/utils/__init__.py +216 -0
- eodag/rest/utils/cql_evaluate.py +119 -0
- eodag/rest/utils/rfc3339.py +64 -0
- eodag/types/__init__.py +106 -10
- eodag/types/bbox.py +15 -14
- eodag/types/download_args.py +40 -0
- eodag/types/search_args.py +57 -7
- eodag/types/whoosh.py +79 -0
- eodag/utils/__init__.py +110 -91
- eodag/utils/constraints.py +37 -45
- eodag/utils/exceptions.py +39 -22
- eodag/utils/import_system.py +0 -4
- eodag/utils/logging.py +37 -80
- eodag/utils/notebook.py +4 -4
- eodag/utils/repr.py +113 -0
- eodag/utils/requests.py +128 -0
- eodag/utils/rest.py +100 -0
- eodag/utils/stac_reader.py +93 -21
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
- eodag-3.0.0.dist-info/RECORD +109 -0
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/entry_points.txt +7 -5
- eodag/plugins/apis/cds.py +0 -540
- eodag/rest/types/stac_queryables.py +0 -134
- eodag/rest/utils.py +0 -1133
- eodag-2.12.0.dist-info/RECORD +0 -94
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
- {eodag-2.12.0.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
eodag/api/search_result.py
CHANGED
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
from collections import UserList
|
|
21
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union
|
|
22
22
|
|
|
23
23
|
from shapely.geometry import GeometryCollection, shape
|
|
24
|
+
from typing_extensions import Doc
|
|
24
25
|
|
|
25
26
|
from eodag.api.product import EOProduct
|
|
26
27
|
from eodag.plugins.crunch.filter_date import FilterDate
|
|
@@ -28,6 +29,7 @@ from eodag.plugins.crunch.filter_latest_intersect import FilterLatestIntersect
|
|
|
28
29
|
from eodag.plugins.crunch.filter_latest_tpl_name import FilterLatestByName
|
|
29
30
|
from eodag.plugins.crunch.filter_overlap import FilterOverlap
|
|
30
31
|
from eodag.plugins.crunch.filter_property import FilterProperty
|
|
32
|
+
from eodag.utils import Annotated
|
|
31
33
|
|
|
32
34
|
if TYPE_CHECKING:
|
|
33
35
|
from shapely.geometry.base import BaseGeometry
|
|
@@ -39,23 +41,34 @@ class SearchResult(UserList):
|
|
|
39
41
|
"""An object representing a collection of :class:`~eodag.api.product._product.EOProduct` resulting from a search.
|
|
40
42
|
|
|
41
43
|
:param products: A list of products resulting from a search
|
|
42
|
-
:
|
|
44
|
+
:param number_matched: (optional) the estimated total number of matching results
|
|
45
|
+
|
|
46
|
+
:cvar data: List of products
|
|
47
|
+
:ivar number_matched: Estimated total number of matching results
|
|
43
48
|
"""
|
|
44
49
|
|
|
45
50
|
data: List[EOProduct]
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
errors: Annotated[
|
|
53
|
+
List[Tuple[str, Exception]], Doc("Tuple of provider name, exception")
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
products: List[EOProduct],
|
|
59
|
+
number_matched: Optional[int] = None,
|
|
60
|
+
errors: List[Tuple[str, Exception]] = [],
|
|
61
|
+
) -> None:
|
|
62
|
+
super().__init__(products)
|
|
63
|
+
self.number_matched = number_matched
|
|
64
|
+
self.errors = errors
|
|
49
65
|
|
|
50
66
|
def crunch(self, cruncher: Crunch, **search_params: Any) -> SearchResult:
|
|
51
67
|
"""Do some crunching with the underlying EO products.
|
|
52
68
|
|
|
53
69
|
:param cruncher: The plugin instance to use to work on the products
|
|
54
|
-
:type cruncher: subclass of :class:`~eodag.plugins.crunch.base.Crunch`
|
|
55
70
|
:param search_params: The criteria that have been used to produce this result
|
|
56
|
-
:type search_params: dict
|
|
57
71
|
:returns: The result of the application of the crunching method to the EO products
|
|
58
|
-
:rtype: :class:`~eodag.api.search_result.SearchResult`
|
|
59
72
|
"""
|
|
60
73
|
crunched_results = cruncher.proceed(self.data, **search_params)
|
|
61
74
|
return SearchResult(crunched_results)
|
|
@@ -130,9 +143,7 @@ class SearchResult(UserList):
|
|
|
130
143
|
"""Builds an :class:`~eodag.api.search_result.SearchResult` object from its representation as geojson
|
|
131
144
|
|
|
132
145
|
:param feature_collection: A collection representing a search result.
|
|
133
|
-
:type feature_collection: dict
|
|
134
146
|
:returns: An eodag representation of a search result
|
|
135
|
-
:rtype: :class:`~eodag.api.search_result.SearchResult`
|
|
136
147
|
"""
|
|
137
148
|
return SearchResult(
|
|
138
149
|
[
|
|
@@ -149,7 +160,7 @@ class SearchResult(UserList):
|
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
def as_shapely_geometry_object(self) -> GeometryCollection:
|
|
152
|
-
""":class:`shapely.
|
|
163
|
+
""":class:`shapely.GeometryCollection` representation of SearchResult"""
|
|
153
164
|
return GeometryCollection(
|
|
154
165
|
[
|
|
155
166
|
shape(feature["geometry"]).buffer(0)
|
|
@@ -168,3 +179,51 @@ class SearchResult(UserList):
|
|
|
168
179
|
See https://gist.github.com/sgillies/2217756
|
|
169
180
|
"""
|
|
170
181
|
return self.as_geojson_object()
|
|
182
|
+
|
|
183
|
+
def _repr_html_(self):
|
|
184
|
+
total_count = f"/{self.number_matched}" if self.number_matched else ""
|
|
185
|
+
return (
|
|
186
|
+
f"""<table>
|
|
187
|
+
<thead><tr><td style='text-align: left; color: grey;'>
|
|
188
|
+
{type(self).__name__} ({len(self)}{total_count})
|
|
189
|
+
</td></tr></thead>
|
|
190
|
+
"""
|
|
191
|
+
+ "".join(
|
|
192
|
+
[
|
|
193
|
+
f"""<tr><td style='text-align: left;'>
|
|
194
|
+
<details><summary style='color: grey; font-family: monospace;'>
|
|
195
|
+
{i} 
|
|
196
|
+
{type(p).__name__}(id=<span style='color: black;'>{
|
|
197
|
+
p.properties['id']
|
|
198
|
+
}</span>, provider={p.provider})
|
|
199
|
+
</summary>
|
|
200
|
+
{p._repr_html_()}
|
|
201
|
+
</details>
|
|
202
|
+
</td></tr>
|
|
203
|
+
"""
|
|
204
|
+
for i, p in enumerate(self)
|
|
205
|
+
]
|
|
206
|
+
)
|
|
207
|
+
+ "</table>"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
def extend(self, other: Iterable) -> None:
|
|
211
|
+
"""override extend method to include errors"""
|
|
212
|
+
if isinstance(other, SearchResult):
|
|
213
|
+
self.errors.extend(other.errors)
|
|
214
|
+
|
|
215
|
+
return super().extend(other)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class RawSearchResult(UserList):
|
|
219
|
+
"""An object representing a collection of raw/unparsed search results obtained from a provider.
|
|
220
|
+
|
|
221
|
+
:param results: A list of raw/unparsed search results
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
data: List[Any]
|
|
225
|
+
query_params: Dict[str, Any]
|
|
226
|
+
product_type_def_params: Dict[str, Any]
|
|
227
|
+
|
|
228
|
+
def __init__(self, results: List[Any]) -> None:
|
|
229
|
+
super(RawSearchResult, self).__init__(results)
|
eodag/cli.py
CHANGED
|
@@ -39,6 +39,7 @@ Commands:
|
|
|
39
39
|
|
|
40
40
|
noqa: D103
|
|
41
41
|
"""
|
|
42
|
+
|
|
42
43
|
from __future__ import annotations
|
|
43
44
|
|
|
44
45
|
import json
|
|
@@ -50,7 +51,6 @@ from importlib.metadata import metadata
|
|
|
50
51
|
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Set
|
|
51
52
|
|
|
52
53
|
import click
|
|
53
|
-
import uvicorn
|
|
54
54
|
|
|
55
55
|
from eodag.api.core import EODataAccessGateway
|
|
56
56
|
from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE, parse_qs
|
|
@@ -242,6 +242,11 @@ def version() -> None:
|
|
|
242
242
|
"or a maximum value defined internally for the requested provider, or a default "
|
|
243
243
|
"maximum value equals to 50.",
|
|
244
244
|
)
|
|
245
|
+
@click.option(
|
|
246
|
+
"--count",
|
|
247
|
+
is_flag=True,
|
|
248
|
+
help="Whether to run a query with a count request or not.",
|
|
249
|
+
)
|
|
245
250
|
@click.option(
|
|
246
251
|
"--locations",
|
|
247
252
|
type=str,
|
|
@@ -334,6 +339,8 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
|
|
|
334
339
|
if locs_file:
|
|
335
340
|
locs_file = click.format_filename(locs_file)
|
|
336
341
|
|
|
342
|
+
count = kwargs.pop("count")
|
|
343
|
+
|
|
337
344
|
# Process inputs for crunch
|
|
338
345
|
cruncher_names: Set[Any] = set(kwargs.pop("cruncher") or [])
|
|
339
346
|
cruncher_args = kwargs.pop("cruncher_args")
|
|
@@ -361,10 +368,13 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
|
|
|
361
368
|
items_per_page = (
|
|
362
369
|
DEFAULT_ITEMS_PER_PAGE if items_per_page is None else items_per_page
|
|
363
370
|
)
|
|
364
|
-
results
|
|
365
|
-
page=page, items_per_page=items_per_page, **criteria
|
|
371
|
+
results = gateway.search(
|
|
372
|
+
count=count, page=page, items_per_page=items_per_page, **criteria
|
|
366
373
|
)
|
|
367
|
-
|
|
374
|
+
if results.number_matched is not None:
|
|
375
|
+
click.echo(
|
|
376
|
+
"Found a total number of {} products".format(results.number_matched)
|
|
377
|
+
)
|
|
368
378
|
click.echo("Returned {} products".format(len(results)))
|
|
369
379
|
|
|
370
380
|
# Crunch !
|
|
@@ -446,8 +456,6 @@ def list_pt(ctx: Context, **kwargs: Any) -> None:
|
|
|
446
456
|
provider=provider, fetch_providers=fetch_providers
|
|
447
457
|
)
|
|
448
458
|
if pt["ID"] in guessed_product_types
|
|
449
|
-
or "alias" in pt
|
|
450
|
-
and pt["alias"] in guessed_product_types
|
|
451
459
|
]
|
|
452
460
|
else:
|
|
453
461
|
product_types = dag.list_product_types(
|
|
@@ -546,12 +554,13 @@ def download(ctx: Context, **kwargs: Any) -> None:
|
|
|
546
554
|
|
|
547
555
|
for idx, product in enumerate(search_results):
|
|
548
556
|
if product.downloader is None:
|
|
557
|
+
downloader = satim_api._plugins_manager.get_download_plugin(product)
|
|
549
558
|
auth = product.downloader_auth
|
|
550
559
|
if auth is None:
|
|
551
|
-
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
)
|
|
560
|
+
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
561
|
+
downloader, product
|
|
562
|
+
)
|
|
563
|
+
search_results[idx].register_downloader(downloader, auth)
|
|
555
564
|
|
|
556
565
|
downloaded_file = product.get_quicklook()
|
|
557
566
|
if not downloaded_file:
|
|
@@ -566,12 +575,13 @@ def download(ctx: Context, **kwargs: Any) -> None:
|
|
|
566
575
|
# register downloader
|
|
567
576
|
for idx, product in enumerate(search_results):
|
|
568
577
|
if product.downloader is None:
|
|
578
|
+
downloader = satim_api._plugins_manager.get_download_plugin(product)
|
|
569
579
|
auth = product.downloader_auth
|
|
570
580
|
if auth is None:
|
|
571
|
-
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
)
|
|
581
|
+
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
582
|
+
downloader, product
|
|
583
|
+
)
|
|
584
|
+
search_results[idx].register_downloader(downloader, auth)
|
|
575
585
|
|
|
576
586
|
downloaded_files = satim_api.download_all(search_results)
|
|
577
587
|
if downloaded_files and len(downloaded_files) > 0:
|
|
@@ -645,6 +655,13 @@ def serve_rest(
|
|
|
645
655
|
) -> None:
|
|
646
656
|
"""Serve EODAG functionalities through a WEB interface"""
|
|
647
657
|
setup_logging(verbose=ctx.obj["verbosity"])
|
|
658
|
+
try:
|
|
659
|
+
import uvicorn
|
|
660
|
+
except ImportError:
|
|
661
|
+
raise ImportError(
|
|
662
|
+
"Feature not available, please install eodag[server] or eodag[all]"
|
|
663
|
+
)
|
|
664
|
+
|
|
648
665
|
# Set the settings of the app
|
|
649
666
|
# IMPORTANT: the order of imports counts here (first we override the settings,
|
|
650
667
|
# then we import the app so that the updated settings is taken into account in
|
|
@@ -670,19 +687,32 @@ def serve_rest(
|
|
|
670
687
|
else:
|
|
671
688
|
sys.exit(0)
|
|
672
689
|
else:
|
|
690
|
+
import logging
|
|
691
|
+
|
|
673
692
|
logging_config = uvicorn.config.LOGGING_CONFIG
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
693
|
+
uvicorn_fmt = "%(asctime)-15s %(name)-32s [%(levelname)-8s] %(message)s"
|
|
694
|
+
logging_config["formatters"]["access"]["fmt"] = uvicorn_fmt
|
|
695
|
+
logging_config["formatters"]["default"]["fmt"] = uvicorn_fmt
|
|
696
|
+
|
|
697
|
+
eodag_formatter = logging.Formatter(
|
|
698
|
+
"%(asctime)-15s %(name)-32s [%(levelname)-8s] (tid=%(thread)d) %(message)s"
|
|
699
|
+
)
|
|
700
|
+
logging.getLogger("eodag").handlers[0].setFormatter(eodag_formatter)
|
|
701
|
+
|
|
702
|
+
if ctx.obj["verbosity"] <= 1:
|
|
703
|
+
logging_config["handlers"]["null"] = {
|
|
704
|
+
"level": "DEBUG",
|
|
705
|
+
"class": "logging.NullHandler",
|
|
685
706
|
}
|
|
707
|
+
logging_config["loggers"]["uvicorn"]["handlers"] = ["null"]
|
|
708
|
+
logging_config["loggers"]["uvicorn.error"]["handlers"] = ["null"]
|
|
709
|
+
logging_config["loggers"]["uvicorn.access"]["handlers"] = ["null"]
|
|
710
|
+
else:
|
|
711
|
+
log_level = "INFO" if ctx.obj["verbosity"] == 2 else "DEBUG"
|
|
712
|
+
logging_config["loggers"]["uvicorn"]["level"] = log_level
|
|
713
|
+
logging_config["loggers"]["uvicorn.error"]["level"] = log_level
|
|
714
|
+
logging_config["loggers"]["uvicorn.access"]["level"] = log_level
|
|
715
|
+
|
|
686
716
|
uvicorn.run(
|
|
687
717
|
"eodag.rest.server:app",
|
|
688
718
|
host=bind_host,
|