cecil 0.0.14__py3-none-any.whl → 0.0.16__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 cecil might be problematic. Click here for more details.

cecil/__init__.py CHANGED
@@ -1 +1,3 @@
1
1
  from .client import Client
2
+ from .errors import Error
3
+ from .version import __version__
cecil/client.py CHANGED
@@ -1,18 +1,25 @@
1
1
  import os
2
- from typing import Dict, List
3
-
4
2
  import requests
5
3
  import snowflake.connector
4
+
6
5
  from pydantic import BaseModel
7
6
  from requests import auth
7
+ from typing import Dict, List
8
+
9
+ from .errors import (
10
+ Error,
11
+ _handle_bad_request,
12
+ _handle_not_found,
13
+ _handle_unprocessable_entity,
14
+ )
8
15
 
9
16
  from .models import (
10
17
  AOI,
11
18
  AOICreate,
12
19
  DataRequest,
13
20
  DataRequestCreate,
14
- Reprojection,
15
- ReprojectionCreate,
21
+ Transformation,
22
+ TransformationCreate,
16
23
  RecoverAPIKey,
17
24
  RecoverAPIKeyRequest,
18
25
  RotateAPIKey,
@@ -20,8 +27,7 @@ from .models import (
20
27
  SnowflakeCredentials,
21
28
  )
22
29
 
23
- # TODO: find a way to get this version from __about__.py
24
- SDK_VERSION = "0.0.14"
30
+ from .version import __version__
25
31
 
26
32
  # TODO: Documentation (Google style)
27
33
  # TODO: Add HTTP retries
@@ -31,9 +37,7 @@ class Client:
31
37
  def __init__(self, env=None):
32
38
  self._api_auth = None
33
39
  self._base_url = (
34
- "https://api.cecil.earth"
35
- if env is None
36
- else f"https://{env}-api.cecil.earth"
40
+ "https://api.cecil.earth" if env is None else f"https://{env}.cecil.earth"
37
41
  )
38
42
  self._snowflake_creds = None
39
43
 
@@ -65,27 +69,27 @@ class Client:
65
69
  res = self._get(url="/v0/data-requests")
66
70
  return [DataRequest(**record) for record in res["records"]]
67
71
 
68
- def create_reprojection(
69
- self, data_request_id: str, crs: str, resolution: float
70
- ) -> Reprojection:
71
- # TODO: check if data request is completed before creating reprojection
72
+ def create_transformation(
73
+ self, data_request_id: str, crs: str, spatial_resolution: float
74
+ ) -> Transformation:
75
+ # TODO: check if data request is completed before creating transformation
72
76
  res = self._post(
73
- url="/v0/reprojections",
74
- model=ReprojectionCreate(
77
+ url="/v0/transformations",
78
+ model=TransformationCreate(
75
79
  data_request_id=data_request_id,
76
80
  crs=crs,
77
- resolution=resolution,
81
+ spatial_resolution=spatial_resolution,
78
82
  ),
79
83
  )
80
- return Reprojection(**res)
84
+ return Transformation(**res)
81
85
 
82
- def get_reprojection(self, id: str) -> Reprojection:
83
- res = self._get(url=f"/v0/reprojections/{id}")
84
- return Reprojection(**res)
86
+ def get_transformation(self, id: str) -> Transformation:
87
+ res = self._get(url=f"/v0/transformations/{id}")
88
+ return Transformation(**res)
85
89
 
86
- def list_reprojections(self) -> List[Reprojection]:
87
- res = self._get(url="/v0/reprojections")
88
- return [Reprojection(**record) for record in res["records"]]
90
+ def list_transformations(self) -> List[Transformation]:
91
+ res = self._get(url="/v0/transformations")
92
+ return [Transformation(**record) for record in res["records"]]
89
93
 
90
94
  def query(self, sql):
91
95
  if self._snowflake_creds is None:
@@ -121,7 +125,7 @@ class Client:
121
125
  if skip_auth is False:
122
126
  self._set_auth()
123
127
 
124
- headers = {"cecil-python-sdk-version": SDK_VERSION}
128
+ headers = {"cecil-python-sdk-version": __version__}
125
129
 
126
130
  try:
127
131
  r = requests.request(
@@ -135,14 +139,25 @@ class Client:
135
139
  r.raise_for_status()
136
140
  return r.json()
137
141
 
138
- except requests.exceptions.ConnectionError as err:
139
- raise ValueError("Connection error") from err
142
+ except requests.exceptions.ConnectionError:
143
+ raise Error("failed to connect to the Cecil Platform")
140
144
  except requests.exceptions.HTTPError as err:
141
- message = f"Request failed with status code {err.response.status_code}"
142
- if err.response.text != "":
143
- message += f": {err.response.text}"
144
-
145
- raise ValueError(message) from err
145
+ match err.response.status_code:
146
+ case 400:
147
+ _handle_bad_request(err.response)
148
+ case 401:
149
+ raise Error("unauthorised")
150
+ case 404:
151
+ _handle_not_found(err.response)
152
+ case 422:
153
+ _handle_unprocessable_entity(err.response)
154
+ case 500:
155
+ raise Error("internal server error")
156
+ case _:
157
+ raise Error(
158
+ f"request failed with code {err.response.status_code}",
159
+ err.response.text,
160
+ )
146
161
 
147
162
  def _get(self, url: str, **kwargs) -> Dict:
148
163
  return self._request(method="get", url=url, **kwargs)
cecil/errors.py ADDED
@@ -0,0 +1,62 @@
1
+ import json
2
+
3
+
4
+ class Error(Exception):
5
+ def __init__(self, message: str, details=None):
6
+ self.message = message
7
+ self.details = details
8
+
9
+ if self.details is not None:
10
+ super().__init__(f"{self.message} \n{json.dumps(self.details, indent=2)}")
11
+ return
12
+
13
+ super().__init__(self.message)
14
+
15
+
16
+ def _format_json_key(value: str):
17
+ return "".join(["_" + i.lower() if i.isupper() else i for i in value]).lstrip("_")
18
+
19
+
20
+ def _is_json(value: str):
21
+ try:
22
+ json.loads(value)
23
+ return True
24
+ except ValueError:
25
+ return False
26
+
27
+
28
+ def _handle_bad_request(response):
29
+ if not _is_json(response.text):
30
+ raise Error("bad request")
31
+
32
+ details = {}
33
+ for key, value in response.json().items():
34
+ details[_format_json_key(key)] = value
35
+
36
+ raise Error("bad request", details)
37
+
38
+
39
+ def _handle_not_found(response):
40
+ if not _is_json(response.text):
41
+ raise Error("resource not found")
42
+
43
+ details = {}
44
+ for key, value in response.json().items():
45
+ details[_format_json_key(key)] = value
46
+
47
+ raise Error("resource not found", details)
48
+
49
+
50
+ def _handle_unprocessable_entity(response):
51
+ if not _is_json(response.text):
52
+ raise Error(f"failed to process request")
53
+
54
+ res_body = response.json()
55
+ if "params" not in res_body:
56
+ raise Error(f"failed to process request")
57
+
58
+ details = {}
59
+ for key, value in res_body["params"].items():
60
+ details[_format_json_key(key)] = value
61
+
62
+ raise Error(f"failed to process request", details)
cecil/models.py CHANGED
@@ -1,23 +1,10 @@
1
1
  import datetime
2
- from enum import Enum
3
- from typing import Dict, List, Optional
2
+ from typing import Dict
4
3
 
5
4
  from pydantic import BaseModel, ConfigDict, SecretStr
6
5
  from pydantic.alias_generators import to_camel
7
6
 
8
7
 
9
- class SubRequestStatus(str, Enum):
10
- COMPLETED = "completed"
11
- FAILED = "failed"
12
- PROCESSING = "processing"
13
-
14
-
15
- class DataRequestStatus(str, Enum):
16
- COMPLETED = "completed"
17
- FAILED = "failed"
18
- PROCESSING = "processing"
19
-
20
-
21
8
  class AOI(BaseModel):
22
9
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
23
10
  id: str
@@ -34,21 +21,11 @@ class AOICreate(BaseModel):
34
21
  geometry: Dict
35
22
 
36
23
 
37
- class SubRequest(BaseModel):
38
- model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
39
- external_id: str
40
- description: str
41
- status: SubRequestStatus
42
- error_message: Optional[str]
43
-
44
-
45
24
  class DataRequest(BaseModel):
46
25
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
47
26
  id: str
48
27
  aoi_id: str
49
28
  dataset_id: str
50
- sub_requests: List[SubRequest]
51
- status: DataRequestStatus
52
29
  created_at: datetime.datetime
53
30
  created_by: str
54
31
 
@@ -78,21 +55,21 @@ class RotateAPIKeyRequest(BaseModel):
78
55
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
79
56
 
80
57
 
81
- class Reprojection(BaseModel):
58
+ class Transformation(BaseModel):
82
59
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
83
60
  id: str
84
61
  data_request_id: str
85
62
  crs: str
86
- resolution: float
63
+ spatial_resolution: float
87
64
  created_at: datetime.datetime
88
65
  created_by: str
89
66
 
90
67
 
91
- class ReprojectionCreate(BaseModel):
68
+ class TransformationCreate(BaseModel):
92
69
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
93
70
  data_request_id: str
94
71
  crs: str
95
- resolution: float
72
+ spatial_resolution: float
96
73
 
97
74
 
98
75
  class SnowflakeCredentials(BaseModel):
cecil/version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.0.16"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: cecil
3
- Version: 0.0.14
3
+ Version: 0.0.16
4
4
  Summary: Python SDK for Cecil Earth
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE.txt
@@ -71,30 +71,30 @@ my_data_request = client.create_data_request(
71
71
  dataset_id=planet_forest_carbon_diligence_id,
72
72
  )
73
73
 
74
- print(client.get_data_request(my_data_request.id).status)
74
+ print(client.get_data_request(my_data_request.id))
75
75
  ```
76
76
 
77
- ### Create a reprojection using the Cecil client (once data request is completed)
77
+ ### Create a transformation using the Cecil client
78
78
 
79
79
  ```python
80
- my_reprojection = client.create_reprojection(
80
+ my_transformation = client.create_transformation(
81
81
  data_request_id=my_data_request.id,
82
82
  crs="EPSG:4326",
83
- resolution=0.005,
83
+ spatial_resolution=0.005,
84
84
  )
85
85
 
86
- print(client.get_reprojection(my_reprojection.id).status)
86
+ print(client.get_transformation(my_transformation.id))
87
87
  ```
88
88
 
89
- ### Query data (once reprojection is completed)
89
+ ### Query data (once transformation is completed)
90
90
 
91
91
  ```python
92
92
  df = client.query(f'''
93
93
  SELECT *
94
- FROM
94
+ FROM
95
95
  planet.forest_carbon_diligence
96
96
  WHERE
97
- reprojection_id = '{my_reprojection.id}'
97
+ transformation_id = '{my_transformation.id}'
98
98
  ''')
99
99
  ```
100
100
 
@@ -109,9 +109,9 @@ client.list_data_requests()
109
109
 
110
110
  client.get_data_request(my_data_request.id)
111
111
 
112
- client.list_reprojections()
112
+ client.list_transformations()
113
113
 
114
- client.get_reprojection(my_reprojection.id)
114
+ client.get_transformation(my_transformation.id)
115
115
  ```
116
116
 
117
117
  ## License
@@ -0,0 +1,9 @@
1
+ cecil/__init__.py,sha256=AEcRl73BDSAQe6W0d1PDD87IEcumARtREl7dCVa_YQY,86
2
+ cecil/client.py,sha256=hykvGQrpuAEQR_TawLKCwc_mWsjD72uxAZHaOTZjf1M,5764
3
+ cecil/errors.py,sha256=r9NNRtv_oO_hblnh-VOlAf767cJqmlnbJfHB63OA_zY,1538
4
+ cecil/models.py,sha256=xkjrLAOenJboRtzRPihT-C8jpadngePz4KzLNJoemHM,2012
5
+ cecil/version.py,sha256=8ss7zPyQ3YfaQJw9IIGX35NOL7ASZ7-LErAxKnQDN7c,23
6
+ cecil-0.0.16.dist-info/METADATA,sha256=7f2D9tc10goXMoeFgZqjHWeENrVyUq6H0OXmma75eWY,2659
7
+ cecil-0.0.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ cecil-0.0.16.dist-info/licenses/LICENSE.txt,sha256=mUexcmfYx3bG1VIzAdQTOf_NzStYw6-QkKVdUY_d4i4,1066
9
+ cecil-0.0.16.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.25.0
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,7 +0,0 @@
1
- cecil/__init__.py,sha256=MF64bwUCd4sm3dvcxZnkb4ujHtxT_KeuXdD0nVieEt4,27
2
- cecil/client.py,sha256=86TRdEGdxgO5nmoKpxdIIU6xeR9EjcOht7ZcKg7tInI,5237
3
- cecil/models.py,sha256=6p9AedCDdkK-ptK2r5pc9AKG8rgZHw-YYDvRgXPAQqI,2544
4
- cecil-0.0.14.dist-info/METADATA,sha256=xzTRj3FnDLmzk9pA11-YI3xBMMGKJ7p1e6rZlxO4YVw,2677
5
- cecil-0.0.14.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
6
- cecil-0.0.14.dist-info/licenses/LICENSE.txt,sha256=mUexcmfYx3bG1VIzAdQTOf_NzStYw6-QkKVdUY_d4i4,1066
7
- cecil-0.0.14.dist-info/RECORD,,