cognite-neat 0.127.27__py3-none-any.whl → 0.127.29__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.
@@ -0,0 +1,185 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from cognite.neat._issues import ConsistencyError, ModelSyntaxError
7
+ from cognite.neat._utils.useful_types import ModusOperandi
8
+
9
+ if sys.version_info >= (3, 11):
10
+ import tomllib as tomli # Python 3.11+
11
+ else:
12
+ import tomli # type: ignore
13
+
14
+
15
+ class ValidationConfig(BaseModel, populate_by_name=True):
16
+ """Validation configuration."""
17
+
18
+ exclude: list[str] = Field(default_factory=list)
19
+
20
+ def can_run_validator(self, code: str, issue_type: type) -> bool:
21
+ """
22
+ Check if a specific validator should run.
23
+
24
+ Args:
25
+ code: Validation code (e.g., "NEAT-DMS-CONTAINER-001")
26
+ issue_type: Issue type (e.g., ModelSyntaxError, ConsistencyError, Recommendation)
27
+
28
+ Returns:
29
+ True if validator should run, False otherwise
30
+ """
31
+
32
+ is_excluded = self._is_excluded(code, self.exclude)
33
+
34
+ if issue_type in [ModelSyntaxError, ConsistencyError] and is_excluded:
35
+ print(f"Validator {code} was excluded however it is a critical validator and will still run.")
36
+ return True
37
+ else:
38
+ return not is_excluded
39
+
40
+ @classmethod
41
+ def _is_excluded(cls, code: str, patterns: list[str]) -> bool:
42
+ """Check if code matches any pattern (supports wildcards)."""
43
+ for pattern in patterns:
44
+ if "*" in pattern:
45
+ # Split both pattern and code by hyphens
46
+ pattern_parts = pattern.split("-")
47
+ code_parts = code.split("-")
48
+
49
+ # Pattern must have same or fewer parts than code
50
+ if len(pattern_parts) > len(code_parts):
51
+ continue
52
+
53
+ # Check if all pattern parts match (allowing wildcards)
54
+ match = True
55
+ for p_part, c_part in zip(pattern_parts, code_parts, strict=False):
56
+ if p_part != "*" and p_part != c_part:
57
+ match = False
58
+ break
59
+
60
+ if match:
61
+ return True
62
+ elif code == pattern:
63
+ return True
64
+
65
+ return False
66
+
67
+ def __str__(self) -> str:
68
+ """Human-readable configuration summary."""
69
+ if not self.exclude:
70
+ return "All validators enabled"
71
+ return f"Excluded Rules: {', '.join(self.exclude)}"
72
+
73
+
74
+ class ModelingConfig(BaseModel, populate_by_name=True):
75
+ """Modeling configuration."""
76
+
77
+ mode: ModusOperandi = "additive"
78
+
79
+
80
+ class NeatConfig(BaseModel, populate_by_name=True):
81
+ """Configuration for a custom profile."""
82
+
83
+ profile: str
84
+ validation: ValidationConfig
85
+ modeling: ModelingConfig
86
+
87
+ def __str__(self) -> str:
88
+ """Human-readable configuration summary."""
89
+ lines = [
90
+ f"Profile: {self.profile}",
91
+ f"Modeling Mode: {self.modeling.mode}",
92
+ f"Validation: {self.validation}",
93
+ ]
94
+ return "\n".join(lines)
95
+
96
+
97
+ def internal_profiles() -> dict[str, NeatConfig]:
98
+ """Get internal NeatConfig profile by name."""
99
+ return {
100
+ "legacy-additive": NeatConfig(
101
+ profile="legacy-additive",
102
+ modeling=ModelingConfig(mode="additive"),
103
+ validation=ValidationConfig(
104
+ exclude=[
105
+ "NEAT-DMS-AI-READINESS-*",
106
+ "NEAT-DMS-CONNECTIONS-002",
107
+ "NEAT-DMS-CONNECTIONS-REVERSE-007",
108
+ "NEAT-DMS-CONNECTIONS-REVERSE-008",
109
+ "NEAT-DMS-CONSISTENCY-001",
110
+ ]
111
+ ),
112
+ ),
113
+ "legacy-rebuild": NeatConfig(
114
+ profile="legacy-rebuild",
115
+ modeling=ModelingConfig(mode="rebuild"),
116
+ validation=ValidationConfig(
117
+ exclude=[
118
+ "NEAT-DMS-AI-READINESS-*",
119
+ "NEAT-DMS-CONNECTIONS-002",
120
+ "NEAT-DMS-CONNECTIONS-REVERSE-007",
121
+ "NEAT-DMS-CONNECTIONS-REVERSE-008",
122
+ "NEAT-DMS-CONSISTENCY-001",
123
+ ]
124
+ ),
125
+ ),
126
+ "deep-additive": NeatConfig(
127
+ profile="deep-additive",
128
+ modeling=ModelingConfig(mode="additive"),
129
+ validation=ValidationConfig(exclude=[]),
130
+ ),
131
+ "deep-rebuild": NeatConfig(
132
+ profile="deep-rebuild",
133
+ modeling=ModelingConfig(mode="rebuild"),
134
+ validation=ValidationConfig(exclude=[]),
135
+ ),
136
+ }
137
+
138
+
139
+ def get_neat_config(config_file_name: str, profile: str) -> NeatConfig:
140
+ """Get NeatConfig from file or internal profiles.
141
+
142
+ Args:
143
+ config_file_name: Path to configuration file.
144
+ profile: Profile name to use.
145
+ Returns:
146
+ NeatConfig instance.
147
+ """
148
+
149
+ if not config_file_name.endswith(".toml"):
150
+ raise ValueError("config_file_name must end with '.toml'")
151
+
152
+ file_path = Path.cwd() / config_file_name
153
+
154
+ if file_path.exists():
155
+ with file_path.open("rb") as f:
156
+ toml = tomli.load(f)
157
+
158
+ if "tool" in toml and "neat" in toml["tool"]:
159
+ data = toml["tool"]["neat"]
160
+ elif "neat" in toml:
161
+ data = toml["neat"]
162
+ else:
163
+ raise ValueError("No [tool.neat] or [neat] section found in the configuration file.")
164
+
165
+ toml_profile = data.get("profile")
166
+ toml_profiles = data.get("profiles")
167
+ hardcoded_profiles = internal_profiles()
168
+
169
+ if toml_profile and toml_profile in hardcoded_profiles:
170
+ raise ValueError(f"Internal profile '{toml_profile}' cannot be used in external configuration file.")
171
+
172
+ if toml_profiles and any(p in hardcoded_profiles for p in toml_profiles.keys()):
173
+ raise ValueError(
174
+ "Internal profiles cannot be redefined in external configuration file: "
175
+ f"{set(hardcoded_profiles.keys()).intersection(toml_profiles.keys())}"
176
+ )
177
+
178
+ if toml_profile and profile == toml_profile:
179
+ return NeatConfig(**data)
180
+ elif (built_in_profiles := data.get("profiles")) and profile in built_in_profiles:
181
+ return NeatConfig(profile=profile, **data["profiles"][profile])
182
+ else:
183
+ raise ValueError(f"Profile '{profile}' not found in configuration file.")
184
+ else:
185
+ raise FileNotFoundError(f"Configuration file '{file_path}' not found.")
@@ -23,6 +23,7 @@ class DataModelMissingName(DataModelValidator):
23
23
  """
24
24
 
25
25
  code = f"{BASE_CODE}-001"
26
+ issue_type = Recommendation
26
27
 
27
28
  def run(self) -> list[Recommendation]:
28
29
  recommendations: list[Recommendation] = []
@@ -61,6 +62,7 @@ class DataModelMissingDescription(DataModelValidator):
61
62
  """
62
63
 
63
64
  code = f"{BASE_CODE}-002"
65
+ issue_type = Recommendation
64
66
 
65
67
  def run(self) -> list[Recommendation]:
66
68
  recommendations: list[Recommendation] = []
@@ -95,6 +97,7 @@ class ViewMissingName(DataModelValidator):
95
97
  """
96
98
 
97
99
  code = f"{BASE_CODE}-003"
100
+ issue_type = Recommendation
98
101
 
99
102
  def run(self) -> list[Recommendation]:
100
103
  recommendations: list[Recommendation] = []
@@ -145,6 +148,7 @@ class ViewMissingDescription(DataModelValidator):
145
148
  """
146
149
 
147
150
  code = f"{BASE_CODE}-004"
151
+ issue_type = Recommendation
148
152
 
149
153
  def run(self) -> list[Recommendation]:
150
154
  recommendations: list[Recommendation] = []
@@ -185,6 +189,7 @@ class ViewPropertyMissingName(DataModelValidator):
185
189
  """
186
190
 
187
191
  code = f"{BASE_CODE}-005"
192
+ issue_type = Recommendation
188
193
 
189
194
  def run(self) -> list[Recommendation]:
190
195
  recommendations: list[Recommendation] = []
@@ -233,6 +238,7 @@ class ViewPropertyMissingDescription(DataModelValidator):
233
238
  """
234
239
 
235
240
  code = f"{BASE_CODE}-006"
241
+ issue_type = Recommendation
236
242
 
237
243
  def run(self) -> list[Recommendation]:
238
244
  recommendations: list[Recommendation] = []
@@ -58,6 +58,7 @@ class DataModelValidator(ABC):
58
58
  """Assessors for fundamental data model principles."""
59
59
 
60
60
  code: ClassVar[str]
61
+ issue_type: ClassVar[type[ConsistencyError] | type[Recommendation]]
61
62
 
62
63
  def __init__(
63
64
  self,
@@ -31,6 +31,7 @@ class ConnectionValueTypeUnexisting(DataModelValidator):
31
31
  """
32
32
 
33
33
  code = f"{BASE_CODE}-001"
34
+ issue_type = ConsistencyError
34
35
 
35
36
  def run(self) -> list[ConsistencyError]:
36
37
  undefined_value_types = []
@@ -80,6 +81,7 @@ class ConnectionValueTypeUndefined(DataModelValidator):
80
81
  """
81
82
 
82
83
  code = f"{BASE_CODE}-002"
84
+ issue_type = Recommendation
83
85
 
84
86
  def run(self) -> list[Recommendation]:
85
87
  missing_value_types = []
@@ -149,6 +151,7 @@ class ReverseConnectionSourceViewMissing(DataModelValidator):
149
151
  """
150
152
 
151
153
  code = f"{BASE_CODE}-REVERSE-001"
154
+ issue_type = ConsistencyError
152
155
 
153
156
  def run(self) -> list[ConsistencyError]:
154
157
  errors: list[ConsistencyError] = []
@@ -193,6 +196,7 @@ class ReverseConnectionSourcePropertyMissing(DataModelValidator):
193
196
  """
194
197
 
195
198
  code = f"{BASE_CODE}-REVERSE-002"
199
+ issue_type = ConsistencyError
196
200
 
197
201
  def run(self) -> list[ConsistencyError]:
198
202
  errors: list[ConsistencyError] = []
@@ -240,6 +244,7 @@ class ReverseConnectionSourcePropertyWrongType(DataModelValidator):
240
244
  """
241
245
 
242
246
  code = f"{BASE_CODE}-REVERSE-003"
247
+ issue_type = ConsistencyError
243
248
 
244
249
  def run(self) -> list[ConsistencyError]:
245
250
  errors: list[ConsistencyError] = []
@@ -290,6 +295,7 @@ class ReverseConnectionContainerMissing(DataModelValidator):
290
295
  """
291
296
 
292
297
  code = f"{BASE_CODE}-REVERSE-004"
298
+ issue_type = ConsistencyError
293
299
 
294
300
  def run(self) -> list[ConsistencyError]:
295
301
  errors: list[ConsistencyError] = []
@@ -347,6 +353,7 @@ class ReverseConnectionContainerPropertyMissing(DataModelValidator):
347
353
  """
348
354
 
349
355
  code = f"{BASE_CODE}-REVERSE-005"
356
+ issue_type = ConsistencyError
350
357
 
351
358
  def run(self) -> list[ConsistencyError]:
352
359
  errors: list[ConsistencyError] = []
@@ -407,6 +414,7 @@ class ReverseConnectionContainerPropertyWrongType(DataModelValidator):
407
414
  """
408
415
 
409
416
  code = f"{BASE_CODE}-REVERSE-006"
417
+ issue_type = ConsistencyError
410
418
 
411
419
  def run(self) -> list[ConsistencyError]:
412
420
  errors: list[ConsistencyError] = []
@@ -469,6 +477,7 @@ class ReverseConnectionTargetMissing(DataModelValidator):
469
477
  """
470
478
 
471
479
  code = f"{BASE_CODE}-REVERSE-007"
480
+ issue_type = Recommendation
472
481
 
473
482
  def run(self) -> list[Recommendation]:
474
483
  recommendations: list[Recommendation] = []
@@ -525,6 +534,7 @@ class ReverseConnectionPointsToAncestor(DataModelValidator):
525
534
  """
526
535
 
527
536
  code = f"{BASE_CODE}-REVERSE-008"
537
+ issue_type = Recommendation
528
538
 
529
539
  def run(self) -> list[Recommendation]:
530
540
  recommendations: list[Recommendation] = []
@@ -584,6 +594,7 @@ class ReverseConnectionTargetMismatch(DataModelValidator):
584
594
  """
585
595
 
586
596
  code = f"{BASE_CODE}-REVERSE-009"
597
+ issue_type = ConsistencyError
587
598
 
588
599
  def run(self) -> list[ConsistencyError]:
589
600
  errors: list[ConsistencyError] = []
@@ -23,6 +23,7 @@ class ViewSpaceVersionInconsistentWithDataModel(DataModelValidator):
23
23
  """
24
24
 
25
25
  code = f"{BASE_CODE}-001"
26
+ issue_type = Recommendation
26
27
 
27
28
  def run(self) -> list[Recommendation]:
28
29
  recommendations: list[Recommendation] = []
@@ -31,6 +31,7 @@ class ExternalContainerDoesNotExist(DataModelValidator):
31
31
  """
32
32
 
33
33
  code = f"{BASE_CODE}-001"
34
+ issue_type = ConsistencyError
34
35
 
35
36
  def run(self) -> list[ConsistencyError]:
36
37
  errors: list[ConsistencyError] = []
@@ -81,6 +82,7 @@ class ExternalContainerPropertyDoesNotExist(DataModelValidator):
81
82
  """
82
83
 
83
84
  code = f"{BASE_CODE}-002"
85
+ issue_type = ConsistencyError
84
86
 
85
87
  def run(self) -> list[ConsistencyError]:
86
88
  errors: list[ConsistencyError] = []
@@ -137,6 +139,7 @@ class RequiredContainerDoesNotExist(DataModelValidator):
137
139
  """
138
140
 
139
141
  code = f"{BASE_CODE}-003"
142
+ issue_type = ConsistencyError
140
143
 
141
144
  def run(self) -> list[ConsistencyError]:
142
145
  errors: list[ConsistencyError] = []
@@ -38,6 +38,7 @@ class DataModelViewCountIsOutOfLimits(DataModelValidator):
38
38
  """
39
39
 
40
40
  code = f"{BASE_CODE}-DATA-MODEL-001"
41
+ issue_type = ConsistencyError
41
42
 
42
43
  def __init__(
43
44
  self,
@@ -82,6 +83,7 @@ class ViewPropertyCountIsOutOfLimits(DataModelValidator):
82
83
  """
83
84
 
84
85
  code = f"{BASE_CODE}-VIEW-001"
86
+ issue_type = ConsistencyError
85
87
 
86
88
  def __init__(
87
89
  self,
@@ -145,6 +147,7 @@ class ViewContainerCountIsOutOfLimits(DataModelValidator):
145
147
  """
146
148
 
147
149
  code = f"{BASE_CODE}-VIEW-002"
150
+ issue_type = ConsistencyError
148
151
 
149
152
  def __init__(
150
153
  self,
@@ -204,6 +207,7 @@ class ViewImplementsCountIsOutOfLimits(DataModelValidator):
204
207
  """
205
208
 
206
209
  code = f"{BASE_CODE}-VIEW-003"
210
+ issue_type = ConsistencyError
207
211
 
208
212
  def __init__(
209
213
  self,
@@ -258,6 +262,7 @@ class ContainerPropertyCountIsOutOfLimits(DataModelValidator):
258
262
  """
259
263
 
260
264
  code = f"{BASE_CODE}-CONTAINER-001"
265
+ issue_type = ConsistencyError
261
266
 
262
267
  def __init__(
263
268
  self,
@@ -326,6 +331,7 @@ class ContainerPropertyListSizeIsOutOfLimits(DataModelValidator):
326
331
  """
327
332
 
328
333
  code = f"{BASE_CODE}-CONTAINER-002"
334
+ issue_type = ConsistencyError
329
335
 
330
336
  def __init__(
331
337
  self,
@@ -1,3 +1,4 @@
1
+ from collections.abc import Callable
1
2
  from itertools import chain
2
3
 
3
4
  from cognite.neat._client import NeatClient
@@ -56,12 +57,12 @@ class DmsDataModelValidation(OnSuccessIssuesChecker):
56
57
  def __init__(
57
58
  self,
58
59
  client: NeatClient | None = None,
59
- codes: list[str] | None = None,
60
60
  modus_operandi: ModusOperandi = "additive",
61
+ can_run_validator: Callable[[str, type], bool] | None = None,
61
62
  ) -> None:
62
63
  super().__init__()
63
64
  self._client = client
64
- self._codes = codes or ["all"]
65
+ self._can_run_validator = can_run_validator or (lambda code, issue_type: True) # type: ignore
65
66
  self._modus_operandi = modus_operandi
66
67
  self._has_run = False
67
68
 
@@ -155,7 +156,7 @@ class DmsDataModelValidation(OnSuccessIssuesChecker):
155
156
 
156
157
  # Run validators
157
158
  for validator in validators:
158
- if "all" in self._codes or validator.code in self._codes:
159
+ if self._can_run_validator(validator.code, validator.issue_type):
159
160
  self._issues.extend(validator.run())
160
161
 
161
162
  self._has_run = True
@@ -24,6 +24,7 @@ class ViewToContainerMappingNotPossible(DataModelValidator):
24
24
  """
25
25
 
26
26
  code = f"{BASE_CODE}-001"
27
+ issue_type = ConsistencyError
27
28
 
28
29
  def run(self) -> list[ConsistencyError]:
29
30
  errors: list[ConsistencyError] = []
@@ -81,6 +82,7 @@ class ImplementedViewNotExisting(DataModelValidator):
81
82
  """
82
83
 
83
84
  code = f"{BASE_CODE}-002"
85
+ issue_type = ConsistencyError
84
86
 
85
87
  def run(self) -> list[ConsistencyError]:
86
88
  errors: list[ConsistencyError] = []
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Literal
2
2
 
3
3
  from cognite.neat._client import NeatClient
4
+ from cognite.neat._config import NeatConfig
4
5
  from cognite.neat._data_model.deployer.deployer import DeploymentOptions, SchemaDeployer
5
6
  from cognite.neat._data_model.exporters import (
6
7
  DMSAPIExporter,
@@ -19,7 +20,6 @@ from cognite.neat._exceptions import UserInputError
19
20
  from cognite.neat._state_machine import PhysicalState
20
21
  from cognite.neat._store._store import NeatStore
21
22
  from cognite.neat._utils._reader import NeatReader
22
- from cognite.neat._utils.useful_types import ModusOperandi
23
23
 
24
24
  from ._wrappers import session_wrapper
25
25
 
@@ -27,11 +27,12 @@ from ._wrappers import session_wrapper
27
27
  class PhysicalDataModel:
28
28
  """Read from a data source into NeatSession graph store."""
29
29
 
30
- def __init__(self, store: NeatStore, client: NeatClient, mode: ModusOperandi) -> None:
30
+ def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
31
31
  self._store = store
32
32
  self._client = client
33
- self.read = ReadPhysicalDataModel(self._store, self._client)
34
- self.write = WritePhysicalDataModel(self._store, self._client, mode)
33
+ self._config = config
34
+ self.read = ReadPhysicalDataModel(self._store, self._client, self._config)
35
+ self.write = WritePhysicalDataModel(self._store, self._client, self._config)
35
36
 
36
37
  def _repr_html_(self) -> str:
37
38
  if not isinstance(self._store.state, PhysicalState):
@@ -69,9 +70,10 @@ class PhysicalDataModel:
69
70
  class ReadPhysicalDataModel:
70
71
  """Read physical data model from various sources into NeatSession graph store."""
71
72
 
72
- def __init__(self, store: NeatStore, client: NeatClient) -> None:
73
+ def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
73
74
  self._store = store
74
75
  self._client = client
76
+ self._config = config
75
77
 
76
78
  def yaml(self, io: Any, format: Literal["neat", "toolkit"] = "neat") -> None:
77
79
  """Read physical data model from YAML file(s)
@@ -92,7 +94,11 @@ class ReadPhysicalDataModel:
92
94
  reader = DMSAPIImporter.from_yaml(path)
93
95
  else:
94
96
  raise UserInputError(f"Unsupported format: {format}. Supported formats are 'neat' and 'toolkit'.")
95
- on_success = DmsDataModelValidation(self._client)
97
+ on_success = DmsDataModelValidation(
98
+ self._client,
99
+ modus_operandi=self._config.modeling.mode,
100
+ can_run_validator=self._config.validation.can_run_validator,
101
+ )
96
102
 
97
103
  return self._store.read_physical(reader, on_success)
98
104
 
@@ -115,7 +121,11 @@ class ReadPhysicalDataModel:
115
121
  reader = DMSAPIImporter.from_json(path)
116
122
  else:
117
123
  raise UserInputError(f"Unsupported format: {format}. Supported formats are 'neat' and 'toolkit'.")
118
- on_success = DmsDataModelValidation(self._client)
124
+ on_success = DmsDataModelValidation(
125
+ self._client,
126
+ modus_operandi=self._config.modeling.mode,
127
+ can_run_validator=self._config.validation.can_run_validator,
128
+ )
119
129
 
120
130
  return self._store.read_physical(reader, on_success)
121
131
 
@@ -124,7 +134,11 @@ class ReadPhysicalDataModel:
124
134
 
125
135
  path = NeatReader.create(io).materialize_path()
126
136
  reader = DMSTableImporter.from_excel(path)
127
- on_success = DmsDataModelValidation(self._client)
137
+ on_success = DmsDataModelValidation(
138
+ self._client,
139
+ modus_operandi=self._config.modeling.mode,
140
+ can_run_validator=self._config.validation.can_run_validator,
141
+ )
128
142
 
129
143
  return self._store.read_physical(reader, on_success)
130
144
 
@@ -140,7 +154,11 @@ class ReadPhysicalDataModel:
140
154
  reader = DMSAPIImporter.from_cdf(
141
155
  DataModelReference(space=space, external_id=external_id, version=version), self._client
142
156
  )
143
- on_success = DmsDataModelValidation(self._client)
157
+ on_success = DmsDataModelValidation(
158
+ self._client,
159
+ modus_operandi=self._config.modeling.mode,
160
+ can_run_validator=self._config.validation.can_run_validator,
161
+ )
144
162
 
145
163
  return self._store.read_physical(reader, on_success)
146
164
 
@@ -149,10 +167,10 @@ class ReadPhysicalDataModel:
149
167
  class WritePhysicalDataModel:
150
168
  """Write physical data model to various sources from NeatSession graph store."""
151
169
 
152
- def __init__(self, store: NeatStore, client: NeatClient, mode: ModusOperandi) -> None:
170
+ def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
153
171
  self._store = store
154
172
  self._client = client
155
- self._mode = mode
173
+ self._config = config
156
174
 
157
175
  def yaml(self, io: Any, format: Literal["neat", "toolkit"] = "neat") -> None:
158
176
  """Write physical data model to YAML file
@@ -215,7 +233,7 @@ class WritePhysicalDataModel:
215
233
  """Write physical data model with views, containers, and spaces that are in the same space as the data model
216
234
  to CDF.
217
235
 
218
- This method depends on the session mode set when creating the NeatSession.
236
+ This method depends on the session governance profile for data modeling set when creating the NeatSession.
219
237
  - In 'additive' mode, only new or updates to data models/views/containers will be applied.
220
238
  You cannot remove views from data models, properties from views or containers, or
221
239
  indexes or constraints from containers.
@@ -234,7 +252,10 @@ class WritePhysicalDataModel:
234
252
  """
235
253
  writer = DMSAPIExporter()
236
254
  options = DeploymentOptions(
237
- dry_run=dry_run, auto_rollback=rollback, drop_data=drop_data, modus_operandi=self._mode
255
+ dry_run=dry_run,
256
+ auto_rollback=rollback,
257
+ drop_data=drop_data,
258
+ modus_operandi=self._config.modeling.mode,
238
259
  )
239
260
  on_success = SchemaDeployer(self._client, options)
240
261
  return self._store.write_physical(writer, on_success)
@@ -1,13 +1,14 @@
1
1
  import json
2
+ from typing import Literal
2
3
 
3
4
  from cognite.client import ClientConfig, CogniteClient
4
5
 
5
6
  from cognite.neat import _version
6
7
  from cognite.neat._client import NeatClient
8
+ from cognite.neat._config import internal_profiles
7
9
  from cognite.neat._state_machine import EmptyState, PhysicalState
8
10
  from cognite.neat._store import NeatStore
9
11
  from cognite.neat._utils.http_client import ParametersRequest, SuccessResponse
10
- from cognite.neat._utils.useful_types import ModusOperandi
11
12
 
12
13
  from ._issues import Issues
13
14
  from ._opt import Opt
@@ -16,21 +17,38 @@ from ._result import Result
16
17
 
17
18
 
18
19
  class NeatSession:
19
- """A session is an interface for neat operations. It works as
20
- a manager for handling user interactions and orchestrating
21
- the state machine for data model and instance operations.
22
- """
20
+ """A session is an interface for neat operations."""
23
21
 
24
- def __init__(self, client: CogniteClient | ClientConfig, mode: ModusOperandi = "additive") -> None:
22
+ def __init__(
23
+ self,
24
+ client: CogniteClient | ClientConfig,
25
+ config: Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] | None = None,
26
+ ) -> None:
27
+ """Initialize a Neat session.
28
+
29
+ Args:
30
+ client (CogniteClient | ClientConfig): The Cognite client or client configuration to use for the session.
31
+ config (Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] | None):
32
+ The configuration profile to use for the session.
33
+ If None, the default profile "legacy-additive" is used. Meaning that Neat will perform additive modeling
34
+ and apply only validations that were part of the legacy Neat version."""
35
+
36
+ # Load configuration
37
+ if config and config not in internal_profiles():
38
+ raise ValueError(f"Profile '{config}' not found among internal profiles.")
39
+
40
+ self._config = internal_profiles()[config or "legacy-additive"]
41
+
42
+ # Use configuration for physical data model
25
43
  self._store = NeatStore()
26
44
  self._client = NeatClient(client)
27
- self.physical_data_model = PhysicalDataModel(self._store, self._client, mode)
45
+ self.physical_data_model = PhysicalDataModel(self._store, self._client, self._config)
28
46
  self.issues = Issues(self._store)
29
47
  self.result = Result(self._store)
30
48
  self.opt = Opt(self._store)
31
49
 
32
50
  if self.opt._collector.can_collect:
33
- self.opt._collector.collect("initSession", {"mode": mode})
51
+ self.opt._collector.collect("initSession", {"mode": self._config.modeling.mode})
34
52
 
35
53
  self._welcome_message()
36
54
 
@@ -50,6 +68,7 @@ class NeatSession:
50
68
  message += f" (Organization: '{organization}')"
51
69
 
52
70
  print(message)
71
+ print(self._config)
53
72
 
54
73
  @property
55
74
  def version(self) -> str:
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.127.27"
1
+ __version__ = "0.127.29"
2
2
  __engine__ = "^2.0.4"
@@ -243,17 +243,21 @@ class _DMSExporter:
243
243
  connection = cast(EdgeEntity, prop.connection)
244
244
 
245
245
  if connection.direction == "inwards" and isinstance(prop.value_type, ViewEntity):
246
- edge_type_candidates = outwards_type_by_view_value_type.get((prop.view, prop.value_type), [])
247
- if len(edge_type_candidates) == 0:
248
- # Warning in validation, should not have an inwards connection without an outwards connection
249
- edge_type = cls._default_edge_type_from_view_id(prop.view.as_id(), prop_id)
250
- elif len(edge_type_candidates) == 1:
251
- edge_type = edge_type_candidates[0]
246
+ if connection.edge_type is not None:
247
+ edge_type = connection.edge_type.as_reference()
252
248
  else:
253
- raise NeatValueError(
254
- f"Cannot infer edge type for {view_id}.{prop_id}, multiple candidates: {edge_type_candidates}."
255
- "Please specify edge type explicitly, i.e., edge(type=<YOUR_TYPE>)."
256
- )
249
+ edge_type_candidates = outwards_type_by_view_value_type.get((prop.view, prop.value_type), [])
250
+ if len(edge_type_candidates) == 0:
251
+ # Warning in validation, should not have an inwards connection without an outwards connection
252
+ edge_type = cls._default_edge_type_from_view_id(prop.view.as_id(), prop_id)
253
+ elif len(edge_type_candidates) == 1:
254
+ edge_type = edge_type_candidates[0]
255
+ else:
256
+ raise NeatValueError(
257
+ f"Cannot infer edge type for {view_id}.{prop_id}, multiple candidates: "
258
+ f"{edge_type_candidates}. "
259
+ "Please specify edge type explicitly, i.e., edge(type=<YOUR_TYPE>)."
260
+ )
257
261
  view_property_id = (prop.view, prop.view_property)
258
262
  edge_types_by_view_property_id[view_property_id] = edge_type
259
263
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-neat
3
- Version: 0.127.27
3
+ Version: 0.127.29
4
4
  Summary: Knowledge graph transformation
5
5
  Project-URL: Documentation, https://cognite-neat.readthedocs-hosted.com/
6
6
  Project-URL: Homepage, https://cognite-neat.readthedocs-hosted.com/
@@ -1,7 +1,8 @@
1
1
  cognite/neat/__init__.py,sha256=Lo4DbjDOwnhCYUoAgPp5RG1fDdF7OlnomalTe7n1ydw,211
2
+ cognite/neat/_config.py,sha256=W42Lj5PNR9X1is9vIPHLHvkl_rpU5PCrDuQI4zSNTJQ,6359
2
3
  cognite/neat/_exceptions.py,sha256=ox-5hXpee4UJlPE7HpuEHV2C96aLbLKo-BhPDoOAzhA,1650
3
4
  cognite/neat/_issues.py,sha256=wH1mnkrpBsHUkQMGUHFLUIQWQlfJ_qMfdF7q0d9wNhY,1871
4
- cognite/neat/_version.py,sha256=cz0CG5j6gdpaAmogpF9j31GRp2vxv4fKDwo6HE72kx0,47
5
+ cognite/neat/_version.py,sha256=HnkgU0oEwuWFxHjFpJFKDNKAV0Zn342c-qMWtztHfKE,47
5
6
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
7
  cognite/neat/v1.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
7
8
  cognite/neat/_client/__init__.py,sha256=75Bh7eGhaN4sOt3ZcRzHl7pXaheu1z27kmTHeaI05vo,114
@@ -74,20 +75,20 @@ cognite/neat/_data_model/models/entities/_identifiers.py,sha256=uBiK4ot3V0b_LGXu
74
75
  cognite/neat/_data_model/models/entities/_parser.py,sha256=zef_pSDZYMZrJl4IKreFDR577KutfhtN1xpH3Ayjt2o,7669
75
76
  cognite/neat/_data_model/validation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
77
  cognite/neat/_data_model/validation/dms/__init__.py,sha256=Ps26r8bY4sLPsceaS6CpgS-s6JxN_zKbiteF5RrQYYw,2512
77
- cognite/neat/_data_model/validation/dms/_ai_readiness.py,sha256=_7R4BVVcJw27OYmMS-GxxcvQdocr67sU1gGouUo81EU,10525
78
- cognite/neat/_data_model/validation/dms/_base.py,sha256=HJRoo1fX2Fu0bzlPKhn3VOsrtaOv4ea49BxvEpfOjIY,13272
79
- cognite/neat/_data_model/validation/dms/_connections.py,sha256=goIcsGV4yYb-d9ltXg4eM5qxt8iErIIxtkiKJXZUYyM,26937
80
- cognite/neat/_data_model/validation/dms/_consistency.py,sha256=K0mfAwwpW6zQXGDEldrA7Lgqk-giV_Y-gUKyb8Dw47k,2445
81
- cognite/neat/_data_model/validation/dms/_containers.py,sha256=Jqwu2JH36eyqA7xt6_UaLEl-zhZi4itI1dmb73GpJMY,7431
82
- cognite/neat/_data_model/validation/dms/_limits.py,sha256=LVxF1qmeEdUVVAPmywCgxWJ9BR7Ai6nI4X1ylJ7pv8I,15982
83
- cognite/neat/_data_model/validation/dms/_orchestrator.py,sha256=1cwGrtsSs6Q1vT-8vbZ2DM_3WYu9YTSRDbDGJhkTCBs,10672
84
- cognite/neat/_data_model/validation/dms/_views.py,sha256=3bHEEbFKTR_QH_tiJYHppQsZ9ruApv-kdyfehEjIlCU,4198
78
+ cognite/neat/_data_model/validation/dms/_ai_readiness.py,sha256=yKT9KHS4iD7ijKBjjAGbREKWDGunBuDTl9FCX0RiW2Q,10717
79
+ cognite/neat/_data_model/validation/dms/_base.py,sha256=3Lqs8pzMQZznxhUr8esEe9H7F2wM3EKEl2H64GLS5OA,13344
80
+ cognite/neat/_data_model/validation/dms/_connections.py,sha256=6ea8WZNqYT9SPluwXvg0mgDJpzZMqpPC5xonbdLrT4E,27305
81
+ cognite/neat/_data_model/validation/dms/_consistency.py,sha256=uJ6coAVupD3WfeeXxoCIebM8WSnR78GXBXIBnN59aao,2477
82
+ cognite/neat/_data_model/validation/dms/_containers.py,sha256=UuvzrBBw45F5f9uzCh97lW4t7m7XIIT1I9FnzzYUYv4,7533
83
+ cognite/neat/_data_model/validation/dms/_limits.py,sha256=UDJ3oY2Xp96Zw2QpsGM4MQ6gZCrDuoKhhuEY2uQJLP4,16186
84
+ cognite/neat/_data_model/validation/dms/_orchestrator.py,sha256=0KpXMWlofpELCE3QXQh6SDyP3IoATPrR1412HJzAN6w,10811
85
+ cognite/neat/_data_model/validation/dms/_views.py,sha256=M4egIa7UAMGtZlqzIxx6ZzL4e_qo8GbDGh7vs9wywD8,4266
85
86
  cognite/neat/_session/__init__.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
86
87
  cognite/neat/_session/_issues.py,sha256=E8UQeSJURg2dm4MF1pfD9dp-heSRT7pgQZgKlD1-FGs,2723
87
88
  cognite/neat/_session/_opt.py,sha256=QcVK08JMmVzJpD0GKHelbljMOQi6CMD1w-maQOlbyZQ,1350
88
- cognite/neat/_session/_physical.py,sha256=VyMvvPyN9khR_26rMS0kVuZg23k5c8Hfs7XDeMrkF_w,10526
89
+ cognite/neat/_session/_physical.py,sha256=0EdpFS5v4pk4XJju8vZm3pHIM4AlSaCjxGRKeMAcq9g,11304
89
90
  cognite/neat/_session/_result.py,sha256=po2X4s-Tioe0GQAGCfK862hKXNRX5YjJZsEzNcTC8nI,7879
90
- cognite/neat/_session/_session.py,sha256=rezw1MEMN8DPA6waULt5-mdeMuwq3ZNnSb1CSsKM0ko,2584
91
+ cognite/neat/_session/_session.py,sha256=tWEvzi_85Ji52i2q5vJ7xIg3yR-8ywVCH8FjgncdlnU,3471
91
92
  cognite/neat/_session/_wrappers.py,sha256=9t_MnJ0Sw_v-f6oTIh8dtAT-3oEbqumGuND97aPCC3M,3581
92
93
  cognite/neat/_session/_html/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
94
  cognite/neat/_session/_html/_render.py,sha256=fD8iee4ql50CrHGH41SSh9Tw1lM0tHt-NF0OnnxHosg,1193
@@ -190,7 +191,7 @@ cognite/neat/v0/core/_data_model/models/mapping/__init__.py,sha256=T68Hf7rhiXa7b
190
191
  cognite/neat/v0/core/_data_model/models/mapping/_classic2core.py,sha256=F0zusTh9pPR4z-RExPw3o4EMBSU2si6FJLuej2a3JzM,1430
191
192
  cognite/neat/v0/core/_data_model/models/mapping/_classic2core.yaml,sha256=ei-nuivNWVW9HmvzDBKIPF6ZdgaMq64XHw_rKm0CMxg,22584
192
193
  cognite/neat/v0/core/_data_model/models/physical/__init__.py,sha256=pH5ZF8jiW0A2w7VCSoHUsXxe894QFvTtgjxXNGVVaxk,990
193
- cognite/neat/v0/core/_data_model/models/physical/_exporter.py,sha256=ega1Yyosq-D9XYCypx9wtuMTFwS0EMRo_e3JwW434XU,30335
194
+ cognite/neat/v0/core/_data_model/models/physical/_exporter.py,sha256=slECWjHinQscoR86dDmvB4OalyCvE9D_bSKp4v_GINg,30555
194
195
  cognite/neat/v0/core/_data_model/models/physical/_unverified.py,sha256=eE2D2DPfqEfOnYaUmOTPhBpHp9oN-gxjn8CTe_GKtz0,22419
195
196
  cognite/neat/v0/core/_data_model/models/physical/_validation.py,sha256=g0uSZImYaGmMZEwHUpb8KF9LTPiztz7k7cFQT3jrs4Y,41326
196
197
  cognite/neat/v0/core/_data_model/models/physical/_verified.py,sha256=49dn3K_gWO3VgfS-SK4kHpbSJywtj4mwES3iy-43z4M,29824
@@ -312,7 +313,7 @@ cognite/neat/v0/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4
312
313
  cognite/neat/v0/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
313
314
  cognite/neat/v0/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
314
315
  cognite/neat/v0/session/engine/_load.py,sha256=u0x7vuQCRoNcPt25KJBJRn8sJabonYK4vtSZpiTdP4k,5201
315
- cognite_neat-0.127.27.dist-info/METADATA,sha256=QOiSm6GFoghApq_RDl6xHl3ACh9pnoLdrVRV_j-uAGk,9150
316
- cognite_neat-0.127.27.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
317
- cognite_neat-0.127.27.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
318
- cognite_neat-0.127.27.dist-info/RECORD,,
316
+ cognite_neat-0.127.29.dist-info/METADATA,sha256=BODhl8sBHt2YtVcTIr7NK7Pcew1A68onQGB63HaKYDo,9150
317
+ cognite_neat-0.127.29.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
318
+ cognite_neat-0.127.29.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
319
+ cognite_neat-0.127.29.dist-info/RECORD,,