cognite-neat 0.81.10__py3-none-any.whl → 0.81.11__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.81.10"
1
+ __version__ = "0.81.11"
@@ -2,7 +2,6 @@ from collections.abc import Iterable
2
2
  from datetime import datetime, timezone
3
3
  from pathlib import Path
4
4
  from typing import cast
5
- from urllib.parse import quote
6
5
 
7
6
  from cognite.client import CogniteClient
8
7
  from cognite.client.data_classes import Asset, AssetList
@@ -11,7 +10,7 @@ from rdflib import RDF, Literal, Namespace
11
10
  from cognite.neat.constants import DEFAULT_NAMESPACE
12
11
  from cognite.neat.graph.extractors._base import BaseExtractor
13
12
  from cognite.neat.graph.models import Triple
14
- from cognite.neat.utils.utils import string_to_ideal_type
13
+ from cognite.neat.utils.utils import create_sha256_hash, string_to_ideal_type
15
14
 
16
15
 
17
16
  class AssetsExtractor(BaseExtractor):
@@ -86,7 +85,9 @@ class AssetsExtractor(BaseExtractor):
86
85
  for label in asset.labels:
87
86
  # external_id can create ill-formed URIs, so we create websafe URIs
88
87
  # since labels do not have internal ids, we use the external_id as the id
89
- triples.append((id_, namespace.label, namespace[f"Label_{quote(label.dump()['externalId'])}"]))
88
+ triples.append(
89
+ (id_, namespace.label, namespace[f"Label_{create_sha256_hash(label.dump()['externalId'])}"])
90
+ )
90
91
 
91
92
  if asset.metadata:
92
93
  for key, value in asset.metadata.items():
@@ -2,7 +2,6 @@ from collections.abc import Iterable
2
2
  from datetime import datetime, timezone
3
3
  from pathlib import Path
4
4
  from typing import cast
5
- from urllib.parse import quote
6
5
 
7
6
  from cognite.client import CogniteClient
8
7
  from cognite.client.data_classes import LabelDefinition, LabelDefinitionList
@@ -11,6 +10,7 @@ from rdflib import RDF, Literal, Namespace
11
10
  from cognite.neat.constants import DEFAULT_NAMESPACE
12
11
  from cognite.neat.graph.extractors._base import BaseExtractor
13
12
  from cognite.neat.graph.models import Triple
13
+ from cognite.neat.utils.utils import create_sha256_hash
14
14
 
15
15
 
16
16
  class LabelsExtractor(BaseExtractor):
@@ -44,28 +44,32 @@ class LabelsExtractor(BaseExtractor):
44
44
 
45
45
  @classmethod
46
46
  def _labels2triples(cls, label: LabelDefinition, namespace: Namespace) -> list[Triple]:
47
- id_ = namespace[f"Label_{quote(label.dump()['externalId'])}"]
48
-
49
- # Set rdf type
50
- triples: list[Triple] = [(id_, RDF.type, namespace.Label)]
47
+ if label.external_id:
48
+ id_ = namespace[f"Label_{create_sha256_hash(label.external_id)}"]
51
49
 
52
- # Create attributes
50
+ # Set rdf type
51
+ triples: list[Triple] = [(id_, RDF.type, namespace.Label)]
53
52
 
54
- if label.external_id:
53
+ # Create attributes
55
54
  triples.append((id_, namespace.external_id, Literal(label.external_id)))
56
55
 
57
- if label.name:
58
- triples.append((id_, namespace.name, Literal(label.name)))
56
+ if label.name:
57
+ triples.append((id_, namespace.name, Literal(label.name)))
59
58
 
60
- if label.description:
61
- triples.append((id_, namespace.description, Literal(label.description)))
59
+ if label.description:
60
+ triples.append((id_, namespace.description, Literal(label.description)))
62
61
 
63
- if label.created_time:
64
- triples.append(
65
- (id_, namespace.created_time, Literal(datetime.fromtimestamp(label.created_time / 1000, timezone.utc)))
66
- )
62
+ if label.created_time:
63
+ triples.append(
64
+ (
65
+ id_,
66
+ namespace.created_time,
67
+ Literal(datetime.fromtimestamp(label.created_time / 1000, timezone.utc)),
68
+ )
69
+ )
67
70
 
68
- if label.data_set_id:
69
- triples.append((id_, namespace.data_set_id, namespace[f"Dataset_{label.data_set_id}"]))
71
+ if label.data_set_id:
72
+ triples.append((id_, namespace.data_set_id, namespace[f"Dataset_{label.data_set_id}"]))
70
73
 
71
- return triples
74
+ return triples
75
+ return []
@@ -1,4 +1,3 @@
1
- import uuid
2
1
  from collections.abc import Iterable
3
2
  from datetime import datetime, timezone
4
3
  from pathlib import Path
@@ -12,6 +11,7 @@ from rdflib import RDF, Literal, Namespace
12
11
  from cognite.neat.constants import DEFAULT_NAMESPACE
13
12
  from cognite.neat.graph.extractors._base import BaseExtractor
14
13
  from cognite.neat.graph.models import Triple
14
+ from cognite.neat.utils.utils import create_sha256_hash
15
15
 
16
16
 
17
17
  class RelationshipsExtractor(BaseExtractor):
@@ -47,36 +47,36 @@ class RelationshipsExtractor(BaseExtractor):
47
47
  def _relationship2triples(cls, relationship: Relationship, namespace: Namespace) -> list[Triple]:
48
48
  """Converts an asset to triples."""
49
49
 
50
- # relationships do not have an internal id, so we generate one
51
- id_ = namespace[f"Relationship_{uuid.uuid4()}"]
52
-
53
- # Set rdf type
54
- triples: list[Triple] = [(id_, RDF.type, namespace["Relationship"])]
55
-
56
- # Set source and target types
57
- if source_type := relationship.source_type:
58
- triples.append(
59
- (
60
- id_,
61
- namespace.source_type,
62
- namespace[source_type.title()],
50
+ if relationship.external_id and relationship.source_external_id and relationship.target_external_id:
51
+ # relationships do not have an internal id, so we generate one
52
+ id_ = namespace[f"Relationship_{create_sha256_hash(relationship.external_id)}"]
53
+
54
+ # Set rdf type
55
+ triples: list[Triple] = [(id_, RDF.type, namespace["Relationship"])]
56
+
57
+ # Set source and target types
58
+ if source_type := relationship.source_type:
59
+ triples.append(
60
+ (
61
+ id_,
62
+ namespace.source_type,
63
+ namespace[source_type.title()],
64
+ )
63
65
  )
64
- )
65
66
 
66
- if target_type := relationship.target_type:
67
- triples.append(
68
- (
69
- id_,
70
- namespace.target_type,
71
- namespace[target_type.title()],
67
+ if target_type := relationship.target_type:
68
+ triples.append(
69
+ (
70
+ id_,
71
+ namespace.target_type,
72
+ namespace[target_type.title()],
73
+ )
72
74
  )
73
- )
74
75
 
75
- # Create attributes
76
- if relationship.external_id:
76
+ # Create attributes
77
+
77
78
  triples.append((id_, namespace.external_id, Literal(relationship.external_id)))
78
79
 
79
- if relationship.source_external_id:
80
80
  triples.append(
81
81
  (
82
82
  id_,
@@ -85,7 +85,6 @@ class RelationshipsExtractor(BaseExtractor):
85
85
  )
86
86
  )
87
87
 
88
- if relationship.target_external_id:
89
88
  triples.append(
90
89
  (
91
90
  id_,
@@ -94,59 +93,60 @@ class RelationshipsExtractor(BaseExtractor):
94
93
  )
95
94
  )
96
95
 
97
- if relationship.start_time:
98
- triples.append(
99
- (
100
- id_,
101
- namespace.start_time,
102
- Literal(datetime.fromtimestamp(relationship.start_time / 1000, timezone.utc)),
96
+ if relationship.start_time:
97
+ triples.append(
98
+ (
99
+ id_,
100
+ namespace.start_time,
101
+ Literal(datetime.fromtimestamp(relationship.start_time / 1000, timezone.utc)),
102
+ )
103
103
  )
104
- )
105
104
 
106
- if relationship.end_time:
107
- triples.append(
108
- (
109
- id_,
110
- namespace.end_time,
111
- Literal(datetime.fromtimestamp(relationship.end_time / 1000, timezone.utc)),
105
+ if relationship.end_time:
106
+ triples.append(
107
+ (
108
+ id_,
109
+ namespace.end_time,
110
+ Literal(datetime.fromtimestamp(relationship.end_time / 1000, timezone.utc)),
111
+ )
112
112
  )
113
- )
114
113
 
115
- if relationship.created_time:
116
- triples.append(
117
- (
118
- id_,
119
- namespace.created_time,
120
- Literal(datetime.fromtimestamp(relationship.created_time / 1000, timezone.utc)),
114
+ if relationship.created_time:
115
+ triples.append(
116
+ (
117
+ id_,
118
+ namespace.created_time,
119
+ Literal(datetime.fromtimestamp(relationship.created_time / 1000, timezone.utc)),
120
+ )
121
121
  )
122
- )
123
122
 
124
- if relationship.last_updated_time:
125
- triples.append(
126
- (
127
- id_,
128
- namespace.last_updated_time,
129
- Literal(datetime.fromtimestamp(relationship.last_updated_time / 1000, timezone.utc)),
123
+ if relationship.last_updated_time:
124
+ triples.append(
125
+ (
126
+ id_,
127
+ namespace.last_updated_time,
128
+ Literal(datetime.fromtimestamp(relationship.last_updated_time / 1000, timezone.utc)),
129
+ )
130
130
  )
131
- )
132
131
 
133
- if relationship.confidence:
134
- triples.append(
135
- (
136
- id_,
137
- namespace.confidence,
138
- Literal(relationship.confidence),
132
+ if relationship.confidence:
133
+ triples.append(
134
+ (
135
+ id_,
136
+ namespace.confidence,
137
+ Literal(relationship.confidence),
138
+ )
139
139
  )
140
- )
141
140
 
142
- if relationship.labels:
143
- for label in relationship.labels:
144
- # external_id can create ill-formed URIs, so we create websafe URIs
145
- # since labels do not have internal ids, we use the external_id as the id
146
- triples.append((id_, namespace.label, namespace[f"Label_{quote(label.dump()['externalId'])}"]))
141
+ if relationship.labels:
142
+ for label in relationship.labels:
143
+ # external_id can create ill-formed URIs, so we create websafe URIs
144
+ # since labels do not have internal ids, we use the external_id as the id
145
+ triples.append((id_, namespace.label, namespace[f"Label_{quote(label.dump()['externalId'])}"]))
147
146
 
148
- # Create connection
149
- if relationship.data_set_id:
150
- triples.append((id_, namespace.dataset, namespace[f"Dataset_{relationship.data_set_id}"]))
147
+ # Create connection
148
+ if relationship.data_set_id:
149
+ triples.append((id_, namespace.dataset, namespace[f"Dataset_{relationship.data_set_id}"]))
151
150
 
152
- return triples
151
+ return triples
152
+ return []
@@ -2,6 +2,7 @@ from ._classic_cdf import (
2
2
  AddAssetDepth,
3
3
  AssetEventConnector,
4
4
  AssetFileConnector,
5
+ AssetRelationshipConnector,
5
6
  AssetSequenceConnector,
6
7
  AssetTimeSeriesConnector,
7
8
  )
@@ -12,8 +13,14 @@ __all__ = [
12
13
  "AssetSequenceConnector",
13
14
  "AssetFileConnector",
14
15
  "AssetEventConnector",
16
+ "AssetRelationshipConnector",
15
17
  ]
16
18
 
17
19
  Transformers = (
18
- AddAssetDepth | AssetTimeSeriesConnector | AssetSequenceConnector | AssetFileConnector | AssetEventConnector
20
+ AddAssetDepth
21
+ | AssetTimeSeriesConnector
22
+ | AssetSequenceConnector
23
+ | AssetFileConnector
24
+ | AssetEventConnector
25
+ | AssetRelationshipConnector
19
26
  )
@@ -205,3 +205,64 @@ class AssetEventConnector(BaseTransformer):
205
205
  # files can be connected to multiple assets in the graph
206
206
  for (asset_id,) in cast(list[tuple], assets_id_res):
207
207
  graph.add((asset_id, DEFAULT_NAMESPACE.event, event_id))
208
+
209
+
210
+ class AssetRelationshipConnector(BaseTransformer):
211
+ description: str = "Connects assets via relationships"
212
+ _use_only_once: bool = True
213
+ _need_changes = frozenset(
214
+ {str(extractors.AssetsExtractor.__name__), str(extractors.RelationshipsExtractor.__name__)}
215
+ )
216
+ _asset_template: str = """SELECT ?source ?target WHERE {{
217
+ <{relationship_id}> <{relationship_source_xid_prop}> ?source_xid .
218
+ ?source <{asset_xid_property}> ?source_xid .
219
+ ?source a <{asset_type}> .
220
+
221
+ <{relationship_id}> <{relationship_target_xid_prop}> ?target_xid .
222
+ ?target <{asset_xid_property}> ?target_xid .
223
+ ?target a <{asset_type}> .}}"""
224
+
225
+ def __init__(
226
+ self,
227
+ asset_type: URIRef | None = None,
228
+ relationship_type: URIRef | None = None,
229
+ relationship_source_xid_prop: URIRef | None = None,
230
+ relationship_target_xid_prop: URIRef | None = None,
231
+ asset_xid_property: URIRef | None = None,
232
+ ):
233
+ self.asset_type = asset_type or DEFAULT_NAMESPACE.Asset
234
+ self.relationship_type = relationship_type or DEFAULT_NAMESPACE.Relationship
235
+ self.relationship_source_xid_prop = relationship_source_xid_prop or DEFAULT_NAMESPACE.source_external_id
236
+ self.relationship_target_xid_prop = relationship_target_xid_prop or DEFAULT_NAMESPACE.target_external_id
237
+ self.asset_xid_property = asset_xid_property or DEFAULT_NAMESPACE.external_id
238
+
239
+ def transform(self, graph: Graph) -> None:
240
+ for relationship_id_result in graph.query(
241
+ f"SELECT DISTINCT ?relationship_id WHERE {{?relationship_id a <{self.relationship_type}>}}"
242
+ ):
243
+ relationship_id: URIRef = cast(tuple, relationship_id_result)[0]
244
+
245
+ if assets_id_res := list(
246
+ graph.query(
247
+ self._asset_template.format(
248
+ relationship_id=relationship_id,
249
+ asset_xid_property=self.asset_xid_property,
250
+ relationship_source_xid_prop=self.relationship_source_xid_prop,
251
+ relationship_target_xid_prop=self.relationship_target_xid_prop,
252
+ asset_type=self.asset_type,
253
+ )
254
+ )
255
+ ):
256
+ # files can be connected to multiple assets in the graph
257
+ for source_asset_id, target_asset_id in cast(list[tuple], assets_id_res):
258
+ # create a relationship between the two assets
259
+ graph.add((source_asset_id, DEFAULT_NAMESPACE.relationship, relationship_id))
260
+ graph.add((target_asset_id, DEFAULT_NAMESPACE.relationship, relationship_id))
261
+
262
+ # add source and target to the relationship
263
+ graph.add((relationship_id, DEFAULT_NAMESPACE.source, source_asset_id))
264
+ graph.add((relationship_id, DEFAULT_NAMESPACE.target, target_asset_id))
265
+
266
+ # remove properties that are not needed, specifically the external ids
267
+ graph.remove((relationship_id, self.relationship_source_xid_prop, None))
268
+ graph.remove((relationship_id, self.relationship_target_xid_prop, None))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.81.10
3
+ Version: 0.81.11
4
4
  Summary: Knowledge graph transformation
5
5
  Home-page: https://cognite-neat.readthedocs-hosted.com/
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
1
  cognite/neat/__init__.py,sha256=v-rRiDOgZ3sQSMQKq0vgUQZvpeOkoHFXissAx6Ktg84,61
2
- cognite/neat/_version.py,sha256=kfoIPOj8SqOTkglqTypeVhT7HKM-I90zJCvSL7WOcg8,24
2
+ cognite/neat/_version.py,sha256=j--riKojvVEPIAPPLvKSvPrl9iDUa3FZVs9awN4iAfo,24
3
3
  cognite/neat/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  cognite/neat/app/api/asgi/metrics.py,sha256=nxFy7L5cChTI0a-zkCiJ59Aq8yLuIJp5c9Dg0wRXtV0,152
5
5
  cognite/neat/app/api/configuration.py,sha256=2U5M6M252swvQPQyooA1EBzFUZNtcTmuSaywfJDgckM,4232
@@ -56,11 +56,11 @@ cognite/neat/graph/exceptions.py,sha256=R6pyOH774n9w2x_X_nrUr8OMAdjJMf_XPIqAvxIQ
56
56
  cognite/neat/graph/extractors/__init__.py,sha256=ozXL6ZLK36wp3uX4UACRVs6rbvynQg2JQlDgL1UM1Wk,1025
57
57
  cognite/neat/graph/extractors/_base.py,sha256=TOXDnlqske8DgnJwA0THDVRgmR79Acjm56yF0E-2w7I,356
58
58
  cognite/neat/graph/extractors/_classic_cdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
- cognite/neat/graph/extractors/_classic_cdf/_assets.py,sha256=8FQvJqi8nclkQJ7YmSo4yNqs9uExaoCn_whMW8cIAx0,3855
59
+ cognite/neat/graph/extractors/_classic_cdf/_assets.py,sha256=6e_glBuYl6ArBabOblERQ57g55qsiA0UHrmHVvBqk_s,3895
60
60
  cognite/neat/graph/extractors/_classic_cdf/_events.py,sha256=Z0vPcyOz4mCwY0Dqa5wAQZjczO1dbTUGM0X4Y10NLGQ,3995
61
61
  cognite/neat/graph/extractors/_classic_cdf/_files.py,sha256=-6nCkXUCAnDsv4eDFDEiQ-U4SGhmW1VLxZJFUcszqjU,4831
62
- cognite/neat/graph/extractors/_classic_cdf/_labels.py,sha256=wm7JFmsk7sHsOVpTsGBE0wargIuHD09Xu-OHK_Bm20g,2386
63
- cognite/neat/graph/extractors/_classic_cdf/_relationships.py,sha256=n7gISeyhLjiaWYLWWRj20jmaYgdvJBdYSiZ0G8ZW6mk,5035
62
+ cognite/neat/graph/extractors/_classic_cdf/_labels.py,sha256=nGQyrOZrZzxniIKWROzS1jYrLDT861NARyi-VM_u9-0,2587
63
+ cognite/neat/graph/extractors/_classic_cdf/_relationships.py,sha256=w16hu_REIFEVEenhdxEInOmgCouZaBwxXSlxl9_7vTA,5398
64
64
  cognite/neat/graph/extractors/_classic_cdf/_sequences.py,sha256=o4yxkf81FGFrKkflvlyDYie05fTYsT_LcRFM63OTVCI,3406
65
65
  cognite/neat/graph/extractors/_classic_cdf/_timeseries.py,sha256=KTYmL8vhXijlmkN1UFQrGpaCllpRekr1y55SoLhlLbg,4559
66
66
  cognite/neat/graph/extractors/_dexpi.py,sha256=CYSLt0Fl7Y2RCqOfIAT0N8Cjs-Yu2lRLvB13axtAaWw,9384
@@ -76,9 +76,9 @@ cognite/neat/graph/stores/__init__.py,sha256=G-VG_YwfRt1kuPao07PDJyZ3w_0-eguzLUM
76
76
  cognite/neat/graph/stores/_base.py,sha256=-MwqfVCBd4j4xWaI7zOvypsGqtbET-2eKQKf36DO0A0,11001
77
77
  cognite/neat/graph/stores/_oxrdflib.py,sha256=A5zeRm5_e8ui_ihGpgstRDg_N7qcLZ3QZBRGrOXSGI0,9569
78
78
  cognite/neat/graph/stores/_provenance.py,sha256=Hr9WBhFj-eoet4czL8XSBGYnu9Yn66YsTgH_G0n3QpY,3293
79
- cognite/neat/graph/transformers/__init__.py,sha256=ZlTjOGX1ly4AlYvc4-D-xoTonkP5OUuJF1t8cj-EUac,440
79
+ cognite/neat/graph/transformers/__init__.py,sha256=wXrNSyJNGnis3haaCKVPZ5y5kKSUsOUHnh-860ekatk,555
80
80
  cognite/neat/graph/transformers/_base.py,sha256=b37Ek-9njuM5pTR_3XhnxCMrg_ip_2BMwM7ZhKpAAlw,328
81
- cognite/neat/graph/transformers/_classic_cdf.py,sha256=fsKwbm__5YpgeOwbUbbFatK5_VBWdZo5F8k97zX0nxw,8876
81
+ cognite/neat/graph/transformers/_classic_cdf.py,sha256=ZHHJU-1-lXeufuZJSuDa2Zmu56PS9PcGeFYI91VZNI4,12214
82
82
  cognite/neat/issues.py,sha256=pxQfqfBseMDE8JM0iqZnkLXngeyeFfT0TFtu1UuAd4c,4629
83
83
  cognite/neat/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
84
  cognite/neat/legacy/graph/__init__.py,sha256=31uTeejWOSd-I8iUG8GOZFhHZcQCsBitJ6X8vu2r1nU,73
@@ -297,8 +297,8 @@ cognite/neat/workflows/steps_registry.py,sha256=fkTX14ZA7_gkUYfWIlx7A1XbCidvqR23
297
297
  cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
298
298
  cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
299
299
  cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
300
- cognite_neat-0.81.10.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
301
- cognite_neat-0.81.10.dist-info/METADATA,sha256=PJxDNxDQFz5LZWpOfU8ud4ihSmj3lNlF4996gUUR8I0,9291
302
- cognite_neat-0.81.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
303
- cognite_neat-0.81.10.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
304
- cognite_neat-0.81.10.dist-info/RECORD,,
300
+ cognite_neat-0.81.11.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
301
+ cognite_neat-0.81.11.dist-info/METADATA,sha256=MmBADLlk7wOEvD_MHykgRKYfjDJIoVYbf8WeGTeF9ac,9291
302
+ cognite_neat-0.81.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
303
+ cognite_neat-0.81.11.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
304
+ cognite_neat-0.81.11.dist-info/RECORD,,