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

@@ -6,6 +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
10
  from datacosmos.stac.collection.models.collection_update import CollectionUpdate
10
11
  from datacosmos.utils.http_response.check_api_response import check_api_response
11
12
 
@@ -145,5 +146,8 @@ class CollectionClient:
145
146
  """
146
147
  try:
147
148
  return next_href.split("?")[1].split("=")[-1]
148
- except (IndexError, AttributeError):
149
- raise InvalidRequest(f"Failed to parse pagination token from {next_href}")
149
+ except (IndexError, AttributeError) as e:
150
+ raise DatacosmosException(
151
+ f"Failed to parse pagination token from {next_href}",
152
+ response=e.response,
153
+ ) from e
@@ -2,6 +2,7 @@
2
2
 
3
3
  Allows partial updates where only the provided fields are modified.
4
4
  """
5
+
5
6
  from typing import Any, Dict, List, Optional
6
7
 
7
8
  from pydantic import BaseModel, Field
@@ -0,0 +1 @@
1
+ """Constants for STAC."""
@@ -0,0 +1,20 @@
1
+ """Satellite name mapping."""
2
+
3
+ SATELLITE_NAME_MAPPING = {
4
+ "GEOSAT-2": "2014-033D",
5
+ "SUPERVIEW-1-01": "2016-083A",
6
+ "SUPERVIEW-1-02": "2016-083B",
7
+ "SUPERVIEW-1-03": "2018-002A",
8
+ "SUPERVIEW-1-04": "2018-002B",
9
+ "MANTIS": "2023-174B",
10
+ "MENUT": "2023-001B",
11
+ "HAMMER": "2024-043BC",
12
+ "HAMMER-EM": "COSPAR-HAMMER-EM-TBD",
13
+ "Alisio": "2023-185M",
14
+ "Platero": "2023-174G",
15
+ "PHISAT-2": "2024-149C",
16
+ "PHISAT-2 EM": "COSPAR-PHISAT2-EM-TBD",
17
+ "Sentinel-2A": "2015-028A",
18
+ "Sentinel-2B": "2017-013A",
19
+ "Sentinel-2C": "2024-157A",
20
+ }
@@ -0,0 +1,15 @@
1
+ """Level enum class."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class ProcessingLevel(Enum):
7
+ """Enum class for the processing levels of the data."""
8
+
9
+ L0 = "L0"
10
+ L1A = "L1A"
11
+ L2A = "L2A"
12
+ L1B = "L1B"
13
+ L1C = "L1C"
14
+ L1D = "L1D"
15
+ L3 = "L3"
@@ -0,0 +1,11 @@
1
+ """Product type enum class."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class ProductType(str, Enum):
7
+ """Different product types."""
8
+
9
+ SATELLITE = "Satellite"
10
+ VECTOR = "Vector"
11
+ INSIGHT = "Insight"
@@ -0,0 +1,14 @@
1
+ """Season enum class."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class Season(str, Enum):
7
+ """Different Open Cosmos seasons."""
8
+
9
+ SUMMER = "Summer"
10
+ WINTER = "Winter"
11
+ AUTUMN = "Autumn"
12
+ SPRING = "Spring"
13
+ RAINY = "Rainy"
14
+ DRY = "Dry"
@@ -9,6 +9,9 @@ from pystac import Item
9
9
 
10
10
  from datacosmos.datacosmos_client import DatacosmosClient
11
11
  from datacosmos.exceptions.datacosmos_exception import DatacosmosException
12
+ from datacosmos.stac.item.models.catalog_search_parameters import (
13
+ CatalogSearchParameters,
14
+ )
12
15
  from datacosmos.stac.item.models.datacosmos_item import DatacosmosItem
13
16
  from datacosmos.stac.item.models.item_update import ItemUpdate
14
17
  from datacosmos.stac.item.models.search_parameters import SearchParameters
@@ -59,17 +62,20 @@ class ItemClient:
59
62
 
60
63
  return self.search_items(parameters)
61
64
 
62
- def search_items(self, parameters: SearchParameters) -> Generator[Item, None, None]:
65
+ def search_items(
66
+ self, parameters: CatalogSearchParameters, project_id: str
67
+ ) -> Generator[Item, None, None]:
63
68
  """Query the STAC catalog using the POST endpoint with filtering and pagination.
64
69
 
65
70
  Args:
66
- parameters (SearchParameters): The search parameters.
71
+ parameters (CatalogSearchParameters): The search parameters.
67
72
 
68
73
  Yields:
69
74
  Item: Parsed STAC item.
70
75
  """
71
76
  url = self.base_url.with_suffix("/search")
72
- body = parameters.model_dump(by_alias=True, exclude_none=True)
77
+ parameters_query = parameters.to_query()
78
+ body = {"project": project_id, "limit": 50, "query": parameters_query}
73
79
  return self._paginate_items(url, body)
74
80
 
75
81
  def create_item(self, collection_id: str, item: Item | DatacosmosItem) -> None:
@@ -0,0 +1,132 @@
1
+ """Query parameters for catalog search."""
2
+
3
+ from datetime import datetime, timedelta
4
+ from typing import Any, List, Optional
5
+
6
+ from pydantic import BaseModel, field_validator, model_validator
7
+
8
+ from datacosmos.stac.constants.satellite_name_mapping import SATELLITE_NAME_MAPPING
9
+ from datacosmos.stac.enums.processing_level import ProcessingLevel
10
+ from datacosmos.stac.enums.product_type import ProductType
11
+ from datacosmos.stac.enums.season import Season
12
+
13
+
14
+ class CatalogSearchParameters(BaseModel):
15
+ """Query parameters for catalog search."""
16
+
17
+ start_date: Optional[str] = None
18
+ end_date: Optional[str] = None
19
+ seasons: Optional[List[Season]] = None
20
+ satellite: Optional[List[str]] = None
21
+ product_type: Optional[List[ProductType]] = None
22
+ processing_level: Optional[List[ProcessingLevel]] = None
23
+
24
+ # --- Field Validators ---
25
+
26
+ @field_validator("seasons", mode="before")
27
+ @classmethod
28
+ def parse_seasons(cls, value):
29
+ """Parses seasons values into a list of Season object."""
30
+ if value is None:
31
+ return None
32
+ return [Season(v) if not isinstance(v, Season) else v for v in value]
33
+
34
+ @field_validator("product_type", mode="before")
35
+ @classmethod
36
+ def parse_product_types(cls, value):
37
+ """Parses product types values into a list of ProductType object."""
38
+ if value is None:
39
+ return None
40
+ return [ProductType(v) if not isinstance(v, ProductType) else v for v in value]
41
+
42
+ @field_validator("processing_level", mode="before")
43
+ @classmethod
44
+ def parse_processing_levels(cls, value):
45
+ """Parses processing levels values into a list of ProcessingLevel object."""
46
+ if value is None:
47
+ return None
48
+ return [
49
+ ProcessingLevel(v) if not isinstance(v, ProcessingLevel) else v
50
+ for v in value
51
+ ]
52
+
53
+ @field_validator("start_date", mode="before")
54
+ @classmethod
55
+ def parse_start_date(cls, value: Any) -> Optional[str]:
56
+ """Validations on start_date."""
57
+ if value is None:
58
+ return None
59
+ try:
60
+ dt = datetime.strptime(value, "%m/%d/%Y")
61
+ if dt < datetime(2015, 5, 15):
62
+ raise ValueError("Date must be 5/15/2015 or later.")
63
+ return dt.isoformat() + "Z"
64
+ except ValueError:
65
+ raise ValueError(
66
+ "Invalid start_date format. Use mm/dd/yyyy (e.g., 05/15/2024)"
67
+ )
68
+
69
+ @field_validator("end_date", mode="before")
70
+ @classmethod
71
+ def parse_end_date(cls, value: Any) -> Optional[str]:
72
+ """Validations on end_date."""
73
+ if value is None:
74
+ return None
75
+ try:
76
+ dt = datetime.strptime(value, "%m/%d/%Y")
77
+ if dt < datetime(2015, 5, 15):
78
+ raise ValueError("Date must be 5/15/2015 or later.")
79
+ dt = dt + timedelta(days=1) - timedelta(milliseconds=1)
80
+ return dt.isoformat() + "Z"
81
+ except ValueError:
82
+ raise ValueError(
83
+ "Invalid end_date format. Use mm/dd/yyyy (e.g., 05/15/2024)"
84
+ )
85
+
86
+ # --- Model Validator ---
87
+
88
+ @model_validator(mode="after")
89
+ def validate_date_range(self) -> "CatalogSearchParameters":
90
+ """Checks if end_date is after the start_date."""
91
+ if self.start_date and self.end_date:
92
+ start_dt = datetime.fromisoformat(self.start_date.rstrip("Z"))
93
+ end_dt = datetime.fromisoformat(self.end_date.rstrip("Z"))
94
+ if start_dt > end_dt:
95
+ raise ValueError("end_date cannot be before start_date.")
96
+ return self
97
+
98
+ # --- Query Mapper ---
99
+
100
+ def to_query(self) -> dict:
101
+ """Map user-friendly input to STAC query structure."""
102
+ query = {}
103
+
104
+ if self.start_date or self.end_date:
105
+ query["datetime"] = {"gte": self.start_date, "lte": self.end_date}
106
+
107
+ if self.seasons:
108
+ query["opencosmos:season"] = {
109
+ "in": [seasons.value for seasons in self.seasons]
110
+ }
111
+
112
+ if self.product_type:
113
+ query["opencosmos:product_type"] = {
114
+ "in": [product_type.value for product_type in self.product_type]
115
+ }
116
+
117
+ if self.processing_level:
118
+ query["processing:level"] = {
119
+ "in": [
120
+ processing_level.value for processing_level in self.processing_level
121
+ ]
122
+ }
123
+
124
+ if self.satellite:
125
+ cospars = [
126
+ SATELLITE_NAME_MAPPING[ui]
127
+ for ui in self.satellite
128
+ if ui in SATELLITE_NAME_MAPPING
129
+ ]
130
+ query["sat:platform_international_designator"] = {"in": cospars}
131
+
132
+ return query
@@ -4,7 +4,7 @@ from datetime import datetime
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
- from datacosmos.stac.enums.level import Level
7
+ from datacosmos.stac.enums.processing_level import ProcessingLevel
8
8
  from datacosmos.stac.item.models.asset import Asset
9
9
 
10
10
 
@@ -36,9 +36,9 @@ class DatacosmosItem(BaseModel):
36
36
  return datetime.strptime(self.properties["datetime"], "%Y-%m-%dT%H:%M:%SZ")
37
37
 
38
38
  @property
39
- def level(self) -> Level:
39
+ def level(self) -> ProcessingLevel:
40
40
  """Get the processing level of the Datacosmos item."""
41
- return Level(self.properties["processing:level"].lower())
41
+ return ProcessingLevel(self.properties["processing:level"].lower())
42
42
 
43
43
  @property
44
44
  def sat_int_designator(self) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datacosmos
3
- Version: 0.0.3
3
+ Version: 0.0.4
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
@@ -13,6 +13,7 @@ Requires-Dist: oauthlib==3.2.0
13
13
  Requires-Dist: requests-oauthlib==1.3.1
14
14
  Requires-Dist: pydantic==2.10.6
15
15
  Requires-Dist: pystac==1.12.1
16
+ Requires-Dist: pyyaml==6.0.2
16
17
  Provides-Extra: dev
17
18
  Requires-Dist: black==22.3.0; extra == "dev"
18
19
  Requires-Dist: ruff==0.9.5; extra == "dev"
@@ -10,16 +10,21 @@ datacosmos/exceptions/datacosmos_exception.py,sha256=rKjJvQDvCEbxXWWccxB5GI_sth6
10
10
  datacosmos/stac/__init__.py,sha256=B4x_Mr4X7TzQoYtRC-VzI4W-fEON5WUOaz8cWJbk3Fc,214
11
11
  datacosmos/stac/stac_client.py,sha256=Cz_p96RmAgWX8t7Sye4OJRanQpCLihKStvfEw7IgYZc,472
12
12
  datacosmos/stac/collection/__init__.py,sha256=VQMLnsU3sER5kh4YxHrHP7XCA3DG1y0n9yoSmvycOY0,212
13
- datacosmos/stac/collection/collection_client.py,sha256=XTO2s309-cktJosvnwnFFXHDVmJc4vjvbEsZjpsCDmY,5904
13
+ datacosmos/stac/collection/collection_client.py,sha256=-Nn3yqL4mQS05YAMd0IUmv03hdHKYBtVG2_EqoaAQWc,6064
14
14
  datacosmos/stac/collection/models/__init__.py,sha256=TQaihUS_CM9Eaekm4SbzFTNfv7BmabHv3Z-f37Py5Qs,40
15
- datacosmos/stac/collection/models/collection_update.py,sha256=Tqmfg4H4UQj5jsgy1dpKJCR59NSfWeiCSi9y8CY8-Cg,1656
15
+ datacosmos/stac/collection/models/collection_update.py,sha256=XC6-29nLz1VGWMxYAw7r1OuL8PdJ3b2oI-RPvnM-XXI,1657
16
+ datacosmos/stac/constants/__init__.py,sha256=dDRSsF7CKqNF44yIlNdE-PD1sp0Q5mhTEPT7hHIK7YE,26
17
+ datacosmos/stac/constants/satellite_name_mapping.py,sha256=EJqNdO9uW5B-sIeDF72AjnW7va5BM9mm4oNwijtl51w,575
16
18
  datacosmos/stac/enums/__init__.py,sha256=GUEL2xGtdjsrszrxivs0X6daxkaZs2JsTu2JoBtsvB4,22
17
- datacosmos/stac/enums/level.py,sha256=dqrkSRtoutMTWatGyRRUz3uNKVlNXn3qa_ubXNbw618,237
19
+ datacosmos/stac/enums/processing_level.py,sha256=5gHG-0kG5rCUxmXYwF3t94ALKk6zUqguOdyTL-jwgps,247
20
+ datacosmos/stac/enums/product_type.py,sha256=7lL0unJ1hxevW8Pepn9rmydUUWIORu2x4MEtp6rSFbA,196
21
+ datacosmos/stac/enums/season.py,sha256=QvUzXBYtPEfixhlbV0SAw2u_HK3tRFEnHKshJyIatdg,241
18
22
  datacosmos/stac/item/__init__.py,sha256=lRuD_yp-JxoLqBA23q0XMkCNImf4T-X3BJnSw9u_3Yk,200
19
- datacosmos/stac/item/item_client.py,sha256=E6zHf3ANzVXd5Di_u05mLen5-EOKTdCs0VKcXXJ6lUc,6826
23
+ datacosmos/stac/item/item_client.py,sha256=ib848Pb2j6njvbx97vFFw7AWeKyBnBlK-05D3pFmIdU,7027
20
24
  datacosmos/stac/item/models/__init__.py,sha256=bcOrOcIxGxGBrRVIyQVxSM3C3Xj_qzxIHgQeWo6f7Q8,34
21
25
  datacosmos/stac/item/models/asset.py,sha256=mvg_fenYCGOTMGwXXpK2nyqBk5RMsUYxl6KhQTWW_b0,631
22
- datacosmos/stac/item/models/datacosmos_item.py,sha256=jHuOkNvbVXUHdpplClPnA5mR4mcrfYQNm51EgQaVtNk,1704
26
+ datacosmos/stac/item/models/catalog_search_parameters.py,sha256=VKMBnaTYjpn_zAM6wdk3P69ZoGBcqdzPcTGBlYPFvVk,4704
27
+ datacosmos/stac/item/models/datacosmos_item.py,sha256=AImz0GRxrpZfIETdzzNfaKX35wpr39Q4f4u0z6r8eys,1745
23
28
  datacosmos/stac/item/models/eo_band.py,sha256=YC3Scn_wFhIo51pIVcJeuJienF7JGWoEv39JngDM6rI,309
24
29
  datacosmos/stac/item/models/item_update.py,sha256=_CpjQn9SsfedfuxlHSiGeptqY4M-p15t9YX__mBRueI,2088
25
30
  datacosmos/stac/item/models/raster_band.py,sha256=CoEVs-YyPE5Fse0He9DdOs4dGZpzfCsCuVzOcdXa_UM,354
@@ -37,8 +42,8 @@ datacosmos/utils/http_response/check_api_response.py,sha256=dKWW01jn2_lWV0xpOBAB
37
42
  datacosmos/utils/http_response/models/__init__.py,sha256=Wj8YT6dqw7rAz_rctllxo5Or_vv8DwopvQvBzwCTvpw,45
38
43
  datacosmos/utils/http_response/models/datacosmos_error.py,sha256=Uqi2uM98nJPeCbM7zngV6vHSk97jEAb_nkdDEeUjiQM,740
39
44
  datacosmos/utils/http_response/models/datacosmos_response.py,sha256=oV4n-sue7K1wwiIQeHpxdNU8vxeqF3okVPE2rydw5W0,336
40
- datacosmos-0.0.3.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
41
- datacosmos-0.0.3.dist-info/METADATA,sha256=ejuFFFnmdaInVXwXQtO6m35BQdTyotYYMLDVT-oWec4,843
42
- datacosmos-0.0.3.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
43
- datacosmos-0.0.3.dist-info/top_level.txt,sha256=Iu5b533Fmdfz0rFKTnuBPjSUOQL2lEkTfHxsokP72s4,18
44
- datacosmos-0.0.3.dist-info/RECORD,,
45
+ datacosmos-0.0.4.dist-info/licenses/LICENSE.md,sha256=vpbRI-UUbZVQfr3VG_CXt9HpRnL1b5kt8uTVbirxeyI,1486
46
+ datacosmos-0.0.4.dist-info/METADATA,sha256=8yM44qsv1vBDxutLlUte916Z6zgPVT0mnyR1Bz2drJ8,872
47
+ datacosmos-0.0.4.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
48
+ datacosmos-0.0.4.dist-info/top_level.txt,sha256=Iu5b533Fmdfz0rFKTnuBPjSUOQL2lEkTfHxsokP72s4,18
49
+ datacosmos-0.0.4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,15 +0,0 @@
1
- """Level enum class."""
2
-
3
- from enum import Enum
4
-
5
-
6
- class Level(Enum):
7
- """Enum class for the processing levels of the data."""
8
-
9
- L0 = "l0"
10
- L1A = "l1a"
11
- L2A = "l2a"
12
- L1B = "l1b"
13
- L1C = "l1c"
14
- L1D = "l1d"
15
- L3 = "l3"