cognite-neat 1.0.23__py3-none-any.whl → 1.0.25__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/__init__.py +1 -1
- cognite/neat/_client/init/credentials.py +0 -10
- cognite/neat/_client/init/env_vars.py +34 -22
- cognite/neat/_client/init/interactive.py +133 -0
- cognite/neat/_client/init/main.py +25 -15
- cognite/neat/_data_model/exporters/_api_exporter.py +90 -5
- cognite/neat/_session/_session.py +13 -1
- cognite/neat/_utils/repo.py +6 -4
- cognite/neat/_version.py +1 -1
- {cognite_neat-1.0.23.dist-info → cognite_neat-1.0.25.dist-info}/METADATA +1 -1
- {cognite_neat-1.0.23.dist-info → cognite_neat-1.0.25.dist-info}/RECORD +12 -11
- {cognite_neat-1.0.23.dist-info → cognite_neat-1.0.25.dist-info}/WHEEL +0 -0
cognite/neat/__init__.py
CHANGED
|
@@ -12,7 +12,6 @@ def get_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
|
12
12
|
"client_credentials": create_client_credentials,
|
|
13
13
|
"interactive": create_interactive_credentials,
|
|
14
14
|
"token": create_token_credentials,
|
|
15
|
-
"infer": create_infer_credentials,
|
|
16
15
|
}
|
|
17
16
|
return options[env_vars.LOGIN_FLOW](env_vars)
|
|
18
17
|
|
|
@@ -59,12 +58,3 @@ def create_token_credentials(env_vars: ClientEnvironmentVariables) -> Credential
|
|
|
59
58
|
if not env_vars.CDF_TOKEN:
|
|
60
59
|
raise ValueError("CDF_TOKEN environment variable must be set for token authentication.")
|
|
61
60
|
return Token(env_vars.CDF_TOKEN)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def create_infer_credentials(env_vars: ClientEnvironmentVariables) -> CredentialProvider:
|
|
65
|
-
if env_vars.IDP_CLIENT_SECRET:
|
|
66
|
-
return create_client_credentials(env_vars)
|
|
67
|
-
elif env_vars.CDF_TOKEN:
|
|
68
|
-
return create_token_credentials(env_vars)
|
|
69
|
-
else:
|
|
70
|
-
return create_interactive_credentials(env_vars)
|
|
@@ -4,7 +4,6 @@ from typing import Any, Literal, TypeAlias, get_args
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, ConfigDict, ValidationError
|
|
6
6
|
|
|
7
|
-
from cognite.neat._utils.repo import get_repo_root
|
|
8
7
|
from cognite.neat._utils.validation import humanize_validation_error
|
|
9
8
|
|
|
10
9
|
if sys.version_info >= (3, 11):
|
|
@@ -12,9 +11,10 @@ if sys.version_info >= (3, 11):
|
|
|
12
11
|
else:
|
|
13
12
|
from typing_extensions import Self
|
|
14
13
|
|
|
15
|
-
LoginFlow: TypeAlias = Literal["
|
|
16
|
-
|
|
14
|
+
LoginFlow: TypeAlias = Literal["client_credentials", "interactive", "token"]
|
|
15
|
+
AVAILABLE_LOGIN_FLOWS: tuple[LoginFlow, ...] = get_args(LoginFlow)
|
|
17
16
|
Provider: TypeAlias = Literal["entra_id", "auth0", "cdf", "other"]
|
|
17
|
+
AVAILABLE_PROVIDERS: tuple[Provider, ...] = get_args(Provider)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ClientEnvironmentVariables(BaseModel):
|
|
@@ -24,7 +24,7 @@ class ClientEnvironmentVariables(BaseModel):
|
|
|
24
24
|
CDF_CLUSTER: str
|
|
25
25
|
CDF_PROJECT: str
|
|
26
26
|
PROVIDER: Provider = "entra_id"
|
|
27
|
-
LOGIN_FLOW: LoginFlow = "
|
|
27
|
+
LOGIN_FLOW: LoginFlow = "client_credentials"
|
|
28
28
|
|
|
29
29
|
IDP_CLIENT_ID: str | None = None
|
|
30
30
|
IDP_CLIENT_SECRET: str | None = None
|
|
@@ -103,24 +103,7 @@ class ClientEnvironmentVariables(BaseModel):
|
|
|
103
103
|
)
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def
|
|
107
|
-
to_search: list[tuple[str, Path]] = []
|
|
108
|
-
try:
|
|
109
|
-
repo_root = get_repo_root()
|
|
110
|
-
except RuntimeError:
|
|
111
|
-
...
|
|
112
|
-
else:
|
|
113
|
-
to_search.append(("repository root", repo_root))
|
|
114
|
-
to_search.append(("current working directory", Path.cwd()))
|
|
115
|
-
for location_desc, path in to_search:
|
|
116
|
-
env_path = path / env_file_name
|
|
117
|
-
if env_path.is_file():
|
|
118
|
-
print(f"Found {env_file_name} in {location_desc}.")
|
|
119
|
-
return _parse_env_file(env_path)
|
|
120
|
-
raise FileNotFoundError(f"Could not find {env_file_name} in the repository root or current working directory.")
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def _parse_env_file(env_file_path: Path) -> ClientEnvironmentVariables:
|
|
106
|
+
def parse_env_file(env_file_path: Path) -> ClientEnvironmentVariables:
|
|
124
107
|
content = env_file_path.read_text()
|
|
125
108
|
variables: dict[str, Any] = {}
|
|
126
109
|
for line in content.splitlines():
|
|
@@ -129,3 +112,32 @@ def _parse_env_file(env_file_path: Path) -> ClientEnvironmentVariables:
|
|
|
129
112
|
key, value = line.strip().split("=", 1)
|
|
130
113
|
variables[key] = value
|
|
131
114
|
return ClientEnvironmentVariables.create_humanize(variables)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def create_env_file_content(provider: Provider, login_flow: LoginFlow) -> str:
|
|
118
|
+
lines = [
|
|
119
|
+
"# Cognite NEAT Client Environment Variables",
|
|
120
|
+
"CDF_CLUSTER=<your-cdf-cluster>",
|
|
121
|
+
"CDF_PROJECT=<your-cdf-project>",
|
|
122
|
+
"",
|
|
123
|
+
]
|
|
124
|
+
if login_flow != "token":
|
|
125
|
+
lines.append(f"PROVIDER={provider}")
|
|
126
|
+
lines.append(f"LOGIN_FLOW={login_flow}")
|
|
127
|
+
lines.append("")
|
|
128
|
+
if login_flow in ("client_credentials", "interactive"):
|
|
129
|
+
lines.append("IDP_CLIENT_ID=<your-idp-client-id>")
|
|
130
|
+
if login_flow == "client_credentials":
|
|
131
|
+
lines.append("IDP_CLIENT_SECRET=<your-idp-client-secret>")
|
|
132
|
+
if provider == "entra_id":
|
|
133
|
+
lines.append("IDP_TENANT_ID=<your-idp-tenant-id>")
|
|
134
|
+
if provider not in ("cdf", "entra_id"):
|
|
135
|
+
lines.append("IDP_TOKEN_URL=<your-idp-token-url>")
|
|
136
|
+
if provider == "other":
|
|
137
|
+
lines.append("IDP_AUDIENCE=<your-idp-audience>")
|
|
138
|
+
lines.append("IDP_SCOPES=<your-idp-scopes-comma-separated>")
|
|
139
|
+
lines.append("IDP_AUTHORITY_URL=<your-idp-authority-url>")
|
|
140
|
+
elif login_flow == "token":
|
|
141
|
+
lines.append("CDF_TOKEN=<your-cdf-token>")
|
|
142
|
+
|
|
143
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .env_vars import AVAILABLE_LOGIN_FLOWS, AVAILABLE_PROVIDERS, LoginFlow, Provider, create_env_file_content
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InteractiveFlow(ABC):
|
|
10
|
+
def __init__(self, env_path: Path):
|
|
11
|
+
self.env_path = env_path
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def run(self) -> None: ...
|
|
15
|
+
|
|
16
|
+
def create_env_file(self, provider: Provider, login_flow: LoginFlow) -> None:
|
|
17
|
+
env_content = create_env_file_content(provider, login_flow)
|
|
18
|
+
self.env_path.write_text(env_content, encoding="utf-8", newline="\n")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_interactive_flow(env_file_path: Path) -> InteractiveFlow:
|
|
22
|
+
try:
|
|
23
|
+
importlib.util.find_spec("IPython")
|
|
24
|
+
importlib.util.find_spec("ipywidgets")
|
|
25
|
+
if not _is_in_notebook():
|
|
26
|
+
return NoDependencyFlow(env_file_path)
|
|
27
|
+
return NotebookFlow(env_file_path)
|
|
28
|
+
except ImportError:
|
|
29
|
+
return NoDependencyFlow(env_file_path)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _is_in_notebook() -> bool:
|
|
33
|
+
try:
|
|
34
|
+
from IPython import get_ipython
|
|
35
|
+
|
|
36
|
+
if "IPKernelApp" not in get_ipython().config:
|
|
37
|
+
return False
|
|
38
|
+
except ImportError:
|
|
39
|
+
return False
|
|
40
|
+
except AttributeError:
|
|
41
|
+
return False
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NoDependencyFlow(InteractiveFlow):
|
|
46
|
+
def run(self) -> None:
|
|
47
|
+
if not self.should_create_env_file():
|
|
48
|
+
return None
|
|
49
|
+
provider = self.provider()
|
|
50
|
+
login_flow: LoginFlow
|
|
51
|
+
if provider != "cdf":
|
|
52
|
+
login_flow = self.login_flow()
|
|
53
|
+
else:
|
|
54
|
+
login_flow = "client_credentials"
|
|
55
|
+
self.create_env_file(provider, login_flow)
|
|
56
|
+
print(f"Created environment file at {self.env_path!r}.")
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def should_create_env_file(self) -> bool:
|
|
60
|
+
env_file_name = self.env_path.name
|
|
61
|
+
answer = input(
|
|
62
|
+
f"Would you like to create a new {env_file_name!r} file with the required environment variables? [y/N]: "
|
|
63
|
+
)
|
|
64
|
+
return answer.strip().lower() == "y"
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def provider(cls) -> Provider:
|
|
68
|
+
index = cls._prompt_choice(AVAILABLE_PROVIDERS, "Select provider:")
|
|
69
|
+
return AVAILABLE_PROVIDERS[index]
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def login_flow(cls) -> LoginFlow:
|
|
73
|
+
index = cls._prompt_choice(AVAILABLE_LOGIN_FLOWS, "Select login flow:")
|
|
74
|
+
return AVAILABLE_LOGIN_FLOWS[index]
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def _prompt_choice(cls, options: tuple[str, ...], prompt: str) -> int:
|
|
78
|
+
for i, option in enumerate(options, start=1):
|
|
79
|
+
print(f"{i}. {option}")
|
|
80
|
+
question = f"{prompt} [1-{len(options)}]: "
|
|
81
|
+
while True:
|
|
82
|
+
answer = input(question)
|
|
83
|
+
if answer.isdigit():
|
|
84
|
+
index = int(answer)
|
|
85
|
+
if 1 <= index <= len(options):
|
|
86
|
+
return index - 1
|
|
87
|
+
print(f"Invalid input. Please enter a number between 1 and {len(options)}.")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class NotebookFlow(InteractiveFlow):
|
|
91
|
+
def __init__(self, env_path: Path):
|
|
92
|
+
super().__init__(env_path)
|
|
93
|
+
import ipywidgets as widgets # type: ignore[import-untyped]
|
|
94
|
+
from IPython.display import display
|
|
95
|
+
|
|
96
|
+
self._widgets = widgets
|
|
97
|
+
self._display = display
|
|
98
|
+
|
|
99
|
+
def run(self) -> None:
|
|
100
|
+
dropdown_providers = self._widgets.Dropdown(
|
|
101
|
+
options=list(AVAILABLE_PROVIDERS),
|
|
102
|
+
value=AVAILABLE_PROVIDERS[0],
|
|
103
|
+
description="Provider:",
|
|
104
|
+
)
|
|
105
|
+
dropdown_login_flows = self._widgets.Dropdown(
|
|
106
|
+
options=list(AVAILABLE_LOGIN_FLOWS),
|
|
107
|
+
value=AVAILABLE_LOGIN_FLOWS[0],
|
|
108
|
+
description="Login Flow:",
|
|
109
|
+
)
|
|
110
|
+
confirm_button = self._widgets.Button(description="Create template .env file", button_style="primary")
|
|
111
|
+
dropdowns = self._widgets.HBox([dropdown_providers, dropdown_login_flows])
|
|
112
|
+
output = self._widgets.Output()
|
|
113
|
+
container = self._widgets.VBox([dropdowns, confirm_button, output])
|
|
114
|
+
|
|
115
|
+
self._display(container)
|
|
116
|
+
|
|
117
|
+
def on_confirm_clicked(b: Any) -> None:
|
|
118
|
+
with output:
|
|
119
|
+
provider = dropdown_providers.value
|
|
120
|
+
login_flow = dropdown_login_flows.value
|
|
121
|
+
if provider == "cdf" and login_flow != "client_credentials":
|
|
122
|
+
print(
|
|
123
|
+
"Warning: 'cdf' provider only supports 'client_credentials' login flow. Overriding selection."
|
|
124
|
+
)
|
|
125
|
+
login_flow = "client_credentials"
|
|
126
|
+
is_existing = self.env_path.exists()
|
|
127
|
+
self.create_env_file(provider, login_flow)
|
|
128
|
+
if is_existing:
|
|
129
|
+
print(f"Overwrote existing environment file at {self.env_path!r}.")
|
|
130
|
+
else:
|
|
131
|
+
print(f"Created environment file at {self.env_path!r}.")
|
|
132
|
+
|
|
133
|
+
confirm_button.on_click(on_confirm_clicked)
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
1
3
|
from cognite.client import CogniteClient
|
|
2
4
|
from cognite.client.config import ClientConfig, global_config
|
|
3
5
|
|
|
4
6
|
from cognite.neat import _version
|
|
7
|
+
from cognite.neat._utils.repo import get_repo_root
|
|
5
8
|
|
|
6
9
|
from .credentials import get_credentials
|
|
7
|
-
from .env_vars import ClientEnvironmentVariables,
|
|
10
|
+
from .env_vars import ClientEnvironmentVariables, parse_env_file
|
|
11
|
+
from .interactive import get_interactive_flow
|
|
8
12
|
|
|
9
13
|
CLIENT_NAME = f"CogniteNeat:{_version.__version__}"
|
|
10
14
|
|
|
11
15
|
|
|
12
|
-
def get_cognite_client(env_file_name: str) -> CogniteClient:
|
|
16
|
+
def get_cognite_client(env_file_name: str) -> CogniteClient | None:
|
|
13
17
|
"""Get a CogniteClient using environment variables from a .env file."
|
|
14
18
|
|
|
15
19
|
Args:
|
|
@@ -20,24 +24,30 @@ def get_cognite_client(env_file_name: str) -> CogniteClient:
|
|
|
20
24
|
Returns:
|
|
21
25
|
CogniteClient: An instance of CogniteClient configured with the loaded environment variables.
|
|
22
26
|
"""
|
|
23
|
-
try:
|
|
24
|
-
return get_cognite_client_internal(env_file_name)
|
|
25
|
-
except Exception as e:
|
|
26
|
-
raise RuntimeError(f"Failed to create client ❌: {e!s}") from None
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def get_cognite_client_internal(env_file_name: str) -> CogniteClient:
|
|
30
27
|
# This function raises exceptions on failure
|
|
31
28
|
if not env_file_name.endswith(".env"):
|
|
32
29
|
raise ValueError(f"env_file_name must end with '.env'. Got: {env_file_name!r}")
|
|
33
30
|
global_config.disable_pypi_version_check = True
|
|
34
31
|
global_config.silence_feature_preview_warnings = True
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
|
|
33
|
+
repo_root = get_repo_root()
|
|
34
|
+
if repo_root and (env_path := repo_root / env_file_name).exists():
|
|
35
|
+
print(f"Found {env_file_name} in repository root.")
|
|
36
|
+
elif (env_path := Path.cwd() / env_file_name).exists():
|
|
37
|
+
print(f"Found {env_file_name} in current working directory.")
|
|
38
|
+
|
|
39
|
+
if env_path.exists():
|
|
40
|
+
env_vars = parse_env_file(env_path)
|
|
41
|
+
client_config = create_client_config_from_env_vars(env_vars)
|
|
42
|
+
return CogniteClient(client_config)
|
|
43
|
+
print(f"Failed to find {env_file_name} in repository root or current working directory.")
|
|
44
|
+
|
|
45
|
+
env_folder = repo_root if repo_root is not None else Path.cwd()
|
|
46
|
+
new_env_path = env_folder / env_file_name
|
|
47
|
+
flow = get_interactive_flow(new_env_path)
|
|
48
|
+
flow.run()
|
|
49
|
+
print("Could not create CogniteClient because no environment file was found.")
|
|
50
|
+
return None
|
|
41
51
|
|
|
42
52
|
|
|
43
53
|
def create_client_config_from_env_vars(env_vars: ClientEnvironmentVariables) -> ClientConfig:
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
import zipfile
|
|
3
|
+
from collections.abc import Iterator
|
|
1
4
|
from pathlib import Path
|
|
2
5
|
|
|
3
6
|
import yaml
|
|
7
|
+
from pydantic import BaseModel
|
|
4
8
|
|
|
5
9
|
from cognite.neat._data_model.exporters._base import DMSExporter, DMSFileExporter
|
|
6
10
|
from cognite.neat._data_model.models.dms import RequestSchema
|
|
11
|
+
from cognite.neat._data_model.models.dms._container import ContainerRequest
|
|
12
|
+
from cognite.neat._data_model.models.dms._references import NodeReference
|
|
13
|
+
from cognite.neat._data_model.models.dms._views import ViewRequest
|
|
7
14
|
|
|
8
15
|
|
|
9
16
|
class DMSAPIExporter(DMSExporter[RequestSchema]):
|
|
@@ -16,12 +23,90 @@ class DMSAPIExporter(DMSExporter[RequestSchema]):
|
|
|
16
23
|
|
|
17
24
|
class DMSAPIYAMLExporter(DMSAPIExporter, DMSFileExporter[RequestSchema]):
|
|
18
25
|
def export_to_file(self, data_model: RequestSchema, file_path: Path) -> None:
|
|
19
|
-
"""Export the data model to a YAML file in API format.
|
|
20
|
-
if file_path.suffix.lower() not in {".yaml", ".yml"}:
|
|
21
|
-
raise ValueError("The file path must have a .yaml or .yml extension.")
|
|
26
|
+
"""Export the data model to a YAML files or zip file in API format.
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
Args:
|
|
29
|
+
data_model: Request schema
|
|
30
|
+
file_path: The directory or zip file to save the schema to.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
if file_path.is_dir():
|
|
35
|
+
self._export_to_directory(data_model, file_path)
|
|
36
|
+
else:
|
|
37
|
+
self._export_to_zip_file(data_model, file_path)
|
|
38
|
+
|
|
39
|
+
def _export_to_directory(self, data_model: RequestSchema, directory: Path) -> None:
|
|
40
|
+
"""Save the schema to a directory as YAML files. This is compatible with the Cognite-Toolkit convention.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data_model: Request schema
|
|
44
|
+
directory: The directory to save the schema to.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
subdir = directory / "data_models"
|
|
48
|
+
subdir.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
|
|
50
|
+
for file_path, yaml_content in self._generate_yaml_entries(data_model):
|
|
51
|
+
full_path = subdir / file_path
|
|
52
|
+
# Create parent directories if needed (e.g., for views/, containers/, nodes/)
|
|
53
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
full_path.write_text(
|
|
55
|
+
yaml_content,
|
|
56
|
+
encoding=self.ENCODING,
|
|
57
|
+
newline=self.NEW_LINE,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def _export_to_zip_file(self, data_model: RequestSchema, zip_file: Path) -> None:
|
|
61
|
+
"""Save the schema as a zip file containing a directory as YAML files.
|
|
62
|
+
This is compatible with the Cognite-Toolkit convention.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
data_model: Request schema
|
|
66
|
+
zip_file: The zip file path to save the schema to.
|
|
67
|
+
"""
|
|
68
|
+
if zip_file.suffix not in {".zip"}:
|
|
69
|
+
warnings.warn("File extension is not .zip, adding it to the file name", stacklevel=2)
|
|
70
|
+
zip_file = zip_file.with_suffix(".zip")
|
|
71
|
+
|
|
72
|
+
with zipfile.ZipFile(zip_file, "w") as zip_ref:
|
|
73
|
+
for file_path, yaml_content in self._generate_yaml_entries(data_model):
|
|
74
|
+
zip_ref.writestr(f"data_models/{file_path}", yaml_content)
|
|
75
|
+
|
|
76
|
+
def _generate_yaml_entries(self, data_model: RequestSchema) -> Iterator[tuple[str, str]]:
|
|
77
|
+
"""Generate file paths and YAML content for all data model components.
|
|
78
|
+
|
|
79
|
+
This helper method eliminates duplication by centralizing the logic for
|
|
80
|
+
iterating through spaces, views, containers, and node types.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
data_model: Request schema
|
|
84
|
+
|
|
85
|
+
Yields:
|
|
86
|
+
Tuples of (file_path, yaml_content) for each component.
|
|
87
|
+
File paths are relative to the data_models directory.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def _dump(item: BaseModel) -> str:
|
|
91
|
+
return yaml.safe_dump(item.model_dump(mode="json", by_alias=True), sort_keys=False)
|
|
92
|
+
|
|
93
|
+
# Export spaces
|
|
94
|
+
for space in data_model.spaces:
|
|
95
|
+
yield f"{space.space}.space.yaml", _dump(space)
|
|
96
|
+
|
|
97
|
+
# Export data model
|
|
98
|
+
yield f"{data_model.data_model.external_id}.datamodel.yaml", _dump(data_model.data_model)
|
|
99
|
+
|
|
100
|
+
component_configs: list[tuple[str, list[ViewRequest] | list[ContainerRequest] | list[NodeReference]]] = [
|
|
101
|
+
("views", data_model.views),
|
|
102
|
+
("containers", data_model.containers),
|
|
103
|
+
("nodes", data_model.node_types),
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
for dir_prefix, components in component_configs:
|
|
107
|
+
file_suffix = dir_prefix.removesuffix("s")
|
|
108
|
+
for component in components:
|
|
109
|
+
yield f"{dir_prefix}/{component.external_id}.{file_suffix}.yaml", _dump(component)
|
|
25
110
|
|
|
26
111
|
|
|
27
112
|
class DMSAPIJSONExporter(DMSAPIExporter, DMSFileExporter[RequestSchema]):
|
|
@@ -15,11 +15,19 @@ from ._physical import PhysicalDataModel
|
|
|
15
15
|
from ._result import Result
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def _is_in_browser() -> bool:
|
|
19
|
+
try:
|
|
20
|
+
from pyodide.ffi import IN_BROWSER # type: ignore [import-not-found]
|
|
21
|
+
except ModuleNotFoundError:
|
|
22
|
+
return False
|
|
23
|
+
return IN_BROWSER
|
|
24
|
+
|
|
25
|
+
|
|
18
26
|
class NeatSession:
|
|
19
27
|
"""A session is an interface for neat operations."""
|
|
20
28
|
|
|
21
29
|
def __init__(
|
|
22
|
-
self, client: CogniteClient | ClientConfig, config: PredefinedProfile | NeatConfig = "legacy-additive"
|
|
30
|
+
self, client: CogniteClient | ClientConfig | None, config: PredefinedProfile | NeatConfig = "legacy-additive"
|
|
23
31
|
) -> None:
|
|
24
32
|
"""Initialize a Neat session.
|
|
25
33
|
|
|
@@ -30,6 +38,10 @@ class NeatSession:
|
|
|
30
38
|
Defaults to "legacy-additive". This means Neat will perform additive modeling
|
|
31
39
|
and apply only validations that were part of the legacy Neat version.
|
|
32
40
|
"""
|
|
41
|
+
if client is None and _is_in_browser():
|
|
42
|
+
client = CogniteClient()
|
|
43
|
+
elif client is None:
|
|
44
|
+
raise ValueError("A CogniteClient or ClientConfig must be provided to initialize a NeatSession.")
|
|
33
45
|
self._config = NeatConfig.create_predefined(config) if isinstance(config, str) else config
|
|
34
46
|
|
|
35
47
|
# Use configuration for physical data model
|
cognite/neat/_utils/repo.py
CHANGED
|
@@ -2,7 +2,7 @@ import subprocess
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def get_repo_root() -> Path:
|
|
5
|
+
def get_repo_root() -> Path | None:
|
|
6
6
|
"""Get the root path of the git repository.
|
|
7
7
|
|
|
8
8
|
Raises:
|
|
@@ -11,9 +11,11 @@ def get_repo_root() -> Path:
|
|
|
11
11
|
"""
|
|
12
12
|
try:
|
|
13
13
|
result = subprocess.run("git rev-parse --show-toplevel".split(), stdout=subprocess.PIPE)
|
|
14
|
-
except FileNotFoundError
|
|
15
|
-
|
|
14
|
+
except FileNotFoundError:
|
|
15
|
+
# Git is not installed or not found in PATH
|
|
16
|
+
return None
|
|
16
17
|
output = result.stdout.decode().strip()
|
|
17
18
|
if not output:
|
|
18
|
-
|
|
19
|
+
# Not in a git repository
|
|
20
|
+
return None
|
|
19
21
|
return Path(output)
|
cognite/neat/_version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "1.0.
|
|
1
|
+
__version__ = "1.0.25"
|
|
2
2
|
__engine__ = "^2.0.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cognite-neat
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.25
|
|
4
4
|
Summary: Knowledge graph transformation
|
|
5
5
|
Author: Nikola Vasiljevic, Anders Albert
|
|
6
6
|
Author-email: Nikola Vasiljevic <nikola.vasiljevic@cognite.com>, Anders Albert <anders.albert@cognite.com>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
cognite/neat/__init__.py,sha256=
|
|
1
|
+
cognite/neat/__init__.py,sha256=YU2PPtzByiyLcUeWePkckaL9v-ZxoR6OF6wiq-2XVwM,297
|
|
2
2
|
cognite/neat/_client/__init__.py,sha256=DIHMeggZ81rVJwO97E5PVP8AwVAhhLpjBQu7Ns3_xdc,178
|
|
3
3
|
cognite/neat/_client/api.py,sha256=nbxCdWBXcTVM6MrQeT_VpB6ehfoI544JHPFq-ejQKCY,292
|
|
4
4
|
cognite/neat/_client/client.py,sha256=h0HELAHiBFxMNInkDu4AzbgfEIXqeM0BqqnMBmXjgi0,903
|
|
@@ -7,9 +7,10 @@ cognite/neat/_client/containers_api.py,sha256=7bVIlL5PwoAG5Bks1ortW_bCG8iTkFqFVy
|
|
|
7
7
|
cognite/neat/_client/data_classes.py,sha256=HYPsrAJGVCUmlWTSIxJgAnIHAOzcyDveMM6Z-cuA92M,1404
|
|
8
8
|
cognite/neat/_client/data_model_api.py,sha256=ogVHOabQ3HTqWaaoiGClmbtYdP-pl6DPN2zmPdH5LWY,4253
|
|
9
9
|
cognite/neat/_client/init/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
cognite/neat/_client/init/credentials.py,sha256=
|
|
11
|
-
cognite/neat/_client/init/env_vars.py,sha256=
|
|
12
|
-
cognite/neat/_client/init/
|
|
10
|
+
cognite/neat/_client/init/credentials.py,sha256=8PCKk-rc3rxG3l9vNiY3DklIziJgJpBr9nleozS7BZw,2385
|
|
11
|
+
cognite/neat/_client/init/env_vars.py,sha256=9nOEVABp4rI2TtFHCMItLouQ5MXFL0zzL4PTclT9m-c,5469
|
|
12
|
+
cognite/neat/_client/init/interactive.py,sha256=YVnsvRblQJPojf24dA050gVlrrqADK8afSQ6hkXn7Cs,4801
|
|
13
|
+
cognite/neat/_client/init/main.py,sha256=D-9bsor07T84iFnTZLnhIFA1Xey9zsooHaDhXVeNdos,2508
|
|
13
14
|
cognite/neat/_client/spaces_api.py,sha256=xHtSMt_2k2YwZ5_8kH2dfa7fWxQQrky7wra4Ar2jwqs,4111
|
|
14
15
|
cognite/neat/_client/statistics_api.py,sha256=HcYb2nNC9M_iaI1xyjjLn2Cz1tcyu7BJeaqVps79tg4,773
|
|
15
16
|
cognite/neat/_client/views_api.py,sha256=Qzk_wiLtaWszxCQFDBoWCH1yDc4GOEJsVOcL061rcK0,5639
|
|
@@ -29,7 +30,7 @@ cognite/neat/_data_model/deployer/_differ_view.py,sha256=g1xHwsoxFUaTOTtQa19nntK
|
|
|
29
30
|
cognite/neat/_data_model/deployer/data_classes.py,sha256=cq86u7wuKCcvH-A_cGI_gWzlQCTIG6mrXG2MahdiGco,27299
|
|
30
31
|
cognite/neat/_data_model/deployer/deployer.py,sha256=lEdTh4jOwTxLkSEx-SlcnXUZyPZCUtIzop1zhAe2s44,19681
|
|
31
32
|
cognite/neat/_data_model/exporters/__init__.py,sha256=AskjmB_0Vqib4kN84bWt8-M8nO42QypFf-l-E8oA5W8,482
|
|
32
|
-
cognite/neat/_data_model/exporters/_api_exporter.py,sha256=
|
|
33
|
+
cognite/neat/_data_model/exporters/_api_exporter.py,sha256=nBDHx9dGbaje0T4IEQv0Kulk2Yu7FkPXgXK_MgLbE50,4948
|
|
33
34
|
cognite/neat/_data_model/exporters/_base.py,sha256=rG_qAU5i5Hh5hUMep2UmDFFZID4x3LEenL6Z5C6N8GQ,646
|
|
34
35
|
cognite/neat/_data_model/exporters/_table_exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
36
|
cognite/neat/_data_model/exporters/_table_exporter/exporter.py,sha256=4BPu_Chtjh1EyOaKbThXYohsqllVOkCbSoNekNZuBXc,5159
|
|
@@ -106,7 +107,7 @@ cognite/neat/_session/_result/_deployment/_physical/_changes.py,sha256=CmFWxAMtV
|
|
|
106
107
|
cognite/neat/_session/_result/_deployment/_physical/_statistics.py,sha256=aOXZTSOGmVggERB4mKaqQEUx40vrxiN9ZkwjKU1555A,6324
|
|
107
108
|
cognite/neat/_session/_result/_deployment/_physical/serializer.py,sha256=BbCUb7_C75enMimGgTPg-ZW1QGcRhPDf5dNwpWdM790,1272
|
|
108
109
|
cognite/neat/_session/_result/_result.py,sha256=OnjIJQSnIzYr-IE50rq7z3CVO0LpVE8tAaZN7XPjlKw,1188
|
|
109
|
-
cognite/neat/_session/_session.py,sha256=
|
|
110
|
+
cognite/neat/_session/_session.py,sha256=SsPEYupMiW7zRFruTjC1Ga9yvvs5gkygdpI_tVyh1rA,3729
|
|
110
111
|
cognite/neat/_session/_usage_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
111
112
|
cognite/neat/_session/_usage_analytics/_collector.py,sha256=8yVfzt8KFfZ-ldkVjDWazuQbs45Q3r6vWKcZEwU8i18,4734
|
|
112
113
|
cognite/neat/_session/_usage_analytics/_constants.py,sha256=-tVdYrCTMKfuMlbO7AlzC29Nug41ug6uuX9DFuihpJg,561
|
|
@@ -127,7 +128,7 @@ cognite/neat/_utils/http_client/_client.py,sha256=TO9C77LcsqX0R3Fu-mP560nnV6rP5o
|
|
|
127
128
|
cognite/neat/_utils/http_client/_config.py,sha256=C8IF1JoijmVMjA_FEMgAkiD1buEV1cY5Og3t-Ecyfmk,756
|
|
128
129
|
cognite/neat/_utils/http_client/_data_classes.py,sha256=McyCQZUcwkbOXdBmc99DdNsO6mQz8dNVsepDvXmaINA,10138
|
|
129
130
|
cognite/neat/_utils/http_client/_tracker.py,sha256=EBBnd-JZ7nc_jYNFJokCHN2UZ9sx0McFLZvlceUYYic,1215
|
|
130
|
-
cognite/neat/_utils/repo.py,sha256=
|
|
131
|
+
cognite/neat/_utils/repo.py,sha256=Ex_lW7qd4fi0YLaOJaLRwSyrN46kfMXbxecVE4mSs2I,598
|
|
131
132
|
cognite/neat/_utils/text.py,sha256=-ujNaG_hLkdurKsUmZB9ZI_kJkddlCKEf8g-g_XCk10,2010
|
|
132
133
|
cognite/neat/_utils/useful_types.py,sha256=BwTjcWnpxnxN8rWXuYXMgU55O_YjVteMtYK0y25OmH0,1260
|
|
133
134
|
cognite/neat/_utils/validation.py,sha256=U422V0TY5KujFJFyfhRLONVj5A4AcCWgqIKVK6BUm7M,6938
|
|
@@ -321,9 +322,9 @@ cognite/neat/_v0/session/_template.py,sha256=BNcvrW5y7LWzRM1XFxZkfR1Nc7e8UgjBClH
|
|
|
321
322
|
cognite/neat/_v0/session/_to.py,sha256=AnsRSDDdfFyYwSgi0Z-904X7WdLtPfLlR0x1xsu_jAo,19447
|
|
322
323
|
cognite/neat/_v0/session/_wizard.py,sha256=baPJgXAAF3d1bn4nbIzon1gWfJOeS5T43UXRDJEnD3c,1490
|
|
323
324
|
cognite/neat/_v0/session/exceptions.py,sha256=jv52D-SjxGfgqaHR8vnpzo0SOJETIuwbyffSWAxSDJw,3495
|
|
324
|
-
cognite/neat/_version.py,sha256=
|
|
325
|
+
cognite/neat/_version.py,sha256=kEQ71HVQEACqgtyau8hgTcILi0EwbWj3B7RI2KWSEHg,45
|
|
325
326
|
cognite/neat/legacy.py,sha256=eI2ecxOV8ilGHyLZlN54ve_abtoK34oXognkFv3yvF0,219
|
|
326
327
|
cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
327
|
-
cognite_neat-1.0.
|
|
328
|
-
cognite_neat-1.0.
|
|
329
|
-
cognite_neat-1.0.
|
|
328
|
+
cognite_neat-1.0.25.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
|
|
329
|
+
cognite_neat-1.0.25.dist-info/METADATA,sha256=Puzp0pIIcNYZAvb2M1YswRXTzr6pvtqk4cfBT-oeKBg,6689
|
|
330
|
+
cognite_neat-1.0.25.dist-info/RECORD,,
|
|
File without changes
|