cognite-neat 0.97.3__py3-none-any.whl → 0.98.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/_graph/loaders/__init__.py +1 -2
- cognite/neat/_issues/warnings/_models.py +9 -0
- cognite/neat/_rules/_shared.py +3 -8
- cognite/neat/_rules/analysis/__init__.py +1 -2
- cognite/neat/_rules/analysis/_base.py +2 -23
- cognite/neat/_rules/analysis/_dms.py +4 -10
- cognite/neat/_rules/analysis/_information.py +2 -10
- cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
- cognite/neat/_rules/exporters/_rules2excel.py +15 -72
- cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
- cognite/neat/_rules/importers/_base.py +3 -4
- cognite/neat/_rules/importers/_dms2rules.py +17 -40
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
- cognite/neat/_rules/importers/_rdf/_base.py +17 -29
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +30 -18
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
- cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
- cognite/neat/_rules/models/__init__.py +2 -16
- cognite/neat/_rules/models/_base_rules.py +98 -52
- cognite/neat/_rules/models/dms/_exporter.py +7 -160
- cognite/neat/_rules/models/dms/_rules.py +18 -126
- cognite/neat/_rules/models/dms/_rules_input.py +20 -48
- cognite/neat/_rules/models/dms/_schema.py +11 -0
- cognite/neat/_rules/models/dms/_validation.py +9 -107
- cognite/neat/_rules/models/information/_rules.py +19 -114
- cognite/neat/_rules/models/information/_rules_input.py +32 -41
- cognite/neat/_rules/models/information/_validation.py +34 -102
- cognite/neat/_rules/transformers/__init__.py +1 -4
- cognite/neat/_rules/transformers/_converters.py +18 -195
- cognite/neat/_rules/transformers/_mapping.py +1 -5
- cognite/neat/_rules/transformers/_verification.py +0 -14
- cognite/neat/_session/_base.py +34 -13
- cognite/neat/_session/_collector.py +126 -0
- cognite/neat/_session/_inspect.py +5 -5
- cognite/neat/_session/_prepare.py +4 -4
- cognite/neat/_session/_read.py +62 -9
- cognite/neat/_session/_set.py +2 -2
- cognite/neat/_session/_show.py +11 -11
- cognite/neat/_session/_to.py +24 -11
- cognite/neat/_session/exceptions.py +20 -3
- cognite/neat/_store/_provenance.py +2 -2
- cognite/neat/_utils/auxiliary.py +19 -0
- cognite/neat/_version.py +1 -1
- cognite/neat/_workflows/steps/data_contracts.py +2 -10
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +6 -46
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +2 -7
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.98.0.dist-info}/METADATA +2 -1
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.98.0.dist-info}/RECORD +58 -64
- cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
- cognite/neat/_rules/analysis/_asset.py +0 -173
- cognite/neat/_rules/models/asset/__init__.py +0 -13
- cognite/neat/_rules/models/asset/_rules.py +0 -109
- cognite/neat/_rules/models/asset/_rules_input.py +0 -101
- cognite/neat/_rules/models/asset/_validation.py +0 -45
- cognite/neat/_rules/models/domain.py +0 -136
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.98.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.98.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.97.3.dist-info → cognite_neat-0.98.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
import tempfile
|
|
4
|
+
import threading
|
|
5
|
+
import uuid
|
|
6
|
+
from contextlib import suppress
|
|
7
|
+
from functools import cached_property
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from mixpanel import Consumer, Mixpanel # type: ignore[import-untyped]
|
|
12
|
+
|
|
13
|
+
from cognite.neat._constants import IN_NOTEBOOK, IN_PYODIDE
|
|
14
|
+
from cognite.neat._version import __version__
|
|
15
|
+
|
|
16
|
+
_NEAT_MIXPANEL_TOKEN: str = "bd630ad149d19999df3989c3a3750c4f"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Collector:
|
|
20
|
+
def __init__(self, skip_tracking: bool = False) -> None:
|
|
21
|
+
self.mp = Mixpanel(_NEAT_MIXPANEL_TOKEN, consumer=Consumer(api_host="api-eu.mixpanel.com"))
|
|
22
|
+
tmp_dir = Path(tempfile.gettempdir()).resolve()
|
|
23
|
+
self._opt_status_file = tmp_dir / "neat-opt-status.bin"
|
|
24
|
+
self._distinct_id_file = tmp_dir / "neat-distinct-id.bin"
|
|
25
|
+
self.skip_tracking = self.opted_out or skip_tracking
|
|
26
|
+
|
|
27
|
+
@cached_property
|
|
28
|
+
def _opt_status(self) -> str:
|
|
29
|
+
if self._opt_status_file.exists():
|
|
30
|
+
return self._opt_status_file.read_text()
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
def _bust_opt_status(self) -> None:
|
|
34
|
+
self.__dict__.pop("_opt_status", None)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def opted_out(self) -> bool:
|
|
38
|
+
return self._opt_status == "opted-out"
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def opted_in(self) -> bool:
|
|
42
|
+
return self._opt_status == "opted-in"
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _get_environment() -> str:
|
|
46
|
+
if IN_PYODIDE:
|
|
47
|
+
return "pyodide"
|
|
48
|
+
if IN_NOTEBOOK:
|
|
49
|
+
return "notebook"
|
|
50
|
+
return "python"
|
|
51
|
+
|
|
52
|
+
def track_session_command(self, command: str, *args, **kwargs) -> None:
|
|
53
|
+
event_information = {
|
|
54
|
+
"neatVersion": __version__,
|
|
55
|
+
"$os": platform.system(),
|
|
56
|
+
"pythonVersion": platform.python_version(),
|
|
57
|
+
"environment": self._get_environment(),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if len(args) > 1:
|
|
61
|
+
# The first argument is self.
|
|
62
|
+
for i, arg in enumerate(args[1:]):
|
|
63
|
+
event_information[f"arg{i}"] = arg
|
|
64
|
+
|
|
65
|
+
if kwargs:
|
|
66
|
+
for key, value in kwargs.items():
|
|
67
|
+
event_information[key] = self._serialize_value(value)[:500]
|
|
68
|
+
self._track(command, event_information)
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _serialize_value(value: Any) -> str:
|
|
72
|
+
if isinstance(value, (str | int | float | bool)):
|
|
73
|
+
return str(value)
|
|
74
|
+
if isinstance(value, list | tuple | dict):
|
|
75
|
+
return str(value)
|
|
76
|
+
return str(type(value))
|
|
77
|
+
|
|
78
|
+
def _track(self, event_name: str, event_information: dict[str, Any]) -> bool:
|
|
79
|
+
if self.skip_tracking or not self.opted_in or "PYTEST_CURRENT_TEST" in os.environ:
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
distinct_id = self.get_distinct_id()
|
|
83
|
+
|
|
84
|
+
def track() -> None:
|
|
85
|
+
# If we are unable to connect to Mixpanel, we don't want to crash the program
|
|
86
|
+
with suppress(ConnectionError):
|
|
87
|
+
self.mp.track(
|
|
88
|
+
distinct_id,
|
|
89
|
+
event_name,
|
|
90
|
+
event_information,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
thread = threading.Thread(
|
|
94
|
+
target=track,
|
|
95
|
+
daemon=False,
|
|
96
|
+
)
|
|
97
|
+
thread.start()
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
def get_distinct_id(self) -> str:
|
|
101
|
+
if self._distinct_id_file.exists():
|
|
102
|
+
return self._distinct_id_file.read_text()
|
|
103
|
+
|
|
104
|
+
distinct_id = f"{platform.system()}-{platform.python_version()}-{uuid.uuid4()!s}"
|
|
105
|
+
self._distinct_id_file.write_text(distinct_id)
|
|
106
|
+
with suppress(ConnectionError):
|
|
107
|
+
self.mp.people_set(
|
|
108
|
+
distinct_id,
|
|
109
|
+
{
|
|
110
|
+
"$os": platform.system(),
|
|
111
|
+
"$python_version": platform.python_version(),
|
|
112
|
+
"$distinct_id": distinct_id,
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
return distinct_id
|
|
116
|
+
|
|
117
|
+
def enable(self) -> None:
|
|
118
|
+
self._opt_status_file.write_text("opted-in")
|
|
119
|
+
self._bust_opt_status()
|
|
120
|
+
|
|
121
|
+
def disable(self) -> None:
|
|
122
|
+
self._opt_status_file.write_text("opted-out")
|
|
123
|
+
self._bust_opt_status()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_COLLECTOR = Collector()
|
|
@@ -9,10 +9,10 @@ from cognite.neat._issues import IssueList
|
|
|
9
9
|
from cognite.neat._utils.upload import UploadResult, UploadResultCore, UploadResultList
|
|
10
10
|
|
|
11
11
|
from ._state import SessionState
|
|
12
|
-
from .exceptions import
|
|
12
|
+
from .exceptions import session_class_wrapper
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
@
|
|
15
|
+
@session_class_wrapper
|
|
16
16
|
class InspectAPI:
|
|
17
17
|
def __init__(self, state: SessionState) -> None:
|
|
18
18
|
self._state = state
|
|
@@ -25,7 +25,7 @@ class InspectAPI:
|
|
|
25
25
|
return self._state.data_model.last_verified_rule[1].properties.to_pandas()
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
@
|
|
28
|
+
@session_class_wrapper
|
|
29
29
|
class InspectIssues:
|
|
30
30
|
"""Inspect issues of the current data model."""
|
|
31
31
|
|
|
@@ -92,14 +92,14 @@ class InspectIssues:
|
|
|
92
92
|
)
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
@
|
|
95
|
+
@session_class_wrapper
|
|
96
96
|
class InspectOutcome:
|
|
97
97
|
def __init__(self, state: SessionState) -> None:
|
|
98
98
|
self.data_model = InspectUploadOutcome(lambda: state.data_model.last_outcome)
|
|
99
99
|
self.instances = InspectUploadOutcome(lambda: state.instances.last_outcome)
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
@
|
|
102
|
+
@session_class_wrapper
|
|
103
103
|
class InspectUploadOutcome:
|
|
104
104
|
def __init__(self, get_last_outcome: Callable[[], UploadResultList]) -> None:
|
|
105
105
|
self._get_last_outcome = get_last_outcome
|
|
@@ -12,10 +12,10 @@ from cognite.neat._rules.transformers import ReduceCogniteModel, ToCompliantEnti
|
|
|
12
12
|
from cognite.neat._store._provenance import Change
|
|
13
13
|
|
|
14
14
|
from ._state import SessionState
|
|
15
|
-
from .exceptions import NeatSessionError,
|
|
15
|
+
from .exceptions import NeatSessionError, session_class_wrapper
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
@
|
|
18
|
+
@session_class_wrapper
|
|
19
19
|
class PrepareAPI:
|
|
20
20
|
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
21
21
|
self._state = state
|
|
@@ -24,7 +24,7 @@ class PrepareAPI:
|
|
|
24
24
|
self.instances = InstancePrepareAPI(state, verbose)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
@
|
|
27
|
+
@session_class_wrapper
|
|
28
28
|
class InstancePrepareAPI:
|
|
29
29
|
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
30
30
|
self._state = state
|
|
@@ -95,7 +95,7 @@ class InstancePrepareAPI:
|
|
|
95
95
|
return type_uri[0], property_uri[0]
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
@
|
|
98
|
+
@session_class_wrapper
|
|
99
99
|
class DataModelPrepareAPI:
|
|
100
100
|
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
101
101
|
self._state = state
|
cognite/neat/_session/_read.py
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import tempfile
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, Literal
|
|
5
5
|
|
|
6
6
|
from cognite.client import CogniteClient
|
|
7
7
|
from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier
|
|
8
8
|
|
|
9
|
+
from cognite.neat._constants import COGNITE_SPACES
|
|
9
10
|
from cognite.neat._graph import examples as instances_examples
|
|
10
11
|
from cognite.neat._graph import extractors
|
|
11
12
|
from cognite.neat._issues import IssueList
|
|
12
13
|
from cognite.neat._issues.errors import NeatValueError
|
|
13
14
|
from cognite.neat._rules import importers
|
|
14
15
|
from cognite.neat._rules._shared import ReadRules
|
|
16
|
+
from cognite.neat._rules.importers import BaseImporter
|
|
15
17
|
from cognite.neat._store._provenance import Activity as ProvenanceActivity
|
|
16
18
|
from cognite.neat._store._provenance import Change
|
|
17
19
|
from cognite.neat._store._provenance import Entity as ProvenanceEntity
|
|
@@ -20,10 +22,10 @@ from cognite.neat._utils.reader import GitHubReader, NeatReader, PathReader
|
|
|
20
22
|
from ._state import SessionState
|
|
21
23
|
from ._wizard import NeatObjectType, RDFFileType, object_wizard, rdf_dm_wizard
|
|
22
24
|
from .engine import import_engine
|
|
23
|
-
from .exceptions import NeatSessionError,
|
|
25
|
+
from .exceptions import NeatSessionError, session_class_wrapper
|
|
24
26
|
|
|
25
27
|
|
|
26
|
-
@
|
|
28
|
+
@session_class_wrapper
|
|
27
29
|
class ReadAPI:
|
|
28
30
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
29
31
|
self._state = state
|
|
@@ -32,9 +34,10 @@ class ReadAPI:
|
|
|
32
34
|
self.rdf = RDFReadAPI(state, client, verbose)
|
|
33
35
|
self.excel = ExcelReadAPI(state, client, verbose)
|
|
34
36
|
self.csv = CSVReadAPI(state, client, verbose)
|
|
37
|
+
self.yaml = YamlReadAPI(state, client, verbose)
|
|
35
38
|
|
|
36
39
|
|
|
37
|
-
@
|
|
40
|
+
@session_class_wrapper
|
|
38
41
|
class BaseReadAPI:
|
|
39
42
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
40
43
|
self._state = state
|
|
@@ -62,7 +65,7 @@ class BaseReadAPI:
|
|
|
62
65
|
raise NeatValueError(f"Expected str or Path, got {type(io)}")
|
|
63
66
|
|
|
64
67
|
|
|
65
|
-
@
|
|
68
|
+
@session_class_wrapper
|
|
66
69
|
class CDFReadAPI(BaseReadAPI):
|
|
67
70
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
68
71
|
super().__init__(state, client, verbose)
|
|
@@ -107,7 +110,7 @@ class CDFReadAPI(BaseReadAPI):
|
|
|
107
110
|
return self._store_rules(rules, change)
|
|
108
111
|
|
|
109
112
|
|
|
110
|
-
@
|
|
113
|
+
@session_class_wrapper
|
|
111
114
|
class CDFClassicAPI(BaseReadAPI):
|
|
112
115
|
@property
|
|
113
116
|
def _get_client(self) -> CogniteClient:
|
|
@@ -122,7 +125,7 @@ class CDFClassicAPI(BaseReadAPI):
|
|
|
122
125
|
print(f"Asset hierarchy {root_asset_external_id} read successfully")
|
|
123
126
|
|
|
124
127
|
|
|
125
|
-
@
|
|
128
|
+
@session_class_wrapper
|
|
126
129
|
class ExcelReadAPI(BaseReadAPI):
|
|
127
130
|
def __call__(self, io: Any) -> IssueList:
|
|
128
131
|
reader = NeatReader.create(io)
|
|
@@ -146,7 +149,57 @@ class ExcelReadAPI(BaseReadAPI):
|
|
|
146
149
|
return input_rules.issues
|
|
147
150
|
|
|
148
151
|
|
|
149
|
-
@
|
|
152
|
+
@session_class_wrapper
|
|
153
|
+
class YamlReadAPI(BaseReadAPI):
|
|
154
|
+
def __call__(self, io: Any, format: Literal["neat", "toolkit"] = "neat") -> IssueList:
|
|
155
|
+
reader = NeatReader.create(io)
|
|
156
|
+
if not isinstance(reader, PathReader):
|
|
157
|
+
raise NeatValueError("Only file paths are supported for YAML files")
|
|
158
|
+
start = datetime.now(timezone.utc)
|
|
159
|
+
importer: BaseImporter
|
|
160
|
+
if format == "neat":
|
|
161
|
+
importer = importers.YAMLImporter.from_file(reader.path)
|
|
162
|
+
elif format == "toolkit":
|
|
163
|
+
if reader.path.is_file():
|
|
164
|
+
dms_importer = importers.DMSImporter.from_zip_file(reader.path)
|
|
165
|
+
elif reader.path.is_dir():
|
|
166
|
+
dms_importer = importers.DMSImporter.from_directory(reader.path)
|
|
167
|
+
else:
|
|
168
|
+
raise NeatValueError(f"Unsupported YAML format: {format}")
|
|
169
|
+
ref_containers = dms_importer.root_schema.referenced_container()
|
|
170
|
+
if system_container_ids := [
|
|
171
|
+
container_id for container_id in ref_containers if container_id.space in COGNITE_SPACES
|
|
172
|
+
]:
|
|
173
|
+
if self._client is None:
|
|
174
|
+
raise NeatSessionError(
|
|
175
|
+
"No client provided. You are referencing Cognite containers in your data model, "
|
|
176
|
+
"NEAT needs a client to lookup the container definitions. "
|
|
177
|
+
"Please set the client in the session, NeatSession(client=client)."
|
|
178
|
+
)
|
|
179
|
+
system_containers = self._client.data_modeling.containers.retrieve(system_container_ids)
|
|
180
|
+
dms_importer.update_referenced_containers(system_containers)
|
|
181
|
+
|
|
182
|
+
importer = dms_importer
|
|
183
|
+
else:
|
|
184
|
+
raise NeatValueError(f"Unsupported YAML format: {format}")
|
|
185
|
+
input_rules: ReadRules = importer.to_rules()
|
|
186
|
+
|
|
187
|
+
end = datetime.now(timezone.utc)
|
|
188
|
+
|
|
189
|
+
if input_rules.rules:
|
|
190
|
+
change = Change.from_rules_activity(
|
|
191
|
+
input_rules,
|
|
192
|
+
importer.agent,
|
|
193
|
+
start,
|
|
194
|
+
end,
|
|
195
|
+
description=f"YAML file {reader!s} read as unverified data model",
|
|
196
|
+
)
|
|
197
|
+
self._store_rules(input_rules, change)
|
|
198
|
+
|
|
199
|
+
return input_rules.issues
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@session_class_wrapper
|
|
150
203
|
class CSVReadAPI(BaseReadAPI):
|
|
151
204
|
def __call__(self, io: Any, type: str, primary_key: str) -> None:
|
|
152
205
|
reader = NeatReader.create(io)
|
|
@@ -167,7 +220,7 @@ class CSVReadAPI(BaseReadAPI):
|
|
|
167
220
|
self._state.instances.store.write(extractor)
|
|
168
221
|
|
|
169
222
|
|
|
170
|
-
@
|
|
223
|
+
@session_class_wrapper
|
|
171
224
|
class RDFReadAPI(BaseReadAPI):
|
|
172
225
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
173
226
|
super().__init__(state, client, verbose)
|
cognite/neat/_session/_set.py
CHANGED
|
@@ -7,10 +7,10 @@ from cognite.neat._rules.transformers import SetIDDMSModel
|
|
|
7
7
|
from cognite.neat._store._provenance import Change
|
|
8
8
|
|
|
9
9
|
from ._state import SessionState
|
|
10
|
-
from .exceptions import NeatSessionError,
|
|
10
|
+
from .exceptions import NeatSessionError, session_class_wrapper
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@
|
|
13
|
+
@session_class_wrapper
|
|
14
14
|
class SetAPI:
|
|
15
15
|
def __init__(self, state: SessionState, verbose: bool) -> None:
|
|
16
16
|
self._state = state
|
cognite/neat/_session/_show.py
CHANGED
|
@@ -16,10 +16,10 @@ from cognite.neat._session.exceptions import NeatSessionError
|
|
|
16
16
|
from cognite.neat._utils.rdf_ import remove_namespace_from_uri
|
|
17
17
|
|
|
18
18
|
from ._state import SessionState
|
|
19
|
-
from .exceptions import
|
|
19
|
+
from .exceptions import session_class_wrapper
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
@
|
|
22
|
+
@session_class_wrapper
|
|
23
23
|
class ShowAPI:
|
|
24
24
|
def __init__(self, state: SessionState) -> None:
|
|
25
25
|
self._state = state
|
|
@@ -27,7 +27,7 @@ class ShowAPI:
|
|
|
27
27
|
self.instances = ShowInstanceAPI(self._state)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
@
|
|
30
|
+
@session_class_wrapper
|
|
31
31
|
class ShowBaseAPI:
|
|
32
32
|
def __init__(self, state: SessionState) -> None:
|
|
33
33
|
self._state = state
|
|
@@ -63,7 +63,7 @@ class ShowBaseAPI:
|
|
|
63
63
|
return net.show(name)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
@
|
|
66
|
+
@session_class_wrapper
|
|
67
67
|
class ShowDataModelAPI(ShowBaseAPI):
|
|
68
68
|
def __init__(self, state: SessionState) -> None:
|
|
69
69
|
super().__init__(state)
|
|
@@ -111,7 +111,7 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
111
111
|
di_graph.add_edge(
|
|
112
112
|
prop_.view.suffix,
|
|
113
113
|
prop_.value_type.suffix,
|
|
114
|
-
label=prop_.name or prop_.
|
|
114
|
+
label=prop_.name or prop_.view_property,
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
return di_graph
|
|
@@ -145,7 +145,7 @@ class ShowDataModelAPI(ShowBaseAPI):
|
|
|
145
145
|
return di_graph
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
@
|
|
148
|
+
@session_class_wrapper
|
|
149
149
|
class ShowDataModelImplementsAPI(ShowBaseAPI):
|
|
150
150
|
def __init__(self, state: SessionState) -> None:
|
|
151
151
|
super().__init__(state)
|
|
@@ -206,27 +206,27 @@ class ShowDataModelImplementsAPI(ShowBaseAPI):
|
|
|
206
206
|
# if possible use human readable label coming from the view name
|
|
207
207
|
|
|
208
208
|
# add subClassOff as edges
|
|
209
|
-
if class_.
|
|
209
|
+
if class_.implements:
|
|
210
210
|
if not di_graph.has_node(class_.class_.suffix):
|
|
211
211
|
di_graph.add_node(
|
|
212
212
|
class_.class_.suffix,
|
|
213
213
|
label=class_.name or class_.class_.suffix,
|
|
214
214
|
)
|
|
215
215
|
|
|
216
|
-
for parent in class_.
|
|
216
|
+
for parent in class_.implements:
|
|
217
217
|
if not di_graph.has_node(parent.suffix):
|
|
218
218
|
di_graph.add_node(parent.suffix, label=parent.suffix)
|
|
219
219
|
di_graph.add_edge(
|
|
220
220
|
class_.class_.suffix,
|
|
221
221
|
parent.suffix,
|
|
222
|
-
label="
|
|
222
|
+
label="implements",
|
|
223
223
|
dashes=True,
|
|
224
224
|
)
|
|
225
225
|
|
|
226
226
|
return di_graph
|
|
227
227
|
|
|
228
228
|
|
|
229
|
-
@
|
|
229
|
+
@session_class_wrapper
|
|
230
230
|
class ShowDataModelProvenanceAPI(ShowBaseAPI):
|
|
231
231
|
def __init__(self, state: SessionState) -> None:
|
|
232
232
|
super().__init__(state)
|
|
@@ -286,7 +286,7 @@ class ShowDataModelProvenanceAPI(ShowBaseAPI):
|
|
|
286
286
|
return remove_namespace_from_uri(thing)
|
|
287
287
|
|
|
288
288
|
|
|
289
|
-
@
|
|
289
|
+
@session_class_wrapper
|
|
290
290
|
class ShowInstanceAPI(ShowBaseAPI):
|
|
291
291
|
def __init__(self, state: SessionState) -> None:
|
|
292
292
|
super().__init__(state)
|
cognite/neat/_session/_to.py
CHANGED
|
@@ -12,10 +12,10 @@ from cognite.neat._rules._shared import VerifiedRules
|
|
|
12
12
|
from cognite.neat._utils.upload import UploadResultCore, UploadResultList
|
|
13
13
|
|
|
14
14
|
from ._state import SessionState
|
|
15
|
-
from .exceptions import NeatSessionError,
|
|
15
|
+
from .exceptions import NeatSessionError, session_class_wrapper
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
@
|
|
18
|
+
@session_class_wrapper
|
|
19
19
|
class ToAPI:
|
|
20
20
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
21
21
|
self._state = state
|
|
@@ -46,21 +46,34 @@ class ToAPI:
|
|
|
46
46
|
return None
|
|
47
47
|
|
|
48
48
|
@overload
|
|
49
|
-
def yaml(self, io: None) -> str: ...
|
|
49
|
+
def yaml(self, io: None, format: Literal["neat"] = "neat") -> str: ...
|
|
50
50
|
|
|
51
51
|
@overload
|
|
52
|
-
def yaml(self, io: Any) -> None: ...
|
|
53
|
-
|
|
54
|
-
def yaml(self, io: Any | None = None) -> str | None:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
def yaml(self, io: Any, format: Literal["neat", "toolkit"] = "neat") -> None: ...
|
|
53
|
+
|
|
54
|
+
def yaml(self, io: Any | None = None, format: Literal["neat", "toolkit"] = "neat") -> str | None:
|
|
55
|
+
if format == "neat":
|
|
56
|
+
exporter = exporters.YAMLExporter()
|
|
57
|
+
last_verified = self._state.data_model.last_verified_rule[1]
|
|
58
|
+
if io is None:
|
|
59
|
+
return exporter.export(last_verified)
|
|
60
|
+
|
|
61
|
+
exporter.export_to_file(last_verified, Path(io))
|
|
62
|
+
elif format == "toolkit":
|
|
63
|
+
if io is None or not isinstance(io, str | Path):
|
|
64
|
+
raise NeatSessionError(
|
|
65
|
+
"Please provide a zip file or directory path to write the YAML files to."
|
|
66
|
+
"This is required for the 'toolkit' format."
|
|
67
|
+
)
|
|
68
|
+
dms_rule = self._state.data_model.last_verified_dms_rules[1]
|
|
69
|
+
exporters.DMSExporter().export_to_file(dms_rule, Path(io))
|
|
70
|
+
else:
|
|
71
|
+
raise NeatSessionError("Please provide a valid format. {['neat', 'toolkit']}")
|
|
58
72
|
|
|
59
|
-
exporter.export_to_file(self._state.data_model.last_verified_rule[1], Path(io))
|
|
60
73
|
return None
|
|
61
74
|
|
|
62
75
|
|
|
63
|
-
@
|
|
76
|
+
@session_class_wrapper
|
|
64
77
|
class CDFToAPI:
|
|
65
78
|
def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
|
|
66
79
|
self._client = client
|
|
@@ -2,6 +2,8 @@ import functools
|
|
|
2
2
|
from collections.abc import Callable
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from ._collector import _COLLECTOR
|
|
6
|
+
|
|
5
7
|
try:
|
|
6
8
|
from rich import print
|
|
7
9
|
|
|
@@ -16,9 +18,10 @@ class NeatSessionError(Exception):
|
|
|
16
18
|
...
|
|
17
19
|
|
|
18
20
|
|
|
19
|
-
def
|
|
21
|
+
def _session_method_wrapper(func: Callable, cls_name: str):
|
|
20
22
|
@functools.wraps(func)
|
|
21
23
|
def wrapper(*args: Any, **kwargs: Any):
|
|
24
|
+
_COLLECTOR.track_session_command(f"{cls_name}.{func.__name__}", *args, **kwargs)
|
|
22
25
|
try:
|
|
23
26
|
return func(*args, **kwargs)
|
|
24
27
|
except NeatSessionError as e:
|
|
@@ -40,7 +43,21 @@ def _intercept_session_exceptions(func: Callable):
|
|
|
40
43
|
return wrapper
|
|
41
44
|
|
|
42
45
|
|
|
43
|
-
def
|
|
46
|
+
def session_class_wrapper(cls: type):
|
|
47
|
+
"""This decorator wraps all methods of a class.
|
|
48
|
+
|
|
49
|
+
It should be used with all composition classes used with the NeatSession class.
|
|
50
|
+
|
|
51
|
+
It does the following:
|
|
52
|
+
* Intercepts all NeatSession exceptions and prints them in a user-friendly way.
|
|
53
|
+
* Collects user metrics.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
cls: NeatSession composition class
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
cls: NeatSession composition class with all methods wrapped
|
|
60
|
+
"""
|
|
44
61
|
to_check = [cls]
|
|
45
62
|
while to_check:
|
|
46
63
|
cls = to_check.pop()
|
|
@@ -49,7 +66,7 @@ def intercept_session_exceptions(cls: type):
|
|
|
49
66
|
continue
|
|
50
67
|
attr = getattr(cls, attr_name)
|
|
51
68
|
if callable(attr):
|
|
52
|
-
setattr(cls, attr_name,
|
|
69
|
+
setattr(cls, attr_name, _session_method_wrapper(attr, cls.__name__))
|
|
53
70
|
elif isinstance(attr, type):
|
|
54
71
|
to_check.append(attr)
|
|
55
72
|
return cls
|
|
@@ -93,14 +93,14 @@ class Entity:
|
|
|
93
93
|
return cls(
|
|
94
94
|
was_attributed_to=agent,
|
|
95
95
|
was_generated_by=activity,
|
|
96
|
-
id_=rules.
|
|
96
|
+
id_=rules.metadata.identifier,
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
elif isinstance(rules, ReadRules | JustRules) and rules.rules is not None:
|
|
100
100
|
return cls(
|
|
101
101
|
was_attributed_to=agent,
|
|
102
102
|
was_generated_by=activity,
|
|
103
|
-
id_=rules.rules.
|
|
103
|
+
id_=rules.rules.metadata.identifier,
|
|
104
104
|
)
|
|
105
105
|
else:
|
|
106
106
|
return cls(
|
cognite/neat/_utils/auxiliary.py
CHANGED
|
@@ -140,3 +140,22 @@ def string_to_ideal_type(input_string: str) -> int | bool | float | datetime | s
|
|
|
140
140
|
except ValueError:
|
|
141
141
|
# Return the input string if no conversion is possible
|
|
142
142
|
return input_string
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_parameters_by_method(obj: object, prefix: str = "") -> dict[str, dict[str, type]]:
|
|
146
|
+
annotation_by_method = {}
|
|
147
|
+
namespace_dict = {**dict(obj.__class__.__dict__.items()), **vars(obj)}
|
|
148
|
+
for name, value in namespace_dict.items():
|
|
149
|
+
if name != "__call__" and (name.startswith("_") or isinstance(value, property)):
|
|
150
|
+
continue
|
|
151
|
+
if callable(value) and type(value).__name__ == "function":
|
|
152
|
+
annotation_by_method[f"{prefix}{name}"] = get_parameters(value)
|
|
153
|
+
elif isinstance(value, object) and type(value).__module__ != "builtins":
|
|
154
|
+
sub_prefix = f"{prefix}{name}." if prefix else f"{name}."
|
|
155
|
+
annotation_by_method.update(get_parameters_by_method(value, sub_prefix))
|
|
156
|
+
return annotation_by_method
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def get_parameters(obj: Callable) -> dict[str, type]:
|
|
160
|
+
annotations = inspect.get_annotations(obj)
|
|
161
|
+
return {name: annotations[name] for name in annotations if name != "return"}
|
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.98.0"
|
|
2
2
|
__engine__ = "^1.0.3"
|
|
@@ -10,9 +10,7 @@ from cognite.client.data_classes import (
|
|
|
10
10
|
from cognite.client.data_classes.data_modeling import EdgeApply, NodeApply
|
|
11
11
|
|
|
12
12
|
from cognite.neat._rules.models import (
|
|
13
|
-
AssetRules,
|
|
14
13
|
DMSRules,
|
|
15
|
-
DomainRules,
|
|
16
14
|
InformationRules,
|
|
17
15
|
)
|
|
18
16
|
from cognite.neat._store import NeatGraphStore
|
|
@@ -20,19 +18,13 @@ from cognite.neat._workflows.steps.step_model import DataContract
|
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
class MultiRuleData(DataContract):
|
|
23
|
-
domain: DomainRules | None = None
|
|
24
21
|
information: InformationRules | None = None
|
|
25
|
-
asset: AssetRules | None = None
|
|
26
22
|
dms: DMSRules | None = None
|
|
27
23
|
|
|
28
24
|
@classmethod
|
|
29
|
-
def from_rules(cls, rules:
|
|
30
|
-
if isinstance(rules,
|
|
31
|
-
return cls(domain=rules)
|
|
32
|
-
elif isinstance(rules, InformationRules):
|
|
25
|
+
def from_rules(cls, rules: InformationRules | DMSRules):
|
|
26
|
+
if isinstance(rules, InformationRules):
|
|
33
27
|
return cls(information=rules)
|
|
34
|
-
elif isinstance(rules, AssetRules):
|
|
35
|
-
return cls(asset=rules)
|
|
36
28
|
elif isinstance(rules, DMSRules):
|
|
37
29
|
return cls(dms=rules)
|
|
38
30
|
else:
|