terrakio-core 0.3.2__py3-none-any.whl → 0.3.3__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 -111
- terrakio_core/config.py +63 -21
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.3.dist-info}/METADATA +1 -1
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.3.dist-info}/RECORD +8 -8
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.3.dist-info}/WHEEL +0 -0
- {terrakio_core-0.3.2.dist-info → terrakio_core-0.3.3.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,
|
|
@@ -187,38 +259,67 @@ class BaseClient:
|
|
|
187
259
|
raise
|
|
188
260
|
continue
|
|
189
261
|
|
|
262
|
+
@require_api_key
|
|
190
263
|
async def close_async(self):
|
|
191
264
|
"""Close the aiohttp session"""
|
|
192
265
|
if self._aiohttp_session and not self._aiohttp_session.closed:
|
|
193
266
|
await self._aiohttp_session.close()
|
|
194
267
|
self._aiohttp_session = None
|
|
195
268
|
|
|
269
|
+
@require_api_key
|
|
196
270
|
async def __aenter__(self):
|
|
197
271
|
return self
|
|
198
272
|
|
|
273
|
+
@require_api_key
|
|
199
274
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
200
275
|
await self.close_async()
|
|
201
276
|
|
|
202
277
|
def signup(self, email: str, password: str) -> Dict[str, Any]:
|
|
203
278
|
if not self.auth_client:
|
|
204
|
-
|
|
279
|
+
from terrakio_core.auth import AuthClient
|
|
280
|
+
self.auth_client = AuthClient(
|
|
281
|
+
base_url=self.url,
|
|
282
|
+
verify=self.verify,
|
|
283
|
+
timeout=self.timeout
|
|
284
|
+
)
|
|
285
|
+
self.auth_client.session = self.session
|
|
205
286
|
return self.auth_client.signup(email, password)
|
|
206
287
|
|
|
207
288
|
def login(self, email: str, password: str) -> Dict[str, str]:
|
|
289
|
+
|
|
208
290
|
if not self.auth_client:
|
|
209
|
-
|
|
210
|
-
|
|
291
|
+
from terrakio_core.auth import AuthClient
|
|
292
|
+
self.auth_client = AuthClient(
|
|
293
|
+
base_url=self.url,
|
|
294
|
+
verify=self.verify,
|
|
295
|
+
timeout=self.timeout
|
|
296
|
+
)
|
|
297
|
+
self.auth_client.session = self.session
|
|
211
298
|
try:
|
|
212
299
|
# First attempt to login
|
|
213
300
|
token_response = self.auth_client.login(email, password)
|
|
214
301
|
|
|
215
|
-
print("the token response is ", token_response)
|
|
216
302
|
# Only proceed with API key retrieval if login was successful
|
|
217
303
|
if token_response:
|
|
218
304
|
# After successful login, get the API key
|
|
219
|
-
api_key_response = self.view_api_key()
|
|
305
|
+
api_key_response = self.auth_client.view_api_key()
|
|
220
306
|
self.key = api_key_response
|
|
221
307
|
|
|
308
|
+
# Make sure URL is set
|
|
309
|
+
if not self.url:
|
|
310
|
+
self.url = "https://api.terrak.io"
|
|
311
|
+
|
|
312
|
+
# Make sure session headers are updated with the new key
|
|
313
|
+
self.session.headers.update({
|
|
314
|
+
'Content-Type': 'application/json',
|
|
315
|
+
'x-api-key': self.key
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
# Set URL if not already set
|
|
319
|
+
if not self.url:
|
|
320
|
+
self.url = "https://api.terrak.io"
|
|
321
|
+
self.url = self.url.rstrip('/')
|
|
322
|
+
|
|
222
323
|
# Save email and API key to config file
|
|
223
324
|
import os
|
|
224
325
|
import json
|
|
@@ -230,6 +331,7 @@ class BaseClient:
|
|
|
230
331
|
config = json.load(f)
|
|
231
332
|
config["EMAIL"] = email
|
|
232
333
|
config["TERRAKIO_API_KEY"] = self.key
|
|
334
|
+
config["PERSONAL_TOKEN"] = token_response
|
|
233
335
|
|
|
234
336
|
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
235
337
|
with open(config_path, 'w') as f:
|
|
@@ -237,6 +339,7 @@ class BaseClient:
|
|
|
237
339
|
|
|
238
340
|
if not self.quiet:
|
|
239
341
|
print(f"Successfully authenticated as: {email}")
|
|
342
|
+
print(f"Using Terrakio API at: {self.url}")
|
|
240
343
|
print(f"API key saved to {config_path}")
|
|
241
344
|
except Exception as e:
|
|
242
345
|
if not self.quiet:
|
|
@@ -248,47 +351,81 @@ class BaseClient:
|
|
|
248
351
|
print(f"Login failed: {str(e)}")
|
|
249
352
|
raise
|
|
250
353
|
|
|
354
|
+
@require_api_key
|
|
251
355
|
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
|
-
|
|
356
|
+
# If we have auth_client and it has a token, refresh the key
|
|
357
|
+
if self.auth_client and self.auth_client.token:
|
|
358
|
+
self.key = self.auth_client.refresh_api_key()
|
|
359
|
+
self.session.headers.update({'x-api-key': self.key})
|
|
360
|
+
|
|
361
|
+
# Update the config file with the new key
|
|
362
|
+
import os
|
|
363
|
+
config_path = os.path.join(os.environ.get("HOME", ""), ".tkio_config.json")
|
|
364
|
+
try:
|
|
365
|
+
config = {"EMAIL": "", "TERRAKIO_API_KEY": ""}
|
|
366
|
+
if os.path.exists(config_path):
|
|
367
|
+
with open(config_path, 'r') as f:
|
|
368
|
+
config = json.load(f)
|
|
369
|
+
config["TERRAKIO_API_KEY"] = self.key
|
|
370
|
+
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
371
|
+
with open(config_path, 'w') as f:
|
|
372
|
+
json.dump(config, f, indent=4)
|
|
373
|
+
if not self.quiet:
|
|
374
|
+
print(f"API key generated successfully and updated in {config_path}")
|
|
375
|
+
except Exception as e:
|
|
376
|
+
if not self.quiet:
|
|
377
|
+
print(f"Warning: Failed to update config file: {e}")
|
|
378
|
+
return self.key
|
|
379
|
+
else:
|
|
380
|
+
# If we don't have auth_client with a token but have a key already, return it
|
|
381
|
+
if self.key:
|
|
382
|
+
if not self.quiet:
|
|
383
|
+
print("Using existing API key from config file.")
|
|
384
|
+
return self.key
|
|
385
|
+
else:
|
|
386
|
+
raise ConfigurationError("No authentication token available. Please login first to refresh the API key.")
|
|
387
|
+
|
|
388
|
+
@require_api_key
|
|
389
|
+
def view_api_key(self) -> str:
|
|
390
|
+
# If we have the auth client and token, refresh the key
|
|
391
|
+
if self.auth_client and self.auth_client.token:
|
|
392
|
+
self.key = self.auth_client.view_api_key()
|
|
393
|
+
self.session.headers.update({'x-api-key': self.key})
|
|
394
|
+
|
|
274
395
|
return self.key
|
|
275
396
|
|
|
397
|
+
@require_api_key
|
|
276
398
|
def view_api_key(self) -> str:
|
|
399
|
+
# If we have the auth client and token, refresh the key
|
|
277
400
|
if not self.auth_client:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
401
|
+
from terrakio_core.auth import AuthClient
|
|
402
|
+
self.auth_client = AuthClient(
|
|
403
|
+
base_url=self.url,
|
|
404
|
+
verify=self.verify,
|
|
405
|
+
timeout=self.timeout
|
|
406
|
+
)
|
|
407
|
+
self.auth_client.session = self.session
|
|
408
|
+
return self.auth_client.view_api_key()
|
|
284
409
|
|
|
410
|
+
# @require_api_key
|
|
411
|
+
# def get_user_info(self) -> Dict[str, Any]:
|
|
412
|
+
# return self.auth_client.get_user_info()
|
|
413
|
+
@require_api_key
|
|
285
414
|
def get_user_info(self) -> Dict[str, Any]:
|
|
415
|
+
# Initialize auth_client if it doesn't exist
|
|
286
416
|
if not self.auth_client:
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
417
|
+
from terrakio_core.auth import AuthClient
|
|
418
|
+
self.auth_client = AuthClient(
|
|
419
|
+
base_url=self.url,
|
|
420
|
+
verify=self.verify,
|
|
421
|
+
timeout=self.timeout
|
|
422
|
+
)
|
|
423
|
+
# Use the same session as the base client
|
|
424
|
+
self.auth_client.session = self.session
|
|
425
|
+
|
|
290
426
|
return self.auth_client.get_user_info()
|
|
291
427
|
|
|
428
|
+
@require_api_key
|
|
292
429
|
def wcs(self, expr: str, feature: Union[Dict[str, Any], ShapelyGeometry], in_crs: str = "epsg:4326",
|
|
293
430
|
out_crs: str = "epsg:4326", output: str = "csv", resolution: int = -1,
|
|
294
431
|
**kwargs):
|
|
@@ -341,6 +478,7 @@ class BaseClient:
|
|
|
341
478
|
raise APIError(f"Request failed: {str(e)}")
|
|
342
479
|
|
|
343
480
|
# Admin/protected methods
|
|
481
|
+
@require_api_key
|
|
344
482
|
def _get_user_by_id(self, user_id: str):
|
|
345
483
|
if not self.user_management:
|
|
346
484
|
from terrakio_core.user_management import UserManagement
|
|
@@ -354,6 +492,7 @@ class BaseClient:
|
|
|
354
492
|
)
|
|
355
493
|
return self.user_management.get_user_by_id(user_id)
|
|
356
494
|
|
|
495
|
+
@require_api_key
|
|
357
496
|
def _get_user_by_email(self, email: str):
|
|
358
497
|
if not self.user_management:
|
|
359
498
|
from terrakio_core.user_management import UserManagement
|
|
@@ -367,6 +506,7 @@ class BaseClient:
|
|
|
367
506
|
)
|
|
368
507
|
return self.user_management.get_user_by_email(email)
|
|
369
508
|
|
|
509
|
+
@require_api_key
|
|
370
510
|
def _list_users(self, substring: str = None, uid: bool = False):
|
|
371
511
|
if not self.user_management:
|
|
372
512
|
from terrakio_core.user_management import UserManagement
|
|
@@ -380,6 +520,7 @@ class BaseClient:
|
|
|
380
520
|
)
|
|
381
521
|
return self.user_management.list_users(substring=substring, uid=uid)
|
|
382
522
|
|
|
523
|
+
@require_api_key
|
|
383
524
|
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
525
|
if not self.user_management:
|
|
385
526
|
from terrakio_core.user_management import UserManagement
|
|
@@ -401,6 +542,7 @@ class BaseClient:
|
|
|
401
542
|
quota=quota
|
|
402
543
|
)
|
|
403
544
|
|
|
545
|
+
@require_api_key
|
|
404
546
|
def _reset_quota(self, email: str, quota: int = None):
|
|
405
547
|
if not self.user_management:
|
|
406
548
|
from terrakio_core.user_management import UserManagement
|
|
@@ -414,6 +556,7 @@ class BaseClient:
|
|
|
414
556
|
)
|
|
415
557
|
return self.user_management.reset_quota(email=email, quota=quota)
|
|
416
558
|
|
|
559
|
+
@require_api_key
|
|
417
560
|
def _delete_user(self, uid: str):
|
|
418
561
|
if not self.user_management:
|
|
419
562
|
from terrakio_core.user_management import UserManagement
|
|
@@ -428,6 +571,7 @@ class BaseClient:
|
|
|
428
571
|
return self.user_management.delete_user(uid=uid)
|
|
429
572
|
|
|
430
573
|
# Dataset management protected methods
|
|
574
|
+
@require_api_key
|
|
431
575
|
def _get_dataset(self, name: str, collection: str = "terrakio-datasets"):
|
|
432
576
|
if not self.dataset_management:
|
|
433
577
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -441,6 +585,7 @@ class BaseClient:
|
|
|
441
585
|
)
|
|
442
586
|
return self.dataset_management.get_dataset(name=name, collection=collection)
|
|
443
587
|
|
|
588
|
+
@require_api_key
|
|
444
589
|
def _list_datasets(self, substring: str = None, collection: str = "terrakio-datasets"):
|
|
445
590
|
if not self.dataset_management:
|
|
446
591
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -454,6 +599,7 @@ class BaseClient:
|
|
|
454
599
|
)
|
|
455
600
|
return self.dataset_management.list_datasets(substring=substring, collection=collection)
|
|
456
601
|
|
|
602
|
+
@require_api_key
|
|
457
603
|
def _create_dataset(self, name: str, collection: str = "terrakio-datasets", **kwargs):
|
|
458
604
|
if not self.dataset_management:
|
|
459
605
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -467,6 +613,7 @@ class BaseClient:
|
|
|
467
613
|
)
|
|
468
614
|
return self.dataset_management.create_dataset(name=name, collection=collection, **kwargs)
|
|
469
615
|
|
|
616
|
+
@require_api_key
|
|
470
617
|
def _update_dataset(self, name: str, append: bool = True, collection: str = "terrakio-datasets", **kwargs):
|
|
471
618
|
if not self.dataset_management:
|
|
472
619
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -480,6 +627,7 @@ class BaseClient:
|
|
|
480
627
|
)
|
|
481
628
|
return self.dataset_management.update_dataset(name=name, append=append, collection=collection, **kwargs)
|
|
482
629
|
|
|
630
|
+
@require_api_key
|
|
483
631
|
def _overwrite_dataset(self, name: str, collection: str = "terrakio-datasets", **kwargs):
|
|
484
632
|
if not self.dataset_management:
|
|
485
633
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -493,6 +641,7 @@ class BaseClient:
|
|
|
493
641
|
)
|
|
494
642
|
return self.dataset_management.overwrite_dataset(name=name, collection=collection, **kwargs)
|
|
495
643
|
|
|
644
|
+
@require_api_key
|
|
496
645
|
def _delete_dataset(self, name: str, collection: str = "terrakio-datasets"):
|
|
497
646
|
if not self.dataset_management:
|
|
498
647
|
from terrakio_core.dataset_management import DatasetManagement
|
|
@@ -506,6 +655,7 @@ class BaseClient:
|
|
|
506
655
|
)
|
|
507
656
|
return self.dataset_management.delete_dataset(name=name, collection=collection)
|
|
508
657
|
|
|
658
|
+
@require_api_key
|
|
509
659
|
def close(self):
|
|
510
660
|
"""Close all client sessions"""
|
|
511
661
|
self.session.close()
|
|
@@ -532,13 +682,16 @@ class BaseClient:
|
|
|
532
682
|
# Event loop may already be closed, ignore
|
|
533
683
|
pass
|
|
534
684
|
|
|
685
|
+
@require_api_key
|
|
535
686
|
def __enter__(self):
|
|
536
687
|
return self
|
|
537
688
|
|
|
689
|
+
@require_api_key
|
|
538
690
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
539
691
|
self.close()
|
|
540
692
|
|
|
541
693
|
@admin_only_params('location', 'force_loc', 'server')
|
|
694
|
+
@require_api_key
|
|
542
695
|
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
696
|
if not self.mass_stats:
|
|
544
697
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -553,6 +706,7 @@ class BaseClient:
|
|
|
553
706
|
return self.mass_stats.execute_job(name, region, output, config, overwrite, skip_existing, request_json, manifest_json, location, force_loc, server)
|
|
554
707
|
|
|
555
708
|
|
|
709
|
+
@require_api_key
|
|
556
710
|
def get_mass_stats_task_id(self, name, stage, uid=None):
|
|
557
711
|
if not self.mass_stats:
|
|
558
712
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -566,6 +720,7 @@ class BaseClient:
|
|
|
566
720
|
)
|
|
567
721
|
return self.mass_stats.get_task_id(name, stage, uid)
|
|
568
722
|
|
|
723
|
+
@require_api_key
|
|
569
724
|
def track_mass_stats_job(self, ids: Optional[list] = None):
|
|
570
725
|
if not self.mass_stats:
|
|
571
726
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -579,6 +734,7 @@ class BaseClient:
|
|
|
579
734
|
)
|
|
580
735
|
return self.mass_stats.track_job(ids)
|
|
581
736
|
|
|
737
|
+
@require_api_key
|
|
582
738
|
def get_mass_stats_history(self, limit=100):
|
|
583
739
|
if not self.mass_stats:
|
|
584
740
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -592,6 +748,7 @@ class BaseClient:
|
|
|
592
748
|
)
|
|
593
749
|
return self.mass_stats.get_history(limit)
|
|
594
750
|
|
|
751
|
+
@require_api_key
|
|
595
752
|
def start_mass_stats_post_processing(self, process_name, data_name, output, consumer_path, overwrite=False):
|
|
596
753
|
if not self.mass_stats:
|
|
597
754
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -605,6 +762,7 @@ class BaseClient:
|
|
|
605
762
|
)
|
|
606
763
|
return self.mass_stats.start_post_processing(process_name, data_name, output, consumer_path, overwrite)
|
|
607
764
|
|
|
765
|
+
@require_api_key
|
|
608
766
|
def download_mass_stats_results(self, id=None, force_loc=False, **kwargs):
|
|
609
767
|
if not self.mass_stats:
|
|
610
768
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -618,6 +776,7 @@ class BaseClient:
|
|
|
618
776
|
)
|
|
619
777
|
return self.mass_stats.download_results(id, force_loc, **kwargs)
|
|
620
778
|
|
|
779
|
+
@require_api_key
|
|
621
780
|
def cancel_mass_stats_job(self, id):
|
|
622
781
|
if not self.mass_stats:
|
|
623
782
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -631,6 +790,7 @@ class BaseClient:
|
|
|
631
790
|
)
|
|
632
791
|
return self.mass_stats.cancel_job(id)
|
|
633
792
|
|
|
793
|
+
@require_api_key
|
|
634
794
|
def cancel_all_mass_stats_jobs(self):
|
|
635
795
|
if not self.mass_stats:
|
|
636
796
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -644,6 +804,7 @@ class BaseClient:
|
|
|
644
804
|
)
|
|
645
805
|
return self.mass_stats.cancel_all_jobs()
|
|
646
806
|
|
|
807
|
+
@require_api_key
|
|
647
808
|
def _create_pyramids(self, name, levels, config):
|
|
648
809
|
if not self.mass_stats:
|
|
649
810
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -657,6 +818,7 @@ class BaseClient:
|
|
|
657
818
|
)
|
|
658
819
|
return self.mass_stats.create_pyramids(name, levels, config)
|
|
659
820
|
|
|
821
|
+
@require_api_key
|
|
660
822
|
def random_sample(self, name, **kwargs):
|
|
661
823
|
if not self.mass_stats:
|
|
662
824
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -670,6 +832,7 @@ class BaseClient:
|
|
|
670
832
|
)
|
|
671
833
|
return self.mass_stats.random_sample(name, **kwargs)
|
|
672
834
|
|
|
835
|
+
@require_api_key
|
|
673
836
|
async def zonal_stats_async(self, gdb, expr, conc=20, inplace=False, output="csv",
|
|
674
837
|
in_crs="epsg:4326", out_crs="epsg:4326", resolution=-1, buffer=False):
|
|
675
838
|
"""
|
|
@@ -858,6 +1021,7 @@ class BaseClient:
|
|
|
858
1021
|
else:
|
|
859
1022
|
return result_gdf
|
|
860
1023
|
|
|
1024
|
+
@require_api_key
|
|
861
1025
|
def zonal_stats(self, gdb, expr, conc=20, inplace=False, output="csv",
|
|
862
1026
|
in_crs="epsg:4326", out_crs="epsg:4326", resolution=-1, buffer=False):
|
|
863
1027
|
"""
|
|
@@ -920,75 +1084,80 @@ class BaseClient:
|
|
|
920
1084
|
return result
|
|
921
1085
|
|
|
922
1086
|
# Group access management protected methods
|
|
1087
|
+
@require_api_key
|
|
923
1088
|
def _get_group_users_and_datasets(self, group_name: str):
|
|
924
|
-
if not
|
|
1089
|
+
if not self.group_access:
|
|
925
1090
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
926
1091
|
if not self.url or not self.key:
|
|
927
1092
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
928
|
-
self.
|
|
1093
|
+
self.group_access = GroupAccessManagement(
|
|
929
1094
|
api_url=self.url,
|
|
930
1095
|
api_key=self.key,
|
|
931
1096
|
verify=self.verify,
|
|
932
1097
|
timeout=self.timeout
|
|
933
1098
|
)
|
|
934
|
-
return self.
|
|
1099
|
+
return self.group_access.get_group_users_and_datasets(group_name=group_name)
|
|
935
1100
|
|
|
1101
|
+
@require_api_key
|
|
936
1102
|
def _add_group_to_dataset(self, dataset: str, group: str):
|
|
937
|
-
if not
|
|
1103
|
+
if not self.group_access:
|
|
938
1104
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
939
1105
|
if not self.url or not self.key:
|
|
940
1106
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
941
|
-
self.
|
|
1107
|
+
self.group_access = GroupAccessManagement(
|
|
942
1108
|
api_url=self.url,
|
|
943
1109
|
api_key=self.key,
|
|
944
1110
|
verify=self.verify,
|
|
945
1111
|
timeout=self.timeout
|
|
946
1112
|
)
|
|
947
|
-
return self.
|
|
1113
|
+
return self.group_access.add_group_to_dataset(dataset=dataset, group=group)
|
|
948
1114
|
|
|
1115
|
+
@require_api_key
|
|
949
1116
|
def _add_group_to_user(self, uid: str, group: str):
|
|
950
|
-
if not
|
|
1117
|
+
if not self.group_access:
|
|
951
1118
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
952
1119
|
if not self.url or not self.key:
|
|
953
1120
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
954
|
-
self.
|
|
1121
|
+
self.group_access = GroupAccessManagement(
|
|
955
1122
|
api_url=self.url,
|
|
956
1123
|
api_key=self.key,
|
|
957
1124
|
verify=self.verify,
|
|
958
1125
|
timeout=self.timeout
|
|
959
1126
|
)
|
|
960
|
-
|
|
961
|
-
return self.group_access_management.add_group_to_user(uid, group)
|
|
1127
|
+
return self.group_access.add_group_to_user(uid=uid, group=group)
|
|
962
1128
|
|
|
1129
|
+
@require_api_key
|
|
963
1130
|
def _delete_group_from_user(self, uid: str, group: str):
|
|
964
|
-
if not
|
|
1131
|
+
if not self.group_access:
|
|
965
1132
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
966
1133
|
if not self.url or not self.key:
|
|
967
1134
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
968
|
-
self.
|
|
1135
|
+
self.group_access = GroupAccessManagement(
|
|
969
1136
|
api_url=self.url,
|
|
970
1137
|
api_key=self.key,
|
|
971
1138
|
verify=self.verify,
|
|
972
1139
|
timeout=self.timeout
|
|
973
1140
|
)
|
|
974
|
-
return self.
|
|
1141
|
+
return self.group_access.delete_group_from_user(uid=uid, group=group)
|
|
975
1142
|
|
|
1143
|
+
@require_api_key
|
|
976
1144
|
def _delete_group_from_dataset(self, dataset: str, group: str):
|
|
977
|
-
if not
|
|
1145
|
+
if not self.group_access:
|
|
978
1146
|
from terrakio_core.group_access_management import GroupAccessManagement
|
|
979
1147
|
if not self.url or not self.key:
|
|
980
1148
|
raise ConfigurationError("Group access management client not initialized. Make sure API URL and key are set.")
|
|
981
|
-
self.
|
|
1149
|
+
self.group_access = GroupAccessManagement(
|
|
982
1150
|
api_url=self.url,
|
|
983
1151
|
api_key=self.key,
|
|
984
1152
|
verify=self.verify,
|
|
985
1153
|
timeout=self.timeout
|
|
986
1154
|
)
|
|
987
|
-
return self.
|
|
1155
|
+
return self.group_access.delete_group_from_dataset(dataset=dataset, group=group)
|
|
988
1156
|
|
|
989
1157
|
# Space management protected methods
|
|
1158
|
+
@require_api_key
|
|
990
1159
|
def _get_total_space_used(self):
|
|
991
|
-
if not
|
|
1160
|
+
if not self.space_management:
|
|
992
1161
|
from terrakio_core.space_management import SpaceManagement
|
|
993
1162
|
if not self.url or not self.key:
|
|
994
1163
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1000,8 +1169,9 @@ class BaseClient:
|
|
|
1000
1169
|
)
|
|
1001
1170
|
return self.space_management.get_total_space_used()
|
|
1002
1171
|
|
|
1172
|
+
@require_api_key
|
|
1003
1173
|
def _get_space_used_by_job(self, name: str, region: str = None):
|
|
1004
|
-
if not
|
|
1174
|
+
if not self.space_management:
|
|
1005
1175
|
from terrakio_core.space_management import SpaceManagement
|
|
1006
1176
|
if not self.url or not self.key:
|
|
1007
1177
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1013,8 +1183,9 @@ class BaseClient:
|
|
|
1013
1183
|
)
|
|
1014
1184
|
return self.space_management.get_space_used_by_job(name, region)
|
|
1015
1185
|
|
|
1186
|
+
@require_api_key
|
|
1016
1187
|
def _delete_user_job(self, name: str, region: str = None):
|
|
1017
|
-
if not
|
|
1188
|
+
if not self.space_management:
|
|
1018
1189
|
from terrakio_core.space_management import SpaceManagement
|
|
1019
1190
|
if not self.url or not self.key:
|
|
1020
1191
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1026,8 +1197,9 @@ class BaseClient:
|
|
|
1026
1197
|
)
|
|
1027
1198
|
return self.space_management.delete_user_job(name, region)
|
|
1028
1199
|
|
|
1200
|
+
@require_api_key
|
|
1029
1201
|
def _delete_data_in_path(self, path: str, region: str = None):
|
|
1030
|
-
if not
|
|
1202
|
+
if not self.space_management:
|
|
1031
1203
|
from terrakio_core.space_management import SpaceManagement
|
|
1032
1204
|
if not self.url or not self.key:
|
|
1033
1205
|
raise ConfigurationError("Space management client not initialized. Make sure API URL and key are set.")
|
|
@@ -1039,6 +1211,7 @@ class BaseClient:
|
|
|
1039
1211
|
)
|
|
1040
1212
|
return self.space_management.delete_data_in_path(path, region)
|
|
1041
1213
|
|
|
1214
|
+
@require_api_key
|
|
1042
1215
|
def start_mass_stats_job(self, task_id):
|
|
1043
1216
|
if not self.mass_stats:
|
|
1044
1217
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -1053,6 +1226,7 @@ class BaseClient:
|
|
|
1053
1226
|
return self.mass_stats.start_job(task_id)
|
|
1054
1227
|
|
|
1055
1228
|
|
|
1229
|
+
@require_api_key
|
|
1056
1230
|
def generate_ai_dataset(
|
|
1057
1231
|
self,
|
|
1058
1232
|
name: str,
|
|
@@ -1147,6 +1321,7 @@ class BaseClient:
|
|
|
1147
1321
|
# print("the task id is ", task_id)
|
|
1148
1322
|
return task_id
|
|
1149
1323
|
|
|
1324
|
+
@require_api_key
|
|
1150
1325
|
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
1326
|
"""
|
|
1152
1327
|
Train a model using the external model training API.
|
|
@@ -1191,6 +1366,7 @@ class BaseClient:
|
|
|
1191
1366
|
raise APIError(f"Model training request failed: {str(e)}")
|
|
1192
1367
|
|
|
1193
1368
|
# Mass Stats methods
|
|
1369
|
+
@require_api_key
|
|
1194
1370
|
def combine_tiles(self,
|
|
1195
1371
|
data_name: str,
|
|
1196
1372
|
usezarr: bool,
|
|
@@ -1211,6 +1387,7 @@ class BaseClient:
|
|
|
1211
1387
|
|
|
1212
1388
|
|
|
1213
1389
|
|
|
1390
|
+
@require_api_key
|
|
1214
1391
|
def create_dataset_file(
|
|
1215
1392
|
self,
|
|
1216
1393
|
name: str,
|
|
@@ -1226,9 +1403,22 @@ class BaseClient:
|
|
|
1226
1403
|
skip_existing: bool = False,
|
|
1227
1404
|
non_interactive: bool = True,
|
|
1228
1405
|
usezarr: bool = False,
|
|
1229
|
-
poll_interval: int = 30
|
|
1406
|
+
poll_interval: int = 30,
|
|
1407
|
+
download_path: str = "/home/user/Downloads",
|
|
1230
1408
|
) -> dict:
|
|
1231
1409
|
|
|
1410
|
+
if not self.mass_stats:
|
|
1411
|
+
from terrakio_core.mass_stats import MassStats
|
|
1412
|
+
if not self.url or not self.key:
|
|
1413
|
+
raise ConfigurationError("Mass Stats client not initialized. Make sure API URL and key are set.")
|
|
1414
|
+
self.mass_stats = MassStats(
|
|
1415
|
+
base_url=self.url,
|
|
1416
|
+
api_key=self.key,
|
|
1417
|
+
verify=self.verify,
|
|
1418
|
+
timeout=self.timeout
|
|
1419
|
+
)
|
|
1420
|
+
|
|
1421
|
+
|
|
1232
1422
|
from terrakio_core.generation.tiles import tiles
|
|
1233
1423
|
import tempfile
|
|
1234
1424
|
import time
|
|
@@ -1317,26 +1507,60 @@ class BaseClient:
|
|
|
1317
1507
|
os.unlink(tempmanifestname)
|
|
1318
1508
|
|
|
1319
1509
|
|
|
1510
|
+
# return self.mass_stats.combine_tiles(body["name"], usezarr, body["overwrite"], body["output"])
|
|
1511
|
+
|
|
1320
1512
|
# Start combining tiles
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1513
|
+
combine_result = self.mass_stats.combine_tiles(body["name"], usezarr, body["overwrite"], body["output"])
|
|
1514
|
+
combine_task_id = combine_result.get("task_id")
|
|
1515
|
+
|
|
1516
|
+
# Poll combine_tiles job status
|
|
1517
|
+
combine_start_time = time.time()
|
|
1518
|
+
while True:
|
|
1519
|
+
try:
|
|
1520
|
+
trackinfo = self.mass_stats.track_job([combine_task_id])
|
|
1521
|
+
download_file_name = trackinfo[combine_task_id]['folder'] + '.nc'
|
|
1522
|
+
bucket = trackinfo[combine_task_id]['bucket']
|
|
1523
|
+
combine_status = trackinfo[combine_task_id]['status']
|
|
1524
|
+
if combine_status == 'Completed':
|
|
1525
|
+
print('Tiles combined successfully!')
|
|
1526
|
+
break
|
|
1527
|
+
elif combine_status in ['Failed', 'Cancelled', 'Error']:
|
|
1528
|
+
raise RuntimeError(f"Combine job {combine_task_id} failed with status: {combine_status}")
|
|
1529
|
+
else:
|
|
1530
|
+
elapsed_time = time.time() - combine_start_time
|
|
1531
|
+
print(f"Combine job status: {combine_status} - Elapsed time: {elapsed_time:.1f}s", end='\r')
|
|
1532
|
+
time.sleep(poll_interval)
|
|
1533
|
+
except KeyboardInterrupt:
|
|
1534
|
+
print(f"\nInterrupted! Combine job {combine_task_id} is still running in the background.")
|
|
1535
|
+
raise
|
|
1536
|
+
except Exception as e:
|
|
1537
|
+
print(f"\nError tracking combine job: {e}")
|
|
1538
|
+
raise
|
|
1539
|
+
|
|
1540
|
+
# Download the resulting file
|
|
1541
|
+
if download_path:
|
|
1542
|
+
self.mass_stats.download_file(body["name"], bucket, download_file_name, download_path)
|
|
1543
|
+
else:
|
|
1544
|
+
path = f"{body['name']}/combinedOutput/{download_file_name}"
|
|
1545
|
+
print(f"Combined file is available at {path}")
|
|
1546
|
+
|
|
1547
|
+
return {"generation_task_id": task_id, "combine_task_id": combine_task_id}
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
# taskid = self.mass_stats.get_task_id(job_name, stage).get('task_id')
|
|
1551
|
+
# trackinfo = self.mass_stats.track_job([taskid])
|
|
1552
|
+
# bucket = trackinfo[taskid]['bucket']
|
|
1553
|
+
# return self.mass_stats.download_file(job_name, bucket, file_name, output_path)
|
|
1333
1554
|
|
|
1555
|
+
|
|
1556
|
+
@require_api_key
|
|
1334
1557
|
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
1558
|
script_content = self._generate_script(model_name, product, model_training_job_name, uid)
|
|
1336
1559
|
script_name = f"{product}.py"
|
|
1337
1560
|
self._upload_script_to_bucket(script_content, script_name, model_training_job_name, uid)
|
|
1338
1561
|
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
1562
|
|
|
1563
|
+
@require_api_key
|
|
1340
1564
|
def _generate_script(self, model_name: str, product: str, model_training_job_name: str, uid: str) -> str:
|
|
1341
1565
|
return textwrap.dedent(f'''
|
|
1342
1566
|
import logging
|
|
@@ -1443,6 +1667,7 @@ class BaseClient:
|
|
|
1443
1667
|
return result
|
|
1444
1668
|
''').strip()
|
|
1445
1669
|
|
|
1670
|
+
@require_api_key
|
|
1446
1671
|
def _upload_script_to_bucket(self, script_content: str, script_name: str, model_training_job_name: str, uid: str):
|
|
1447
1672
|
"""Upload the generated script to Google Cloud Storage"""
|
|
1448
1673
|
|
|
@@ -1455,6 +1680,7 @@ class BaseClient:
|
|
|
1455
1680
|
|
|
1456
1681
|
|
|
1457
1682
|
|
|
1683
|
+
@require_api_key
|
|
1458
1684
|
def download_file_to_path(self, job_name, stage, file_name, output_path):
|
|
1459
1685
|
if not self.mass_stats:
|
|
1460
1686
|
from terrakio_core.mass_stats import MassStats
|
|
@@ -1472,4 +1698,34 @@ class BaseClient:
|
|
|
1472
1698
|
taskid = self.mass_stats.get_task_id(job_name, stage).get('task_id')
|
|
1473
1699
|
trackinfo = self.mass_stats.track_job([taskid])
|
|
1474
1700
|
bucket = trackinfo[taskid]['bucket']
|
|
1475
|
-
return self.mass_stats.download_file(job_name, bucket, file_name, output_path)
|
|
1701
|
+
return self.mass_stats.download_file(job_name, bucket, file_name, output_path)
|
|
1702
|
+
|
|
1703
|
+
|
|
1704
|
+
# Apply the @require_api_key decorator to ALL methods in BaseClient
|
|
1705
|
+
# except only the absolute minimum that shouldn't require auth
|
|
1706
|
+
def _apply_api_key_decorator():
|
|
1707
|
+
# Only these methods can be used without API key
|
|
1708
|
+
skip_auth_methods = [
|
|
1709
|
+
'__init__', 'login', 'signup'
|
|
1710
|
+
]
|
|
1711
|
+
|
|
1712
|
+
# Get all attributes of BaseClient
|
|
1713
|
+
for attr_name in dir(BaseClient):
|
|
1714
|
+
# Skip special methods and methods in skip list
|
|
1715
|
+
if attr_name.startswith('__') and attr_name not in ['__enter__', '__exit__', '__aenter__', '__aexit__'] or attr_name in skip_auth_methods:
|
|
1716
|
+
continue
|
|
1717
|
+
|
|
1718
|
+
# Get the attribute
|
|
1719
|
+
attr = getattr(BaseClient, attr_name)
|
|
1720
|
+
|
|
1721
|
+
# Skip if not callable (not a method) or already decorated
|
|
1722
|
+
if not callable(attr) or hasattr(attr, '_is_decorated'):
|
|
1723
|
+
continue
|
|
1724
|
+
|
|
1725
|
+
# Apply decorator to EVERY method not in skip list
|
|
1726
|
+
setattr(BaseClient, attr_name, require_api_key(attr))
|
|
1727
|
+
# Mark as decorated to avoid double decoration
|
|
1728
|
+
getattr(BaseClient, attr_name)._is_decorated = True
|
|
1729
|
+
|
|
1730
|
+
# Run the decorator application
|
|
1731
|
+
_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=MhRAUXppa228Lm-kvJGTngU1cUkJF5Q6PIBPwtqxn0E,88
|
|
2
|
+
terrakio_core/auth.py,sha256=tqviZwtW7qWSo1TEjvv0dMDkegUtaEN_tfdrJ6cPJtc,7182
|
|
3
|
+
terrakio_core/client.py,sha256=b76IaqhWYSq2XKgVlj4BQTtCPiBJ2wUNzwiNlVZ3X2k,73860
|
|
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.3.dist-info/METADATA,sha256=DIKsBR8Uqgt5ExhlwcaPNYXSLxWV3AZMRmkehbmgF3I,1476
|
|
14
|
+
terrakio_core-0.3.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
terrakio_core-0.3.3.dist-info/top_level.txt,sha256=5cBj6O7rNWyn97ND4YuvvXm0Crv4RxttT4JZvNdOG6Q,14
|
|
16
|
+
terrakio_core-0.3.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|