Source code for ionworks.cell_specification

"""Cell specification client for managing cell type definitions.

This module provides the :class:`CellSpecificationClient` for creating,
reading, updating, and deleting cell specifications, which define the
properties of battery cell types (manufacturer, chemistry, ratings, etc.).
"""

from __future__ import annotations

from typing import Any
import warnings

from ionworks.errors import IonworksError

from .models import CellSpecification


[docs] class CellSpecificationClient: """Client for managing cell specifications. Provides methods to create, read, update, and delete cell specifications, which define the properties of battery cell types (manufacturer, chemistry, ratings, etc.). """
[docs] def __init__(self, client: Any) -> None: """Initialize the CellSpecificationClient. Parameters ---------- client : Any The HTTP client instance for making API requests. """ self.client = client
[docs] def get(self, cell_spec_id: str) -> CellSpecification: """Get a specific cell specification by ID. Parameters ---------- cell_spec_id : str The ID of the cell specification to retrieve. Returns ------- CellSpecification The requested cell specification object. """ endpoint = f"/cell_specifications/{cell_spec_id}" response_data = self.client.get(endpoint) return CellSpecification(**response_data)
[docs] def list( self, include_components: bool = False, ) -> list[CellSpecification]: """List all cell specifications. Parameters ---------- include_components : bool, optional If True, returns each specification with its nested component and material data. Defaults to False (metadata only). Returns ------- list[CellSpecification] A list of all cell specification objects. """ endpoint = "/cell_specifications" if include_components: endpoint += "?full=true" response_data = self.client.get(endpoint) return [CellSpecification(**item) for item in response_data]
[docs] def create(self, data: dict[str, Any]) -> CellSpecification: """Create a new cell specification. Parameters ---------- data : dict[str, Any] Dictionary containing the cell specification data. Returns ------- CellSpecification The newly created cell specification object. """ endpoint = "/cell_specifications" response_data = self.client.post(endpoint, data) return CellSpecification(**response_data)
[docs] def create_or_get(self, data: dict[str, Any]) -> CellSpecification: """Create a new cell specification or get an existing one. Creates a new cell specification if it doesn't exist, otherwise returns the existing one. Parameters ---------- data : dict[str, Any] Dictionary containing the cell specification data. Returns ------- CellSpecification The cell specification object (newly created or existing). """ try: return self.create(data) except IonworksError as e: if e.error_code == "CONFLICT" or e.status_code == 409: # Try to get existing spec by ID from error detail if e.data is not None: detail = e.data.get("detail", {}) existing_id = ( detail.get("existing_id") if isinstance(detail, dict) else None ) if existing_id: return self.get(existing_id) # Deprecated: legacy error format fallback legacy_id = e.data.get("existing_cell_specification_id") if legacy_id: warnings.warn( "Received legacy error key " "'existing_cell_specification_id'. " "Update the backend to use the " "standardized error format.", DeprecationWarning, stacklevel=2, ) return self.get(legacy_id) # Fall back to listing and matching by name spec_name = data.get("name") if spec_name: for spec in self.list(): if spec.name == spec_name: return spec raise ValueError( f"Cell specification '{spec_name}' reported as " "duplicate but could not be found" ) from e raise
[docs] def update(self, cell_spec_id: str, data: dict[str, Any]) -> CellSpecification: """Update an existing cell specification. Parameters ---------- cell_spec_id : str The ID of the cell specification to update. data : dict[str, Any] Dictionary containing the fields to update. Supports nested component/material data for upsert. Returns ------- CellSpecification The updated cell specification object. """ endpoint = f"/cell_specifications/{cell_spec_id}" response_data = self.client.put(endpoint, data) return CellSpecification(**response_data)
[docs] def delete(self, cell_spec_id: str) -> None: """Delete a cell specification by ID. Parameters ---------- cell_spec_id : str The ID of the cell specification to delete. """ endpoint = f"/cell_specifications/{cell_spec_id}" self.client.delete(endpoint)