diff --git a/CHANGELOG.md b/CHANGELOG.md index 976d2e2574..6156bf0939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ Changes are grouped as follows - `Fixed` for any bug fixes. - `Security` in case of vulnerabilities. +## [7.54.7] - 2024-07-22 +### Fixed +- The method `client.three_d.models.update` no longer accepts `ThreeDModelWrite` as this will raise a `ValueError`. +- The method `client.three_d.models.create` now supports creating multiple models with different metdata fields + in a single call. + ## [7.54.6] - 2024-07-19 ### Fixed - In the data classe, `NodeApply` and `EdgeApply` the argument `camel_case=False` is now diff --git a/cognite/client/_api/three_d.py b/cognite/client/_api/three_d.py index 96626a38df..a97688578b 100644 --- a/cognite/client/_api/three_d.py +++ b/cognite/client/_api/three_d.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Iterator, Sequence, overload +from typing import TYPE_CHECKING, Iterator, Sequence, overload from cognite.client._api_client import APIClient from cognite.client._constants import DEFAULT_LIMIT_READ @@ -148,12 +148,16 @@ def list(self, published: bool | None = None, limit: int | None = DEFAULT_LIMIT_ ) def create( - self, name: str | SequenceNotStr[str], data_set_id: int | None = None, metadata: dict[str, str] | None = None + self, + name: str | ThreeDModelWrite | SequenceNotStr[str | ThreeDModelWrite], + data_set_id: int | None = None, + metadata: dict[str, str] | None = None, ) -> ThreeDModel | ThreeDModelList: """`Create new 3d models. `_ Args: - name (str | SequenceNotStr[str]): The name of the 3d model(s) to create. + name (str | ThreeDModelWrite | SequenceNotStr[str | ThreeDModelWrite]): The name of the 3d model(s) or 3D + model object to create. If a 3D model object is provided, the other arguments are ignored. data_set_id (int | None): The id of the dataset this 3D model belongs to. metadata (dict[str, str] | None): Custom, application-specific metadata. String key -> String value. Limits: Maximum length of key is 32 bytes, value 512 bytes, up to 16 key-value pairs. @@ -168,32 +172,40 @@ def create( >>> from cognite.client import CogniteClient >>> client = CogniteClient() >>> res = client.three_d.models.create(name="My Model", data_set_id=1, metadata={"key1": "value1", "key2": "value2"}) + + Create multiple new 3D Models:: + + >>> from cognite.client import CogniteClient + >>> from cognite.client.data_classes import ThreeDModelWrite + >>> client = CogniteClient() + >>> my_model = ThreeDModelWrite(name="My Model", data_set_id=1, metadata={"key1": "value1", "key2": "value2"}) + >>> my_other_model = ThreeDModelWrite(name="My Other Model", data_set_id=1, metadata={"key1": "value1", "key2": "value2"}) + >>> res = client.three_d.models.create([my_model, my_other_model]) + """ - assert_type(name, "name", [str, Sequence]) - item_processed: dict[str, Any] | list[dict[str, Any]] + items: ThreeDModelWrite | list[ThreeDModelWrite] if isinstance(name, str): - item_processed = {"name": name, "dataSetId": data_set_id, "metadata": metadata} + items = ThreeDModelWrite(name, data_set_id, metadata) + elif isinstance(name, ThreeDModelWrite): + items = name else: - item_processed = [{"name": n, "dataSetId": data_set_id, "metadata": metadata} for n in name] - return self._create_multiple(list_cls=ThreeDModelList, resource_cls=ThreeDModel, items=item_processed) + items = [ThreeDModelWrite(n, data_set_id, metadata) if isinstance(n, str) else n for n in name] + return self._create_multiple(list_cls=ThreeDModelList, resource_cls=ThreeDModel, items=items) @overload - def update(self, item: ThreeDModel | ThreeDModelWrite) -> ThreeDModel: ... + def update(self, item: ThreeDModel | ThreeDModelUpdate) -> ThreeDModel: ... @overload - def update(self, item: Sequence[ThreeDModel | ThreeDModelWrite]) -> ThreeDModelList: ... + def update(self, item: Sequence[ThreeDModel | ThreeDModelUpdate]) -> ThreeDModelList: ... def update( self, - item: ThreeDModel - | ThreeDModelWrite - | ThreeDModelUpdate - | Sequence[ThreeDModel | ThreeDModelWrite | ThreeDModelUpdate], + item: ThreeDModel | ThreeDModelUpdate | Sequence[ThreeDModel | ThreeDModelUpdate], ) -> ThreeDModel | ThreeDModelList: """`Update 3d models. `_ Args: - item (ThreeDModel | ThreeDModelWrite | ThreeDModelUpdate | Sequence[ThreeDModel | ThreeDModelWrite | ThreeDModelUpdate]): ThreeDModel(s) to update + item (ThreeDModel | ThreeDModelUpdate | Sequence[ThreeDModel | ThreeDModelUpdate]): ThreeDModel(s) to update Returns: ThreeDModel | ThreeDModelList: Updated ThreeDModel(s) @@ -217,6 +229,8 @@ def update( >>> res = client.three_d.models.update(my_update) """ + # Note that we cannot use the ThreeDModelWrite to update as the write format of a 3D model + # does not have ID or External ID, thus no identifier to know which model to update. return self._update_multiple( list_cls=ThreeDModelList, resource_cls=ThreeDModel, update_cls=ThreeDModelUpdate, items=item ) diff --git a/cognite/client/_version.py b/cognite/client/_version.py index 72ecbf0d1a..a96177f8f2 100644 --- a/cognite/client/_version.py +++ b/cognite/client/_version.py @@ -1,4 +1,4 @@ from __future__ import annotations -__version__ = "7.54.6" +__version__ = "7.54.7" __api_subversion__ = "20230101" diff --git a/pyproject.toml b/pyproject.toml index eeeb8357fd..67c9263baf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "cognite-sdk" -version = "7.54.6" +version = "7.54.7" description = "Cognite Python SDK" readme = "README.md" documentation = "https://cognite-sdk-python.readthedocs-hosted.com" diff --git a/tests/tests_integration/test_api/test_three_d.py b/tests/tests_integration/test_api/test_three_d.py index c341372d5c..d083a84954 100644 --- a/tests/tests_integration/test_api/test_three_d.py +++ b/tests/tests_integration/test_api/test_three_d.py @@ -1,6 +1,12 @@ +from __future__ import annotations + +from contextlib import suppress + import pytest -from cognite.client.data_classes import ThreeDModelRevisionUpdate, ThreeDModelUpdate +from cognite.client import CogniteClient +from cognite.client.data_classes import ThreeDModel, ThreeDModelRevisionUpdate, ThreeDModelUpdate, ThreeDModelWrite +from cognite.client.exceptions import CogniteAPIError @pytest.fixture(scope="class") @@ -11,7 +17,7 @@ def test_revision(cognite_client): @pytest.fixture(scope="class") -def new_model(cognite_client): +def new_model(cognite_client: CogniteClient) -> ThreeDModel: res = cognite_client.three_d.models.create(name="NewTestModel") yield res cognite_client.three_d.models.delete(id=res.id) @@ -31,6 +37,26 @@ def test_list_and_retrieve(self, cognite_client): res = next(r for r in cognite_client.three_d.models(limit=None) if r.name == "MyModel775") assert res == cognite_client.three_d.models.retrieve(res.id) + def test_create_update_delete(self, cognite_client) -> None: + my_model = ThreeDModelWrite(name="MyModel775", metadata={"key": "value"}) + + created: ThreeDModel | None = None + try: + created = cognite_client.three_d.models.create(my_model) + assert created.as_write().dump() == my_model.dump() + + update = ThreeDModelUpdate(id=created.id).metadata.add({"key2": "value2"}) + + updated = cognite_client.three_d.models.update(update) + + assert updated.metadata == {"key": "value", "key2": "value2"} + + cognite_client.three_d.models.delete(id=created.id) + finally: + if created: + with suppress(CogniteAPIError): + cognite_client.three_d.models.delete(id=created.id) + def test_update_with_resource(self, new_model, cognite_client): model = new_model model.name = "NewTestModelVer2" diff --git a/tests/tests_unit/test_api/test_3d.py b/tests/tests_unit/test_api/test_3d.py index 73442536f6..2bd0a71a83 100644 --- a/tests/tests_unit/test_api/test_3d.py +++ b/tests/tests_unit/test_api/test_3d.py @@ -99,7 +99,7 @@ def test_create(self, cognite_client, mock_3d_model_response): assert isinstance(res, ThreeDModel) request_body = jsgz_load(mock_3d_model_response.calls[0].request.body) - assert request_body == {"items": [{"dataSetId": None, "metadata": None, "name": "My Model"}]} + assert request_body == {"items": [{"name": "My Model"}]} assert mock_3d_model_response.calls[0].response.json()["items"][0] == res.dump(camel_case=True) def test_create_multiple(self, cognite_client, mock_3d_model_response): @@ -107,7 +107,7 @@ def test_create_multiple(self, cognite_client, mock_3d_model_response): assert isinstance(res, ThreeDModelList) request_body = jsgz_load(mock_3d_model_response.calls[0].request.body) - assert request_body == {"items": [{"dataSetId": None, "metadata": None, "name": "My Model"}]} + assert request_body == {"items": [{"name": "My Model"}]} assert mock_3d_model_response.calls[0].response.json()["items"] == res.dump(camel_case=True)