From 9e087cae2e898ced6e5f7b77e15383b1c7b4abcd Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Fri, 28 Feb 2025 01:59:32 +0100 Subject: [PATCH 01/18] add support for simulation runs --- cognite/client/_api/simulators/__init__.py | 2 + cognite/client/_api/simulators/runs.py | 217 ++++++++++++++++++ .../data_classes/simulators/__init__.py | 10 + .../client/data_classes/simulators/filters.py | 38 +++ .../client/data_classes/simulators/runs.py | 183 +++++++++++++++ cognite/client/testing.py | 2 + 6 files changed, 452 insertions(+) create mode 100644 cognite/client/_api/simulators/runs.py diff --git a/cognite/client/_api/simulators/__init__.py b/cognite/client/_api/simulators/__init__.py index 849fe088b1..0d89294505 100644 --- a/cognite/client/_api/simulators/__init__.py +++ b/cognite/client/_api/simulators/__init__.py @@ -5,6 +5,7 @@ from cognite.client._api.simulators.integrations import SimulatorIntegrationsAPI from cognite.client._api.simulators.models import SimulatorModelsAPI +from cognite.client._api.simulators.runs import SimulatorRunsAPI from cognite.client._api_client import APIClient from cognite.client._constants import DEFAULT_LIMIT_READ from cognite.client.data_classes.simulators.simulators import Simulator, SimulatorList @@ -22,6 +23,7 @@ def __init__(self, config: ClientConfig, api_version: str | None, cognite_client super().__init__(config, api_version, cognite_client) self.integrations = SimulatorIntegrationsAPI(config, api_version, cognite_client) self.models = SimulatorModelsAPI(config, api_version, cognite_client) + self.runs = SimulatorRunsAPI(config, api_version, cognite_client) self._warning = FeaturePreviewWarning( api_maturity="General Availability", sdk_maturity="alpha", feature_name="Simulators" ) diff --git a/cognite/client/_api/simulators/runs.py b/cognite/client/_api/simulators/runs.py new file mode 100644 index 0000000000..549d126d52 --- /dev/null +++ b/cognite/client/_api/simulators/runs.py @@ -0,0 +1,217 @@ +from __future__ import annotations + +from collections.abc import Iterator, Sequence +from typing import TYPE_CHECKING, overload + +from cognite.client._api_client import APIClient +from cognite.client._constants import DEFAULT_LIMIT_READ +from cognite.client.data_classes.simulators.filters import SimulatorRunsFilter +from cognite.client.data_classes.simulators.runs import SimulationRun, SimulationRunWrite, SimulatorRunsList +from cognite.client.utils._experimental import FeaturePreviewWarning +from cognite.client.utils._validation import assert_type + +if TYPE_CHECKING: + from cognite.client import CogniteClient + from cognite.client.config import ClientConfig + + +class SimulatorRunsAPI(APIClient): + _RESOURCE_PATH = "/simulators/runs" + _RESOURCE_PATH_RUN = "/simulators/run" + + def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: CogniteClient) -> None: + super().__init__(config, api_version, cognite_client) + self._warning = FeaturePreviewWarning( + api_maturity="General Availability", sdk_maturity="alpha", feature_name="Simulators" + ) + + def __iter__(self) -> Iterator[SimulationRun]: + """Iterate over simulation runs + + Fetches simulation runs as they are iterated over, so you keep a limited number of simulation runs in memory. + + Returns: + Iterator[SimulationRun]: yields simulation runs one by one. + """ + return self() + + @overload + def __call__( + self, + chunk_size: int, + limit: int | None = None, + status: str | None = None, + run_type: str | None = None, + model_external_ids: Sequence[str] | None = None, + simulator_integration_external_ids: Sequence[str] | None = None, + simulator_external_ids: Sequence[str] | None = None, + routine_external_ids: Sequence[str] | None = None, + routine_revision_external_ids: Sequence[str] | None = None, + model_revision_external_ids: Sequence[str] | None = None, + ) -> Iterator[SimulationRun]: ... + + @overload + def __call__( + self, + chunk_size: None = None, + limit: int | None = None, + status: str | None = None, + run_type: str | None = None, + model_external_ids: Sequence[str] | None = None, + simulator_integration_external_ids: Sequence[str] | None = None, + simulator_external_ids: Sequence[str] | None = None, + routine_external_ids: Sequence[str] | None = None, + routine_revision_external_ids: Sequence[str] | None = None, + model_revision_external_ids: Sequence[str] | None = None, + ) -> Iterator[SimulationRun]: ... + + def __call__( + self, + chunk_size: int | None = None, + limit: int | None = None, + status: str | None = None, + run_type: str | None = None, + model_external_ids: Sequence[str] | None = None, + simulator_integration_external_ids: Sequence[str] | None = None, + simulator_external_ids: Sequence[str] | None = None, + routine_external_ids: Sequence[str] | None = None, + routine_revision_external_ids: Sequence[str] | None = None, + model_revision_external_ids: Sequence[str] | None = None, + ) -> Iterator[SimulationRun] | Iterator[SimulatorRunsList]: + """Iterate over simulation runs + + Fetches simulation runs as they are iterated over, so you keep a limited number of simulation runs in memory. + + Args: + chunk_size (int | None): Number of simulation runs to return in each chunk. Defaults to yielding one simulation run a time. + limit (int | None): The maximum number of simulation runs to return, pass None to return all. + status (str | None): Filter by simulation run status + run_type (str | None): Filter by simulation run type + model_external_ids (Sequence[str] | None): Filter by simulator model external ids + simulator_integration_external_ids (Sequence[str] | None): Filter by simulator integration external ids + simulator_external_ids (Sequence[str] | None): Filter by simulator external ids + routine_external_ids (Sequence[str] | None): Filter by routine external ids + routine_revision_external_ids (Sequence[str] | None): Filter by routine revision external ids + model_revision_external_ids (Sequence[str] | None): Filter by model revision external ids + + Returns: + Iterator[SimulationRun] | Iterator[SimulatorRunsList]: yields Simulation Run one by one if chunk is not specified, else SimulatorRunsList objects. + """ + + filter_runs = SimulatorRunsFilter( + status=status, + run_type=run_type, + model_external_ids=model_external_ids, + simulator_integration_external_ids=simulator_integration_external_ids, + simulator_external_ids=simulator_external_ids, + routine_external_ids=routine_external_ids, + routine_revision_external_ids=routine_revision_external_ids, + model_revision_external_ids=model_revision_external_ids, + ) + + return self._list_generator( + list_cls=SimulatorRunsList, + resource_cls=SimulationRun, + method="POST", + filter=filter_runs.dump(), + chunk_size=chunk_size, + limit=limit, + ) + + def list( + self, + limit: int | None = DEFAULT_LIMIT_READ, + status: str | None = None, + run_type: str | None = None, + model_external_ids: Sequence[str] | None = None, + simulator_integration_external_ids: Sequence[str] | None = None, + simulator_external_ids: Sequence[str] | None = None, + routine_external_ids: Sequence[str] | None = None, + routine_revision_external_ids: Sequence[str] | None = None, + model_revision_external_ids: Sequence[str] | None = None, + ) -> SimulatorRunsList: + """`Filter simulation runs `_ + Retrieves a list of simulation runs that match the given criteria + + Args: + limit (int | None): The maximum number of simulation runs to return, pass None to return all. + status (str | None): Filter by simulation run status + run_type (str | None): Filter by simulation run type + model_external_ids (Sequence[str] | None): Filter by simulator model external ids + simulator_integration_external_ids (Sequence[str] | None): Filter by simulator integration external ids + simulator_external_ids (Sequence[str] | None): Filter by simulator external ids + routine_external_ids (Sequence[str] | None): Filter by routine external ids + routine_revision_external_ids (Sequence[str] | None): Filter by routine revision external ids + model_revision_external_ids (Sequence[str] | None): Filter by model revision external ids + + Returns: + SimulatorRunsList: List of simulation runs + + Examples: + + List simulation runs: + >>> from cognite.client import CogniteClient + >>> client = CogniteClient() + >>> res = client.simulators.runs.list() + + Filter runs by status and simulator external ids: + >>> res = client.simulators.runs.list( + ... simulator_external_ids=["PROSPER", "DWSIM"], + ... status="success" + ... ) + """ + + filter_runs = SimulatorRunsFilter( + status=status, + run_type=run_type, + model_external_ids=model_external_ids, + simulator_integration_external_ids=simulator_integration_external_ids, + simulator_external_ids=simulator_external_ids, + routine_external_ids=routine_external_ids, + routine_revision_external_ids=routine_revision_external_ids, + model_revision_external_ids=model_revision_external_ids, + ) + self._warning.warn() + return self._list( + method="POST", + limit=limit, + resource_cls=SimulationRun, + list_cls=SimulatorRunsList, + filter=filter_runs.dump(), + ) + + @overload + def create(self, run: SimulationRunWrite) -> SimulationRun: ... + + @overload + def create(self, run: Sequence[SimulationRunWrite]) -> SimulatorRunsList: ... + + def create(self, run: SimulationRunWrite | Sequence[SimulationRunWrite]) -> SimulationRun | SimulatorRunsList: + """`Create simulation runs `_ + Args: + run (SimulationRunWrite | Sequence[SimulationRunWrite]): The simulation run(s) to execute. + Returns: + SimulationRun | SimulatorRunsList: Created simulation run(s) + Examples: + Create new simulation run: + >>> from cognite.client import CogniteClient + >>> from cognite.client.data_classes.simulators.runs import SimulationRunWrite + >>> client = CogniteClient() + >>> run = [ + ... SimulationRunWrite( + ... routine_external_id="routine1", + ... log_severity="Debug", + ... run_type="external", + ... ), + ... ] + >>> res = client.simulators.runs.create(run) + """ + assert_type(run, "simulation_run", [SimulationRunWrite, Sequence]) + + return self._create_multiple( + list_cls=SimulatorRunsList, + resource_cls=SimulationRun, + items=run, + input_resource_cls=SimulationRunWrite, + resource_path=self._RESOURCE_PATH_RUN, + ) diff --git a/cognite/client/data_classes/simulators/__init__.py b/cognite/client/data_classes/simulators/__init__.py index fb0d7f88cc..e3c8f02dd3 100644 --- a/cognite/client/data_classes/simulators/__init__.py +++ b/cognite/client/data_classes/simulators/__init__.py @@ -15,6 +15,12 @@ SimulatorModelRevisionWrite, SimulatorModelWrite, ) +from cognite.client.data_classes.simulators.runs import ( + SimulationRun, + SimulationRunWrite, + SimulationRunWriteList, + SimulatorRunsList, +) from cognite.client.data_classes.simulators.simulators import ( Simulator, SimulatorIntegration, @@ -29,6 +35,9 @@ __all__ = [ "CreatedTimeSort", "PropertySort", + "SimulationRun", + "SimulationRunWrite", + "SimulationRunWriteList", "Simulator", "SimulatorIntegration", "SimulatorIntegrationFilter", @@ -42,6 +51,7 @@ "SimulatorModelRevisionsFilter", "SimulatorModelWrite", "SimulatorModelsFilter", + "SimulatorRunsList", "SimulatorStep", "SimulatorStepField", "SimulatorStepOption", diff --git a/cognite/client/data_classes/simulators/filters.py b/cognite/client/data_classes/simulators/filters.py index 450f3d8d16..5e626a5202 100644 --- a/cognite/client/data_classes/simulators/filters.py +++ b/cognite/client/data_classes/simulators/filters.py @@ -34,3 +34,41 @@ def __init__( all_versions: bool | None = None, ) -> None: self.model_external_ids = [model_external_ids] if isinstance(model_external_ids, str) else model_external_ids + + +class SimulatorRunsFilter(CogniteFilter): + def __init__( + self, + status: str | None = None, + run_type: str | None = None, + model_external_ids: str | Sequence[str] | None = None, + simulator_integration_external_ids: str | Sequence[str] | None = None, + simulator_external_ids: str | Sequence[str] | None = None, + routine_external_ids: str | Sequence[str] | None = None, + routine_revision_external_ids: str | Sequence[str] | None = None, + model_revision_external_ids: str | Sequence[str] | None = None, + ) -> None: + self.model_external_ids = [model_external_ids] if isinstance(model_external_ids, str) else model_external_ids + self.simulator_integration_external_ids = ( + [simulator_integration_external_ids] + if isinstance(simulator_integration_external_ids, str) + else simulator_integration_external_ids + ) + self.simulator_external_ids = ( + [simulator_external_ids] if isinstance(simulator_external_ids, str) else simulator_external_ids + ) + self.routine_external_ids = ( + [routine_external_ids] if isinstance(routine_external_ids, str) else routine_external_ids + ) + self.routine_revision_external_ids = ( + [routine_revision_external_ids] + if isinstance(routine_revision_external_ids, str) + else routine_revision_external_ids + ) + self.model_revision_external_ids = ( + [model_revision_external_ids] + if isinstance(model_revision_external_ids, str) + else model_revision_external_ids + ) + self.status = status + self.run_type = run_type diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index 291b0ab4b7..03c74b2d94 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -7,6 +7,11 @@ from cognite.client.data_classes._base import ( CogniteObject, + CogniteResourceList, + ExternalIDTransformerMixin, + IdTransformerMixin, + WriteableCogniteResource, + WriteableCogniteResourceList, ) from cognite.client.utils._experimental import FeaturePreviewWarning @@ -56,3 +61,181 @@ def dump(self, camel_case: bool = True) -> dict[str, Any]: output["unit"] = self.unit.dump(camel_case=camel_case) return output + + +class SimulationRunCore(WriteableCogniteResource["SimulationRunWrite"]): + def __init__( + self, + run_type: str | None, + routine_external_id: str, + run_time: int | None = None, + ) -> None: + self.run_type = run_type + self.run_time = run_time + self.routine_external_id = routine_external_id + + +class SimulationRunWrite(SimulationRunCore): + """ + Request to run a simulator routine asynchronously. + + Args: + routine_external_id (str): External id of the associated simulator routine + run_type (str | None): The type of the simulation run + run_time (int | None): Run time in milliseconds. Reference timestamp used for data pre-processing and data sampling. + queue (bool | None): Queue the simulation run when connector is down. + log_severity (str | None): Override the minimum severity level for the simulation run logs. If not provided, the minimum severity is read from the connector logger configuration. + inputs (list[SimulationInputOverride] | None): List of input overrides + """ + + def __init__( + self, + routine_external_id: str, + run_type: str | None = None, + run_time: int | None = None, + queue: bool | None = None, + log_severity: str | None = None, + inputs: list[SimulationInputOverride] | None = None, + ) -> None: + super().__init__( + routine_external_id=routine_external_id, + run_type=run_type, + run_time=run_time, + ) + self.queue = queue + self.log_severity = log_severity + self.inputs = inputs + + @classmethod + def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> SimulationRunWrite: + return cls( + run_type=resource["runType"], + run_time=resource["runTime"], + queue=resource["queue"], + log_severity=resource["logSeverity"], + inputs=[SimulationInputOverride._load(input) for input in resource["inputs"]], + routine_external_id=resource["routineExternalId"], + ) + + def dump(self, camel_case: bool = True) -> dict[str, Any]: + output = super().dump(camel_case=camel_case) + output["inputs"] = [input.dump(camel_case=camel_case) for input in self.inputs] + return output + + def as_write(self) -> SimulationRunWrite: + return self + + +class SimulationRun(SimulationRunCore): + """ + Every time a simulation routine executes, a simulation run object is created. + This object ensures that each execution of a routine is documented and traceable. + Each run has an associated simulation data resource, which stores the inputs and outputs of a + simulation run, capturing the values set into and read from the simulator model to ensure + the traceability and integrity of the simulation data. + + Simulation runs provide a historical record of the simulations performed, allowing users to analyze + and compare different runs, track changes over time, and make informed decisions based on the simulation results. + + This is the read/response format of a simulation run. + + Args: + id (int): The id of the simulation run + simulator_external_id (str): External id of the associated simulator + simulator_integration_external_id (str): External id of the associated simulator integration + model_external_id (str): External id of the associated simulator model + model_revision_external_id (str): External id of the associated simulator model revision + routine_revision_external_id (str): External id of the associated simulator routine revision + routine_external_id (str): External id of the associated simulator routine + run_type (str): The type of the simulation run + status (str): The status of the simulation run + data_set_id (int): The id of the dataset associated with the simulation run + user_id (str): The id of the user who executed the simulation run + log_id (int): The id of the log associated with the simulation run + created_time (int): The number of milliseconds since epoch + last_updated_time (int): The number of milliseconds since epoch + status_message (str | None): The status message of the simulation run + simulation_time (int | None): Simulation time in milliseconds. Timestamp when the input data was sampled. Used for indexing input and output time series. + run_time (int | None): Run time in milliseconds. Reference timestamp used for data pre-processing and data sampling. + + """ + + def __init__( + self, + id: int, + simulator_external_id: str, + simulator_integration_external_id: str, + model_external_id: str, + model_revision_external_id: str, + routine_revision_external_id: str, + routine_external_id: str, + run_type: str, + status: str, + data_set_id: int, + user_id: str, + log_id: int, + created_time: int, + last_updated_time: int, + status_message: str | None = None, + simulation_time: int | None = None, + run_time: int | None = None, + ) -> None: + super().__init__( + run_type=run_type, + routine_external_id=routine_external_id, + run_time=run_time, + ) + self.id = id + self.created_time = created_time + self.last_updated_time = last_updated_time + self.simulator_external_id = simulator_external_id + self.simulator_integration_external_id = simulator_integration_external_id + self.model_external_id = model_external_id + self.model_revision_external_id = model_revision_external_id + self.routine_revision_external_id = routine_revision_external_id + self.simulation_time = simulation_time + self.status = status + self.status_message = status_message + self.data_set_id = data_set_id + self.user_id = user_id + self.log_id = log_id + + def as_write(self) -> SimulationRunWrite: + return SimulationRunWrite( + routine_external_id=self.routine_external_id, + run_type=self.run_type, + run_time=self.run_time, + ) + + @classmethod + def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> SimulationRun: + return cls( + id=resource["id"], + created_time=resource["createdTime"], + last_updated_time=resource["lastUpdatedTime"], + simulator_external_id=resource["simulatorExternalId"], + simulator_integration_external_id=resource["simulatorIntegrationExternalId"], + model_external_id=resource["modelExternalId"], + model_revision_external_id=resource["modelRevisionExternalId"], + routine_external_id=resource["routineExternalId"], + routine_revision_external_id=resource["routineRevisionExternalId"], + status=resource["status"], + data_set_id=resource["dataSetId"], + run_type=resource["runType"], + user_id=resource["userId"], + log_id=resource["logId"], + status_message=resource.get("statusMessage"), + run_time=resource.get("runTime"), + simulation_time=resource.get("simulationTime"), + ) + + +class SimulationRunWriteList(CogniteResourceList[SimulationRunWrite], ExternalIDTransformerMixin): + _RESOURCE = SimulationRunWrite + + +class SimulatorRunsList(WriteableCogniteResourceList[SimulationRunWrite, SimulationRun], IdTransformerMixin): + _RESOURCE = SimulationRun + + def as_write(self) -> SimulationRunWriteList: + return SimulationRunWriteList([a.as_write() for a in self.data], cognite_client=self._get_cognite_client()) diff --git a/cognite/client/testing.py b/cognite/client/testing.py index b5828d9692..133f579c1f 100644 --- a/cognite/client/testing.py +++ b/cognite/client/testing.py @@ -50,6 +50,7 @@ from cognite.client._api.simulators.integrations import SimulatorIntegrationsAPI from cognite.client._api.simulators.models import SimulatorModelsAPI from cognite.client._api.simulators.models_revisions import SimulatorModelRevisionsAPI +from cognite.client._api.simulators.runs import SimulatorRunsAPI from cognite.client._api.synthetic_time_series import SyntheticDatapointsAPI from cognite.client._api.templates import ( TemplateGroupsAPI, @@ -157,6 +158,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.simulators.integrations = MagicMock(spec_set=SimulatorIntegrationsAPI) self.simulators.models = MagicMock(spec=SimulatorModelsAPI) self.simulators.models.revisions = MagicMock(spec_set=SimulatorModelRevisionsAPI) + self.simulators.runs = MagicMock(spec_set=SimulatorRunsAPI) self.sequences = MagicMock(spec=SequencesAPI) self.sequences.rows = MagicMock(spec_set=SequencesDataAPI) From c0553ef2dd65d318b9da678b4377650c73f365db Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Fri, 28 Feb 2025 02:02:02 +0100 Subject: [PATCH 02/18] fix check --- cognite/client/data_classes/simulators/runs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index 03c74b2d94..7a3c81ed47 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -119,7 +119,8 @@ def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = def dump(self, camel_case: bool = True) -> dict[str, Any]: output = super().dump(camel_case=camel_case) - output["inputs"] = [input.dump(camel_case=camel_case) for input in self.inputs] + if self.inputs is not None: + output["inputs"] = [ input_.dump(camel_case=camel_case) for input_ in self.inputs] return output def as_write(self) -> SimulationRunWrite: From 41a0a55ea1d5565f7244c2f7bcd3c7aab67411cd Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Fri, 28 Feb 2025 12:02:26 +0100 Subject: [PATCH 03/18] fix --- cognite/client/data_classes/simulators/runs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index 7a3c81ed47..3ac1e2334b 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -120,7 +120,7 @@ def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = def dump(self, camel_case: bool = True) -> dict[str, Any]: output = super().dump(camel_case=camel_case) if self.inputs is not None: - output["inputs"] = [ input_.dump(camel_case=camel_case) for input_ in self.inputs] + output["inputs"] = [input_.dump(camel_case=camel_case) for input_ in self.inputs] return output def as_write(self) -> SimulationRunWrite: From 3b4414302b0cb4704e22b0a413ed7716827e1efe Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Sun, 2 Mar 2025 22:31:39 +0100 Subject: [PATCH 04/18] test --- .../client/data_classes/simulators/runs.py | 8 ++--- .../test_api/test_simulators/test_runs.py | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/tests_integration/test_api/test_simulators/test_runs.py diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index 3ac1e2334b..71c2085f9f 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -110,11 +110,11 @@ def __init__( def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> SimulationRunWrite: return cls( run_type=resource["runType"], - run_time=resource["runTime"], - queue=resource["queue"], - log_severity=resource["logSeverity"], - inputs=[SimulationInputOverride._load(input) for input in resource["inputs"]], routine_external_id=resource["routineExternalId"], + run_time=resource.get("runTime"), + queue=resource.get("queue"), + log_severity=resource.get("logSeverity"), + inputs=[SimulationInputOverride._load(input) for input in resource.get("inputs", [])], ) def dump(self, camel_case: bool = True) -> dict[str, Any]: diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py new file mode 100644 index 0000000000..8b59e17bad --- /dev/null +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -0,0 +1,30 @@ +import pytest + +from cognite.client._cognite_client import CogniteClient +from cognite.client.data_classes.simulators.runs import ( + SimulationRunWrite, +) + + +@pytest.mark.usefixtures("seed_resource_names", "seed_simulator_routine_revision") +class TestSimulatorRuns: + # todo: base conftest on top of routine revisions PR + def test_create_run( + self, cognite_client: CogniteClient, seed_simulator_routine_revision, seed_resource_names + ) -> None: + routine_external_id = seed_resource_names["simulator_routine_external_id"] + runs = [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] + created_runs = cognite_client.simulators.runs.create(runs) + assert len(created_runs) == 1 + assert created_runs[0].run_type == "external" + assert created_runs[0].routine_external_id == routine_external_id + assert created_runs[0].status == "ready" + + def test_list_runs(self, cognite_client: CogniteClient) -> None: + runs = cognite_client.simulators.runs.list() + assert len(runs) > 0 From 631779f0f6624c55ef3ad8626c7615f6c4a01c9d Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Wed, 2 Apr 2025 20:06:39 +0200 Subject: [PATCH 05/18] fixture --- cognite/client/data_classes/simulators/filters.py | 3 ++- .../test_api/test_simulators/test_runs.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cognite/client/data_classes/simulators/filters.py b/cognite/client/data_classes/simulators/filters.py index 8934974873..2335ed0f1d 100644 --- a/cognite/client/data_classes/simulators/filters.py +++ b/cognite/client/data_classes/simulators/filters.py @@ -75,7 +75,8 @@ def __init__( ) self.status = status self.run_type = run_type -======= + + class SimulatorRoutinesFilter(CogniteFilter): def __init__( self, diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index 8b59e17bad..3a7b72da6e 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -6,11 +6,10 @@ ) -@pytest.mark.usefixtures("seed_resource_names", "seed_simulator_routine_revision") +@pytest.mark.usefixtures("seed_resource_names", "seed_simulator_routine_revisions") class TestSimulatorRuns: - # todo: base conftest on top of routine revisions PR def test_create_run( - self, cognite_client: CogniteClient, seed_simulator_routine_revision, seed_resource_names + self, cognite_client: CogniteClient, seed_simulator_routine_revisions, seed_resource_names ) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] runs = [ @@ -27,4 +26,4 @@ def test_create_run( def test_list_runs(self, cognite_client: CogniteClient) -> None: runs = cognite_client.simulators.runs.list() - assert len(runs) > 0 + assert len(runs) >= 1 From 71d911360baa20f6d200c4fc382bb971ac5e6b89 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Wed, 2 Apr 2025 20:13:28 +0200 Subject: [PATCH 06/18] fix --- cognite/client/_api/simulators/__init__.py | 2 +- cognite/client/testing.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cognite/client/_api/simulators/__init__.py b/cognite/client/_api/simulators/__init__.py index a39fd23ef1..ce5eb259bc 100644 --- a/cognite/client/_api/simulators/__init__.py +++ b/cognite/client/_api/simulators/__init__.py @@ -6,8 +6,8 @@ from cognite.client._api.simulators.integrations import SimulatorIntegrationsAPI from cognite.client._api.simulators.logs import SimulatorLogsAPI from cognite.client._api.simulators.models import SimulatorModelsAPI -from cognite.client._api.simulators.runs import SimulatorRunsAPI from cognite.client._api.simulators.routines import SimulatorRoutinesAPI +from cognite.client._api.simulators.runs import SimulatorRunsAPI from cognite.client._api_client import APIClient from cognite.client._constants import DEFAULT_LIMIT_READ from cognite.client.data_classes.simulators.simulators import Simulator, SimulatorList diff --git a/cognite/client/testing.py b/cognite/client/testing.py index 8fead09fe2..062ed78ce6 100644 --- a/cognite/client/testing.py +++ b/cognite/client/testing.py @@ -51,9 +51,9 @@ from cognite.client._api.simulators.logs import SimulatorLogsAPI from cognite.client._api.simulators.models import SimulatorModelsAPI from cognite.client._api.simulators.models_revisions import SimulatorModelRevisionsAPI -from cognite.client._api.simulators.runs import SimulatorRunsAPI from cognite.client._api.simulators.routine_revisions import SimulatorRoutineRevisionsAPI from cognite.client._api.simulators.routines import SimulatorRoutinesAPI +from cognite.client._api.simulators.runs import SimulatorRunsAPI from cognite.client._api.synthetic_time_series import SyntheticDatapointsAPI from cognite.client._api.templates import ( TemplateGroupsAPI, From 9d4f6171e0c2a57bd7bb28a3a89e84cfac22895b Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Wed, 2 Apr 2025 20:25:47 +0200 Subject: [PATCH 07/18] fix --- cognite/client/data_classes/simulators/runs.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index 71c2085f9f..a987add5d3 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -108,13 +108,14 @@ def __init__( @classmethod def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> SimulationRunWrite: + inputs = resource.get("inputs", None) return cls( - run_type=resource["runType"], + run_type=resource.get("runType"), routine_external_id=resource["routineExternalId"], run_time=resource.get("runTime"), queue=resource.get("queue"), log_severity=resource.get("logSeverity"), - inputs=[SimulationInputOverride._load(input) for input in resource.get("inputs", [])], + inputs=[SimulationInputOverride._load(_input) for _input in inputs] if inputs else None, ) def dump(self, camel_case: bool = True) -> dict[str, Any]: From c52bf6a7860bf7b7953c3e28caae5e491bf1fc92 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Wed, 2 Apr 2025 23:34:50 +0200 Subject: [PATCH 08/18] add docstrings testing --- tests/tests_unit/test_docstring_examples.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/tests_unit/test_docstring_examples.py b/tests/tests_unit/test_docstring_examples.py index cf5a908431..2234fa0744 100644 --- a/tests/tests_unit/test_docstring_examples.py +++ b/tests/tests_unit/test_docstring_examples.py @@ -151,3 +151,4 @@ def test_simulators(self): run_docstring_tests(simulators.routines) run_docstring_tests(simulators.routine_revisions) run_docstring_tests(simulators.logs) + run_docstring_tests(simulators.runs) From 2457905a4f0994a2c389a6b178546ccf1a8199b4 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 3 Apr 2025 01:34:45 +0200 Subject: [PATCH 09/18] fix --- .../test_api/test_simulators/test_runs.py | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index 3a7b72da6e..8d22bddf51 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -24,6 +24,44 @@ def test_create_run( assert created_runs[0].routine_external_id == routine_external_id assert created_runs[0].status == "ready" - def test_list_runs(self, cognite_client: CogniteClient) -> None: + def test_list_runs(self, cognite_client: CogniteClient, seed_resource_names) -> None: + routine_external_id = seed_resource_names["simulator_routine_external_id"] runs = cognite_client.simulators.runs.list() - assert len(runs) >= 1 + assert len(runs) > 0 + + list_by_routine = cognite_client.simulators.runs.list(routine_external_ids=[routine_external_id]) + assert all(run.routine_external_id == routine_external_id for run in list_by_routine) + + def test_list_runs_by_status(self, cognite_client: CogniteClient, seed_resource_names) -> None: + routine_external_id = seed_resource_names["simulator_routine_external_id"] + status = ["running", "success", "failure"] + for _ in range(3): + created_runs = cognite_client.simulators.runs.create( + [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] + ) + assert created_runs[0].routine_external_id == routine_external_id + run_id = created_runs[0].id + status_to_be_set = status.pop(0) + + cognite_client.simulators._post( + "/simulators/run/callback", + json={ + "items": [ + { + "id": run_id, + "status": status_to_be_set, + } + ] + }, + ) + + list_runs_status_filter = cognite_client.simulators.runs.list( + status=status_to_be_set, routine_external_ids=[routine_external_id] + ) + assert list_runs_status_filter[0].status == status_to_be_set + assert list_runs_status_filter[0].id == run_id From 431f50e8ffc87b493126e1c2d158f2ee51751d3e Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 3 Apr 2025 01:42:48 +0200 Subject: [PATCH 10/18] fix --- .../test_api/test_simulators/test_runs.py | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index 8d22bddf51..9e79b48979 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -12,27 +12,18 @@ def test_create_run( self, cognite_client: CogniteClient, seed_simulator_routine_revisions, seed_resource_names ) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] - runs = [ - SimulationRunWrite( - run_type="external", - routine_external_id=routine_external_id, - ) - ] - created_runs = cognite_client.simulators.runs.create(runs) - assert len(created_runs) == 1 - assert created_runs[0].run_type == "external" + created_runs = cognite_client.simulators.runs.create( + [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] + ) assert created_runs[0].routine_external_id == routine_external_id - assert created_runs[0].status == "ready" + assert created_runs[0].id is not None - def test_list_runs(self, cognite_client: CogniteClient, seed_resource_names) -> None: - routine_external_id = seed_resource_names["simulator_routine_external_id"] - runs = cognite_client.simulators.runs.list() - assert len(runs) > 0 - - list_by_routine = cognite_client.simulators.runs.list(routine_external_ids=[routine_external_id]) - assert all(run.routine_external_id == routine_external_id for run in list_by_routine) - - def test_list_runs_by_status(self, cognite_client: CogniteClient, seed_resource_names) -> None: + def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] status = ["running", "success", "failure"] for _ in range(3): @@ -45,7 +36,6 @@ def test_list_runs_by_status(self, cognite_client: CogniteClient, seed_resource_ ] ) assert created_runs[0].routine_external_id == routine_external_id - run_id = created_runs[0].id status_to_be_set = status.pop(0) cognite_client.simulators._post( @@ -53,15 +43,18 @@ def test_list_runs_by_status(self, cognite_client: CogniteClient, seed_resource_ json={ "items": [ { - "id": run_id, + "id": created_runs[0].id, "status": status_to_be_set, } ] }, ) - list_runs_status_filter = cognite_client.simulators.runs.list( + filter_by_status = cognite_client.simulators.runs.list( status=status_to_be_set, routine_external_ids=[routine_external_id] ) - assert list_runs_status_filter[0].status == status_to_be_set - assert list_runs_status_filter[0].id == run_id + assert filter_by_status[0].status == status_to_be_set + assert filter_by_status[0].id == created_runs[0].id + + filter_by_routine = cognite_client.simulators.runs.list(routine_external_ids=[routine_external_id]) + assert len(filter_by_routine) >= 3 From 4631812105bfc045473bee562f9ef1a5b616eb5b Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 3 Apr 2025 01:46:50 +0200 Subject: [PATCH 11/18] refactor --- .../test_api/test_simulators/test_runs.py | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index 9e79b48979..d8ba070a7e 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -1,9 +1,21 @@ import pytest from cognite.client._cognite_client import CogniteClient -from cognite.client.data_classes.simulators.runs import ( - SimulationRunWrite, -) +from cognite.client.data_classes.simulators.runs import SimulationRunWrite, SimulatorRunsList + + +def create_simulation_run( + cognite_client: CogniteClient, + routine_external_id: str, +) -> SimulatorRunsList: + return cognite_client.simulators.runs.create( + [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] + ) @pytest.mark.usefixtures("seed_resource_names", "seed_simulator_routine_revisions") @@ -12,13 +24,9 @@ def test_create_run( self, cognite_client: CogniteClient, seed_simulator_routine_revisions, seed_resource_names ) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] - created_runs = cognite_client.simulators.runs.create( - [ - SimulationRunWrite( - run_type="external", - routine_external_id=routine_external_id, - ) - ] + created_runs = create_simulation_run( + cognite_client=cognite_client, + routine_external_id=routine_external_id, ) assert created_runs[0].routine_external_id == routine_external_id assert created_runs[0].id is not None @@ -27,13 +35,9 @@ def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names routine_external_id = seed_resource_names["simulator_routine_external_id"] status = ["running", "success", "failure"] for _ in range(3): - created_runs = cognite_client.simulators.runs.create( - [ - SimulationRunWrite( - run_type="external", - routine_external_id=routine_external_id, - ) - ] + created_runs = create_simulation_run( + cognite_client=cognite_client, + routine_external_id=routine_external_id, ) assert created_runs[0].routine_external_id == routine_external_id status_to_be_set = status.pop(0) From dda9998517d3fb40242fbdebb3b5e8c9b9bb38e2 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 3 Apr 2025 01:48:04 +0200 Subject: [PATCH 12/18] refactor --- .../test_api/test_simulators/test_runs.py | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index d8ba070a7e..9c2342a877 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -1,21 +1,7 @@ import pytest from cognite.client._cognite_client import CogniteClient -from cognite.client.data_classes.simulators.runs import SimulationRunWrite, SimulatorRunsList - - -def create_simulation_run( - cognite_client: CogniteClient, - routine_external_id: str, -) -> SimulatorRunsList: - return cognite_client.simulators.runs.create( - [ - SimulationRunWrite( - run_type="external", - routine_external_id=routine_external_id, - ) - ] - ) +from cognite.client.data_classes.simulators.runs import SimulationRunWrite @pytest.mark.usefixtures("seed_resource_names", "seed_simulator_routine_revisions") @@ -24,9 +10,13 @@ def test_create_run( self, cognite_client: CogniteClient, seed_simulator_routine_revisions, seed_resource_names ) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] - created_runs = create_simulation_run( - cognite_client=cognite_client, - routine_external_id=routine_external_id, + created_runs = cognite_client.simulators.runs.create( + [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] ) assert created_runs[0].routine_external_id == routine_external_id assert created_runs[0].id is not None @@ -35,9 +25,13 @@ def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names routine_external_id = seed_resource_names["simulator_routine_external_id"] status = ["running", "success", "failure"] for _ in range(3): - created_runs = create_simulation_run( - cognite_client=cognite_client, - routine_external_id=routine_external_id, + created_runs = cognite_client.simulators.runs.create( + [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] ) assert created_runs[0].routine_external_id == routine_external_id status_to_be_set = status.pop(0) From 9653e9dd192eed90bfff527cc09092e4f7fbf0a7 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Sun, 6 Apr 2025 22:59:20 +0200 Subject: [PATCH 13/18] fix --- cognite/client/_api/simulators/runs.py | 3 ++- .../test_api/test_simulators/test_runs.py | 18 ++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cognite/client/_api/simulators/runs.py b/cognite/client/_api/simulators/runs.py index 549d126d52..006980bf3b 100644 --- a/cognite/client/_api/simulators/runs.py +++ b/cognite/client/_api/simulators/runs.py @@ -21,6 +21,7 @@ class SimulatorRunsAPI(APIClient): def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: CogniteClient) -> None: super().__init__(config, api_version, cognite_client) + self._CREATE_LIMIT = 1 self._warning = FeaturePreviewWarning( api_maturity="General Availability", sdk_maturity="alpha", feature_name="Simulators" ) @@ -48,7 +49,7 @@ def __call__( routine_external_ids: Sequence[str] | None = None, routine_revision_external_ids: Sequence[str] | None = None, model_revision_external_ids: Sequence[str] | None = None, - ) -> Iterator[SimulationRun]: ... + ) -> Iterator[SimulatorRunsList]: ... @overload def __call__( diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index 9c2342a877..df6e446702 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -23,8 +23,8 @@ def test_create_run( def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] - status = ["running", "success", "failure"] - for _ in range(3): + runs_filtered_by_status = [] + for current_status in ["running", "success", "failure"]: created_runs = cognite_client.simulators.runs.create( [ SimulationRunWrite( @@ -33,8 +33,6 @@ def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names ) ] ) - assert created_runs[0].routine_external_id == routine_external_id - status_to_be_set = status.pop(0) cognite_client.simulators._post( "/simulators/run/callback", @@ -42,17 +40,17 @@ def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names "items": [ { "id": created_runs[0].id, - "status": status_to_be_set, + "status": current_status, } ] }, ) filter_by_status = cognite_client.simulators.runs.list( - status=status_to_be_set, routine_external_ids=[routine_external_id] + status=current_status, routine_external_ids=[routine_external_id] ) - assert filter_by_status[0].status == status_to_be_set - assert filter_by_status[0].id == created_runs[0].id + runs_filtered_by_status.append(filter_by_status[0].dump()) - filter_by_routine = cognite_client.simulators.runs.list(routine_external_ids=[routine_external_id]) - assert len(filter_by_routine) >= 3 + filter_by_routine = cognite_client.simulators.runs.list(routine_external_ids=[routine_external_id]).dump() + for run in runs_filtered_by_status: + assert run in filter_by_routine From b87f42d7e582576b958904b35539860c61f9855b Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Sun, 6 Apr 2025 23:04:44 +0200 Subject: [PATCH 14/18] fix --- .../client/data_classes/simulators/filters.py | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/cognite/client/data_classes/simulators/filters.py b/cognite/client/data_classes/simulators/filters.py index 2335ed0f1d..01fcb4a214 100644 --- a/cognite/client/data_classes/simulators/filters.py +++ b/cognite/client/data_classes/simulators/filters.py @@ -8,6 +8,14 @@ from cognite.client.utils.useful_types import SequenceNotStr +def parse_str_or_sequence(value: str | Sequence[str] | None) -> list[str] | None: + if isinstance(value, str): + return [value] + elif isinstance(value, Sequence): + return list(value) + return None + + class SimulatorIntegrationFilter(CogniteFilter): def __init__( self, @@ -52,27 +60,11 @@ def __init__( model_revision_external_ids: str | Sequence[str] | None = None, ) -> None: self.model_external_ids = [model_external_ids] if isinstance(model_external_ids, str) else model_external_ids - self.simulator_integration_external_ids = ( - [simulator_integration_external_ids] - if isinstance(simulator_integration_external_ids, str) - else simulator_integration_external_ids - ) - self.simulator_external_ids = ( - [simulator_external_ids] if isinstance(simulator_external_ids, str) else simulator_external_ids - ) - self.routine_external_ids = ( - [routine_external_ids] if isinstance(routine_external_ids, str) else routine_external_ids - ) - self.routine_revision_external_ids = ( - [routine_revision_external_ids] - if isinstance(routine_revision_external_ids, str) - else routine_revision_external_ids - ) - self.model_revision_external_ids = ( - [model_revision_external_ids] - if isinstance(model_revision_external_ids, str) - else model_revision_external_ids - ) + self.simulator_integration_external_ids = parse_str_or_sequence(simulator_integration_external_ids) + self.simulator_external_ids = parse_str_or_sequence(simulator_external_ids) + self.routine_external_ids = parse_str_or_sequence(routine_external_ids) + self.routine_revision_external_ids = parse_str_or_sequence(routine_revision_external_ids) + self.model_revision_external_ids = parse_str_or_sequence(model_revision_external_ids) self.status = status self.run_type = run_type From bb1ddbf824ad7c17bf513b41a3ea0678856362ab Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Tue, 8 Apr 2025 00:06:44 +0200 Subject: [PATCH 15/18] fix --- .../test_api/test_simulators/test_runs.py | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index df6e446702..671a69c5bb 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -6,21 +6,6 @@ @pytest.mark.usefixtures("seed_resource_names", "seed_simulator_routine_revisions") class TestSimulatorRuns: - def test_create_run( - self, cognite_client: CogniteClient, seed_simulator_routine_revisions, seed_resource_names - ) -> None: - routine_external_id = seed_resource_names["simulator_routine_external_id"] - created_runs = cognite_client.simulators.runs.create( - [ - SimulationRunWrite( - run_type="external", - routine_external_id=routine_external_id, - ) - ] - ) - assert created_runs[0].routine_external_id == routine_external_id - assert created_runs[0].id is not None - def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names) -> None: routine_external_id = seed_resource_names["simulator_routine_external_id"] runs_filtered_by_status = [] @@ -50,7 +35,23 @@ def test_list_filtering(self, cognite_client: CogniteClient, seed_resource_names status=current_status, routine_external_ids=[routine_external_id] ) runs_filtered_by_status.append(filter_by_status[0].dump()) - filter_by_routine = cognite_client.simulators.runs.list(routine_external_ids=[routine_external_id]).dump() - for run in runs_filtered_by_status: - assert run in filter_by_routine + assert filter_by_routine == runs_filtered_by_status + assert sorted(runs_filtered_by_status, key=lambda x: x["id"]) == sorted( + filter_by_routine, key=lambda x: x["id"] + ) + + def test_create_run( + self, cognite_client: CogniteClient, seed_simulator_routine_revisions, seed_resource_names + ) -> None: + routine_external_id = seed_resource_names["simulator_routine_external_id"] + created_runs = cognite_client.simulators.runs.create( + [ + SimulationRunWrite( + run_type="external", + routine_external_id=routine_external_id, + ) + ] + ) + assert created_runs[0].routine_external_id == routine_external_id + assert created_runs[0].id is not None From 8053988124c401193e0a64c6dea1b3306d404370 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 24 Apr 2025 14:21:52 +0200 Subject: [PATCH 16/18] fix --- .../client/data_classes/simulators/filters.py | 28 +++++++++---------- .../client/data_classes/simulators/runs.py | 6 ++-- .../test_api/test_simulators/test_runs.py | 1 + 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/cognite/client/data_classes/simulators/filters.py b/cognite/client/data_classes/simulators/filters.py index 01fcb4a214..4c44006e6b 100644 --- a/cognite/client/data_classes/simulators/filters.py +++ b/cognite/client/data_classes/simulators/filters.py @@ -8,10 +8,10 @@ from cognite.client.utils.useful_types import SequenceNotStr -def parse_str_or_sequence(value: str | Sequence[str] | None) -> list[str] | None: +def _parse_str_or_sequence(value: str | SequenceNotStr[str] | None) -> list[str] | None: if isinstance(value, str): return [value] - elif isinstance(value, Sequence): + elif isinstance(value, SequenceNotStr): return list(value) return None @@ -52,19 +52,19 @@ def __init__( self, status: str | None = None, run_type: str | None = None, - model_external_ids: str | Sequence[str] | None = None, - simulator_integration_external_ids: str | Sequence[str] | None = None, - simulator_external_ids: str | Sequence[str] | None = None, - routine_external_ids: str | Sequence[str] | None = None, - routine_revision_external_ids: str | Sequence[str] | None = None, - model_revision_external_ids: str | Sequence[str] | None = None, + model_external_ids: str | SequenceNotStr[str] | None = None, + simulator_integration_external_ids: str | SequenceNotStr[str] | None = None, + simulator_external_ids: str | SequenceNotStr[str] | None = None, + routine_external_ids: str | SequenceNotStr[str] | None = None, + routine_revision_external_ids: str | SequenceNotStr[str] | None = None, + model_revision_external_ids: str | SequenceNotStr[str] | None = None, ) -> None: - self.model_external_ids = [model_external_ids] if isinstance(model_external_ids, str) else model_external_ids - self.simulator_integration_external_ids = parse_str_or_sequence(simulator_integration_external_ids) - self.simulator_external_ids = parse_str_or_sequence(simulator_external_ids) - self.routine_external_ids = parse_str_or_sequence(routine_external_ids) - self.routine_revision_external_ids = parse_str_or_sequence(routine_revision_external_ids) - self.model_revision_external_ids = parse_str_or_sequence(model_revision_external_ids) + self.model_external_ids = _parse_str_or_sequence(model_external_ids) + self.simulator_integration_external_ids = _parse_str_or_sequence(simulator_integration_external_ids) + self.simulator_external_ids = _parse_str_or_sequence(simulator_external_ids) + self.routine_external_ids = _parse_str_or_sequence(routine_external_ids) + self.routine_revision_external_ids = _parse_str_or_sequence(routine_revision_external_ids) + self.model_revision_external_ids = _parse_str_or_sequence(model_revision_external_ids) self.status = status self.run_type = run_type diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index a987add5d3..ff1f3d0c64 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from typing_extensions import Self @@ -171,8 +171,8 @@ def __init__( model_revision_external_id: str, routine_revision_external_id: str, routine_external_id: str, - run_type: str, - status: str, + run_type: Literal["external", "manual", "scheduled"], + status: Literal["ready", "running", "success", "failure"], data_set_id: int, user_id: str, log_id: int, diff --git a/tests/tests_integration/test_api/test_simulators/test_runs.py b/tests/tests_integration/test_api/test_simulators/test_runs.py index 671a69c5bb..dee49f51b5 100644 --- a/tests/tests_integration/test_api/test_simulators/test_runs.py +++ b/tests/tests_integration/test_api/test_simulators/test_runs.py @@ -53,5 +53,6 @@ def test_create_run( ) ] ) + assert len(created_runs) == 1 assert created_runs[0].routine_external_id == routine_external_id assert created_runs[0].id is not None From 7ce9ad170c902de3423223919f6e39a993c973e4 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 24 Apr 2025 14:28:13 +0200 Subject: [PATCH 17/18] fix --- cognite/client/_api/simulators/runs.py | 73 +++++++++++++------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/cognite/client/_api/simulators/runs.py b/cognite/client/_api/simulators/runs.py index 006980bf3b..7baabbf075 100644 --- a/cognite/client/_api/simulators/runs.py +++ b/cognite/client/_api/simulators/runs.py @@ -9,6 +9,7 @@ from cognite.client.data_classes.simulators.runs import SimulationRun, SimulationRunWrite, SimulatorRunsList from cognite.client.utils._experimental import FeaturePreviewWarning from cognite.client.utils._validation import assert_type +from cognite.client.utils.useful_types import SequenceNotStr if TYPE_CHECKING: from cognite.client import CogniteClient @@ -43,12 +44,12 @@ def __call__( limit: int | None = None, status: str | None = None, run_type: str | None = None, - model_external_ids: Sequence[str] | None = None, - simulator_integration_external_ids: Sequence[str] | None = None, - simulator_external_ids: Sequence[str] | None = None, - routine_external_ids: Sequence[str] | None = None, - routine_revision_external_ids: Sequence[str] | None = None, - model_revision_external_ids: Sequence[str] | None = None, + model_external_ids: SequenceNotStr[str] | None = None, + simulator_integration_external_ids: SequenceNotStr[str] | None = None, + simulator_external_ids: SequenceNotStr[str] | None = None, + routine_external_ids: SequenceNotStr[str] | None = None, + routine_revision_external_ids: SequenceNotStr[str] | None = None, + model_revision_external_ids: SequenceNotStr[str] | None = None, ) -> Iterator[SimulatorRunsList]: ... @overload @@ -58,12 +59,12 @@ def __call__( limit: int | None = None, status: str | None = None, run_type: str | None = None, - model_external_ids: Sequence[str] | None = None, - simulator_integration_external_ids: Sequence[str] | None = None, - simulator_external_ids: Sequence[str] | None = None, - routine_external_ids: Sequence[str] | None = None, - routine_revision_external_ids: Sequence[str] | None = None, - model_revision_external_ids: Sequence[str] | None = None, + model_external_ids: SequenceNotStr[str] | None = None, + simulator_integration_external_ids: SequenceNotStr[str] | None = None, + simulator_external_ids: SequenceNotStr[str] | None = None, + routine_external_ids: SequenceNotStr[str] | None = None, + routine_revision_external_ids: SequenceNotStr[str] | None = None, + model_revision_external_ids: SequenceNotStr[str] | None = None, ) -> Iterator[SimulationRun]: ... def __call__( @@ -72,12 +73,12 @@ def __call__( limit: int | None = None, status: str | None = None, run_type: str | None = None, - model_external_ids: Sequence[str] | None = None, - simulator_integration_external_ids: Sequence[str] | None = None, - simulator_external_ids: Sequence[str] | None = None, - routine_external_ids: Sequence[str] | None = None, - routine_revision_external_ids: Sequence[str] | None = None, - model_revision_external_ids: Sequence[str] | None = None, + model_external_ids: SequenceNotStr[str] | None = None, + simulator_integration_external_ids: SequenceNotStr[str] | None = None, + simulator_external_ids: SequenceNotStr[str] | None = None, + routine_external_ids: SequenceNotStr[str] | None = None, + routine_revision_external_ids: SequenceNotStr[str] | None = None, + model_revision_external_ids: SequenceNotStr[str] | None = None, ) -> Iterator[SimulationRun] | Iterator[SimulatorRunsList]: """Iterate over simulation runs @@ -88,12 +89,12 @@ def __call__( limit (int | None): The maximum number of simulation runs to return, pass None to return all. status (str | None): Filter by simulation run status run_type (str | None): Filter by simulation run type - model_external_ids (Sequence[str] | None): Filter by simulator model external ids - simulator_integration_external_ids (Sequence[str] | None): Filter by simulator integration external ids - simulator_external_ids (Sequence[str] | None): Filter by simulator external ids - routine_external_ids (Sequence[str] | None): Filter by routine external ids - routine_revision_external_ids (Sequence[str] | None): Filter by routine revision external ids - model_revision_external_ids (Sequence[str] | None): Filter by model revision external ids + model_external_ids (SequenceNotStr[str] | None): Filter by simulator model external ids + simulator_integration_external_ids (SequenceNotStr[str] | None): Filter by simulator integration external ids + simulator_external_ids (SequenceNotStr[str] | None): Filter by simulator external ids + routine_external_ids (SequenceNotStr[str] | None): Filter by routine external ids + routine_revision_external_ids (SequenceNotStr[str] | None): Filter by routine revision external ids + model_revision_external_ids (SequenceNotStr[str] | None): Filter by model revision external ids Returns: Iterator[SimulationRun] | Iterator[SimulatorRunsList]: yields Simulation Run one by one if chunk is not specified, else SimulatorRunsList objects. @@ -124,12 +125,12 @@ def list( limit: int | None = DEFAULT_LIMIT_READ, status: str | None = None, run_type: str | None = None, - model_external_ids: Sequence[str] | None = None, - simulator_integration_external_ids: Sequence[str] | None = None, - simulator_external_ids: Sequence[str] | None = None, - routine_external_ids: Sequence[str] | None = None, - routine_revision_external_ids: Sequence[str] | None = None, - model_revision_external_ids: Sequence[str] | None = None, + model_external_ids: SequenceNotStr[str] | None = None, + simulator_integration_external_ids: SequenceNotStr[str] | None = None, + simulator_external_ids: SequenceNotStr[str] | None = None, + routine_external_ids: SequenceNotStr[str] | None = None, + routine_revision_external_ids: SequenceNotStr[str] | None = None, + model_revision_external_ids: SequenceNotStr[str] | None = None, ) -> SimulatorRunsList: """`Filter simulation runs `_ Retrieves a list of simulation runs that match the given criteria @@ -138,12 +139,12 @@ def list( limit (int | None): The maximum number of simulation runs to return, pass None to return all. status (str | None): Filter by simulation run status run_type (str | None): Filter by simulation run type - model_external_ids (Sequence[str] | None): Filter by simulator model external ids - simulator_integration_external_ids (Sequence[str] | None): Filter by simulator integration external ids - simulator_external_ids (Sequence[str] | None): Filter by simulator external ids - routine_external_ids (Sequence[str] | None): Filter by routine external ids - routine_revision_external_ids (Sequence[str] | None): Filter by routine revision external ids - model_revision_external_ids (Sequence[str] | None): Filter by model revision external ids + model_external_ids (SequenceNotStr[str] | None): Filter by simulator model external ids + simulator_integration_external_ids (SequenceNotStr[str] | None): Filter by simulator integration external ids + simulator_external_ids (SequenceNotStr[str] | None): Filter by simulator external ids + routine_external_ids (SequenceNotStr[str] | None): Filter by routine external ids + routine_revision_external_ids (SequenceNotStr[str] | None): Filter by routine revision external ids + model_revision_external_ids (SequenceNotStr[str] | None): Filter by model revision external ids Returns: SimulatorRunsList: List of simulation runs From 2519f710d2eb8a483b4843b11227036eef48e014 Mon Sep 17 00:00:00 2001 From: Luis Pereira Date: Thu, 24 Apr 2025 14:33:54 +0200 Subject: [PATCH 18/18] fix --- cognite/client/data_classes/simulators/runs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cognite/client/data_classes/simulators/runs.py b/cognite/client/data_classes/simulators/runs.py index ff1f3d0c64..648f79863e 100644 --- a/cognite/client/data_classes/simulators/runs.py +++ b/cognite/client/data_classes/simulators/runs.py @@ -149,8 +149,8 @@ class SimulationRun(SimulationRunCore): model_revision_external_id (str): External id of the associated simulator model revision routine_revision_external_id (str): External id of the associated simulator routine revision routine_external_id (str): External id of the associated simulator routine - run_type (str): The type of the simulation run - status (str): The status of the simulation run + run_type (Literal['external', 'manual', 'scheduled']): The type of the simulation run + status (Literal['ready', 'running', 'success', 'failure']): The status of the simulation run data_set_id (int): The id of the dataset associated with the simulation run user_id (str): The id of the user who executed the simulation run log_id (int): The id of the log associated with the simulation run