Skip to content

Commit

Permalink
InstanceID for TimeSeries (#1841)
Browse files Browse the repository at this point in the history
  • Loading branch information
doctrino authored Jul 11, 2024
1 parent d0b57a4 commit 1f95e27
Show file tree
Hide file tree
Showing 27 changed files with 225 additions and 59 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ Changes are grouped as follows
- `Security` in case of vulnerabilities.


## [7.53.3] - 2024-07-11
### Added
- [Feature Preview - alpha] Support for `instanceId` in the `client.time_series` `.retrieve`, `.retrieve_multiple`,
and `.update` methods. This is an experimental feature and may change without warning.

## [7.53.2] - 2024-07-03
### Fixed
- If you derived from `TypedNode` or `TypedEdge`, and then derived the `load` method would not include the parent
Expand Down
10 changes: 5 additions & 5 deletions cognite/client/_api/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from cognite.client.exceptions import CogniteAPIError, CogniteAuthorizationError, CogniteFileUploadError
from cognite.client.utils._auxiliary import find_duplicates
from cognite.client.utils._concurrency import execute_tasks
from cognite.client.utils._identifier import Identifier, IdentifierSequence
from cognite.client.utils._identifier import Identifier, IdentifierSequence, InstanceId
from cognite.client.utils._validation import process_asset_subtree_ids, process_data_set_ids
from cognite.client.utils.useful_types import SequenceNotStr

Expand Down Expand Up @@ -919,7 +919,7 @@ def download(
@staticmethod
def _get_ids_filepaths_directories(
directory: Path,
id_to_metadata: dict[str | int, FileMetadata],
id_to_metadata: dict[str | int | InstanceId, FileMetadata],
keep_directory_structure: bool = False,
) -> tuple[list[dict[str, str | int]], list[Path], list[Path]]:
# Note on type hint: Too much of the SDK is wrongly typed with 'dict[str, str | int]',
Expand Down Expand Up @@ -952,13 +952,13 @@ def _warn_on_duplicate_filenames(filepaths: list[Path]) -> None:
stacklevel=2,
)

def _get_id_to_metadata_map(self, all_ids: Sequence[dict]) -> dict[str | int, FileMetadata]:
def _get_id_to_metadata_map(self, all_ids: Sequence[dict]) -> dict[str | int | InstanceId, FileMetadata]:
ids = [id["id"] for id in all_ids if "id" in id]
external_ids = [id["externalId"] for id in all_ids if "externalId" in id]

files_metadata = self.retrieve_multiple(ids=ids, external_ids=external_ids)

id_to_metadata: dict[str | int, FileMetadata] = {}
id_to_metadata: dict[str | int | InstanceId, FileMetadata] = {}
for f in files_metadata:
id_to_metadata[f.id] = f
if f.external_id is not None:
Expand All @@ -970,7 +970,7 @@ def _download_files_to_directory(
self,
directory: Path,
all_ids: Sequence[dict[str, int | str]],
id_to_metadata: dict[str | int, FileMetadata],
id_to_metadata: dict[str | int | InstanceId, FileMetadata],
filepaths: list[Path],
) -> None:
self._warn_on_duplicate_filenames(filepaths)
Expand Down
36 changes: 33 additions & 3 deletions cognite/client/_api/time_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
filters,
)
from cognite.client.data_classes.aggregations import AggregationFilter, CountAggregate, UniqueResultList
from cognite.client.data_classes.data_modeling import NodeId
from cognite.client.data_classes.filters import _BASIC_FILTERS, Filter, _validate_filter
from cognite.client.data_classes.time_series import (
SortableTimeSeriesProperty,
TimeSeriesProperty,
TimeSeriesSort,
TimeSeriesWrite,
)
from cognite.client.utils._experimental import FeaturePreviewWarning
from cognite.client.utils._identifier import IdentifierSequence
from cognite.client.utils._validation import prepare_filter_sort, process_asset_subtree_ids, process_data_set_ids
from cognite.client.utils.useful_types import SequenceNotStr
Expand Down Expand Up @@ -200,12 +202,15 @@ def __iter__(self) -> Iterator[TimeSeries]:
"""
return self()

def retrieve(self, id: int | None = None, external_id: str | None = None) -> TimeSeries | None:
def retrieve(
self, id: int | None = None, external_id: str | None = None, instance_id: NodeId | None = None
) -> TimeSeries | None:
"""`Retrieve a single time series by id. <https://developer.cognite.com/api#tag/Time-series/operation/getTimeSeriesByIds>`_
Args:
id (int | None): ID
external_id (str | None): External ID
instance_id (NodeId | None): Instance ID
Returns:
TimeSeries | None: Requested time series or None if it does not exist.
Expand All @@ -224,25 +229,32 @@ def retrieve(self, id: int | None = None, external_id: str | None = None) -> Tim
>>> client = CogniteClient()
>>> res = client.time_series.retrieve(external_id="1")
"""
identifiers = IdentifierSequence.load(ids=id, external_ids=external_id).as_singleton()
headers: dict | None = None
if instance_id is not None:
self._warn_alpha()
headers = {"cdf-version": "alpha"}
identifiers = IdentifierSequence.load(ids=id, external_ids=external_id, instance_ids=instance_id).as_singleton()

return self._retrieve_multiple(
list_cls=TimeSeriesList,
resource_cls=TimeSeries,
identifiers=identifiers,
headers=headers,
)

def retrieve_multiple(
self,
ids: Sequence[int] | None = None,
external_ids: SequenceNotStr[str] | None = None,
instance_ids: Sequence[NodeId] | None = None,
ignore_unknown_ids: bool = False,
) -> TimeSeriesList:
"""`Retrieve multiple time series by id. <https://developer.cognite.com/api#tag/Time-series/operation/getTimeSeriesByIds>`_
Args:
ids (Sequence[int] | None): IDs
external_ids (SequenceNotStr[str] | None): External IDs
instance_ids (Sequence[NodeId] | None): Instance IDs
ignore_unknown_ids (bool): Ignore IDs and external IDs that are not found rather than throw an exception.
Returns:
Expand All @@ -262,12 +274,17 @@ def retrieve_multiple(
>>> client = CogniteClient()
>>> res = client.time_series.retrieve_multiple(external_ids=["abc", "def"])
"""
identifiers = IdentifierSequence.load(ids=ids, external_ids=external_ids)
header: dict | None = None
if instance_ids is not None:
self._warn_alpha()
header = {"cdf-version": "alpha"}
identifiers = IdentifierSequence.load(ids=ids, external_ids=external_ids, instance_ids=instance_ids)
return self._retrieve_multiple(
list_cls=TimeSeriesList,
resource_cls=TimeSeries,
identifiers=identifiers,
ignore_unknown_ids=ignore_unknown_ids,
headers=header,
)

def aggregate(self, filter: TimeSeriesFilter | dict[str, Any] | None = None) -> list[CountAggregate]:
Expand Down Expand Up @@ -546,6 +563,12 @@ def create(
input_resource_cls=TimeSeriesWrite,
)

@staticmethod
def _warn_alpha() -> None:
FeaturePreviewWarning(
api_maturity="alpha", feature_name="TimeSeries with Instance ID", sdk_maturity="alpha"
).warn()

def delete(
self,
id: int | Sequence[int] | None = None,
Expand Down Expand Up @@ -612,12 +635,19 @@ def update(
>>> my_update = TimeSeriesUpdate(id=1).description.set("New description").metadata.add({"key": "value"})
>>> res = client.time_series.update(my_update)
"""
headers: dict | None = None
if (isinstance(item, Sequence) and any(ts.instance_id for ts in item)) or (
not isinstance(item, Sequence) and item.instance_id
):
self._warn_alpha()
headers = {"cdf-version": "alpha"}

return self._update_multiple(
list_cls=TimeSeriesList,
resource_cls=TimeSeries,
update_cls=TimeSeriesUpdate,
items=item,
headers=headers,
)

@overload
Expand Down
8 changes: 6 additions & 2 deletions cognite/client/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ def _update_multiple(
for index, item in enumerate(item_list):
if isinstance(item, CogniteResource):
patch_objects.append(
self._convert_resource_to_patch_object(item, update_cls._get_update_properties(), mode)
self._convert_resource_to_patch_object(item, update_cls._get_update_properties(item), mode)
)
elif isinstance(item, CogniteUpdate):
patch_objects.append(item.dump(camel_case=True))
Expand Down Expand Up @@ -1124,9 +1124,13 @@ def _convert_resource_to_patch_object(
dumped_resource = resource.dump(camel_case=True)
has_id = "id" in dumped_resource
has_external_id = "externalId" in dumped_resource
has_instance_id = "instanceId" in dumped_resource

patch_object: dict[str, dict[str, dict]] = {"update": {}}
if has_id:
if has_instance_id:
patch_object["instanceId"] = dumped_resource.pop("instanceId")
dumped_resource.pop("id", None)
elif has_id:
patch_object["id"] = dumped_resource.pop("id")
elif has_external_id:
patch_object["externalId"] = dumped_resource.pop("externalId")
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.53.2"
__version__ = "7.53.3"
__api_subversion__ = "20230101"
2 changes: 1 addition & 1 deletion cognite/client/data_classes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ def dump(self, camel_case: Literal[True] = True) -> dict[str, Any]:

@classmethod
@abstractmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
raise NotImplementedError


Expand Down
3 changes: 2 additions & 1 deletion cognite/client/data_classes/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
CogniteFilter,
CogniteObjectUpdate,
CognitePrimitiveUpdate,
CogniteResource,
CogniteResourceList,
CogniteUpdate,
PropertySpec,
Expand Down Expand Up @@ -359,7 +360,7 @@ def annotation_type(self) -> AnnotationUpdate._StrUpdate:
return AnnotationUpdate._StrUpdate(self, "annotationType")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("data", is_nullable=False),
PropertySpec("status", is_nullable=False),
Expand Down
3 changes: 2 additions & 1 deletion cognite/client/data_classes/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
CogniteObject,
CogniteObjectUpdate,
CognitePrimitiveUpdate,
CogniteResource,
CogniteResourceList,
CogniteSort,
CogniteUpdate,
Expand Down Expand Up @@ -515,7 +516,7 @@ def geo_location(self) -> _PrimitiveAssetUpdate:
return AssetUpdate._PrimitiveAssetUpdate(self, "geoLocation")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
# External ID is nullable, but is used in the upsert logic and thus cannot be nulled out.
PropertySpec("external_id", is_nullable=False),
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/data_classes/contextualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def description(self) -> _PrimitiveUpdate:
return EntityMatchingModelUpdate._PrimitiveUpdate(self, "description")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("name", is_nullable=False),
PropertySpec("description", is_nullable=False),
Expand Down
3 changes: 2 additions & 1 deletion cognite/client/data_classes/data_sets.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
CogniteListUpdate,
CogniteObjectUpdate,
CognitePrimitiveUpdate,
CogniteResource,
CogniteResourceList,
CogniteUpdate,
ExternalIDTransformerMixin,
Expand Down Expand Up @@ -231,7 +232,7 @@ def write_protected(self) -> _PrimitiveDataSetUpdate:
return DataSetUpdate._PrimitiveDataSetUpdate(self, "writeProtected")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
# External ID is nullable, but is used in the upsert logic and thus cannot be nulled out.
PropertySpec("external_id", is_nullable=False),
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/data_classes/datapoints_subscriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def filter(self) -> _FilterDataPointSubscriptionUpdate:
return DataPointSubscriptionUpdate._FilterDataPointSubscriptionUpdate(self, "filter")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("name"),
PropertySpec("time_series_ids", is_container=True),
Expand Down
3 changes: 2 additions & 1 deletion cognite/client/data_classes/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
CogniteObject,
CogniteObjectUpdate,
CognitePrimitiveUpdate,
CogniteResource,
CogniteResourceList,
CogniteSort,
CogniteUpdate,
Expand Down Expand Up @@ -361,7 +362,7 @@ def subtype(self) -> _PrimitiveEventUpdate:
return EventUpdate._PrimitiveEventUpdate(self, "subtype")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
# External ID is nullable, but is used in the upsert logic and thus cannot be nulled out.
PropertySpec("external_id", is_nullable=False),
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/data_classes/extractionpipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ def contacts(self) -> _ListExtractionPipelineUpdate:
return ExtractionPipelineUpdate._ListExtractionPipelineUpdate(self, "contacts")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("external_id", is_nullable=False),
PropertySpec("name", is_nullable=False),
Expand Down
3 changes: 2 additions & 1 deletion cognite/client/data_classes/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
CogniteListUpdate,
CogniteObjectUpdate,
CognitePrimitiveUpdate,
CogniteResource,
CogniteResourceList,
CogniteUpdate,
ExternalIDTransformerMixin,
Expand Down Expand Up @@ -445,7 +446,7 @@ def security_categories(self) -> _ListFileMetadataUpdate:
return FileMetadataUpdate._ListFileMetadataUpdate(self, "securityCategories")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
# External ID is nullable, but is used in the upsert logic and thus cannot be nulled out.
PropertySpec("external_id", is_nullable=False),
Expand Down
3 changes: 2 additions & 1 deletion cognite/client/data_classes/relationships.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
CogniteFilter,
CogniteLabelUpdate,
CognitePrimitiveUpdate,
CogniteResource,
CogniteResourceList,
CogniteUpdate,
ExternalIDTransformerMixin,
Expand Down Expand Up @@ -400,7 +401,7 @@ def labels(self) -> _LabelRelationshipUpdate:
return RelationshipUpdate._LabelRelationshipUpdate(self, "labels")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("source_type", is_nullable=False),
PropertySpec("source_external_id", is_nullable=False),
Expand Down
4 changes: 2 additions & 2 deletions cognite/client/data_classes/sequences.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def metadata(self) -> _ObjectSequenceColumnUpdate:
return SequenceColumnUpdate._ObjectSequenceColumnUpdate(self, "metadata")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("description"),
PropertySpec("external_id", is_nullable=False),
Expand Down Expand Up @@ -578,7 +578,7 @@ def columns(self) -> _ColumnsSequenceUpdate:
return SequenceUpdate._ColumnsSequenceUpdate(self, "columns")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
# External ID is nullable, but is used in the upsert logic and thus cannot be nulled out.
PropertySpec("external_id", is_nullable=False),
Expand Down
2 changes: 1 addition & 1 deletion cognite/client/data_classes/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ def field_resolvers(self) -> _ObjectAssetUpdate:
return TemplateInstanceUpdate._ObjectAssetUpdate(self, "fieldResolvers")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("field_resolvers"),
]
Expand Down
4 changes: 2 additions & 2 deletions cognite/client/data_classes/three_d.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ def data_set_id(self) -> _PrimitiveThreeDModelUpdate:
return ThreeDModelUpdate._PrimitiveThreeDModelUpdate(self, "dataSetId")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("name", is_nullable=False),
PropertySpec("metadata", is_container=True),
Expand Down Expand Up @@ -464,7 +464,7 @@ def metadata(self) -> _ObjectThreeDModelRevisionUpdate:
return ThreeDModelRevisionUpdate._ObjectThreeDModelRevisionUpdate(self, "metadata")

@classmethod
def _get_update_properties(cls) -> list[PropertySpec]:
def _get_update_properties(cls, item: CogniteResource | None = None) -> list[PropertySpec]:
return [
PropertySpec("published", is_nullable=False),
PropertySpec("rotation", is_nullable=False),
Expand Down
Loading

0 comments on commit 1f95e27

Please sign in to comment.