Skip to content

Commit

Permalink
[CDF-22128] Model 3D Update/Create (#1857)
Browse files Browse the repository at this point in the history
  • Loading branch information
doctrino authored Jul 22, 2024
1 parent b63a5e3 commit 8d5f541
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 21 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 29 additions & 15 deletions cognite/client/_api/three_d.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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. <https://developer.cognite.com/api#tag/3D-Models/operation/create3DModels>`_
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.
Expand All @@ -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. <https://developer.cognite.com/api#tag/3D-Models/operation/update3DModels>`_
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)
Expand All @@ -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
)
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations

__version__ = "7.54.6"
__version__ = "7.54.7"
__api_subversion__ = "20230101"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
30 changes: 28 additions & 2 deletions tests/tests_integration/test_api/test_three_d.py
Original file line number Diff line number Diff line change
@@ -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")
Expand All @@ -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)
Expand All @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions tests/tests_unit/test_api/test_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,15 @@ 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):
res = cognite_client.three_d.models.create(name=["My Model"])
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)


Expand Down

0 comments on commit 8d5f541

Please sign in to comment.