cognite-neat 0.81.5__py3-none-any.whl → 0.81.7__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.5"
1
+ __version__ = "0.81.7"
@@ -9,9 +9,11 @@ from rdflib import RDF, Graph, Namespace, URIRef
9
9
  from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore
10
10
  from rdflib.query import ResultRow
11
11
 
12
+ from cognite.neat.constants import DEFAULT_NAMESPACE
12
13
  from cognite.neat.graph._shared import MIMETypes
13
14
  from cognite.neat.graph.extractors import RdfFileExtractor, TripleExtractors
14
15
  from cognite.neat.graph.models import Triple
16
+ from cognite.neat.graph.transformers import Transformers
15
17
  from cognite.neat.rules.models.information import InformationRules
16
18
  from cognite.neat.utils import remove_namespace
17
19
  from cognite.neat.utils.auxiliary import local_import
@@ -56,6 +58,9 @@ class NeatGraphStore:
56
58
 
57
59
  if self.rules and self.rules.prefixes:
58
60
  self._upsert_prefixes(self.rules.prefixes)
61
+ self.base_namespace = self.rules.metadata.namespace
62
+ else:
63
+ self.base_namespace = DEFAULT_NAMESPACE
59
64
 
60
65
  self.queries = _Queries(self)
61
66
 
@@ -206,6 +211,38 @@ class NeatGraphStore:
206
211
 
207
212
  check_commit(force_commit=True)
208
213
 
214
+ def transform(self, transformer: Transformers) -> None:
215
+ """Transforms the graph store using a transformer."""
216
+
217
+ missing_changes = [
218
+ change for change in transformer._need_changes if not self.provenance.activity_took_place(change)
219
+ ]
220
+ if self.provenance.activity_took_place(type(transformer).__name__) and transformer._use_only_once:
221
+ warnings.warn(
222
+ f"Cannot transform graph store with {type(transformer).__name__}, already applied",
223
+ stacklevel=2,
224
+ )
225
+ elif missing_changes:
226
+ warnings.warn(
227
+ (
228
+ f"Cannot transform graph store with {type(transformer).__name__}, "
229
+ f"missing one or more required changes [{', '.join(missing_changes)}]"
230
+ ),
231
+ stacklevel=2,
232
+ )
233
+
234
+ else:
235
+ _start = datetime.now(timezone.utc)
236
+ transformer.transform(self.graph)
237
+ self.provenance.append(
238
+ Change.record(
239
+ activity=f"{type(transformer).__name__}",
240
+ start=_start,
241
+ end=datetime.now(timezone.utc),
242
+ description=transformer.description,
243
+ )
244
+ )
245
+
209
246
 
210
247
  class _Queries:
211
248
  """Helper class for storing standard queries for the graph store."""
@@ -89,8 +89,8 @@ class Provenance(UserList[T_Change]):
89
89
  def __init__(self, changes: Sequence[T_Change] | None = None):
90
90
  super().__init__(changes or [])
91
91
 
92
- def did_this_happen(self, this: str) -> bool:
93
- return any(change.description == this for change in self)
92
+ def activity_took_place(self, activity: str) -> bool:
93
+ return any(change.activity.used == activity for change in self)
94
94
 
95
95
  def __delitem__(self, *args, **kwargs):
96
96
  raise TypeError("Cannot delete change from provenance")
@@ -0,0 +1,5 @@
1
+ from ._classic_cdf import AddAssetDepth, AssetTimeSeriesConnector
2
+
3
+ __all__ = ["AddAssetDepth", "AssetTimeSeriesConnector"]
4
+
5
+ Transformers = AddAssetDepth | AssetTimeSeriesConnector
@@ -0,0 +1,14 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import ClassVar
3
+
4
+ from rdflib import Graph
5
+
6
+
7
+ class BaseTransformer(ABC):
8
+ description: str
9
+ _use_only_once: bool
10
+ _need_changes: ClassVar[frozenset[str]] = frozenset()
11
+
12
+ @abstractmethod
13
+ def transform(self, graph: Graph) -> None:
14
+ raise NotImplementedError()
@@ -0,0 +1,97 @@
1
+ from typing import cast
2
+
3
+ from rdflib import Graph, Literal, URIRef
4
+
5
+ from cognite.neat.constants import DEFAULT_NAMESPACE
6
+ from cognite.neat.graph import extractors
7
+
8
+ from ._base import BaseTransformer
9
+
10
+
11
+ class AddAssetDepth(BaseTransformer):
12
+ description: str = "Adds depth of asset in the asset hierarchy to the graph"
13
+ _use_only_once: bool = True
14
+ _need_changes = frozenset({str(extractors.AssetsExtractor.__name__)})
15
+
16
+ _parent_template: str = """SELECT ?child ?parent WHERE {{
17
+ <{asset_id}> <{parent_prop}> ?child .
18
+ OPTIONAL{{?child <{parent_prop}>+ ?parent .}}}}"""
19
+
20
+ _root_template: str = """SELECT ?root WHERE {{
21
+ <{asset_id}> <{root_prop}> ?root .}}"""
22
+
23
+ def __init__(
24
+ self, asset_type: URIRef | None = None, root_prop: URIRef | None = None, parent_prop: URIRef | None = None
25
+ ):
26
+ self.asset_type = asset_type or DEFAULT_NAMESPACE.Asset
27
+ self.root_prop = root_prop or DEFAULT_NAMESPACE.root
28
+ self.parent_prop = parent_prop or DEFAULT_NAMESPACE.parent
29
+
30
+ def transform(self, graph: Graph) -> None:
31
+ """Adds depth of asset in the asset hierarchy to the graph."""
32
+ for result in graph.query(f"SELECT DISTINCT ?asset_id WHERE {{?asset_id a <{self.asset_type}>}}"):
33
+ asset_id = cast(tuple, result)[0]
34
+ if depth := self.get_depth(graph, asset_id, self.root_prop, self.parent_prop):
35
+ graph.add((asset_id, DEFAULT_NAMESPACE.depth, Literal(depth)))
36
+
37
+ @classmethod
38
+ def get_depth(
39
+ cls,
40
+ graph: Graph,
41
+ asset_id: URIRef,
42
+ root_prop: URIRef,
43
+ parent_prop: URIRef,
44
+ ) -> int | None:
45
+ """Get asset depth in the asset hierarchy."""
46
+
47
+ # Handles non-root assets
48
+ if result := list(graph.query(cls._parent_template.format(asset_id=asset_id, parent_prop=parent_prop))):
49
+ return len(cast(list[tuple], result)) + 2 if cast(list[tuple], result)[0][1] else 2
50
+
51
+ # Handles root assets
52
+ elif (
53
+ (result := list(graph.query(cls._root_template.format(asset_id=asset_id, root_prop=root_prop))))
54
+ and len(cast(list[tuple], result)) == 1
55
+ and cast(list[tuple], result)[0][0] == asset_id
56
+ ):
57
+ return 1
58
+ else:
59
+ return None
60
+
61
+
62
+ class AssetTimeSeriesConnector(BaseTransformer):
63
+ description: str = "Connects assets to timeseries, thus forming bi-directional connection"
64
+ _use_only_once: bool = True
65
+ _need_changes = frozenset({str(extractors.AssetsExtractor.__name__), str(extractors.TimeSeriesExtractor.__name__)})
66
+ _asset_template: str = """SELECT ?asset_id WHERE {{
67
+ <{timeseries_id}> <{asset_prop}> ?asset_id .
68
+ ?asset_id a <{asset_type}>}}"""
69
+
70
+ def __init__(
71
+ self,
72
+ asset_type: URIRef | None = None,
73
+ timeseries_type: URIRef | None = None,
74
+ asset_prop: URIRef | None = None,
75
+ ):
76
+ self.asset_type = asset_type or DEFAULT_NAMESPACE.Asset
77
+ self.timeseries_type = timeseries_type or DEFAULT_NAMESPACE.TimeSeries
78
+ self.asset_prop = asset_prop or DEFAULT_NAMESPACE.asset
79
+
80
+ def transform(self, graph: Graph) -> None:
81
+ for ts_id_result in graph.query(
82
+ f"SELECT DISTINCT ?timeseries_id WHERE {{?timeseries_id a <{self.timeseries_type}>}}"
83
+ ):
84
+ timeseries_id: URIRef = cast(tuple, ts_id_result)[0]
85
+
86
+ if asset_id_res := list(
87
+ graph.query(
88
+ self._asset_template.format(
89
+ timeseries_id=timeseries_id,
90
+ asset_prop=self.asset_prop,
91
+ asset_type=self.asset_type,
92
+ )
93
+ )
94
+ ):
95
+ # timeseries can be connected to only one asset in the graph
96
+ asset_id = cast(list[tuple], asset_id_res)[0][0]
97
+ graph.add((asset_id, DEFAULT_NAMESPACE.timeSeries, timeseries_id))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.81.5
3
+ Version: 0.81.7
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=-rgvqeQvj63Mky34JbPxAAl9xrHscdkw61qMwZFo8m0,23
2
+ cognite/neat/_version.py,sha256=PtP0Hej8fz6jEW0rAYtnpncSgq-Zo4KnEW-y7gZEHdA,23
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
@@ -73,9 +73,12 @@ cognite/neat/graph/loaders/_base.py,sha256=bdYC6CwsHVqnQa1QzOhL68qQhF1OtrsearqH6
73
73
  cognite/neat/graph/loaders/_rdf2dms.py,sha256=w0y2ECKw7RrQndGyTVIWeFF2WDxs9yvl_eWNqg-tKO8,13018
74
74
  cognite/neat/graph/models.py,sha256=AtLgZh2qyRP6NRetjQCy9qLMuTQB0CH52Zsev-qa2sk,149
75
75
  cognite/neat/graph/stores/__init__.py,sha256=G-VG_YwfRt1kuPao07PDJyZ3w_0-eguzLUM13n-Z_RA,64
76
- cognite/neat/graph/stores/_base.py,sha256=6MZAXygT6sHTQ1LWm_TDb2Ws6fgNJ-r4evwcLywpBVk,9481
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
- cognite/neat/graph/stores/_provenance.py,sha256=Y20-I8dP3DwTQ1sdI_eC4va2Az2FpK0oZwdfJ5T-2wc,3279
78
+ cognite/neat/graph/stores/_provenance.py,sha256=Hr9WBhFj-eoet4czL8XSBGYnu9Yn66YsTgH_G0n3QpY,3293
79
+ cognite/neat/graph/transformers/__init__.py,sha256=P304fcH6U9tV049DMxxHMzUoRnTTER_GdAzcFfrOSc4,180
80
+ cognite/neat/graph/transformers/_base.py,sha256=b37Ek-9njuM5pTR_3XhnxCMrg_ip_2BMwM7ZhKpAAlw,328
81
+ cognite/neat/graph/transformers/_classic_cdf.py,sha256=qeF5untGEM1DL8vg75SIPE0vZn0TN8DL9n3OWNJuJ9A,4029
79
82
  cognite/neat/issues.py,sha256=pxQfqfBseMDE8JM0iqZnkLXngeyeFfT0TFtu1UuAd4c,4629
80
83
  cognite/neat/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
84
  cognite/neat/legacy/graph/__init__.py,sha256=31uTeejWOSd-I8iUG8GOZFhHZcQCsBitJ6X8vu2r1nU,73
@@ -294,8 +297,8 @@ cognite/neat/workflows/steps_registry.py,sha256=fkTX14ZA7_gkUYfWIlx7A1XbCidvqR23
294
297
  cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
295
298
  cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
296
299
  cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
297
- cognite_neat-0.81.5.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
298
- cognite_neat-0.81.5.dist-info/METADATA,sha256=cxHrVuDuAEzdXm-x9I3H0kJvwXVH6twvFmxkwxj3hMY,9290
299
- cognite_neat-0.81.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
300
- cognite_neat-0.81.5.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
301
- cognite_neat-0.81.5.dist-info/RECORD,,
300
+ cognite_neat-0.81.7.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
301
+ cognite_neat-0.81.7.dist-info/METADATA,sha256=Y6mWnpldS0Lex_kygQrZX-ODmiPmPqhfVAPWURhElEo,9290
302
+ cognite_neat-0.81.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
303
+ cognite_neat-0.81.7.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
304
+ cognite_neat-0.81.7.dist-info/RECORD,,