ionworks-api 0.1.0__tar.gz → 0.1.3__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.
@@ -2,9 +2,15 @@
2
2
  .pytest_cache
3
3
  .venv
4
4
  .env*
5
+ !.env.example
5
6
  .ruff_cache
6
7
  .vscode
7
8
  __pycache__
8
9
  .DS_Store
9
10
  .idea
10
11
  .local
12
+ uv.lock
13
+
14
+ # Docs
15
+ docs/_build/
16
+ docs/.jupyter_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ionworks Technologies Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ionworks-api
3
- Version: 0.1.0
3
+ Version: 0.1.3
4
4
  Summary: Python client for interacting with the Ionworks API
5
+ License-File: LICENSE.md
5
6
  Requires-Python: >=3.10
6
7
  Requires-Dist: black
7
8
  Requires-Dist: iwutil
8
9
  Requires-Dist: numpy
9
10
  Requires-Dist: pandas
10
- Requires-Dist: polars
11
+ Requires-Dist: polars-lts-cpu
11
12
  Requires-Dist: pyarrow
12
13
  Requires-Dist: pybamm>=25.10.0
13
14
  Requires-Dist: pydantic>=2.6.0
@@ -17,6 +18,10 @@ Requires-Dist: supabase
17
18
  Requires-Dist: types-requests>=2.31.0
18
19
  Provides-Extra: dev
19
20
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
21
+ Provides-Extra: docs
22
+ Requires-Dist: furo; extra == 'docs'
23
+ Requires-Dist: myst-nb>=1.0.0; extra == 'docs'
24
+ Requires-Dist: sphinx>=7.0.0; extra == 'docs'
20
25
  Description-Content-Type: text/markdown
21
26
 
22
27
  # Ionworks API Client
@@ -34,7 +39,13 @@ A Python client for interacting with the Ionworks API.
34
39
  pip install -e .
35
40
  ```
36
41
 
37
- 3. Get your API key from the [Ionworks account settings](https://app.ionworks.com/dashboard/account) and set it as the `IONWORKS_API_KEY` environment variable (or in a `.env` file).
42
+ 3. Copy the example environment file and fill in your credentials:
43
+
44
+ ```bash
45
+ cp .env.example .env
46
+ ```
47
+
48
+ Get your API key from the [Ionworks account settings](https://app.ionworks.com/dashboard/account) and your project ID from your project settings page. See [Environment Setup](#environment-setup) for details.
38
49
 
39
50
  ## Usage
40
51
 
@@ -185,7 +196,6 @@ Available element types:
185
196
  - `validation`: Validate model against data
186
197
 
187
198
  ```python
188
- import time
189
199
  from ionworks import Ionworks
190
200
 
191
201
  client = Ionworks()
@@ -236,22 +246,15 @@ pipeline_config = {
236
246
  },
237
247
  }
238
248
 
239
- # Submit pipeline
249
+ # Submit pipeline and wait for completion
240
250
  pipeline = client.pipeline.create(pipeline_config)
241
251
  print(f"Pipeline submitted: {pipeline.id}")
242
252
 
243
- # Poll for completion
244
- while True:
245
- pipeline = client.pipeline.get(pipeline.id)
246
- print(f"Status: {pipeline.status}")
247
- if pipeline.status == "completed":
248
- result = client.pipeline.result(pipeline.id)
249
- print("Fitted parameters:", result.element_results["fit data"])
250
- break
251
- elif pipeline.status == "failed":
252
- print("Pipeline failed:", pipeline.error)
253
- break
254
- time.sleep(2)
253
+ pipeline = client.pipeline.wait_for_completion(pipeline.id, timeout=600)
254
+
255
+ if pipeline.status == "completed":
256
+ result = client.pipeline.result(pipeline.id)
257
+ print("Fitted parameters:", result.element_results["fit data"])
255
258
  ```
256
259
 
257
260
  ### Running simulations
@@ -307,6 +310,22 @@ simulation_data = client.simulation.get_result(result.simulation_id)
307
310
  time_series = simulation_data.get("time_series", {})
308
311
  ```
309
312
 
313
+ ## Environment Setup
314
+
315
+ The client uses environment variables for configuration. Copy the example file and fill in your values:
316
+
317
+ ```bash
318
+ cp .env.example .env
319
+ ```
320
+
321
+ | Variable | Required | Default | Description |
322
+ |---|---|---|---|
323
+ | `IONWORKS_API_KEY` | Yes | — | API key from [account settings](https://app.ionworks.com/dashboard/account) |
324
+ | `IONWORKS_API_URL` | No | `https://api.ionworks.com` | API base URL |
325
+ | `PROJECT_ID` | For pipelines | — | Project ID from your project settings page |
326
+
327
+ The client loads `.env` automatically via `python-dotenv`.
328
+
310
329
  ## Error Handling
311
330
 
312
331
  The client will raise exceptions in the following cases:
@@ -13,7 +13,13 @@ A Python client for interacting with the Ionworks API.
13
13
  pip install -e .
14
14
  ```
15
15
 
16
- 3. Get your API key from the [Ionworks account settings](https://app.ionworks.com/dashboard/account) and set it as the `IONWORKS_API_KEY` environment variable (or in a `.env` file).
16
+ 3. Copy the example environment file and fill in your credentials:
17
+
18
+ ```bash
19
+ cp .env.example .env
20
+ ```
21
+
22
+ Get your API key from the [Ionworks account settings](https://app.ionworks.com/dashboard/account) and your project ID from your project settings page. See [Environment Setup](#environment-setup) for details.
17
23
 
18
24
  ## Usage
19
25
 
@@ -164,7 +170,6 @@ Available element types:
164
170
  - `validation`: Validate model against data
165
171
 
166
172
  ```python
167
- import time
168
173
  from ionworks import Ionworks
169
174
 
170
175
  client = Ionworks()
@@ -215,22 +220,15 @@ pipeline_config = {
215
220
  },
216
221
  }
217
222
 
218
- # Submit pipeline
223
+ # Submit pipeline and wait for completion
219
224
  pipeline = client.pipeline.create(pipeline_config)
220
225
  print(f"Pipeline submitted: {pipeline.id}")
221
226
 
222
- # Poll for completion
223
- while True:
224
- pipeline = client.pipeline.get(pipeline.id)
225
- print(f"Status: {pipeline.status}")
226
- if pipeline.status == "completed":
227
- result = client.pipeline.result(pipeline.id)
228
- print("Fitted parameters:", result.element_results["fit data"])
229
- break
230
- elif pipeline.status == "failed":
231
- print("Pipeline failed:", pipeline.error)
232
- break
233
- time.sleep(2)
227
+ pipeline = client.pipeline.wait_for_completion(pipeline.id, timeout=600)
228
+
229
+ if pipeline.status == "completed":
230
+ result = client.pipeline.result(pipeline.id)
231
+ print("Fitted parameters:", result.element_results["fit data"])
234
232
  ```
235
233
 
236
234
  ### Running simulations
@@ -286,6 +284,22 @@ simulation_data = client.simulation.get_result(result.simulation_id)
286
284
  time_series = simulation_data.get("time_series", {})
287
285
  ```
288
286
 
287
+ ## Environment Setup
288
+
289
+ The client uses environment variables for configuration. Copy the example file and fill in your values:
290
+
291
+ ```bash
292
+ cp .env.example .env
293
+ ```
294
+
295
+ | Variable | Required | Default | Description |
296
+ |---|---|---|---|
297
+ | `IONWORKS_API_KEY` | Yes | — | API key from [account settings](https://app.ionworks.com/dashboard/account) |
298
+ | `IONWORKS_API_URL` | No | `https://api.ionworks.com` | API base URL |
299
+ | `PROJECT_ID` | For pipelines | — | Project ID from your project settings page |
300
+
301
+ The client loads `.env` automatically via `python-dotenv`.
302
+
289
303
  ## Error Handling
290
304
 
291
305
  The client will raise exceptions in the following cases:
@@ -0,0 +1,28 @@
1
+ """
2
+ Ionworks API Client.
3
+
4
+ A Python client for interacting with the Ionworks platform for battery cell
5
+ testing, simulation, and modeling.
6
+
7
+ Example:
8
+ -------
9
+ >>> from ionworks import Ionworks
10
+ >>> client = Ionworks()
11
+ >>> specs = client.cell_spec.list()
12
+ """
13
+
14
+ from .client import Ionworks
15
+ from .errors import IonworksError
16
+ from .validators import (
17
+ MeasurementValidationError,
18
+ get_dataframe_backend,
19
+ set_dataframe_backend,
20
+ )
21
+
22
+ __all__ = [
23
+ "Ionworks",
24
+ "IonworksError",
25
+ "MeasurementValidationError",
26
+ "get_dataframe_backend",
27
+ "set_dataframe_backend",
28
+ ]
@@ -1,3 +1,11 @@
1
+ """
2
+ Cell instance client for managing individual cell records.
3
+
4
+ This module provides the :class:`CellInstanceClient` for creating, reading,
5
+ updating, and deleting cell instances, which represent individual physical
6
+ battery cells linked to a cell specification.
7
+ """
8
+
1
9
  from typing import Any
2
10
 
3
11
  from pydantic import ValidationError
@@ -12,13 +20,20 @@ from .models import (
12
20
 
13
21
 
14
22
  class CellInstanceClient:
15
- def __init__(self, client: Any):
23
+ """Client for managing cell instances."""
24
+
25
+ def __init__(self, client: Any) -> None:
26
+ """Initialize the CellInstanceClient.
27
+
28
+ Parameters
29
+ ----------
30
+ client : Any
31
+ The HTTP client instance used for API calls.
32
+ """
16
33
  self.client = client
17
34
 
18
35
  def get(self, cell_instance_id: str) -> CellInstance:
19
- """
20
- Get a specific cell instance by ID.
21
- """
36
+ """Get a specific cell instance by ID."""
22
37
  endpoint = f"/cell_instances/{cell_instance_id}"
23
38
  try:
24
39
  response_data = self.client.get(endpoint)
@@ -30,9 +45,7 @@ class CellInstanceClient:
30
45
  raise RuntimeError(f"API call to {endpoint} failed: {e}") from e
31
46
 
32
47
  def get_by_slug(self, cell_instance_slug: str) -> CellInstance:
33
- """
34
- Get a specific cell instance by slug.
35
- """
48
+ """Get a specific cell instance by slug."""
36
49
  endpoint = f"/cell_instances/slug/{cell_instance_slug}"
37
50
  try:
38
51
  response_data = self.client.get(endpoint)
@@ -43,9 +56,7 @@ class CellInstanceClient:
43
56
  raise RuntimeError(f"API call to {endpoint} failed: {e}") from e
44
57
 
45
58
  def list(self, cell_spec_id: str) -> list[CellInstance]:
46
- """
47
- List all cell instances for a cell specification by specification ID.
48
- """
59
+ """List all cell instances for a cell specification by specification ID."""
49
60
  endpoint = f"/cell_specifications/{cell_spec_id}/cell_instances"
50
61
  try:
51
62
  response_data = self.client.get(endpoint)
@@ -74,14 +85,13 @@ class CellInstanceClient:
74
85
  # return [CellMeasurementDetailBase(**item) for item in response_data]
75
86
 
76
87
  def update(self, cell_instance_id: str, data: dict[str, Any]) -> CellInstance:
77
- """
78
- Update an existing cell instance.
88
+ """Update an existing cell instance.
79
89
 
80
90
  Parameters
81
91
  ----------
82
92
  cell_instance_id : str
83
93
  The ID of the cell instance to update.
84
- data : dict
94
+ data : dict[str, Any]
85
95
  Dictionary containing the fields to update.
86
96
 
87
97
  Returns
@@ -94,30 +104,36 @@ class CellInstanceClient:
94
104
  return CellInstance(**response_data)
95
105
 
96
106
  def delete(self, cell_instance_id: str) -> None:
97
- """
98
- Delete a cell instance by ID.
99
- """
107
+ """Delete a cell instance by ID."""
100
108
  endpoint = f"/cell_instances/{cell_instance_id}"
101
109
  self.client.delete(endpoint)
102
110
 
103
111
  # Renamed and moved method from Data class
104
112
  def detail(self, cell_instance_id: str) -> CellInstanceDetail:
105
- """
106
- Loads the full details for a cell instance from a single API endpoint,
107
- including its spec, instance data, steps DataFrame, and time series DataFrame.
108
- Uses the /detail endpoint.
113
+ """Load the full details for a cell instance.
109
114
 
110
- Args:
111
- organization: The organization slug.
112
- cell_instance_id: The cell instance ID.
115
+ Loads the full details for a cell instance from a single API endpoint,
116
+ including its spec, instance data, steps DataFrame, and time series
117
+ DataFrame. Uses the /detail endpoint.
113
118
 
114
- Returns:
115
- A FullCellInstance object containing validated data.
119
+ Parameters
120
+ ----------
121
+ cell_instance_id : str
122
+ The cell instance ID.
116
123
 
117
- Raises:
118
- RuntimeError: If the API call fails.
119
- ValueError: If the API response format is unexpected or parsing fails.
120
- ValidationError: If the response data doesn't match expected models.
124
+ Returns
125
+ -------
126
+ CellInstanceDetail
127
+ Object containing validated cell instance data.
128
+
129
+ Raises
130
+ ------
131
+ RuntimeError
132
+ If the API call fails.
133
+ ValueError
134
+ If the API response format is unexpected or parsing fails.
135
+ ValidationError
136
+ If the response data doesn't match expected models.
121
137
  """
122
138
  endpoint = f"/cell_instances/{cell_instance_id}/detail"
123
139
  try:
@@ -127,9 +143,9 @@ class CellInstanceClient:
127
143
  except ValidationError as e:
128
144
  # Pydantic validation error during parsing
129
145
  raise ValueError(f"Response validation failed for {endpoint}: {e}") from e
130
- except ValueError as e:
146
+ except ValueError:
131
147
  # Re-raise our explicit format validation errors
132
- raise e
148
+ raise
133
149
  except Exception as e:
134
150
  # Catch client errors or any other unexpected issues
135
151
  raise RuntimeError(
@@ -137,19 +153,13 @@ class CellInstanceClient:
137
153
  ) from e
138
154
 
139
155
  def create(self, cell_spec_id: str, data: dict[str, Any]) -> CellInstance:
140
- """
141
- Create a new cell instance under a cell specification by
142
- specification ID.
143
- """
156
+ """Create a new cell instance under a cell specification by specification ID."""
144
157
  endpoint = f"/cell_specifications/{cell_spec_id}/cell_instances"
145
158
  response_data = self.client.post(endpoint, data)
146
159
  return CellInstance(**response_data)
147
160
 
148
161
  def create_or_get(self, cell_spec_id: str, data: dict[str, Any]) -> CellInstance:
149
- """
150
- Create a new cell instance if it doesn't exist, otherwise get the
151
- existing one.
152
- """
162
+ """Create a new cell instance if it doesn't exist, otherwise get it."""
153
163
  try:
154
164
  return self.create(cell_spec_id, data)
155
165
  except IonworksError as e:
@@ -157,11 +167,11 @@ class CellInstanceClient:
157
167
  existing_id = e.data.get("existing_cell_instance_id")
158
168
  if existing_id:
159
169
  return self.get(existing_id)
160
- raise e
170
+ raise
161
171
 
162
172
  def list_with_measurements(self, cell_spec_id: str) -> CellSpecificationInstances:
163
- """
164
- List all instances and measurements for a cell specification.
173
+ """List all instances and measurements for a cell specification.
174
+
165
175
  Returns normalized data: separate lists for specification, instances, and
166
176
  measurements.
167
177
  """
@@ -179,8 +189,8 @@ class CellInstanceClient:
179
189
  def list_with_measurements_and_steps(
180
190
  self, cell_spec_id: str
181
191
  ) -> CellSpecificationInstancesWithSteps:
182
- """
183
- List all instances, measurements, and steps for a cell specification.
192
+ """List all instances, measurements, and steps for a cell specification.
193
+
184
194
  Returns normalized data: separate lists for specification, instances,
185
195
  measurements, and steps.
186
196
  """
@@ -195,7 +205,3 @@ class CellInstanceClient:
195
205
  raise ValueError(f"Response validation failed for {endpoint}: {e}") from e
196
206
  except Exception as e:
197
207
  raise RuntimeError(f"API call to {endpoint} failed: {e}") from e
198
-
199
-
200
- class CellMeasurementClient:
201
- pass