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

Potentially problematic release.


This version of cognite-neat might be problematic. Click here for more details.

Files changed (44) hide show
  1. cognite/neat/_constants.py +1 -1
  2. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +8 -4
  3. cognite/neat/_graph/queries/_base.py +4 -0
  4. cognite/neat/_graph/transformers/__init__.py +3 -3
  5. cognite/neat/_graph/transformers/_base.py +4 -4
  6. cognite/neat/_graph/transformers/_classic_cdf.py +13 -13
  7. cognite/neat/_graph/transformers/_prune_graph.py +3 -3
  8. cognite/neat/_graph/transformers/_rdfpath.py +3 -4
  9. cognite/neat/_graph/transformers/_value_type.py +23 -16
  10. cognite/neat/_issues/errors/__init__.py +2 -0
  11. cognite/neat/_issues/errors/_external.py +8 -0
  12. cognite/neat/_issues/warnings/_resources.py +1 -1
  13. cognite/neat/_rules/exporters/_rules2yaml.py +1 -1
  14. cognite/neat/_rules/importers/_rdf/_inference2rules.py +179 -118
  15. cognite/neat/_rules/models/_base_rules.py +9 -8
  16. cognite/neat/_rules/models/dms/_exporter.py +5 -4
  17. cognite/neat/_rules/transformers/__init__.py +4 -3
  18. cognite/neat/_rules/transformers/_base.py +6 -1
  19. cognite/neat/_rules/transformers/_converters.py +436 -361
  20. cognite/neat/_rules/transformers/_mapping.py +4 -4
  21. cognite/neat/_session/_base.py +71 -69
  22. cognite/neat/_session/_create.py +133 -0
  23. cognite/neat/_session/_drop.py +55 -1
  24. cognite/neat/_session/_fix.py +28 -0
  25. cognite/neat/_session/_inspect.py +20 -6
  26. cognite/neat/_session/_mapping.py +8 -8
  27. cognite/neat/_session/_prepare.py +3 -247
  28. cognite/neat/_session/_read.py +78 -4
  29. cognite/neat/_session/_set.py +34 -12
  30. cognite/neat/_session/_show.py +14 -41
  31. cognite/neat/_session/_state.py +48 -51
  32. cognite/neat/_session/_to.py +7 -3
  33. cognite/neat/_session/exceptions.py +7 -1
  34. cognite/neat/_store/_graph_store.py +14 -13
  35. cognite/neat/_store/_provenance.py +36 -20
  36. cognite/neat/_store/_rules_store.py +172 -293
  37. cognite/neat/_store/exceptions.py +40 -4
  38. cognite/neat/_utils/auth.py +4 -2
  39. cognite/neat/_version.py +1 -1
  40. {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/METADATA +1 -1
  41. {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/RECORD +44 -42
  42. {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/LICENSE +0 -0
  43. {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/WHEEL +0 -0
  44. {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.1.dist-info}/entry_points.txt +0 -0
@@ -1,36 +1,19 @@
1
- from collections.abc import Callable, Collection
2
- from typing import Any, Literal, cast
1
+ from collections.abc import Callable
2
+ from typing import Any
3
3
 
4
- from cognite.client.data_classes.data_modeling import DataModelIdentifier
5
4
  from rdflib import URIRef
6
5
 
7
- from cognite.neat._constants import (
8
- get_default_prefixes_and_namespaces,
9
- )
10
6
  from cognite.neat._graph.transformers import (
11
- AttachPropertyFromTargetToSource,
12
7
  ConnectionToLiteral,
13
8
  ConvertLiteral,
14
9
  LiteralToEntity,
15
- PruneDeadEndEdges,
16
- PruneInstancesOfUnknownType,
17
- PruneTypes,
18
10
  RelationshipAsEdgeTransformer,
19
- Transformers,
20
11
  )
21
12
  from cognite.neat._graph.transformers._rdfpath import MakeConnectionOnExactMatch
22
13
  from cognite.neat._issues import IssueList
23
14
  from cognite.neat._issues.errors import NeatValueError
24
- from cognite.neat._rules.models.dms import DMSValidation
25
15
  from cognite.neat._rules.transformers import (
26
- IncludeReferenced,
27
16
  PrefixEntities,
28
- ReduceCogniteModel,
29
- RulesTransformer,
30
- ToCompliantEntities,
31
- ToDataProductModel,
32
- ToEnterpriseModel,
33
- ToSolutionModel,
34
17
  )
35
18
  from cognite.neat._utils.text import humanize_collection
36
19
 
@@ -59,91 +42,6 @@ class InstancePrepareAPI:
59
42
  self._state = state
60
43
  self._verbose = verbose
61
44
 
62
- def dexpi(self) -> None:
63
- """Prepares extracted DEXPI graph for further usage in CDF
64
-
65
- !!! note "This method bundles several graph transformers which"
66
- - attach values of generic attributes to nodes
67
- - create associations between nodes
68
- - remove unused generic attributes
69
- - remove associations between nodes that do not exist in the extracted graph
70
- - remove edges to nodes that do not exist in the extracted graph
71
-
72
- and therefore safeguard CDF from a bad graph
73
-
74
- Example:
75
- Apply Dexpi specific transformations:
76
- ```python
77
- neat.prepare.instances.dexpi()
78
- ```
79
- """
80
-
81
- DEXPI = get_default_prefixes_and_namespaces()["dexpi"]
82
-
83
- transformers = [
84
- # Remove any instance which type is unknown
85
- PruneInstancesOfUnknownType(),
86
- # Directly connect generic attributes
87
- AttachPropertyFromTargetToSource(
88
- target_property=DEXPI.Value,
89
- target_property_holding_new_property=DEXPI.Name,
90
- target_node_type=DEXPI.GenericAttribute,
91
- delete_target_node=True,
92
- ),
93
- # Directly connect associations
94
- AttachPropertyFromTargetToSource(
95
- target_property=DEXPI.ItemID,
96
- target_property_holding_new_property=DEXPI.Type,
97
- target_node_type=DEXPI.Association,
98
- delete_target_node=True,
99
- ),
100
- # Remove unused generic attributes and associations
101
- PruneTypes([DEXPI.GenericAttribute, DEXPI.Association]),
102
- # Remove edges to nodes that do not exist in the extracted graph
103
- PruneDeadEndEdges(),
104
- ]
105
-
106
- for transformer in transformers:
107
- self._state.instances.store.transform(cast(Transformers, transformer))
108
-
109
- def aml(self) -> None:
110
- """Prepares extracted AutomationML graph for further usage in CDF
111
-
112
- !!! note "This method bundles several graph transformers which"
113
- - attach values of attributes to nodes
114
- - remove unused attributes
115
- - remove edges to nodes that do not exist in the extracted graph
116
-
117
- and therefore safeguard CDF from a bad graph
118
-
119
- Example:
120
- Apply AML specific transformations:
121
- ```python
122
- neat.prepare.instances.aml()
123
- ```
124
- """
125
-
126
- AML = get_default_prefixes_and_namespaces()["aml"]
127
-
128
- transformers = [
129
- # Remove any instance which type is unknown
130
- PruneInstancesOfUnknownType(),
131
- # Directly connect generic attributes
132
- AttachPropertyFromTargetToSource(
133
- target_property=AML.Value,
134
- target_property_holding_new_property=AML.Name,
135
- target_node_type=AML.Attribute,
136
- delete_target_node=True,
137
- ),
138
- # Prune unused attributes
139
- PruneTypes([AML.Attribute]),
140
- # # Remove edges to nodes that do not exist in the extracted graph
141
- PruneDeadEndEdges(),
142
- ]
143
-
144
- for transformer in transformers:
145
- self._state.instances.store.transform(cast(Transformers, transformer))
146
-
147
45
  def make_connection_on_exact_match(
148
46
  self,
149
47
  source: tuple[str, str],
@@ -357,10 +255,6 @@ class DataModelPrepareAPI:
357
255
  self._state = state
358
256
  self._verbose = verbose
359
257
 
360
- def cdf_compliant_external_ids(self) -> IssueList:
361
- """Convert data model component external ids to CDF compliant entities."""
362
- return self._state.rule_transform(ToCompliantEntities())
363
-
364
258
  def prefix(self, prefix: str) -> IssueList:
365
259
  """Prefix all views in the data model with the given prefix.
366
260
 
@@ -368,143 +262,5 @@ class DataModelPrepareAPI:
368
262
  prefix: The prefix to add to the views in the data model.
369
263
 
370
264
  """
371
- return self._state.rule_transform(PrefixEntities(prefix))
372
-
373
- def to_enterprise(
374
- self,
375
- data_model_id: DataModelIdentifier,
376
- org_name: str = "My",
377
- dummy_property: str = "GUID",
378
- move_connections: bool = False,
379
- ) -> IssueList:
380
- """Uses the current data model as a basis to create enterprise data model
381
-
382
- Args:
383
- data_model_id: The enterprise data model id that is being created
384
- org_name: Organization name to use for the views in the enterprise data model.
385
- dummy_property: The dummy property to use as placeholder for the views in the new data model.
386
- move_connections: If True, the connections will be moved to the new data model.
387
-
388
- !!! note "Enterprise Data Model Creation"
389
-
390
- Always create an enterprise data model from a Cognite Data Model as this will
391
- assure all the Cognite Data Fusion applications to run smoothly, such as
392
- - Search
393
- - Atlas AI
394
- - ...
395
-
396
- !!! note "Move Connections"
397
-
398
- If you want to move the connections to the new data model, set the move_connections
399
- to True. This will move the connections to the new data model and use new model
400
- views as the source and target views.
401
-
402
- """
403
- return self._state.rule_transform(
404
- ToEnterpriseModel(
405
- new_model_id=data_model_id,
406
- org_name=org_name,
407
- dummy_property=dummy_property,
408
- move_connections=move_connections,
409
- )
410
- )
411
265
 
412
- def to_solution(
413
- self,
414
- data_model_id: DataModelIdentifier,
415
- org_name: str = "My",
416
- mode: Literal["read", "write"] = "read",
417
- dummy_property: str = "GUID",
418
- ) -> IssueList:
419
- """Uses the current data model as a basis to create solution data model
420
-
421
- Args:
422
- data_model_id: The solution data model id that is being created.
423
- org_name: Organization name to use for the views in the new data model.
424
- mode: The mode of the solution data model. Can be either "read" or "write".
425
- dummy_property: The dummy property to use as placeholder for the views in the new data model.
426
-
427
- !!! note "Solution Data Model Mode"
428
-
429
- The read-only solution model will only be able to read from the existing containers
430
- from the enterprise data model, therefore the solution data model will not have
431
- containers in the solution data model space. Meaning the solution data model views
432
- will be read-only.
433
-
434
- The write mode will have additional containers in the solution data model space,
435
- allowing in addition to reading through the solution model views, also writing to
436
- the containers in the solution data model space.
437
-
438
- """
439
- return self._state.rule_transform(
440
- ToSolutionModel(
441
- new_model_id=data_model_id,
442
- org_name=org_name,
443
- mode=mode,
444
- dummy_property=dummy_property,
445
- )
446
- )
447
-
448
- def to_data_product(
449
- self,
450
- data_model_id: DataModelIdentifier,
451
- org_name: str = "",
452
- include: Literal["same-space", "all"] = "same-space",
453
- ) -> None:
454
- """Uses the current data model as a basis to create data product data model.
455
-
456
- A data product model is a data model that ONLY maps to containers and do not use implements. This is
457
- typically used for defining the data in a data product.
458
-
459
- Args:
460
- data_model_id: The data product data model id that is being created.
461
- org_name: Organization name used as prefix if the model is building on top of a Cognite Data Model.
462
- include: The views to include in the data product data model. Can be either "same-space" or "all".
463
- If you set same-space, only the properties of the views in the same space as the data model
464
- will be included.
465
- """
466
-
467
- view_ids, container_ids = DMSValidation(
468
- self._state.rule_store.last_verified_dms_rules
469
- ).imported_views_and_containers_ids()
470
- transformers: list[RulesTransformer] = []
471
- client = self._state.client
472
- if (view_ids or container_ids) and client is None:
473
- raise NeatSessionError(
474
- "No client provided. You are referencing unknown views and containers in your data model, "
475
- "NEAT needs a client to lookup the definitions. "
476
- "Please set the client in the session, NeatSession(client=client)."
477
- )
478
- elif (view_ids or container_ids) and client:
479
- transformers.append(IncludeReferenced(client, include_properties=True))
480
-
481
- transformers.append(
482
- ToDataProductModel(
483
- new_model_id=data_model_id,
484
- org_name=org_name,
485
- include=include,
486
- )
487
- )
488
-
489
- self._state.rule_transform(*transformers)
490
-
491
- def reduce(self, drop: Collection[Literal["3D", "Annotation", "BaseViews"] | str]) -> IssueList:
492
- """This is a special method that allow you to drop parts of the data model.
493
- This only applies to Cognite Data Models.
494
-
495
- Args:
496
- drop: What to drop from the data model. The values 3D, Annotation, and BaseViews are special values that
497
- drops multiple views at once. You can also pass externalIds of views to drop individual views.
498
-
499
- """
500
- return self._state.rule_transform(ReduceCogniteModel(drop))
501
-
502
- def include_referenced(self) -> IssueList:
503
- """Include referenced views and containers in the data model."""
504
- if self._state.client is None:
505
- raise NeatSessionError(
506
- "No client provided. You are referencing unknown views and containers in your data model, "
507
- "NEAT needs a client to lookup the definitions. "
508
- "Please set the client in the session, NeatSession(client=client)."
509
- )
510
- return self._state.rule_transform(IncludeReferenced(self._state.client))
266
+ return self._state.rule_transform(PrefixEntities(prefix)) # type: ignore[arg-type]
@@ -4,10 +4,23 @@ from cognite.client.data_classes.data_modeling import DataModelId, DataModelIden
4
4
  from cognite.client.utils.useful_types import SequenceNotStr
5
5
 
6
6
  from cognite.neat._client import NeatClient
7
- from cognite.neat._constants import CLASSIC_CDF_NAMESPACE
7
+ from cognite.neat._constants import (
8
+ CLASSIC_CDF_NAMESPACE,
9
+ get_default_prefixes_and_namespaces,
10
+ )
8
11
  from cognite.neat._graph import examples as instances_examples
9
12
  from cognite.neat._graph import extractors
10
- from cognite.neat._graph.transformers import ConvertLiteral, LiteralToEntity
13
+ from cognite.neat._graph.transformers import (
14
+ ConvertLiteral,
15
+ LiteralToEntity,
16
+ Transformers,
17
+ )
18
+ from cognite.neat._graph.transformers._prune_graph import (
19
+ AttachPropertyFromTargetToSource,
20
+ PruneDeadEndEdges,
21
+ PruneInstancesOfUnknownType,
22
+ PruneTypes,
23
+ )
11
24
  from cognite.neat._issues import IssueList
12
25
  from cognite.neat._issues.errors import NeatValueError
13
26
  from cognite.neat._issues.warnings import MissingCogniteClientWarning
@@ -387,7 +400,7 @@ class XMLReadAPI(BaseReadAPI):
387
400
  raise NeatValueError("Only support XML files of DEXPI format at the moment.")
388
401
 
389
402
  def dexpi(self, io: Any) -> None:
390
- """Reads a DEXPI file into the NeatSession.
403
+ """Reads a DEXPI file into the NeatSession and executes set of predefined transformations.
391
404
 
392
405
  Args:
393
406
  io: file path or url to the DEXPI file
@@ -396,6 +409,13 @@ class XMLReadAPI(BaseReadAPI):
396
409
  ```python
397
410
  neat.read.xml.dexpi("url_or_path_to_dexpi_file")
398
411
  ```
412
+
413
+ !!! note "This method bundles several graph transformers which"
414
+ - attach values of generic attributes to nodes
415
+ - create associations between nodes
416
+ - remove unused generic attributes
417
+ - remove associations between nodes that do not exist in the extracted graph
418
+ - remove edges to nodes that do not exist in the extracted graph
399
419
  """
400
420
  path = NeatReader.create(io).materialize_path()
401
421
  engine = import_engine()
@@ -404,8 +424,36 @@ class XMLReadAPI(BaseReadAPI):
404
424
  extractor = engine.create_extractor()
405
425
  self._state.instances.store.write(extractor)
406
426
 
427
+ DEXPI = get_default_prefixes_and_namespaces()["dexpi"]
428
+
429
+ transformers = [
430
+ # Remove any instance which type is unknown
431
+ PruneInstancesOfUnknownType(),
432
+ # Directly connect generic attributes
433
+ AttachPropertyFromTargetToSource(
434
+ target_property=DEXPI.Value,
435
+ target_property_holding_new_property=DEXPI.Name,
436
+ target_node_type=DEXPI.GenericAttribute,
437
+ delete_target_node=True,
438
+ ),
439
+ # Directly connect associations
440
+ AttachPropertyFromTargetToSource(
441
+ target_property=DEXPI.ItemID,
442
+ target_property_holding_new_property=DEXPI.Type,
443
+ target_node_type=DEXPI.Association,
444
+ delete_target_node=True,
445
+ ),
446
+ # Remove unused generic attributes and associations
447
+ PruneTypes([DEXPI.GenericAttribute, DEXPI.Association]),
448
+ # Remove edges to nodes that do not exist in the extracted graph
449
+ PruneDeadEndEdges(),
450
+ ]
451
+
452
+ for transformer in transformers:
453
+ self._state.instances.store.transform(cast(Transformers, transformer))
454
+
407
455
  def aml(self, io: Any):
408
- """Reads an AML file into NeatSession.
456
+ """Reads an AML file into NeatSession and executes a set of predefined transformations.
409
457
 
410
458
  Args:
411
459
  io: file path or url to the AML file
@@ -414,6 +462,11 @@ class XMLReadAPI(BaseReadAPI):
414
462
  ```python
415
463
  neat.read.xml.aml("url_or_path_to_aml_file")
416
464
  ```
465
+
466
+ !!! note "This method bundles several graph transformers which"
467
+ - attach values of attributes to nodes
468
+ - remove unused attributes
469
+ - remove edges to nodes that do not exist in the extracted graph
417
470
  """
418
471
  path = NeatReader.create(io).materialize_path()
419
472
  engine = import_engine()
@@ -422,6 +475,27 @@ class XMLReadAPI(BaseReadAPI):
422
475
  extractor = engine.create_extractor()
423
476
  self._state.instances.store.write(extractor)
424
477
 
478
+ AML = get_default_prefixes_and_namespaces()["aml"]
479
+
480
+ transformers = [
481
+ # Remove any instance which type is unknown
482
+ PruneInstancesOfUnknownType(),
483
+ # Directly connect generic attributes
484
+ AttachPropertyFromTargetToSource(
485
+ target_property=AML.Value,
486
+ target_property_holding_new_property=AML.Name,
487
+ target_node_type=AML.Attribute,
488
+ delete_target_node=True,
489
+ ),
490
+ # Prune unused attributes
491
+ PruneTypes([AML.Attribute]),
492
+ # # Remove edges to nodes that do not exist in the extracted graph
493
+ PruneDeadEndEdges(),
494
+ ]
495
+
496
+ for transformer in transformers:
497
+ self._state.instances.store.transform(cast(Transformers, transformer))
498
+
425
499
 
426
500
  @session_class_wrapper
427
501
  class RDFReadAPI(BaseReadAPI):
@@ -3,7 +3,7 @@ from cognite.client import data_modeling as dm
3
3
 
4
4
  from cognite.neat._client import NeatClient
5
5
  from cognite.neat._constants import COGNITE_MODELS
6
- from cognite.neat._graph.transformers import SetNeatType
6
+ from cognite.neat._graph.transformers import SetType
7
7
  from cognite.neat._issues import IssueList
8
8
  from cognite.neat._issues.errors import NeatValueError
9
9
  from cognite.neat._rules.models import DMSRules
@@ -21,6 +21,7 @@ class SetAPI:
21
21
  def __init__(self, state: SessionState, verbose: bool) -> None:
22
22
  self._state = state
23
23
  self._verbose = verbose
24
+ self.instances = SetInstances(state, verbose)
24
25
 
25
26
  def data_model_id(self, new_model_id: dm.DataModelId | tuple[str, str, str]) -> IssueList:
26
27
  """Sets the data model ID of the latest verified data model. Set the data model id as a tuple of strings
@@ -32,7 +33,9 @@ class SetAPI:
32
33
  neat.set.data_model_id(("my_data_model_space", "My_Data_Model", "v1"))
33
34
  ```
34
35
  """
35
- rules = self._state.rule_store.get_last_successful_entity().result
36
+ if self._state.rule_store.empty:
37
+ raise NeatSessionError("No rules to set the data model ID.")
38
+ rules = self._state.rule_store.provenance[-1].target_entity.dms
36
39
  if isinstance(rules, DMSRules):
37
40
  if rules.metadata.as_data_model_id() in COGNITE_MODELS:
38
41
  raise NeatSessionError(
@@ -48,26 +51,45 @@ class SetAPI:
48
51
  print(f"Client set to {self._state.client.config.project} CDF project.")
49
52
  return None
50
53
 
51
- def _instance_sub_type(self, type: str, property: str, drop_property: bool = False) -> None:
52
- """Sets the sub type of an instance based on the property."""
53
- type_uri = self._state.instances.store.queries.type_uri(type)
54
- property_uri = self._state.instances.store.queries.property_uri(property)
54
+
55
+ @session_class_wrapper
56
+ class SetInstances:
57
+ """Used to change instances"""
58
+
59
+ def __init__(self, state: SessionState, verbose: bool) -> None:
60
+ self._state = state
61
+ self._verbose = verbose
62
+
63
+ def type_using_property(self, current_type: str, property_type: str, drop_property: bool = True) -> None:
64
+ """Replaces the type of all instances with the value of a property.
65
+
66
+ Example:
67
+ All Assets have a property `assetCategory` that we want to use as the type of all asset instances.
68
+
69
+ ```python
70
+ neat.set.instances.replace_type("Asset", "assetCategory")
71
+ ```
72
+ """
73
+ type_uri = self._state.instances.store.queries.type_uri(current_type)
74
+ property_uri = self._state.instances.store.queries.property_uri(property_type)
55
75
 
56
76
  if not type_uri:
57
- raise NeatValueError(f"Type {type} does not exist in the graph.")
77
+ raise NeatValueError(f"Type {current_type} does not exist in the graph.")
58
78
  elif len(type_uri) > 1:
59
- raise NeatValueError(f"{type} has multiple ids found in the graph: {humanize_collection(type_uri)}.")
79
+ raise NeatValueError(
80
+ f"{current_type} has multiple ids found in the graph: {humanize_collection(type_uri)}."
81
+ )
60
82
 
61
83
  if not property_uri:
62
- raise NeatValueError(f"Property {property} does not exist in the graph.")
84
+ raise NeatValueError(f"Property {property_type} does not exist in the graph.")
63
85
  elif len(type_uri) > 1:
64
86
  raise NeatValueError(
65
- f"{property} has multiple ids found in the graph: {humanize_collection(property_uri)}."
87
+ f"{property_type} has multiple ids found in the graph: {humanize_collection(property_uri)}."
66
88
  )
67
89
 
68
90
  if not self._state.instances.store.queries.type_with_property(type_uri[0], property_uri[0]):
69
- raise NeatValueError(f"Property {property} is not defined for type {type}.")
91
+ raise NeatValueError(f"Property {property_type} is not defined for type {current_type}.")
70
92
 
71
- self._state.instances.store.transform(SetNeatType(type_uri[0], property_uri[0], drop_property))
93
+ self._state.instances.store.transform(SetType(type_uri[0], property_uri[0], drop_property))
72
94
 
73
95
  return None
@@ -98,22 +98,15 @@ class ShowDataModelAPI(ShowBaseAPI):
98
98
  self.implements = ShowDataModelImplementsAPI(self._state)
99
99
 
100
100
  def __call__(self) -> Any:
101
- if not self._state.rule_store.has_verified_rules:
102
- raise NeatSessionError(
103
- "No verified data model available. Try using [bold].verify()[/bold] to verify data model."
104
- )
105
-
106
- rules = self._state.rule_store.last_verified_rule
101
+ if self._state.rule_store.empty:
102
+ raise NeatSessionError("No data model available. Try using [bold].read[/bold] to read a new data model.")
103
+ last_target = self._state.rule_store.provenance[-1].target_entity
104
+ rules = last_target.dms or last_target.information
107
105
 
108
- if isinstance(rules, DMSRules):
109
- di_graph = self._generate_dms_di_graph(rules)
110
- elif isinstance(rules, InformationRules):
111
- di_graph = self._generate_info_di_graph(rules)
106
+ if last_target.dms is not None:
107
+ di_graph = self._generate_dms_di_graph(last_target.dms)
112
108
  else:
113
- # This should never happen, but we need to handle it to satisfy mypy
114
- raise NeatSessionError(
115
- f"Unsupported type {type(rules)}. Make sure you have either information or DMS rules."
116
- )
109
+ di_graph = self._generate_info_di_graph(last_target.information)
117
110
  identifier = to_directory_compatible(str(rules.metadata.identifier))
118
111
  name = f"{identifier}.html"
119
112
 
@@ -187,22 +180,16 @@ class ShowDataModelImplementsAPI(ShowBaseAPI):
187
180
  self._state = state
188
181
 
189
182
  def __call__(self) -> Any:
190
- if not self._state.rule_store.has_verified_rules:
191
- raise NeatSessionError(
192
- "No verified data model available. Try using [bold].verify()[/bold] to verify data model."
193
- )
183
+ if self._state.rule_store.empty:
184
+ raise NeatSessionError("No data model available. Try using [bold].read[/bold] to read a data model.")
194
185
 
195
- rules = self._state.rule_store.last_verified_rule
186
+ last_target = self._state.rule_store.provenance[-1].target_entity
187
+ rules = last_target.dms or last_target.information
196
188
 
197
- if isinstance(rules, DMSRules):
198
- di_graph = self._generate_dms_di_graph(rules)
199
- elif isinstance(rules, InformationRules):
200
- di_graph = self._generate_info_di_graph(rules)
189
+ if last_target.dms is not None:
190
+ di_graph = self._generate_dms_di_graph(last_target.dms)
201
191
  else:
202
- # This should never happen, but we need to handle it to satisfy mypy
203
- raise NeatSessionError(
204
- f"Unsupported type {type(rules)}. Make sure you have either information or DMS rules."
205
- )
192
+ di_graph = self._generate_info_di_graph(last_target.information)
206
193
  identifier = to_directory_compatible(str(rules.metadata.identifier))
207
194
  name = f"{identifier}_implements.html"
208
195
  return self._generate_visualization(di_graph, name)
@@ -325,20 +312,6 @@ class ShowDataModelProvenanceAPI(ShowBaseAPI):
325
312
  )
326
313
  di_graph.add_edge(source_shorten, export_id, label="exported", color="grey")
327
314
 
328
- for pruned_lists in self._state.rule_store.pruned_by_source_entity_id.values():
329
- for prune_path in pruned_lists:
330
- for change in prune_path:
331
- source = uri_display_name(change.source_entity.id_)
332
- target = uri_display_name(change.target_entity.id_)
333
- di_graph.add_node(
334
- target,
335
- label=target,
336
- type="Pruned",
337
- title="Pruned",
338
- color=hex_colored_types["Pruned"],
339
- )
340
- di_graph.add_edge(source, target, label="pruned", color="grey")
341
-
342
315
  return di_graph
343
316
 
344
317