terrakio-core 0.3.4__py3-none-any.whl → 0.3.7__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.

@@ -0,0 +1,72 @@
1
+ from typing import Dict, Any, Optional
2
+ from ..helper.decorators import require_token, require_api_key, require_auth
3
+ class SpaceManagement:
4
+ def __init__(self, client):
5
+ self._client = client
6
+
7
+ @require_api_key
8
+ def get_total_space_used(self) -> Dict[str, Any]:
9
+ """
10
+ Get total space used by the user.
11
+
12
+ Returns:
13
+ Dict[str, Any]: Total space used by the user.
14
+
15
+ Raises:
16
+ APIError: If the API request fails
17
+ """
18
+ return self._client._terrakio_request("GET", "/users/jobs")
19
+
20
+ @require_api_key
21
+ def get_space_used_by_job(self, name: str, region: str) -> Dict[str, Any]:
22
+ """
23
+ Get space used by a specific job.
24
+
25
+ Args:
26
+ name: The name of the job
27
+ region: The region of the job
28
+
29
+ Returns:
30
+ Dict[str, Any]: Space used by the job.
31
+
32
+ Raises:
33
+ APIError: If the API request fails
34
+ """
35
+ params = {"region": region}
36
+ return self._client._terrakio_request("GET", f"/users/jobs/{name}", params=params)
37
+
38
+ @require_api_key
39
+ def delete_user_job(self, name: str, region: str) -> Dict[str, Any]:
40
+ """
41
+ Delete a user job by name and region.
42
+
43
+ Args:
44
+ name: The name of the job
45
+ region: The region of the job
46
+
47
+ Returns:
48
+ Dict[str, Any]: Response from the delete operation.
49
+
50
+ Raises:
51
+ APIError: If the API request fails
52
+ """
53
+ params = {"region": region}
54
+ return self._client._terrakio_request("DELETE", f"/users/jobs/{name}", params=params)
55
+
56
+ @require_api_key
57
+ def delete_data_in_path(self, path: str, region: str) -> Dict[str, Any]:
58
+ """
59
+ Delete data in a GCS path for a given region.
60
+
61
+ Args:
62
+ path: The GCS path to delete data from
63
+ region: The region where the data is located
64
+
65
+ Returns:
66
+ Dict[str, Any]: Response from the delete operation.
67
+
68
+ Raises:
69
+ APIError: If the API request fails
70
+ """
71
+ params = {"path": path, "region": region}
72
+ return self._client._terrakio_request("DELETE", "/users/jobs", params=params)
@@ -0,0 +1,131 @@
1
+ from typing import Dict, Any, List, Optional
2
+ from ..helper.decorators import require_token, require_api_key, require_auth
3
+
4
+ class UserManagement:
5
+ def __init__(self, client):
6
+ self._client = client
7
+
8
+ @require_api_key
9
+ def get_user_by_id(self, id: str) -> Dict[str, Any]:
10
+ """
11
+ Get user by ID.
12
+
13
+ Args:
14
+ user_id: User ID
15
+
16
+ Returns:
17
+ User information
18
+
19
+ Raises:
20
+ APIError: If the API request fails
21
+ """
22
+ return self._client._terrakio_request("GET", f"admin/users/{id}")
23
+
24
+ @require_api_key
25
+ def get_user_by_email(self, email: str) -> Dict[str, Any]:
26
+ """
27
+ Get user by email.
28
+
29
+ Args:
30
+ email: User email
31
+
32
+ Returns:
33
+ User information
34
+
35
+ Raises:
36
+ APIError: If the API request fails
37
+ """
38
+ return self._client._terrakio_request("GET", f"admin/users/email/{email}")
39
+
40
+ @require_api_key
41
+ def list_users(self, substring: Optional[str] = None, uid: bool = False) -> List[Dict[str, Any]]:
42
+ """
43
+ List users, optionally filtering by a substring.
44
+
45
+ Args:
46
+ substring: Optional substring to filter users
47
+ uid: If True, includes the user ID in the response (default: False)
48
+
49
+ Returns:
50
+ List of users
51
+
52
+ Raises:
53
+ APIError: If the API request fails
54
+ """
55
+ params = {"uid": str(uid).lower()}
56
+ if substring:
57
+ params['substring'] = substring
58
+ return self._client._terrakio_request("GET", "admin/users", params=params)
59
+
60
+ @require_api_key
61
+ def edit_user(
62
+ self,
63
+ uid: str,
64
+ email: Optional[str],
65
+ role: Optional[str],
66
+ apiKey: Optional[str],
67
+ groups: Optional[List[str]],
68
+ quota: Optional[int]
69
+ ) -> Dict[str, Any]:
70
+ """
71
+ Edit user info. Only provided fields will be updated.
72
+
73
+ Args:
74
+ uid: User ID
75
+ email: New user email
76
+ role: New user role
77
+ apiKey: New API key
78
+ groups: New list of groups
79
+ quota: New quota
80
+
81
+ Returns:
82
+ Updated user information
83
+
84
+ Raises:
85
+ APIError: If the API request fails
86
+ """
87
+ payload = {"uid": uid}
88
+ payload_mapping = {
89
+ "email": email,
90
+ "role": role,
91
+ "apiKey": apiKey,
92
+ "groups": groups,
93
+ "quota": quota
94
+ }
95
+ for key, value in payload_mapping.items():
96
+ if value is not None:
97
+ payload[key] = value
98
+ return self._client._terrakio_request("PATCH", "admin/users", json=payload)
99
+
100
+ @require_api_key
101
+ def reset_quota(self, email: str, quota: Optional[int] = None) -> Dict[str, Any]:
102
+ """
103
+ Reset the quota for a user by email.
104
+
105
+ Args:
106
+ email: The user's email (required)
107
+ quota: The new quota value (optional)
108
+
109
+ Returns:
110
+ API response as a dictionary
111
+ """
112
+ payload = {"email": email}
113
+ if quota is not None:
114
+ payload["quota"] = quota
115
+ return self._client._terrakio_request("PATCH", f"admin/users/reset_quota/{email}", json=payload)
116
+
117
+ @require_api_key
118
+ def delete_user(self, uid: str) -> Dict[str, Any]:
119
+ """
120
+ Delete a user by UID.
121
+
122
+ Args:
123
+ uid: The user's UID (required)
124
+
125
+ Returns:
126
+ API response as a dictionary
127
+
128
+ Raises:
129
+ APIError: If the API request fails
130
+ """
131
+ return self._client._terrakio_request("DELETE", f"admin/users/{uid}")
@@ -1,7 +1,9 @@
1
1
  class APIError(Exception):
2
2
  """Exception raised for errors in the API responses."""
3
- pass
4
-
3
+
4
+ def __init__(self, message, status_code=None):
5
+ super().__init__(message)
6
+ self.status_code = status_code
5
7
 
6
8
  class ConfigurationError(Exception):
7
9
  """Exception raised for errors in the configuration."""
@@ -0,0 +1,20 @@
1
+ import asyncio
2
+
3
+ # Adapted from https://discuss.python.org/t/boundedtaskgroup-to-control-parallelism/27171
4
+
5
+ class BoundedTaskGroup(asyncio.TaskGroup):
6
+ def __init__(self, *args, max_concurrency = 0, **kwargs):
7
+ super().__init__(*args)
8
+ if max_concurrency:
9
+ self._sem = asyncio.Semaphore(max_concurrency)
10
+ else:
11
+ self._sem = None
12
+
13
+ def create_task(self, coro, *args, **kwargs):
14
+ if self._sem:
15
+ async def _wrapped_coro(sem, coro):
16
+ async with sem:
17
+ return await coro
18
+ coro = _wrapped_coro(self._sem, coro)
19
+
20
+ return super().create_task(coro, *args, **kwargs)
@@ -0,0 +1,58 @@
1
+ # terrakio_core/decorators.py
2
+ from functools import wraps
3
+ from ..exceptions import ConfigurationError
4
+
5
+ def require_token(func):
6
+ """Decorator to ensure a token is available before a method can be executed."""
7
+ @wraps(func)
8
+ def wrapper(self, *args, **kwargs):
9
+ # Check both direct token and client token
10
+ has_token = False
11
+ if hasattr(self, 'token') and self.token:
12
+ has_token = True
13
+ elif hasattr(self, '_client') and hasattr(self._client, 'token') and self._client.token:
14
+ has_token = True
15
+
16
+ if not has_token:
17
+ raise ConfigurationError("Authentication token required. Please login first.")
18
+ return func(self, *args, **kwargs)
19
+
20
+ wrapper._is_decorated = True
21
+ return wrapper
22
+
23
+ def require_api_key(func):
24
+ """Decorator to ensure an API key is available before a method can be executed."""
25
+ @wraps(func)
26
+ def wrapper(self, *args, **kwargs):
27
+ # Check both direct key and client key
28
+ has_key = False
29
+ if hasattr(self, 'key') and self.key:
30
+ has_key = True
31
+ elif hasattr(self, '_client') and hasattr(self._client, 'key') and self._client.key:
32
+ has_key = True
33
+
34
+ if not has_key:
35
+ raise ConfigurationError("API key required. Please provide an API key or login first.")
36
+ return func(self, *args, **kwargs)
37
+
38
+ wrapper._is_decorated = True
39
+ return wrapper
40
+
41
+ def require_auth(func):
42
+ """Decorator that requires either a token OR an API key"""
43
+ @wraps(func)
44
+ def wrapper(self, *args, **kwargs):
45
+ # Check both direct auth and client auth
46
+ has_token = (hasattr(self, 'token') and self.token) or \
47
+ (hasattr(self, '_client') and hasattr(self._client, 'token') and self._client.token)
48
+ has_api_key = (hasattr(self, 'key') and self.key) or \
49
+ (hasattr(self, '_client') and hasattr(self._client, 'key') and self._client.key)
50
+
51
+ if not has_token and not has_api_key:
52
+ raise ConfigurationError(
53
+ "Authentication required. Please provide either an API key or login to get a token."
54
+ )
55
+ return func(self, *args, **kwargs)
56
+
57
+ wrapper._is_decorated = True
58
+ return wrapper
@@ -1,10 +1,6 @@
1
- ### implementing generation-tiles in python api
2
- ### function should just generate the json file for mass_stats to pick up.
3
-
4
1
  import geopandas as gpd
5
2
  import shapely.geometry
6
3
  import json
7
- from rich import print
8
4
 
9
5
  def escape_newline(string):
10
6
  if isinstance(string, list):
@@ -28,7 +24,6 @@ def tile_generator(x_min, y_min, x_max, y_max, aoi, crs, res, tile_size, express
28
24
  j_max += 1
29
25
  for j in range(0, int(j_max)):
30
26
  for i in range(0, int(i_max)):
31
- #print(f"Processing tile {i} {j}")
32
27
  x = x_min + i*(tile_size*res)
33
28
  y = y_max - j*(tile_size*res)
34
29
  bbox = shapely.geometry.box(x, y-(tile_size*res), x + (tile_size*res), y)
@@ -62,10 +57,8 @@ def tiles(
62
57
  non_interactive: bool = False,
63
58
  ):
64
59
 
65
- # Create requests for each tile
66
60
  reqs = []
67
61
  x_min, y_min, x_max, y_max, aoi = get_bounds(aoi, crs, to_crs)
68
- #print(f"Bounds: {x_min}, {y_min}, {x_max}, {y_max}")
69
62
 
70
63
  if to_crs is None:
71
64
  to_crs = crs
@@ -73,9 +66,6 @@ def tiles(
73
66
  req_name = f"{name}_{i:02d}_{j:02d}"
74
67
  reqs.append({"group": "tiles", "file": req_name, "request": tile_req})
75
68
 
76
- #print(f"Generated {len(reqs)} tile requests.")
77
-
78
-
79
69
  count = len(reqs)
80
70
  groups = list(set(dic["group"] for dic in reqs))
81
71
 
@@ -91,5 +81,4 @@ def tiles(
91
81
  request_json = json.dumps(reqs)
92
82
  manifest_json = json.dumps(groups)
93
83
 
94
- return body, request_json, manifest_json
95
-
84
+ return body, request_json, manifest_json