accli 0.1.1__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.
@@ -0,0 +1,377 @@
1
+ import json
2
+ import base64
3
+ import urllib3
4
+
5
+ class AcceleratorProjectService:
6
+ def __init__(
7
+ self,
8
+ user_token,
9
+ cli_base_url='http://accelerator-api/v1/acli',
10
+ verify_cert=True
11
+ ):
12
+ self.user_token = user_token
13
+
14
+ if verify_cert:
15
+ self.http_client = urllib3.poolmanager.PoolManager(num_pools=1)
16
+ else:
17
+ self.http_client = urllib3.poolmanager.PoolManager(cert_reqs="CERT_NONE", num_pools=1)
18
+
19
+ self.cli_base_url = cli_base_url
20
+ self.common_request_headers = {
21
+ 'x-authorization': user_token
22
+ }
23
+
24
+ def get_file_stat(self, bucket_object_id):
25
+ res = self.http_client.request(
26
+ "GET",
27
+ f"{self.cli_base_url}/file-stat/{bucket_object_id}",
28
+ headers=self.common_request_headers
29
+ )
30
+ return res.json()
31
+
32
+ def get_dataset_type(self, *args, **kwargs):
33
+ return self.get_bucket_object_validation_type(*args, **kwargs)
34
+
35
+ def get_bucket_object_validation_type(self, bucket_object_id):
36
+ res = self.http_client.request(
37
+ "GET",
38
+ f"{self.cli_base_url}/dataset-type/{bucket_object_id}",
39
+ headers=self.common_request_headers
40
+ )
41
+ if res.data:
42
+ return res.data.decode()
43
+
44
+
45
+ def get_file_url(self, bucket_object_id):
46
+ res = self.http_client.request(
47
+ "GET",
48
+ f"{self.cli_base_url}/file-url/{bucket_object_id}",
49
+ headers=self.common_request_headers
50
+ )
51
+
52
+ if res.data:
53
+ return res.data.decode()
54
+
55
+
56
+ def get_file_stream(self, bucket_object_id):
57
+ url = self.get_file_url(bucket_object_id)
58
+ if url:
59
+ url = res.data.decode()
60
+ resp = http_client.request("GET", url, preload_content=False)
61
+ return resp
62
+
63
+ def get_multipart_put_create_signed_url(
64
+ self,
65
+ app_bucket_id,
66
+ object_name,
67
+ upload_id,
68
+ part_number
69
+ ):
70
+ res = self.http_client.request(
71
+ "GET",
72
+ f"{self.cli_base_url}/put-create-signed-url",
73
+ fields=dict(
74
+ app_bucket_id=app_bucket_id,
75
+ object_name=object_name,
76
+ upload_id=upload_id,
77
+ part_number=part_number
78
+ ),
79
+ headers=self.common_request_headers
80
+ )
81
+
82
+ return res.data.decode()
83
+
84
+ def get_multipart_put_update_signed_url(
85
+ self,
86
+ bucket_object_id,
87
+ upload_id,
88
+ part_number,
89
+ ):
90
+ res = self.http_client.request(
91
+ "GET",
92
+ f"{self.cli_base_url}/put-update-signed-url",
93
+ fields=dict(
94
+ bucket_object_id=bucket_object_id,
95
+ upload_id=upload_id,
96
+ part_number=part_number
97
+ ),
98
+ headers=self.common_request_headers
99
+ )
100
+
101
+ return res.data.decode()
102
+
103
+ def get_put_create_multipart_upload_id(self, filename):
104
+ res = self.http_client.request(
105
+ "GET",
106
+ f"{self.cli_base_url}/create-multipart-upload-id/{filename}",
107
+ headers=self.common_request_headers
108
+ )
109
+
110
+ return res.data.decode()
111
+
112
+ def get_put_update_multipart_upload_id(self, bucket_object_id):
113
+ res = self.http_client.request(
114
+ "GET",
115
+ f"{self.cli_base_url}/create-multipart-upload-id/{bucket_object_id}",
116
+ headers=self.common_request_headers
117
+ )
118
+
119
+ return res.data.decode()
120
+
121
+ def complete_job_multipart_upload(
122
+ self,
123
+ app_bucket_id,
124
+ filename,
125
+ upload_id,
126
+ parts: list[tuple[str, str]]
127
+ ):
128
+ headers = {"Content-Type": "application/json"}
129
+
130
+ headers.update(self.common_request_headers)
131
+
132
+ res = self.http_client.request(
133
+ "PUT",
134
+ f"{self.cli_base_url}/complete-create-multipart-upload",
135
+ json=dict(
136
+ app_bucket_id=app_bucket_id,
137
+ filename=filename,
138
+ upload_id=upload_id,
139
+ parts=base64.b64encode(json.dumps(parts))
140
+ ),
141
+ headers=headers
142
+ )
143
+
144
+ def complete_update_multipart_upload(
145
+ self, bucket_object_id, upload_id, parts: list[tuple[str, str]]
146
+ ):
147
+
148
+ headers = {"Content-Type": "application/json"}
149
+
150
+ headers.update(self.common_request_headers)
151
+
152
+ res = self.http_client.request(
153
+ "PUT",
154
+ f"{self.cli_base_url}/complete-update-multipart-upload",
155
+ json=dict(
156
+ bucket_object_id=bucket_object_id,
157
+ upload_id=upload_id,
158
+ parts=base64.b64encode(json.dumps(parts))
159
+ ),
160
+ headers=headers
161
+ )
162
+
163
+ def abort_create_multipart_upload(self, app_bucket_id, filename, upload_id):
164
+
165
+ headers = {"Content-Type": "application/json"}
166
+
167
+ headers.update(self.common_request_headers)
168
+
169
+ res = self.http_client.request(
170
+ "PUT",
171
+ f"{self.cli_base_url}/abort-create-multipart-upload",
172
+ json=dict(
173
+ app_bucket_id=app_bucket_id,
174
+ filename=filename,
175
+ upload_id=upload_id
176
+ ),
177
+ headers=headers
178
+ )
179
+
180
+ def abort_update_multipart_upload(self, bucket_object_id, upload_id):
181
+
182
+ headers = {"Content-Type": "application/json"}
183
+
184
+ headers.update(self.common_request_headers)
185
+
186
+ res = self.http_client.request(
187
+ "PUT",
188
+ f"{self.cli_base_url}/abort-update-multipart-upload",
189
+ json=dict(
190
+ bucket_object_id=bucket_object_id,
191
+ upload_id=upload_id
192
+ ),
193
+ headers=headers
194
+ )
195
+ # TODO @wrufesh ensure it requires special token
196
+ def register_iamc_validation(
197
+ self, validated_bucket_object_id, indexdb_bucket_object_id
198
+ ):
199
+
200
+ headers = {"Content-Type": "application/json"}
201
+
202
+ headers.update(self.common_request_headers)
203
+
204
+ res = self.http_client.request(
205
+ "PUT",
206
+ f"{self.cli_base_url}/register-iamc-validation",
207
+ json=dict(
208
+ validated_bucket_object_id=validated_bucket_object_id,
209
+ indexdb_bucket_object_id=indexdb_bucket_object_id
210
+ ),
211
+ headers=headers
212
+ )
213
+
214
+
215
+
216
+ def read_part_data(self, stream, size, part_data=b"", progress=None):
217
+ """Read part data of given size from stream."""
218
+ size -= len(part_data)
219
+ while size:
220
+ data = stream.read(size)
221
+ if not data:
222
+ break # EOF reached
223
+ if not isinstance(data, bytes):
224
+ raise ValueError("read() must return 'bytes' object")
225
+ part_data += data
226
+ size -= len(data)
227
+ if progress:
228
+ progress.update(len(data))
229
+ return part_data
230
+
231
+ def add_filestream_as_job_output(self, filename, file_stream, user_id, job_id):
232
+ headers = dict()
233
+ headers["Content-Type"] = "application/octet-stream"
234
+
235
+ part_size, part_count = 5 * 1024**2, -1
236
+
237
+ upload_id = None
238
+ app_bucket_id = None
239
+ uniqified_filename = None
240
+
241
+ one_byte = b""
242
+ stop = False
243
+ part_number = 0
244
+ parts = []
245
+ uploaded_size = 0
246
+ put_presigned_url = None
247
+
248
+ try:
249
+ while not stop:
250
+ part_number += 1
251
+ part_data = self.read_part_data(
252
+ file_stream,
253
+ part_size + 1,
254
+ one_byte,
255
+ progress=None,
256
+ )
257
+
258
+ # If part_data_size is less or equal to part_size,
259
+ # then we have reached last part.
260
+ if len(part_data) <= part_size:
261
+ part_count = part_number
262
+ stop = True
263
+ else:
264
+ one_byte = part_data[-1:]
265
+ part_data = part_data[:-1]
266
+
267
+ uploaded_size += len(part_data)
268
+
269
+ if not upload_id:
270
+ (
271
+ upload_id,
272
+ app_bucket_id,
273
+ uniqified_filename,
274
+ ) = self.get_put_create_multipart_upload_id(
275
+ filename, headers=headers
276
+ )
277
+
278
+ put_presigned_url = self.get_multipart_put_create_signed_url(
279
+ app_bucket_id, uniqified_filename, upload_id, part_number
280
+ )
281
+
282
+ part_upload_response = requests.put(
283
+ put_presigned_url,
284
+ data=part_data,
285
+ # headers=headers,
286
+ verify=False,
287
+ )
288
+
289
+ etag = part_upload_response.headers.get("etag").replace('"', "")
290
+ parts.append((part_number, etag))
291
+
292
+ result, created_bucket_object_id = self.complete_job_multipart_upload(
293
+ app_bucket_id, uniqified_filename, upload_id, parts, job_id, user_id
294
+ )
295
+ return created_bucket_object_id
296
+
297
+ except Exception as err:
298
+ # Cancel if any error
299
+ if upload_id:
300
+ self.abort_create_multipart_upload(
301
+ app_bucket_id,
302
+ uniqified_filename,
303
+ upload_id,
304
+ )
305
+ raise err
306
+
307
+ def replace_bucket_object_id_content(self, bucket_object_id, file_stream):
308
+ headers = dict()
309
+ headers["Content-Type"] = "application/octet-stream"
310
+
311
+ part_size, part_count = 5 * 1024**2, -1
312
+
313
+ upload_id = None
314
+ app_bucket_id = None
315
+ uniqified_filename = None
316
+
317
+ one_byte = b""
318
+ stop = False
319
+ part_number = 0
320
+ parts = []
321
+ uploaded_size = 0
322
+ put_presigned_url = None
323
+
324
+ try:
325
+ while not stop:
326
+ part_number += 1
327
+ part_data = self.read_part_data(
328
+ file_stream,
329
+ part_size + 1,
330
+ one_byte,
331
+ progress=None,
332
+ )
333
+
334
+ # If part_data_size is less or equal to part_size,
335
+ # then we have reached last part.
336
+ if len(part_data) <= part_size:
337
+ part_count = part_number
338
+ stop = True
339
+ else:
340
+ one_byte = part_data[-1:]
341
+ part_data = part_data[:-1]
342
+
343
+ uploaded_size += len(part_data)
344
+
345
+ if not upload_id:
346
+ upload_id = self.get_put_update_multipart_upload_id(
347
+ bucket_object_id, headers=headers
348
+ )
349
+
350
+ put_presigned_url = self.get_multipart_put_update_signed_url(
351
+ bucket_object_id, upload_id, part_number
352
+ )
353
+
354
+ part_upload_response = requests.put(
355
+ put_presigned_url,
356
+ data=part_data,
357
+ # headers=headers,
358
+ verify=False,
359
+ )
360
+
361
+ etag = part_upload_response.headers.get("etag").replace('"', "")
362
+ parts.append((part_number, etag))
363
+
364
+ result = self.complete_update_multipart_upload(
365
+ bucket_object_id,
366
+ upload_id,
367
+ parts,
368
+ )
369
+ except Exception as err:
370
+ # Cancel if any error
371
+ if upload_id:
372
+ self.abort_update_multipart_upload(
373
+ bucket_object_id,
374
+ upload_id,
375
+ )
376
+
377
+ raise err
accli/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .AcceleratorProjectService import AcceleratorProjectService
2
+
3
+ ACliService = AcceleratorProjectService
accli/enums.py ADDED
@@ -0,0 +1,3 @@
1
+ class ValidationType(enum.Enum):
2
+ IAMC = 1
3
+ SPATIALX = 2
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.1
2
+ Name: accli
3
+ Version: 0.1.1
4
+ Summary: IIASA Accelerator Client
5
+ Author-email: Wrufesh S <wrufesh@gmail.com>
6
+ License: Copyright (c) 2018 The Python Packaging Authority
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
25
+ Project-URL: Homepage, https://github.com/iiasa/accli
26
+ Project-URL: Bug Tracker, https://github.com/iiasa/accli/issues
27
+ Classifier: Programming Language :: Python :: 3
28
+ Classifier: License :: OSI Approved :: MIT License
29
+ Classifier: Operating System :: OS Independent
30
+ Requires-Python: >=3.7
31
+ Description-Content-Type: text/markdown
32
+ License-File: LICENSE
33
+ Requires-Dist: urllib3 >=2.0.4
34
+
35
+ ## Accelerator python client
@@ -0,0 +1,8 @@
1
+ accli/AcceleratorProjectService.py,sha256=sVoQQqmNgtXahNWxiag27MAWwnyj6H7iFg9H1ADp0sk,12031
2
+ accli/__init__.py,sha256=GT9LGFh1C0q2V7aKWSN1x52ESO7p0cybOK12N4B0ySU,105
3
+ accli/enums.py,sha256=hyvf0FyMsqrPXWVjE8_pUdtn6_LRFEO9M3ACIHmX6YE,62
4
+ accli-0.1.1.dist-info/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
5
+ accli-0.1.1.dist-info/METADATA,sha256=1Nz22CIat2TYaJShVrxIJiVSK7ps3q-vNPQNgwCJpdA,1764
6
+ accli-0.1.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
7
+ accli-0.1.1.dist-info/top_level.txt,sha256=XI5YW6x7TyhYQ0YF-dJwgPrAiRKlmBeQEY70-LyLJoo,6
8
+ accli-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.41.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ accli