terrakio-core 0.3.2__py3-none-any.whl → 0.3.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 terrakio-core might be problematic. Click here for more details.
- terrakio_core/__init__.py +1 -1
- terrakio_core/auth.py +0 -15
- terrakio_core/client.py +367 -115
- terrakio_core/config.py +63 -21
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.4.dist-info}/METADATA +1 -1
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.4.dist-info}/RECORD +8 -8
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.4.dist-info}/WHEEL +0 -0
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.4.dist-info}/top_level.txt +0 -0
terrakio_core/__init__.py
CHANGED
terrakio_core/auth.py
CHANGED
|
@@ -127,12 +127,8 @@ class AuthClient:
|
|
|
127
127
|
API key
|
|
128
128
|
|
|
129
129
|
Raises:
|
|
130
|
-
ConfigurationError: If not authenticated
|
|
131
130
|
APIError: If refresh fails
|
|
132
131
|
"""
|
|
133
|
-
if not self.token:
|
|
134
|
-
raise ConfigurationError("Not authenticated. Call login() first.")
|
|
135
|
-
|
|
136
132
|
endpoint = f"{self.base_url}/users/refresh_key"
|
|
137
133
|
|
|
138
134
|
try:
|
|
@@ -167,14 +163,9 @@ class AuthClient:
|
|
|
167
163
|
API key
|
|
168
164
|
|
|
169
165
|
Raises:
|
|
170
|
-
ConfigurationError: If not authenticated
|
|
171
166
|
APIError: If retrieval fails
|
|
172
167
|
"""
|
|
173
|
-
if not self.token:
|
|
174
|
-
raise ConfigurationError("Not authenticated. Call login() first.")
|
|
175
|
-
|
|
176
168
|
endpoint = f"{self.base_url}/users/key"
|
|
177
|
-
|
|
178
169
|
try:
|
|
179
170
|
# Use session with updated headers from login
|
|
180
171
|
response = self.session.get(
|
|
@@ -207,14 +198,9 @@ class AuthClient:
|
|
|
207
198
|
User information data
|
|
208
199
|
|
|
209
200
|
Raises:
|
|
210
|
-
ConfigurationError: If not authenticated
|
|
211
201
|
APIError: If retrieval fails
|
|
212
202
|
"""
|
|
213
|
-
if not self.token:
|
|
214
|
-
raise ConfigurationError("Not authenticated. Call login() first.")
|
|
215
|
-
|
|
216
203
|
endpoint = f"{self.base_url}/users/info"
|
|
217
|
-
|
|
218
204
|
try:
|
|
219
205
|
# Use session with updated headers from login
|
|
220
206
|
response = self.session.get(
|
|
@@ -222,7 +208,6 @@ class AuthClient:
|
|
|
222
208
|
verify=self.verify,
|
|
223
209
|
timeout=self.timeout
|
|
224
210
|
)
|
|
225
|
-
|
|
226
211
|
if not response.ok:
|
|
227
212
|
error_msg = f"Failed to retrieve user info: {response.status_code} {response.reason}"
|
|
228
213
|
try:
|
terrakio_core/client.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
import asyncio
|
|
3
3
|
from io import BytesIO
|
|
4
4
|
from typing import Dict, Any, Optional, Union
|
|
5
|
+
from functools import wraps
|
|
5
6
|
|
|
6
7
|
import requests
|
|
7
8
|
import aiohttp
|
|
@@ -18,8 +19,39 @@ import logging
|
|
|
18
19
|
import textwrap
|
|
19
20
|
|
|
20
21
|
|
|
22
|
+
def require_api_key(func):
|
|
23
|
+
"""
|
|
24
|
+
Decorator to ensure an API key is available before a method can be executed.
|
|
25
|
+
Will check for the presence of an API key before allowing the function to be called.
|
|
26
|
+
"""
|
|
27
|
+
@wraps(func)
|
|
28
|
+
def wrapper(self, *args, **kwargs):
|
|
29
|
+
# Check if the API key is set
|
|
30
|
+
if not self.key:
|
|
31
|
+
error_msg = "No API key found. Please provide an API key to use this client."
|
|
32
|
+
if not self.quiet:
|
|
33
|
+
print(error_msg)
|
|
34
|
+
print("API key is required for this function.")
|
|
35
|
+
print("You can set an API key by either:")
|
|
36
|
+
print("1. Loading from a config file")
|
|
37
|
+
print("2. Using the login() method: client.login(email='your-email@example.com', password='your-password')")
|
|
38
|
+
raise ConfigurationError(error_msg)
|
|
39
|
+
|
|
40
|
+
# Check if URL is set (required for API calls)
|
|
41
|
+
if not self.url:
|
|
42
|
+
if not self.quiet:
|
|
43
|
+
print("API URL is not set. Using default API URL: https://api.terrak.io")
|
|
44
|
+
self.url = "https://api.terrak.io"
|
|
45
|
+
|
|
46
|
+
return func(self, *args, **kwargs)
|
|
47
|
+
|
|
48
|
+
# Mark as decorated to avoid double decoration
|
|
49
|
+
wrapper._is_decorated = True
|
|
50
|
+
return wrapper
|
|
51
|
+
|
|
52
|
+
|
|
21
53
|
class BaseClient:
|
|
22
|
-
def __init__(self, url: Optional[str] = None,
|
|
54
|
+
def __init__(self, url: Optional[str] = None,
|
|
23
55
|
auth_url: Optional[str] = "https://dev-au.terrak.io",
|
|
24
56
|
quiet: bool = False, config_file: Optional[str] = None,
|
|
25
57
|
verify: bool = True, timeout: int = 300):
|
|
@@ -28,57 +60,96 @@ class BaseClient:
|
|
|
28
60
|
self.verify = verify
|
|
29
61
|
self.timeout = timeout
|
|
30
62
|
self.auth_client = None
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
63
|
+
# Initialize session early to avoid AttributeError
|
|
64
|
+
self.session = requests.Session()
|
|
65
|
+
self.user_management = None
|
|
66
|
+
self.dataset_management = None
|
|
67
|
+
self.mass_stats = None
|
|
68
|
+
self._aiohttp_session = None
|
|
69
|
+
|
|
70
|
+
# if auth_url:
|
|
71
|
+
# from terrakio_core.auth import AuthClient
|
|
72
|
+
# self.auth_client = AuthClient(
|
|
73
|
+
# base_url=auth_url,
|
|
74
|
+
# verify=verify,
|
|
75
|
+
# timeout=timeout
|
|
76
|
+
# )
|
|
38
77
|
self.url = url
|
|
39
|
-
self.key =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
self.key = None
|
|
79
|
+
|
|
80
|
+
# Load configuration from file
|
|
81
|
+
from terrakio_core.config import read_config_file, DEFAULT_CONFIG_FILE
|
|
82
|
+
|
|
83
|
+
if config_file is None:
|
|
84
|
+
config_file = DEFAULT_CONFIG_FILE
|
|
85
|
+
|
|
86
|
+
# Get config using the read_config_file function that now handles all cases
|
|
87
|
+
config = read_config_file(config_file, quiet=self.quiet)
|
|
88
|
+
|
|
89
|
+
# If URL is not provided, try to get it from config
|
|
90
|
+
if self.url is None:
|
|
91
|
+
self.url = config.get('url')
|
|
92
|
+
|
|
93
|
+
# Get API key from config file (never from parameters)
|
|
94
|
+
self.key = config.get('key')
|
|
95
|
+
|
|
96
|
+
self.token = config.get('token')
|
|
97
|
+
|
|
98
|
+
# Update auth_client with token if it exists
|
|
99
|
+
if self.auth_client and self.token:
|
|
100
|
+
self.auth_client.token = self.token
|
|
101
|
+
self.auth_client.session.headers.update({
|
|
102
|
+
"Authorization": self.token
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
# If we have a key, we're good to go even if not logged in
|
|
106
|
+
if self.key:
|
|
107
|
+
# If URL is missing but we have a key, use the default URL
|
|
108
|
+
if not self.url:
|
|
109
|
+
self.url = "https://api.terrak.io"
|
|
110
|
+
print("the token is! ", self.token)
|
|
111
|
+
print("the key is! ", self.key)
|
|
112
|
+
# Update session headers with the key
|
|
113
|
+
headers = {
|
|
114
|
+
'Content-Type': 'application/json',
|
|
115
|
+
'x-api-key': self.key
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Only add Authorization header if token exists and is not None
|
|
119
|
+
if self.token:
|
|
120
|
+
headers['Authorization'] = self.token
|
|
121
|
+
|
|
122
|
+
self.session.headers.update(headers)
|
|
123
|
+
print("the session headers are ", self.session.headers)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if not self.quiet and config.get('user_email'):
|
|
127
|
+
print(f"Using API key for: {config.get('user_email')}")
|
|
128
|
+
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Check if we have the required configuration
|
|
66
132
|
if not self.key:
|
|
67
|
-
|
|
133
|
+
# No API key available - inform the user
|
|
134
|
+
if not self.quiet:
|
|
135
|
+
print("API key is required to use this client.")
|
|
136
|
+
print("You can set an API key by either:")
|
|
137
|
+
print("1. Loading from a config file")
|
|
138
|
+
print("2. Using the login() method: client.login(email='your-email@example.com', password='your-password')")
|
|
139
|
+
return
|
|
140
|
+
|
|
68
141
|
self.url = self.url.rstrip('/')
|
|
69
142
|
if not self.quiet:
|
|
70
143
|
print(f"Using Terrakio API at: {self.url}")
|
|
71
|
-
|
|
144
|
+
|
|
145
|
+
# Update the session headers with API key
|
|
72
146
|
self.session.headers.update({
|
|
73
147
|
'Content-Type': 'application/json',
|
|
74
148
|
'x-api-key': self.key
|
|
75
149
|
})
|
|
76
|
-
self.user_management = None
|
|
77
|
-
self.dataset_management = None
|
|
78
|
-
self.mass_stats = None
|
|
79
|
-
self._aiohttp_session = None
|
|
80
150
|
|
|
81
151
|
@property
|
|
152
|
+
@require_api_key
|
|
82
153
|
async def aiohttp_session(self):
|
|
83
154
|
if self._aiohttp_session is None or self._aiohttp_session.closed:
|
|
84
155
|
self._aiohttp_session = aiohttp.ClientSession(
|
|
@@ -90,6 +161,7 @@ class BaseClient:
|
|
|
90
161
|
)
|
|
91
162
|
return self._aiohttp_session
|
|
92
163
|
|
|
164
|
+
@require_api_key
|
|
93
165
|
async def wcs_async(self, expr: str, feature: Union[Dict[str, Any], ShapelyGeometry],
|
|
94
166
|
in_crs: str = "epsg:4326", out_crs: str = "epsg:4326",
|
|
95
167
|
output: str = "csv", resolution: int = -1, buffer: bool = False,
|
|
@@ -130,7 +202,6 @@ class BaseClient:
|
|
|
130
202
|
"resolution": resolution,
|
|
131
203
|
**kwargs
|
|
132
204
|
}
|
|
133
|
-
print("the payload is ", payload)
|
|
134
205
|
request_url = f"{self.url}/geoquery"
|
|
135
206
|
for attempt in range(retry + 1):
|
|
136
207
|
try:
|
|
@@ -187,38 +258,67 @@ class BaseClient:
|
|
|
187
258
|
raise
|
|
188
259
|
continue
|
|
189
260
|
|
|
261
|
+
@require_api_key
|
|
190
262
|
async def close_async(self):
|
|
191
263
|
"""Close the aiohttp session"""
|
|
192
264
|
if self._aiohttp_session and not self._aiohttp_session.closed:
|
|
193
265
|
await self._aiohttp_session.close()
|
|
194
266
|
self._aiohttp_session = None
|
|
195
267
|
|
|
268
|
+
@require_api_key
|
|
196
269
|
async def __aenter__(self):
|
|
197
270
|
return self
|
|
198
271
|
|
|
272
|
+
@require_api_key
|
|
199
273
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
200
274
|
await self.close_async()
|
|
201
275
|
|
|
202
276
|
def signup(self, email: str, password: str) -> Dict[str, Any]:
|
|
203
277
|
if not self.auth_client:
|
|
204
|
-
|
|
278
|
+
from terrakio_core.auth import AuthClient
|
|
279
|
+
self.auth_client = AuthClient(
|
|
280
|
+
base_url=self.url,
|
|
281
|
+
verify=self.verify,
|
|
282
|
+
timeout=self.timeout
|
|
283
|
+
)
|
|
284
|
+
self.auth_client.session = self.session
|
|
205
285
|
return self.auth_client.signup(email, password)
|
|
206
286
|
|
|
207
287
|
def login(self, email: str, password: str) -> Dict[str, str]:
|
|
288
|
+
|
|
208
289
|
if not self.auth_client:
|
|
209
|
-
|
|
210
|
-
|
|
290
|
+
from terrakio_core.auth import AuthClient
|
|
291
|
+
self.auth_client = AuthClient(
|
|
292
|
+
base_url=self.url,
|
|
293
|
+
verify=self.verify,
|
|
294
|
+
timeout=self.timeout
|
|
295
|
+
)
|
|
296
|
+
self.auth_client.session = self.session
|
|
211
297
|
try:
|
|
212
298
|
# First attempt to login
|
|
213
299
|
token_response = self.auth_client.login(email, password)
|
|
214
300
|
|
|
215
|
-
print("the token response is ", token_response)
|
|
216
301
|
# Only proceed with API key retrieval if login was successful
|
|
217
302
|
if token_response:
|
|
218
303
|
# After successful login, get the API key
|
|
219
|
-
api_key_response = self.view_api_key()
|
|
304
|
+
api_key_response = self.auth_client.view_api_key()
|
|
220
305
|
self.key = api_key_response
|
|
221
306
|
|
|
307
|
+
# Make sure URL is set
|
|
308
|
+
if not self.url:
|
|
309
|
+
self.url = "https://api.terrak.io"
|
|
310
|
+
|
|
311
|
+
# Make sure session headers are updated with the new key
|
|
312
|
+
self.session.headers.update({
|
|
313
|
+
'Content-Type': 'application/json',
|
|
314
|
+
'x-api-key': self.key
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
# Set URL if not already set
|
|
318
|
+
if not self.url:
|
|
319
|
+
self.url = "https://api.terrak.io"
|
|
320
|
+
self.url = self.url.rstrip('/')
|
|
321
|
+
|
|
222
322
|
# Save email and API key to config file
|
|
223
323
|
import os
|
|
224
324
|
import json
|
|
@@ -230,6 +330,7 @@ class BaseClient:
|
|
|
230
330
|
config = json.load(f)
|
|
231
331
|
config["EMAIL"] = email
|
|
232
332
|
config["TERRAKIO_API_KEY"] = self.key
|
|
333
|
+
config["PERSONAL_TOKEN"] = token_response
|
|
233
334
|
|
|
234
335
|
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
235
336
|
with open(config_path, 'w') as f:
|
|
@@ -237,6 +338,7 @@ class BaseClient:
|
|
|
237
338
|
|
|
238
339
|
if not self.quiet:
|
|
239
340
|
print(f"Successfully authenticated as: {email}")
|
|
341
|
+
print(f"Using Terrakio API at: {self.url}")
|
|
240
342
|
print(f"API key saved to {config_path}")
|
|
241
343
|
except Exception as e:
|
|
242
344
|
if not self.quiet:
|
|
@@ -248,47 +350,81 @@ class BaseClient:
|
|
|
248
350
|
print(f"Login failed: {str(e)}")
|
|
249
351
|
raise
|
|
250
352
|
|
|
353
|
+
@require_api_key
|
|
251
354
|
def refresh_api_key(self) -> str:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
355
|
+
# If we have auth_client and it has a token, refresh the key
|
|
356
|
+
if self.auth_client and self.auth_client.token:
|
|
357
|
+
self.key = self.auth_client.refresh_api_key()
|
|
358
|
+
self.session.headers.update({'x-api-key': self.key})
|
|
359
|
+
|
|
360
|
+
# Update the config file with the new key
|
|
361
|
+
import os
|
|
362
|
+
config_path = os.path.join(os.environ.get("HOME", ""), ".tkio_config.json")
|
|
363
|
+
try:
|
|
364
|
+
config = {"EMAIL": "", "TERRAKIO_API_KEY": ""}
|
|
365
|
+
if os.path.exists(config_path):
|
|
366
|
+
with open(config_path, 'r') as f:
|
|
367
|
+
config = json.load(f)
|
|
368
|
+
config["TERRAKIO_API_KEY"] = self.key
|
|
369
|
+
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
370
|
+
with open(config_path, 'w') as f:
|
|
371
|
+
json.dump(config, f, indent=4)
|
|
372
|
+
if not self.quiet:
|
|
373
|
+
print(f"API key generated successfully and updated in {config_path}")
|
|
374
|
+
except Exception as e:
|
|
375
|
+
if not self.quiet:
|
|
376
|
+
print(f"Warning: Failed to update config file: {e}")
|
|
377
|
+
return self.key
|
|
378
|
+
else:
|
|
379
|
+
# If we don't have auth_client with a token but have a key already, return it
|
|
380
|
+
if self.key:
|
|
381
|
+
if not self.quiet:
|
|
382
|
+
print("Using existing API key from config file.")
|
|
383
|
+
return self.key
|
|
384
|
+
else:
|
|
385
|
+
raise ConfigurationError("No authentication token available. Please login first to refresh the API key.")
|
|
386
|
+
|
|
387
|
+
@require_api_key
|
|
388
|
+
def view_api_key(self) -> str:
|
|
389
|
+
# If we have the auth client and token, refresh the key
|
|
390
|
+
if self.auth_client and self.auth_client.token:
|
|
391
|
+
self.key = self.auth_client.view_api_key()
|
|
392
|
+
self.session.headers.update({'x-api-key': self.key})
|
|
393
|
+
|
|
274
394
|
return self.key
|
|
275
395
|
|
|
396
|
+
@require_api_key
|
|
276
397
|
def view_api_key(self) -> str:
|
|
398
|
+
# If we have the auth client and token, refresh the key
|
|
277
399
|
if not self.auth_client:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
400
|
+
from terrakio_core.auth import AuthClient
|
|
401
|
+
self.auth_client = AuthClient(
|
|
402
|
+
base_url=self.url,
|
|
403
|
+
verify=self.verify,
|
|
404
|
+
timeout=self.timeout
|
|
405
|
+
)
|
|
406
|
+
self.auth_client.session = self.session
|
|
407
|
+
return self.auth_client.view_api_key()
|
|
284
408
|
|
|
409
|
+
# @require_api_key
|
|
410
|
+
# def get_user_info(self) -> Dict[str, Any]:
|
|
411
|
+
# return self.auth_client.get_user_info()
|
|
412
|
+
@require_api_key
|
|
285
413
|
def get_user_info(self) -> Dict[str, Any]:
|
|
414
|
+
# Initialize auth_client if it doesn't exist
|
|
286
415
|
if not self.auth_client:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
416
|
+
from terrakio_core.auth import AuthClient
|
|
417
|
+
self.auth_client = AuthClient(
|
|
418
|
+
base_url=self.url,
|
|
419
|
+
verify=self.verify,
|
|
420
|
+
timeout=self.timeout
|
|
421
|
+
)
|
|
422
|
+
# Use the same session as the base client
|
|
423
|
+
self.auth_client.session = self.session
|
|
424
|
+
|
|
290
425
|
return self.auth_client.get_user_info()
|
|
291
426
|
|
|
427
|
+
@require_api_key
|
|
292
428
|
def wcs(self, expr: str, feature: Union[Dict[str, Any], ShapelyGeometry], in_crs: str = "epsg:4326",
|
|
293
429
|
out_crs: str = "epsg:4326", output: str = "csv", resolution: int = -1,
|
|
294
430
|
**kwargs):
|
|
@@ -310,8 +446,6 @@ class BaseClient:
|
|
|
310
446
|
}
|
|
311
447
|
request_url = f"{self.url}/geoquery"
|
|
312
448
|
try:
|
|
313
|
-
print("the request url is ", request_url)
|
|
314
|
-
print("the payload is ", payload)
|
|
315
449
|
response = self.session.post(request_url, json=payload, timeout=self.timeout, verify=self.verify)
|
|
316
450
|
print("the response is ", response.text)
|
|
317
451
|
if not response.ok:
|
|
@@ -341,6 +475,7 @@ class BaseClient:
|
|
|
341
475
|
raise APIError(f"Request failed: {str(e)}")
|
|
342
476
|
|
|
343
477
|
# Admin/protected methods
|
|
478
|
+
@require_api_key
|
|
344
479
|
def _get_user_by_id(self, user_id: str):
|
|
345
480
|
if not self.user_management:
|
|
346
481
|
from terrakio_core.user_management import UserManagement
|
|
@@ -354,6 +489,7 @@ class BaseClient:
|
|
|
354
489
|
)
|
|
355
490
|
return self.user_management.get_user_by_id(user_id)
|
|
356
491
|
|
|
492
|
+
@require_api_key
|
|
357
493
|
def _get_user_by_email(self, email: str):
|
|
358
494
|
if not self.user_management:
|
|
359
495
|
from terrakio_core.user_management import UserManagement
|
|
@@ -367,6 +503,7 @@ class BaseClient:
|
|
|
367
503
|
)
|
|
368
504
|
return self.user_management.get_user_by_email(email)
|
|
369
505
|
|
|
506
|
+
@require_api_key
|
|
370
507
|
def _list_users(self, substring: str = None, uid: bool = False):
|
|
371
508
|
if not self.user_management:
|
|
372
509
|
from terrakio_core.user_management import UserManagement
|
|
@@ -380,6 +517,7 @@ class BaseClient:
|
|
|
380
517
|
)
|
|
381
518
|
return self.user_management.list_users(substring=substring, uid=uid)
|
|
382
519
|
|
|
520
|
+
@require_api_key
|
|
383
521
|
def _edit_user(self, user_id: str, uid: str = None, email: str = None, role: str = None, apiKey: str = None, groups: list = None, quota: int = None):
|
|
384
522
|
if not self.user_management:
|
|
385
523
|
from terrakio_core.user_management import UserManagement
|
|
@@ -401,6 +539,7 @@ class BaseClient:
|
|
|
401
539
|
quota=quota
|
|
402
540
|
)
|
|
403
541
|
|
|
542
|
+
@require_api_key
|
|
404
543
|
def _reset_quota(self, email: str, quota: int = None):
|
|
405
544
|
if not self.user_management:
|
|
406
545
|
from terrakio_core.user_management import UserManagement
|
|
@@ -414,6 +553,7 @@ class BaseClient:
|
|
|
414
553
|
)
|
|
415
554
|
return self.user_management.reset_quota(email=email, quota=quota)
|
|
416
555
|
|
|
556
|
+
@require_api_key
|
|
417
557
|
def _delete_user(self, uid: str):
|
|
418
558
|
if not self.user_management:
|
|
419
559
|
from terrakio_core.user_management import UserManagement
|
|
@@ -428,6 +568,7 @@ class BaseClient:
|
|
|
428
568
|
return self.user_management.delete_user(uid=uid)
|
|
429
569
|
|
|
430
570
|
# Dataset management protected methods
|
|
571
|
+
@require_api_key
|
|
431
572
|
def _get_dataset(self, name: str, collection: str = "terrakio-datasets"):
|
|
432
573
|
if not self.dataset_management:
|
|
433
574
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -441,6 +582,7 @@ class BaseClient:
|
|
|
441
582
|
)
|
|
442
583
|
return self.dataset_management.get_dataset(name=name, collection=collection)
|
|
443
584
|
|
|
585
|
+
@require_api_key
|
|
444
586
|
def _list_datasets(self, substring: str = None, collection: str = "terrakio-datasets"):
|
|
445
587
|
if not self.dataset_management:
|
|
446
588
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -454,6 +596,7 @@ class BaseClient:
|
|
|
454
596
|
)
|
|
455
597
|
return self.dataset_management.list_datasets(substring=substring, collection=collection)
|
|
456
598
|
|
|
599
|
+
@require_api_key
|
|
457
600
|
def _create_dataset(self, name: str, collection: str = "terrakio-datasets", **kwargs):
|
|
458
601
|
if not self.dataset_management:
|
|
459
602
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -467,6 +610,7 @@ class BaseClient:
|
|
|
467
610
|
)
|
|
468
611
|
return self.dataset_management.create_dataset(name=name, collection=collection, **kwargs)
|
|
469
612
|
|
|
613
|
+
@require_api_key
|
|
470
614
|
def _update_dataset(self, name: str, append: bool = True, collection: str = "terrakio-datasets", **kwargs):
|
|
471
615
|
if not self.dataset_management:
|
|
472
616
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -480,6 +624,7 @@ class BaseClient:
|
|
|
480
624
|
)
|
|
481
625
|
return self.dataset_management.update_dataset(name=name, append=append, collection=collection, **kwargs)
|
|
482
626
|
|
|
627
|
+
@require_api_key
|
|
483
628
|
def _overwrite_dataset(self, name: str, collection: str = "terrakio-datasets", **kwargs):
|
|
484
629
|
if not self.dataset_management:
|
|
485
630
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -493,6 +638,7 @@ class BaseClient:
|
|
|
493
638
|
)
|
|
494
639
|
return self.dataset_management.overwrite_dataset(name=name, collection=collection, **kwargs)
|
|
495
640
|
|
|
641
|
+
@require_api_key
|
|
496
642
|
def _delete_dataset(self, name: str, collection: str = "terrakio-datasets"):
|
|
497
643
|
if not self.dataset_management:
|
|
498
644
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -506,6 +652,7 @@ class BaseClient:
|
|
|
506
652
|
)
|
|
507
653
|
return self.dataset_management.delete_dataset(name=name, collection=collection)
|
|
508
654
|
|
|
655
|
+
@require_api_key
|
|
509
656
|
def close(self):
|
|
510
657
|
"""Close all client sessions"""
|
|
511
658
|
self.session.close()
|
|
@@ -532,13 +679,16 @@ class BaseClient:
|
|
|
532
679
|
# Event loop may already be closed, ignore
|
|
533
680
|
pass
|
|
534
681
|
|
|
682
|
+
@require_api_key
|
|
535
683
|
def __enter__(self):
|
|
536
684
|
return self
|
|
537
685
|
|
|
686
|
+
@require_api_key
|
|
538
687
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
539
688
|
self.close()
|
|
540
689
|
|
|
541
690
|
@admin_only_params('location', 'force_loc', 'server')
|
|
691
|
+
@require_api_key
|
|
542
692
|
def execute_job(self, name, region, output, config, overwrite=False, skip_existing=False, request_json=None, manifest_json=None, location=None, force_loc=None, server="dev-au.terrak.io"):
|
|
543
693
|
if not self.mass_stats:
|
|
544
694
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -553,6 +703,7 @@ class BaseClient:
|
|
|
553
703
|
return self.mass_stats.execute_job(name, region, output, config, overwrite, skip_existing, request_json, manifest_json, location, force_loc, server)
|
|
554
704
|
|
|
555
705
|
|
|
706
|
+
@require_api_key
|
|
556
707
|
def get_mass_stats_task_id(self, name, stage, uid=None):
|
|
557
708
|
if not self.mass_stats:
|
|
558
709
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -566,6 +717,7 @@ class BaseClient:
|
|
|
566
717
|
)
|
|
567
718
|
return self.mass_stats.get_task_id(name, stage, uid)
|
|
568
719
|
|
|
720
|
+
@require_api_key
|
|
569
721
|
def track_mass_stats_job(self, ids: Optional[list] = None):
|
|
570
722
|
if not self.mass_stats:
|
|
571
723
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -579,6 +731,7 @@ class BaseClient:
|
|
|
579
731
|
)
|
|
580
732
|
return self.mass_stats.track_job(ids)
|
|
581
733
|
|
|
734
|
+
@require_api_key
|
|
582
735
|
def get_mass_stats_history(self, limit=100):
|
|
583
736
|
if not self.mass_stats:
|
|
584
737
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -592,6 +745,7 @@ class BaseClient:
|
|
|
592
745
|
)
|
|
593
746
|
return self.mass_stats.get_history(limit)
|
|
594
747
|
|
|
748
|
+
@require_api_key
|
|
595
749
|
def start_mass_stats_post_processing(self, process_name, data_name, output, consumer_path, overwrite=False):
|
|
596
750
|
if not self.mass_stats:
|
|
597
751
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -605,6 +759,7 @@ class BaseClient:
|
|
|
605
759
|
)
|
|
606
760
|
return self.mass_stats.start_post_processing(process_name, data_name, output, consumer_path, overwrite)
|
|
607
761
|
|
|
762
|
+
@require_api_key
|
|
608
763
|
def download_mass_stats_results(self, id=None, force_loc=False, **kwargs):
|
|
609
764
|
if not self.mass_stats:
|
|
610
765
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -618,6 +773,7 @@ class BaseClient:
|
|
|
618
773
|
)
|
|
619
774
|
return self.mass_stats.download_results(id, force_loc, **kwargs)
|
|
620
775
|
|
|
776
|
+
@require_api_key
|
|
621
777
|
def cancel_mass_stats_job(self, id):
|
|
622
778
|
if not self.mass_stats:
|
|
623
779
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -631,6 +787,7 @@ class BaseClient:
|
|
|
631
787
|
)
|
|
632
788
|
return self.mass_stats.cancel_job(id)
|
|
633
789
|
|
|
790
|
+
@require_api_key
|
|
634
791
|
def cancel_all_mass_stats_jobs(self):
|
|
635
792
|
if not self.mass_stats:
|
|
636
793
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -644,6 +801,7 @@ class BaseClient:
|
|
|
644
801
|
)
|
|
645
802
|
return self.mass_stats.cancel_all_jobs()
|
|
646
803
|
|
|
804
|
+
@require_api_key
|
|
647
805
|
def _create_pyramids(self, name, levels, config):
|
|
648
806
|
if not self.mass_stats:
|
|
649
807
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -657,6 +815,7 @@ class BaseClient:
|
|
|
657
815
|
)
|
|
658
816
|
return self.mass_stats.create_pyramids(name, levels, config)
|
|
659
817
|
|
|
818
|
+
@require_api_key
|
|
660
819
|
def random_sample(self, name, **kwargs):
|
|
661
820
|
if not self.mass_stats:
|
|
662
821
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -670,6 +829,7 @@ class BaseClient:
|
|
|
670
829
|
)
|
|
671
830
|
return self.mass_stats.random_sample(name, **kwargs)
|
|
672
831
|
|
|
832
|
+
@require_api_key
|
|
673
833
|
async def zonal_stats_async(self, gdb, expr, conc=20, inplace=False, output="csv",
|
|
674
834
|
in_crs="epsg:4326", out_crs="epsg:4326", resolution=-1, buffer=False):
|
|
675
835
|
"""
|
|
@@ -858,6 +1018,7 @@ class BaseClient:
|
|
|
858
1018
|
else:
|
|
859
1019
|
return result_gdf
|
|
860
1020
|
|
|
1021
|
+
@require_api_key
|
|
861
1022
|
def zonal_stats(self, gdb, expr, conc=20, inplace=False, output="csv",
|
|
862
1023
|
in_crs="epsg:4326", out_crs="epsg:4326", resolution=-1, buffer=False):
|
|
863
1024
|
"""
|
|
@@ -920,75 +1081,80 @@ class BaseClient:
|
|
|
920
1081
|
return result
|
|
921
1082
|
|
|
922
1083
|
# Group access management protected methods
|
|
1084
|
+
@require_api_key
|
|
923
1085
|
def _get_group_users_and_datasets(self, group_name: str):
|
|
924
|
-
if not
|
|
1086
|
+
if not self.group_access:
|
|
925
1087
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
926
1088
|
if not self.url or not self.key:
|
|
927
1089
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
928
|
-
self.
|
|
1090
|
+
self.group_access = GroupAccessManagement(
|
|
929
1091
|
api_url=self.url,
|
|
930
1092
|
api_key=self.key,
|
|
931
1093
|
verify=self.verify,
|
|
932
1094
|
timeout=self.timeout
|
|
933
1095
|
)
|
|
934
|
-
return self.
|
|
1096
|
+
return self.group_access.get_group_users_and_datasets(group_name=group_name)
|
|
935
1097
|
|
|
1098
|
+
@require_api_key
|
|
936
1099
|
def _add_group_to_dataset(self, dataset: str, group: str):
|
|
937
|
-
if not
|
|
1100
|
+
if not self.group_access:
|
|
938
1101
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
939
1102
|
if not self.url or not self.key:
|
|
940
1103
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
941
|
-
self.
|
|
1104
|
+
self.group_access = GroupAccessManagement(
|
|
942
1105
|
api_url=self.url,
|
|
943
1106
|
api_key=self.key,
|
|
944
1107
|
verify=self.verify,
|
|
945
1108
|
timeout=self.timeout
|
|
946
1109
|
)
|
|
947
|
-
return self.
|
|
1110
|
+
return self.group_access.add_group_to_dataset(dataset=dataset, group=group)
|
|
948
1111
|
|
|
1112
|
+
@require_api_key
|
|
949
1113
|
def _add_group_to_user(self, uid: str, group: str):
|
|
950
|
-
if not
|
|
1114
|
+
if not self.group_access:
|
|
951
1115
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
952
1116
|
if not self.url or not self.key:
|
|
953
1117
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
954
|
-
self.
|
|
1118
|
+
self.group_access = GroupAccessManagement(
|
|
955
1119
|
api_url=self.url,
|
|
956
1120
|
api_key=self.key,
|
|
957
1121
|
verify=self.verify,
|
|
958
1122
|
timeout=self.timeout
|
|
959
1123
|
)
|
|
960
|
-
|
|
961
|
-
return self.group_access_management.add_group_to_user(uid, group)
|
|
1124
|
+
return self.group_access.add_group_to_user(uid=uid, group=group)
|
|
962
1125
|
|
|
1126
|
+
@require_api_key
|
|
963
1127
|
def _delete_group_from_user(self, uid: str, group: str):
|
|
964
|
-
if not
|
|
1128
|
+
if not self.group_access:
|
|
965
1129
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
966
1130
|
if not self.url or not self.key:
|
|
967
1131
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
968
|
-
self.
|
|
1132
|
+
self.group_access = GroupAccessManagement(
|
|
969
1133
|
api_url=self.url,
|
|
970
1134
|
api_key=self.key,
|
|
971
1135
|
verify=self.verify,
|
|
972
1136
|
timeout=self.timeout
|
|
973
1137
|
)
|
|
974
|
-
return self.
|
|
1138
|
+
return self.group_access.delete_group_from_user(uid=uid, group=group)
|
|
975
1139
|
|
|
1140
|
+
@require_api_key
|
|
976
1141
|
def _delete_group_from_dataset(self, dataset: str, group: str):
|
|
977
|
-
if not
|
|
1142
|
+
if not self.group_access:
|
|
978
1143
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
979
1144
|
if not self.url or not self.key:
|
|
980
1145
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
981
|
-
self.
|
|
1146
|
+
self.group_access = GroupAccessManagement(
|
|
982
1147
|
api_url=self.url,
|
|
983
1148
|
api_key=self.key,
|
|
984
1149
|
verify=self.verify,
|
|
985
1150
|
timeout=self.timeout
|
|
986
1151
|
)
|
|
987
|
-
return self.
|
|
1152
|
+
return self.group_access.delete_group_from_dataset(dataset=dataset, group=group)
|
|
988
1153
|
|
|
989
1154
|
# Space management protected methods
|
|
1155
|
+
@require_api_key
|
|
990
1156
|
def _get_total_space_used(self):
|
|
991
|
-
if not
|
|
1157
|
+
if not self.space_management:
|
|
992
1158
|
from terrakio_core.space_management import SpaceManagement
|
|
993
1159
|
if not self.url or not self.key:
|
|
994
1160
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1000,8 +1166,9 @@ class BaseClient:
|
|
|
1000
1166
|
)
|
|
1001
1167
|
return self.space_management.get_total_space_used()
|
|
1002
1168
|
|
|
1169
|
+
@require_api_key
|
|
1003
1170
|
def _get_space_used_by_job(self, name: str, region: str = None):
|
|
1004
|
-
if not
|
|
1171
|
+
if not self.space_management:
|
|
1005
1172
|
from terrakio_core.space_management import SpaceManagement
|
|
1006
1173
|
if not self.url or not self.key:
|
|
1007
1174
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1013,8 +1180,9 @@ class BaseClient:
|
|
|
1013
1180
|
)
|
|
1014
1181
|
return self.space_management.get_space_used_by_job(name, region)
|
|
1015
1182
|
|
|
1183
|
+
@require_api_key
|
|
1016
1184
|
def _delete_user_job(self, name: str, region: str = None):
|
|
1017
|
-
if not
|
|
1185
|
+
if not self.space_management:
|
|
1018
1186
|
from terrakio_core.space_management import SpaceManagement
|
|
1019
1187
|
if not self.url or not self.key:
|
|
1020
1188
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1026,8 +1194,9 @@ class BaseClient:
|
|
|
1026
1194
|
)
|
|
1027
1195
|
return self.space_management.delete_user_job(name, region)
|
|
1028
1196
|
|
|
1197
|
+
@require_api_key
|
|
1029
1198
|
def _delete_data_in_path(self, path: str, region: str = None):
|
|
1030
|
-
if not
|
|
1199
|
+
if not self.space_management:
|
|
1031
1200
|
from terrakio_core.space_management import SpaceManagement
|
|
1032
1201
|
if not self.url or not self.key:
|
|
1033
1202
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1039,6 +1208,7 @@ class BaseClient:
|
|
|
1039
1208
|
)
|
|
1040
1209
|
return self.space_management.delete_data_in_path(path, region)
|
|
1041
1210
|
|
|
1211
|
+
@require_api_key
|
|
1042
1212
|
def start_mass_stats_job(self, task_id):
|
|
1043
1213
|
if not self.mass_stats:
|
|
1044
1214
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -1053,6 +1223,7 @@ class BaseClient:
|
|
|
1053
1223
|
return self.mass_stats.start_job(task_id)
|
|
1054
1224
|
|
|
1055
1225
|
|
|
1226
|
+
@require_api_key
|
|
1056
1227
|
def generate_ai_dataset(
|
|
1057
1228
|
self,
|
|
1058
1229
|
name: str,
|
|
@@ -1147,6 +1318,7 @@ class BaseClient:
|
|
|
1147
1318
|
# print("the task id is ", task_id)
|
|
1148
1319
|
return task_id
|
|
1149
1320
|
|
|
1321
|
+
@require_api_key
|
|
1150
1322
|
def train_model(self, model_name: str, training_dataset: str, task_type: str, model_category: str, architecture: str, region: str, hyperparameters: dict = None) -> dict:
|
|
1151
1323
|
"""
|
|
1152
1324
|
Train a model using the external model training API.
|
|
@@ -1173,7 +1345,6 @@ class BaseClient:
|
|
|
1173
1345
|
"hyperparameters": hyperparameters
|
|
1174
1346
|
}
|
|
1175
1347
|
endpoint = f"{self.url.rstrip('/')}/train_model"
|
|
1176
|
-
print("the payload is ", payload)
|
|
1177
1348
|
try:
|
|
1178
1349
|
response = self.session.post(endpoint, json=payload, timeout=self.timeout, verify=self.verify)
|
|
1179
1350
|
if not response.ok:
|
|
@@ -1191,6 +1362,7 @@ class BaseClient:
|
|
|
1191
1362
|
raise APIError(f"Model training request failed: {str(e)}")
|
|
1192
1363
|
|
|
1193
1364
|
# Mass Stats methods
|
|
1365
|
+
@require_api_key
|
|
1194
1366
|
def combine_tiles(self,
|
|
1195
1367
|
data_name: str,
|
|
1196
1368
|
usezarr: bool,
|
|
@@ -1211,6 +1383,7 @@ class BaseClient:
|
|
|
1211
1383
|
|
|
1212
1384
|
|
|
1213
1385
|
|
|
1386
|
+
@require_api_key
|
|
1214
1387
|
def create_dataset_file(
|
|
1215
1388
|
self,
|
|
1216
1389
|
name: str,
|
|
@@ -1226,9 +1399,22 @@ class BaseClient:
|
|
|
1226
1399
|
skip_existing: bool = False,
|
|
1227
1400
|
non_interactive: bool = True,
|
|
1228
1401
|
usezarr: bool = False,
|
|
1229
|
-
poll_interval: int = 30
|
|
1402
|
+
poll_interval: int = 30,
|
|
1403
|
+
download_path: str = "/home/user/Downloads",
|
|
1230
1404
|
) -> dict:
|
|
1231
1405
|
|
|
1406
|
+
if not self.mass_stats:
|
|
1407
|
+
from terrakio_core.mass_stats import MassStats
|
|
1408
|
+
if not self.url or not self.key:
|
|
1409
|
+
raise ConfigurationError("Mass Stats client not initialized. Make sure API URL and key are set.")
|
|
1410
|
+
self.mass_stats = MassStats(
|
|
1411
|
+
base_url=self.url,
|
|
1412
|
+
api_key=self.key,
|
|
1413
|
+
verify=self.verify,
|
|
1414
|
+
timeout=self.timeout
|
|
1415
|
+
)
|
|
1416
|
+
|
|
1417
|
+
|
|
1232
1418
|
from terrakio_core.generation.tiles import tiles
|
|
1233
1419
|
import tempfile
|
|
1234
1420
|
import time
|
|
@@ -1317,26 +1503,60 @@ class BaseClient:
|
|
|
1317
1503
|
os.unlink(tempmanifestname)
|
|
1318
1504
|
|
|
1319
1505
|
|
|
1506
|
+
# return self.mass_stats.combine_tiles(body["name"], usezarr, body["overwrite"], body["output"])
|
|
1507
|
+
|
|
1320
1508
|
# Start combining tiles
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1509
|
+
combine_result = self.mass_stats.combine_tiles(body["name"], usezarr, body["overwrite"], body["output"])
|
|
1510
|
+
combine_task_id = combine_result.get("task_id")
|
|
1511
|
+
|
|
1512
|
+
# Poll combine_tiles job status
|
|
1513
|
+
combine_start_time = time.time()
|
|
1514
|
+
while True:
|
|
1515
|
+
try:
|
|
1516
|
+
trackinfo = self.mass_stats.track_job([combine_task_id])
|
|
1517
|
+
download_file_name = trackinfo[combine_task_id]['folder'] + '.nc'
|
|
1518
|
+
bucket = trackinfo[combine_task_id]['bucket']
|
|
1519
|
+
combine_status = trackinfo[combine_task_id]['status']
|
|
1520
|
+
if combine_status == 'Completed':
|
|
1521
|
+
print('Tiles combined successfully!')
|
|
1522
|
+
break
|
|
1523
|
+
elif combine_status in ['Failed', 'Cancelled', 'Error']:
|
|
1524
|
+
raise RuntimeError(f"Combine job {combine_task_id} failed with status: {combine_status}")
|
|
1525
|
+
else:
|
|
1526
|
+
elapsed_time = time.time() - combine_start_time
|
|
1527
|
+
print(f"Combine job status: {combine_status} - Elapsed time: {elapsed_time:.1f}s", end='\r')
|
|
1528
|
+
time.sleep(poll_interval)
|
|
1529
|
+
except KeyboardInterrupt:
|
|
1530
|
+
print(f"\nInterrupted! Combine job {combine_task_id} is still running in the background.")
|
|
1531
|
+
raise
|
|
1532
|
+
except Exception as e:
|
|
1533
|
+
print(f"\nError tracking combine job: {e}")
|
|
1534
|
+
raise
|
|
1535
|
+
|
|
1536
|
+
# Download the resulting file
|
|
1537
|
+
if download_path:
|
|
1538
|
+
self.mass_stats.download_file(body["name"], bucket, download_file_name, download_path)
|
|
1539
|
+
else:
|
|
1540
|
+
path = f"{body['name']}/combinedOutput/{download_file_name}"
|
|
1541
|
+
print(f"Combined file is available at {path}")
|
|
1542
|
+
|
|
1543
|
+
return {"generation_task_id": task_id, "combine_task_id": combine_task_id}
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
# taskid = self.mass_stats.get_task_id(job_name, stage).get('task_id')
|
|
1547
|
+
# trackinfo = self.mass_stats.track_job([taskid])
|
|
1548
|
+
# bucket = trackinfo[taskid]['bucket']
|
|
1549
|
+
# return self.mass_stats.download_file(job_name, bucket, file_name, output_path)
|
|
1333
1550
|
|
|
1551
|
+
|
|
1552
|
+
@require_api_key
|
|
1334
1553
|
def deploy_model(self, dataset: str, product:str, model_name:str, input_expression: str, model_training_job_name: str, uid: str, dates_iso8601: list):
|
|
1335
1554
|
script_content = self._generate_script(model_name, product, model_training_job_name, uid)
|
|
1336
1555
|
script_name = f"{product}.py"
|
|
1337
1556
|
self._upload_script_to_bucket(script_content, script_name, model_training_job_name, uid)
|
|
1338
1557
|
self._create_dataset(name = dataset, collection = "terrakio-datasets", products = [product], path = f"gs://terrakio-mass-requests/{uid}/{model_training_job_name}/inference_scripts", input = input_expression, dates_iso8601 = dates_iso8601, padding = 0)
|
|
1339
1558
|
|
|
1559
|
+
@require_api_key
|
|
1340
1560
|
def _generate_script(self, model_name: str, product: str, model_training_job_name: str, uid: str) -> str:
|
|
1341
1561
|
return textwrap.dedent(f'''
|
|
1342
1562
|
import logging
|
|
@@ -1443,6 +1663,7 @@ class BaseClient:
|
|
|
1443
1663
|
return result
|
|
1444
1664
|
''').strip()
|
|
1445
1665
|
|
|
1666
|
+
@require_api_key
|
|
1446
1667
|
def _upload_script_to_bucket(self, script_content: str, script_name: str, model_training_job_name: str, uid: str):
|
|
1447
1668
|
"""Upload the generated script to Google Cloud Storage"""
|
|
1448
1669
|
|
|
@@ -1455,6 +1676,7 @@ class BaseClient:
|
|
|
1455
1676
|
|
|
1456
1677
|
|
|
1457
1678
|
|
|
1679
|
+
@require_api_key
|
|
1458
1680
|
def download_file_to_path(self, job_name, stage, file_name, output_path):
|
|
1459
1681
|
if not self.mass_stats:
|
|
1460
1682
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -1472,4 +1694,34 @@ class BaseClient:
|
|
|
1472
1694
|
taskid = self.mass_stats.get_task_id(job_name, stage).get('task_id')
|
|
1473
1695
|
trackinfo = self.mass_stats.track_job([taskid])
|
|
1474
1696
|
bucket = trackinfo[taskid]['bucket']
|
|
1475
|
-
return self.mass_stats.download_file(job_name, bucket, file_name, output_path)
|
|
1697
|
+
return self.mass_stats.download_file(job_name, bucket, file_name, output_path)
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
# Apply the @require_api_key decorator to ALL methods in BaseClient
|
|
1701
|
+
# except only the absolute minimum that shouldn't require auth
|
|
1702
|
+
def _apply_api_key_decorator():
|
|
1703
|
+
# Only these methods can be used without API key
|
|
1704
|
+
skip_auth_methods = [
|
|
1705
|
+
'__init__', 'login', 'signup'
|
|
1706
|
+
]
|
|
1707
|
+
|
|
1708
|
+
# Get all attributes of BaseClient
|
|
1709
|
+
for attr_name in dir(BaseClient):
|
|
1710
|
+
# Skip special methods and methods in skip list
|
|
1711
|
+
if attr_name.startswith('__') and attr_name not in ['__enter__', '__exit__', '__aenter__', '__aexit__'] or attr_name in skip_auth_methods:
|
|
1712
|
+
continue
|
|
1713
|
+
|
|
1714
|
+
# Get the attribute
|
|
1715
|
+
attr = getattr(BaseClient, attr_name)
|
|
1716
|
+
|
|
1717
|
+
# Skip if not callable (not a method) or already decorated
|
|
1718
|
+
if not callable(attr) or hasattr(attr, '_is_decorated'):
|
|
1719
|
+
continue
|
|
1720
|
+
|
|
1721
|
+
# Apply decorator to EVERY method not in skip list
|
|
1722
|
+
setattr(BaseClient, attr_name, require_api_key(attr))
|
|
1723
|
+
# Mark as decorated to avoid double decoration
|
|
1724
|
+
getattr(BaseClient, attr_name)._is_decorated = True
|
|
1725
|
+
|
|
1726
|
+
# Run the decorator application
|
|
1727
|
+
_apply_api_key_decorator()
|
terrakio_core/config.py
CHANGED
|
@@ -9,51 +9,97 @@ from .exceptions import ConfigurationError
|
|
|
9
9
|
DEFAULT_CONFIG_FILE = os.path.join(os.environ.get("HOME", ""), ".tkio_config.json")
|
|
10
10
|
DEFAULT_API_URL = "https://api.terrak.io"
|
|
11
11
|
|
|
12
|
-
def read_config_file(config_file: str = DEFAULT_CONFIG_FILE) -> Dict[str, Any]:
|
|
12
|
+
def read_config_file(config_file: str = DEFAULT_CONFIG_FILE, quiet: bool = False) -> Dict[str, Any]:
|
|
13
13
|
"""
|
|
14
14
|
Read and parse the configuration file.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
config_file: Path to the configuration file
|
|
18
|
+
quiet: If True, suppress informational messages
|
|
18
19
|
|
|
19
20
|
Returns:
|
|
20
|
-
Dict[str, Any]: Configuration parameters
|
|
21
|
+
Dict[str, Any]: Configuration parameters with additional flags:
|
|
22
|
+
'is_logged_in': True if user is logged in
|
|
23
|
+
'user_email': The email of the logged in user
|
|
24
|
+
'token': Personal token if available
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
Note:
|
|
27
|
+
This function no longer raises ConfigurationError. Instead, it creates an empty config
|
|
28
|
+
file if one doesn't exist and returns appropriate status flags.
|
|
24
29
|
"""
|
|
25
30
|
config_path = Path(os.path.expanduser(config_file))
|
|
26
|
-
|
|
31
|
+
# the first circumstance is that the config file does not exist
|
|
32
|
+
# that we need to login before using any of the functions
|
|
33
|
+
# Check if config file exists
|
|
27
34
|
if not config_path.exists():
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
# Create an empty config file
|
|
36
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
with open(config_path, 'w') as f:
|
|
38
|
+
json.dump({}, f)
|
|
39
|
+
if not quiet:
|
|
40
|
+
print("No API key found. Please provide an API key to use this client.")
|
|
41
|
+
return {
|
|
42
|
+
'url': DEFAULT_API_URL,
|
|
43
|
+
'key': None,
|
|
44
|
+
'is_logged_in': False,
|
|
45
|
+
'user_email': None,
|
|
46
|
+
'token': None
|
|
47
|
+
}
|
|
33
48
|
|
|
34
49
|
try:
|
|
50
|
+
# Read the config file
|
|
35
51
|
with open(config_path, 'r') as f:
|
|
36
52
|
config_data = json.load(f)
|
|
37
|
-
|
|
53
|
+
|
|
54
|
+
# Read the config file data
|
|
55
|
+
# Check if config has an API key
|
|
56
|
+
if not config_data or 'TERRAKIO_API_KEY' not in config_data or not config_data.get('TERRAKIO_API_KEY'):
|
|
57
|
+
if not quiet:
|
|
58
|
+
print("No API key found. Please provide an API key to use this client.")
|
|
59
|
+
return {
|
|
60
|
+
'url': DEFAULT_API_URL,
|
|
61
|
+
'key': None,
|
|
62
|
+
'is_logged_in': False,
|
|
63
|
+
'user_email': None,
|
|
64
|
+
'token': config_data.get('PERSONAL_TOKEN')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# If we have config values, use them
|
|
68
|
+
if not quiet:
|
|
69
|
+
print(f"Currently logged in as: {config_data.get('EMAIL')}")
|
|
70
|
+
# this meanb that we have already logged in to the tkio account
|
|
71
|
+
|
|
38
72
|
# Convert the JSON config to our expected format
|
|
39
73
|
config = {
|
|
40
|
-
#
|
|
41
|
-
'url':
|
|
42
|
-
'key': config_data.get('TERRAKIO_API_KEY')
|
|
74
|
+
# Always use the default URL, not from config file
|
|
75
|
+
'url': DEFAULT_API_URL,
|
|
76
|
+
'key': config_data.get('TERRAKIO_API_KEY'),
|
|
77
|
+
'is_logged_in': True,
|
|
78
|
+
'user_email': config_data.get('EMAIL'),
|
|
79
|
+
'token': config_data.get('PERSONAL_TOKEN')
|
|
43
80
|
}
|
|
44
81
|
return config
|
|
45
82
|
|
|
83
|
+
|
|
46
84
|
except Exception as e:
|
|
47
|
-
|
|
85
|
+
if not quiet:
|
|
86
|
+
print(f"Error reading config: {e}")
|
|
87
|
+
print("No API key found. Please provide an API key to use this client.")
|
|
88
|
+
return {
|
|
89
|
+
'url': DEFAULT_API_URL,
|
|
90
|
+
'key': None,
|
|
91
|
+
'is_logged_in': False,
|
|
92
|
+
'user_email': None,
|
|
93
|
+
'token': None
|
|
94
|
+
}
|
|
48
95
|
|
|
49
|
-
def create_default_config(email: str, api_key: str,
|
|
96
|
+
def create_default_config(email: str, api_key: str, config_file: str = DEFAULT_CONFIG_FILE) -> None:
|
|
50
97
|
"""
|
|
51
98
|
Create a default configuration file in JSON format.
|
|
52
99
|
|
|
53
100
|
Args:
|
|
54
101
|
email: User email
|
|
55
102
|
api_key: Terrakio API key
|
|
56
|
-
api_url: Optional API URL (if different from default)
|
|
57
103
|
config_file: Path to configuration file
|
|
58
104
|
|
|
59
105
|
Raises:
|
|
@@ -70,10 +116,6 @@ def create_default_config(email: str, api_key: str, api_url: Optional[str] = Non
|
|
|
70
116
|
"TERRAKIO_API_KEY": api_key
|
|
71
117
|
}
|
|
72
118
|
|
|
73
|
-
# Add API URL if provided
|
|
74
|
-
if api_url:
|
|
75
|
-
config_data["TERRAKIO_API_URL"] = api_url
|
|
76
|
-
|
|
77
119
|
with open(config_path, 'w') as f:
|
|
78
120
|
json.dump(config_data, f, indent=2)
|
|
79
121
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
terrakio_core/__init__.py,sha256=
|
|
2
|
-
terrakio_core/auth.py,sha256=
|
|
3
|
-
terrakio_core/client.py,sha256=
|
|
4
|
-
terrakio_core/config.py,sha256=
|
|
1
|
+
terrakio_core/__init__.py,sha256=Pn9_C8YL77990JgyJlSu_zCafX8L-zCDjnFiKl_SDUY,88
|
|
2
|
+
terrakio_core/auth.py,sha256=tqviZwtW7qWSo1TEjvv0dMDkegUtaEN_tfdrJ6cPJtc,7182
|
|
3
|
+
terrakio_core/client.py,sha256=YMbLyevSdxAKlX7K93aOhvfjnM7NCCb4ohetcYzigJM,73676
|
|
4
|
+
terrakio_core/config.py,sha256=MKTGSQRji8ty9dJlX8SOP7_3K2iR-3BXL-hUG7PsZQY,4324
|
|
5
5
|
terrakio_core/dataset_management.py,sha256=Hdk3nkwd70jw3lBNEaGixrqNVhUWOmsIYktzm_8vXdc,10913
|
|
6
6
|
terrakio_core/decorators.py,sha256=QeNOUX6WEAmdgBL5Igt5DXyYduh3jnmLbodttmwvXhE,785
|
|
7
7
|
terrakio_core/exceptions.py,sha256=9S-I20-QiDRj1qgjFyYUwYM7BLic_bxurcDOIm2Fu_0,410
|
|
@@ -10,7 +10,7 @@ terrakio_core/mass_stats.py,sha256=UGZo8BH4hzWe3k7pevsYAdRwnVZl-08lXjTlHD4nMgQ,1
|
|
|
10
10
|
terrakio_core/space_management.py,sha256=wlUUQrlj_4U_Lpjn9lbF5oj0Rv3NPvvnrd5mWej5kmA,4211
|
|
11
11
|
terrakio_core/user_management.py,sha256=MMNWkz0V_9X7ZYjjteuRU4H4W3F16iuQw1dpA2wVTGg,7400
|
|
12
12
|
terrakio_core/generation/tiles.py,sha256=eiiMNzqaga-c42kG_7zHXTF2o8ZInCPUj0Vu4Ye30Ts,2980
|
|
13
|
-
terrakio_core-0.3.
|
|
14
|
-
terrakio_core-0.3.
|
|
15
|
-
terrakio_core-0.3.
|
|
16
|
-
terrakio_core-0.3.
|
|
13
|
+
terrakio_core-0.3.4.dist-info/METADATA,sha256=yC1XxiHDyyzVvXsqgYP7T2mvXHuN_w6h7UYp4rO-fBY,1476
|
|
14
|
+
terrakio_core-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
terrakio_core-0.3.4.dist-info/top_level.txt,sha256=5cBj6O7rNWyn97ND4YuvvXm0Crv4RxttT4JZvNdOG6Q,14
|
|
16
|
+
terrakio_core-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|