Source code for ionworks.models
"""Pydantic models for the Ionworks API client.
These models use extra="allow" to accept any fields from the API response,
letting the API handle validation. Required fields are kept minimal.
"""
from typing import Any
from pydantic import BaseModel, ConfigDict, field_validator
from .validators import DataFrame, dict_to_df_validator
# --- Cell Specification Models --- #
[docs]
class CellSpecification(BaseModel):
"""Cell specification model - accepts any fields from the API.
The API returns nested component/material data and ratings objects.
This model is permissive to allow the API to define the schema.
"""
model_config = ConfigDict(extra="allow")
id: str
name: str
# --- Cell Instance Models --- #
[docs]
class CellInstance(BaseModel):
"""Cell instance model - accepts any fields from the API."""
model_config = ConfigDict(extra="allow")
id: str
name: str
cell_specification_id: str
# --- Cell Measurement Models --- #
[docs]
class CellMeasurement(BaseModel):
"""Cell measurement model - accepts any fields from the API."""
model_config = ConfigDict(extra="allow")
id: str
name: str
cell_instance_id: str
# --- Bundle Models --- #
[docs]
class CellMeasurementBundleResponse(BaseModel):
"""Response from creating a measurement bundle."""
measurement: CellMeasurement
steps_created: int
file_path: str
[docs]
class InitiateUploadResponse(BaseModel):
"""Response from the initiate-upload endpoint for signed URL uploads."""
measurement_id: str
signed_url: str
token: str
path: str
[docs]
class ConfirmUploadResponse(BaseModel):
"""Response from the confirm-upload endpoint after successful upload."""
instance: CellInstance
measurement: CellMeasurement
steps_created: int
file_path: str
# --- Detail Models --- #
[docs]
class CellMeasurementDetail(BaseModel):
"""Detail model for a measurement with steps and time series.
Returns minimal data by default: foreign keys for parent objects
rather than nested objects. Use the spec/instance clients to
fetch parent objects if needed.
"""
model_config = ConfigDict(arbitrary_types_allowed=True)
measurement: CellMeasurement
specification_id: str | None = None
instance_id: str | None = None
steps: DataFrame | None = None
time_series: DataFrame | None = None
cycles: DataFrame | None = None
[docs]
@field_validator("steps", "time_series", "cycles", mode="before")
@classmethod
def convert_dict_to_df(cls, v: Any) -> Any:
"""Convert dictionary to DataFrame (polars or pandas based on config)."""
return dict_to_df_validator(v)
[docs]
class CellInstanceDetail(BaseModel):
"""Detail model for a cell instance with all measurements.
Returns a foreign key for the parent specification rather
than a nested object. Use ``client.cell_spec.get(detail
.specification_id)`` to fetch the full specification.
"""
instance: CellInstance
specification_id: str
measurements: list[CellMeasurementDetail]
[docs]
class StepsAndCycles(BaseModel):
"""Steps and cycle metrics for a measurement.
Returned by the ``/steps_and_cycles`` endpoint which
fetches both in one call (cycles are derived from steps).
"""
model_config = ConfigDict(arbitrary_types_allowed=True)
steps: DataFrame
cycles: DataFrame
[docs]
@field_validator("steps", "cycles", mode="before")
@classmethod
def convert_dict_to_df(cls, v: Any) -> Any:
"""Convert dictionary to DataFrame."""
return dict_to_df_validator(v)