cognite-neat 0.127.31__py3-none-any.whl → 0.128.1__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/_config.py CHANGED
@@ -1,9 +1,12 @@
1
1
  import sys
2
2
  from pathlib import Path
3
+ from typing import Any, Literal, TypeAlias
3
4
 
4
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel, ConfigDict, Field
5
6
 
7
+ from cognite.neat._exceptions import UserInputError
6
8
  from cognite.neat._issues import ConsistencyError, ModelSyntaxError
9
+ from cognite.neat._utils.text import humanize_collection
7
10
  from cognite.neat._utils.useful_types import ModusOperandi
8
11
 
9
12
  if sys.version_info >= (3, 11):
@@ -11,8 +14,14 @@ if sys.version_info >= (3, 11):
11
14
  else:
12
15
  import tomli # type: ignore
13
16
 
17
+ PredefinedProfile: TypeAlias = Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"]
14
18
 
15
- class ValidationConfig(BaseModel, populate_by_name=True):
19
+
20
+ class ConfigModel(BaseModel):
21
+ model_config = ConfigDict(populate_by_name=True, validate_assignment=True)
22
+
23
+
24
+ class ValidationConfig(ConfigModel):
16
25
  """Validation configuration."""
17
26
 
18
27
  exclude: list[str] = Field(default_factory=list)
@@ -71,18 +80,45 @@ class ValidationConfig(BaseModel, populate_by_name=True):
71
80
  return f"Excluded Rules: {', '.join(self.exclude)}"
72
81
 
73
82
 
74
- class ModelingConfig(BaseModel, populate_by_name=True):
83
+ class ModelingConfig(ConfigModel):
75
84
  """Modeling configuration."""
76
85
 
77
86
  mode: ModusOperandi = "additive"
78
87
 
79
88
 
80
- class NeatConfig(BaseModel, populate_by_name=True):
89
+ class AlphaFlagConfig(ConfigModel):
90
+ """Alpha feature flags configuration."""
91
+
92
+ fix_validation_issues: bool = Field(
93
+ default=False,
94
+ description="If enabled, Neat will attempt to automatically fix certain validation issues in the data model.",
95
+ )
96
+
97
+ def __setattr__(self, key: str, value: Any) -> None:
98
+ """Set attribute value or raise AttributeError."""
99
+ if key in self.model_fields:
100
+ super().__setattr__(key, value)
101
+ else:
102
+ available_flags = humanize_collection(type(self).model_fields.keys())
103
+ raise UserInputError(f"Alpha flag '{key}' not found. Available flags: {available_flags}.")
104
+
105
+ def _repr_html_(self) -> str:
106
+ """HTML representation for Jupyter notebooks."""
107
+ lines = ["<b>Alpha Feature Flags:</b>"]
108
+ for field_name, field in type(self).model_fields.items():
109
+ value = getattr(self, field_name)
110
+ display = "Enabled" if value else "Disabled"
111
+ lines.append(f"<li><b>{field_name}</b>: {display} - {field.description}</li>")
112
+ return "<ul>" + "\n".join(lines) + "</ul>"
113
+
114
+
115
+ class NeatConfig(ConfigModel):
81
116
  """Configuration for a custom profile."""
82
117
 
83
118
  profile: str
84
119
  validation: ValidationConfig
85
120
  modeling: ModelingConfig
121
+ alpha: AlphaFlagConfig = Field(default_factory=AlphaFlagConfig)
86
122
 
87
123
  def __str__(self) -> str:
88
124
  """Human-readable configuration summary."""
@@ -93,8 +129,19 @@ class NeatConfig(BaseModel, populate_by_name=True):
93
129
  ]
94
130
  return "\n".join(lines)
95
131
 
132
+ @classmethod
133
+ def create_predefined(cls, profile: PredefinedProfile = "legacy-additive") -> "NeatConfig":
134
+ """Create NeatConfig from internal profiles."""
135
+ available_profiles = internal_profiles()
136
+ if profile not in available_profiles:
137
+ raise UserInputError(
138
+ f"Profile {profile!r} not found. Available predefined profiles: "
139
+ f"{humanize_collection(available_profiles.keys())}."
140
+ )
141
+ return available_profiles[profile]
142
+
96
143
 
97
- def internal_profiles() -> dict[str, NeatConfig]:
144
+ def internal_profiles() -> dict[PredefinedProfile, NeatConfig]:
98
145
  """Get internal NeatConfig profile by name."""
99
146
  return {
100
147
  "legacy-additive": NeatConfig(
@@ -136,7 +183,7 @@ def internal_profiles() -> dict[str, NeatConfig]:
136
183
  }
137
184
 
138
185
 
139
- def get_neat_config(config_file_name: str, profile: str) -> NeatConfig:
186
+ def get_neat_config_from_file(config_file_name: str, profile: str) -> NeatConfig:
140
187
  """Get NeatConfig from file or internal profiles.
141
188
 
142
189
  Args:
@@ -68,7 +68,7 @@ class PhysicalDataModel:
68
68
 
69
69
  @session_wrapper
70
70
  class ReadPhysicalDataModel:
71
- """Read physical data model from various sources into NeatSession graph store."""
71
+ """Read physical data model from various sources into NeatSession store."""
72
72
 
73
73
  def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
74
74
  self._store = store
@@ -130,7 +130,12 @@ class ReadPhysicalDataModel:
130
130
  return self._store.read_physical(reader, on_success)
131
131
 
132
132
  def excel(self, io: Any) -> None:
133
- """Read physical data model from Excel file"""
133
+ """Read physical data model from Excel file
134
+
135
+ Args:
136
+ io (Any): The file path or buffer to read from.
137
+
138
+ """
134
139
 
135
140
  path = NeatReader.create(io).materialize_path()
136
141
  reader = DMSTableImporter.from_excel(path)
@@ -165,7 +170,7 @@ class ReadPhysicalDataModel:
165
170
 
166
171
  @session_wrapper
167
172
  class WritePhysicalDataModel:
168
- """Write physical data model to various sources from NeatSession graph store."""
173
+ """Write physical data model to various sources from NeatSession store."""
169
174
 
170
175
  def __init__(self, store: NeatStore, client: NeatClient, config: NeatConfig) -> None:
171
176
  self._store = store
@@ -1,11 +1,10 @@
1
1
  import json
2
- from typing import Literal
3
2
 
4
3
  from cognite.client import ClientConfig, CogniteClient
5
4
 
6
5
  from cognite.neat import _version
7
6
  from cognite.neat._client import NeatClient
8
- from cognite.neat._config import internal_profiles
7
+ from cognite.neat._config import NeatConfig, PredefinedProfile
9
8
  from cognite.neat._state_machine import EmptyState, PhysicalState
10
9
  from cognite.neat._store import NeatStore
11
10
  from cognite.neat._utils.http_client import ParametersRequest, SuccessResponse
@@ -20,24 +19,18 @@ class NeatSession:
20
19
  """A session is an interface for neat operations."""
21
20
 
22
21
  def __init__(
23
- self,
24
- client: CogniteClient | ClientConfig,
25
- config: Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] | None = None,
22
+ self, client: CogniteClient | ClientConfig, config: PredefinedProfile | NeatConfig = "legacy-additive"
26
23
  ) -> None:
27
24
  """Initialize a Neat session.
28
25
 
29
26
  Args:
30
27
  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):
28
+ config (Literal["legacy-additive", "legacy-rebuild", "deep-additive", "deep-rebuild"] | NeatConfig):
32
29
  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"]
30
+ Defaults to "legacy-additive". This means Neat will perform additive modeling
31
+ and apply only validations that were part of the legacy Neat version.
32
+ """
33
+ self._config = NeatConfig.create_predefined(config) if isinstance(config, str) else config
41
34
 
42
35
  # Use configuration for physical data model
43
36
  self._store = NeatStore()
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.127.31"
1
+ __version__ = "0.128.1"
2
2
  __engine__ = "^2.0.4"
@@ -93,7 +93,7 @@ class EnvironmentVariables:
93
93
  LOGIN_FLOW: _LOGIN_FLOW = "infer"
94
94
  IDP_CLIENT_ID: str | None = None
95
95
  IDP_CLIENT_SECRET: str | None = None
96
- TOKEN: str | None = None
96
+ CDF_TOKEN: str | None = None
97
97
 
98
98
  IDP_TENANT_ID: str | None = None
99
99
  IDP_TOKEN_URL: str | None = None
@@ -103,7 +103,7 @@ class EnvironmentVariables:
103
103
  IDP_SCOPES: str | None = None
104
104
  IDP_AUTHORITY_URL: str | None = None
105
105
  CDF_MAX_WORKERS: int | None = None
106
- CDF_TIMEOUT: int | None = None
106
+ CDF_CLIENT_TIMEOUT: int | None = None
107
107
  CDF_REDIRECT_PORT: int = 53_000
108
108
 
109
109
  def __post_init__(self) -> None:
@@ -155,7 +155,7 @@ class EnvironmentVariables:
155
155
  LOGIN_FLOW=os.environ.get("LOGIN_FLOW", "infer"), # type: ignore[arg-type]
156
156
  IDP_CLIENT_ID=os.environ.get("IDP_CLIENT_ID"),
157
157
  IDP_CLIENT_SECRET=os.environ.get("IDP_CLIENT_SECRET"),
158
- TOKEN=os.environ.get("TOKEN"),
158
+ CDF_TOKEN=os.environ.get("CDF_TOKEN"),
159
159
  CDF_URL=os.environ.get("CDF_URL"),
160
160
  IDP_TOKEN_URL=os.environ.get("IDP_TOKEN_URL"),
161
161
  IDP_TENANT_ID=os.environ.get("IDP_TENANT_ID"),
@@ -163,7 +163,7 @@ class EnvironmentVariables:
163
163
  IDP_SCOPES=os.environ.get("IDP_SCOPES"),
164
164
  IDP_AUTHORITY_URL=os.environ.get("IDP_AUTHORITY_URL"),
165
165
  CDF_MAX_WORKERS=int(os.environ["CDF_MAX_WORKERS"]) if "CDF_MAX_WORKERS" in os.environ else None,
166
- CDF_TIMEOUT=int(os.environ["CDF_TIMEOUT"]) if "CDF_TIMEOUT" in os.environ else None,
166
+ CDF_CLIENT_TIMEOUT=int(os.environ["CDF_CLIENT_TIMEOUT"]) if "CDF_CLIENT_TIMEOUT" in os.environ else None,
167
167
  CDF_REDIRECT_PORT=int(os.environ.get("CDF_REDIRECT_PORT", 53_000)),
168
168
  )
169
169
 
@@ -179,7 +179,7 @@ class EnvironmentVariables:
179
179
  IDP_CLIENT_ID="neat",
180
180
  IDP_CLIENT_SECRET="secret",
181
181
  IDP_SCOPES="project:read,project:write",
182
- CDF_TIMEOUT=60,
182
+ CDF_CLIENT_TIMEOUT=60,
183
183
  CDF_MAX_WORKERS=3,
184
184
  )
185
185
 
@@ -228,9 +228,9 @@ class EnvironmentVariables:
228
228
  )
229
229
 
230
230
  def get_token(self) -> Token:
231
- if not self.TOKEN:
232
- raise KeyError("TOKEN must be set in the environment", "TOKEN")
233
- return Token(self.TOKEN)
231
+ if not self.CDF_TOKEN:
232
+ raise KeyError("CDF_TOKEN must be set in the environment", "CDF_TOKEN")
233
+ return Token(self.CDF_TOKEN)
234
234
 
235
235
  def get_client(self) -> CogniteClient:
236
236
  config = ClientConfig(
@@ -239,7 +239,7 @@ class EnvironmentVariables:
239
239
  credentials=self.get_credentials(),
240
240
  base_url=self.cdf_url,
241
241
  max_workers=self.CDF_MAX_WORKERS,
242
- timeout=self.CDF_TIMEOUT,
242
+ timeout=self.CDF_CLIENT_TIMEOUT,
243
243
  )
244
244
  return CogniteClient(config)
245
245
 
@@ -298,7 +298,7 @@ def _prompt_user() -> EnvironmentVariables:
298
298
  variables.LOGIN_FLOW = login_flow # type: ignore[assignment]
299
299
  if login_flow == "token":
300
300
  token = Prompt.ask("Enter token")
301
- variables.TOKEN = token
301
+ variables.CDF_TOKEN = token
302
302
  return variables
303
303
 
304
304
  variables.IDP_CLIENT_ID = Prompt.ask("Enter IDP Client ID")
cognite/neat/v1.py CHANGED
@@ -1,3 +1,4 @@
1
+ from ._config import NeatConfig
1
2
  from ._session import NeatSession
2
3
 
3
- __all__ = ["NeatSession"]
4
+ __all__ = ["NeatConfig", "NeatSession"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cognite-neat
3
- Version: 0.127.31
3
+ Version: 0.128.1
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,10 +1,10 @@
1
1
  cognite/neat/__init__.py,sha256=Lo4DbjDOwnhCYUoAgPp5RG1fDdF7OlnomalTe7n1ydw,211
2
- cognite/neat/_config.py,sha256=W42Lj5PNR9X1is9vIPHLHvkl_rpU5PCrDuQI4zSNTJQ,6359
2
+ cognite/neat/_config.py,sha256=NXObeA-860LV40KlY4orsqjMGACa0jKRz2UE5L9kH6U,8401
3
3
  cognite/neat/_exceptions.py,sha256=ox-5hXpee4UJlPE7HpuEHV2C96aLbLKo-BhPDoOAzhA,1650
4
4
  cognite/neat/_issues.py,sha256=wH1mnkrpBsHUkQMGUHFLUIQWQlfJ_qMfdF7q0d9wNhY,1871
5
- cognite/neat/_version.py,sha256=rvkcySAmEggTDqAglgVEXmb2SsDZO3WaXGVcdNwHKNU,47
5
+ cognite/neat/_version.py,sha256=8_r13miyHXqhT9zkzKnh5TPTyZ_MrFhvDyCdgRp7mMg,46
6
6
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- cognite/neat/v1.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
7
+ cognite/neat/v1.py,sha256=sLLxIO5MPsC0hsaex7HmMVVOzH1A8KTQBF2INrp_YCg,107
8
8
  cognite/neat/_client/__init__.py,sha256=75Bh7eGhaN4sOt3ZcRzHl7pXaheu1z27kmTHeaI05vo,114
9
9
  cognite/neat/_client/api.py,sha256=nbxCdWBXcTVM6MrQeT_VpB6ehfoI544JHPFq-ejQKCY,292
10
10
  cognite/neat/_client/client.py,sha256=h0HELAHiBFxMNInkDu4AzbgfEIXqeM0BqqnMBmXjgi0,903
@@ -86,9 +86,9 @@ cognite/neat/_data_model/validation/dms/_views.py,sha256=M4egIa7UAMGtZlqzIxx6ZzL
86
86
  cognite/neat/_session/__init__.py,sha256=owqW5Mml2DSZx1AvPvwNRTBngfhBNrQ6EH-7CKL7Jp0,61
87
87
  cognite/neat/_session/_issues.py,sha256=E8UQeSJURg2dm4MF1pfD9dp-heSRT7pgQZgKlD1-FGs,2723
88
88
  cognite/neat/_session/_opt.py,sha256=QcVK08JMmVzJpD0GKHelbljMOQi6CMD1w-maQOlbyZQ,1350
89
- cognite/neat/_session/_physical.py,sha256=0EdpFS5v4pk4XJju8vZm3pHIM4AlSaCjxGRKeMAcq9g,11304
89
+ cognite/neat/_session/_physical.py,sha256=ajNanS87THBESx_y1x6p6LomqOe51b197VKPaBPLZ30,11377
90
90
  cognite/neat/_session/_result.py,sha256=po2X4s-Tioe0GQAGCfK862hKXNRX5YjJZsEzNcTC8nI,7879
91
- cognite/neat/_session/_session.py,sha256=tWEvzi_85Ji52i2q5vJ7xIg3yR-8ywVCH8FjgncdlnU,3471
91
+ cognite/neat/_session/_session.py,sha256=BNDrG5VdjviDGHiZD9HZcyuDCePdZAGHE-n0Yo8FW7Q,3238
92
92
  cognite/neat/_session/_wrappers.py,sha256=9t_MnJ0Sw_v-f6oTIh8dtAT-3oEbqumGuND97aPCC3M,3581
93
93
  cognite/neat/_session/_html/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
94
  cognite/neat/_session/_html/_render.py,sha256=fD8iee4ql50CrHGH41SSh9Tw1lM0tHt-NF0OnnxHosg,1193
@@ -268,7 +268,7 @@ cognite/neat/v0/core/_store/_instance.py,sha256=zt7pgYwgMbnAwz7Xn-_AiMsXhbBdwXWB
268
268
  cognite/neat/v0/core/_store/_provenance.py,sha256=Q96wkVXRovO_uTlNvwCAOl6pAoWItTgFq1F79L_FqBk,7335
269
269
  cognite/neat/v0/core/_store/exceptions.py,sha256=dTaBSt7IV7XWtS3EsE8lBX1Dv3tfWX1nIEgGHkluy3s,1668
270
270
  cognite/neat/v0/core/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
271
- cognite/neat/v0/core/_utils/auth.py,sha256=CAAmIYT_Cy458XXsx10ggPhatPbL6sGt90C3DpJAog0,14766
271
+ cognite/neat/v0/core/_utils/auth.py,sha256=SHGujntO3_dGMzCXyF6R24ECtN8DZmmM_1cKigD6byk,14840
272
272
  cognite/neat/v0/core/_utils/auxiliary.py,sha256=lflbBC_cxO3PyQGvnwIGDZZIOSGNNyNrbawaFe8tNys,7115
273
273
  cognite/neat/v0/core/_utils/collection_.py,sha256=hYyvje47AAvY7sCmZy0NouX1OMVKLhJ6qAzwg0DIOhg,2382
274
274
  cognite/neat/v0/core/_utils/graph_transformations_report.py,sha256=-V0LM9BI_bHT6PTTG9Qby4PGtFkYU3cxtF0cz8KoKek,1238
@@ -313,7 +313,7 @@ cognite/neat/v0/session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4
313
313
  cognite/neat/v0/session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
314
314
  cognite/neat/v0/session/engine/_interface.py,sha256=3W-cYr493c_mW3P5O6MKN1xEQg3cA7NHR_ev3zdF9Vk,533
315
315
  cognite/neat/v0/session/engine/_load.py,sha256=u0x7vuQCRoNcPt25KJBJRn8sJabonYK4vtSZpiTdP4k,5201
316
- cognite_neat-0.127.31.dist-info/METADATA,sha256=M2giADSPBb8V9B1ejRjCNgGcYL8hZCdPoyg3Ddwrt9M,9150
317
- cognite_neat-0.127.31.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
318
- cognite_neat-0.127.31.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
319
- cognite_neat-0.127.31.dist-info/RECORD,,
316
+ cognite_neat-0.128.1.dist-info/METADATA,sha256=AfgJZmmNY7WbFk1_4pTwjfxNXsJiVvNW_eD29cQWlfE,9149
317
+ cognite_neat-0.128.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
318
+ cognite_neat-0.128.1.dist-info/licenses/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
319
+ cognite_neat-0.128.1.dist-info/RECORD,,