eodag 3.0.0b3__py3-none-any.whl → 3.1.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.
Files changed (94) hide show
  1. eodag/api/core.py +347 -247
  2. eodag/api/product/_assets.py +44 -15
  3. eodag/api/product/_product.py +58 -47
  4. eodag/api/product/drivers/__init__.py +81 -4
  5. eodag/api/product/drivers/base.py +65 -4
  6. eodag/api/product/drivers/generic.py +65 -0
  7. eodag/api/product/drivers/sentinel1.py +97 -0
  8. eodag/api/product/drivers/sentinel2.py +95 -0
  9. eodag/api/product/metadata_mapping.py +129 -93
  10. eodag/api/search_result.py +28 -12
  11. eodag/cli.py +61 -24
  12. eodag/config.py +457 -167
  13. eodag/plugins/apis/base.py +10 -4
  14. eodag/plugins/apis/ecmwf.py +53 -23
  15. eodag/plugins/apis/usgs.py +41 -17
  16. eodag/plugins/authentication/aws_auth.py +30 -18
  17. eodag/plugins/authentication/base.py +14 -3
  18. eodag/plugins/authentication/generic.py +14 -3
  19. eodag/plugins/authentication/header.py +14 -6
  20. eodag/plugins/authentication/keycloak.py +44 -25
  21. eodag/plugins/authentication/oauth.py +18 -4
  22. eodag/plugins/authentication/openid_connect.py +192 -171
  23. eodag/plugins/authentication/qsauth.py +12 -4
  24. eodag/plugins/authentication/sas_auth.py +22 -5
  25. eodag/plugins/authentication/token.py +95 -17
  26. eodag/plugins/authentication/token_exchange.py +19 -19
  27. eodag/plugins/base.py +4 -4
  28. eodag/plugins/crunch/base.py +8 -5
  29. eodag/plugins/crunch/filter_date.py +9 -6
  30. eodag/plugins/crunch/filter_latest_intersect.py +9 -8
  31. eodag/plugins/crunch/filter_latest_tpl_name.py +8 -8
  32. eodag/plugins/crunch/filter_overlap.py +9 -11
  33. eodag/plugins/crunch/filter_property.py +10 -10
  34. eodag/plugins/download/aws.py +181 -105
  35. eodag/plugins/download/base.py +49 -67
  36. eodag/plugins/download/creodias_s3.py +40 -2
  37. eodag/plugins/download/http.py +247 -223
  38. eodag/plugins/download/s3rest.py +29 -28
  39. eodag/plugins/manager.py +176 -41
  40. eodag/plugins/search/__init__.py +6 -5
  41. eodag/plugins/search/base.py +123 -60
  42. eodag/plugins/search/build_search_result.py +1046 -355
  43. eodag/plugins/search/cop_marine.py +132 -39
  44. eodag/plugins/search/creodias_s3.py +19 -68
  45. eodag/plugins/search/csw.py +48 -8
  46. eodag/plugins/search/data_request_search.py +124 -23
  47. eodag/plugins/search/qssearch.py +531 -310
  48. eodag/plugins/search/stac_list_assets.py +85 -0
  49. eodag/plugins/search/static_stac_search.py +23 -24
  50. eodag/resources/ext_product_types.json +1 -1
  51. eodag/resources/product_types.yml +1295 -355
  52. eodag/resources/providers.yml +1819 -3010
  53. eodag/resources/stac.yml +3 -163
  54. eodag/resources/stac_api.yml +2 -2
  55. eodag/resources/user_conf_template.yml +115 -99
  56. eodag/rest/cache.py +2 -2
  57. eodag/rest/config.py +3 -4
  58. eodag/rest/constants.py +0 -1
  59. eodag/rest/core.py +157 -117
  60. eodag/rest/errors.py +181 -0
  61. eodag/rest/server.py +57 -339
  62. eodag/rest/stac.py +133 -581
  63. eodag/rest/types/collections_search.py +3 -3
  64. eodag/rest/types/eodag_search.py +41 -30
  65. eodag/rest/types/queryables.py +42 -32
  66. eodag/rest/types/stac_search.py +15 -16
  67. eodag/rest/utils/__init__.py +14 -21
  68. eodag/rest/utils/cql_evaluate.py +6 -6
  69. eodag/rest/utils/rfc3339.py +2 -2
  70. eodag/types/__init__.py +153 -32
  71. eodag/types/bbox.py +2 -2
  72. eodag/types/download_args.py +4 -4
  73. eodag/types/queryables.py +183 -73
  74. eodag/types/search_args.py +6 -6
  75. eodag/types/whoosh.py +127 -3
  76. eodag/utils/__init__.py +228 -106
  77. eodag/utils/exceptions.py +47 -26
  78. eodag/utils/import_system.py +2 -2
  79. eodag/utils/logging.py +37 -77
  80. eodag/utils/repr.py +65 -6
  81. eodag/utils/requests.py +13 -15
  82. eodag/utils/rest.py +2 -2
  83. eodag/utils/s3.py +231 -0
  84. eodag/utils/stac_reader.py +11 -11
  85. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/METADATA +81 -81
  86. eodag-3.1.0.dist-info/RECORD +113 -0
  87. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/WHEEL +1 -1
  88. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/entry_points.txt +5 -2
  89. eodag/resources/constraints/climate-dt.json +0 -13
  90. eodag/resources/constraints/extremes-dt.json +0 -8
  91. eodag/utils/constraints.py +0 -244
  92. eodag-3.0.0b3.dist-info/RECORD +0 -110
  93. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/LICENSE +0 -0
  94. {eodag-3.0.0b3.dist-info → eodag-3.1.0.dist-info}/top_level.txt +0 -0
eodag/types/__init__.py CHANGED
@@ -16,20 +16,34 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
  """EODAG types"""
19
+
19
20
  from __future__ import annotations
20
21
 
21
- from typing import Any, Dict, List, Literal, Optional, Tuple, TypedDict, Union
22
+ from typing import (
23
+ Annotated,
24
+ Any,
25
+ Literal,
26
+ Optional,
27
+ Type,
28
+ TypedDict,
29
+ Union,
30
+ get_args,
31
+ get_origin,
32
+ )
22
33
 
23
34
  from annotated_types import Gt, Lt
24
- from pydantic import Field
35
+ from pydantic import BaseModel, ConfigDict, Field, create_model
36
+ from pydantic.annotated_handlers import GetJsonSchemaHandler
25
37
  from pydantic.fields import FieldInfo
38
+ from pydantic.json_schema import JsonSchemaValue
39
+ from pydantic_core import CoreSchema
26
40
 
27
- from eodag.utils import Annotated, copy_deepcopy, get_args, get_origin
41
+ from eodag.utils import copy_deepcopy
28
42
  from eodag.utils.exceptions import ValidationError
29
43
 
30
44
  # Types mapping from JSON Schema and OpenAPI 3.1.0 specifications to Python
31
45
  # See https://spec.openapis.org/oas/v3.1.0#data-types
32
- JSON_TYPES_MAPPING: Dict[str, type] = {
46
+ JSON_TYPES_MAPPING: dict[str, type] = {
33
47
  "boolean": bool,
34
48
  "integer": int,
35
49
  "number": float,
@@ -40,7 +54,7 @@ JSON_TYPES_MAPPING: Dict[str, type] = {
40
54
  }
41
55
 
42
56
 
43
- def json_type_to_python(json_type: Union[str, List[str]]) -> type:
57
+ def json_type_to_python(json_type: Union[str, list[str]]) -> type:
44
58
  """Get python type from json type https://spec.openapis.org/oas/v3.1.0#data-types
45
59
 
46
60
  >>> json_type_to_python("number")
@@ -57,9 +71,9 @@ def json_type_to_python(json_type: Union[str, List[str]]) -> type:
57
71
  return type(None)
58
72
 
59
73
 
60
- def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> Tuple[str, Any]:
61
- """
62
- checks if the value from an Annotated object is a minimum or maximum
74
+ def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> tuple[str, Any]:
75
+ """Checks if the value from an Annotated object is a minimum or maximum
76
+
63
77
  :param type_info: info from Annotated
64
78
  :return: "min" or "max"
65
79
  """
@@ -71,10 +85,10 @@ def _get_min_or_max(type_info: Union[Lt, Gt, Any]) -> Tuple[str, Any]:
71
85
 
72
86
 
73
87
  def _get_type_info_from_annotated(
74
- annotated_type: Annotated[type, Any]
75
- ) -> Dict[str, Any]:
76
- """
77
- retrieves type information from an annotated object
88
+ annotated_type: Annotated[type, Any],
89
+ ) -> dict[str, Any]:
90
+ """Retrieves type information from an annotated object
91
+
78
92
  :param annotated_type: annotated object
79
93
  :return: dict containing type and min/max if available
80
94
  """
@@ -95,7 +109,7 @@ def _get_type_info_from_annotated(
95
109
 
96
110
  def python_type_to_json(
97
111
  python_type: type,
98
- ) -> Optional[Union[str, List[Dict[str, Any]]]]:
112
+ ) -> Optional[Union[str, list[dict[str, Any]]]]:
99
113
  """Get json type from python https://spec.openapis.org/oas/v3.1.0#data-types
100
114
 
101
115
  >>> python_type_to_json(int)
@@ -106,7 +120,8 @@ def python_type_to_json(
106
120
  :param python_type: the python type
107
121
  :returns: the json type
108
122
  """
109
- if get_origin(python_type) is Union:
123
+ origin = get_origin(python_type)
124
+ if origin is Union:
110
125
  json_type = list()
111
126
  for single_python_type in get_args(python_type):
112
127
  type_data = {}
@@ -126,14 +141,16 @@ def python_type_to_json(
126
141
  return list(JSON_TYPES_MAPPING.keys())[
127
142
  list(JSON_TYPES_MAPPING.values()).index(python_type)
128
143
  ]
129
- elif get_origin(python_type) == Annotated:
144
+ elif origin is Annotated:
130
145
  return [_get_type_info_from_annotated(python_type)]
146
+ elif origin is list:
147
+ raise NotImplementedError("Never completed")
131
148
  else:
132
149
  return None
133
150
 
134
151
 
135
152
  def json_field_definition_to_python(
136
- json_field_definition: Dict[str, Any],
153
+ json_field_definition: dict[str, Any],
137
154
  default_value: Optional[Any] = None,
138
155
  required: Optional[bool] = False,
139
156
  ) -> Annotated[Any, FieldInfo]:
@@ -161,29 +178,52 @@ def json_field_definition_to_python(
161
178
  title=json_field_definition.get("title", None),
162
179
  description=json_field_definition.get("description", None),
163
180
  pattern=json_field_definition.get("pattern", None),
181
+ le=json_field_definition.get("maximum"),
182
+ ge=json_field_definition.get("minimum"),
164
183
  )
165
184
 
166
- if "enum" in json_field_definition and (
167
- isinstance(json_field_definition["enum"], (list, set))
168
- ):
169
- python_type = Literal[tuple(sorted(json_field_definition["enum"]))] # type: ignore
185
+ enum = json_field_definition.get("enum")
186
+
187
+ if python_type in (list, set):
188
+ items = json_field_definition.get("items", None)
189
+ if isinstance(items, list):
190
+ python_type = tuple[ # type: ignore
191
+ tuple(
192
+ json_field_definition_to_python(item, required=required)
193
+ for item in items
194
+ )
195
+ ]
196
+ elif isinstance(items, dict):
197
+ enum = items.get("enum")
198
+
199
+ if enum:
200
+ literal = Literal[tuple(sorted(enum))] # type: ignore
201
+ python_type = list[literal] if python_type in (list, set) else literal # type: ignore
170
202
 
171
203
  if "$ref" in json_field_definition:
172
204
  field_type_kwargs["json_schema_extra"] = {"$ref": json_field_definition["$ref"]}
173
205
 
174
- if not required or default_value:
175
- return Annotated[python_type, Field(default=default_value, **field_type_kwargs)]
176
- else:
177
- return Annotated[python_type, Field(..., **field_type_kwargs)]
206
+ metadata = [
207
+ python_type,
208
+ Field(
209
+ default_value if not required or default_value is not None else ...,
210
+ **field_type_kwargs,
211
+ ),
212
+ ]
213
+
214
+ if required:
215
+ metadata.append("json_schema_required")
216
+
217
+ return Annotated[tuple(metadata)]
178
218
 
179
219
 
180
220
  def python_field_definition_to_json(
181
- python_field_definition: Annotated[Any, FieldInfo]
182
- ) -> Dict[str, Any]:
221
+ python_field_definition: Annotated[Any, FieldInfo],
222
+ ) -> dict[str, Any]:
183
223
  """Get json field definition from python `typing.Annotated`
184
224
 
185
225
  >>> from pydantic import Field
186
- >>> from eodag.utils import Annotated
226
+ >>> from typing import Annotated
187
227
  >>> python_field_definition_to_json(
188
228
  ... Annotated[
189
229
  ... Optional[str],
@@ -200,7 +240,7 @@ def python_field_definition_to_json(
200
240
  "%s must be an instance of Annotated" % python_field_definition
201
241
  )
202
242
 
203
- json_field_definition: Dict[str, Any] = dict()
243
+ json_field_definition: dict[str, Any] = dict()
204
244
 
205
245
  python_field_args = get_args(python_field_definition)
206
246
 
@@ -240,6 +280,7 @@ def python_field_definition_to_json(
240
280
  json_field_definition["max"] = [
241
281
  row["max"] if "max" in row else None for row in field_type
242
282
  ]
283
+
243
284
  if "min" in json_field_definition and json_field_definition["min"].count(
244
285
  None
245
286
  ) == len(json_field_definition["min"]):
@@ -279,8 +320,8 @@ def python_field_definition_to_json(
279
320
 
280
321
 
281
322
  def model_fields_to_annotated(
282
- model_fields: Dict[str, FieldInfo]
283
- ) -> Dict[str, Annotated[Any, FieldInfo]]:
323
+ model_fields: dict[str, FieldInfo],
324
+ ) -> dict[str, Annotated[Any, FieldInfo]]:
284
325
  """Convert BaseModel.model_fields from FieldInfo to Annotated
285
326
 
286
327
  >>> from pydantic import create_model
@@ -294,7 +335,7 @@ def model_fields_to_annotated(
294
335
  :param model_fields: BaseModel.model_fields to convert
295
336
  :returns: Annotated tuple usable as create_model argument
296
337
  """
297
- annotated_model_fields = dict()
338
+ annotated_model_fields: dict[str, Annotated[Any, FieldInfo]] = dict()
298
339
  for param, field_info in model_fields.items():
299
340
  field_type = field_info.annotation or type(None)
300
341
  new_field_info = copy_deepcopy(field_info)
@@ -303,6 +344,77 @@ def model_fields_to_annotated(
303
344
  return annotated_model_fields
304
345
 
305
346
 
347
+ class BaseModelCustomJsonSchema(BaseModel):
348
+ """Base class for generated models with custom json schema."""
349
+
350
+ @classmethod
351
+ def __get_pydantic_json_schema__(
352
+ cls: Type[BaseModel], core_schema: CoreSchema, handler: GetJsonSchemaHandler
353
+ ) -> JsonSchemaValue:
354
+ """
355
+ Custom get json schema method to handle required fields with default values.
356
+ This is not supported by Pydantic by default.
357
+ It requires the field to be marked with the key "json_schema_required" in the metadata dict.
358
+ Example: Annotated[str, "json_schema_required"]
359
+ """
360
+ json_schema = handler.resolve_ref_schema(handler(core_schema))
361
+
362
+ json_schema["required"] = [
363
+ key
364
+ for key, field_info in cls.model_fields.items()
365
+ if "json_schema_required" in field_info.metadata
366
+ ]
367
+
368
+ return json_schema
369
+
370
+ model_config = ConfigDict(arbitrary_types_allowed=True)
371
+
372
+
373
+ def annotated_dict_to_model(
374
+ model_name: str, annotated_fields: dict[str, Annotated[Any, FieldInfo]]
375
+ ) -> BaseModel:
376
+ """Convert a dictionary of Annotated values to a Pydantic BaseModel.
377
+
378
+ >>> from pydantic import Field
379
+ >>> annotated_fields = {
380
+ ... "field1": Annotated[str, Field(description="A string field"), "json_schema_required"],
381
+ ... "field2": Annotated[int, Field(default=42, description="An integer field")],
382
+ ... }
383
+ >>> model = annotated_dict_to_model("TestModel", annotated_fields)
384
+ >>> json_schema = model.model_json_schema()
385
+ >>> json_schema["required"]
386
+ ['field1']
387
+ >>> json_schema["properties"]["field1"]
388
+ {'description': 'A string field', 'title': 'Field1', 'type': 'string'}
389
+ >>> json_schema["properties"]["field2"]
390
+ {'default': 42, 'description': 'An integer field', 'title': 'Field2', 'type': 'integer'}
391
+ >>> json_schema["title"]
392
+ 'TestModel'
393
+ >>> json_schema["type"]
394
+ 'object'
395
+
396
+ :param model_name: name of the model to be created
397
+ :param annotated_fields: dict containing the parameters and annotated values that should become
398
+ the properties of the model
399
+ :returns: pydantic model
400
+ """
401
+ fields = {}
402
+ for name, field in annotated_fields.items():
403
+ base_type, field_info, *metadata = get_args(field)
404
+ fields[name] = (
405
+ Annotated[tuple([base_type] + metadata)] if metadata else base_type,
406
+ field_info,
407
+ )
408
+
409
+ custom_model = create_model(
410
+ model_name,
411
+ __base__=BaseModelCustomJsonSchema,
412
+ **fields, # type: ignore
413
+ )
414
+
415
+ return custom_model
416
+
417
+
306
418
  class ProviderSortables(TypedDict):
307
419
  """A class representing sortable parameter(s) of a provider and the allowed
308
420
  maximum number of used sortable(s) in a search request with the provider
@@ -311,5 +423,14 @@ class ProviderSortables(TypedDict):
311
423
  :param max_sort_params: (optional) The allowed maximum number of sortable(s) in a search request with the provider
312
424
  """
313
425
 
314
- sortables: List[str]
426
+ sortables: list[str]
315
427
  max_sort_params: Annotated[Optional[int], Gt(0)]
428
+
429
+
430
+ class S3SessionKwargs(TypedDict, total=False):
431
+ """A class representing available keyword arguments to pass to :class:`boto3.session.Session` for authentication"""
432
+
433
+ aws_access_key_id: Optional[str]
434
+ aws_secret_access_key: Optional[str]
435
+ aws_session_token: Optional[str]
436
+ profile_name: Optional[str]
eodag/types/bbox.py CHANGED
@@ -15,14 +15,14 @@
15
15
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
- from typing import Dict, List, Tuple, Union
18
+ from typing import Union
19
19
 
20
20
  from pydantic import BaseModel, ValidationInfo, field_validator
21
21
  from shapely.geometry.polygon import Polygon
22
22
 
23
23
  NumType = Union[float, int]
24
24
  BBoxArgs = Union[
25
- List[NumType], Tuple[NumType, NumType, NumType, NumType], Dict[str, NumType]
25
+ list[NumType], tuple[NumType, NumType, NumType, NumType], dict[str, NumType]
26
26
  ]
27
27
 
28
28
 
@@ -17,13 +17,13 @@
17
17
  # limitations under the License.
18
18
  from __future__ import annotations
19
19
 
20
- from typing import Dict, Optional, TypedDict
20
+ from typing import Optional, TypedDict, Union
21
21
 
22
22
 
23
23
  class DownloadConf(TypedDict, total=False):
24
24
  """Download configuration
25
25
 
26
- :cvar output_prefix: where to store downloaded products, as an absolute file path
26
+ :cvar output_dir: where to store downloaded products, as an absolute file path
27
27
  (Default: local temporary directory)
28
28
  :cvar output_extension: downloaded file extension
29
29
  :cvar extract: whether to extract the downloaded products, only applies to archived products
@@ -33,8 +33,8 @@ class DownloadConf(TypedDict, total=False):
33
33
  """
34
34
 
35
35
  output_dir: str
36
- output_extension: str
36
+ output_extension: Union[str, None]
37
37
  extract: bool
38
- dl_url_params: Dict[str, str]
38
+ dl_url_params: dict[str, str]
39
39
  delete_archive: bool
40
40
  asset: Optional[str]
eodag/types/queryables.py CHANGED
@@ -15,12 +15,20 @@
15
15
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
- from typing import Optional
18
+ from __future__ import annotations
19
+
20
+ from collections import UserDict
21
+ from datetime import date, datetime
22
+ from typing import Annotated, Any, Optional, Union
19
23
 
20
24
  from annotated_types import Lt
21
25
  from pydantic import BaseModel, Field
26
+ from pydantic.fields import FieldInfo
22
27
  from pydantic.types import PositiveInt
23
- from typing_extensions import Annotated
28
+ from pydantic_core import PydanticUndefined
29
+
30
+ from eodag.types import annotated_dict_to_model, model_fields_to_annotated
31
+ from eodag.utils.repr import remove_class_repr, shorter_type_repr
24
32
 
25
33
  Percentage = Annotated[PositiveInt, Lt(100)]
26
34
 
@@ -29,17 +37,11 @@ class CommonQueryables(BaseModel):
29
37
  """A class representing search common queryable properties."""
30
38
 
31
39
  productType: Annotated[str, Field()]
32
- id: Annotated[Optional[str], Field(None)]
33
- start: Annotated[Optional[str], Field(None, alias="startTimeFromAscendingNode")]
34
- end: Annotated[Optional[str], Field(None, alias="completionTimeFromAscendingNode")]
35
- geom: Annotated[Optional[str], Field(None, alias="geometry")]
36
40
 
37
41
  @classmethod
38
42
  def get_queryable_from_alias(cls, value: str) -> str:
39
43
  """Get queryable parameter from alias
40
44
 
41
- >>> CommonQueryables.get_queryable_from_alias('startTimeFromAscendingNode')
42
- 'start'
43
45
  >>> CommonQueryables.get_queryable_from_alias('productType')
44
46
  'productType'
45
47
  """
@@ -50,75 +52,183 @@ class CommonQueryables(BaseModel):
50
52
  }
51
53
  return alias_map.get(value, value)
52
54
 
55
+ @classmethod
56
+ def get_with_default(
57
+ cls, field: str, default: Optional[Any]
58
+ ) -> Annotated[Any, FieldInfo]:
59
+ """Get field and set default value."""
60
+ annotated_fields = model_fields_to_annotated(cls.model_fields)
61
+ f = annotated_fields[field]
62
+ if default is None:
63
+ return f
64
+ f.__metadata__[0].default = default
65
+ return f
66
+
53
67
 
54
68
  class Queryables(CommonQueryables):
55
- """A class representing all search queryable properties."""
69
+ """A class representing all search queryable properties.
56
70
 
57
- uid: Annotated[Optional[str], Field(None)]
71
+ Parameters default value is set to ``None`` to have them not required.
72
+ """
73
+
74
+ start: Annotated[
75
+ Union[datetime, date], Field(None, alias="startTimeFromAscendingNode")
76
+ ]
77
+ end: Annotated[
78
+ Union[datetime, date], Field(None, alias="completionTimeFromAscendingNode")
79
+ ]
80
+ geom: Annotated[str, Field(None, alias="geometry")]
81
+ uid: Annotated[str, Field(None)]
58
82
  # OpenSearch Parameters for Collection Search (Table 3)
59
- doi: Annotated[Optional[str], Field(None)]
60
- platform: Annotated[Optional[str], Field(None)]
61
- platformSerialIdentifier: Annotated[Optional[str], Field(None)]
62
- instrument: Annotated[Optional[str], Field(None)]
63
- sensorType: Annotated[Optional[str], Field(None)]
64
- compositeType: Annotated[Optional[str], Field(None)]
65
- processingLevel: Annotated[Optional[str], Field(None)]
66
- orbitType: Annotated[Optional[str], Field(None)]
67
- spectralRange: Annotated[Optional[str], Field(None)]
68
- wavelengths: Annotated[Optional[str], Field(None)]
69
- hasSecurityConstraints: Annotated[Optional[str], Field(None)]
70
- dissemination: Annotated[Optional[str], Field(None)]
83
+ doi: Annotated[str, Field(None)]
84
+ platform: Annotated[str, Field(None)]
85
+ platformSerialIdentifier: Annotated[str, Field(None)]
86
+ instrument: Annotated[str, Field(None)]
87
+ sensorType: Annotated[str, Field(None)]
88
+ compositeType: Annotated[str, Field(None)]
89
+ processingLevel: Annotated[str, Field(None)]
90
+ orbitType: Annotated[str, Field(None)]
91
+ spectralRange: Annotated[str, Field(None)]
92
+ wavelengths: Annotated[str, Field(None)]
93
+ hasSecurityConstraints: Annotated[str, Field(None)]
94
+ dissemination: Annotated[str, Field(None)]
71
95
  # INSPIRE obligated OpenSearch Parameters for Collection Search (Table 4)
72
- title: Annotated[Optional[str], Field(None)]
73
- topicCategory: Annotated[Optional[str], Field(None)]
74
- keyword: Annotated[Optional[str], Field(None)]
75
- abstract: Annotated[Optional[str], Field(None)]
76
- resolution: Annotated[Optional[int], Field(None)]
77
- organisationName: Annotated[Optional[str], Field(None)]
78
- organisationRole: Annotated[Optional[str], Field(None)]
79
- publicationDate: Annotated[Optional[str], Field(None)]
80
- lineage: Annotated[Optional[str], Field(None)]
81
- useLimitation: Annotated[Optional[str], Field(None)]
82
- accessConstraint: Annotated[Optional[str], Field(None)]
83
- otherConstraint: Annotated[Optional[str], Field(None)]
84
- classification: Annotated[Optional[str], Field(None)]
85
- language: Annotated[Optional[str], Field(None)]
86
- specification: Annotated[Optional[str], Field(None)]
96
+ title: Annotated[str, Field(None)]
97
+ topicCategory: Annotated[str, Field(None)]
98
+ keyword: Annotated[str, Field(None)]
99
+ abstract: Annotated[str, Field(None)]
100
+ resolution: Annotated[int, Field(None)]
101
+ organisationName: Annotated[str, Field(None)]
102
+ organisationRole: Annotated[str, Field(None)]
103
+ publicationDate: Annotated[str, Field(None)]
104
+ lineage: Annotated[str, Field(None)]
105
+ useLimitation: Annotated[str, Field(None)]
106
+ accessConstraint: Annotated[str, Field(None)]
107
+ otherConstraint: Annotated[str, Field(None)]
108
+ classification: Annotated[str, Field(None)]
109
+ language: Annotated[str, Field(None)]
110
+ specification: Annotated[str, Field(None)]
87
111
  # OpenSearch Parameters for Product Search (Table 5)
88
- parentIdentifier: Annotated[Optional[str], Field(None)]
89
- productionStatus: Annotated[Optional[str], Field(None)]
90
- acquisitionType: Annotated[Optional[str], Field(None)]
91
- orbitNumber: Annotated[Optional[int], Field(None)]
92
- orbitDirection: Annotated[Optional[str], Field(None)]
93
- track: Annotated[Optional[str], Field(None)]
94
- frame: Annotated[Optional[str], Field(None)]
95
- swathIdentifier: Annotated[Optional[str], Field(None)]
96
- cloudCover: Annotated[Optional[Percentage], Field(None)]
97
- snowCover: Annotated[Optional[Percentage], Field(None)]
98
- lowestLocation: Annotated[Optional[str], Field(None)]
99
- highestLocation: Annotated[Optional[str], Field(None)]
100
- productVersion: Annotated[Optional[str], Field(None)]
101
- productQualityStatus: Annotated[Optional[str], Field(None)]
102
- productQualityDegradationTag: Annotated[Optional[str], Field(None)]
103
- processorName: Annotated[Optional[str], Field(None)]
104
- processingCenter: Annotated[Optional[str], Field(None)]
105
- creationDate: Annotated[Optional[str], Field(None)]
106
- modificationDate: Annotated[Optional[str], Field(None)]
107
- processingDate: Annotated[Optional[str], Field(None)]
108
- sensorMode: Annotated[Optional[str], Field(None)]
109
- archivingCenter: Annotated[Optional[str], Field(None)]
110
- processingMode: Annotated[Optional[str], Field(None)]
112
+ parentIdentifier: Annotated[str, Field(None)]
113
+ productionStatus: Annotated[str, Field(None)]
114
+ acquisitionType: Annotated[str, Field(None)]
115
+ orbitNumber: Annotated[int, Field(None)]
116
+ orbitDirection: Annotated[str, Field(None)]
117
+ track: Annotated[str, Field(None)]
118
+ frame: Annotated[str, Field(None)]
119
+ swathIdentifier: Annotated[str, Field(None)]
120
+ cloudCover: Annotated[Percentage, Field(None)]
121
+ snowCover: Annotated[Percentage, Field(None)]
122
+ lowestLocation: Annotated[str, Field(None)]
123
+ highestLocation: Annotated[str, Field(None)]
124
+ productVersion: Annotated[str, Field(None)]
125
+ productQualityStatus: Annotated[str, Field(None)]
126
+ productQualityDegradationTag: Annotated[str, Field(None)]
127
+ processorName: Annotated[str, Field(None)]
128
+ processingCenter: Annotated[str, Field(None)]
129
+ creationDate: Annotated[str, Field(None)]
130
+ modificationDate: Annotated[str, Field(None)]
131
+ processingDate: Annotated[str, Field(None)]
132
+ sensorMode: Annotated[str, Field(None)]
133
+ archivingCenter: Annotated[str, Field(None)]
134
+ processingMode: Annotated[str, Field(None)]
111
135
  # OpenSearch Parameters for Acquistion Parameters Search (Table 6)
112
- availabilityTime: Annotated[Optional[str], Field(None)]
113
- acquisitionStation: Annotated[Optional[str], Field(None)]
114
- acquisitionSubType: Annotated[Optional[str], Field(None)]
115
- illuminationAzimuthAngle: Annotated[Optional[str], Field(None)]
116
- illuminationZenithAngle: Annotated[Optional[str], Field(None)]
117
- illuminationElevationAngle: Annotated[Optional[str], Field(None)]
118
- polarizationMode: Annotated[Optional[str], Field(None)]
119
- polarizationChannels: Annotated[Optional[str], Field(None)]
120
- antennaLookDirection: Annotated[Optional[str], Field(None)]
121
- minimumIncidenceAngle: Annotated[Optional[float], Field(None)]
122
- maximumIncidenceAngle: Annotated[Optional[float], Field(None)]
123
- dopplerFrequency: Annotated[Optional[float], Field(None)]
124
- incidenceAngleVariation: Annotated[Optional[float], Field(None)]
136
+ availabilityTime: Annotated[str, Field(None)]
137
+ acquisitionStation: Annotated[str, Field(None)]
138
+ acquisitionSubType: Annotated[str, Field(None)]
139
+ illuminationAzimuthAngle: Annotated[str, Field(None)]
140
+ illuminationZenithAngle: Annotated[str, Field(None)]
141
+ illuminationElevationAngle: Annotated[str, Field(None)]
142
+ polarizationMode: Annotated[str, Field(None)]
143
+ polarizationChannels: Annotated[str, Field(None)]
144
+ antennaLookDirection: Annotated[str, Field(None)]
145
+ minimumIncidenceAngle: Annotated[float, Field(None)]
146
+ maximumIncidenceAngle: Annotated[float, Field(None)]
147
+ dopplerFrequency: Annotated[float, Field(None)]
148
+ incidenceAngleVariation: Annotated[float, Field(None)]
149
+
150
+
151
+ class QueryablesDict(UserDict[str, Any]):
152
+ """Class inheriting from UserDict which contains queryables with their annotated type;
153
+
154
+ :param additional_properties: if additional properties (properties not given in EODAG config)
155
+ are allowed
156
+ :param kwargs: named arguments to initialise the dict (queryable keys + annotated types)
157
+ """
158
+
159
+ additional_properties: bool = Field(True)
160
+ additional_information: str = Field("")
161
+
162
+ def __init__(
163
+ self,
164
+ additional_properties: bool = True,
165
+ additional_information: str = "",
166
+ **kwargs: Any,
167
+ ):
168
+ self.additional_properties = additional_properties
169
+ self.additional_information = additional_information
170
+ super().__init__(kwargs)
171
+
172
+ def _repr_html_(self, embedded: bool = False) -> str:
173
+ add_info = (
174
+ f" additional_information={self.additional_information}"
175
+ if self.additional_information
176
+ else ""
177
+ )
178
+ thead = (
179
+ f"""<thead><tr><td style='text-align: left; color: grey;'>
180
+ {type(self).__name__}&ensp;({len(self)})&ensp;-&ensp;additional_properties={
181
+ self.additional_properties}
182
+ """
183
+ + add_info
184
+ + "</td></tr></thead>"
185
+ if not embedded
186
+ else ""
187
+ )
188
+ tr_style = "style='background-color: transparent;'" if embedded else ""
189
+ return (
190
+ f"<table>{thead}<tbody>"
191
+ + "".join(
192
+ [
193
+ f"""<tr {tr_style}><td style='text-align: left;'>
194
+ <details><summary style='color: grey;'>
195
+ <span style='color: black'>'{k}'</span>:&ensp;
196
+ typing.Annotated[{
197
+ "<span style='color: black'>" + shorter_type_repr(v.__args__[0]) + "</span>,&ensp;"
198
+ }
199
+ FieldInfo({"'default': '<span style='color: black'>"
200
+ + str(v.__metadata__[0].get_default()) + "</span>',&ensp;"
201
+ if v.__metadata__[0].get_default()
202
+ and v.__metadata__[0].get_default() != PydanticUndefined else ""}
203
+ {"'required': <span style='color: black'>"
204
+ + str(v.__metadata__[0].is_required()) + "</span>,"}
205
+ ...
206
+ )]
207
+ </summary>
208
+ <span style='color: grey'>typing.Annotated[</span><table style='margin: 0;'>
209
+ <tr style='background-color: transparent;'>
210
+ <td style='padding: 5px 0 0 10px; text-align: left; vertical-align:top;'>
211
+ {remove_class_repr(str(v.__args__[0]))},</td>
212
+ </tr><tr style='background-color: transparent;'>
213
+ <td style='padding: 5px 0 0 10px; text-align: left; vertical-align:top;'>
214
+ {v.__metadata__[0].__repr__()}</td>
215
+ </tr>
216
+ </table><span style='color: grey'>]</span>
217
+ </details>
218
+ </td></tr>
219
+ """
220
+ for k, v in self.items()
221
+ ]
222
+ )
223
+ + "</tbody></table>"
224
+ )
225
+
226
+ def get_model(self, model_name: str = "Queryables") -> BaseModel:
227
+ """
228
+ Converts object from :class:`eodag.api.product.QueryablesDict` to :class:`pydantic.BaseModel`
229
+ so that validation can be performed
230
+
231
+ :param model_name: name used for :class:`pydantic.BaseModel` creation
232
+ :return: pydantic BaseModel of the queryables dict
233
+ """
234
+ return annotated_dict_to_model(model_name, self.data)