cognite-neat 0.85.9__py3-none-any.whl → 0.85.10__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.
cognite/neat/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.85.9"
1
+ __version__ = "0.85.10"
@@ -2,6 +2,7 @@ import warnings
2
2
  from typing import Literal, cast, overload
3
3
 
4
4
  from rdflib import RDF, Graph, URIRef
5
+ from rdflib import Literal as RdfLiteral
5
6
  from rdflib.query import ResultRow
6
7
 
7
8
  from cognite.neat.rules.models.entities import ClassEntity
@@ -18,6 +19,24 @@ class Queries:
18
19
  self.graph = graph
19
20
  self.rules = rules
20
21
 
22
+ def summarize_instances(self) -> list[tuple]:
23
+ """Summarize instances in the graph store by class and count"""
24
+
25
+ query_statement = """ SELECT ?class (COUNT(?instance) AS ?instanceCount)
26
+ WHERE {
27
+ ?instance a ?class .
28
+ }
29
+ GROUP BY ?class
30
+ ORDER BY DESC(?instanceCount) """
31
+
32
+ return [
33
+ (
34
+ remove_namespace_from_uri(cast(URIRef, cast(tuple, res)[0])),
35
+ cast(RdfLiteral, cast(tuple, res)[1]).value,
36
+ )
37
+ for res in list(self.graph.query(query_statement))
38
+ ]
39
+
21
40
  def list_instances_ids_of_class(self, class_uri: URIRef, limit: int = -1) -> list[URIRef]:
22
41
  """Get instances ids for a given class
23
42
 
@@ -68,7 +87,10 @@ class Queries:
68
87
  # We cannot include the RDF.type in case there is a neat:type property
69
88
  return [remove_namespace_from_uri(*triple) for triple in result if triple[1] != RDF.type] # type: ignore[misc, index]
70
89
  else:
71
- warnings.warn("No rules found for the graph store, returning empty list.", stacklevel=2)
90
+ warnings.warn(
91
+ "No rules found for the graph store, returning empty list.",
92
+ stacklevel=2,
93
+ )
72
94
  return []
73
95
 
74
96
  def construct_instances_of_class(self, class_: str, properties_optional: bool = True) -> list[tuple[str, str, str]]:
@@ -95,7 +117,10 @@ class Queries:
95
117
  # We cannot include the RDF.type in case there is a neat:type property
96
118
  return [remove_namespace_from_uri(*triple) for triple in result if triple[1] != RDF.type] # type: ignore[misc, index]
97
119
  else:
98
- warnings.warn("No rules found for the graph store, returning empty list.", stacklevel=2)
120
+ warnings.warn(
121
+ "No rules found for the graph store, returning empty list.",
122
+ stacklevel=2,
123
+ )
99
124
  return []
100
125
 
101
126
  def list_triples(self, limit: int = 25) -> list[ResultRow]:
@@ -5,6 +5,7 @@ from datetime import datetime, timezone
5
5
  from pathlib import Path
6
6
  from typing import cast
7
7
 
8
+ import pandas as pd
8
9
  from rdflib import Graph, Namespace, URIRef
9
10
  from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore
10
11
 
@@ -283,12 +284,23 @@ class NeatGraphStore:
283
284
  )
284
285
  )
285
286
 
287
+ @property
288
+ def summary(self) -> pd.DataFrame:
289
+ return pd.DataFrame(self.queries.summarize_instances(), columns=["Type", "Occurrence"])
290
+
286
291
  def _repr_html_(self) -> str:
287
292
  provenance = self.provenance._repr_html_()
293
+ summary: pd.DataFrame = self.summary
294
+ summary_text = (
295
+ "<br /><strong>Graph is empty</strong><br />"
296
+ if summary.empty
297
+ else f"<br /><strong>Graph content</strong><br />{cast(pd.DataFrame, summary)._repr_html_()}" # type: ignore[operator]
298
+ )
288
299
 
289
300
  return (
290
301
  f"<strong>{type(self).__name__}</strong> A graph store is a container for storing triples. "
291
302
  "It can be queried and transformed to extract information.<br />"
292
303
  "<strong>Provenance</strong> Provenance is a record of changes that have occurred in the graph store.<br />"
293
304
  f"{provenance}"
305
+ f"{summary_text}"
294
306
  )
@@ -1,6 +1,6 @@
1
1
  from typing import cast
2
2
 
3
- from rdflib import Graph, Literal, URIRef
3
+ from rdflib import RDF, Graph, Literal, URIRef
4
4
 
5
5
  from cognite.neat.constants import DEFAULT_NAMESPACE
6
6
  from cognite.neat.graph import extractors
@@ -21,11 +21,16 @@ class AddAssetDepth(BaseTransformer):
21
21
  <{asset_id}> <{root_prop}> ?root .}}"""
22
22
 
23
23
  def __init__(
24
- self, asset_type: URIRef | None = None, root_prop: URIRef | None = None, parent_prop: URIRef | None = None
24
+ self,
25
+ asset_type: URIRef | None = None,
26
+ root_prop: URIRef | None = None,
27
+ parent_prop: URIRef | None = None,
28
+ depth_typing: dict[int, str] | None = None,
25
29
  ):
26
30
  self.asset_type = asset_type or DEFAULT_NAMESPACE.Asset
27
31
  self.root_prop = root_prop or DEFAULT_NAMESPACE.root
28
32
  self.parent_prop = parent_prop or DEFAULT_NAMESPACE.parent
33
+ self.depth_typing = depth_typing
29
34
 
30
35
  def transform(self, graph: Graph) -> None:
31
36
  """Adds depth of asset in the asset hierarchy to the graph."""
@@ -34,6 +39,13 @@ class AddAssetDepth(BaseTransformer):
34
39
  if depth := self.get_depth(graph, asset_id, self.root_prop, self.parent_prop):
35
40
  graph.add((asset_id, DEFAULT_NAMESPACE.depth, Literal(depth)))
36
41
 
42
+ if self.depth_typing and (type_ := self.depth_typing.get(depth, None)):
43
+ # remove existing type
44
+ graph.remove((asset_id, RDF.type, None))
45
+
46
+ # add new type
47
+ graph.add((asset_id, RDF.type, DEFAULT_NAMESPACE[type_]))
48
+
37
49
  @classmethod
38
50
  def get_depth(
39
51
  cls,
@@ -62,7 +74,12 @@ class AddAssetDepth(BaseTransformer):
62
74
  class AssetTimeSeriesConnector(BaseTransformer):
63
75
  description: str = "Connects assets to timeseries, thus forming bi-directional connection"
64
76
  _use_only_once: bool = True
65
- _need_changes = frozenset({str(extractors.AssetsExtractor.__name__), str(extractors.TimeSeriesExtractor.__name__)})
77
+ _need_changes = frozenset(
78
+ {
79
+ str(extractors.AssetsExtractor.__name__),
80
+ str(extractors.TimeSeriesExtractor.__name__),
81
+ }
82
+ )
66
83
  _asset_template: str = """SELECT ?asset_id WHERE {{
67
84
  <{timeseries_id}> <{asset_prop}> ?asset_id .
68
85
  ?asset_id a <{asset_type}>}}"""
@@ -100,7 +117,12 @@ class AssetTimeSeriesConnector(BaseTransformer):
100
117
  class AssetSequenceConnector(BaseTransformer):
101
118
  description: str = "Connects assets to sequences, thus forming bi-directional connection"
102
119
  _use_only_once: bool = True
103
- _need_changes = frozenset({str(extractors.AssetsExtractor.__name__), str(extractors.SequencesExtractor.__name__)})
120
+ _need_changes = frozenset(
121
+ {
122
+ str(extractors.AssetsExtractor.__name__),
123
+ str(extractors.SequencesExtractor.__name__),
124
+ }
125
+ )
104
126
  _asset_template: str = """SELECT ?asset_id WHERE {{
105
127
  <{sequence_id}> <{asset_prop}> ?asset_id .
106
128
  ?asset_id a <{asset_type}>}}"""
@@ -138,7 +160,12 @@ class AssetSequenceConnector(BaseTransformer):
138
160
  class AssetFileConnector(BaseTransformer):
139
161
  description: str = "Connects assets to files, thus forming bi-directional connection"
140
162
  _use_only_once: bool = True
141
- _need_changes = frozenset({str(extractors.AssetsExtractor.__name__), str(extractors.FilesExtractor.__name__)})
163
+ _need_changes = frozenset(
164
+ {
165
+ str(extractors.AssetsExtractor.__name__),
166
+ str(extractors.FilesExtractor.__name__),
167
+ }
168
+ )
142
169
  _asset_template: str = """SELECT ?asset_id WHERE {{
143
170
  <{file_id}> <{asset_prop}> ?asset_id .
144
171
  ?asset_id a <{asset_type}>}}"""
@@ -174,7 +201,12 @@ class AssetFileConnector(BaseTransformer):
174
201
  class AssetEventConnector(BaseTransformer):
175
202
  description: str = "Connects assets to events, thus forming bi-directional connection"
176
203
  _use_only_once: bool = True
177
- _need_changes = frozenset({str(extractors.AssetsExtractor.__name__), str(extractors.EventsExtractor.__name__)})
204
+ _need_changes = frozenset(
205
+ {
206
+ str(extractors.AssetsExtractor.__name__),
207
+ str(extractors.EventsExtractor.__name__),
208
+ }
209
+ )
178
210
  _asset_template: str = """SELECT ?asset_id WHERE {{
179
211
  <{event_id}> <{asset_prop}> ?asset_id .
180
212
  ?asset_id a <{asset_type}>}}"""
@@ -211,7 +243,10 @@ class AssetRelationshipConnector(BaseTransformer):
211
243
  description: str = "Connects assets via relationships"
212
244
  _use_only_once: bool = True
213
245
  _need_changes = frozenset(
214
- {str(extractors.AssetsExtractor.__name__), str(extractors.RelationshipsExtractor.__name__)}
246
+ {
247
+ str(extractors.AssetsExtractor.__name__),
248
+ str(extractors.RelationshipsExtractor.__name__),
249
+ }
215
250
  )
216
251
  _asset_template: str = """SELECT ?source ?target WHERE {{
217
252
  <{relationship_id}> <{relationship_source_xid_prop}> ?source_xid .
@@ -256,8 +291,20 @@ class AssetRelationshipConnector(BaseTransformer):
256
291
  # files can be connected to multiple assets in the graph
257
292
  for source_asset_id, target_asset_id in cast(list[tuple], assets_id_res):
258
293
  # 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))
294
+ graph.add(
295
+ (
296
+ source_asset_id,
297
+ DEFAULT_NAMESPACE.relationship,
298
+ relationship_id,
299
+ )
300
+ )
301
+ graph.add(
302
+ (
303
+ target_asset_id,
304
+ DEFAULT_NAMESPACE.relationship,
305
+ relationship_id,
306
+ )
307
+ )
261
308
 
262
309
  # add source and target to the relationship
263
310
  graph.add((relationship_id, DEFAULT_NAMESPACE.source, source_asset_id))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.85.9
3
+ Version: 0.85.10
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=JP9pWIPLDxMPiu0_QxO-aYb83pueDfD_SMEnM6AHb9Q,23
3
+ cognite/neat/_version.py,sha256=r9a11H4Pc6Steq_whDR9D6B9Q8eBS2t0VVcst7vsaQA,24
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=2U5M6M252swvQPQyooA1EBzFUZNtcTmuSaywfJDgckM,4232
@@ -75,16 +75,16 @@ cognite/neat/graph/loaders/_rdf2asset.py,sha256=aFby7BwIrW253LEJ4XqGeUuf4jG9VUe8
75
75
  cognite/neat/graph/loaders/_rdf2dms.py,sha256=2PCL8De02xlEkTmgG299k5tiZcKPnoNzCQfI1AgS2Gg,14060
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
- cognite/neat/graph/queries/_base.py,sha256=RL6YQu4apWG-WM-b1TDu5IKqsvskE6Cvlmo8rr1Trhk,5199
78
+ cognite/neat/graph/queries/_base.py,sha256=yO1i2HPA2RiXMehANImDPxSkL6QB6LwvD22HvfdFt70,6023
79
79
  cognite/neat/graph/queries/_construct.py,sha256=FxzSQqzCpo7lKVYerlLAY03oqCeFM5L6MozfBUblzr4,7341
80
80
  cognite/neat/graph/queries/_shared.py,sha256=1-cBnoqyHQazOdNc7J5kk-_GrlHSSnYsBdNJcRD5QyI,5061
81
81
  cognite/neat/graph/stores/__init__.py,sha256=G-VG_YwfRt1kuPao07PDJyZ3w_0-eguzLUM13n-Z_RA,64
82
- cognite/neat/graph/stores/_base.py,sha256=z69uhzBhzl1JQJU9U-h9zAyySn25ZQTJRXam4BavnoY,10586
82
+ cognite/neat/graph/stores/_base.py,sha256=r6NShdYp7KZBQ9WBY1BUFZa5X0SPm509vZjnllmiSIo,11085
83
83
  cognite/neat/graph/stores/_oxrdflib.py,sha256=A5zeRm5_e8ui_ihGpgstRDg_N7qcLZ3QZBRGrOXSGI0,9569
84
84
  cognite/neat/graph/stores/_provenance.py,sha256=HIXa-p7yc2l3HFkQWMnGPhn-t_FROEG21thADGkgy0c,3590
85
85
  cognite/neat/graph/transformers/__init__.py,sha256=wXrNSyJNGnis3haaCKVPZ5y5kKSUsOUHnh-860ekatk,555
86
86
  cognite/neat/graph/transformers/_base.py,sha256=b37Ek-9njuM5pTR_3XhnxCMrg_ip_2BMwM7ZhKpAAlw,328
87
- cognite/neat/graph/transformers/_classic_cdf.py,sha256=ZHHJU-1-lXeufuZJSuDa2Zmu56PS9PcGeFYI91VZNI4,12214
87
+ cognite/neat/graph/transformers/_classic_cdf.py,sha256=6xX-OBSJT5DAQrTJ-nuhCfGNaSk5Iktxn-WIMfzEIqo,13189
88
88
  cognite/neat/issues.py,sha256=pxQfqfBseMDE8JM0iqZnkLXngeyeFfT0TFtu1UuAd4c,4629
89
89
  cognite/neat/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
90
  cognite/neat/legacy/graph/__init__.py,sha256=31uTeejWOSd-I8iUG8GOZFhHZcQCsBitJ6X8vu2r1nU,73
@@ -310,8 +310,8 @@ cognite/neat/workflows/steps_registry.py,sha256=fkTX14ZA7_gkUYfWIlx7A1XbCidvqR23
310
310
  cognite/neat/workflows/tasks.py,sha256=dqlJwKAb0jlkl7abbY8RRz3m7MT4SK8-7cntMWkOYjw,788
311
311
  cognite/neat/workflows/triggers.py,sha256=_BLNplzoz0iic367u1mhHMHiUrCwP-SLK6_CZzfODX0,7071
312
312
  cognite/neat/workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
313
- cognite_neat-0.85.9.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
314
- cognite_neat-0.85.9.dist-info/METADATA,sha256=5qSjm9GMnwHMFSr2a2ECKRoei-cwE8esuk529j16nYM,9493
315
- cognite_neat-0.85.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
316
- cognite_neat-0.85.9.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
317
- cognite_neat-0.85.9.dist-info/RECORD,,
313
+ cognite_neat-0.85.10.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
314
+ cognite_neat-0.85.10.dist-info/METADATA,sha256=8UOfc9V1ro6ldjvcGGTZek0bLiddHUjhdm4pbAfNoFw,9494
315
+ cognite_neat-0.85.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
316
+ cognite_neat-0.85.10.dist-info/entry_points.txt,sha256=61FPqiWb25vbqB0KI7znG8nsg_ibLHBvTjYnkPvNFso,50
317
+ cognite_neat-0.85.10.dist-info/RECORD,,