datacosmos 0.0.17__py3-none-any.whl → 0.0.19__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.

Potentially problematic release.


This version of datacosmos might be problematic. Click here for more details.

@@ -7,7 +7,7 @@ import requests
7
7
 
8
8
  from datacosmos.auth.base_authenticator import AuthResult, BaseAuthenticator
9
9
  from datacosmos.auth.local_token_fetcher import LocalTokenFetcher
10
- from datacosmos.exceptions.datacosmos_exception import DatacosmosException
10
+ from datacosmos.exceptions.datacosmos_error import DatacosmosError
11
11
 
12
12
 
13
13
  class LocalAuthenticator(BaseAuthenticator):
@@ -36,9 +36,7 @@ class LocalAuthenticator(BaseAuthenticator):
36
36
  token_file=Path(auth.cache_file).expanduser(),
37
37
  )
38
38
  except Exception as e:
39
- raise DatacosmosException(
40
- f"Failed to initialize LocalTokenFetcher: {e}"
41
- ) from e
39
+ raise DatacosmosError(f"Failed to initialize LocalTokenFetcher: {e}") from e
42
40
 
43
41
  def authenticate_and_build_session(self) -> AuthResult:
44
42
  """Builds an authenticated session using the local token fetcher."""
@@ -52,7 +50,7 @@ class LocalAuthenticator(BaseAuthenticator):
52
50
  http_client=http_client, token=token, token_expiry=token_expiry
53
51
  )
54
52
  except Exception as e:
55
- raise DatacosmosException(f"Local authentication failed: {e}") from e
53
+ raise DatacosmosError(f"Local authentication failed: {e}") from e
56
54
 
57
55
  def refresh_token(self) -> AuthResult:
58
56
  """Refreshes the local token non-interactively."""
@@ -69,4 +67,4 @@ class LocalAuthenticator(BaseAuthenticator):
69
67
  http_client=http_client, token=token, token_expiry=token_expiry
70
68
  )
71
69
  except Exception as e:
72
- raise DatacosmosException(f"Local token refresh failed: {e}") from e
70
+ raise DatacosmosError(f"Local token refresh failed: {e}") from e
@@ -13,7 +13,7 @@ from tenacity import (
13
13
  )
14
14
 
15
15
  from datacosmos.auth.base_authenticator import AuthResult, BaseAuthenticator
16
- from datacosmos.exceptions.datacosmos_exception import DatacosmosException
16
+ from datacosmos.exceptions.datacosmos_error import DatacosmosError
17
17
 
18
18
 
19
19
  class M2MAuthenticator(BaseAuthenticator):
@@ -52,9 +52,9 @@ class M2MAuthenticator(BaseAuthenticator):
52
52
  http_client=http_client, token=token, token_expiry=token_expiry
53
53
  )
54
54
  except (HTTPError, ConnectionError, Timeout) as e:
55
- raise DatacosmosException(f"M2M authentication failed: {e}") from e
55
+ raise DatacosmosError(f"M2M authentication failed: {e}") from e
56
56
  except RequestException as e:
57
- raise DatacosmosException(
57
+ raise DatacosmosError(
58
58
  f"Unexpected request failure during M2M authentication: {e}"
59
59
  ) from e
60
60
 
@@ -20,7 +20,7 @@ from datacosmos.auth.base_authenticator import BaseAuthenticator
20
20
  from datacosmos.auth.local_authenticator import LocalAuthenticator
21
21
  from datacosmos.auth.m2m_authenticator import M2MAuthenticator
22
22
  from datacosmos.config.config import Config
23
- from datacosmos.exceptions.datacosmos_exception import DatacosmosException
23
+ from datacosmos.exceptions.datacosmos_error import DatacosmosError
24
24
 
25
25
  _log = logging.getLogger(__name__)
26
26
 
@@ -75,9 +75,7 @@ class DatacosmosClient:
75
75
  try:
76
76
  return Config.model_validate(cfg)
77
77
  except Exception as e:
78
- raise DatacosmosException(
79
- "Invalid config provided to DatacosmosClient"
80
- ) from e
78
+ raise DatacosmosError("Invalid config provided to DatacosmosClient") from e
81
79
 
82
80
  def _init_with_injected_session(
83
81
  self, http_session: requests.Session | OAuth2Session
@@ -88,7 +86,7 @@ class DatacosmosClient:
88
86
  token_data = self._extract_token_data(http_session)
89
87
  self.token = token_data.get("access_token")
90
88
  if not self.token:
91
- raise DatacosmosException(
89
+ raise DatacosmosError(
92
90
  "Failed to extract access token from injected session"
93
91
  )
94
92
  self.token_expiry = self._compute_expiry(
@@ -103,11 +101,11 @@ class DatacosmosClient:
103
101
  if isinstance(http_session, requests.Session):
104
102
  auth_header = http_session.headers.get("Authorization", "")
105
103
  if not auth_header.startswith("Bearer "):
106
- raise DatacosmosException(
104
+ raise DatacosmosError(
107
105
  "Injected requests.Session must include a 'Bearer' token in its headers"
108
106
  )
109
107
  return {"access_token": auth_header.split(" ", 1)[1]}
110
- raise DatacosmosException(f"Unsupported session type: {type(http_session)}")
108
+ raise DatacosmosError(f"Unsupported session type: {type(http_session)}")
111
109
 
112
110
  def _compute_expiry(
113
111
  self,
@@ -134,7 +132,7 @@ class DatacosmosClient:
134
132
  elif auth_type == "local":
135
133
  self._authenticator = LocalAuthenticator(self.config)
136
134
  else:
137
- raise DatacosmosException(f"Unsupported authentication type: {auth_type}")
135
+ raise DatacosmosError(f"Unsupported authentication type: {auth_type}")
138
136
 
139
137
  auth_result = self._authenticator.authenticate_and_build_session()
140
138
  self.token = auth_result.token
@@ -166,7 +164,7 @@ class DatacosmosClient:
166
164
  {"Authorization": f"Bearer {self.token}"}
167
165
  )
168
166
  else:
169
- raise DatacosmosException(
167
+ raise DatacosmosError(
170
168
  "Cannot refresh token, no authenticator initialized."
171
169
  )
172
170
 
@@ -196,7 +194,7 @@ class DatacosmosClient:
196
194
  requests.Response: The HTTP response.
197
195
 
198
196
  Raises:
199
- DatacosmosException: For any HTTP or request-related errors.
197
+ DatacosmosError: For any HTTP or request-related errors.
200
198
  """
201
199
  self._refresh_token_if_needed()
202
200
 
@@ -228,16 +226,16 @@ class DatacosmosClient:
228
226
  retry_response.raise_for_status()
229
227
  return retry_response
230
228
  except HTTPError as e:
231
- raise DatacosmosException(
229
+ raise DatacosmosError(
232
230
  f"HTTP error during {method.upper()} request to {url} after refresh",
233
231
  response=e.response,
234
232
  ) from e
235
- raise DatacosmosException(
233
+ raise DatacosmosError(
236
234
  f"HTTP error during {method.upper()} request to {url}",
237
235
  response=getattr(e, "response", None),
238
236
  ) from e
239
237
  except RequestException as e:
240
- raise DatacosmosException(
238
+ raise DatacosmosError(
241
239
  f"Unexpected request failure during {method.upper()} request to {url}: {e}"
242
240
  ) from e
243
241
 
@@ -1 +1,9 @@
1
1
  """Exceptions for the datacosmos package."""
2
+
3
+ from .datacosmos_error import DatacosmosError
4
+ from .stac_validation_error import StacValidationError
5
+
6
+ __all__ = [
7
+ "DatacosmosError",
8
+ "StacValidationError",
9
+ ]
@@ -6,11 +6,11 @@ from requests import Response
6
6
  from requests.exceptions import RequestException
7
7
 
8
8
 
9
- class DatacosmosException(RequestException):
9
+ class DatacosmosError(RequestException):
10
10
  """Base exception class for all Datacosmos SDK exceptions."""
11
11
 
12
12
  def __init__(self, message: str, response: Optional[Response] = None):
13
- """Initialize DatacosmosException.
13
+ """Initialize DatacosmosError.
14
14
 
15
15
  Args:
16
16
  message (str): The error message.
@@ -0,0 +1,8 @@
1
+ """Custom exception for STAC validation errors."""
2
+ from datacosmos.exceptions.datacosmos_error import DatacosmosError
3
+
4
+
5
+ class StacValidationError(DatacosmosError):
6
+ """Exception raised for errors in STAC item validation."""
7
+
8
+ pass
@@ -6,7 +6,7 @@ from pystac import Collection, Extent, SpatialExtent, TemporalExtent
6
6
  from pystac.utils import str_to_datetime
7
7
 
8
8
  from datacosmos.datacosmos_client import DatacosmosClient
9
- from datacosmos.exceptions.datacosmos_exception import DatacosmosException
9
+ from datacosmos.exceptions.datacosmos_error import DatacosmosError
10
10
  from datacosmos.stac.collection.models.collection_update import CollectionUpdate
11
11
  from datacosmos.utils.http_response.check_api_response import check_api_response
12
12
 
@@ -147,7 +147,7 @@ class CollectionClient:
147
147
  try:
148
148
  return next_href.split("?")[1].split("=")[-1]
149
149
  except (IndexError, AttributeError) as e:
150
- raise DatacosmosException(
150
+ raise DatacosmosError(
151
151
  f"Failed to parse pagination token from {next_href}",
152
152
  response=e.response,
153
153
  ) from e
@@ -3,7 +3,19 @@
3
3
  from enum import Enum
4
4
 
5
5
 
6
- class ProcessingLevel(Enum):
6
+ class CaseInsensitiveEnum(Enum):
7
+ """An enum that can be initialized with case-insensitive strings."""
8
+
9
+ @classmethod
10
+ def _missing_(cls, value: object):
11
+ if isinstance(value, str):
12
+ for member in cls:
13
+ if member.value.lower() == value.lower():
14
+ return member
15
+ return super()._missing_(value)
16
+
17
+
18
+ class ProcessingLevel(CaseInsensitiveEnum):
7
19
  """Enum class for the processing levels of the data."""
8
20
 
9
21
  RAW = "RAW"
@@ -8,7 +8,7 @@ from typing import Generator, Optional
8
8
  from pystac import Item
9
9
 
10
10
  from datacosmos.datacosmos_client import DatacosmosClient
11
- from datacosmos.exceptions.datacosmos_exception import DatacosmosException
11
+ from datacosmos.exceptions import DatacosmosError, StacValidationError
12
12
  from datacosmos.stac.item.models.catalog_search_parameters import (
13
13
  CatalogSearchParameters,
14
14
  )
@@ -72,17 +72,10 @@ class ItemClient:
72
72
 
73
73
  Raises:
74
74
  ValueError: If the item has no collection set.
75
+ StacValidationError: If the collection ID in the links doesn't match the item's collection field.
75
76
  RequestError: If the API returns an error response.
76
77
  """
77
- if isinstance(item, Item):
78
- collection_id = item.collection_id or (
79
- item.get_collection().id if item.get_collection() else None
80
- )
81
- else:
82
- collection_id = item.collection
83
-
84
- if not collection_id:
85
- raise ValueError("Cannot create item: no collection_id found on item")
78
+ collection_id = self._get_validated_collection_id(item, method="create")
86
79
 
87
80
  url = self.base_url.with_suffix(f"/collections/{collection_id}/items")
88
81
  item_json: dict = item.to_dict()
@@ -99,17 +92,13 @@ class ItemClient:
99
92
 
100
93
  Raises:
101
94
  ValueError: If the item has no collection set.
95
+ StacValidationError: If the collection ID in the links doesn't match the item's collection field.
102
96
  RequestError: If the API returns an error response.
103
97
  """
104
- if isinstance(item, Item):
105
- collection_id = item.collection_id or (
106
- item.get_collection().id if item.get_collection() else None
107
- )
108
- else:
109
- collection_id = item.collection
98
+ collection_id = self._get_validated_collection_id(item, method="add")
110
99
 
111
- if not collection_id:
112
- raise ValueError("Cannot create item: no collection_id found on item")
100
+ if not item.id:
101
+ raise ValueError("Cannot add item: no item_id found on item")
113
102
 
114
103
  url = self.base_url.with_suffix(f"/collections/{collection_id}/items/{item.id}")
115
104
  item_json: dict = item.to_dict()
@@ -203,12 +192,79 @@ class ItemClient:
203
192
  Optional[str]: The extracted token, or None if parsing fails.
204
193
 
205
194
  Raises:
206
- DatacosmosException: If pagination token extraction fails.
195
+ DatacosmosError: If pagination token extraction fails.
207
196
  """
208
197
  try:
209
198
  return next_href.split("?")[1].split("=")[-1]
210
199
  except (IndexError, AttributeError) as e:
211
- raise DatacosmosException(
200
+ raise DatacosmosError(
212
201
  f"Failed to parse pagination token from {next_href}",
213
202
  response=e.response,
214
203
  ) from e
204
+
205
+ def _get_validated_collection_id(
206
+ self, item: Item | DatacosmosItem, method: str
207
+ ) -> str:
208
+ """Resolves and validates the collection ID from an item, checking for link consistency.
209
+
210
+ Args:
211
+ item: The STAC item.
212
+ method: The client method calling this helper ("create" or "add").
213
+
214
+ Returns:
215
+ The validated collection_id.
216
+
217
+ Raises:
218
+ ValueError: If collection ID cannot be resolved.
219
+ StacValidationError: If the collection ID and parent link are inconsistent.
220
+ """
221
+ if isinstance(item, Item):
222
+ collection_id = item.collection_id or (
223
+ item.get_collection().id if item.get_collection() else None
224
+ )
225
+ if collection_id and not self._is_collection_link_consistent_pystac(
226
+ item, collection_id
227
+ ):
228
+ raise StacValidationError(
229
+ "Parent link in pystac.Item does not match its collection_id."
230
+ )
231
+ else:
232
+ collection_id = item.collection
233
+ if collection_id and not self._is_collection_link_consistent_datacosmos(
234
+ item
235
+ ):
236
+ raise StacValidationError(
237
+ "Parent link in DatacosmosItem does not match its collection."
238
+ )
239
+
240
+ if not collection_id:
241
+ if method == "create":
242
+ raise ValueError("Cannot create item: no collection_id found on item")
243
+ else:
244
+ raise ValueError("Cannot add item: no collection_id found on item")
245
+
246
+ return collection_id
247
+
248
+ def _is_collection_link_consistent_pystac(
249
+ self, item: Item, collection_id: str
250
+ ) -> bool:
251
+ """Helper to check if the parent link matches the pystac item's collection_id."""
252
+ parent_link = next(
253
+ (link for link in item.get_links("parent") if link.rel == "parent"), None
254
+ )
255
+ if not parent_link:
256
+ return True
257
+
258
+ link_collection_id = parent_link.get_href().rstrip("/").split("/")[-1]
259
+ return link_collection_id == collection_id
260
+
261
+ def _is_collection_link_consistent_datacosmos(self, item: DatacosmosItem) -> bool:
262
+ """Helper to check if the parent link matches the datacosmos item's collection field."""
263
+ if not item.collection:
264
+ return True
265
+
266
+ for link in item.links:
267
+ if link.get("rel") == "parent":
268
+ link_collection_id = link.get("href", "").rstrip("/").split("/")[-1]
269
+ return link_collection_id == item.collection
270
+ return True
@@ -1,28 +1,111 @@
1
1
  """Model representing a datacosmos item."""
2
2
 
3
+ import math
3
4
  from datetime import datetime
5
+ from typing import Any
4
6
 
5
- from pydantic import BaseModel
7
+ from pydantic import BaseModel, ConfigDict, field_validator, model_validator
8
+ from shapely.errors import ShapelyError
9
+ from shapely.geometry import Polygon, shape
6
10
 
11
+ from datacosmos.exceptions.stac_validation_error import StacValidationError
7
12
  from datacosmos.stac.enums.processing_level import ProcessingLevel
8
13
  from datacosmos.stac.item.models.asset import Asset
9
14
 
15
+ _REQUIRED_DATACOSMOS_PROPERTIES = [
16
+ "datetime",
17
+ "processing:level",
18
+ "sat:platform_international_designator",
19
+ ]
20
+
10
21
 
11
22
  class DatacosmosItem(BaseModel):
12
- """Model representing a datacosmos item."""
23
+ """Model representing a flexible Datacosmos STAC item with mandatory business fields."""
24
+
25
+ model_config = ConfigDict(extra="allow")
13
26
 
14
27
  id: str
15
28
  type: str
16
- stac_version: str
17
- stac_extensions: list | None
18
- geometry: dict
19
- properties: dict
20
- links: list
29
+ geometry: dict[str, Any]
30
+ bbox: list[float]
31
+ properties: dict[str, Any]
32
+
33
+ links: list[dict[str, Any]]
21
34
  assets: dict[str, Asset]
22
- collection: str
23
- bbox: tuple[float, float, float, float]
24
35
 
25
- def get_property(self, key: str) -> str | None:
36
+ stac_version: str | None = None
37
+ stac_extensions: list[str] | None = None
38
+ collection: str | None = None
39
+
40
+ @field_validator("properties", mode="before")
41
+ @classmethod
42
+ def validate_datacosmos_properties(
43
+ cls, properties_data: dict[str, Any]
44
+ ) -> dict[str, Any]:
45
+ """Validates that Datacosmos-specific properties exist."""
46
+ missing_keys = [
47
+ key for key in _REQUIRED_DATACOSMOS_PROPERTIES if key not in properties_data
48
+ ]
49
+
50
+ if missing_keys:
51
+ raise StacValidationError(
52
+ f"Datacosmos-specific properties are missing: {', '.join(missing_keys)}."
53
+ )
54
+ return properties_data
55
+
56
+ @field_validator("geometry", mode="before")
57
+ @classmethod
58
+ def validate_geometry_is_polygon(
59
+ cls, geometry_data: dict[str, Any]
60
+ ) -> dict[str, Any]:
61
+ """Validates that the geometry is a Polygon with coordinates and correct winding order."""
62
+ if geometry_data.get("type") != "Polygon" or not geometry_data.get(
63
+ "coordinates"
64
+ ):
65
+ raise StacValidationError("Geometry must be a Polygon with coordinates.")
66
+
67
+ try:
68
+ # Use shape() for robust GeoJSON parsing and validation
69
+ polygon = shape(geometry_data)
70
+
71
+ if not polygon.is_valid:
72
+ raise ValueError(f"Polygon geometry is invalid: {polygon.geom_type}")
73
+
74
+ # right-hand rule validation:
75
+ # The right-hand rule means exterior ring must be counter-clockwise (CCW).
76
+ # Shapely's Polygon stores the exterior as CCW if the input is valid.
77
+ if not polygon.exterior.is_ccw:
78
+ raise ValueError(
79
+ "Polygon winding order violates GeoJSON Right-Hand Rule (Exterior ring is clockwise)."
80
+ )
81
+
82
+ except (KeyError, ShapelyError, ValueError) as e:
83
+ raise StacValidationError(f"Invalid geometry data: {e}") from e
84
+
85
+ return geometry_data
86
+
87
+ @model_validator(mode="after")
88
+ def validate_bbox_vs_geometry(self) -> "DatacosmosItem":
89
+ """Validates that the bbox tightly encloses the geometry."""
90
+ if self.geometry and self.bbox:
91
+ try:
92
+ geom_shape = shape(self.geometry)
93
+ true_bbox = list(geom_shape.bounds)
94
+
95
+ # Check for floating point equality within a tolerance
96
+ if not all(
97
+ math.isclose(a, b, rel_tol=1e-9)
98
+ for a, b in zip(self.bbox, true_bbox)
99
+ ):
100
+ raise StacValidationError(
101
+ "Provided bbox does not match geometry bounds."
102
+ )
103
+ except Exception as e:
104
+ # Catch any errors from Shapely or the comparison
105
+ raise StacValidationError(f"Invalid bbox or geometry: {e}") from e
106
+ return self
107
+
108
+ def get_property(self, key: str) -> Any | None:
26
109
  """Get a property value from the Datacosmos item."""
27
110
  return self.properties.get(key)
28
111
 
@@ -43,13 +126,22 @@ class DatacosmosItem(BaseModel):
43
126
  @property
44
127
  def sat_int_designator(self) -> str:
45
128
  """Get the satellite international designator of the Datacosmos item."""
46
- property = self.get_property("sat:platform_international_designator")
47
- if property is None:
48
- raise ValueError(
49
- "sat:platform_international_designator is missing in STAC item"
50
- )
51
- return property
129
+ return self.properties["sat:platform_international_designator"]
130
+
131
+ @property
132
+ def polygon(self) -> Polygon:
133
+ """Returns the polygon of the item."""
134
+ coordinates = self.geometry["coordinates"][0]
135
+ return Polygon(coordinates)
52
136
 
53
137
  def to_dict(self) -> dict:
54
138
  """Converts the DatacosmosItem instance to a dictionary."""
55
139
  return self.model_dump()
140
+
141
+ def has_self_link(self) -> bool:
142
+ """Checks if the item has a 'self' link."""
143
+ return any(link.get("rel") == "self" for link in self.links)
144
+
145
+ def has_parent_link(self) -> bool:
146
+ """Checks if the item has a 'parent' link."""
147
+ return any(link.get("rel") == "parent" for link in self.links)
@@ -1,20 +1,20 @@
1
- """Validates an API response and raises a DatacosmosException if an error occurs."""
1
+ """Validates an API response and raises a DatacosmosError if an error occurs."""
2
2
 
3
3
  from pydantic import ValidationError
4
4
  from requests import Response
5
5
 
6
- from datacosmos.exceptions.datacosmos_exception import DatacosmosException
6
+ from datacosmos.exceptions.datacosmos_error import DatacosmosError
7
7
  from datacosmos.utils.http_response.models.datacosmos_response import DatacosmosResponse
8
8
 
9
9
 
10
10
  def check_api_response(response: Response) -> None:
11
- """Validates an API response and raises a DatacosmosException if an error occurs.
11
+ """Validates an API response and raises a DatacosmosError if an error occurs.
12
12
 
13
13
  Args:
14
14
  resp (requests.Response): The response object.
15
15
 
16
16
  Raises:
17
- DatacosmosException: If the response status code indicates an error.
17
+ DatacosmosError: If the response status code indicates an error.
18
18
  """
19
19
  if 200 <= response.status_code < 400:
20
20
  return
@@ -26,9 +26,9 @@ def check_api_response(response: Response) -> None:
26
26
  msg = "\n * " + "\n * ".join(
27
27
  error.human_readable() for error in response.errors
28
28
  )
29
- raise DatacosmosException(msg, response=response)
29
+ raise DatacosmosError(msg, response=response)
30
30
 
31
31
  except ValidationError:
32
- raise DatacosmosException(
32
+ raise DatacosmosError(
33
33
  f"HTTP {response.status_code}: {response.text}", response=response
34
34
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacosmos
3
- Version: 0.0.17
3
+ Version: 0.0.19
4
4
  Summary: A library for interacting with DataCosmos from Python code
5
5
  Author-email: Open Cosmos <support@open-cosmos.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -16,6 +16,7 @@ Requires-Dist: pystac==1.12.1
16
16
  Requires-Dist: pyyaml==6.0.2
17
17
  Requires-Dist: structlog==24.4.0
18
18
  Requires-Dist: tenacity>=8.2.3
19
+ Requires-Dist: shapely>=1.8.0
19
20
  Provides-Extra: dev
20
21
  Requires-Dist: black==22.3.0; extra == "dev"
21
22
  Requires-Dist: ruff==0.9.5; extra == "dev"
@@ -1,10 +1,10 @@
1
1
  datacosmos/__init__.py,sha256=dVHKpbz5FVtfoJAWHRdsUENG6H-vs4UrkuwnIvOGJr4,66
2
- datacosmos/datacosmos_client.py,sha256=yK9rit_AELs11ReJWm-zXx46wEWyvlDJDUOXy27bP3c,10524
2
+ datacosmos/datacosmos_client.py,sha256=hydaepbjoEHupRv1CUNUOUMF9t_uZNR4Refwzvzfl0s,10446
3
3
  datacosmos/auth/__init__.py,sha256=ynCThS9QyLKV9miRdnjm8uF_breiGGiCcI0FaOSw_2o,45
4
4
  datacosmos/auth/base_authenticator.py,sha256=bSlb-N-vIUTl4K9KnDd3Dz21MevN_nvpWuwxgifdWBE,1814
5
- datacosmos/auth/local_authenticator.py,sha256=a3jIBZAMB1UlEFUZyECyJTCgSH2yjbTcrFZuUer0F90,2914
5
+ datacosmos/auth/local_authenticator.py,sha256=ah2DWxHjPYP-aTqGR-kEMMPv1Geeh00weHCGpZWgSig,2864
6
6
  datacosmos/auth/local_token_fetcher.py,sha256=E4MI2lTRHAmxIQA7qY6hmpAUZETTzXNO8MViBaXnxGs,5268
7
- datacosmos/auth/m2m_authenticator.py,sha256=tglT5mbqfciSFQksqduroVirHRHDdev9V-B92O0hd0w,2637
7
+ datacosmos/auth/m2m_authenticator.py,sha256=_n5qpyK9ngqHec8uMDBOKL-U3k989geIQ-rzH9PMhZc,2621
8
8
  datacosmos/auth/token.py,sha256=neSV9gnnFa-rxEwMAJlZe_cReV6g4PQf8mq4-1mZzB8,2558
9
9
  datacosmos/config/__init__.py,sha256=KCsaTb9-ZgFui1GM8wZFIPLJy0D0O8l8Z1Sv3NRD9UM,140
10
10
  datacosmos/config/config.py,sha256=JHWmS6Q_T32iMly7cvJncjPCvtIgamBigKJJcvjGH7g,3465
@@ -18,26 +18,27 @@ datacosmos/config/models/local_user_account_authentication_config.py,sha256=SJZR
18
18
  datacosmos/config/models/m2m_authentication_config.py,sha256=4l3Mmgips73rYGX5l7FCoHAWpWSGQYYkzZYvQzbmRz0,782
19
19
  datacosmos/config/models/no_authentication_config.py,sha256=x5xikSGPuqQbrf_S2oIWXo5XxAORci2sSE5KyJvZHVw,312
20
20
  datacosmos/config/models/url.py,sha256=bBeulXQ2c-tLJyIoo3sTi9SPsZIyIDn_D2zmkCGWp9s,1597
21
- datacosmos/exceptions/__init__.py,sha256=Crz8W7mOvPUXYcfDVotvjUt_3HKawBpmJA_-uel9UJk,45
22
- datacosmos/exceptions/datacosmos_exception.py,sha256=rKjJvQDvCEbxXWWccxB5GI_sth662bW8Yml0hX-vRw4,923
21
+ datacosmos/exceptions/__init__.py,sha256=2nS68zE1_AysfbH2S7QtX2pJ984hPLNjSqI_qzod2rc,212
22
+ datacosmos/exceptions/datacosmos_error.py,sha256=6Y9Hzo__aYbjTIpgTaD_Vh6Z271lbxyhoed859LpCY4,915
23
+ datacosmos/exceptions/stac_validation_error.py,sha256=4wmLcJwRraZ6S0JPYxL7jDs8X8k68KmNbcVTl_z1hkI,237
23
24
  datacosmos/stac/__init__.py,sha256=B4x_Mr4X7TzQoYtRC-VzI4W-fEON5WUOaz8cWJbk3Fc,214
24
25
  datacosmos/stac/stac_client.py,sha256=S0HESbZhlIdS0x_VSCeOSuOFaB50U4CMnTOX_0zLjn8,730
25
26
  datacosmos/stac/collection/__init__.py,sha256=VQMLnsU3sER5kh4YxHrHP7XCA3DG1y0n9yoSmvycOY0,212
26
- datacosmos/stac/collection/collection_client.py,sha256=-Nn3yqL4mQS05YAMd0IUmv03hdHKYBtVG2_EqoaAQWc,6064
27
+ datacosmos/stac/collection/collection_client.py,sha256=2ritToTSAC2n1nJgd7HpEDNvzda_qc-zsUUV39rbCRs,6052
27
28
  datacosmos/stac/collection/models/__init__.py,sha256=TQaihUS_CM9Eaekm4SbzFTNfv7BmabHv3Z-f37Py5Qs,40
28
29
  datacosmos/stac/collection/models/collection_update.py,sha256=XC6-29nLz1VGWMxYAw7r1OuL8PdJ3b2oI-RPvnM-XXI,1657
29
30
  datacosmos/stac/constants/__init__.py,sha256=dDRSsF7CKqNF44yIlNdE-PD1sp0Q5mhTEPT7hHIK7YE,26
30
31
  datacosmos/stac/constants/satellite_name_mapping.py,sha256=EJqNdO9uW5B-sIeDF72AjnW7va5BM9mm4oNwijtl51w,575
31
32
  datacosmos/stac/enums/__init__.py,sha256=GUEL2xGtdjsrszrxivs0X6daxkaZs2JsTu2JoBtsvB4,22
32
- datacosmos/stac/enums/processing_level.py,sha256=kWGhpMXgzoyTzwR4yoSFd3UQ5IBw3cUf3TaM1FaOtUg,263
33
+ datacosmos/stac/enums/processing_level.py,sha256=_k4FO818VlZWtlm-rULhg-CYkbewkOdTUfqeCfHN8h4,641
33
34
  datacosmos/stac/enums/product_type.py,sha256=7lL0unJ1hxevW8Pepn9rmydUUWIORu2x4MEtp6rSFbA,196
34
35
  datacosmos/stac/enums/season.py,sha256=QvUzXBYtPEfixhlbV0SAw2u_HK3tRFEnHKshJyIatdg,241
35
36
  datacosmos/stac/item/__init__.py,sha256=lRuD_yp-JxoLqBA23q0XMkCNImf4T-X3BJnSw9u_3Yk,200
36
- datacosmos/stac/item/item_client.py,sha256=HCHl3cHp0u2qxbwLxPk0xkujC1D4uwIBIFI-flpLXfQ,7783
37
+ datacosmos/stac/item/item_client.py,sha256=o05_xHqndaNUrPvHmwkGh1cxe0jnk3tn2Itray7VLh0,10170
37
38
  datacosmos/stac/item/models/__init__.py,sha256=bcOrOcIxGxGBrRVIyQVxSM3C3Xj_qzxIHgQeWo6f7Q8,34
38
39
  datacosmos/stac/item/models/asset.py,sha256=mvg_fenYCGOTMGwXXpK2nyqBk5RMsUYxl6KhQTWW_b0,631
39
40
  datacosmos/stac/item/models/catalog_search_parameters.py,sha256=3HrUm37VezujwuCR45jhMryS5m1FGc1XmX8-fdTy4jU,4870
40
- datacosmos/stac/item/models/datacosmos_item.py,sha256=AImz0GRxrpZfIETdzzNfaKX35wpr39Q4f4u0z6r8eys,1745
41
+ datacosmos/stac/item/models/datacosmos_item.py,sha256=BzuqyHvcCq2ADYkg0F4rsu4Vzj4egPQnttSFw71xGFY,5352
41
42
  datacosmos/stac/item/models/eo_band.py,sha256=YC3Scn_wFhIo51pIVcJeuJienF7JGWoEv39JngDM6rI,309
42
43
  datacosmos/stac/item/models/item_update.py,sha256=_CpjQn9SsfedfuxlHSiGeptqY4M-p15t9YX__mBRueI,2088
43
44
  datacosmos/stac/item/models/raster_band.py,sha256=CoEVs-YyPE5Fse0He9DdOs4dGZpzfCsCuVzOcdXa_UM,354
@@ -50,12 +51,12 @@ datacosmos/stac/storage/dataclasses/upload_path.py,sha256=gbpV67FECFNyXn-yGUSuLv
50
51
  datacosmos/utils/__init__.py,sha256=XQbAnoqJrPpnSpEzAbjh84yqYWw8cBM8mNp8ynTG-54,50
51
52
  datacosmos/utils/url.py,sha256=iQwZr6mYRoePqUZg-k3KQSV9o2wju5ZuCa5WS_GyJo4,2114
52
53
  datacosmos/utils/http_response/__init__.py,sha256=BvOWwC5coYqq_kFn8gIw5m54TLpdfJKlW9vgRkfhXiA,33
53
- datacosmos/utils/http_response/check_api_response.py,sha256=dKWW01jn2_lWV0xpOBABhEP42CFSsx9dP0iSxykbN54,1186
54
+ datacosmos/utils/http_response/check_api_response.py,sha256=l_yQiiekNcNbhFec_5Ue2mFY3YjwWEJS1gilhEJ3Luw,1158
54
55
  datacosmos/utils/http_response/models/__init__.py,sha256=Wj8YT6dqw7rAz_rctllxo5Or_vv8DwopvQvBzwCTvpw,45
55
56
  datacosmos/utils/http_response/models/datacosmos_error.py,sha256=Uqi2uM98nJPeCbM7zngV6vHSk97jEAb_nkdDEeUjiQM,740
56
57
  datacosmos/utils/http_response/models/datacosmos_response.py,sha256=oV4n-sue7K1wwiIQeHpxdNU8vxeqF3okVPE2rydw5W0,336
57
- datacosmos-0.0.17.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
58
- datacosmos-0.0.17.dist-info/METADATA,sha256=HyL2q5BSp_lua5GsQbDpAXRV5c7CE9jBX_Yy1iIOHYc,970
59
- datacosmos-0.0.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
60
- datacosmos-0.0.17.dist-info/top_level.txt,sha256=ueobs5CNeyDbPMgXPcVV0d0yNdm8CvGtDT3CaksRVtA,11
61
- datacosmos-0.0.17.dist-info/RECORD,,
58
+ datacosmos-0.0.19.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
59
+ datacosmos-0.0.19.dist-info/METADATA,sha256=K01wNYqlqufpA_-cpPIF4M_ccP9tVmmYMGE5PLf2LOk,1000
60
+ datacosmos-0.0.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
61
+ datacosmos-0.0.19.dist-info/top_level.txt,sha256=ueobs5CNeyDbPMgXPcVV0d0yNdm8CvGtDT3CaksRVtA,11
62
+ datacosmos-0.0.19.dist-info/RECORD,,