eodag 3.5.1__py3-none-any.whl → 3.6.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 +29 -0
- eodag/api/product/__init__.py +30 -0
- eodag/api/search_result.py +155 -1
- eodag/cli.py +54 -41
- eodag/config.py +2 -6
- eodag/plugins/download/base.py +15 -3
- eodag/plugins/search/build_search_result.py +8 -6
- eodag/plugins/search/qssearch.py +4 -1
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +70 -0
- eodag/resources/providers.yml +10 -0
- eodag/rest/server.py +9 -7
- eodag/utils/__init__.py +7 -0
- {eodag-3.5.1.dist-info → eodag-3.6.0.dist-info}/METADATA +6 -5
- {eodag-3.5.1.dist-info → eodag-3.6.0.dist-info}/RECORD +19 -19
- {eodag-3.5.1.dist-info → eodag-3.6.0.dist-info}/WHEEL +0 -0
- {eodag-3.5.1.dist-info → eodag-3.6.0.dist-info}/entry_points.txt +0 -0
- {eodag-3.5.1.dist-info → eodag-3.6.0.dist-info}/licenses/LICENSE +0 -0
- {eodag-3.5.1.dist-info → eodag-3.6.0.dist-info}/top_level.txt +0 -0
eodag/api/core.py
CHANGED
|
@@ -69,6 +69,7 @@ from eodag.utils import (
|
|
|
69
69
|
DEFAULT_MAX_ITEMS_PER_PAGE,
|
|
70
70
|
DEFAULT_PAGE,
|
|
71
71
|
GENERIC_PRODUCT_TYPE,
|
|
72
|
+
GENERIC_STAC_PROVIDER,
|
|
72
73
|
HTTP_REQ_TIMEOUT,
|
|
73
74
|
MockResponse,
|
|
74
75
|
_deprecated,
|
|
@@ -2476,3 +2477,31 @@ class EODataAccessGateway:
|
|
|
2476
2477
|
)
|
|
2477
2478
|
# Remove the ID since this is equal to productType.
|
|
2478
2479
|
plugin.config.product_type_config.pop("ID", None)
|
|
2480
|
+
|
|
2481
|
+
def import_stac_items(self, items_urls: list[str]) -> SearchResult:
|
|
2482
|
+
"""Import STAC items from a list of URLs and convert them to SearchResult.
|
|
2483
|
+
|
|
2484
|
+
- Origin provider and download links will be set if item comes from an EODAG
|
|
2485
|
+
server.
|
|
2486
|
+
- If item comes from a known EODAG provider, result will be registered to it,
|
|
2487
|
+
ready to download and its metadata normalized.
|
|
2488
|
+
- If item comes from an unknown provider, a generic STAC provider will be used.
|
|
2489
|
+
|
|
2490
|
+
:param items_urls: A list of STAC items URLs to import
|
|
2491
|
+
:returns: A SearchResult containing the imported STAC items
|
|
2492
|
+
"""
|
|
2493
|
+
json_items = []
|
|
2494
|
+
for item_url in items_urls:
|
|
2495
|
+
json_items.extend(fetch_stac_items(item_url))
|
|
2496
|
+
|
|
2497
|
+
# add a generic STAC provider that might be needed to handle the items
|
|
2498
|
+
self.add_provider(GENERIC_STAC_PROVIDER)
|
|
2499
|
+
|
|
2500
|
+
results = SearchResult([])
|
|
2501
|
+
for json_item in json_items:
|
|
2502
|
+
if search_result := SearchResult._from_stac_item(
|
|
2503
|
+
json_item, self._plugins_manager
|
|
2504
|
+
):
|
|
2505
|
+
results.extend(search_result)
|
|
2506
|
+
|
|
2507
|
+
return results
|
eodag/api/product/__init__.py
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
#
|
|
19
19
|
"""EODAG product package"""
|
|
20
|
+
|
|
21
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from eodag.plugins.manager import PluginManager
|
|
25
|
+
|
|
20
26
|
try:
|
|
21
27
|
# import from eodag-cube if installed
|
|
22
28
|
from eodag_cube.api.product import ( # pyright: ignore[reportMissingImports]
|
|
@@ -30,3 +36,27 @@ except ImportError:
|
|
|
30
36
|
|
|
31
37
|
# exportable content
|
|
32
38
|
__all__ = ["Asset", "AssetsDict", "EOProduct"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def unregistered_product_from_item(
|
|
42
|
+
feature: dict[str, Any], provider: str, plugins_manager: "PluginManager"
|
|
43
|
+
) -> Optional[EOProduct]:
|
|
44
|
+
"""Create an EOProduct from a STAC item, map its metadata, but without registering its plugins.
|
|
45
|
+
|
|
46
|
+
:param feature: The STAC item to convert into an EOProduct.
|
|
47
|
+
:param provider: The associated provider from which configuration should be used for mapping.
|
|
48
|
+
:param plugins_manager: The plugins manager instance to use for retrieving search plugins.
|
|
49
|
+
:returns: An EOProduct instance if the item can be normalized, otherwise None.
|
|
50
|
+
"""
|
|
51
|
+
for search_plugin in plugins_manager.get_search_plugins(provider=provider):
|
|
52
|
+
if hasattr(search_plugin, "normalize_results"):
|
|
53
|
+
products = search_plugin.normalize_results([feature])
|
|
54
|
+
if len(products) > 0:
|
|
55
|
+
# properties cleanup
|
|
56
|
+
for prop in ("start_datetime", "end_datetime"):
|
|
57
|
+
products[0].properties.pop(prop, None)
|
|
58
|
+
# set product type if not already set
|
|
59
|
+
if products[0].product_type is None:
|
|
60
|
+
products[0].product_type = products[0].properties.get("productType")
|
|
61
|
+
return products[0]
|
|
62
|
+
return None
|
eodag/api/search_result.py
CHANGED
|
@@ -17,23 +17,30 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import logging
|
|
20
21
|
from collections import UserList
|
|
21
22
|
from typing import TYPE_CHECKING, Annotated, Any, Iterable, Optional, Union
|
|
22
23
|
|
|
23
24
|
from shapely.geometry import GeometryCollection, shape
|
|
24
25
|
from typing_extensions import Doc
|
|
25
26
|
|
|
26
|
-
from eodag.api.product import EOProduct
|
|
27
|
+
from eodag.api.product import EOProduct, unregistered_product_from_item
|
|
27
28
|
from eodag.plugins.crunch.filter_date import FilterDate
|
|
28
29
|
from eodag.plugins.crunch.filter_latest_intersect import FilterLatestIntersect
|
|
29
30
|
from eodag.plugins.crunch.filter_latest_tpl_name import FilterLatestByName
|
|
30
31
|
from eodag.plugins.crunch.filter_overlap import FilterOverlap
|
|
31
32
|
from eodag.plugins.crunch.filter_property import FilterProperty
|
|
33
|
+
from eodag.utils import GENERIC_STAC_PROVIDER, STAC_SEARCH_PLUGINS
|
|
34
|
+
from eodag.utils.exceptions import MisconfiguredError
|
|
32
35
|
|
|
33
36
|
if TYPE_CHECKING:
|
|
34
37
|
from shapely.geometry.base import BaseGeometry
|
|
35
38
|
|
|
36
39
|
from eodag.plugins.crunch.base import Crunch
|
|
40
|
+
from eodag.plugins.manager import PluginManager
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
logger = logging.getLogger("eodag.search_result")
|
|
37
44
|
|
|
38
45
|
|
|
39
46
|
class SearchResult(UserList[EOProduct]):
|
|
@@ -211,6 +218,153 @@ class SearchResult(UserList[EOProduct]):
|
|
|
211
218
|
|
|
212
219
|
return super().extend(other)
|
|
213
220
|
|
|
221
|
+
@classmethod
|
|
222
|
+
def _from_stac_item(
|
|
223
|
+
cls, feature: dict[str, Any], plugins_manager: PluginManager
|
|
224
|
+
) -> SearchResult:
|
|
225
|
+
"""Create a SearchResult from a STAC item.
|
|
226
|
+
|
|
227
|
+
:param feature: A STAC item as a dictionary
|
|
228
|
+
:param plugins_manager: The EODAG plugin manager instance
|
|
229
|
+
:returns: A SearchResult containing the EOProduct(s) created from the STAC item
|
|
230
|
+
"""
|
|
231
|
+
# Try importing from EODAG Server
|
|
232
|
+
if results := _import_stac_item_from_eodag_server(feature, plugins_manager):
|
|
233
|
+
return results
|
|
234
|
+
|
|
235
|
+
# try importing from a known STAC provider
|
|
236
|
+
if results := _import_stac_item_from_known_provider(feature, plugins_manager):
|
|
237
|
+
return results
|
|
238
|
+
|
|
239
|
+
# try importing from an unknown STAC provider
|
|
240
|
+
return _import_stac_item_from_unknown_provider(feature, plugins_manager)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _import_stac_item_from_eodag_server(
|
|
244
|
+
feature: dict[str, Any], plugins_manager: PluginManager
|
|
245
|
+
) -> Optional[SearchResult]:
|
|
246
|
+
"""Import a STAC item from EODAG Server.
|
|
247
|
+
|
|
248
|
+
:param feature: A STAC item as a dictionary
|
|
249
|
+
:param plugins_manager: The EODAG plugin manager instance
|
|
250
|
+
:returns: A SearchResult containing the EOProduct(s) created from the STAC item
|
|
251
|
+
"""
|
|
252
|
+
provider = None
|
|
253
|
+
if backends := feature["properties"].get("federation:backends"):
|
|
254
|
+
provider = backends[0]
|
|
255
|
+
elif providers := feature["properties"].get("providers"):
|
|
256
|
+
provider = providers[0].get("name")
|
|
257
|
+
if provider is not None:
|
|
258
|
+
logger.debug("Trying to import STAC item from EODAG Server")
|
|
259
|
+
# assets coming from a STAC provider
|
|
260
|
+
assets = {
|
|
261
|
+
k: v["alternate"]["origin"]
|
|
262
|
+
for k, v in feature.get("assets", {}).items()
|
|
263
|
+
if k not in ("thumbnail", "downloadLink")
|
|
264
|
+
and "origin" in v.get("alternate", {})
|
|
265
|
+
}
|
|
266
|
+
if assets:
|
|
267
|
+
updated_item = {**feature, **{"assets": assets}}
|
|
268
|
+
else:
|
|
269
|
+
# item coming from a non-STAC provider
|
|
270
|
+
updated_item = {**feature}
|
|
271
|
+
download_link = (
|
|
272
|
+
feature.get("assets", {})
|
|
273
|
+
.get("downloadLink", {})
|
|
274
|
+
.get("alternate", {})
|
|
275
|
+
.get("origin", {})
|
|
276
|
+
.get("href")
|
|
277
|
+
)
|
|
278
|
+
if download_link:
|
|
279
|
+
updated_item["assets"] = {}
|
|
280
|
+
updated_item["links"] = [{"rel": "self", "href": download_link}]
|
|
281
|
+
else:
|
|
282
|
+
updated_item = {}
|
|
283
|
+
try:
|
|
284
|
+
eo_product = unregistered_product_from_item(
|
|
285
|
+
updated_item, GENERIC_STAC_PROVIDER, plugins_manager
|
|
286
|
+
)
|
|
287
|
+
except MisconfiguredError:
|
|
288
|
+
eo_product = None
|
|
289
|
+
if eo_product is not None:
|
|
290
|
+
eo_product.provider = provider
|
|
291
|
+
eo_product._register_downloader_from_manager(plugins_manager)
|
|
292
|
+
return SearchResult([eo_product])
|
|
293
|
+
return None
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _import_stac_item_from_known_provider(
|
|
297
|
+
feature: dict[str, Any], plugins_manager: PluginManager
|
|
298
|
+
) -> Optional[SearchResult]:
|
|
299
|
+
"""Import a STAC item from an already-configured STAC provider.
|
|
300
|
+
|
|
301
|
+
:param feature: A STAC item as a dictionary
|
|
302
|
+
:param plugins_manager: The EODAG plugin manager instance
|
|
303
|
+
:returns: A SearchResult containing the EOProduct(s) created from the STAC item
|
|
304
|
+
"""
|
|
305
|
+
item_hrefs = [f for f in feature.get("links", []) if f.get("rel") == "self"]
|
|
306
|
+
item_href = item_hrefs[0]["href"] if len(item_hrefs) > 0 else None
|
|
307
|
+
imported_products = SearchResult([])
|
|
308
|
+
for search_plugin in plugins_manager.get_search_plugins():
|
|
309
|
+
# only try STAC search plugins
|
|
310
|
+
if (
|
|
311
|
+
search_plugin.config.type in STAC_SEARCH_PLUGINS
|
|
312
|
+
and search_plugin.provider != GENERIC_STAC_PROVIDER
|
|
313
|
+
and hasattr(search_plugin, "normalize_results")
|
|
314
|
+
):
|
|
315
|
+
provider_base_url = search_plugin.config.api_endpoint.removesuffix(
|
|
316
|
+
"/search"
|
|
317
|
+
)
|
|
318
|
+
# compare the item href with the provider base URL
|
|
319
|
+
if item_href and item_href.startswith(provider_base_url):
|
|
320
|
+
products = search_plugin.normalize_results([feature])
|
|
321
|
+
if len(products) == 0 or len(products[0].assets) == 0:
|
|
322
|
+
continue
|
|
323
|
+
logger.debug(
|
|
324
|
+
"Trying to import STAC item from %s", search_plugin.provider
|
|
325
|
+
)
|
|
326
|
+
eo_product = products[0]
|
|
327
|
+
|
|
328
|
+
configured_pts = [
|
|
329
|
+
k
|
|
330
|
+
for k, v in search_plugin.config.products.items()
|
|
331
|
+
if v.get("productType") == feature.get("collection")
|
|
332
|
+
]
|
|
333
|
+
if len(configured_pts) > 0:
|
|
334
|
+
eo_product.product_type = configured_pts[0]
|
|
335
|
+
else:
|
|
336
|
+
eo_product.product_type = feature.get("collection")
|
|
337
|
+
|
|
338
|
+
eo_product._register_downloader_from_manager(plugins_manager)
|
|
339
|
+
imported_products.append(eo_product)
|
|
340
|
+
if len(imported_products) > 0:
|
|
341
|
+
return imported_products
|
|
342
|
+
return None
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _import_stac_item_from_unknown_provider(
|
|
346
|
+
feature: dict[str, Any], plugins_manager: PluginManager
|
|
347
|
+
) -> SearchResult:
|
|
348
|
+
"""Import a STAC item from an unknown STAC provider.
|
|
349
|
+
|
|
350
|
+
:param feature: A STAC item as a dictionary
|
|
351
|
+
:param plugins_manager: The EODAG plugin manager instance
|
|
352
|
+
:returns: A SearchResult containing the EOProduct(s) created from the STAC item
|
|
353
|
+
"""
|
|
354
|
+
try:
|
|
355
|
+
logger.debug("Trying to import STAC item from unknown provider")
|
|
356
|
+
eo_product = unregistered_product_from_item(
|
|
357
|
+
feature, GENERIC_STAC_PROVIDER, plugins_manager
|
|
358
|
+
)
|
|
359
|
+
except MisconfiguredError:
|
|
360
|
+
pass
|
|
361
|
+
if eo_product is not None:
|
|
362
|
+
eo_product.product_type = feature.get("collection")
|
|
363
|
+
eo_product._register_downloader_from_manager(plugins_manager)
|
|
364
|
+
return SearchResult([eo_product])
|
|
365
|
+
else:
|
|
366
|
+
return SearchResult([])
|
|
367
|
+
|
|
214
368
|
|
|
215
369
|
class RawSearchResult(UserList[dict[str, Any]]):
|
|
216
370
|
"""An object representing a collection of raw/unparsed search results obtained from a provider.
|
eodag/cli.py
CHANGED
|
@@ -52,7 +52,7 @@ from typing import TYPE_CHECKING, Any, Mapping
|
|
|
52
52
|
|
|
53
53
|
import click
|
|
54
54
|
|
|
55
|
-
from eodag.api.core import EODataAccessGateway
|
|
55
|
+
from eodag.api.core import EODataAccessGateway, SearchResult
|
|
56
56
|
from eodag.utils import DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE, parse_qs
|
|
57
57
|
from eodag.utils.exceptions import NoMatchingProductType, UnsupportedProvider
|
|
58
58
|
from eodag.utils.logging import setup_logging
|
|
@@ -116,7 +116,7 @@ class MutuallyExclusiveOption(click.Option):
|
|
|
116
116
|
return super(MutuallyExclusiveOption, self).handle_parse_result(ctx, opts, args)
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
@click.group()
|
|
119
|
+
@click.group(chain=True)
|
|
120
120
|
@click.option(
|
|
121
121
|
"-v",
|
|
122
122
|
"--verbose",
|
|
@@ -212,6 +212,18 @@ def version() -> None:
|
|
|
212
212
|
"-S", "--sensorType", help="Search for products matching this type of sensor"
|
|
213
213
|
)
|
|
214
214
|
@click.option("--id", help="Search for the product identified by this id")
|
|
215
|
+
@click.option(
|
|
216
|
+
"--locations",
|
|
217
|
+
type=str,
|
|
218
|
+
help="Custom query-string argument(s) to select locations. "
|
|
219
|
+
"Format :'key1=value1&key2=value2'. Example: --locations country=FRA&continent=Africa",
|
|
220
|
+
)
|
|
221
|
+
@click.option(
|
|
222
|
+
"-q",
|
|
223
|
+
"--query",
|
|
224
|
+
type=str,
|
|
225
|
+
help="Custom query-string argument(s). Format :'key1=value1&key2=value2'",
|
|
226
|
+
)
|
|
215
227
|
@click.option(
|
|
216
228
|
"--cruncher",
|
|
217
229
|
type=click.Choice(CRUNCHERS),
|
|
@@ -262,19 +274,9 @@ def version() -> None:
|
|
|
262
274
|
@click.option(
|
|
263
275
|
"--count",
|
|
264
276
|
is_flag=True,
|
|
265
|
-
help="
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
"--locations",
|
|
269
|
-
type=str,
|
|
270
|
-
help="Custom query-string argument(s) to select locations. "
|
|
271
|
-
"Format :'key1=value1&key2=value2'. Example: --locations country=FRA&continent=Africa",
|
|
272
|
-
)
|
|
273
|
-
@click.option(
|
|
274
|
-
"-q",
|
|
275
|
-
"--query",
|
|
276
|
-
type=str,
|
|
277
|
-
help="Custom query-string argument(s). Format :'key1=value1&key2=value2'",
|
|
277
|
+
help="Make a count request together with search (Enabling count will significantly "
|
|
278
|
+
"slow down search requests for some providers, and might be unavailable for some"
|
|
279
|
+
"others).",
|
|
278
280
|
)
|
|
279
281
|
@click.pass_context
|
|
280
282
|
def search_crunch(ctx: Context, **kwargs: Any) -> None:
|
|
@@ -407,6 +409,7 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
|
|
|
407
409
|
storage_filepath += ".geojson"
|
|
408
410
|
result_storage = gateway.serialize(results, filename=storage_filepath)
|
|
409
411
|
click.echo("Results stored at '{}'".format(result_storage))
|
|
412
|
+
ctx.obj["search_results"] = results
|
|
410
413
|
|
|
411
414
|
|
|
412
415
|
@eodag.command(name="list", help="List supported product types")
|
|
@@ -528,12 +531,26 @@ def discover_pt(ctx: Context, **kwargs: Any) -> None:
|
|
|
528
531
|
click.echo("Results stored at '{}'".format(storage_filepath))
|
|
529
532
|
|
|
530
533
|
|
|
531
|
-
@eodag.command(
|
|
534
|
+
@eodag.command(
|
|
535
|
+
help="""Download a list of products from a serialized search result or STAC items URLs/paths
|
|
536
|
+
|
|
537
|
+
Examples:
|
|
538
|
+
|
|
539
|
+
eodag download --search-results /path/to/search_results.geojson
|
|
540
|
+
|
|
541
|
+
eodag download --stac-item https://example.com/stac/item1.json --stac-item /path/to/item2.json
|
|
542
|
+
""",
|
|
543
|
+
)
|
|
532
544
|
@click.option(
|
|
533
545
|
"--search-results",
|
|
534
546
|
type=click.Path(exists=True, dir_okay=False),
|
|
535
547
|
help="Path to a serialized search result",
|
|
536
548
|
)
|
|
549
|
+
@click.option(
|
|
550
|
+
"--stac-item",
|
|
551
|
+
multiple=True,
|
|
552
|
+
help="URL/path of a STAC item to download (multiple values accepted)",
|
|
553
|
+
)
|
|
537
554
|
@click.option(
|
|
538
555
|
"-f",
|
|
539
556
|
"--conf",
|
|
@@ -546,13 +563,20 @@ def discover_pt(ctx: Context, **kwargs: Any) -> None:
|
|
|
546
563
|
show_default=False,
|
|
547
564
|
help="Download only quicklooks of products instead full set of files",
|
|
548
565
|
)
|
|
566
|
+
@click.option(
|
|
567
|
+
"--output-dir",
|
|
568
|
+
type=click.Path(dir_okay=True, file_okay=False),
|
|
569
|
+
help="Products or quicklooks download directory (Default: local temporary directory)",
|
|
570
|
+
)
|
|
549
571
|
@click.pass_context
|
|
550
572
|
def download(ctx: Context, **kwargs: Any) -> None:
|
|
551
573
|
"""Download a bunch of products from a serialized search result"""
|
|
552
574
|
search_result_path = kwargs.pop("search_results")
|
|
553
|
-
|
|
575
|
+
stac_items = kwargs.pop("stac_item")
|
|
576
|
+
search_results = ctx.obj.get("search_results")
|
|
577
|
+
if not search_result_path and not stac_items and search_results is None:
|
|
554
578
|
with click.Context(download) as ctx:
|
|
555
|
-
click.echo("Nothing to do (no search results file provided)")
|
|
579
|
+
click.echo("Nothing to do (no search results file or stac item provided)")
|
|
556
580
|
click.echo(download.get_help(ctx))
|
|
557
581
|
sys.exit(1)
|
|
558
582
|
setup_logging(verbose=ctx.obj["verbosity"])
|
|
@@ -561,25 +585,24 @@ def download(ctx: Context, **kwargs: Any) -> None:
|
|
|
561
585
|
conf_file = click.format_filename(conf_file)
|
|
562
586
|
|
|
563
587
|
satim_api = EODataAccessGateway(user_conf_file_path=conf_file)
|
|
564
|
-
search_results = satim_api.deserialize(search_result_path)
|
|
565
588
|
|
|
589
|
+
search_results = search_results or SearchResult([])
|
|
590
|
+
if search_result_path:
|
|
591
|
+
search_results.extend(satim_api.deserialize_and_register(search_result_path))
|
|
592
|
+
if stac_items:
|
|
593
|
+
search_results.extend(satim_api.import_stac_items(list(stac_items)))
|
|
594
|
+
|
|
595
|
+
output_dir = kwargs.pop("output_dir")
|
|
566
596
|
get_quicklooks = kwargs.pop("quicklooks")
|
|
597
|
+
|
|
567
598
|
if get_quicklooks:
|
|
599
|
+
# Download only quicklooks
|
|
568
600
|
click.echo(
|
|
569
601
|
"Flag 'quicklooks' specified, downloading only quicklooks of products"
|
|
570
602
|
)
|
|
571
603
|
|
|
572
604
|
for idx, product in enumerate(search_results):
|
|
573
|
-
|
|
574
|
-
downloader = satim_api._plugins_manager.get_download_plugin(product)
|
|
575
|
-
auth = product.downloader_auth
|
|
576
|
-
if auth is None:
|
|
577
|
-
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
578
|
-
downloader, product
|
|
579
|
-
)
|
|
580
|
-
search_results[idx].register_downloader(downloader, auth)
|
|
581
|
-
|
|
582
|
-
downloaded_file = product.get_quicklook()
|
|
605
|
+
downloaded_file = product.get_quicklook(output_dir=output_dir)
|
|
583
606
|
if not downloaded_file:
|
|
584
607
|
click.echo(
|
|
585
608
|
"A quicklook may have been downloaded but we cannot locate it. "
|
|
@@ -589,18 +612,8 @@ def download(ctx: Context, **kwargs: Any) -> None:
|
|
|
589
612
|
click.echo("Downloaded {}".format(downloaded_file))
|
|
590
613
|
|
|
591
614
|
else:
|
|
592
|
-
#
|
|
593
|
-
|
|
594
|
-
if product.downloader is None:
|
|
595
|
-
downloader = satim_api._plugins_manager.get_download_plugin(product)
|
|
596
|
-
auth = product.downloader_auth
|
|
597
|
-
if auth is None:
|
|
598
|
-
auth = satim_api._plugins_manager.get_auth_plugin(
|
|
599
|
-
downloader, product
|
|
600
|
-
)
|
|
601
|
-
search_results[idx].register_downloader(downloader, auth)
|
|
602
|
-
|
|
603
|
-
downloaded_files = satim_api.download_all(search_results)
|
|
615
|
+
# Download products
|
|
616
|
+
downloaded_files = satim_api.download_all(search_results, output_dir=output_dir)
|
|
604
617
|
if downloaded_files and len(downloaded_files) > 0:
|
|
605
618
|
for downloaded_file in downloaded_files:
|
|
606
619
|
if downloaded_file is None:
|
eodag/config.py
CHANGED
|
@@ -46,6 +46,7 @@ from jsonpath_ng import JSONPath
|
|
|
46
46
|
from eodag.api.product.metadata_mapping import mtd_cfg_as_conversion_and_querypath
|
|
47
47
|
from eodag.utils import (
|
|
48
48
|
HTTP_REQ_TIMEOUT,
|
|
49
|
+
STAC_SEARCH_PLUGINS,
|
|
49
50
|
USER_AGENT,
|
|
50
51
|
cached_yaml_load,
|
|
51
52
|
cached_yaml_load_all,
|
|
@@ -819,12 +820,7 @@ def provider_config_init(
|
|
|
819
820
|
if (
|
|
820
821
|
stac_search_default_conf is not None
|
|
821
822
|
and provider_config.search
|
|
822
|
-
and provider_config.search.type
|
|
823
|
-
in [
|
|
824
|
-
"StacSearch",
|
|
825
|
-
"StacListAssets",
|
|
826
|
-
"StaticStacSearch",
|
|
827
|
-
]
|
|
823
|
+
and provider_config.search.type in STAC_SEARCH_PLUGINS
|
|
828
824
|
):
|
|
829
825
|
# search config set to stac defaults overriden with provider config
|
|
830
826
|
per_provider_stac_provider_config = deepcopy(stac_search_default_conf)
|
eodag/plugins/download/base.py
CHANGED
|
@@ -25,6 +25,7 @@ import tarfile
|
|
|
25
25
|
import tempfile
|
|
26
26
|
import zipfile
|
|
27
27
|
from datetime import datetime, timedelta
|
|
28
|
+
from pathlib import Path
|
|
28
29
|
from time import sleep
|
|
29
30
|
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
|
|
30
31
|
|
|
@@ -232,12 +233,23 @@ class Download(PluginTopic):
|
|
|
232
233
|
)
|
|
233
234
|
if os.path.isfile(old_record_filename):
|
|
234
235
|
os.rename(old_record_filename, record_filename)
|
|
235
|
-
|
|
236
|
+
|
|
237
|
+
# path with or without extension
|
|
238
|
+
path_obj = Path(fs_path)
|
|
239
|
+
matched_paths = list(path_obj.parent.glob(f"{path_obj.stem}.*"))
|
|
240
|
+
fs_path_with_ext = matched_paths[0] if matched_paths else fs_path
|
|
241
|
+
if (
|
|
242
|
+
os.path.isfile(record_filename)
|
|
243
|
+
and fs_path_with_ext
|
|
244
|
+
and os.path.isfile(fs_path_with_ext)
|
|
245
|
+
):
|
|
236
246
|
logger.info(
|
|
237
|
-
f"Product already downloaded: {
|
|
247
|
+
f"Product already downloaded: {fs_path_with_ext}",
|
|
238
248
|
)
|
|
239
249
|
return (
|
|
240
|
-
self._finalize(
|
|
250
|
+
self._finalize(
|
|
251
|
+
str(fs_path_with_ext), progress_callback=progress_callback, **kwargs
|
|
252
|
+
),
|
|
241
253
|
None,
|
|
242
254
|
)
|
|
243
255
|
elif os.path.isfile(record_filename) and os.path.isdir(fs_dir_path):
|
|
@@ -317,17 +317,17 @@ def append_time(input_date: date, time: Optional[str]) -> datetime:
|
|
|
317
317
|
|
|
318
318
|
|
|
319
319
|
def parse_date(
|
|
320
|
-
|
|
320
|
+
date: str, time: Optional[Union[str, list[str]]]
|
|
321
321
|
) -> tuple[datetime, datetime]:
|
|
322
322
|
"""Parses a date string in formats YYYY-MM-DD, YYYMMDD, solo or in start/end or start/to/end intervals."""
|
|
323
|
-
if "to" in
|
|
324
|
-
start_date_str, end_date_str =
|
|
325
|
-
elif "/" in
|
|
326
|
-
dates =
|
|
323
|
+
if "to" in date:
|
|
324
|
+
start_date_str, end_date_str = date.split("/to/")
|
|
325
|
+
elif "/" in date:
|
|
326
|
+
dates = date.split("/")
|
|
327
327
|
start_date_str = dates[0]
|
|
328
328
|
end_date_str = dates[-1]
|
|
329
329
|
else:
|
|
330
|
-
start_date_str = end_date_str =
|
|
330
|
+
start_date_str = end_date_str = date
|
|
331
331
|
|
|
332
332
|
# Update YYYYMMDD formatted dates
|
|
333
333
|
if re.match(r"^\d{8}$", start_date_str):
|
|
@@ -401,6 +401,8 @@ def ecmwf_temporal_to_eodag(
|
|
|
401
401
|
start = end = None
|
|
402
402
|
|
|
403
403
|
if date := params.get("date"):
|
|
404
|
+
if isinstance(date, list):
|
|
405
|
+
date = "/".join(date)
|
|
404
406
|
start, end = parse_date(date, params.get("time"))
|
|
405
407
|
|
|
406
408
|
elif year := (params.get("year") or params.get("hyear")):
|
eodag/plugins/search/qssearch.py
CHANGED
|
@@ -1173,7 +1173,10 @@ class QueryStringSearch(Search):
|
|
|
1173
1173
|
|
|
1174
1174
|
collection = getattr(self.config, "collection", None)
|
|
1175
1175
|
if collection is None:
|
|
1176
|
-
collection =
|
|
1176
|
+
collection = (
|
|
1177
|
+
getattr(prep, "product_type_def_params", {}).get("collection")
|
|
1178
|
+
or product_type
|
|
1179
|
+
)
|
|
1177
1180
|
|
|
1178
1181
|
if collection is None:
|
|
1179
1182
|
return ()
|