cognite-neat 0.105.2__py3-none-any.whl → 0.107.0__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/_config.py +6 -260
- cognite/neat/_graph/extractors/__init__.py +5 -1
- cognite/neat/_graph/extractors/_base.py +32 -0
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +42 -16
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +78 -8
- cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +2 -0
- cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +10 -3
- cognite/neat/_graph/extractors/_dms.py +48 -14
- cognite/neat/_graph/extractors/_dms_graph.py +149 -0
- cognite/neat/_graph/extractors/_rdf_file.py +32 -5
- cognite/neat/_graph/loaders/_rdf2dms.py +119 -20
- cognite/neat/_graph/queries/_construct.py +1 -1
- cognite/neat/_graph/transformers/__init__.py +5 -0
- cognite/neat/_graph/transformers/_base.py +13 -9
- cognite/neat/_graph/transformers/_classic_cdf.py +141 -44
- cognite/neat/_graph/transformers/_rdfpath.py +4 -4
- cognite/neat/_graph/transformers/_value_type.py +54 -44
- cognite/neat/_issues/warnings/_external.py +1 -1
- cognite/neat/_rules/analysis/_base.py +1 -1
- cognite/neat/_rules/analysis/_information.py +14 -13
- cognite/neat/_rules/catalog/__init__.py +1 -0
- cognite/neat/_rules/catalog/classic_model.xlsx +0 -0
- cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
- cognite/neat/_rules/importers/_dms2rules.py +7 -5
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +5 -3
- cognite/neat/_rules/models/_base_rules.py +0 -12
- cognite/neat/_rules/models/_types.py +5 -0
- cognite/neat/_rules/models/dms/_rules.py +50 -2
- cognite/neat/_rules/models/information/_rules.py +48 -5
- cognite/neat/_rules/models/information/_rules_input.py +1 -1
- cognite/neat/_rules/models/mapping/_classic2core.py +4 -5
- cognite/neat/_rules/models/mapping/_classic2core.yaml +70 -58
- cognite/neat/_rules/transformers/__init__.py +4 -0
- cognite/neat/_rules/transformers/_converters.py +209 -62
- cognite/neat/_rules/transformers/_mapping.py +3 -2
- cognite/neat/_session/_base.py +8 -13
- cognite/neat/_session/_inspect.py +6 -2
- cognite/neat/_session/_mapping.py +22 -13
- cognite/neat/_session/_prepare.py +9 -57
- cognite/neat/_session/_read.py +96 -29
- cognite/neat/_session/_set.py +9 -0
- cognite/neat/_session/_state.py +10 -1
- cognite/neat/_session/_to.py +51 -15
- cognite/neat/_session/exceptions.py +7 -3
- cognite/neat/_store/_graph_store.py +85 -39
- cognite/neat/_store/_rules_store.py +22 -0
- cognite/neat/_utils/auth.py +2 -0
- cognite/neat/_utils/collection_.py +32 -11
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/METADATA +2 -8
- {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/RECORD +54 -52
- {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/WHEEL +1 -1
- {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.105.2.dist-info → cognite_neat-0.107.0.dist-info}/entry_points.txt +0 -0
|
@@ -4,11 +4,9 @@ from typing import Any, Literal, cast
|
|
|
4
4
|
from cognite.client.data_classes.data_modeling import DataModelIdentifier
|
|
5
5
|
from rdflib import URIRef
|
|
6
6
|
|
|
7
|
-
from cognite.neat._client import NeatClient
|
|
8
7
|
from cognite.neat._constants import (
|
|
9
8
|
get_default_prefixes_and_namespaces,
|
|
10
9
|
)
|
|
11
|
-
from cognite.neat._graph import extractors
|
|
12
10
|
from cognite.neat._graph.transformers import (
|
|
13
11
|
AttachPropertyFromTargetToSource,
|
|
14
12
|
ConnectionToLiteral,
|
|
@@ -25,7 +23,6 @@ from cognite.neat._issues import IssueList
|
|
|
25
23
|
from cognite.neat._issues.errors import NeatValueError
|
|
26
24
|
from cognite.neat._rules.models.dms import DMSValidation
|
|
27
25
|
from cognite.neat._rules.transformers import (
|
|
28
|
-
AddClassImplements,
|
|
29
26
|
IncludeReferenced,
|
|
30
27
|
PrefixEntities,
|
|
31
28
|
ReduceCogniteModel,
|
|
@@ -47,10 +44,10 @@ class PrepareAPI:
|
|
|
47
44
|
inferring a data model or exporting the knowledge graph to a desired destination.
|
|
48
45
|
"""
|
|
49
46
|
|
|
50
|
-
def __init__(self,
|
|
47
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
51
48
|
self._state = state
|
|
52
49
|
self._verbose = verbose
|
|
53
|
-
self.data_model = DataModelPrepareAPI(
|
|
50
|
+
self.data_model = DataModelPrepareAPI(state, verbose)
|
|
54
51
|
self.instances = InstancePrepareAPI(state, verbose)
|
|
55
52
|
|
|
56
53
|
|
|
@@ -349,41 +346,6 @@ class InstancePrepareAPI:
|
|
|
349
346
|
transformer = ConnectionToLiteral(subject_type, subject_predicate)
|
|
350
347
|
self._state.instances.store.transform(transformer)
|
|
351
348
|
|
|
352
|
-
def classic_to_core(self) -> None:
|
|
353
|
-
"""Prepares extracted CDF classic graph for the Core Data model.
|
|
354
|
-
|
|
355
|
-
!!! note "This method bundles several graph transformers which"
|
|
356
|
-
- Convert relationships to edges
|
|
357
|
-
- Convert TimeSeries.type from bool to enum
|
|
358
|
-
- Convert all properties 'source' to a connection to SourceSystem
|
|
359
|
-
- Convert all properties 'labels' from a connection to a string
|
|
360
|
-
|
|
361
|
-
Example:
|
|
362
|
-
Apply classic to core transformations:
|
|
363
|
-
```python
|
|
364
|
-
neat.prepare.instances.classic_to_core()
|
|
365
|
-
```
|
|
366
|
-
"""
|
|
367
|
-
self.relationships_as_edges()
|
|
368
|
-
self.convert_data_type(
|
|
369
|
-
("TimeSeries", "isString"), convert=lambda is_string: "string" if is_string else "numeric"
|
|
370
|
-
)
|
|
371
|
-
self.property_to_type((None, "source"), "SourceSystem", "name")
|
|
372
|
-
for type_ in [
|
|
373
|
-
extractors.EventsExtractor._default_rdf_type,
|
|
374
|
-
extractors.AssetsExtractor._default_rdf_type,
|
|
375
|
-
extractors.FilesExtractor._default_rdf_type,
|
|
376
|
-
]:
|
|
377
|
-
try:
|
|
378
|
-
subject_type, subject_predicate = self._get_type_and_property_uris(type_, "labels")
|
|
379
|
-
except NeatValueError:
|
|
380
|
-
# If the type_.labels does not exist, continue. This is not an error, it just means that the
|
|
381
|
-
# Labels is not used in the graph for that type.
|
|
382
|
-
continue
|
|
383
|
-
else:
|
|
384
|
-
transformer = ConnectionToLiteral(subject_type, subject_predicate)
|
|
385
|
-
self._state.instances.store.transform(transformer)
|
|
386
|
-
|
|
387
349
|
|
|
388
350
|
@session_class_wrapper
|
|
389
351
|
class DataModelPrepareAPI:
|
|
@@ -391,8 +353,7 @@ class DataModelPrepareAPI:
|
|
|
391
353
|
to a desired destination.
|
|
392
354
|
"""
|
|
393
355
|
|
|
394
|
-
def __init__(self,
|
|
395
|
-
self._client = client
|
|
356
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
396
357
|
self._state = state
|
|
397
358
|
self._verbose = verbose
|
|
398
359
|
|
|
@@ -507,14 +468,15 @@ class DataModelPrepareAPI:
|
|
|
507
468
|
self._state.rule_store.last_verified_dms_rules
|
|
508
469
|
).imported_views_and_containers_ids()
|
|
509
470
|
transformers: list[RulesTransformer] = []
|
|
510
|
-
|
|
471
|
+
client = self._state.client
|
|
472
|
+
if (view_ids or container_ids) and client is None:
|
|
511
473
|
raise NeatSessionError(
|
|
512
474
|
"No client provided. You are referencing unknown views and containers in your data model, "
|
|
513
475
|
"NEAT needs a client to lookup the definitions. "
|
|
514
476
|
"Please set the client in the session, NeatSession(client=client)."
|
|
515
477
|
)
|
|
516
|
-
elif (view_ids or container_ids) and
|
|
517
|
-
transformers.append(IncludeReferenced(
|
|
478
|
+
elif (view_ids or container_ids) and client:
|
|
479
|
+
transformers.append(IncludeReferenced(client, include_properties=True))
|
|
518
480
|
|
|
519
481
|
transformers.append(
|
|
520
482
|
ToDataProductModel(
|
|
@@ -539,20 +501,10 @@ class DataModelPrepareAPI:
|
|
|
539
501
|
|
|
540
502
|
def include_referenced(self) -> IssueList:
|
|
541
503
|
"""Include referenced views and containers in the data model."""
|
|
542
|
-
if self.
|
|
504
|
+
if self._state.client is None:
|
|
543
505
|
raise NeatSessionError(
|
|
544
506
|
"No client provided. You are referencing unknown views and containers in your data model, "
|
|
545
507
|
"NEAT needs a client to lookup the definitions. "
|
|
546
508
|
"Please set the client in the session, NeatSession(client=client)."
|
|
547
509
|
)
|
|
548
|
-
return self._state.rule_transform(IncludeReferenced(self.
|
|
549
|
-
|
|
550
|
-
def add_implements_to_classes(self, suffix: Literal["Edge"], implements: str = "Edge") -> IssueList:
|
|
551
|
-
"""All classes with the suffix will have the implements property set to the given value.
|
|
552
|
-
|
|
553
|
-
Args:
|
|
554
|
-
suffix: The suffix of the classes to add the implements property to.
|
|
555
|
-
implements: The value of the implements property to set.
|
|
556
|
-
|
|
557
|
-
"""
|
|
558
|
-
return self._state.rule_transform(AddClassImplements(implements, suffix))
|
|
510
|
+
return self._state.rule_transform(IncludeReferenced(self._state.client))
|
cognite/neat/_session/_read.py
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
from typing import Any, Literal, cast
|
|
2
2
|
|
|
3
3
|
from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier
|
|
4
|
+
from cognite.client.utils.useful_types import SequenceNotStr
|
|
4
5
|
|
|
5
6
|
from cognite.neat._client import NeatClient
|
|
7
|
+
from cognite.neat._constants import CLASSIC_CDF_NAMESPACE
|
|
6
8
|
from cognite.neat._graph import examples as instances_examples
|
|
7
9
|
from cognite.neat._graph import extractors
|
|
10
|
+
from cognite.neat._graph.transformers import ConvertLiteral, LiteralToEntity, LookupRelationshipSourceTarget
|
|
8
11
|
from cognite.neat._issues import IssueList
|
|
9
12
|
from cognite.neat._issues.errors import NeatValueError
|
|
10
13
|
from cognite.neat._issues.warnings import MissingCogniteClientWarning
|
|
11
14
|
from cognite.neat._rules import catalog, importers
|
|
12
15
|
from cognite.neat._rules.importers import BaseImporter
|
|
16
|
+
from cognite.neat._rules.transformers import ClassicPrepareCore
|
|
13
17
|
from cognite.neat._utils.reader import NeatReader
|
|
14
18
|
|
|
15
19
|
from ._state import SessionState
|
|
@@ -22,23 +26,38 @@ from .exceptions import NeatSessionError, session_class_wrapper
|
|
|
22
26
|
class ReadAPI:
|
|
23
27
|
"""Read from a data source into NeatSession graph store."""
|
|
24
28
|
|
|
25
|
-
def __init__(self, state: SessionState,
|
|
29
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
26
30
|
self._state = state
|
|
27
31
|
self._verbose = verbose
|
|
28
|
-
self.cdf = CDFReadAPI(state,
|
|
29
|
-
self.rdf = RDFReadAPI(state,
|
|
30
|
-
self.excel = ExcelReadAPI(state,
|
|
31
|
-
self.csv = CSVReadAPI(state,
|
|
32
|
-
self.yaml = YamlReadAPI(state,
|
|
33
|
-
self.xml = XMLReadAPI(state,
|
|
32
|
+
self.cdf = CDFReadAPI(state, verbose)
|
|
33
|
+
self.rdf = RDFReadAPI(state, verbose)
|
|
34
|
+
self.excel = ExcelReadAPI(state, verbose)
|
|
35
|
+
self.csv = CSVReadAPI(state, verbose)
|
|
36
|
+
self.yaml = YamlReadAPI(state, verbose)
|
|
37
|
+
self.xml = XMLReadAPI(state, verbose)
|
|
38
|
+
|
|
39
|
+
def session(self, io: Any) -> None:
|
|
40
|
+
"""Reads a Neat Session from a zip file.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
io: file path to the Neat Session
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
```python
|
|
47
|
+
neat.read.session("path_to_neat_session")
|
|
48
|
+
```
|
|
49
|
+
"""
|
|
50
|
+
reader = NeatReader.create(io)
|
|
51
|
+
path = reader.materialize_path()
|
|
52
|
+
|
|
53
|
+
self._state.instances.store.write(extractors.RdfFileExtractor.from_zip(path))
|
|
34
54
|
|
|
35
55
|
|
|
36
56
|
@session_class_wrapper
|
|
37
57
|
class BaseReadAPI:
|
|
38
|
-
def __init__(self, state: SessionState,
|
|
58
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
39
59
|
self._state = state
|
|
40
60
|
self._verbose = verbose
|
|
41
|
-
self._client = client
|
|
42
61
|
|
|
43
62
|
|
|
44
63
|
@session_class_wrapper
|
|
@@ -48,15 +67,15 @@ class CDFReadAPI(BaseReadAPI):
|
|
|
48
67
|
|
|
49
68
|
"""
|
|
50
69
|
|
|
51
|
-
def __init__(self, state: SessionState,
|
|
52
|
-
super().__init__(state,
|
|
53
|
-
self.classic = CDFClassicAPI(state,
|
|
70
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
71
|
+
super().__init__(state, verbose)
|
|
72
|
+
self.classic = CDFClassicAPI(state, verbose)
|
|
54
73
|
|
|
55
74
|
@property
|
|
56
75
|
def _get_client(self) -> NeatClient:
|
|
57
|
-
if self.
|
|
76
|
+
if self._state.client is None:
|
|
58
77
|
raise NeatValueError("No client provided. Please provide a client to read a data model.")
|
|
59
|
-
return self.
|
|
78
|
+
return self._state.client
|
|
60
79
|
|
|
61
80
|
def data_model(self, data_model_id: DataModelIdentifier) -> IssueList:
|
|
62
81
|
"""Reads a Data Model from CDF to the knowledge graph.
|
|
@@ -79,6 +98,24 @@ class CDFReadAPI(BaseReadAPI):
|
|
|
79
98
|
importer = importers.DMSImporter.from_data_model_id(self._get_client, data_model_id)
|
|
80
99
|
return self._state.rule_import(importer)
|
|
81
100
|
|
|
101
|
+
def graph(
|
|
102
|
+
self, data_model_id: DataModelIdentifier, instance_space: str | SequenceNotStr[str] | None = None
|
|
103
|
+
) -> IssueList:
|
|
104
|
+
"""Reads a knowledge graph from Cognite Data Fusion (CDF).
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
data_model_id: Tuple of strings with the id of a CDF Data Model.
|
|
108
|
+
instance_space: The instance spaces to extract. If None, all instance spaces are extracted.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
IssueList: A list of issues that occurred during the extraction.
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
extractor = extractors.DMSGraphExtractor.from_data_model_id(
|
|
115
|
+
data_model_id, self._get_client, instance_space=instance_space
|
|
116
|
+
)
|
|
117
|
+
return self._state.write_graph(extractor)
|
|
118
|
+
|
|
82
119
|
|
|
83
120
|
@session_class_wrapper
|
|
84
121
|
class CDFClassicAPI(BaseReadAPI):
|
|
@@ -89,11 +126,11 @@ class CDFClassicAPI(BaseReadAPI):
|
|
|
89
126
|
|
|
90
127
|
@property
|
|
91
128
|
def _get_client(self) -> NeatClient:
|
|
92
|
-
if self.
|
|
129
|
+
if self._state.client is None:
|
|
93
130
|
raise ValueError("No client provided. Please provide a client to read a data model.")
|
|
94
|
-
return self.
|
|
131
|
+
return self._state.client
|
|
95
132
|
|
|
96
|
-
def graph(self, root_asset_external_id: str, limit_per_type: int | None = None) ->
|
|
133
|
+
def graph(self, root_asset_external_id: str, limit_per_type: int | None = None) -> IssueList:
|
|
97
134
|
"""Reads the classic knowledge graph from CDF.
|
|
98
135
|
|
|
99
136
|
The Classic Graph consists of the following core resource type.
|
|
@@ -129,14 +166,45 @@ class CDFClassicAPI(BaseReadAPI):
|
|
|
129
166
|
root_asset_external_id: The external id of the root asset
|
|
130
167
|
limit_per_type: The maximum number of nodes to extract per core node type. If None, all nodes are extracted.
|
|
131
168
|
|
|
169
|
+
Returns:
|
|
170
|
+
IssueList: A list of issues that occurred during the extraction.
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
```python
|
|
174
|
+
neat.read.cdf.graph("root_asset_external_id")
|
|
175
|
+
```
|
|
132
176
|
"""
|
|
177
|
+
namespace = CLASSIC_CDF_NAMESPACE
|
|
133
178
|
extractor = extractors.ClassicGraphExtractor(
|
|
134
|
-
self._get_client,
|
|
179
|
+
self._get_client,
|
|
180
|
+
root_asset_external_id=root_asset_external_id,
|
|
181
|
+
limit_per_type=limit_per_type,
|
|
182
|
+
namespace=namespace,
|
|
183
|
+
prefix="Classic",
|
|
135
184
|
)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if
|
|
139
|
-
print(
|
|
185
|
+
issues = self._state.write_graph(extractor)
|
|
186
|
+
issues.action = "Read Classic Graph"
|
|
187
|
+
if issues:
|
|
188
|
+
print("Use the .inspect.issues() for more details")
|
|
189
|
+
|
|
190
|
+
# Converting the instances from classic to core
|
|
191
|
+
self._state.instances.store.transform(LookupRelationshipSourceTarget(namespace, "Classic"))
|
|
192
|
+
self._state.instances.store.transform(
|
|
193
|
+
ConvertLiteral(
|
|
194
|
+
namespace["ClassicTimeSeries"],
|
|
195
|
+
namespace["isString"],
|
|
196
|
+
lambda is_string: "string" if is_string else "numeric",
|
|
197
|
+
)
|
|
198
|
+
)
|
|
199
|
+
self._state.instances.store.transform(
|
|
200
|
+
LiteralToEntity(None, namespace["source"], "ClassicSourceSystem", "name"),
|
|
201
|
+
)
|
|
202
|
+
# Updating the information model.
|
|
203
|
+
self._state.rule_store.transform(ClassicPrepareCore(namespace))
|
|
204
|
+
# Update the instance store with the latest rules
|
|
205
|
+
information_rules = self._state.rule_store.last_verified_information_rules
|
|
206
|
+
self._state.instances.store.rules = information_rules
|
|
207
|
+
return issues
|
|
140
208
|
|
|
141
209
|
|
|
142
210
|
@session_class_wrapper
|
|
@@ -153,9 +221,9 @@ class ExcelReadAPI(BaseReadAPI):
|
|
|
153
221
|
```
|
|
154
222
|
"""
|
|
155
223
|
|
|
156
|
-
def __init__(self, state: SessionState,
|
|
157
|
-
super().__init__(state,
|
|
158
|
-
self.examples = ExcelExampleAPI(state,
|
|
224
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
225
|
+
super().__init__(state, verbose)
|
|
226
|
+
self.examples = ExcelExampleAPI(state, verbose)
|
|
159
227
|
|
|
160
228
|
def __call__(self, io: Any) -> IssueList:
|
|
161
229
|
"""Reads a Neat Excel Rules sheet to the graph store. The rules sheet may stem from an Information architect,
|
|
@@ -201,7 +269,7 @@ class YamlReadAPI(BaseReadAPI):
|
|
|
201
269
|
if format == "neat":
|
|
202
270
|
importer = importers.YAMLImporter.from_file(path, source_name=f"{reader!s}")
|
|
203
271
|
elif format == "toolkit":
|
|
204
|
-
dms_importer = importers.DMSImporter.from_path(path, self.
|
|
272
|
+
dms_importer = importers.DMSImporter.from_path(path, self._state.client)
|
|
205
273
|
if dms_importer.issue_list.has_warning_type(MissingCogniteClientWarning):
|
|
206
274
|
raise NeatSessionError(
|
|
207
275
|
"No client provided. You are referencing Cognite containers in your data model, "
|
|
@@ -316,8 +384,8 @@ class RDFReadAPI(BaseReadAPI):
|
|
|
316
384
|
io: file path or url to the RDF source
|
|
317
385
|
"""
|
|
318
386
|
|
|
319
|
-
def __init__(self, state: SessionState,
|
|
320
|
-
super().__init__(state,
|
|
387
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
388
|
+
super().__init__(state, verbose)
|
|
321
389
|
self.examples = RDFExamples(state)
|
|
322
390
|
|
|
323
391
|
def ontology(self, io: Any) -> IssueList:
|
|
@@ -387,7 +455,6 @@ class RDFExamples:
|
|
|
387
455
|
def __init__(self, state: SessionState) -> None:
|
|
388
456
|
self._state = state
|
|
389
457
|
|
|
390
|
-
@property
|
|
391
458
|
def nordic44(self) -> IssueList:
|
|
392
459
|
"""Reads the Nordic 44 knowledge graph into the NeatSession graph store."""
|
|
393
460
|
self._state.instances.store.write(extractors.RdfFileExtractor(instances_examples.nordic44_knowledge_graph))
|
cognite/neat/_session/_set.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
from cognite.client import CogniteClient
|
|
1
2
|
from cognite.client import data_modeling as dm
|
|
2
3
|
|
|
4
|
+
from cognite.neat._client import NeatClient
|
|
3
5
|
from cognite.neat._constants import COGNITE_MODELS
|
|
4
6
|
from cognite.neat._issues import IssueList
|
|
5
7
|
from cognite.neat._rules.models import DMSRules
|
|
@@ -35,3 +37,10 @@ class SetAPI:
|
|
|
35
37
|
" due to temporarily issue with the reverse direct relation interpretation"
|
|
36
38
|
)
|
|
37
39
|
return self._state.rule_transform(SetIDDMSModel(new_model_id))
|
|
40
|
+
|
|
41
|
+
def client(self, client: CogniteClient) -> None:
|
|
42
|
+
"""Sets the client to be used in the session."""
|
|
43
|
+
self._state.client = NeatClient(client)
|
|
44
|
+
if self._verbose:
|
|
45
|
+
print(f"Client set to {self._state.client.config.project} CDF project.")
|
|
46
|
+
return None
|
cognite/neat/_session/_state.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from typing import Literal, cast
|
|
3
3
|
|
|
4
|
+
from cognite.neat._client import NeatClient
|
|
5
|
+
from cognite.neat._graph.extractors import KnowledgeGraphExtractor
|
|
4
6
|
from cognite.neat._issues import IssueList
|
|
5
7
|
from cognite.neat._rules.importers import BaseImporter, InferenceImporter
|
|
6
8
|
from cognite.neat._rules.models import DMSRules, InformationRules
|
|
@@ -15,10 +17,11 @@ from .exceptions import NeatSessionError
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class SessionState:
|
|
18
|
-
def __init__(self, store_type: Literal["memory", "oxigraph"]) -> None:
|
|
20
|
+
def __init__(self, store_type: Literal["memory", "oxigraph"], client: NeatClient | None = None) -> None:
|
|
19
21
|
self.instances = InstancesState(store_type)
|
|
20
22
|
self.rule_store = NeatRulesStore()
|
|
21
23
|
self.last_reference: DMSRules | InformationRules | None = None
|
|
24
|
+
self.client = client
|
|
22
25
|
|
|
23
26
|
def rule_transform(self, *transformer: RulesTransformer) -> IssueList:
|
|
24
27
|
if not transformer:
|
|
@@ -60,6 +63,12 @@ class SessionState:
|
|
|
60
63
|
issues.hint = "Use the .inspect.issues() for more details."
|
|
61
64
|
return issues
|
|
62
65
|
|
|
66
|
+
def write_graph(self, extractor: KnowledgeGraphExtractor) -> IssueList:
|
|
67
|
+
self.instances.store.write(extractor)
|
|
68
|
+
issue_list = self.rule_store.import_graph(extractor)
|
|
69
|
+
self.instances.store.add_rules(self.rule_store.last_verified_information_rules)
|
|
70
|
+
return issue_list
|
|
71
|
+
|
|
63
72
|
|
|
64
73
|
@dataclass
|
|
65
74
|
class InstancesState:
|
cognite/neat/_session/_to.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
import zipfile
|
|
1
3
|
from collections.abc import Collection
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import Any, Literal, overload
|
|
4
6
|
|
|
5
7
|
from cognite.client.data_classes.data_modeling import SpaceApply
|
|
6
8
|
|
|
7
|
-
from cognite.neat._client import NeatClient
|
|
8
9
|
from cognite.neat._constants import COGNITE_MODELS
|
|
9
10
|
from cognite.neat._graph import loaders
|
|
10
11
|
from cognite.neat._rules import exporters
|
|
@@ -25,10 +26,10 @@ class ToAPI:
|
|
|
25
26
|
|
|
26
27
|
"""
|
|
27
28
|
|
|
28
|
-
def __init__(self, state: SessionState,
|
|
29
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
29
30
|
self._state = state
|
|
30
31
|
self._verbose = verbose
|
|
31
|
-
self.cdf = CDFToAPI(state,
|
|
32
|
+
self.cdf = CDFToAPI(state, verbose)
|
|
32
33
|
|
|
33
34
|
def excel(
|
|
34
35
|
self,
|
|
@@ -81,11 +82,47 @@ class ToAPI:
|
|
|
81
82
|
exporter = exporters.ExcelExporter(styling="maximal", reference_rules_with_prefix=reference_rules_with_prefix)
|
|
82
83
|
return self._state.rule_store.export_to_file(exporter, Path(io))
|
|
83
84
|
|
|
85
|
+
def session(self, io: Any) -> None:
|
|
86
|
+
"""Export the current session to a file.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
io: The file path to file-like object to write the session to.
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
Export the session to a file
|
|
93
|
+
```python
|
|
94
|
+
session_file_name = "neat_session.zip"
|
|
95
|
+
neat.to.session(session_file_name)
|
|
96
|
+
```
|
|
97
|
+
"""
|
|
98
|
+
filepath = Path(io)
|
|
99
|
+
if filepath.suffix not in {".zip"}:
|
|
100
|
+
warnings.warn("File extension is not .zip, adding it to the file name", stacklevel=2)
|
|
101
|
+
filepath = filepath.with_suffix(".zip")
|
|
102
|
+
|
|
103
|
+
filepath.parent.mkdir(exist_ok=True, parents=True)
|
|
104
|
+
|
|
105
|
+
with zipfile.ZipFile(filepath, "w") as zip_ref:
|
|
106
|
+
zip_ref.writestr(
|
|
107
|
+
"neat-session/instances/instances.ttl",
|
|
108
|
+
self._state.instances.store.serialize(),
|
|
109
|
+
)
|
|
110
|
+
|
|
84
111
|
@overload
|
|
85
|
-
def yaml(
|
|
112
|
+
def yaml(
|
|
113
|
+
self,
|
|
114
|
+
io: None,
|
|
115
|
+
format: Literal["neat"] = "neat",
|
|
116
|
+
skip_system_spaces: bool = True,
|
|
117
|
+
) -> str: ...
|
|
86
118
|
|
|
87
119
|
@overload
|
|
88
|
-
def yaml(
|
|
120
|
+
def yaml(
|
|
121
|
+
self,
|
|
122
|
+
io: Any,
|
|
123
|
+
format: Literal["neat", "toolkit"] = "neat",
|
|
124
|
+
skip_system_spaces: bool = True,
|
|
125
|
+
) -> None: ...
|
|
89
126
|
|
|
90
127
|
def yaml(
|
|
91
128
|
self, io: Any | None = None, format: Literal["neat", "toolkit"] = "neat", skip_system_spaces: bool = True
|
|
@@ -153,8 +190,7 @@ class ToAPI:
|
|
|
153
190
|
class CDFToAPI:
|
|
154
191
|
"""Write a verified Data Model and Instances to CDF."""
|
|
155
192
|
|
|
156
|
-
def __init__(self, state: SessionState,
|
|
157
|
-
self._client = client
|
|
193
|
+
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
158
194
|
self._state = state
|
|
159
195
|
self._verbose = verbose
|
|
160
196
|
|
|
@@ -166,9 +202,9 @@ class CDFToAPI:
|
|
|
166
202
|
Note this space is required to be different than the space with the data model.
|
|
167
203
|
|
|
168
204
|
"""
|
|
169
|
-
if not self.
|
|
205
|
+
if not self._state.client:
|
|
170
206
|
raise NeatSessionError("No CDF client provided!")
|
|
171
|
-
|
|
207
|
+
client = self._state.client
|
|
172
208
|
space = space or f"{self._state.rule_store.last_verified_dms_rules.metadata.space}_instances"
|
|
173
209
|
|
|
174
210
|
if space and space == self._state.rule_store.last_verified_dms_rules.metadata.space:
|
|
@@ -176,16 +212,16 @@ class CDFToAPI:
|
|
|
176
212
|
elif not PATTERNS.space_compliance.match(str(space)):
|
|
177
213
|
raise NeatSessionError("Please provide a valid space name. {PATTERNS.space_compliance.pattern}")
|
|
178
214
|
|
|
179
|
-
if not
|
|
180
|
-
|
|
215
|
+
if not client.data_modeling.spaces.retrieve(space):
|
|
216
|
+
client.data_modeling.spaces.apply(SpaceApply(space=space))
|
|
181
217
|
|
|
182
218
|
loader = loaders.DMSLoader.from_rules(
|
|
183
219
|
self._state.rule_store.last_verified_dms_rules,
|
|
184
220
|
self._state.instances.store,
|
|
185
221
|
instance_space=space,
|
|
186
|
-
client=
|
|
222
|
+
client=client,
|
|
187
223
|
)
|
|
188
|
-
result = loader.load_into_cdf(
|
|
224
|
+
result = loader.load_into_cdf(client)
|
|
189
225
|
self._state.instances.outcome.append(result)
|
|
190
226
|
print("You can inspect the details with the .inspect.outcome.instances(...) method.")
|
|
191
227
|
return result
|
|
@@ -219,9 +255,9 @@ class CDFToAPI:
|
|
|
219
255
|
|
|
220
256
|
exporter = exporters.DMSExporter(existing=existing, export_components=components, drop_data=drop_data)
|
|
221
257
|
|
|
222
|
-
if not self.
|
|
258
|
+
if not self._state.client:
|
|
223
259
|
raise NeatSessionError("No client provided!")
|
|
224
260
|
|
|
225
|
-
result = self._state.rule_store.export_to_cdf(exporter, self.
|
|
261
|
+
result = self._state.rule_store.export_to_cdf(exporter, self._state.client, dry_run)
|
|
226
262
|
print("You can inspect the details with the .inspect.outcome.data_model(...) method.")
|
|
227
263
|
return result
|
|
@@ -2,17 +2,21 @@ import functools
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
-
from cognite.neat._issues.errors import CDFMissingClientError
|
|
5
|
+
from cognite.neat._issues.errors import CDFMissingClientError, NeatImportError
|
|
6
6
|
|
|
7
7
|
from ._collector import _COLLECTOR
|
|
8
8
|
|
|
9
9
|
try:
|
|
10
10
|
from rich import print
|
|
11
|
+
from rich.markup import escape
|
|
11
12
|
|
|
12
13
|
_PREFIX = "[bold red][ERROR][/bold red]"
|
|
13
14
|
except ImportError:
|
|
14
15
|
_PREFIX = "[ERROR]"
|
|
15
16
|
|
|
17
|
+
def escape(x: Any, *_: Any, **__: Any) -> Any: # type: ignore[misc]
|
|
18
|
+
return x
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
class NeatSessionError(Exception):
|
|
18
22
|
"""Base class for all exceptions raised by the NeatSession class."""
|
|
@@ -29,8 +33,8 @@ def _session_method_wrapper(func: Callable, cls_name: str):
|
|
|
29
33
|
except NeatSessionError as e:
|
|
30
34
|
action = _get_action()
|
|
31
35
|
print(f"{_PREFIX} Cannot {action}: {e}")
|
|
32
|
-
except CDFMissingClientError as e:
|
|
33
|
-
print(f"{_PREFIX} {e.as_message()}")
|
|
36
|
+
except (CDFMissingClientError, NeatImportError) as e:
|
|
37
|
+
print(f"{_PREFIX} {escape(e.as_message())}")
|
|
34
38
|
except ModuleNotFoundError as e:
|
|
35
39
|
if e.name == "neatengine":
|
|
36
40
|
action = _get_action()
|