cecil 0.0.15__py3-none-any.whl → 0.0.17__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,2 +1,3 @@
1
1
  from .client import Client
2
2
  from .errors import Error
3
+ from .version import __version__
cecil/client.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from typing import Dict, List
3
3
 
4
+ import pandas as pd
4
5
  import requests
5
6
  import snowflake.connector
6
7
  from pydantic import BaseModel
@@ -10,37 +11,35 @@ from .errors import (
10
11
  Error,
11
12
  _handle_bad_request,
12
13
  _handle_not_found,
14
+ _handle_too_many_requests,
13
15
  _handle_unprocessable_entity,
14
16
  )
15
-
16
17
  from .models import (
17
18
  AOI,
18
19
  AOICreate,
19
20
  DataRequest,
20
21
  DataRequestCreate,
21
- Reprojection,
22
- ReprojectionCreate,
22
+ OrganisationCreate,
23
23
  RecoverAPIKey,
24
24
  RecoverAPIKeyRequest,
25
25
  RotateAPIKey,
26
26
  RotateAPIKeyRequest,
27
+ SignUpRequest,
28
+ SignUpResponse,
27
29
  SnowflakeCredentials,
30
+ Transformation,
31
+ TransformationCreate,
32
+ User,
33
+ UserCreate,
28
34
  )
29
-
30
- # TODO: find a way to get this version from __about__.py
31
- SDK_VERSION = "0.0.15"
32
-
33
- # TODO: Documentation (Google style)
34
- # TODO: Add HTTP retries
35
+ from .version import __version__
35
36
 
36
37
 
37
38
  class Client:
38
- def __init__(self, env=None):
39
+ def __init__(self, env: str = None) -> None:
39
40
  self._api_auth = None
40
41
  self._base_url = (
41
- "https://api.cecil.earth"
42
- if env is None
43
- else f"https://{env}-api.cecil.earth"
42
+ "https://api.cecil.earth" if env is None else f"https://{env}.cecil.earth"
44
43
  )
45
44
  self._snowflake_creds = None
46
45
 
@@ -68,33 +67,32 @@ class Client:
68
67
  res = self._get(url=f"/v0/data-requests/{id}")
69
68
  return DataRequest(**res)
70
69
 
71
- def list_data_requests(self):
70
+ def list_data_requests(self) -> List[DataRequest]:
72
71
  res = self._get(url="/v0/data-requests")
73
72
  return [DataRequest(**record) for record in res["records"]]
74
73
 
75
- def create_reprojection(
76
- self, data_request_id: str, crs: str, resolution: float
77
- ) -> Reprojection:
78
- # TODO: check if data request is completed before creating reprojection
74
+ def create_transformation(
75
+ self, data_request_id: str, crs: str, spatial_resolution: float
76
+ ) -> Transformation:
79
77
  res = self._post(
80
- url="/v0/reprojections",
81
- model=ReprojectionCreate(
78
+ url="/v0/transformations",
79
+ model=TransformationCreate(
82
80
  data_request_id=data_request_id,
83
81
  crs=crs,
84
- resolution=resolution,
82
+ spatial_resolution=spatial_resolution,
85
83
  ),
86
84
  )
87
- return Reprojection(**res)
85
+ return Transformation(**res)
88
86
 
89
- def get_reprojection(self, id: str) -> Reprojection:
90
- res = self._get(url=f"/v0/reprojections/{id}")
91
- return Reprojection(**res)
87
+ def get_transformation(self, id: str) -> Transformation:
88
+ res = self._get(url=f"/v0/transformations/{id}")
89
+ return Transformation(**res)
92
90
 
93
- def list_reprojections(self) -> List[Reprojection]:
94
- res = self._get(url="/v0/reprojections")
95
- return [Reprojection(**record) for record in res["records"]]
91
+ def list_transformations(self) -> List[Transformation]:
92
+ res = self._get(url="/v0/transformations")
93
+ return [Transformation(**record) for record in res["records"]]
96
94
 
97
- def query(self, sql):
95
+ def query(self, sql: str) -> pd.DataFrame:
98
96
  if self._snowflake_creds is None:
99
97
  res = self._get(url="/v0/data-access-credentials")
100
98
  self._snowflake_creds = SnowflakeCredentials(**res)
@@ -111,7 +109,7 @@ class Client:
111
109
 
112
110
  def recover_api_key(self, email: str) -> RecoverAPIKey:
113
111
  res = self._post(
114
- url=f"/v0/recover-api-key",
112
+ url="/v0/api-key/recover",
115
113
  model=RecoverAPIKeyRequest(email=email),
116
114
  skip_auth=True,
117
115
  )
@@ -119,16 +117,49 @@ class Client:
119
117
  return RecoverAPIKey(**res)
120
118
 
121
119
  def rotate_api_key(self) -> RotateAPIKey:
122
- res = self._post(url=f"/v0/rotate-api-key", model=RotateAPIKeyRequest())
120
+ res = self._post(url=f"/v0/api-key/rotate", model=RotateAPIKeyRequest())
123
121
 
124
122
  return RotateAPIKey(**res)
125
123
 
124
+ def sign_up(
125
+ self, organisation: Dict[str, str], user: Dict[str, str]
126
+ ) -> SignUpResponse:
127
+ res = self._post(
128
+ url="/v0/sign-up",
129
+ model=SignUpRequest(
130
+ organisation=OrganisationCreate(**organisation),
131
+ user=UserCreate(**user),
132
+ ),
133
+ skip_auth=True,
134
+ )
135
+
136
+ return SignUpResponse(**res)
137
+
138
+ def create_user(self, first_name: str, last_name: str, email: str) -> User:
139
+ res = self._post(
140
+ url="/v0/users",
141
+ model=UserCreate(
142
+ first_name=first_name,
143
+ last_name=last_name,
144
+ email=email,
145
+ ),
146
+ )
147
+ return User(**res)
148
+
149
+ def get_user(self, id: str) -> User:
150
+ res = self._get(url=f"/v0/users/{id}")
151
+ return User(**res)
152
+
153
+ def list_users(self) -> List[User]:
154
+ res = self._get(url="/v0/users")
155
+ return [User(**record) for record in res["records"]]
156
+
126
157
  def _request(self, method: str, url: str, skip_auth=False, **kwargs) -> Dict:
127
158
 
128
159
  if skip_auth is False:
129
160
  self._set_auth()
130
161
 
131
- headers = {"cecil-python-sdk-version": SDK_VERSION}
162
+ headers = {"cecil-python-sdk-version": __version__}
132
163
 
133
164
  try:
134
165
  r = requests.request(
@@ -144,7 +175,12 @@ class Client:
144
175
 
145
176
  except requests.exceptions.ConnectionError:
146
177
  raise Error("failed to connect to the Cecil Platform")
178
+
147
179
  except requests.exceptions.HTTPError as err:
180
+ message = f"Request failed with status code {err.response.status_code}"
181
+ if err.response.text != "":
182
+ message += f": {err.response.text}"
183
+
148
184
  match err.response.status_code:
149
185
  case 400:
150
186
  _handle_bad_request(err.response)
@@ -154,6 +190,8 @@ class Client:
154
190
  _handle_not_found(err.response)
155
191
  case 422:
156
192
  _handle_unprocessable_entity(err.response)
193
+ case 429:
194
+ _handle_too_many_requests(err.response)
157
195
  case 500:
158
196
  raise Error("internal server error")
159
197
  case _:
cecil/errors.py CHANGED
@@ -47,6 +47,18 @@ def _handle_not_found(response):
47
47
  raise Error("resource not found", details)
48
48
 
49
49
 
50
+ def _handle_too_many_requests(response):
51
+ if not _is_json(response.text):
52
+ raise Error("too many requests")
53
+
54
+ details = {}
55
+
56
+ for key, value in response.json().items():
57
+ details[_format_json_key(key)] = value
58
+
59
+ raise Error("too many requests", details)
60
+
61
+
50
62
  def _handle_unprocessable_entity(response):
51
63
  if not _is_json(response.text):
52
64
  raise Error(f"failed to process request")
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
 
@@ -59,6 +36,11 @@ class DataRequestCreate(BaseModel):
59
36
  dataset_id: str
60
37
 
61
38
 
39
+ class OrganisationCreate(BaseModel):
40
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
41
+ name: str
42
+
43
+
62
44
  class RecoverAPIKey(BaseModel):
63
45
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
64
46
  message: str
@@ -78,21 +60,21 @@ class RotateAPIKeyRequest(BaseModel):
78
60
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
79
61
 
80
62
 
81
- class Reprojection(BaseModel):
63
+ class Transformation(BaseModel):
82
64
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
83
65
  id: str
84
66
  data_request_id: str
85
67
  crs: str
86
- resolution: float
68
+ spatial_resolution: float
87
69
  created_at: datetime.datetime
88
70
  created_by: str
89
71
 
90
72
 
91
- class ReprojectionCreate(BaseModel):
73
+ class TransformationCreate(BaseModel):
92
74
  model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
93
75
  data_request_id: str
94
76
  crs: str
95
- resolution: float
77
+ spatial_resolution: float
96
78
 
97
79
 
98
80
  class SnowflakeCredentials(BaseModel):
@@ -100,3 +82,31 @@ class SnowflakeCredentials(BaseModel):
100
82
  account: SecretStr
101
83
  user: SecretStr
102
84
  password: SecretStr
85
+
86
+
87
+ class User(BaseModel):
88
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
89
+ id: str
90
+ first_name: str
91
+ last_name: str
92
+ email: str
93
+ created_at: datetime.datetime
94
+ created_by: str
95
+
96
+
97
+ class UserCreate(BaseModel):
98
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
99
+ first_name: str
100
+ last_name: str
101
+ email: str
102
+
103
+
104
+ class SignUpRequest(BaseModel):
105
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
106
+ organisation: OrganisationCreate
107
+ user: UserCreate
108
+
109
+
110
+ class SignUpResponse(BaseModel):
111
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
112
+ message: str
cecil/version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.0.17"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: cecil
3
- Version: 0.0.15
3
+ Version: 0.0.17
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=n8Ft-K1XLt430RCM6jUgBwWhwQlCJT8umyZp6RKpIyw,7054
3
+ cecil/errors.py,sha256=ZNiSTYH2MgNZ7tNIgV07-Ge3KtmdncfzWiBi9yjURGs,1818
4
+ cecil/models.py,sha256=0n5q7oUA7khdc5eB6C8aSW6Yx5LgyN_fA-GHb8y9Z7M,2834
5
+ cecil/version.py,sha256=WlMBNrzKywm5RAbqEVgEgcG7Opc4cf3-wpAnO8PdoEI,23
6
+ cecil-0.0.17.dist-info/METADATA,sha256=pK-PX-p6uyf-_MNbICW_ukGHnKzdnHiSFz5pFW3dOLw,2659
7
+ cecil-0.0.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
8
+ cecil-0.0.17.dist-info/licenses/LICENSE.txt,sha256=mUexcmfYx3bG1VIzAdQTOf_NzStYw6-QkKVdUY_d4i4,1066
9
+ cecil-0.0.17.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,8 +0,0 @@
1
- cecil/__init__.py,sha256=9-O5DAnOHvBuzPAjkK10GiOnCY9rb4SW1k0CIrHorr0,53
2
- cecil/client.py,sha256=4ft1FL6KePfleTf-oepi10yRJ3RdVeZ0qNRdFyLm6c8,5783
3
- cecil/errors.py,sha256=r9NNRtv_oO_hblnh-VOlAf767cJqmlnbJfHB63OA_zY,1538
4
- cecil/models.py,sha256=6p9AedCDdkK-ptK2r5pc9AKG8rgZHw-YYDvRgXPAQqI,2544
5
- cecil-0.0.15.dist-info/METADATA,sha256=wnQowxMqcWRjlNsThxwypFnck_TuDDFmNCzZr34_Y54,2677
6
- cecil-0.0.15.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
7
- cecil-0.0.15.dist-info/licenses/LICENSE.txt,sha256=mUexcmfYx3bG1VIzAdQTOf_NzStYw6-QkKVdUY_d4i4,1066
8
- cecil-0.0.15.dist-info/RECORD,,