terrakio-core 0.3.9__py3-none-any.whl → 0.4.2__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/async_client.py +21 -2
- terrakio_core/client.py +101 -5
- terrakio_core/convenience_functions/convenience_functions.py +280 -29
- terrakio_core/endpoints/mass_stats.py +71 -16
- terrakio_core/endpoints/model_management.py +424 -217
- terrakio_core/endpoints/user_management.py +5 -5
- terrakio_core/sync_client.py +106 -185
- {terrakio_core-0.3.9.dist-info → terrakio_core-0.4.2.dist-info}/METADATA +1 -1
- {terrakio_core-0.3.9.dist-info → terrakio_core-0.4.2.dist-info}/RECORD +12 -12
- {terrakio_core-0.3.9.dist-info → terrakio_core-0.4.2.dist-info}/WHEEL +0 -0
- {terrakio_core-0.3.9.dist-info → terrakio_core-0.4.2.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from urllib.parse import urlparse
|
|
7
7
|
from ..helper.decorators import require_token, require_api_key, require_auth
|
|
8
8
|
import aiohttp
|
|
9
|
-
|
|
9
|
+
from typing import Dict, Any, Optional, List, Union
|
|
10
10
|
class MassStats:
|
|
11
11
|
def __init__(self, client):
|
|
12
12
|
self._client = client
|
|
@@ -218,7 +218,7 @@ class MassStats:
|
|
|
218
218
|
params["output"] = output
|
|
219
219
|
|
|
220
220
|
return self._client._terrakio_request("GET", "mass_stats/download", params=params)
|
|
221
|
-
|
|
221
|
+
|
|
222
222
|
@require_api_key
|
|
223
223
|
async def _upload_file(self, file_path: str, url: str, use_gzip: bool = False):
|
|
224
224
|
"""
|
|
@@ -237,6 +237,18 @@ class MassStats:
|
|
|
237
237
|
except json.JSONDecodeError as e:
|
|
238
238
|
raise ValueError(f"Invalid JSON in file {file_path}: {e}")
|
|
239
239
|
|
|
240
|
+
return await self._upload_json_data(json_data, url, use_gzip)
|
|
241
|
+
|
|
242
|
+
@require_api_key
|
|
243
|
+
async def _upload_json_data(self, json_data: Union[Dict, List], url: str, use_gzip: bool = False):
|
|
244
|
+
"""
|
|
245
|
+
Helper method to upload JSON data directly to a signed URL.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
json_data: JSON data (dict or list) to upload
|
|
249
|
+
url: Signed URL to upload to
|
|
250
|
+
use_gzip: Whether to compress the data with gzip
|
|
251
|
+
"""
|
|
240
252
|
if hasattr(json, 'dumps') and 'ignore_nan' in json.dumps.__code__.co_varnames:
|
|
241
253
|
dumps_kwargs = {'ignore_nan': True}
|
|
242
254
|
else:
|
|
@@ -253,6 +265,7 @@ class MassStats:
|
|
|
253
265
|
headers = {
|
|
254
266
|
'Content-Type': 'application/json'
|
|
255
267
|
}
|
|
268
|
+
|
|
256
269
|
response = await self._client._regular_request("PUT", url, data=body, headers=headers)
|
|
257
270
|
return response
|
|
258
271
|
|
|
@@ -386,36 +399,55 @@ class MassStats:
|
|
|
386
399
|
region: str,
|
|
387
400
|
output: str,
|
|
388
401
|
config: Dict[str, Any],
|
|
389
|
-
request_json:
|
|
390
|
-
manifest_json: Dict[str, Any],
|
|
402
|
+
request_json: str, # Path to request JSON file
|
|
391
403
|
overwrite: bool = False,
|
|
392
404
|
skip_existing: bool = False,
|
|
393
405
|
location: str = None,
|
|
394
406
|
force_loc: bool = None,
|
|
395
|
-
server: str =
|
|
407
|
+
server: str = None
|
|
396
408
|
) -> Dict[str, Any]:
|
|
397
409
|
"""
|
|
398
410
|
Execute a mass stats job.
|
|
399
|
-
|
|
411
|
+
|
|
400
412
|
Args:
|
|
401
413
|
name: The name of the job
|
|
402
414
|
region: The region of the job
|
|
403
415
|
output: The output of the job
|
|
404
416
|
config: The config of the job
|
|
405
|
-
request_json:
|
|
406
|
-
manifest_json: The manifest JSON
|
|
417
|
+
request_json: Path to the request JSON file
|
|
407
418
|
overwrite: Whether to overwrite the job
|
|
408
419
|
skip_existing: Whether to skip existing jobs
|
|
409
420
|
location: The location of the job
|
|
410
421
|
force_loc: Whether to force the location
|
|
411
422
|
server: The server to use
|
|
412
|
-
|
|
423
|
+
|
|
413
424
|
Returns:
|
|
414
425
|
API response as a dictionary
|
|
415
|
-
|
|
426
|
+
|
|
416
427
|
Raises:
|
|
417
428
|
APIError: If the API request fails
|
|
418
429
|
"""
|
|
430
|
+
|
|
431
|
+
def extract_manifest_from_request(request_data: List[Dict[str, Any]]) -> List[str]:
|
|
432
|
+
"""Extract unique group names from request data to create manifest list."""
|
|
433
|
+
groups = []
|
|
434
|
+
seen_groups = set()
|
|
435
|
+
|
|
436
|
+
for item in request_data:
|
|
437
|
+
if not isinstance(item, dict):
|
|
438
|
+
raise ValueError("Each item in request JSON should be a dictionary")
|
|
439
|
+
|
|
440
|
+
if 'group' not in item:
|
|
441
|
+
raise ValueError("Each item should have a 'group' field")
|
|
442
|
+
|
|
443
|
+
group = item['group']
|
|
444
|
+
if group not in seen_groups:
|
|
445
|
+
groups.append(group)
|
|
446
|
+
seen_groups.add(group)
|
|
447
|
+
|
|
448
|
+
return groups
|
|
449
|
+
|
|
450
|
+
# Load and validate request JSON
|
|
419
451
|
try:
|
|
420
452
|
with open(request_json, 'r') as file:
|
|
421
453
|
request_data = json.load(file)
|
|
@@ -427,14 +459,35 @@ class MassStats:
|
|
|
427
459
|
return e
|
|
428
460
|
except json.JSONDecodeError as e:
|
|
429
461
|
return e
|
|
430
|
-
|
|
462
|
+
|
|
463
|
+
# Generate manifest from request data (kept in memory)
|
|
464
|
+
try:
|
|
465
|
+
manifest_groups = extract_manifest_from_request(request_data)
|
|
466
|
+
except Exception as e:
|
|
467
|
+
raise ValueError(f"Error extracting manifest from request JSON: {e}")
|
|
468
|
+
|
|
469
|
+
# Get upload URLs
|
|
470
|
+
upload_result = await self._upload_request(
|
|
471
|
+
name=name,
|
|
472
|
+
size=size,
|
|
473
|
+
region=region,
|
|
474
|
+
output=output,
|
|
475
|
+
config=config,
|
|
476
|
+
location=location,
|
|
477
|
+
force_loc=force_loc,
|
|
478
|
+
overwrite=overwrite,
|
|
479
|
+
server=server,
|
|
480
|
+
skip_existing=skip_existing
|
|
481
|
+
)
|
|
482
|
+
|
|
431
483
|
requests_url = upload_result.get('requests_url')
|
|
432
484
|
manifest_url = upload_result.get('manifest_url')
|
|
485
|
+
|
|
433
486
|
if not requests_url:
|
|
434
487
|
raise ValueError("No requests_url returned from server for request JSON upload")
|
|
435
488
|
|
|
489
|
+
# Upload request JSON file
|
|
436
490
|
try:
|
|
437
|
-
# in this place we are uploading the request json file, we need to check whether the json is in the correct format or not
|
|
438
491
|
self.validate_request(request_json)
|
|
439
492
|
requests_response = await self._upload_file(request_json, requests_url, use_gzip=True)
|
|
440
493
|
if requests_response.status not in [200, 201, 204]:
|
|
@@ -446,15 +499,17 @@ class MassStats:
|
|
|
446
499
|
if not manifest_url:
|
|
447
500
|
raise ValueError("No manifest_url returned from server for manifest JSON upload")
|
|
448
501
|
|
|
502
|
+
# Upload manifest JSON data directly (no temporary file needed)
|
|
449
503
|
try:
|
|
450
|
-
manifest_response = await self.
|
|
504
|
+
manifest_response = await self._upload_json_data(manifest_groups, manifest_url, use_gzip=False)
|
|
451
505
|
if manifest_response.status not in [200, 201, 204]:
|
|
452
506
|
self._client.logger.error(f"Manifest upload error: {manifest_response.text()}")
|
|
453
507
|
raise Exception(f"Failed to upload manifest JSON: {manifest_response.text()}")
|
|
454
508
|
except Exception as e:
|
|
455
|
-
raise Exception(f"Error uploading manifest JSON
|
|
456
|
-
|
|
457
|
-
|
|
509
|
+
raise Exception(f"Error uploading manifest JSON: {e}")
|
|
510
|
+
|
|
511
|
+
# Start the job
|
|
512
|
+
start_job_task_id = await self.start_job(upload_result.get("id"))
|
|
458
513
|
return start_job_task_id
|
|
459
514
|
|
|
460
515
|
@require_api_key
|