voltsdk 0.1.0__tar.gz
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.
- voltsdk-0.1.0/PKG-INFO +48 -0
- voltsdk-0.1.0/README.md +33 -0
- voltsdk-0.1.0/__init__.py +4 -0
- voltsdk-0.1.0/client.py +269 -0
- voltsdk-0.1.0/pyproject.toml +30 -0
- voltsdk-0.1.0/setup.cfg +4 -0
- voltsdk-0.1.0/utils.py +154 -0
- voltsdk-0.1.0/voltsdk.egg-info/PKG-INFO +48 -0
- voltsdk-0.1.0/voltsdk.egg-info/SOURCES.txt +13 -0
- voltsdk-0.1.0/voltsdk.egg-info/dependency_links.txt +1 -0
- voltsdk-0.1.0/voltsdk.egg-info/requires.txt +10 -0
- voltsdk-0.1.0/voltsdk.egg-info/top_level.txt +1 -0
voltsdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: voltsdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Volt API SDK for scripting and notebooks
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: msgpack>=1.0.0
|
|
8
|
+
Requires-Dist: pandas>=2.0.0
|
|
9
|
+
Requires-Dist: requests>=2.31.0
|
|
10
|
+
Provides-Extra: visualization
|
|
11
|
+
Requires-Dist: vtk>=9.2.0; extra == "visualization"
|
|
12
|
+
Provides-Extra: notebook
|
|
13
|
+
Requires-Dist: vtk>=9.2.0; extra == "notebook"
|
|
14
|
+
Requires-Dist: k3d>=2.16.0; extra == "notebook"
|
|
15
|
+
|
|
16
|
+
# voltsdk
|
|
17
|
+
|
|
18
|
+
Python SDK for interacting with the Volt API.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install voltsdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Install visualization support with:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install "voltsdk[visualization]"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Install notebook support with:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install "voltsdk[notebook]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from voltsdk import VoltClient
|
|
42
|
+
|
|
43
|
+
client = VoltClient(
|
|
44
|
+
secret_key="your-secret-key",
|
|
45
|
+
base_url="https://api.example.com"
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
voltsdk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# voltsdk
|
|
2
|
+
|
|
3
|
+
Python SDK for interacting with the Volt API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install voltsdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Install visualization support with:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install "voltsdk[visualization]"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Install notebook support with:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install "voltsdk[notebook]"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from voltsdk import VoltClient
|
|
27
|
+
|
|
28
|
+
client = VoltClient(
|
|
29
|
+
secret_key="your-secret-key",
|
|
30
|
+
base_url="https://api.example.com"
|
|
31
|
+
)
|
|
32
|
+
```
|
|
33
|
+
|
voltsdk-0.1.0/client.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
import os
|
|
3
|
+
import requests
|
|
4
|
+
import zipfile
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class VoltClient:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
secret_key: str,
|
|
11
|
+
base_url: Optional[str] = None,
|
|
12
|
+
timeout: int = 30
|
|
13
|
+
) -> None:
|
|
14
|
+
if not secret_key:
|
|
15
|
+
raise ValueError('secret_key is required')
|
|
16
|
+
|
|
17
|
+
if not base_url:
|
|
18
|
+
raise ValueError('base_url is required')
|
|
19
|
+
|
|
20
|
+
self.secret_key = secret_key
|
|
21
|
+
self.base_url = base_url
|
|
22
|
+
self.timeout = timeout
|
|
23
|
+
|
|
24
|
+
self._secret_key_info: Optional[Dict[str, Any]] = None
|
|
25
|
+
self._team_id: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
self.session = requests.Session()
|
|
28
|
+
self.session.headers.update({
|
|
29
|
+
'Authorization': f'Bearer {secret_key}',
|
|
30
|
+
'Accept': 'application/json'
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
self._setup_client()
|
|
34
|
+
|
|
35
|
+
def _request(
|
|
36
|
+
self,
|
|
37
|
+
method: str,
|
|
38
|
+
path: str,
|
|
39
|
+
*,
|
|
40
|
+
params: Optional[Dict[str, Any]] = None
|
|
41
|
+
) -> Any:
|
|
42
|
+
response = self._send(
|
|
43
|
+
method,
|
|
44
|
+
path,
|
|
45
|
+
params=params
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
payload = response.json()
|
|
49
|
+
if payload.get('status') != 'success':
|
|
50
|
+
raise RuntimeError(payload.get('message') or 'Volt API request faield')
|
|
51
|
+
|
|
52
|
+
return payload.get('data')
|
|
53
|
+
|
|
54
|
+
def _send(
|
|
55
|
+
self,
|
|
56
|
+
method: str,
|
|
57
|
+
path: str,
|
|
58
|
+
*,
|
|
59
|
+
params: Optional[Dict[str, Any]] = None,
|
|
60
|
+
stream: bool = False
|
|
61
|
+
) -> requests.Response:
|
|
62
|
+
url = f"{self.base_url.rstrip('/')}/{path.lstrip('/')}"
|
|
63
|
+
response = self.session.request(
|
|
64
|
+
method=method,
|
|
65
|
+
url=url,
|
|
66
|
+
params=params,
|
|
67
|
+
timeout=self.timeout,
|
|
68
|
+
stream=stream
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if response.status_code >= 400:
|
|
72
|
+
try:
|
|
73
|
+
payload = response.json()
|
|
74
|
+
message = payload.get('message') or response.text
|
|
75
|
+
except Exception:
|
|
76
|
+
message = response.text or response.reason
|
|
77
|
+
|
|
78
|
+
raise requests.HTTPError(
|
|
79
|
+
f'{response.status_code} {response.reason}: {message} ({response.url})',
|
|
80
|
+
response=response
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return response
|
|
84
|
+
|
|
85
|
+
def _setup_client(self) -> Dict[str, Any]:
|
|
86
|
+
data = self._request('GET', '/team/secret-keys/me')
|
|
87
|
+
|
|
88
|
+
self._secret_key_info = data
|
|
89
|
+
self._team_id = data.get('team')
|
|
90
|
+
return data
|
|
91
|
+
|
|
92
|
+
def _resolve_download_filename(
|
|
93
|
+
self,
|
|
94
|
+
response: requests.Response,
|
|
95
|
+
fallback_name: str
|
|
96
|
+
) -> str:
|
|
97
|
+
content_disposition = response.headers.get('Content-Disposition', '')
|
|
98
|
+
if 'filename=' not in content_disposition:
|
|
99
|
+
return fallback_name
|
|
100
|
+
|
|
101
|
+
filename = content_disposition.split('filename=')[-1].strip().strip('"').strip("'")
|
|
102
|
+
if not filename:
|
|
103
|
+
return fallback_name
|
|
104
|
+
|
|
105
|
+
return os.path.basename(filename)
|
|
106
|
+
|
|
107
|
+
def _download_stream(
|
|
108
|
+
self,
|
|
109
|
+
path: str,
|
|
110
|
+
fallback_name: str
|
|
111
|
+
) -> str:
|
|
112
|
+
with self._send('GET', path, stream=True) as response:
|
|
113
|
+
downloads_dir = './downloads'
|
|
114
|
+
os.makedirs(downloads_dir, exist_ok=True)
|
|
115
|
+
|
|
116
|
+
filename = self._resolve_download_filename(response, fallback_name)
|
|
117
|
+
file_path = os.path.join(downloads_dir, filename)
|
|
118
|
+
total_bytes = int(response.headers.get('Content-Length', 0) or 0)
|
|
119
|
+
downloaded_bytes = 0
|
|
120
|
+
|
|
121
|
+
with open(file_path, 'wb') as file:
|
|
122
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
123
|
+
if not chunk:
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
file.write(chunk)
|
|
127
|
+
downloaded_bytes += len(chunk)
|
|
128
|
+
|
|
129
|
+
if total_bytes > 0:
|
|
130
|
+
percent = (downloaded_bytes * 100) / total_bytes
|
|
131
|
+
print(
|
|
132
|
+
f'\rDownloading {filename}: {percent:.2f}% ({downloaded_bytes}/{total_bytes} bytes)',
|
|
133
|
+
end='',
|
|
134
|
+
flush=True
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
print(
|
|
138
|
+
f'\rDownloading {filename}: {downloaded_bytes} bytes',
|
|
139
|
+
end='',
|
|
140
|
+
flush=True
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
print()
|
|
144
|
+
|
|
145
|
+
return file_path
|
|
146
|
+
|
|
147
|
+
def _extract_zip_file(self, zip_path: str) -> str:
|
|
148
|
+
extract_dir = zip_path[:-4]
|
|
149
|
+
os.makedirs(extract_dir, exist_ok=True)
|
|
150
|
+
|
|
151
|
+
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
152
|
+
zip_ref.extractall(extract_dir)
|
|
153
|
+
|
|
154
|
+
os.remove(zip_path)
|
|
155
|
+
return extract_dir
|
|
156
|
+
|
|
157
|
+
def _unzip_recursive(self, zip_path: str) -> str:
|
|
158
|
+
if not zip_path.lower().endswith('.zip'):
|
|
159
|
+
return zip_path
|
|
160
|
+
|
|
161
|
+
root_dir = self._extract_zip_file(zip_path)
|
|
162
|
+
while True:
|
|
163
|
+
zip_files = []
|
|
164
|
+
for current_root, _, files in os.walk(root_dir):
|
|
165
|
+
for file_name in files:
|
|
166
|
+
if file_name.lower().endswith('.zip'):
|
|
167
|
+
zip_files.append(os.path.join(current_root, file_name))
|
|
168
|
+
|
|
169
|
+
if not zip_files:
|
|
170
|
+
break
|
|
171
|
+
|
|
172
|
+
total_zip_files = len(zip_files)
|
|
173
|
+
for index, current_zip_path in enumerate(zip_files, start=1):
|
|
174
|
+
print(
|
|
175
|
+
f'Extracting nested zip {index}/{total_zip_files}: {os.path.basename(current_zip_path)}',
|
|
176
|
+
flush=True
|
|
177
|
+
)
|
|
178
|
+
self._extract_zip_file(current_zip_path)
|
|
179
|
+
|
|
180
|
+
return root_dir
|
|
181
|
+
|
|
182
|
+
def list_analyses(
|
|
183
|
+
self,
|
|
184
|
+
trajectory_id: str,
|
|
185
|
+
page: int = 1,
|
|
186
|
+
limit: int = 1000
|
|
187
|
+
) -> Dict[str, Any]:
|
|
188
|
+
response = self._request(
|
|
189
|
+
'GET',
|
|
190
|
+
f'/analysis/{self._team_id}/trajectory/{trajectory_id}',
|
|
191
|
+
params={'page': page, 'limit': limit}
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return response.get('data')
|
|
195
|
+
|
|
196
|
+
def list_analysis_results(
|
|
197
|
+
self,
|
|
198
|
+
analysis_id: str,
|
|
199
|
+
page: int = 1,
|
|
200
|
+
limit: int = 1000
|
|
201
|
+
):
|
|
202
|
+
response = self._request(
|
|
203
|
+
'GET',
|
|
204
|
+
f'/plugin/{self._team_id}/listing/analysis/{analysis_id}',
|
|
205
|
+
params={'page': page, 'limit': limit}
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return response.get('data')
|
|
209
|
+
|
|
210
|
+
def find_analysis_by_id(self, analysis_id: str):
|
|
211
|
+
return self._request(
|
|
212
|
+
'GET',
|
|
213
|
+
f'/analysis/{self._team_id}/{analysis_id}'
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _resolve_trajectory_id(self, analysis_id: str) -> str:
|
|
217
|
+
analysis = self.find_analysis_by_id(analysis_id)
|
|
218
|
+
trajectory_id = analysis.get('trajectory')
|
|
219
|
+
if not trajectory_id:
|
|
220
|
+
raise RuntimeError('analysis trajectory not found')
|
|
221
|
+
|
|
222
|
+
return trajectory_id
|
|
223
|
+
|
|
224
|
+
def download_analysis_artifacts(
|
|
225
|
+
self,
|
|
226
|
+
analysis_id: str,
|
|
227
|
+
unzip: bool = True
|
|
228
|
+
):
|
|
229
|
+
zip_path = self._download_stream(
|
|
230
|
+
f'/plugin/{self._team_id}/exposure/export/analysis/{analysis_id}',
|
|
231
|
+
f'analysis-{analysis_id}-artifacts.zip'
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if unzip:
|
|
235
|
+
return self._unzip_recursive(zip_path)
|
|
236
|
+
|
|
237
|
+
return zip_path
|
|
238
|
+
|
|
239
|
+
def download_plugin_results_file(
|
|
240
|
+
self,
|
|
241
|
+
analysis_id: str,
|
|
242
|
+
unzip: bool = True
|
|
243
|
+
):
|
|
244
|
+
return self.download_analysis_artifacts(analysis_id, unzip=unzip)
|
|
245
|
+
|
|
246
|
+
def download_frame_glb(
|
|
247
|
+
self,
|
|
248
|
+
analysis_id: str,
|
|
249
|
+
timestep: int
|
|
250
|
+
):
|
|
251
|
+
trajectory_id = self._resolve_trajectory_id(analysis_id)
|
|
252
|
+
|
|
253
|
+
return self._download_stream(
|
|
254
|
+
f'/trajectory/{self._team_id}/{trajectory_id}/{timestep}/{analysis_id}',
|
|
255
|
+
f'frame-{analysis_id}-{timestep}.glb'
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def download_plugin_exported_glb(
|
|
259
|
+
self,
|
|
260
|
+
analysis_id: str,
|
|
261
|
+
exposure_id: str,
|
|
262
|
+
timestep: int
|
|
263
|
+
):
|
|
264
|
+
trajectory_id = self._resolve_trajectory_id(analysis_id)
|
|
265
|
+
|
|
266
|
+
return self._download_stream(
|
|
267
|
+
f'/plugin/{self._team_id}/exposure/glb/{trajectory_id}/{analysis_id}/{exposure_id}/{timestep}',
|
|
268
|
+
f'plugin-glb-{analysis_id}-{exposure_id}-{timestep}.glb'
|
|
269
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "voltsdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Volt API SDK for scripting and notebooks"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"msgpack>=1.0.0",
|
|
13
|
+
"pandas>=2.0.0",
|
|
14
|
+
"requests>=2.31.0"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
visualization = [
|
|
19
|
+
"vtk>=9.2.0"
|
|
20
|
+
]
|
|
21
|
+
notebook = [
|
|
22
|
+
"vtk>=9.2.0",
|
|
23
|
+
"k3d>=2.16.0"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
packages = ["voltsdk"]
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.package-dir]
|
|
30
|
+
voltsdk = "."
|
voltsdk-0.1.0/setup.cfg
ADDED
voltsdk-0.1.0/utils.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
import msgpack
|
|
3
|
+
import os
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_nested_value(data: Any, path: Optional[str]) -> Any:
|
|
8
|
+
if not path:
|
|
9
|
+
return data
|
|
10
|
+
|
|
11
|
+
current = data
|
|
12
|
+
for key in path.split('.'):
|
|
13
|
+
if not isinstance(current, dict):
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
if key not in current:
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
current = current[key]
|
|
20
|
+
|
|
21
|
+
return current
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def merged_chunked_value(target: Any, incoming: Any) -> Any:
|
|
25
|
+
if incoming is None:
|
|
26
|
+
return target
|
|
27
|
+
|
|
28
|
+
if target is None:
|
|
29
|
+
return incoming
|
|
30
|
+
|
|
31
|
+
if isinstance(target, list) and isinstance(incoming, list):
|
|
32
|
+
target.extend(incoming)
|
|
33
|
+
return target
|
|
34
|
+
|
|
35
|
+
if isinstance(target, dict) and isinstance(incoming, dict):
|
|
36
|
+
for key, incoming_value in incoming.items():
|
|
37
|
+
target_value = target.get(key)
|
|
38
|
+
|
|
39
|
+
if isinstance(target_value, list) and isinstance(incoming_value, list):
|
|
40
|
+
target_value.extend(incoming_value)
|
|
41
|
+
elif isinstance(target_value, dict) and isinstance(incoming_value, dict):
|
|
42
|
+
target[key] = merged_chunked_value(target_value, incoming_value)
|
|
43
|
+
else:
|
|
44
|
+
target[key] = incoming_value
|
|
45
|
+
return target
|
|
46
|
+
|
|
47
|
+
return incoming
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def is_columnar_dict(value: Any) -> bool:
|
|
51
|
+
if not isinstance(value, dict) or not value:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
lengths = []
|
|
55
|
+
for item in value.values():
|
|
56
|
+
if not isinstance(item, list):
|
|
57
|
+
return False
|
|
58
|
+
lengths.append(len(item))
|
|
59
|
+
return len(set(lengths)) == 1
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def msgpack_as_df(file_path: str, iterable_key: Optional[str] = None):
|
|
63
|
+
if not os.path.exists(file_path):
|
|
64
|
+
raise RuntimeError(f'file not found: {file_path}')
|
|
65
|
+
|
|
66
|
+
data = None
|
|
67
|
+
with open(file_path, 'rb') as file:
|
|
68
|
+
unpacker = msgpack.Unpacker(file, raw=False)
|
|
69
|
+
for message in unpacker:
|
|
70
|
+
chunk = get_nested_value(message, iterable_key)
|
|
71
|
+
data = merged_chunked_value(data, chunk)
|
|
72
|
+
|
|
73
|
+
if data is None:
|
|
74
|
+
return pd.DataFrame()
|
|
75
|
+
|
|
76
|
+
if isinstance(data, list) or is_columnar_dict(data):
|
|
77
|
+
return pd.DataFrame(data)
|
|
78
|
+
|
|
79
|
+
if isinstance(data, dict):
|
|
80
|
+
return pd.DataFrame([data])
|
|
81
|
+
|
|
82
|
+
return pd.DataFrame([{'value': data}])
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def view_glb(file_path: str):
|
|
86
|
+
if not os.path.exists(file_path):
|
|
87
|
+
raise RuntimeError(f'file not found: {file_path}')
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
import vtk
|
|
91
|
+
except Exception as error:
|
|
92
|
+
raise RuntimeError(
|
|
93
|
+
'vtk is required to view GLB files. Install with: pip install "voltsdk[visualization]"'
|
|
94
|
+
) from error
|
|
95
|
+
|
|
96
|
+
reader = vtk.vtkGLTFReader()
|
|
97
|
+
reader.SetFileName(file_path)
|
|
98
|
+
reader.Update()
|
|
99
|
+
|
|
100
|
+
in_notebook = False
|
|
101
|
+
try:
|
|
102
|
+
from IPython import get_ipython
|
|
103
|
+
shell = get_ipython()
|
|
104
|
+
in_notebook = shell is not None and shell.__class__.__name__ == 'ZMQInteractiveShell'
|
|
105
|
+
except Exception:
|
|
106
|
+
in_notebook = False
|
|
107
|
+
|
|
108
|
+
if in_notebook:
|
|
109
|
+
try:
|
|
110
|
+
import k3d
|
|
111
|
+
except Exception as error:
|
|
112
|
+
raise RuntimeError('k3d is required in notebook mode. Install with: pip install k3d') from error
|
|
113
|
+
|
|
114
|
+
plot = k3d.plot()
|
|
115
|
+
multi_block = reader.GetOutput()
|
|
116
|
+
iterator = multi_block.NewIterator()
|
|
117
|
+
iterator.InitTraversal()
|
|
118
|
+
while not iterator.IsDoneWithTraversal():
|
|
119
|
+
item = iterator.GetCurrentDataObject()
|
|
120
|
+
if item is not None:
|
|
121
|
+
plot += k3d.vtk_poly_data(item, color=0x222222)
|
|
122
|
+
iterator.GoToNextItem()
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
from IPython.display import display as ipython_display
|
|
126
|
+
ipython_display(plot)
|
|
127
|
+
except Exception:
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
return plot
|
|
131
|
+
|
|
132
|
+
mapper = vtk.vtkCompositePolyDataMapper2()
|
|
133
|
+
mapper.SetInputDataObject(reader.GetOutput())
|
|
134
|
+
|
|
135
|
+
actor = vtk.vtkActor()
|
|
136
|
+
actor.SetMapper(mapper)
|
|
137
|
+
|
|
138
|
+
renderer = vtk.vtkRenderer()
|
|
139
|
+
renderer.AddActor(actor)
|
|
140
|
+
renderer.SetBackground(0.07, 0.07, 0.07)
|
|
141
|
+
|
|
142
|
+
render_window = vtk.vtkRenderWindow()
|
|
143
|
+
render_window.AddRenderer(renderer)
|
|
144
|
+
render_window.SetSize(1200, 800)
|
|
145
|
+
render_window.SetWindowName(os.path.basename(file_path))
|
|
146
|
+
|
|
147
|
+
interactor = vtk.vtkRenderWindowInteractor()
|
|
148
|
+
interactor.SetRenderWindow(render_window)
|
|
149
|
+
|
|
150
|
+
render_window.Render()
|
|
151
|
+
interactor.Initialize()
|
|
152
|
+
interactor.Start()
|
|
153
|
+
|
|
154
|
+
return interactor
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: voltsdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Volt API SDK for scripting and notebooks
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: msgpack>=1.0.0
|
|
8
|
+
Requires-Dist: pandas>=2.0.0
|
|
9
|
+
Requires-Dist: requests>=2.31.0
|
|
10
|
+
Provides-Extra: visualization
|
|
11
|
+
Requires-Dist: vtk>=9.2.0; extra == "visualization"
|
|
12
|
+
Provides-Extra: notebook
|
|
13
|
+
Requires-Dist: vtk>=9.2.0; extra == "notebook"
|
|
14
|
+
Requires-Dist: k3d>=2.16.0; extra == "notebook"
|
|
15
|
+
|
|
16
|
+
# voltsdk
|
|
17
|
+
|
|
18
|
+
Python SDK for interacting with the Volt API.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install voltsdk
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Install visualization support with:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install "voltsdk[visualization]"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Install notebook support with:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install "voltsdk[notebook]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from voltsdk import VoltClient
|
|
42
|
+
|
|
43
|
+
client = VoltClient(
|
|
44
|
+
secret_key="your-secret-key",
|
|
45
|
+
base_url="https://api.example.com"
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
__init__.py
|
|
3
|
+
client.py
|
|
4
|
+
pyproject.toml
|
|
5
|
+
utils.py
|
|
6
|
+
./__init__.py
|
|
7
|
+
./client.py
|
|
8
|
+
./utils.py
|
|
9
|
+
voltsdk.egg-info/PKG-INFO
|
|
10
|
+
voltsdk.egg-info/SOURCES.txt
|
|
11
|
+
voltsdk.egg-info/dependency_links.txt
|
|
12
|
+
voltsdk.egg-info/requires.txt
|
|
13
|
+
voltsdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
voltsdk
|