Skip to content

Commit

Permalink
Friendly error message if conditions are not met in NeatSession (#1030)
Browse files Browse the repository at this point in the history
# Description

Friendly error message if conditions are not met in NeatSession when
users are trying to execute public methods such as:

## Bump

- [ ] Patch
- [x] Minor
- [ ] Skip

## Changelog
### Added
- NeatSession state checks prior execution of NeatSession methods.
Checks are currently performed for existence of client, whether data
model and instance store are empty or not
  • Loading branch information
nikokaoja authored Mar 3, 2025
1 parent 51f51ae commit 0a214eb
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 24 deletions.
1 change: 1 addition & 0 deletions cognite/neat/_session/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ def infer(
neat.infer()
```
"""
self._state._raise_exception_if_condition_not_met("Data model inference", instances_required=True)
return self._infer_subclasses(model_id)

def _previous_inference(
Expand Down
84 changes: 68 additions & 16 deletions cognite/neat/_session/_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,6 @@ def __init__(self, state: SessionState, verbose: bool) -> None:
super().__init__(state, verbose)
self.classic = CDFClassicAPI(state, verbose)

@property
def _get_client(self) -> NeatClient:
if self._state.client is None:
raise NeatValueError("No client provided. Please provide a client to read a data model.")
return self._state.client

def data_model(self, data_model_id: DataModelIdentifier) -> IssueList:
"""Reads a Data Model from CDF to the knowledge graph.
Expand All @@ -111,7 +105,13 @@ def data_model(self, data_model_id: DataModelIdentifier) -> IssueList:
if not data_model_id.version:
raise NeatSessionError("Data model version is required to read a data model.")

importer = importers.DMSImporter.from_data_model_id(self._get_client, data_model_id)
self._state._raise_exception_if_condition_not_met(
"Read data model from CDF",
empty_rules_store_required=True,
client_required=True,
)

importer = importers.DMSImporter.from_data_model_id(cast(NeatClient, self._state.client), data_model_id)
return self._state.rule_import(importer)

def graph(
Expand All @@ -132,6 +132,12 @@ def graph(
IssueList: A list of issues that occurred during the extraction.
"""
self._state._raise_exception_if_condition_not_met(
"Read DMS Graph",
empty_rules_store_required=True,
empty_instances_store_required=True,
client_required=True,
)
return self._graph(data_model_id, instance_space, skip_cognite_views, unpack_json=False)

def _graph(
Expand All @@ -145,7 +151,7 @@ def _graph(
extractor = extractors.DMSGraphExtractor.from_data_model_id(
# We are skipping the Cognite Views
data_model_id,
self._get_client,
cast(NeatClient, self._state.client),
instance_space=instance_space,
skip_cognite_views=skip_cognite_views,
unpack_json=unpack_json,
Expand Down Expand Up @@ -181,8 +187,13 @@ def raw(
```
"""
self._state._raise_exception_if_condition_not_met(
"Read RAW",
client_required=True,
)

extractor = extractors.RAWExtractor(
self._get_client,
cast(NeatClient, self._state.client),
db_name=db_name,
table_name=table_name,
table_type=type,
Expand All @@ -200,12 +211,6 @@ class CDFClassicAPI(BaseReadAPI):
"""

@property
def _get_client(self) -> NeatClient:
if self._state.client is None:
raise ValueError("No client provided. Please provide a client to read a data model.")
return self._state.client

def graph(
self,
root_asset_external_id: str,
Expand Down Expand Up @@ -257,6 +262,13 @@ def graph(
neat.read.cdf.graph("root_asset_external_id")
```
"""
self._state._raise_exception_if_condition_not_met(
"Read classic graph",
empty_rules_store_required=True,
empty_instances_store_required=True,
client_required=True,
)

return self._graph(
root_asset_external_id, limit_per_type, identifier, reference_timeseries=False, reference_files=False
)
Expand All @@ -273,7 +285,7 @@ def _graph(
) -> IssueList:
namespace = CLASSIC_CDF_NAMESPACE
extractor = extractors.ClassicGraphExtractor(
self._get_client,
cast(NeatClient, self._state.client),
root_asset_external_id=root_asset_external_id,
limit_per_type=limit_per_type,
namespace=namespace,
Expand Down Expand Up @@ -467,6 +479,12 @@ def dexpi(self, io: Any) -> None:
warnings.filterwarnings("default")
AlphaFlags.dexpi_read.warn()

self._state._raise_exception_if_condition_not_met(
"Read DEXPI file",
empty_rules_store_required=True,
empty_instances_store_required=True,
)

path = NeatReader.create(io).materialize_path()
engine = import_engine()
engine.set.format = "dexpi"
Expand Down Expand Up @@ -521,6 +539,12 @@ def aml(self, io: Any):
warnings.filterwarnings("default")
AlphaFlags.aml_read.warn()

self._state._raise_exception_if_condition_not_met(
"Read AML file",
empty_rules_store_required=True,
empty_instances_store_required=True,
)

path = NeatReader.create(io).materialize_path()
engine = import_engine()
engine.set.format = "aml"
Expand Down Expand Up @@ -575,6 +599,11 @@ def ontology(self, io: Any) -> IssueList:
warnings.filterwarnings("default")
AlphaFlags.ontology_read.warn()

self._state._raise_exception_if_condition_not_met(
"Read Ontology file",
empty_rules_store_required=True,
)

reader = NeatReader.create(io)
importer = importers.OWLImporter.from_file(reader.materialize_path(), source_name=f"file {reader!s}")
return self._state.rule_import(importer)
Expand All @@ -593,6 +622,11 @@ def imf(self, io: Any) -> IssueList:
warnings.filterwarnings("default")
AlphaFlags.imf_read.warn()

self._state._raise_exception_if_condition_not_met(
"Read IMF file",
empty_rules_store_required=True,
)

reader = NeatReader.create(io)
importer = importers.IMFImporter.from_file(reader.materialize_path(), source_name=f"file {reader!s}")
return self._state.rule_import(importer)
Expand Down Expand Up @@ -646,17 +680,35 @@ def _get_client(self) -> NeatClient:

def nordic44(self) -> IssueList:
"""Reads the Nordic 44 knowledge graph into the NeatSession graph store."""

self._state._raise_exception_if_condition_not_met(
"Read Nordic44 graph example",
empty_instances_store_required=True,
empty_rules_store_required=True,
)

self._state.instances.store.write(extractors.RdfFileExtractor(instances_examples.nordic44_knowledge_graph))
return IssueList()

def pump_example(self) -> IssueList:
"""Reads the Hello World pump example into the NeatSession."""

self._state._raise_exception_if_condition_not_met(
"Read Pump Data Model example",
empty_rules_store_required=True,
)

importer: importers.ExcelImporter = importers.ExcelImporter(catalog.hello_world_pump)
return self._state.rule_import(importer)

def core_data_model(self) -> IssueList:
"""Reads the core data model example into the NeatSession."""

self._state._raise_exception_if_condition_not_met(
"Read Core Data Model example",
empty_rules_store_required=True,
)

cdm_v1 = DataModelId.load(("cdf_cdm", "CogniteCore", "v1"))
importer: importers.DMSImporter = importers.DMSImporter.from_data_model_id(self._get_client, cdm_v1)
return self._state.rule_import(importer)
28 changes: 28 additions & 0 deletions cognite/neat/_session/_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,34 @@ def write_graph(self, extractor: KnowledgeGraphExtractor) -> IssueList:
issues.extend(extract_issues)
return issues

def _raise_exception_if_condition_not_met(
self,
activity: str,
empty_rules_store_required: bool = False,
empty_instances_store_required: bool = False,
instances_required: bool = False,
client_required: bool = False,
) -> None:
"""Set conditions for raising an error in the session that are used by various methods in the session."""
condition = set()
suggestion = set()

if client_required and not self.client:
condition.add(f"{activity} expects a client in NEAT session")
suggestion.add("Please provide a client")
if empty_rules_store_required and not self.rule_store.empty:
condition.add(f"{activity} expects no data model in NEAT session")
suggestion.add("Start new session")
if empty_instances_store_required and not self.instances.empty:
condition.add(f"{activity} expects no instances in NEAT session")
suggestion.add("Start new session")
if instances_required and self.instances.empty:
condition.add(f"{activity} expects instances in NEAT session")
suggestion.add("Read in instances to neat session")

if condition:
raise NeatSessionError(". ".join(condition) + ". " + ". ".join(suggestion) + ". And try again.")


class InstancesState:
def __init__(
Expand Down
22 changes: 14 additions & 8 deletions cognite/neat/_session/_to.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import zipfile
from collections.abc import Collection
from pathlib import Path
from typing import Any, Literal, overload
from typing import Any, Literal, cast, overload

from cognite.client import data_modeling as dm
from cognite.client.data_classes.data_modeling import DataModelIdentifier

from cognite.neat._alpha import AlphaFlags
from cognite.neat._client._api_client import NeatClient
from cognite.neat._constants import COGNITE_MODELS
from cognite.neat._graph import loaders
from cognite.neat._issues import IssueList, NeatIssue, catch_issues
Expand Down Expand Up @@ -305,9 +306,12 @@ def _instances(
space_from_property: str | None = None,
use_source_space: bool = False,
) -> UploadResultList:
if not self._state.client:
raise NeatSessionError("No CDF client provided!")
client = self._state.client
self._state._raise_exception_if_condition_not_met(
"Export DMS instances to CDF",
client_required=True,
)

client = cast(NeatClient, self._state.client)
dms_rules = self._state.rule_store.last_verified_dms_rules
instance_space = instance_space or f"{dms_rules.metadata.space}_instances"

Expand Down Expand Up @@ -366,12 +370,14 @@ def data_model(
"""

exporter = exporters.DMSExporter(existing=existing, export_components=components, drop_data=drop_data)
self._state._raise_exception_if_condition_not_met(
"Export DMS data model to CDF",
client_required=True,
)

if not self._state.client:
raise NeatSessionError("No client provided!")
exporter = exporters.DMSExporter(existing=existing, export_components=components, drop_data=drop_data)

result = self._state.rule_store.export_to_cdf(exporter, self._state.client, dry_run)
result = self._state.rule_store.export_to_cdf(exporter, cast(NeatClient, self._state.client), dry_run)
print("You can inspect the details with the .inspect.outcome.data_model(...) method.")
return result

Expand Down

0 comments on commit 0a214eb

Please sign in to comment.