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.
- eodag/api/core.py +378 -419
- eodag/api/product/__init__.py +3 -3
- eodag/api/product/_product.py +68 -40
- eodag/api/product/drivers/__init__.py +3 -5
- eodag/api/product/drivers/base.py +1 -18
- eodag/api/product/metadata_mapping.py +151 -215
- eodag/api/search_result.py +13 -7
- eodag/cli.py +72 -395
- eodag/config.py +46 -50
- eodag/plugins/apis/base.py +2 -2
- eodag/plugins/apis/ecmwf.py +20 -21
- eodag/plugins/apis/usgs.py +37 -33
- eodag/plugins/authentication/aws_auth.py +36 -1
- eodag/plugins/authentication/base.py +18 -3
- eodag/plugins/authentication/sas_auth.py +15 -0
- 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 +45 -41
- 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 +3 -4
- eodag/plugins/search/base.py +128 -77
- eodag/plugins/search/build_search_result.py +105 -107
- eodag/plugins/search/cop_marine.py +44 -47
- eodag/plugins/search/csw.py +33 -33
- eodag/plugins/search/qssearch.py +335 -354
- eodag/plugins/search/stac_list_assets.py +1 -1
- eodag/plugins/search/static_stac_search.py +31 -31
- eodag/resources/{product_types.yml → collections.yml} +2353 -2429
- eodag/resources/ext_collections.json +1 -0
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/providers.yml +2432 -2714
- eodag/resources/stac_provider.yml +46 -90
- eodag/types/queryables.py +55 -91
- eodag/types/search_args.py +1 -1
- eodag/utils/__init__.py +94 -21
- eodag/utils/exceptions.py +6 -6
- eodag/utils/free_text_search.py +3 -3
- {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/METADATA +11 -88
- eodag-4.0.0a1.dist-info/RECORD +92 -0
- {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/entry_points.txt +0 -4
- eodag/plugins/authentication/oauth.py +0 -60
- eodag/plugins/download/creodias_s3.py +0 -64
- 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.9.1.dist-info/RECORD +0 -115
- {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/WHEEL +0 -0
- {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {eodag-3.9.1.dist-info → eodag-4.0.0a1.dist-info}/top_level.txt +0 -0
|
@@ -49,7 +49,7 @@ class FilterDate(Crunch):
|
|
|
49
49
|
@staticmethod
|
|
50
50
|
def sort_product_by_start_date(product: EOProduct) -> dt:
|
|
51
51
|
"""Get product start date"""
|
|
52
|
-
start_date = product.properties.get("
|
|
52
|
+
start_date = product.properties.get("start_datetime")
|
|
53
53
|
if not start_date:
|
|
54
54
|
# Retrieve year, month, day, hour, minute, second of EPOCH start
|
|
55
55
|
epoch = time.gmtime(0)[:-3]
|
|
@@ -93,7 +93,7 @@ class FilterDate(Crunch):
|
|
|
93
93
|
for product in products:
|
|
94
94
|
|
|
95
95
|
# product start date
|
|
96
|
-
product_start_str = product.properties.get("
|
|
96
|
+
product_start_str = product.properties.get("start_datetime")
|
|
97
97
|
if product_start_str:
|
|
98
98
|
product_start = dateutil.parser.parse(product_start_str)
|
|
99
99
|
if not product_start.tzinfo:
|
|
@@ -102,7 +102,7 @@ class FilterDate(Crunch):
|
|
|
102
102
|
product_start = None
|
|
103
103
|
|
|
104
104
|
# product end date
|
|
105
|
-
product_end_str = product.properties.get("
|
|
105
|
+
product_end_str = product.properties.get("end_datetime")
|
|
106
106
|
if product_end_str:
|
|
107
107
|
product_end = dateutil.parser.parse(product_end_str)
|
|
108
108
|
if not product_end.tzinfo:
|
|
@@ -46,7 +46,7 @@ class FilterLatestIntersect(Crunch):
|
|
|
46
46
|
@staticmethod
|
|
47
47
|
def sort_product_by_start_date(product: EOProduct) -> dt:
|
|
48
48
|
"""Get product start date"""
|
|
49
|
-
start_date = product.properties.get("
|
|
49
|
+
start_date = product.properties.get("start_datetime")
|
|
50
50
|
if not start_date:
|
|
51
51
|
# Retrieve year, month, day, hour, minute, second of EPOCH start
|
|
52
52
|
epoch = time.gmtime(0)[:-3]
|
|
@@ -67,7 +67,7 @@ class FilterLatestIntersect(Crunch):
|
|
|
67
67
|
logger.debug("Start filtering for latest products")
|
|
68
68
|
if not products:
|
|
69
69
|
return []
|
|
70
|
-
# Warning: May crash if
|
|
70
|
+
# Warning: May crash if start_datetime is not in the appropriate format
|
|
71
71
|
products.sort(key=self.sort_product_by_start_date, reverse=True)
|
|
72
72
|
filtered: list[EOProduct] = []
|
|
73
73
|
add_to_filtered = filtered.append
|
|
@@ -75,7 +75,7 @@ class FilterLatestByName(Crunch):
|
|
|
75
75
|
logger.debug(
|
|
76
76
|
"Latest product found for tileid=%s: date=%s",
|
|
77
77
|
tileid,
|
|
78
|
-
product.properties["
|
|
78
|
+
product.properties["start_datetime"],
|
|
79
79
|
)
|
|
80
80
|
filtered.append(product)
|
|
81
81
|
processed.append(tileid)
|
eodag/plugins/download/aws.py
CHANGED
|
@@ -44,6 +44,7 @@ from eodag.utils import (
|
|
|
44
44
|
ProgressCallback,
|
|
45
45
|
StreamResponse,
|
|
46
46
|
flatten_top_directories,
|
|
47
|
+
format_string,
|
|
47
48
|
get_bucket_name_and_prefix,
|
|
48
49
|
path_to_uri,
|
|
49
50
|
rename_subfolder,
|
|
@@ -53,7 +54,7 @@ from eodag.utils.exceptions import (
|
|
|
53
54
|
AuthenticationError,
|
|
54
55
|
DownloadError,
|
|
55
56
|
MisconfiguredError,
|
|
56
|
-
|
|
57
|
+
NoMatchingCollection,
|
|
57
58
|
NotAvailableError,
|
|
58
59
|
TimeOutError,
|
|
59
60
|
)
|
|
@@ -66,7 +67,6 @@ if TYPE_CHECKING:
|
|
|
66
67
|
from eodag.api.product import EOProduct
|
|
67
68
|
from eodag.api.search_result import SearchResult
|
|
68
69
|
from eodag.config import PluginConfig
|
|
69
|
-
from eodag.types import S3SessionKwargs
|
|
70
70
|
from eodag.types.download_args import DownloadConf
|
|
71
71
|
from eodag.utils import DownloadedCallback, Unpack
|
|
72
72
|
|
|
@@ -202,17 +202,17 @@ class AwsDownload(Download):
|
|
|
202
202
|
* :attr:`~eodag.config.PluginConfig.flatten_top_dirs` (``bool``): if the directory structure
|
|
203
203
|
should be flattened; default: ``True``
|
|
204
204
|
* :attr:`~eodag.config.PluginConfig.ignore_assets` (``bool``): ignore assets and download
|
|
205
|
-
using ``
|
|
205
|
+
using ``eodag:download_link``; default: ``False``
|
|
206
206
|
* :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates should
|
|
207
207
|
be verified in requests; default: ``True``
|
|
208
208
|
* :attr:`~eodag.config.PluginConfig.bucket_path_level` (``int``): at which level of the
|
|
209
209
|
path part of the url the bucket can be found; If no bucket_path_level is given, the bucket
|
|
210
210
|
is taken from the first element of the netloc part.
|
|
211
|
-
* :attr:`~eodag.config.PluginConfig.products` (``dict[str, dict[str, Any]``):
|
|
212
|
-
specific config; the keys are the
|
|
211
|
+
* :attr:`~eodag.config.PluginConfig.products` (``dict[str, dict[str, Any]``): collection
|
|
212
|
+
specific config; the keys are the collections, the values are dictionaries which can contain the keys:
|
|
213
213
|
|
|
214
|
-
* **default_bucket** (``str``): bucket where the
|
|
215
|
-
* **complementary_url_key** (``str``): keys to
|
|
214
|
+
* **default_bucket** (``str``): bucket where the collection can be found
|
|
215
|
+
* **complementary_url_key** (``str``): properties keys pointing to additional urls of content to download
|
|
216
216
|
* **build_safe** (``bool``): if a SAFE (Standard Archive Format for Europe) product should
|
|
217
217
|
be created; used for Sentinel products; default: False
|
|
218
218
|
* **fetch_metadata** (``dict[str, Any]``): config for metadata to be fetched for the SAFE product
|
|
@@ -225,7 +225,7 @@ class AwsDownload(Download):
|
|
|
225
225
|
def download(
|
|
226
226
|
self,
|
|
227
227
|
product: EOProduct,
|
|
228
|
-
auth: Optional[Union[AuthBase,
|
|
228
|
+
auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
|
|
229
229
|
progress_callback: Optional[ProgressCallback] = None,
|
|
230
230
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
231
231
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -234,7 +234,7 @@ class AwsDownload(Download):
|
|
|
234
234
|
"""Download method for AWS S3 API.
|
|
235
235
|
|
|
236
236
|
The product can be downloaded as it is, or as SAFE-formatted product.
|
|
237
|
-
SAFE-build is configured for a given provider and
|
|
237
|
+
SAFE-build is configured for a given provider and collection.
|
|
238
238
|
If the product title is configured to be updated during download and
|
|
239
239
|
SAFE-formatted, its destination path will be:
|
|
240
240
|
`{output_dir}/{title}`
|
|
@@ -266,9 +266,7 @@ class AwsDownload(Download):
|
|
|
266
266
|
if not record_filename or not product_local_path:
|
|
267
267
|
return product_local_path
|
|
268
268
|
|
|
269
|
-
product_conf = getattr(self.config, "products", {}).get(
|
|
270
|
-
product.product_type, {}
|
|
271
|
-
)
|
|
269
|
+
product_conf = getattr(self.config, "products", {}).get(product.collection, {})
|
|
272
270
|
|
|
273
271
|
# do not try to build SAFE if asset filter is used
|
|
274
272
|
asset_filter = kwargs.get("asset")
|
|
@@ -368,7 +366,7 @@ class AwsDownload(Download):
|
|
|
368
366
|
logger.warning("Unexpected error: %s" % e)
|
|
369
367
|
|
|
370
368
|
# finalize safe product
|
|
371
|
-
if build_safe and product.
|
|
369
|
+
if build_safe and product.collection and "S2_MSI" in product.collection:
|
|
372
370
|
self.finalize_s2_safe_product(product_local_path)
|
|
373
371
|
# flatten directory structure
|
|
374
372
|
elif flatten_top_dirs:
|
|
@@ -479,17 +477,15 @@ class AwsDownload(Download):
|
|
|
479
477
|
:param build_safe: if safe build is enabled
|
|
480
478
|
:param product: product to be updated
|
|
481
479
|
"""
|
|
482
|
-
product_conf = getattr(self.config, "products", {}).get(
|
|
483
|
-
product.product_type, {}
|
|
484
|
-
)
|
|
480
|
+
product_conf = getattr(self.config, "products", {}).get(product.collection, {})
|
|
485
481
|
ssl_verify = getattr(self.config, "ssl_verify", True)
|
|
486
482
|
timeout = getattr(self.config, "timeout", HTTP_REQ_TIMEOUT)
|
|
487
483
|
|
|
488
484
|
if build_safe and "fetch_metadata" in product_conf.keys():
|
|
489
485
|
fetch_format = product_conf["fetch_metadata"]["fetch_format"]
|
|
490
486
|
update_metadata = product_conf["fetch_metadata"]["update_metadata"]
|
|
491
|
-
fetch_url =
|
|
492
|
-
**product.properties
|
|
487
|
+
fetch_url = format_string(
|
|
488
|
+
None, product_conf["fetch_metadata"]["fetch_url"], **product.properties
|
|
493
489
|
)
|
|
494
490
|
logger.info("Fetching extra metadata from %s" % fetch_url)
|
|
495
491
|
try:
|
|
@@ -619,14 +615,14 @@ class AwsDownload(Download):
|
|
|
619
615
|
)
|
|
620
616
|
|
|
621
617
|
if not unique_product_chunks and raise_error:
|
|
622
|
-
raise
|
|
618
|
+
raise NoMatchingCollection("No product found to download.")
|
|
623
619
|
|
|
624
620
|
return unique_product_chunks
|
|
625
621
|
|
|
626
622
|
def _stream_download_dict(
|
|
627
623
|
self,
|
|
628
624
|
product: EOProduct,
|
|
629
|
-
auth: Optional[Union[AuthBase,
|
|
625
|
+
auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
|
|
630
626
|
byte_range: tuple[Optional[int], Optional[int]] = (None, None),
|
|
631
627
|
compress: Literal["zip", "raw", "auto"] = "auto",
|
|
632
628
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
@@ -664,7 +660,7 @@ class AwsDownload(Download):
|
|
|
664
660
|
|
|
665
661
|
#### SAFE Archive Support:
|
|
666
662
|
|
|
667
|
-
If the
|
|
663
|
+
If the collection supports SAFE structure and no `asset_regex` is specified (i.e., full product download),
|
|
668
664
|
the method attempts to reconstruct a valid SAFE archive layout in the streamed output.
|
|
669
665
|
|
|
670
666
|
:param product: The EO product to download.
|
|
@@ -680,9 +676,7 @@ class AwsDownload(Download):
|
|
|
680
676
|
"""
|
|
681
677
|
asset_regex = kwargs.get("asset")
|
|
682
678
|
|
|
683
|
-
product_conf = getattr(self.config, "products", {}).get(
|
|
684
|
-
product.product_type, {}
|
|
685
|
-
)
|
|
679
|
+
product_conf = getattr(self.config, "products", {}).get(product.collection, {})
|
|
686
680
|
|
|
687
681
|
build_safe = (
|
|
688
682
|
False if asset_regex is not None else product_conf.get("build_safe", False)
|
|
@@ -724,9 +718,7 @@ class AwsDownload(Download):
|
|
|
724
718
|
endpoint_url=getattr(self.config, "s3_endpoint", None),
|
|
725
719
|
)
|
|
726
720
|
|
|
727
|
-
product_conf = getattr(self.config, "products", {}).get(
|
|
728
|
-
product.product_type, {}
|
|
729
|
-
)
|
|
721
|
+
product_conf = getattr(self.config, "products", {}).get(product.collection, {})
|
|
730
722
|
flatten_top_dirs = product_conf.get(
|
|
731
723
|
"flatten_top_dirs", getattr(self.config, "flatten_top_dirs", True)
|
|
732
724
|
)
|
|
@@ -814,7 +806,7 @@ class AwsDownload(Download):
|
|
|
814
806
|
if bucket is None:
|
|
815
807
|
bucket = (
|
|
816
808
|
getattr(self.config, "products", {})
|
|
817
|
-
.get(product.
|
|
809
|
+
.get(product.collection, {})
|
|
818
810
|
.get("default_bucket", "")
|
|
819
811
|
)
|
|
820
812
|
|
|
@@ -925,7 +917,7 @@ class AwsDownload(Download):
|
|
|
925
917
|
s2_processing_level: str = ""
|
|
926
918
|
s1_title_suffix: Optional[str] = None
|
|
927
919
|
# S2 common
|
|
928
|
-
if product.
|
|
920
|
+
if product.collection and "S2_MSI" in product.collection:
|
|
929
921
|
title_search: Optional[re.Match[str]] = re.search(
|
|
930
922
|
r"^\w+_\w+_(\w+)_(\w+)_(\w+)_(\w+)_(\w+)$",
|
|
931
923
|
product.properties["title"],
|
|
@@ -937,9 +929,9 @@ class AwsDownload(Download):
|
|
|
937
929
|
product.properties.get("originalSceneID", ""),
|
|
938
930
|
)
|
|
939
931
|
ds_dir = ds_dir_search.group(1) if ds_dir_search else 0
|
|
940
|
-
s2_processing_level = product.
|
|
932
|
+
s2_processing_level = product.collection.split("_")[-1]
|
|
941
933
|
# S1 common
|
|
942
|
-
elif product.
|
|
934
|
+
elif product.collection == "S1_SAR_GRD":
|
|
943
935
|
s1_title_suffix_search = re.search(
|
|
944
936
|
r"^.+_([A-Z0-9_]+_[A-Z0-9_]+_[A-Z0-9_]+_[A-Z0-9_]+)_\w+$",
|
|
945
937
|
product.properties["title"],
|
|
@@ -950,6 +942,12 @@ class AwsDownload(Download):
|
|
|
950
942
|
else None
|
|
951
943
|
)
|
|
952
944
|
|
|
945
|
+
# S1 polarization mode
|
|
946
|
+
if product_title := product.properties.get("title"):
|
|
947
|
+
polarization_mode = re.sub(r".{14}([A-Z]{2}).*", r"\1", product_title)
|
|
948
|
+
else:
|
|
949
|
+
polarization_mode = None
|
|
950
|
+
|
|
953
951
|
# S2 L2A Tile files -----------------------------------------------
|
|
954
952
|
if matched := S2L2A_TILE_IMG_REGEX.match(chunk.key):
|
|
955
953
|
found_dict = matched.groupdict()
|
|
@@ -1057,37 +1055,43 @@ class AwsDownload(Download):
|
|
|
1057
1055
|
found_dict = matched.groupdict()
|
|
1058
1056
|
product_path = "%s" % found_dict["file"]
|
|
1059
1057
|
# S1 --------------------------------------------------------------
|
|
1060
|
-
elif
|
|
1058
|
+
elif (
|
|
1059
|
+
matched := S1_CALIB_REGEX.match(chunk.key)
|
|
1060
|
+
) and polarization_mode is not None:
|
|
1061
1061
|
found_dict = matched.groupdict()
|
|
1062
1062
|
product_path = "annotation/calibration/%s-%s-%s-grd-%s-%s-%03d.xml" % (
|
|
1063
1063
|
found_dict["file_prefix"],
|
|
1064
|
-
product.properties["
|
|
1064
|
+
product.properties["platform"].lower(),
|
|
1065
1065
|
found_dict["file_beam"],
|
|
1066
1066
|
found_dict["file_pol"],
|
|
1067
1067
|
s1_title_suffix,
|
|
1068
|
-
S1_IMG_NB_PER_POLAR.get(
|
|
1068
|
+
S1_IMG_NB_PER_POLAR.get(polarization_mode, {}).get(
|
|
1069
1069
|
found_dict["file_pol"].upper(), 1
|
|
1070
1070
|
),
|
|
1071
1071
|
)
|
|
1072
|
-
elif
|
|
1072
|
+
elif (
|
|
1073
|
+
matched := S1_ANNOT_REGEX.match(chunk.key)
|
|
1074
|
+
) and polarization_mode is not None:
|
|
1073
1075
|
found_dict = matched.groupdict()
|
|
1074
1076
|
product_path = "annotation/%s-%s-grd-%s-%s-%03d.xml" % (
|
|
1075
|
-
product.properties["
|
|
1077
|
+
product.properties["platform"].lower(),
|
|
1076
1078
|
found_dict["file_beam"],
|
|
1077
1079
|
found_dict["file_pol"],
|
|
1078
1080
|
s1_title_suffix,
|
|
1079
|
-
S1_IMG_NB_PER_POLAR.get(
|
|
1081
|
+
S1_IMG_NB_PER_POLAR.get(polarization_mode, {}).get(
|
|
1080
1082
|
found_dict["file_pol"].upper(), 1
|
|
1081
1083
|
),
|
|
1082
1084
|
)
|
|
1083
|
-
elif
|
|
1085
|
+
elif (
|
|
1086
|
+
matched := S1_MEAS_REGEX.match(chunk.key)
|
|
1087
|
+
) and polarization_mode is not None:
|
|
1084
1088
|
found_dict = matched.groupdict()
|
|
1085
1089
|
product_path = "measurement/%s-%s-grd-%s-%s-%03d.%s" % (
|
|
1086
|
-
product.properties["
|
|
1090
|
+
product.properties["platform"].lower(),
|
|
1087
1091
|
found_dict["file_beam"],
|
|
1088
1092
|
found_dict["file_pol"],
|
|
1089
1093
|
s1_title_suffix,
|
|
1090
|
-
S1_IMG_NB_PER_POLAR.get(
|
|
1094
|
+
S1_IMG_NB_PER_POLAR.get(polarization_mode, {}).get(
|
|
1091
1095
|
found_dict["file_pol"].upper(), 1
|
|
1092
1096
|
),
|
|
1093
1097
|
found_dict["file_ext"],
|
|
@@ -1112,7 +1116,7 @@ class AwsDownload(Download):
|
|
|
1112
1116
|
def download_all(
|
|
1113
1117
|
self,
|
|
1114
1118
|
products: SearchResult,
|
|
1115
|
-
auth: Optional[Union[AuthBase,
|
|
1119
|
+
auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
|
|
1116
1120
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
1117
1121
|
progress_callback: Optional[ProgressCallback] = None,
|
|
1118
1122
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
eodag/plugins/download/base.py
CHANGED
|
@@ -53,7 +53,6 @@ if TYPE_CHECKING:
|
|
|
53
53
|
from eodag.api.product import EOProduct
|
|
54
54
|
from eodag.api.search_result import SearchResult
|
|
55
55
|
from eodag.config import PluginConfig
|
|
56
|
-
from eodag.types import S3SessionKwargs
|
|
57
56
|
from eodag.types.download_args import DownloadConf
|
|
58
57
|
from eodag.utils import DownloadedCallback, Unpack
|
|
59
58
|
|
|
@@ -84,8 +83,8 @@ class Download(PluginTopic):
|
|
|
84
83
|
(e.g. ``file:///tmp/product_folder`` on Linux or
|
|
85
84
|
``file:///C:/Users/username/AppData/Local/Temp`` on Windows)
|
|
86
85
|
- save a *record* file in the directory ``{output_dir}/.downloaded`` whose name
|
|
87
|
-
is built on the MD5 hash of the product's ``
|
|
88
|
-
attributes (``hashlib.md5((product.
|
|
86
|
+
is built on the MD5 hash of the product's ``collection`` and ``properties['id']``
|
|
87
|
+
attributes (``hashlib.md5((product.collection+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
|
|
89
88
|
and whose content is the product's ``remote_location`` attribute itself.
|
|
90
89
|
- not try to download a product whose ``location`` attribute already points to an
|
|
91
90
|
existing file/directory
|
|
@@ -104,7 +103,7 @@ class Download(PluginTopic):
|
|
|
104
103
|
def download(
|
|
105
104
|
self,
|
|
106
105
|
product: EOProduct,
|
|
107
|
-
auth: Optional[Union[AuthBase,
|
|
106
|
+
auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
|
|
108
107
|
progress_callback: Optional[ProgressCallback] = None,
|
|
109
108
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
110
109
|
timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
|
|
@@ -134,7 +133,7 @@ class Download(PluginTopic):
|
|
|
134
133
|
def _stream_download_dict(
|
|
135
134
|
self,
|
|
136
135
|
product: EOProduct,
|
|
137
|
-
auth: Optional[Union[AuthBase,
|
|
136
|
+
auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
|
|
138
137
|
byte_range: tuple[Optional[int], Optional[int]] = (None, None),
|
|
139
138
|
compress: Literal["zip", "raw", "auto"] = "auto",
|
|
140
139
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
@@ -279,14 +278,14 @@ class Download(PluginTopic):
|
|
|
279
278
|
def generate_record_hash(self, product: EOProduct) -> str:
|
|
280
279
|
"""Generate the record hash of the given product.
|
|
281
280
|
|
|
282
|
-
The MD5 hash is built from the product's ``
|
|
283
|
-
(``hashlib.md5((product.
|
|
281
|
+
The MD5 hash is built from the product's ``collection`` and ``properties['id']`` attributes
|
|
282
|
+
(``hashlib.md5((product.collection+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
|
|
284
283
|
|
|
285
284
|
:param product: The product to calculate the record hash
|
|
286
285
|
:returns: The MD5 hash
|
|
287
286
|
"""
|
|
288
|
-
# In some unit tests, `product.
|
|
289
|
-
product_hash = str(product.
|
|
287
|
+
# In some unit tests, `product.collection` is `None` and `product.properties["id"]` is `ìnt`
|
|
288
|
+
product_hash = str(product.collection) + "-" + str(product.properties["id"])
|
|
290
289
|
return hashlib.md5(product_hash.encode("utf-8")).hexdigest()
|
|
291
290
|
|
|
292
291
|
def _resolve_archive_depth(self, product_path: str) -> str:
|
|
@@ -445,7 +444,7 @@ class Download(PluginTopic):
|
|
|
445
444
|
def download_all(
|
|
446
445
|
self,
|
|
447
446
|
products: SearchResult,
|
|
448
|
-
auth: Optional[Union[AuthBase,
|
|
447
|
+
auth: Optional[Union[AuthBase, S3ServiceResource]] = None,
|
|
449
448
|
downloaded_callback: Optional[DownloadedCallback] = None,
|
|
450
449
|
progress_callback: Optional[ProgressCallback] = None,
|
|
451
450
|
wait: float = DEFAULT_DOWNLOAD_WAIT,
|
|
@@ -620,7 +619,7 @@ class Download(PluginTopic):
|
|
|
620
619
|
not_available_info = str(e)
|
|
621
620
|
else:
|
|
622
621
|
if (
|
|
623
|
-
product.properties.get("
|
|
622
|
+
product.properties.get("order:status", ONLINE_STATUS)
|
|
624
623
|
== ONLINE_STATUS
|
|
625
624
|
) or timeout <= 0:
|
|
626
625
|
return download
|
|
@@ -676,11 +675,11 @@ class Download(PluginTopic):
|
|
|
676
675
|
nb_info.display_html(retry_info)
|
|
677
676
|
sleep(wait_seconds)
|
|
678
677
|
elif datetime_now >= stop_time and timeout > 0:
|
|
679
|
-
if "
|
|
680
|
-
product.properties["
|
|
678
|
+
if "order:status" not in product.properties:
|
|
679
|
+
product.properties["order:status"] = "N/A status"
|
|
681
680
|
logger.info(not_available_info)
|
|
682
681
|
raise NotAvailableError(
|
|
683
|
-
f"{product.properties['title']} is not available ({product.properties['
|
|
682
|
+
f"{product.properties['title']} is not available ({product.properties['order:status']})"
|
|
684
683
|
f" and order was not successfull, timeout reached"
|
|
685
684
|
)
|
|
686
685
|
elif datetime_now >= stop_time:
|