cognite-neat 0.94.0__py3-none-any.whl → 0.96.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.

@@ -1,19 +1,20 @@
1
1
  from typing import Literal, cast
2
2
 
3
- import pandas as pd
4
3
  from cognite.client import CogniteClient
5
4
 
5
+ from cognite.neat import _version
6
6
  from cognite.neat._issues import IssueList
7
7
  from cognite.neat._rules import importers
8
8
  from cognite.neat._rules._shared import ReadRules
9
9
  from cognite.neat._rules.models import DMSRules
10
- from cognite.neat._rules.models._base_input import InputComponent
11
10
  from cognite.neat._rules.models.information._rules import InformationRules
12
11
  from cognite.neat._rules.models.information._rules_input import InformationInputRules
13
12
  from cognite.neat._rules.transformers import ConvertToRules, VerifyAnyRules
14
13
 
14
+ from ._inspect import InspectAPI
15
15
  from ._prepare import PrepareAPI
16
16
  from ._read import ReadAPI
17
+ from ._set import SetAPI
17
18
  from ._show import ShowAPI
18
19
  from ._state import SessionState
19
20
  from ._to import ToAPI
@@ -35,6 +36,12 @@ class NeatSession:
35
36
  self.to = ToAPI(self._state, client, verbose)
36
37
  self.prepare = PrepareAPI(self._state, verbose)
37
38
  self.show = ShowAPI(self._state)
39
+ self.set = SetAPI(self._state, verbose)
40
+ self.inspect = InspectAPI(self._state)
41
+
42
+ @property
43
+ def version(self) -> str:
44
+ return _version.__version__
38
45
 
39
46
  def verify(self) -> IssueList:
40
47
  output = VerifyAnyRules("continue").try_transform(self._state.input_rule)
@@ -42,6 +49,9 @@ class NeatSession:
42
49
  self._state.verified_rules.append(output.rules)
43
50
  if isinstance(output.rules, InformationRules):
44
51
  self._state.store.add_rules(output.rules)
52
+ self._state.issue_lists.append(output.issues)
53
+ if output.issues:
54
+ print("You can inspect the issues with the .inspect attribute.")
45
55
  return output.issues
46
56
 
47
57
  def convert(self, target: Literal["dms"]) -> None:
@@ -72,13 +82,10 @@ class NeatSession:
72
82
 
73
83
  output = []
74
84
  if state.input_rules and not state.verified_rules:
75
- metadata = cast(InputComponent, state.input_rule.rules.metadata) # type: ignore[union-attr]
76
- table = pd.DataFrame([metadata.dump()]).T._repr_html_() # type: ignore[operator]
77
- output.append(f"<H2>Raw Data Model</H2><br />{table}")
85
+ output.append(f"<H2>Unverified Data Model</H2><br />{state.input_rule.rules._repr_html_()}") # type: ignore
78
86
 
79
87
  if state.verified_rules:
80
- table = pd.DataFrame([state.last_verified_rule.metadata.model_dump()]).T._repr_html_() # type: ignore[operator]
81
- output.append(f"<H2>Data Model</H2><br />{table}")
88
+ output.append(f"<H2>Verified Data Model</H2><br />{state.last_verified_rule._repr_html_()}") # type: ignore
82
89
 
83
90
  if state.has_store:
84
91
  output.append(f"<H2>Instances</H2> {state.store._repr_html_()}")
@@ -0,0 +1,89 @@
1
+ import difflib
2
+ from typing import Literal, overload
3
+
4
+ import pandas as pd
5
+
6
+ from cognite.neat._constants import IN_NOTEBOOK
7
+ from cognite.neat._issues import IssueList
8
+
9
+ from ._state import SessionState
10
+ from .exceptions import intercept_session_exceptions
11
+
12
+
13
+ @intercept_session_exceptions
14
+ class InspectAPI:
15
+ def __init__(self, state: SessionState) -> None:
16
+ self._state = state
17
+ self.issues = InspectIssues(state)
18
+
19
+ @property
20
+ def properties(self) -> pd.DataFrame:
21
+ """Returns the properties of the current data model."""
22
+ return self._state.last_verified_rule.properties.to_pandas()
23
+
24
+
25
+ @intercept_session_exceptions
26
+ class InspectIssues:
27
+ """Inspect issues of the current data model."""
28
+
29
+ def __init__(self, state: SessionState) -> None:
30
+ self._state = state
31
+
32
+ @overload
33
+ def __call__(
34
+ self,
35
+ search: str | None = None,
36
+ return_dataframe: Literal[True] = (False if IN_NOTEBOOK else True), # type: ignore[assignment]
37
+ ) -> pd.DataFrame: ...
38
+
39
+ @overload
40
+ def __call__(
41
+ self,
42
+ search: str | None = None,
43
+ return_dataframe: Literal[False] = (False if IN_NOTEBOOK else True), # type: ignore[assignment]
44
+ ) -> None: ...
45
+
46
+ def __call__(
47
+ self,
48
+ search: str | None = None,
49
+ return_dataframe: bool = (False if IN_NOTEBOOK else True), # type: ignore[assignment]
50
+ ) -> pd.DataFrame | None:
51
+ """Returns the issues of the current data model."""
52
+ issues = self._state.last_issues
53
+ if not issues:
54
+ self._print("No issues found.")
55
+
56
+ if issues and search is not None:
57
+ unique_types = {type(issue).__name__ for issue in issues}
58
+ closest_match = set(difflib.get_close_matches(search, unique_types))
59
+ issues = IssueList([issue for issue in issues if type(issue).__name__ in closest_match])
60
+
61
+ if IN_NOTEBOOK:
62
+ from IPython.display import Markdown, display
63
+
64
+ issue_str = "\n".join(
65
+ [f" * **{type(issue).__name__}**: {issue.as_message(include_type=False)}" for issue in issues]
66
+ )
67
+ message = f"### {len(issues)} issues found\n\n{issue_str}"
68
+ display(Markdown(message))
69
+
70
+ if return_dataframe:
71
+ return issues.to_pandas()
72
+ return None
73
+
74
+ def _print(self, message: str) -> None:
75
+ if IN_NOTEBOOK:
76
+ from IPython.display import Markdown, display
77
+
78
+ display(Markdown(message))
79
+ else:
80
+ print(message)
81
+
82
+ def __repr__(self) -> str:
83
+ return self.__repr_html__()
84
+
85
+ def __repr_html__(self) -> str:
86
+ return (
87
+ "Inspect issues by calling .inspect.issues() or "
88
+ "search for specific issues by calling .inspect.issues('MyTypeWarning')."
89
+ )
@@ -9,8 +9,10 @@ from cognite.neat._rules.models.information._rules_input import InformationInput
9
9
  from cognite.neat._rules.transformers import ReduceCogniteModel, ToCompliantEntities, ToExtension
10
10
 
11
11
  from ._state import SessionState
12
+ from .exceptions import intercept_session_exceptions
12
13
 
13
14
 
15
+ @intercept_session_exceptions
14
16
  class PrepareAPI:
15
17
  def __init__(self, state: SessionState, verbose: bool) -> None:
16
18
  self._state = state
@@ -18,6 +20,7 @@ class PrepareAPI:
18
20
  self.data_model = DataModelPrepareAPI(state, verbose)
19
21
 
20
22
 
23
+ @intercept_session_exceptions
21
24
  class DataModelPrepareAPI:
22
25
  def __init__(self, state: SessionState, verbose: bool) -> None:
23
26
  self._state = state
@@ -35,25 +38,79 @@ class DataModelPrepareAPI:
35
38
  )
36
39
  )
37
40
 
38
- def to_extension(self, new_data_model_id: DataModelIdentifier, org_name: str | None = None) -> None:
39
- """Uses the current data model as a basis to extend from.
41
+ def to_enterprise(
42
+ self,
43
+ data_model_id: DataModelIdentifier,
44
+ org_name: str = "My",
45
+ dummy_property: str = "GUID",
46
+ ) -> None:
47
+ """Uses the current data model as a basis to create enterprise data model
40
48
 
41
49
  Args:
42
- new_data_model_id: The new data model that is extending the current data model.
43
- org_name: Organization name to use for the views in the new data model. This is required if you are
44
- creating an extension from a Cognite Data Model.
50
+ data_model_id: The enterprise data model id that is being created
51
+ org_name: Organization name to use for the views in the enterprise data model.
52
+ dummy_property: The dummy property to use as placeholder for the views in the new data model.
53
+
54
+ !!! note "Enterprise Data Model Creation"
55
+ Always create an enterprise data model from a Cognite Data Model as this will
56
+ assure all the Cognite Data Fusion applications to run smoothly, such as
57
+ - Search
58
+ - Atlas AI
59
+ - ...
60
+
61
+ """
62
+ if dms := self._state.last_verified_dms_rules:
63
+ output = ToExtension(
64
+ new_model_id=data_model_id,
65
+ org_name=org_name,
66
+ type_="enterprise",
67
+ dummy_property=dummy_property,
68
+ ).transform(dms)
69
+ self._state.verified_rules.append(output.rules)
70
+
71
+ def to_solution(
72
+ self,
73
+ data_model_id: DataModelIdentifier,
74
+ org_name: str = "My",
75
+ mode: Literal["read", "write"] = "read",
76
+ dummy_property: str = "dummy",
77
+ ) -> None:
78
+ """Uses the current data model as a basis to create solution data model
79
+
80
+ Args:
81
+ data_model_id: The solution data model id that is being created.
82
+ org_name: Organization name to use for the views in the new data model.
83
+ mode: The mode of the solution data model. Can be either "read" or "write".
84
+ dummy_property: The dummy property to use as placeholder for the views in the new data model.
85
+
86
+ !!! note "Solution Data Model Mode"
87
+ The read-only solution model will only be able to read from the existing containers
88
+ from the enterprise data model, therefore the solution data model will not have
89
+ containers in the solution data model space. Meaning the solution data model views
90
+ will be read-only.
91
+
92
+ The write mode will have additional containers in the solution data model space,
93
+ allowing in addition to reading through the solution model views, also writing to
94
+ the containers in the solution data model space.
45
95
 
46
96
  """
47
97
  if dms := self._state.last_verified_dms_rules:
48
- output = ToExtension(new_data_model_id, org_name).transform(dms)
98
+ output = ToExtension(
99
+ new_model_id=data_model_id,
100
+ org_name=org_name,
101
+ type_="solution",
102
+ mode=mode,
103
+ dummy_property=dummy_property,
104
+ ).transform(dms)
49
105
  self._state.verified_rules.append(output.rules)
50
106
 
51
- def reduce(self, drop: Collection[Literal["3D", "Annotation", "BaseViews"]]) -> None:
107
+ def reduce(self, drop: Collection[Literal["3D", "Annotation", "BaseViews"] | str]) -> None:
52
108
  """This is a special method that allow you to drop parts of the data model.
53
109
  This only applies to Cognite Data Models.
54
110
 
55
111
  Args:
56
- drop: Which parts of the data model to drop.
112
+ drop: What to drop from the data model. The values 3D, Annotation, and BaseViews are special values that
113
+ drops multiple views at once. You can also pass externalIds of views to drop individual views.
57
114
 
58
115
  """
59
116
  if dms := self._state.last_verified_dms_rules:
@@ -7,13 +7,16 @@ from cognite.client.data_classes.data_modeling import DataModelIdentifier
7
7
  from cognite.neat._graph import examples as instances_examples
8
8
  from cognite.neat._graph import extractors
9
9
  from cognite.neat._issues import IssueList
10
+ from cognite.neat._issues.errors import NeatValueError
10
11
  from cognite.neat._rules import importers
11
12
  from cognite.neat._rules._shared import ReadRules
12
13
 
13
14
  from ._state import SessionState
14
15
  from ._wizard import NeatObjectType, RDFFileType, object_wizard, rdf_dm_wizard
16
+ from .exceptions import intercept_session_exceptions
15
17
 
16
18
 
19
+ @intercept_session_exceptions
17
20
  class ReadAPI:
18
21
  def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
19
22
  self._state = state
@@ -23,6 +26,7 @@ class ReadAPI:
23
26
  self.excel = ExcelReadAPI(state, client, verbose)
24
27
 
25
28
 
29
+ @intercept_session_exceptions
26
30
  class BaseReadAPI:
27
31
  def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
28
32
  self._state = state
@@ -44,20 +48,44 @@ class BaseReadAPI:
44
48
  elif isinstance(io, Path):
45
49
  return io
46
50
  else:
47
- raise ValueError(f"Expected str or Path, got {type(io)}")
51
+ raise NeatValueError(f"Expected str or Path, got {type(io)}")
48
52
 
49
53
 
54
+ @intercept_session_exceptions
50
55
  class CDFReadAPI(BaseReadAPI):
51
- def data_model(self, data_model_id: DataModelIdentifier) -> IssueList:
56
+ def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
57
+ super().__init__(state, client, verbose)
58
+ self.classic = CDFClassicAPI(state, client, verbose)
59
+
60
+ @property
61
+ def _get_client(self) -> CogniteClient:
52
62
  if self._client is None:
53
- raise ValueError("No client provided. Please provide a client to read a data model.")
63
+ raise NeatValueError("No client provided. Please provide a client to read a data model.")
64
+ return self._client
54
65
 
55
- importer = importers.DMSImporter.from_data_model_id(self._client, data_model_id)
66
+ def data_model(self, data_model_id: DataModelIdentifier) -> IssueList:
67
+ importer = importers.DMSImporter.from_data_model_id(self._get_client, data_model_id)
56
68
  input_rules = importer.to_rules()
57
69
  self._store_rules(data_model_id, input_rules, "CDF")
58
70
  return input_rules.issues
59
71
 
60
72
 
73
+ @intercept_session_exceptions
74
+ class CDFClassicAPI(BaseReadAPI):
75
+ @property
76
+ def _get_client(self) -> CogniteClient:
77
+ if self._client is None:
78
+ raise ValueError("No client provided. Please provide a client to read a data model.")
79
+ return self._client
80
+
81
+ def assets(self, root_asset_external_id: str) -> None:
82
+ extractor = extractors.AssetsExtractor.from_hierarchy(self._get_client, root_asset_external_id)
83
+ self._state.store.write(extractor)
84
+ if self._verbose:
85
+ print(f"Asset hierarchy {root_asset_external_id} read successfully")
86
+
87
+
88
+ @intercept_session_exceptions
61
89
  class ExcelReadAPI(BaseReadAPI):
62
90
  def __call__(self, io: Any) -> IssueList:
63
91
  filepath = self._return_filepath(io)
@@ -66,6 +94,7 @@ class ExcelReadAPI(BaseReadAPI):
66
94
  return input_rules.issues
67
95
 
68
96
 
97
+ @intercept_session_exceptions
69
98
  class RDFReadAPI(BaseReadAPI):
70
99
  def __init__(self, state: SessionState, client: CogniteClient | None, verbose: bool) -> None:
71
100
  super().__init__(state, client, verbose)
@@ -0,0 +1,23 @@
1
+ from cognite.client import data_modeling as dm
2
+
3
+ from cognite.neat._rules.transformers import SetIDDMSModel
4
+
5
+ from ._state import SessionState
6
+ from .exceptions import intercept_session_exceptions
7
+
8
+
9
+ @intercept_session_exceptions
10
+ class SetAPI:
11
+ def __init__(self, state: SessionState, verbose: bool) -> None:
12
+ self._state = state
13
+ self._verbose = verbose
14
+
15
+ def data_model_id(self, new_model_id: dm.DataModelId | tuple[str, str, str]) -> None:
16
+ """Sets the data model ID of the latest verified data model."""
17
+ if dms := self._state.last_verified_dms_rules:
18
+ output = SetIDDMSModel(new_model_id).transform(dms)
19
+ self._state.verified_rules.append(output.rules)
20
+ if self._verbose:
21
+ print(f"Data model ID set to {new_model_id}")
22
+ else:
23
+ print("No verified DMS data model available")