cognite-neat 0.87.4__py3-none-any.whl → 0.87.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cognite-neat might be problematic. Click here for more details.

cognite/neat/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.87.4"
1
+ __version__ = "0.87.6"
@@ -166,7 +166,7 @@ def get_original_rules_from_workflow(workflow_name: str):
166
166
  return {"error": f"Workflow {workflow_name} is not found"}
167
167
  context = workflow.get_context()
168
168
  rules_data = context["RulesData"]
169
- if type(rules_data) != RulesData:
169
+ if type(rules_data) is not RulesData:
170
170
  return {"error": "RulesData is not found in workflow context"}
171
171
 
172
172
  return Response(content=rules_data.rules.model_dump_json(), media_type="application/json")
@@ -30,6 +30,8 @@ class AssetsExtractor(BaseExtractor):
30
30
  limit the extraction to 1000 assets to test the setup.
31
31
  unpack_metadata (bool, optional): Whether to unpack metadata. Defaults to False, which yields the metadata as
32
32
  a JSON string.
33
+ skip_metadata_values (set[str] | frozenset[str] | None, optional): A set of values to skip when unpacking
34
+ metadata. Defaults to frozenset({"nan", "null", "none", ""}).
33
35
  """
34
36
 
35
37
  _SPACE_PATTERN = re.compile(r"\s+")
@@ -42,6 +44,7 @@ class AssetsExtractor(BaseExtractor):
42
44
  total: int | None = None,
43
45
  limit: int | None = None,
44
46
  unpack_metadata: bool = True,
47
+ skip_metadata_values: set[str] | frozenset[str] | None = frozenset({"nan", "null", "none", ""}),
45
48
  ):
46
49
  self.namespace = namespace or DEFAULT_NAMESPACE
47
50
  self.assets = assets
@@ -49,6 +52,7 @@ class AssetsExtractor(BaseExtractor):
49
52
  self.total = total
50
53
  self.limit = min(limit, total) if limit and total else limit
51
54
  self.unpack_metadata = unpack_metadata
55
+ self.skip_metadata_values = skip_metadata_values
52
56
 
53
57
  @classmethod
54
58
  def from_dataset(
@@ -162,7 +166,7 @@ class AssetsExtractor(BaseExtractor):
162
166
  if asset.source:
163
167
  triples.append((id_, self.namespace.source, Literal(asset.source)))
164
168
 
165
- # properties ref creation and update
169
+ # properties' ref creation and update
166
170
  triples.append(
167
171
  (
168
172
  id_,
@@ -193,7 +197,9 @@ class AssetsExtractor(BaseExtractor):
193
197
  if asset.metadata:
194
198
  if self.unpack_metadata:
195
199
  for key, value in asset.metadata.items():
196
- if value:
200
+ if value and (
201
+ self.skip_metadata_values is None or value.casefold() not in self.skip_metadata_values
202
+ ):
197
203
  triples.append(
198
204
  (
199
205
  id_,
@@ -250,9 +250,9 @@ def _generate_mock_data_property_triples(
250
250
  python_type = value_type.python
251
251
  triples = []
252
252
  for id_ in instance_ids:
253
- if python_type == int:
253
+ if python_type is int:
254
254
  triples.append((id_, URIRef(namespace[property_]), Literal(random.randint(1, 1983))))
255
- elif python_type == float:
255
+ elif python_type is float:
256
256
  triples.append((id_, URIRef(namespace[property_]), Literal(numpy.float32(random.uniform(1, 1983)))))
257
257
  # generate string
258
258
  else:
@@ -48,18 +48,23 @@ class BaseLoader(ABC, Generic[T_Output]):
48
48
  class CDFLoader(BaseLoader[T_Output]):
49
49
  _UPLOAD_BATCH_SIZE: ClassVar[int] = 1000
50
50
 
51
- def load_into_cdf(self, client: CogniteClient, dry_run: bool = False) -> UploadResultList:
52
- return UploadResultList(self.load_into_cdf_iterable(client, dry_run))
53
-
54
- def load_into_cdf_iterable(self, client: CogniteClient, dry_run: bool = False) -> Iterable[UploadResult]:
55
- missing_capabilities = client.iam.verify_capabilities(self._get_required_capabilities())
56
- if missing_capabilities:
57
- upload_result = UploadResult[Hashable](name=type(self).__name__)
58
- upload_result.issues.append(
59
- FailedAuthorizationError(action="Upload to CDF", reason=str(missing_capabilities))
60
- )
61
- yield upload_result
62
- return
51
+ def load_into_cdf(
52
+ self, client: CogniteClient, dry_run: bool = False, check_client: bool = True
53
+ ) -> UploadResultList:
54
+ return UploadResultList(self.load_into_cdf_iterable(client, dry_run, check_client))
55
+
56
+ def load_into_cdf_iterable(
57
+ self, client: CogniteClient, dry_run: bool = False, check_client: bool = True
58
+ ) -> Iterable[UploadResult]:
59
+ if check_client:
60
+ missing_capabilities = client.iam.verify_capabilities(self._get_required_capabilities())
61
+ if missing_capabilities:
62
+ upload_result = UploadResult[Hashable](name=type(self).__name__)
63
+ upload_result.issues.append(
64
+ FailedAuthorizationError(action="Upload to CDF", reason=str(missing_capabilities))
65
+ )
66
+ yield upload_result
67
+ return
63
68
 
64
69
  issues = NeatIssueList[NeatIssue]()
65
70
  items: list[T_Output] = []
@@ -1,14 +1,21 @@
1
1
  import json
2
2
  from collections.abc import Iterable, Sequence
3
- from dataclasses import dataclass, fields
4
3
  from pathlib import Path
5
- from typing import cast
4
+ from typing import Any, cast
6
5
 
7
6
  import yaml
8
7
  from cognite.client import CogniteClient
9
- from cognite.client.data_classes import AssetWrite
10
- from cognite.client.data_classes.capabilities import AssetsAcl, Capability
11
- from cognite.client.exceptions import CogniteAPIError
8
+ from cognite.client.data_classes import (
9
+ AssetWrite,
10
+ LabelDefinitionWrite,
11
+ RelationshipWrite,
12
+ )
13
+ from cognite.client.data_classes.capabilities import (
14
+ AssetsAcl,
15
+ Capability,
16
+ RelationshipsAcl,
17
+ )
18
+ from cognite.client.exceptions import CogniteAPIError, CogniteDuplicatedError
12
19
 
13
20
  from cognite.neat.graph._tracking.base import Tracker
14
21
  from cognite.neat.graph._tracking.log import LogTracker
@@ -17,40 +24,15 @@ from cognite.neat.graph.stores import NeatGraphStore
17
24
  from cognite.neat.issues import NeatIssue, NeatIssueList
18
25
  from cognite.neat.rules.analysis._asset import AssetAnalysis
19
26
  from cognite.neat.rules.models import AssetRules
27
+ from cognite.neat.rules.models.entities import ClassEntity, EntityTypes
28
+ from cognite.neat.utils.auxiliary import create_sha256_hash
20
29
  from cognite.neat.utils.upload import UploadResult
21
30
 
22
31
  from ._base import _END_OF_CLASS, CDFLoader
23
32
 
24
33
 
25
- @dataclass(frozen=True)
26
- class AssetLoaderMetadataKeys:
27
- """Class holding mapping between NEAT metadata key names and their desired names
28
- in CDF Asset metadata
29
-
30
- Args:
31
- start_time: Start time key name
32
- end_time: End time key name
33
- update_time: Update time key name
34
- resurrection_time: Resurrection time key name
35
- identifier: Identifier key name
36
- active: Active key name
37
- type: Type key name
38
- """
39
-
40
- start_time: str = "start_time"
41
- end_time: str = "end_time"
42
- update_time: str = "update_time"
43
- resurrection_time: str = "resurrection_time"
44
- identifier: str = "identifier"
45
- active: str = "active"
46
- type: str = "type"
47
-
48
- def as_aliases(self) -> dict[str, str]:
49
- return {str(field.default): getattr(self, field.name) for field in fields(self)}
50
-
51
-
52
34
  class AssetLoader(CDFLoader[AssetWrite]):
53
- """Load Assets from NeatGraph to Cognite Data Fusions.
35
+ """Load Assets and their relationships from NeatGraph to Cognite Data Fusions.
54
36
 
55
37
  Args:
56
38
  graph_store (NeatGraphStore): The graph store to load the data into.
@@ -59,10 +41,7 @@ class AssetLoader(CDFLoader[AssetWrite]):
59
41
  use_orphanage (bool): Whether to use an orphanage for assets that are not part
60
42
  of the hierarchy. Defaults to False.
61
43
  use_labels (bool): Whether to use labels for assets. Defaults to False.
62
- asset_external_id_prefix (str | None): The prefix to use for the external id of the assets.
63
- Defaults to None.
64
- metadata_keys (AssetLoaderMetadataKeys | None): Mapping between NEAT metadata key names and
65
- their desired names in CDF Asset metadata. Defaults to None.
44
+ external_id_prefix (str | None): The prefix to use for the external ids. Defaults to None.
66
45
  create_issues (Sequence[NeatIssue] | None): A list of issues that occurred during reading. Defaults to None.
67
46
  tracker (type[Tracker] | None): The tracker to use. Defaults to None.
68
47
  """
@@ -74,8 +53,7 @@ class AssetLoader(CDFLoader[AssetWrite]):
74
53
  data_set_id: int,
75
54
  use_orphanage: bool = False,
76
55
  use_labels: bool = False,
77
- asset_external_id_prefix: str | None = None,
78
- metadata_keys: AssetLoaderMetadataKeys | None = None,
56
+ external_id_prefix: str | None = None,
79
57
  create_issues: Sequence[NeatIssue] | None = None,
80
58
  tracker: type[Tracker] | None = None,
81
59
  ):
@@ -83,15 +61,14 @@ class AssetLoader(CDFLoader[AssetWrite]):
83
61
 
84
62
  self.rules = rules
85
63
  self.data_set_id = data_set_id
64
+
86
65
  self.use_labels = use_labels
87
66
 
88
67
  self.orphanage = (
89
68
  AssetWrite.load(
90
69
  {
91
70
  "dataSetId": self.data_set_id,
92
- "externalId": (
93
- f"{asset_external_id_prefix or ''}orphanage-{data_set_id}" if use_orphanage else None
94
- ),
71
+ "externalId": (f"{external_id_prefix or ''}orphanage-{data_set_id}" if use_orphanage else None),
95
72
  "name": "Orphanage",
96
73
  "description": "Orphanage for assets whose parents do not exist",
97
74
  }
@@ -100,9 +77,9 @@ class AssetLoader(CDFLoader[AssetWrite]):
100
77
  else None
101
78
  )
102
79
 
103
- self.asset_external_id_prefix = asset_external_id_prefix
104
- self.metadata_keys = metadata_keys or AssetLoaderMetadataKeys()
80
+ self.external_id_prefix = external_id_prefix
105
81
 
82
+ self.processed_assets: set[str] = set()
106
83
  self._issues = NeatIssueList[NeatIssue](create_issues or [])
107
84
  self._tracker: type[Tracker] = tracker or LogTracker
108
85
 
@@ -124,27 +101,50 @@ class AssetLoader(CDFLoader[AssetWrite]):
124
101
  "classes",
125
102
  )
126
103
 
127
- processed_instances = set()
104
+ if self.use_labels:
105
+ yield from self._create_labels()
128
106
 
129
107
  if self.orphanage:
130
108
  yield self.orphanage
131
- processed_instances.add(self.orphanage.external_id)
109
+ self.processed_assets.add(cast(str, self.orphanage.external_id))
110
+
111
+ yield from self._create_assets(ordered_classes, tracker, stop_on_exception)
112
+ yield from self._create_relationship(ordered_classes, tracker, stop_on_exception)
113
+
114
+ def _create_labels(self) -> Iterable[Any]:
115
+ for label in AssetAnalysis(self.rules).define_labels():
116
+ yield LabelDefinitionWrite(name=label, external_id=label, data_set_id=self.data_set_id)
117
+ yield _END_OF_CLASS
132
118
 
119
+ def _create_assets(
120
+ self,
121
+ ordered_classes: list[ClassEntity],
122
+ tracker: Tracker,
123
+ stop_on_exception: bool,
124
+ ) -> Iterable[Any]:
133
125
  for class_ in ordered_classes:
134
126
  tracker.start(repr(class_.id))
135
127
 
136
- property_renaming_config = AssetAnalysis(self.rules).define_property_renaming_config(class_)
128
+ property_renaming_config = AssetAnalysis(self.rules).define_asset_property_renaming_config(class_)
137
129
 
138
130
  for identifier, properties in self.graph_store.read(class_.suffix):
139
- fields = _process_properties(properties, property_renaming_config)
131
+ identifier = f"{self.external_id_prefix or ''}{identifier}"
132
+
133
+ fields = _process_asset_properties(properties, property_renaming_config)
140
134
  # set data set id and external id
141
135
  fields["dataSetId"] = self.data_set_id
142
136
  fields["externalId"] = identifier
143
137
 
138
+ if self.use_labels:
139
+ fields["labels"] = [class_.suffix]
140
+
141
+ if parent_external_id := fields.get("parentExternalId", None):
142
+ fields["parentExternalId"] = f"{self.external_id_prefix or ''}{parent_external_id}"
143
+
144
144
  # check on parent
145
- if "parentExternalId" in fields and fields["parentExternalId"] not in processed_instances:
145
+ if "parentExternalId" in fields and fields["parentExternalId"] not in self.processed_assets:
146
146
  error = loader_issues.InvalidInstanceError(
147
- type_="asset",
147
+ type_=EntityTypes.asset,
148
148
  identifier=identifier,
149
149
  reason=(
150
150
  f"Parent asset {fields['parentExternalId']} does not exist or failed creation"
@@ -169,9 +169,11 @@ class AssetLoader(CDFLoader[AssetWrite]):
169
169
 
170
170
  try:
171
171
  yield AssetWrite.load(fields)
172
- processed_instances.add(identifier)
172
+ self.processed_assets.add(identifier)
173
173
  except KeyError as e:
174
- error = loader_issues.InvalidInstanceError(type_="asset", identifier=identifier, reason=str(e))
174
+ error = loader_issues.InvalidInstanceError(
175
+ type_=EntityTypes.asset, identifier=identifier, reason=str(e)
176
+ )
175
177
  tracker.issue(error)
176
178
  if stop_on_exception:
177
179
  raise error.as_exception() from e
@@ -179,6 +181,87 @@ class AssetLoader(CDFLoader[AssetWrite]):
179
181
 
180
182
  yield _END_OF_CLASS
181
183
 
184
+ def _create_relationship(
185
+ self,
186
+ ordered_classes: list[ClassEntity],
187
+ tracker: Tracker,
188
+ stop_on_exception: bool,
189
+ ) -> Iterable[Any]:
190
+ for class_ in ordered_classes:
191
+ tracker.start(repr(class_.id))
192
+
193
+ property_renaming_config = AssetAnalysis(self.rules).define_relationship_property_renaming_config(class_)
194
+
195
+ # class does not have any relationship properties
196
+ if not property_renaming_config:
197
+ continue
198
+
199
+ for source_external_id, properties in self.graph_store.read(class_.suffix):
200
+ relationships = _process_relationship_properties(properties, property_renaming_config)
201
+
202
+ source_external_id = f"{self.external_id_prefix or ''}{source_external_id}"
203
+
204
+ # check if source asset exists
205
+ if source_external_id not in self.processed_assets:
206
+ error = loader_issues.InvalidInstanceError(
207
+ type_=EntityTypes.relationship,
208
+ identifier=source_external_id,
209
+ reason=(
210
+ f"Asset {source_external_id} does not exist! "
211
+ "Aborting creation of relationships which use this asset as the source."
212
+ ),
213
+ )
214
+ tracker.issue(error)
215
+ if stop_on_exception:
216
+ raise error.as_exception()
217
+ yield error
218
+ continue
219
+
220
+ for label, target_external_ids in relationships.items():
221
+ # we can have 1-many relationships
222
+ for target_external_id in target_external_ids:
223
+ target_external_id = f"{self.external_id_prefix or ''}{target_external_id}"
224
+ # check if source asset exists
225
+ if target_external_id not in self.processed_assets:
226
+ error = loader_issues.InvalidInstanceError(
227
+ type_=EntityTypes.relationship,
228
+ identifier=target_external_id,
229
+ reason=(
230
+ f"Asset {target_external_id} does not exist! "
231
+ f"Cannot create relationship between {source_external_id}"
232
+ f" and {target_external_id}. "
233
+ ),
234
+ )
235
+ tracker.issue(error)
236
+ if stop_on_exception:
237
+ raise error.as_exception()
238
+ yield error
239
+ continue
240
+
241
+ external_id = "relationship_" + create_sha256_hash(f"{source_external_id}_{target_external_id}")
242
+ try:
243
+ yield RelationshipWrite(
244
+ external_id=external_id,
245
+ source_external_id=source_external_id,
246
+ target_external_id=target_external_id,
247
+ source_type="asset",
248
+ target_type="asset",
249
+ data_set_id=self.data_set_id,
250
+ labels=[label] if self.use_labels else None,
251
+ )
252
+ except KeyError as e:
253
+ error = loader_issues.InvalidInstanceError(
254
+ type_=EntityTypes.relationship,
255
+ identifier=external_id,
256
+ reason=str(e),
257
+ )
258
+ tracker.issue(error)
259
+ if stop_on_exception:
260
+ raise error.as_exception() from e
261
+ yield error
262
+
263
+ yield _END_OF_CLASS
264
+
182
265
  def _get_required_capabilities(self) -> list[Capability]:
183
266
  return [
184
267
  AssetsAcl(
@@ -187,10 +270,58 @@ class AssetLoader(CDFLoader[AssetWrite]):
187
270
  AssetsAcl.Action.Read,
188
271
  ],
189
272
  scope=AssetsAcl.Scope.DataSet([self.data_set_id]),
190
- )
273
+ ),
274
+ RelationshipsAcl(
275
+ actions=[
276
+ RelationshipsAcl.Action.Write,
277
+ RelationshipsAcl.Action.Read,
278
+ ],
279
+ scope=RelationshipsAcl.Scope.DataSet([self.data_set_id]),
280
+ ),
191
281
  ]
192
282
 
193
283
  def _upload_to_cdf(
284
+ self,
285
+ client: CogniteClient,
286
+ items: list[AssetWrite] | list[RelationshipWrite] | list[LabelDefinitionWrite],
287
+ dry_run: bool,
288
+ read_issues: NeatIssueList,
289
+ ) -> Iterable[UploadResult]:
290
+ if isinstance(items[0], AssetWrite) and all(isinstance(item, AssetWrite) for item in items):
291
+ yield from self._upload_assets_to_cdf(client, cast(list[AssetWrite], items), dry_run, read_issues)
292
+ elif isinstance(items[0], RelationshipWrite) and all(isinstance(item, RelationshipWrite) for item in items):
293
+ yield from self._upload_relationships_to_cdf(
294
+ client, cast(list[RelationshipWrite], items), dry_run, read_issues
295
+ )
296
+ elif isinstance(items[0], LabelDefinitionWrite) and all(
297
+ isinstance(item, LabelDefinitionWrite) for item in items
298
+ ):
299
+ yield from self._upload_labels_to_cdf(client, cast(list[LabelDefinitionWrite], items), dry_run, read_issues)
300
+ else:
301
+ raise ValueError(f"Item {items[0]} is not supported. This is a bug in neat please report it.")
302
+
303
+ def _upload_labels_to_cdf(
304
+ self,
305
+ client: CogniteClient,
306
+ items: list[LabelDefinitionWrite],
307
+ dry_run: bool,
308
+ read_issues: NeatIssueList,
309
+ ) -> Iterable[UploadResult]:
310
+ try:
311
+ created = client.labels.create(items)
312
+ except (CogniteAPIError, CogniteDuplicatedError) as e:
313
+ result = UploadResult[str](name="Label", issues=read_issues)
314
+ result.error_messages.append(str(e))
315
+ result.failed_created.update(item.external_id for item in e.failed + e.unknown)
316
+ result.created.update(item.external_id for item in e.successful)
317
+ yield result
318
+ else:
319
+ for label in created:
320
+ result = UploadResult[str](name="Label", issues=read_issues)
321
+ result.upserted.add(cast(str, label.external_id))
322
+ yield result
323
+
324
+ def _upload_assets_to_cdf(
194
325
  self,
195
326
  client: CogniteClient,
196
327
  items: list[AssetWrite],
@@ -202,22 +333,44 @@ class AssetLoader(CDFLoader[AssetWrite]):
202
333
  except CogniteAPIError as e:
203
334
  result = UploadResult[str](name="Asset", issues=read_issues)
204
335
  result.error_messages.append(str(e))
205
- result.failed_upserted.update(item.as_id() for item in e.failed + e.unknown)
206
- result.upserted.update(item.as_id() for item in e.successful)
336
+ result.failed_upserted.update(item.external_id for item in e.failed + e.unknown)
337
+ result.upserted.update(item.external_id for item in e.successful)
207
338
  yield result
208
339
  else:
209
340
  for asset in upserted:
210
- result = UploadResult[str](name="asset", issues=read_issues)
341
+ result = UploadResult[str](name="Asset", issues=read_issues)
211
342
  result.upserted.add(cast(str, asset.external_id))
212
343
  yield result
213
344
 
345
+ def _upload_relationships_to_cdf(
346
+ self,
347
+ client: CogniteClient,
348
+ items: list[RelationshipWrite],
349
+ dry_run: bool,
350
+ read_issues: NeatIssueList,
351
+ ) -> Iterable[UploadResult]:
352
+ try:
353
+ upserted = client.relationships.upsert(items, mode="replace")
354
+ except CogniteAPIError as e:
355
+ result = UploadResult[str](name="Relationship", issues=read_issues)
356
+ result.error_messages.append(str(e))
357
+ result.failed_upserted.update(item.external_id for item in e.failed + e.unknown)
358
+ result.upserted.update(item.external_id for item in e.successful)
359
+ yield result
360
+ else:
361
+ for relationship in upserted:
362
+ result = UploadResult[str](name="relationship", issues=read_issues)
363
+ result.upserted.add(cast(str, relationship.external_id))
364
+ yield result
365
+
214
366
  def write_to_file(self, filepath: Path) -> None:
215
367
  if filepath.suffix not in [".json", ".yaml", ".yml"]:
216
368
  raise ValueError(f"File format {filepath.suffix} is not supported")
217
- dumped: dict[str, list] = {"assets": []}
369
+ dumped: dict[str, list] = {"assets": [], "relationship": []}
218
370
  for item in self.load(stop_on_exception=False):
219
371
  key = {
220
372
  AssetWrite: "assets",
373
+ RelationshipWrite: "relationship",
221
374
  NeatIssue: "issues",
222
375
  _END_OF_CLASS: "end_of_class",
223
376
  }.get(type(item))
@@ -234,7 +387,7 @@ class AssetLoader(CDFLoader[AssetWrite]):
234
387
  yaml.safe_dump(dumped, f, sort_keys=False)
235
388
 
236
389
 
237
- def _process_properties(properties: dict[str, list[str]], property_renaming_config: dict[str, str]) -> dict:
390
+ def _process_asset_properties(properties: dict[str, list[str]], property_renaming_config: dict[str, str]) -> dict:
238
391
  metadata: dict[str, str] = {}
239
392
  fields: dict[str, str | dict] = {}
240
393
 
@@ -251,3 +404,15 @@ def _process_properties(properties: dict[str, list[str]], property_renaming_conf
251
404
  fields["metadata"] = metadata
252
405
 
253
406
  return fields
407
+
408
+
409
+ def _process_relationship_properties(
410
+ properties: dict[str, list[str]], property_renaming_config: dict[str, str]
411
+ ) -> dict:
412
+ relationships: dict[str, list[str]] = {}
413
+
414
+ for original_property, values in properties.items():
415
+ if renamed_property := property_renaming_config.get(original_property, None):
416
+ relationships[renamed_property] = values
417
+
418
+ return relationships
@@ -34,7 +34,7 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
34
34
  data_model (dm.DataModel[dm.View] | None): The data model to load.
35
35
  instance_space (str): The instance space to load the data into.
36
36
  class_by_view_id (dict[ViewId, str] | None): A mapping from view id to class name. Defaults to None.
37
- creat_issues (Sequence[NeatIssue] | None): A list of issues that occurred during reading. Defaults to None.
37
+ create_issues (Sequence[NeatIssue] | None): A list of issues that occurred during reading. Defaults to None.
38
38
  tracker (type[Tracker] | None): The tracker to use. Defaults to None.
39
39
  """
40
40
 
@@ -241,9 +241,9 @@ def _generate_mock_data_property_triples(
241
241
  python_type = XSD_VALUE_TYPE_MAPPINGS[value_type].python
242
242
  triples = []
243
243
  for id_ in instance_ids:
244
- if python_type == int:
244
+ if python_type is int:
245
245
  triples.append((id_, URIRef(namespace[property_]), Literal(random.randint(1, 1983))))
246
- elif python_type == float:
246
+ elif python_type is float:
247
247
  triples.append((id_, URIRef(namespace[property_]), Literal(numpy.float32(random.uniform(1, 1983)))))
248
248
  # generate string
249
249
  else:
@@ -886,7 +886,7 @@ class Property(Resource):
886
886
  if self.property_type == "DatatypeProperty" and self.default:
887
887
  default_value = self.default[0] if isinstance(self.default, list) else self.default
888
888
 
889
- if type(default_value) != self.expected_value_type.python:
889
+ if type(default_value) is not self.expected_value_type.python:
890
890
  try:
891
891
  if isinstance(self.default, list):
892
892
  updated_list = []
@@ -128,7 +128,7 @@ class AssetAnalysis(BaseAnalysis[AssetRules, AssetClass, AssetProperty, ClassEnt
128
128
  implementation_type=EntityTypes.relationship,
129
129
  )
130
130
 
131
- def define_property_renaming_config(self, class_: ClassEntity) -> dict[str, str]:
131
+ def define_asset_property_renaming_config(self, class_: ClassEntity) -> dict[str, str]:
132
132
  property_renaming_configuration = {}
133
133
 
134
134
  if asset_definition := self.asset_definition().get(class_, None):
@@ -141,3 +141,33 @@ class AssetAnalysis(BaseAnalysis[AssetRules, AssetClass, AssetProperty, ClassEnt
141
141
  property_renaming_configuration[property_] = f"{asset_property}.{property_}"
142
142
 
143
143
  return property_renaming_configuration
144
+
145
+ def define_relationship_property_renaming_config(self, class_: ClassEntity) -> dict[str, str]:
146
+ property_renaming_configuration = {}
147
+
148
+ if relationship_definition := self.relationship_definition().get(class_, None):
149
+ for property_, transformation in relationship_definition.items():
150
+ relationship = cast(list[RelationshipEntity], transformation.implementation)[0]
151
+
152
+ if relationship.label:
153
+ property_renaming_configuration[property_] = relationship.label
154
+ else:
155
+ property_renaming_configuration[property_] = property_
156
+
157
+ return property_renaming_configuration
158
+
159
+ def define_labels(self) -> set:
160
+ labels = set()
161
+
162
+ for _, properties in AssetAnalysis(self.rules).relationship_definition().items():
163
+ for property_, definition in properties.items():
164
+ labels.add(
165
+ cast(RelationshipEntity, definition.implementation[0]).label
166
+ if cast(RelationshipEntity, definition.implementation[0]).label
167
+ else property_
168
+ )
169
+
170
+ for class_ in AssetAnalysis(self.rules).asset_definition().keys():
171
+ labels.add(class_.suffix)
172
+
173
+ return labels
@@ -216,7 +216,7 @@ class InformationProperty(SheetEntity):
216
216
  if self.type_ == EntityTypes.data_property and self.default:
217
217
  default_value = self.default[0] if isinstance(self.default, list) else self.default
218
218
 
219
- if type(default_value) != self.value_type.python:
219
+ if type(default_value) is not self.value_type.python:
220
220
  try:
221
221
  if isinstance(self.default, list):
222
222
  updated_list = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.87.4
3
+ Version: 0.87.6
4
4
  Summary: Knowledge graph transformation
5
5
  Home-page: https://cognite-neat.readthedocs-hosted.com/
6
6
  License: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  cognite/neat/__init__.py,sha256=AiexNcHdAHFbrrbo9c65gtil1dqx_SGraDH1PSsXjKE,126
2
2
  cognite/neat/_shared.py,sha256=RSaHm2eJceTlvb-hMMe4nHgoHdPYDfN3XcxDXo24k3A,1530
3
- cognite/neat/_version.py,sha256=OfryV3sOjTF59UqStdnBWEoxVFY6XvFYyQtNvH5uSf8,23
3
+ cognite/neat/_version.py,sha256=zHK3D8i3mdcm2cU252645u9BQTB66eyTBLNGSit98n4,23
4
4
  cognite/neat/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  cognite/neat/app/api/asgi/metrics.py,sha256=nxFy7L5cChTI0a-zkCiJ59Aq8yLuIJp5c9Dg0wRXtV0,152
6
6
  cognite/neat/app/api/configuration.py,sha256=L1DCtLZ1HZku8I2z-JWd5RDsXhIsboFsKwAMhkrm-bY,3600
@@ -14,7 +14,7 @@ cognite/neat/app/api/routers/core.py,sha256=WznuQHzGkhBzf_GrHFiaSCXewBiQcrT4O6kY
14
14
  cognite/neat/app/api/routers/crud.py,sha256=VCQMjvcLosELkmMZRiunyd-RK5sltQFjcCSrGk5kwXc,4597
15
15
  cognite/neat/app/api/routers/data_exploration.py,sha256=xTE2nK8qfnG7iqIYa4KaLByi_EZPpD6NXQg5HQtqDcg,13703
16
16
  cognite/neat/app/api/routers/metrics.py,sha256=S_bUQk_GjfQq7WbEhSVdow4MUYBZ_bZNafzgcKogXK8,210
17
- cognite/neat/app/api/routers/rules.py,sha256=MECSzurNe7y07-GfsggBDmfNtYkFuLdAm9ogSjLbzzk,8121
17
+ cognite/neat/app/api/routers/rules.py,sha256=olITHs56xyGz7Lo7vOQWH3ju5iHtpy86vh4NzbVuxhQ,8125
18
18
  cognite/neat/app/api/routers/workflows.py,sha256=fPqNT0swH-sfcjD8PLK5NzKY1sVSU7GaCMkHfH78gxw,12393
19
19
  cognite/neat/app/api/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  cognite/neat/app/api/utils/data_mapping.py,sha256=ocqRyeCLbk3gS1NrQQnDr0w-q-xbkqV60uLZzsJIdyE,564
@@ -57,7 +57,7 @@ cognite/neat/graph/exceptions.py,sha256=R6pyOH774n9w2x_X_nrUr8OMAdjJMf_XPIqAvxIQ
57
57
  cognite/neat/graph/extractors/__init__.py,sha256=nXcNp6i3-1HteIkr8Ujxk4b09W5jk27Q3eWuwjcnGnM,1647
58
58
  cognite/neat/graph/extractors/_base.py,sha256=8IWygpkQTwo0UOmbbwWVI7540_klTVdUVX2JjVPFRIs,498
59
59
  cognite/neat/graph/extractors/_classic_cdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
- cognite/neat/graph/extractors/_classic_cdf/_assets.py,sha256=3rhQU_jabVkfBy05BHYS-mbQzCEN_fkeZiTUzj3qeyM,7854
60
+ cognite/neat/graph/extractors/_classic_cdf/_assets.py,sha256=saRtiT-TPvp3pzm-PBfyXf-bCWEEjhwWUel5SBesUvg,8344
61
61
  cognite/neat/graph/extractors/_classic_cdf/_events.py,sha256=_XhfNcDVrxhPsQD6jgh3OZVhyzF-bjjrW3onePBUU-A,5113
62
62
  cognite/neat/graph/extractors/_classic_cdf/_files.py,sha256=lP_y1HmF5rQ0Ufz4KL0fyPvM02Qv9Rvu6jwBZKyAsBY,6290
63
63
  cognite/neat/graph/extractors/_classic_cdf/_labels.py,sha256=Cd2l_WWFypryTc3zB8z5upgxh4BVf-vWSH2G7plh9lo,3015
@@ -65,14 +65,14 @@ cognite/neat/graph/extractors/_classic_cdf/_relationships.py,sha256=zpJlRbx1Jx5J
65
65
  cognite/neat/graph/extractors/_classic_cdf/_sequences.py,sha256=K6A_vZ_3HNI-51DSB23y9hhVajxCdkQKw3gv1TXIyuA,4681
66
66
  cognite/neat/graph/extractors/_classic_cdf/_timeseries.py,sha256=5n73uLcA5l30zGZuPq6xSs6hti1Al2qnj3c3q7zkQQg,6126
67
67
  cognite/neat/graph/extractors/_dexpi.py,sha256=Q3whJpEi3uFMzJGAAeUfgRnAzz6ZHmtuEdVBWqsZsTM,9384
68
- cognite/neat/graph/extractors/_mock_graph_generator.py,sha256=UNSAkspuQTA8uTR65BDgwzU89NJ1k0NkJLwwLatGmFk,14678
68
+ cognite/neat/graph/extractors/_mock_graph_generator.py,sha256=2qHh3fJxxAR9QdR6TPu-6DOvcjJkjHF4bwZN7lulziY,14678
69
69
  cognite/neat/graph/extractors/_rdf_file.py,sha256=ialMCLv9WH5k6v1YMfozfcmAYhz8OVo9jVhsKMyQkDA,763
70
70
  cognite/neat/graph/issues/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  cognite/neat/graph/issues/loader.py,sha256=v8YDsehkUT1QUG61JM9BDV_lqowMUnDmGmbay0aFzN4,3085
72
72
  cognite/neat/graph/loaders/__init__.py,sha256=TbeJqifd16JLOglPVNOeb6pN_w060UYag50KquBM_r0,769
73
- cognite/neat/graph/loaders/_base.py,sha256=p58Lb0yBDQpxhpo7tfLiyPxSB0TDoqlo6WKBWC-huRw,3498
74
- cognite/neat/graph/loaders/_rdf2asset.py,sha256=n83OW2P0sH54gAtcdVj2yiBrX_W4sjRiANvzwD7ShyY,10125
75
- cognite/neat/graph/loaders/_rdf2dms.py,sha256=HxfHmUnkDPaQWyEyEqmqI7xGBNwJY8XmcWALaOMinNk,14595
73
+ cognite/neat/graph/loaders/_base.py,sha256=497Jc1Gu3SmBpafUNTQ6fCAJz6dKq8HkweecdUmDsu4,3651
74
+ cognite/neat/graph/loaders/_rdf2asset.py,sha256=CqSXDcF4gVPSvE_9jy7yyuaCv-8HA9g0ld_2hqhnXug,17793
75
+ cognite/neat/graph/loaders/_rdf2dms.py,sha256=7HdFAVMORLaHIWzxKbm1DDGi45TcvqxXZYXYnggLYBI,14596
76
76
  cognite/neat/graph/models.py,sha256=AtLgZh2qyRP6NRetjQCy9qLMuTQB0CH52Zsev-qa2sk,149
77
77
  cognite/neat/graph/queries/__init__.py,sha256=BgDd-037kvtWwAoGAy8eORVNMiZ5-E9sIV0txIpeaN4,50
78
78
  cognite/neat/graph/queries/_base.py,sha256=82WJMT2SreLCKq00hujrk2H5W_w_Rq53bJQTK7E_Bz4,8500
@@ -98,7 +98,7 @@ cognite/neat/legacy/graph/extractors/__init__.py,sha256=wqCiqz-sXhUpTL5LRcrl_KFT
98
98
  cognite/neat/legacy/graph/extractors/_base.py,sha256=ohiuEzwZ1Fh9ers07MCbjGOGQ0HLb-ctwgEvGy7o_AQ,363
99
99
  cognite/neat/legacy/graph/extractors/_dexpi.py,sha256=R4itgYxWUiPlZgRqUB4IrmLKzpsxhHrxSv7Gq4PeodM,1469
100
100
  cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py,sha256=sEV6P4a4OlFx_O-vtKoB1H-ex1RnD5VhqmBcazGfLwk,17695
101
- cognite/neat/legacy/graph/extractors/_mock_graph_generator.py,sha256=Df_9IxlLq2j9i3FBA2s-ybRKv7wXDpvIh5kNv5qQyxw,14955
101
+ cognite/neat/legacy/graph/extractors/_mock_graph_generator.py,sha256=rBWiQGPsEBmI31UNU3v8ZoJO8BvY2ybGUualCGjemxE,14955
102
102
  cognite/neat/legacy/graph/loaders/__init__.py,sha256=Nap1LGaXA3AotL5U6pwY1Yc3J92Y7GC60qVPBhGl5LA,701
103
103
  cognite/neat/legacy/graph/loaders/_asset_loader.py,sha256=mFYIF7bzwsV7RcEoNgObW5NRj-as7XUJBs97QpogGRU,23873
104
104
  cognite/neat/legacy/graph/loaders/_base.py,sha256=oj1vo8dGPXxJORQXPa_kJa5G53VZBXOiIoWOngtMw5E,2383
@@ -167,7 +167,7 @@ cognite/neat/legacy/rules/models/__init__.py,sha256=23T73EaHuS0dsYTh6tww6gXAc7S4
167
167
  cognite/neat/legacy/rules/models/_base.py,sha256=1WNXBJHJ3nwnVoeNhpm9B6TBuqWYYgChkrdK4FJIpQM,4989
168
168
  cognite/neat/legacy/rules/models/raw_rules.py,sha256=o7iPhV7sS7h22edcyOpvJAQFPE7PiTULZAmGmZtKZPI,12491
169
169
  cognite/neat/legacy/rules/models/rdfpath.py,sha256=iXEPnx0rdOzkJ68FoJmsWQBbMBABenQ_cyDlsqwPCBg,7351
170
- cognite/neat/legacy/rules/models/rules.py,sha256=p4Ul8Yuz-ol3PMaxHQ87bwykFEtC1N400iZm0o2wkYo,51367
170
+ cognite/neat/legacy/rules/models/rules.py,sha256=yBtUwpsd3CDJkTj6jBFPOTND-Q-wZkPiBX70b_2_4GU,51371
171
171
  cognite/neat/legacy/rules/models/tables.py,sha256=hj7qjjIpwDXBnkVQrL47V2kjFxDz7aE4mkYOZTwepj8,171
172
172
  cognite/neat/legacy/rules/models/value_types.py,sha256=wAjYe0yv90FgKqMjJITxyg4QgCYaolFVM9WAr7g0bJY,4402
173
173
  cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml,sha256=dViI5aotf6cvcsePXjDBB_osyw1IUbNu2-rsGS1-5NM,1898
@@ -184,7 +184,7 @@ cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
184
184
  cognite/neat/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
185
185
  cognite/neat/rules/_shared.py,sha256=G0bIu5XSrtEa93qqtOS5P0IDJUkU32gX5ypFhcV6t5c,216
186
186
  cognite/neat/rules/analysis/__init__.py,sha256=1qQXgddwSmRjC5s22XfQhsn8XPYfwAV_2n6lqeWJmKc,141
187
- cognite/neat/rules/analysis/_asset.py,sha256=UvBl8vM83azkCI2As97xAfXe33nNqt4QSCmc5KvcZ-4,6044
187
+ cognite/neat/rules/analysis/_asset.py,sha256=SCiDatwDPi80jj41yQAp740irRWijMKEtAObkwKOKYs,7322
188
188
  cognite/neat/rules/analysis/_base.py,sha256=jb9G2g59QaSZDwJmG1Kh9rq7CprvyrokTqfMytr0ZOk,16758
189
189
  cognite/neat/rules/analysis/_information.py,sha256=TU9QbhtyhPVYqAxR-9L5awevl9i3q7x1Y25D5xb_Ciw,8085
190
190
  cognite/neat/rules/examples/__init__.py,sha256=nxIwueAcHgZhkYriGxnDLQmIyiT8PByPHbScjYKDKe0,374
@@ -247,7 +247,7 @@ cognite/neat/rules/models/domain.py,sha256=qG1387w6E4XIviOb7EwAjMaavUUQBweYlmYrZ
247
247
  cognite/neat/rules/models/entities.py,sha256=xYUNZ9nhU2y741xW6tqvF_JeQS1KsLi3YB-3zE0E79w,20771
248
248
  cognite/neat/rules/models/information/__init__.py,sha256=HR6g8xgyU53U7Ck8pPdbT70817Q4NC1r1pCRq5SA8iw,291
249
249
  cognite/neat/rules/models/information/_converter.py,sha256=J3mY5clqMY1PR_EmIT-GsTBJ16XCpHQG8EkQWvpqdnI,13930
250
- cognite/neat/rules/models/information/_rules.py,sha256=9qUfKGUSNgJRjIpm8hBDUDDbM1ikBKHqEcUU1ZEglXI,13472
250
+ cognite/neat/rules/models/information/_rules.py,sha256=-PYfsyQa7IqU8ziVZQkz6h2fl_VeVszVRq6c7zc5hEo,13476
251
251
  cognite/neat/rules/models/information/_rules_input.py,sha256=AOTslaehKZH67VJaJO5bu8tT-1iSQz2uwf9mWFpK_44,10580
252
252
  cognite/neat/rules/models/information/_serializer.py,sha256=yti9I_xJruxrib66YIBInhze___Io-oPTQH6uWDumPE,3503
253
253
  cognite/neat/rules/models/information/_validation.py,sha256=rkDGbRHCLKyoZ6ooBZBfjDslouVUu9GXmfBcCorwxt4,8216
@@ -312,8 +312,8 @@ cognite/neat/workflows/steps_registry.py,sha256=fkTX14ZA7_gkUYfWIlx7A1XbCidvqR23
312
312
  cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
313
313
  cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
314
314
  cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
315
- cognite_neat-0.87.4.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
316
- cognite_neat-0.87.4.dist-info/METADATA,sha256=VUyUs_uMSEbsqFItp0nDp27QBGmx_MWYjaqWtgf-_1A,9493
317
- cognite_neat-0.87.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
318
- cognite_neat-0.87.4.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
319
- cognite_neat-0.87.4.dist-info/RECORD,,
315
+ cognite_neat-0.87.6.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
316
+ cognite_neat-0.87.6.dist-info/METADATA,sha256=652vw8eKhaR_1-682l1xl4ODE8CC89qx1jlIjRyxRXg,9493
317
+ cognite_neat-0.87.6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
318
+ cognite_neat-0.87.6.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
319
+ cognite_neat-0.87.6.dist-info/RECORD,,