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 +1 -0
- cecil/client.py +70 -32
- cecil/errors.py +12 -0
- cecil/models.py +38 -28
- cecil/version.py +1 -0
- {cecil-0.0.15.dist-info → cecil-0.0.17.dist-info}/METADATA +12 -12
- cecil-0.0.17.dist-info/RECORD +9 -0
- {cecil-0.0.15.dist-info → cecil-0.0.17.dist-info}/WHEEL +1 -1
- cecil-0.0.15.dist-info/RECORD +0 -8
- {cecil-0.0.15.dist-info → cecil-0.0.17.dist-info}/licenses/LICENSE.txt +0 -0
cecil/__init__.py
CHANGED
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
|
-
|
|
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
|
|
76
|
-
self, data_request_id: str, crs: str,
|
|
77
|
-
) ->
|
|
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/
|
|
81
|
-
model=
|
|
78
|
+
url="/v0/transformations",
|
|
79
|
+
model=TransformationCreate(
|
|
82
80
|
data_request_id=data_request_id,
|
|
83
81
|
crs=crs,
|
|
84
|
-
|
|
82
|
+
spatial_resolution=spatial_resolution,
|
|
85
83
|
),
|
|
86
84
|
)
|
|
87
|
-
return
|
|
85
|
+
return Transformation(**res)
|
|
88
86
|
|
|
89
|
-
def
|
|
90
|
-
res = self._get(url=f"/v0/
|
|
91
|
-
return
|
|
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
|
|
94
|
-
res = self._get(url="/v0/
|
|
95
|
-
return [
|
|
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=
|
|
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/
|
|
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":
|
|
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
|
|
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
|
|
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
|
-
|
|
68
|
+
spatial_resolution: float
|
|
87
69
|
created_at: datetime.datetime
|
|
88
70
|
created_by: str
|
|
89
71
|
|
|
90
72
|
|
|
91
|
-
class
|
|
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
|
-
|
|
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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cecil
|
|
3
|
-
Version: 0.0.
|
|
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)
|
|
74
|
+
print(client.get_data_request(my_data_request.id))
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
### Create a
|
|
77
|
+
### Create a transformation using the Cecil client
|
|
78
78
|
|
|
79
79
|
```python
|
|
80
|
-
|
|
80
|
+
my_transformation = client.create_transformation(
|
|
81
81
|
data_request_id=my_data_request.id,
|
|
82
82
|
crs="EPSG:4326",
|
|
83
|
-
|
|
83
|
+
spatial_resolution=0.005,
|
|
84
84
|
)
|
|
85
85
|
|
|
86
|
-
print(client.
|
|
86
|
+
print(client.get_transformation(my_transformation.id))
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
### Query data (once
|
|
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
|
-
|
|
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.
|
|
112
|
+
client.list_transformations()
|
|
113
113
|
|
|
114
|
-
client.
|
|
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,,
|
cecil-0.0.15.dist-info/RECORD
DELETED
|
@@ -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,,
|
|
File without changes
|