terrakio-core 0.2.3__py3-none-any.whl → 0.2.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.

@@ -0,0 +1,262 @@
1
+ import requests
2
+ from typing import Optional, Dict, Any
3
+
4
+ class MassStats:
5
+ def __init__(self, base_url: str, api_key: str, verify: bool = True, timeout: int = 60):
6
+ self.base_url = base_url.rstrip('/')
7
+ self.api_key = api_key
8
+ self.verify = verify
9
+ self.timeout = timeout
10
+ self.session = requests.Session()
11
+ self.session.headers.update({
12
+ 'x-api-key': self.api_key
13
+ })
14
+
15
+ def upload_request(
16
+ self,
17
+ name: str,
18
+ size: int,
19
+ bucket: str,
20
+ output: str,
21
+ location: Optional[str] = None,
22
+ force_loc: bool = False,
23
+ config: Optional[Dict[str, Any]] = None,
24
+ overwrite: bool = False,
25
+ server: Optional[str] = None,
26
+ skip_existing: bool = False
27
+ ) -> Dict[str, Any]:
28
+ """
29
+ Initiate a mass stats upload job.
30
+
31
+ Args:
32
+ name: Name of the job
33
+ size: Size of the data
34
+ bucket: Storage bucket
35
+ output: Output path or identifier
36
+ location: (Optional) Location for the upload
37
+ force_loc: Force location usage
38
+ config: Optional configuration dictionary
39
+ overwrite: Overwrite existing data
40
+ server: Optional server
41
+ skip_existing: Skip existing files
42
+ """
43
+ url = f"{self.base_url}/mass_stats/upload"
44
+ data = {
45
+ "name": name,
46
+ "size": size,
47
+ "bucket": bucket,
48
+ "output": output,
49
+ "force_loc": force_loc,
50
+ "overwrite": overwrite,
51
+ "skip_existing": skip_existing
52
+ }
53
+ if location is not None:
54
+ data["location"] = location
55
+ if config is not None:
56
+ data["config"] = config
57
+ if server is not None:
58
+ data["server"] = server
59
+ response = self.session.post(url, json=data, verify=self.verify, timeout=self.timeout)
60
+ print("the response is ", response.text)
61
+ # response.raise_for_status()
62
+ return response.json()
63
+
64
+ def start_job(self, task_id: str) -> Dict[str, Any]:
65
+ """
66
+ Start a mass stats job by task ID.
67
+ """
68
+ url = f"{self.base_url}/mass_stats/start/{task_id}"
69
+ print("the self session header is ", self.session.headers)
70
+ response = self.session.post(url, verify=self.verify, timeout=self.timeout)
71
+ response.raise_for_status()
72
+ return response.json()
73
+
74
+ def get_task_id(self, name: str, stage: str, uid: Optional[str] = None) -> Dict[str, Any]:
75
+ """
76
+ Get the task ID for a mass stats job by name and stage (and optionally user ID).
77
+ """
78
+ url = f"{self.base_url}/mass_stats/job_id?name={name}&stage={stage}"
79
+ if uid is not None:
80
+ url += f"&uid={uid}"
81
+ response = self.session.get(url, verify=self.verify, timeout=self.timeout)
82
+ print("response text is ", response.text)
83
+ return response.json()
84
+
85
+ def track_job(self, ids: Optional[list] = None) -> Dict[str, Any]:
86
+ """
87
+ Track the status of one or more mass stats jobs.
88
+ If ids is None, gets progress for all of the user's jobs.
89
+ """
90
+ url = f"{self.base_url}/mass_stats/track"
91
+ data = {"ids": ids} if ids is not None else {}
92
+ response = self.session.post(url, json=data, verify=self.verify, timeout=self.timeout)
93
+ response.raise_for_status()
94
+ return response.json()
95
+
96
+ def get_history(self, limit: int = 100) -> Dict[str, Any]:
97
+ """
98
+ Get the history of mass stats jobs.
99
+ """
100
+ url = f"{self.base_url}/mass_stats/history"
101
+ params = {"limit": limit}
102
+ response = self.session.get(url, params=params, verify=self.verify, timeout=self.timeout)
103
+ response.raise_for_status()
104
+ return response.json()
105
+
106
+ def start_post_processing(
107
+ self,
108
+ process_name: str,
109
+ data_name: str,
110
+ output: str,
111
+ consumer_path: str,
112
+ overwrite: bool = False
113
+ ) -> Dict[str, Any]:
114
+ """
115
+ Start post processing for a mass stats job.
116
+ Args:
117
+ process_name: Folder to store output
118
+ data_name: Name of job used to create data
119
+ output: Output type
120
+ consumer_path: Path to the post processing script (Python file)
121
+ overwrite: Overwrite existing post processing output in same location
122
+ Returns:
123
+ Dict with task_id
124
+ """
125
+ url = f"{self.base_url}/mass_stats/post_process"
126
+ files = {
127
+ 'consumer': (consumer_path, open(consumer_path, 'rb'), 'text/x-python')
128
+ }
129
+ data = {
130
+ 'process_name': process_name,
131
+ 'data_name': data_name,
132
+ 'output': output,
133
+ 'overwrite': str(overwrite).lower()
134
+ }
135
+ response = self.session.post(url, data=data, files=files, verify=self.verify, timeout=self.timeout)
136
+ print("the response is ", response.text)
137
+ # response.raise_for_status()
138
+ return response.json()
139
+
140
+ def download_results(
141
+ self,
142
+ id: Optional[str] = None,
143
+ force_loc: bool = False,
144
+ bucket: Optional[str] = None,
145
+ location: Optional[str] = None,
146
+ output: Optional[str] = None,
147
+ file_name: Optional[str] = None
148
+ ) -> bytes:
149
+ """
150
+ Download results from a mass stats job or arbitrary results if force_loc is True.
151
+ Returns the content of the .zip file.
152
+ """
153
+ url = f"{self.base_url}/mass_stats/download"
154
+ data = {}
155
+ if id is not None:
156
+ data["id"] = id
157
+ if force_loc:
158
+ data["force_loc"] = True
159
+ if bucket is not None:
160
+ data["bucket"] = bucket
161
+ if location is not None:
162
+ data["location"] = location
163
+ if output is not None:
164
+ data["output"] = output
165
+ if file_name is not None:
166
+ data["file_name"] = file_name
167
+ response = self.session.post(url, json=data, verify=self.verify, timeout=self.timeout)
168
+ print("the response is ", response.text)
169
+ # response.raise_for_status()
170
+ print("the response content is ", response.content)
171
+ return response.content
172
+
173
+ def cancel_job(self, id: str) -> Dict[str, Any]:
174
+ """
175
+ Cancel a mass stats job by ID.
176
+ """
177
+ url = f"{self.base_url}/mass_stats/cancel/{id}"
178
+ response = self.session.post(url, verify=self.verify, timeout=self.timeout)
179
+ response.raise_for_status()
180
+ return response.json()
181
+
182
+ def cancel_all_jobs(self) -> Dict[str, Any]:
183
+ """
184
+ Cancel all mass stats jobs for the user.
185
+ """
186
+ url = f"{self.base_url}/mass_stats/cancel"
187
+ response = self.session.post(url, verify=self.verify, timeout=self.timeout)
188
+ response.raise_for_status()
189
+ return response.json()
190
+
191
+ def create_pyramids(self, name: str, levels: int, config: Dict[str, Any]) -> Dict[str, Any]:
192
+ """
193
+ Create pyramids for a dataset.
194
+ Args:
195
+ name: Name for the pyramid job
196
+ levels: Number of zoom levels to compute
197
+ config: Dataset config (mapping)
198
+ Returns:
199
+ Dict with task_id
200
+ """
201
+ url = f"{self.base_url}/pyramids/create"
202
+ data = {
203
+ "name": name,
204
+ "levels": levels,
205
+ "config": config
206
+ }
207
+ response = self.session.post(url, json=data, verify=self.verify, timeout=self.timeout)
208
+ print("the url is ", url)
209
+ print("the response is ", response.text)
210
+ print("the response status code is ", response.status_code)
211
+ # response.raise_for_status()
212
+ return response.json()
213
+
214
+ def random_sample(
215
+ self,
216
+ name: str,
217
+ config: dict,
218
+ aoi: dict,
219
+ samples: int,
220
+ year_range: list,
221
+ crs: str,
222
+ tile_size: int,
223
+ res: float,
224
+ output: str,
225
+ server: str,
226
+ region: str,
227
+ bucket: str,
228
+ overwrite: bool = False
229
+ ) -> Dict[str, Any]:
230
+ """
231
+ Submit a random sample job.
232
+ """
233
+ if year_range is None or len(year_range) != 2:
234
+ raise ValueError("year_range must be a list of two integers")
235
+ start_year, end_year = year_range
236
+ if start_year is None or end_year is None:
237
+ raise ValueError("Both start_year and end_year must be provided for year_range.")
238
+
239
+ url = f"{self.base_url}/random_sample"
240
+ data = {
241
+ "name": name,
242
+ "overwrite": overwrite,
243
+ "config": config,
244
+ "aoi": aoi,
245
+ "samples": samples,
246
+ "year_range": [start_year, end_year],
247
+ "crs": crs,
248
+ "tile_size": tile_size,
249
+ "res": res,
250
+ "output": output,
251
+ "server": server,
252
+ "region": region,
253
+ "bucket": bucket
254
+ }
255
+ print("the data is ", data)
256
+ print("the url is ", url)
257
+ response = self.session.post(url, json=data, verify=self.verify, timeout=self.timeout)
258
+ print("Status code:", response.status_code)
259
+ print("Response text:", response.text)
260
+ # response.raise_for_status()
261
+ return response.json()
262
+
@@ -0,0 +1,101 @@
1
+ import requests
2
+ from typing import Dict, Any, Optional
3
+ from .exceptions import APIError
4
+
5
+ class SpaceManagement:
6
+ def __init__(self, api_url: str, api_key: str, verify: bool = True, timeout: int = 60):
7
+ self.api_url = api_url.rstrip('/')
8
+ self.api_key = api_key
9
+ self.verify = verify
10
+ self.timeout = timeout
11
+ self.session = requests.Session()
12
+ self.session.headers.update({
13
+ 'x-api-key': self.api_key,
14
+ 'Content-Type': 'application/json'
15
+ })
16
+
17
+ def get_total_space_used(self) -> Dict[str, Any]:
18
+ """
19
+ Get total space used by the user.
20
+ Returns a dict with user, total, and jobs breakdown.
21
+ """
22
+ endpoint = f"{self.api_url}/users/jobs"
23
+ try:
24
+ response = self.session.get(endpoint, timeout=self.timeout, verify=self.verify)
25
+ if not response.ok:
26
+ error_msg = f"API request failed: {response.status_code} {response.reason}"
27
+ try:
28
+ error_data = response.json()
29
+ if "detail" in error_data:
30
+ error_msg += f" - {error_data['detail']}"
31
+ except:
32
+ pass
33
+ raise APIError(error_msg)
34
+ return response.json()
35
+ except requests.RequestException as e:
36
+ raise APIError(f"Request failed: {str(e)}")
37
+
38
+ def get_space_used_by_job(self, name: str, region: Optional[str] = None) -> Dict[str, Any]:
39
+ """
40
+ Get space used by a specific job.
41
+ """
42
+ endpoint = f"{self.api_url}/users/jobs/{name}"
43
+ params = {"region": region} if region else {}
44
+ try:
45
+ response = self.session.get(endpoint, params=params, timeout=self.timeout, verify=self.verify)
46
+ if not response.ok:
47
+ error_msg = f"API request failed: {response.status_code} {response.reason}"
48
+ try:
49
+ error_data = response.json()
50
+ if "detail" in error_data:
51
+ error_msg += f" - {error_data['detail']}"
52
+ except:
53
+ pass
54
+ raise APIError(error_msg)
55
+ return response.json()
56
+ except requests.RequestException as e:
57
+ raise APIError(f"Request failed: {str(e)}")
58
+
59
+ def delete_user_job(self, name: str, region: Optional[str] = None) -> Dict[str, Any]:
60
+ """
61
+ Delete a user job by name and region.
62
+ """
63
+ endpoint = f"{self.api_url}/users/job/{name}"
64
+ params = {"region": region} if region else {}
65
+ try:
66
+ response = self.session.delete(endpoint, params=params, timeout=self.timeout, verify=self.verify)
67
+ if not response.ok:
68
+ error_msg = f"API request failed: {response.status_code} {response.reason}"
69
+ try:
70
+ error_data = response.json()
71
+ if "detail" in error_data:
72
+ error_msg += f" - {error_data['detail']}"
73
+ except:
74
+ pass
75
+ raise APIError(error_msg)
76
+ return response.json()
77
+ except requests.RequestException as e:
78
+ raise APIError(f"Request failed: {str(e)}")
79
+
80
+ def delete_data_in_path(self, path: str, region: Optional[str] = None) -> Dict[str, Any]:
81
+ """
82
+ Delete data in a GCS path for a given region.
83
+ """
84
+ endpoint = f"{self.api_url}/users/jobs"
85
+ params = {"path": path}
86
+ if region:
87
+ params["region"] = region
88
+ try:
89
+ response = self.session.delete(endpoint, params=params, timeout=self.timeout, verify=self.verify)
90
+ if not response.ok:
91
+ error_msg = f"API request failed: {response.status_code} {response.reason}"
92
+ try:
93
+ error_data = response.json()
94
+ if "detail" in error_data:
95
+ error_msg += f" - {error_data['detail']}"
96
+ except:
97
+ pass
98
+ raise APIError(error_msg)
99
+ return response.json()
100
+ except requests.RequestException as e:
101
+ raise APIError(f"Request failed: {str(e)}")
@@ -0,0 +1,227 @@
1
+ import requests
2
+ from typing import Dict, Any, List, Optional
3
+ from .exceptions import APIError, ConfigurationError
4
+
5
+ class UserManagement:
6
+ def __init__(self, api_url: str, api_key: str, verify: bool = True, timeout: int = 60):
7
+ """
8
+ Initialize the User Management client.
9
+
10
+ Args:
11
+ api_url: API base URL
12
+ api_key: API key for authentication
13
+ verify: Verify SSL certificates
14
+ timeout: Request timeout in seconds
15
+ """
16
+ self.api_url = api_url.rstrip('/')
17
+ self.api_key = api_key
18
+ self.verify = verify
19
+ self.timeout = timeout
20
+ self.session = requests.Session()
21
+ self.session.headers.update({
22
+ 'x-api-key': self.api_key,
23
+ 'Content-Type': 'application/json'
24
+ })
25
+
26
+ def get_user_by_id(self, user_id: str) -> Dict[str, Any]:
27
+ """
28
+ Retrieve user info by user ID.
29
+
30
+ Args:
31
+ user_id: User ID to retrieve
32
+
33
+ Returns:
34
+ User information as a dictionary
35
+
36
+ Raises:
37
+ APIError: If the API request fails
38
+ """
39
+ endpoint = f"{self.api_url}/admin/users/{user_id}"
40
+ try:
41
+ response = self.session.get(
42
+ endpoint,
43
+ timeout=self.timeout,
44
+ verify=self.verify
45
+ )
46
+ if not response.ok:
47
+ raise APIError(f"API request failed: {response.status_code} {response.reason}")
48
+ return response.json()
49
+ except requests.RequestException as e:
50
+ raise APIError(f"Request failed: {str(e)}")
51
+
52
+ def get_user_by_email(self, email: str) -> Dict[str, Any]:
53
+ """
54
+ Retrieve user info by email.
55
+
56
+ Args:
57
+ email: User email to retrieve
58
+
59
+ Returns:
60
+ User information as a dictionary
61
+
62
+ Raises:
63
+ APIError: If the API request fails
64
+ """
65
+
66
+ endpoint = f"{self.api_url}/admin/users/email/{email}"
67
+ try:
68
+ response = self.session.get(
69
+ endpoint,
70
+ timeout=self.timeout,
71
+ verify=self.verify
72
+ )
73
+ if not response.ok:
74
+ raise APIError(f"API request failed: {response.status_code} {response.reason}")
75
+ return response.json()
76
+ except requests.RequestException as e:
77
+ raise APIError(f"Request failed: {str(e)}")
78
+
79
+ def edit_user(
80
+ self,
81
+ user_id: str,
82
+ uid: Optional[str] = None,
83
+ email: Optional[str] = None,
84
+ role: Optional[str] = None,
85
+ apiKey: Optional[str] = None,
86
+ groups: Optional[List[str]] = None,
87
+ quota: Optional[int] = None
88
+ ) -> Dict[str, Any]:
89
+ """
90
+ Edit user info. Only provided fields will be updated.
91
+
92
+ Args:
93
+ user_id: User ID (required)
94
+ uid: New user ID (optional)
95
+ email: New user email (optional)
96
+ role: New user role (optional)
97
+ apiKey: New API key (optional)
98
+ groups: New list of groups (optional)
99
+ quota: New quota value (optional)
100
+
101
+ Returns:
102
+ Updated user information
103
+
104
+ Raises:
105
+ APIError: If the API request fails
106
+ """
107
+ endpoint = f"{self.api_url}/admin/users"
108
+ payload = {"uid": user_id}
109
+
110
+ if uid is not None:
111
+ payload["uid"] = uid
112
+ if email is not None:
113
+ payload["email"] = email
114
+ if role is not None:
115
+ payload["role"] = role
116
+ if apiKey is not None:
117
+ payload["apiKey"] = apiKey
118
+ if groups is not None:
119
+ payload["groups"] = groups
120
+ if quota is not None:
121
+ payload["quota"] = quota
122
+
123
+ try:
124
+ response = self.session.patch(
125
+ endpoint,
126
+ json=payload,
127
+ timeout=self.timeout,
128
+ verify=self.verify
129
+ )
130
+ if not response.ok:
131
+ raise APIError(f"API request failed: {response.status_code} {response.reason}")
132
+ return response.json()
133
+ except requests.RequestException as e:
134
+ raise APIError(f"Request failed: {str(e)}")
135
+
136
+ def list_users(self, substring: Optional[str] = None, uid: bool = False) -> List[Dict[str, Any]]:
137
+ """
138
+ List users, optionally filtering by a substring.
139
+
140
+ Args:
141
+ substring: Optional substring to filter users
142
+ uid: If True, includes the user ID in the response (default: False)
143
+
144
+ Returns:
145
+ List of users
146
+
147
+ Raises:
148
+ APIError: If the API request fails
149
+ """
150
+ # Use the base API URL instead of hardcoding
151
+ endpoint = "https://terrakio-server-lark-573248941006.australia-southeast1.run.app/admin/users"
152
+
153
+ params = {}
154
+ if substring:
155
+ params["substring"] = substring
156
+ if uid:
157
+ params["uid"] = "true"
158
+
159
+ try:
160
+ response = self.session.get(
161
+ endpoint,
162
+ params=params,
163
+ timeout=self.timeout,
164
+ verify=self.verify
165
+ )
166
+ if not response.ok:
167
+ raise APIError(f"API request failed: {response.status_code} {response.reason}")
168
+ return response.json()
169
+ except requests.RequestException as e:
170
+ raise APIError(f"Request failed: {str(e)}")
171
+
172
+ def reset_quota(self, email: str, quota: Optional[int] = None) -> Dict[str, Any]:
173
+ """
174
+ Reset the quota for a user by email.
175
+
176
+ Args:
177
+ email: The user's email (required)
178
+ quota: The new quota value (optional)
179
+
180
+ Returns:
181
+ API response as a dictionary
182
+
183
+ Raises:
184
+ APIError: If the API request fails
185
+ """
186
+ endpoint = f"{self.api_url}/admin/users/reset_quota/{email}"
187
+ payload = {"email": email}
188
+ if quota is not None:
189
+ payload["quota"] = quota
190
+ try:
191
+ response = self.session.patch(
192
+ endpoint,
193
+ json=payload,
194
+ timeout=self.timeout,
195
+ verify=self.verify
196
+ )
197
+ if not response.ok:
198
+ raise APIError(f"API request failed: {response.status_code} {response.reason}")
199
+ return response.json()
200
+ except requests.RequestException as e:
201
+ raise APIError(f"Request failed: {str(e)}")
202
+
203
+ def delete_user(self, uid: str) -> Dict[str, Any]:
204
+ """
205
+ Delete a user by UID.
206
+
207
+ Args:
208
+ uid: The user's UID (required)
209
+
210
+ Returns:
211
+ API response as a dictionary
212
+
213
+ Raises:
214
+ APIError: If the API request fails
215
+ """
216
+ endpoint = f"{self.api_url}/admin/users/{uid}"
217
+ try:
218
+ response = self.session.delete(
219
+ endpoint,
220
+ timeout=self.timeout,
221
+ verify=self.verify
222
+ )
223
+ if not response.ok:
224
+ raise APIError(f"API request failed: {response.status_code} {response.reason}")
225
+ return response.json()
226
+ except requests.RequestException as e:
227
+ raise APIError(f"Request failed: {str(e)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: terrakio-core
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: Core components for Terrakio API clients
5
5
  Author-email: Yupeng Chao <yupeng@haizea.com.au>
6
6
  Project-URL: Homepage, https://github.com/HaizeaAnalytics/terrakio-python-api
@@ -0,0 +1,14 @@
1
+ terrakio_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ terrakio_core/auth.py,sha256=Nuj0_X3Hiy17svYgGxrSAR-LXpTlP0J0dSrfMnkPUbI,7717
3
+ terrakio_core/client.py,sha256=ChKbsGsCbkngRZ8mWgAijhKzElL0ZSdRHU2_XF9C7Ss,44850
4
+ terrakio_core/config.py,sha256=AwJ1VgR5K7N32XCU5k7_Dp1nIv_FYt8MBonq9yKlGzA,2658
5
+ terrakio_core/dataset_management.py,sha256=hhO35fwStS6HYFQdKP9wkr3DxHgjvpctmIU8UWH6w6U,8742
6
+ terrakio_core/exceptions.py,sha256=9S-I20-QiDRj1qgjFyYUwYM7BLic_bxurcDOIm2Fu_0,410
7
+ terrakio_core/group_access_management.py,sha256=NJ7SX4keUzZAUENmJ5L6ynKf4eRlqtyir5uoKFyY17A,7315
8
+ terrakio_core/mass_stats.py,sha256=AqYJsd6nqo2BDh4vEPUDgsv4T0UR1_TPDoXa3WO3gTU,9284
9
+ terrakio_core/space_management.py,sha256=wlUUQrlj_4U_Lpjn9lbF5oj0Rv3NPvvnrd5mWej5kmA,4211
10
+ terrakio_core/user_management.py,sha256=MMNWkz0V_9X7ZYjjteuRU4H4W3F16iuQw1dpA2wVTGg,7400
11
+ terrakio_core-0.2.4.dist-info/METADATA,sha256=dMdHIPIFLVF1UMfTSa7D3GMLOw6nVJwl4hMeMcs0FdQ,1405
12
+ terrakio_core-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ terrakio_core-0.2.4.dist-info/top_level.txt,sha256=5cBj6O7rNWyn97ND4YuvvXm0Crv4RxttT4JZvNdOG6Q,14
14
+ terrakio_core-0.2.4.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ terrakio_core
@@ -1,4 +0,0 @@
1
- terrakio_core-0.2.3.dist-info/METADATA,sha256=y2lq4MvHKznM_bVtqtlrTW92lVpaOO9MpRz-e2wPVPQ,1405
2
- terrakio_core-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3
- terrakio_core-0.2.3.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
4
- terrakio_core-0.2.3.dist-info/RECORD,,
@@ -1 +0,0 @@
1
-