cognite-neat 0.87.0__py3-none-any.whl → 0.87.3__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 (85) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/app/api/configuration.py +1 -10
  3. cognite/neat/app/api/routers/data_exploration.py +1 -1
  4. cognite/neat/config.py +84 -17
  5. cognite/neat/graph/extractors/_classic_cdf/_assets.py +1 -1
  6. cognite/neat/graph/extractors/_classic_cdf/_events.py +1 -1
  7. cognite/neat/graph/extractors/_classic_cdf/_files.py +1 -1
  8. cognite/neat/graph/extractors/_classic_cdf/_labels.py +1 -1
  9. cognite/neat/graph/extractors/_classic_cdf/_relationships.py +1 -1
  10. cognite/neat/graph/extractors/_classic_cdf/_sequences.py +1 -1
  11. cognite/neat/graph/extractors/_classic_cdf/_timeseries.py +1 -1
  12. cognite/neat/graph/extractors/_dexpi.py +1 -1
  13. cognite/neat/graph/extractors/_mock_graph_generator.py +1 -1
  14. cognite/neat/graph/loaders/_rdf2asset.py +108 -52
  15. cognite/neat/graph/loaders/_rdf2dms.py +6 -6
  16. cognite/neat/graph/queries/_base.py +20 -11
  17. cognite/neat/graph/queries/_construct.py +3 -3
  18. cognite/neat/graph/queries/_shared.py +1 -1
  19. cognite/neat/graph/stores/_base.py +14 -3
  20. cognite/neat/graph/transformers/__init__.py +3 -0
  21. cognite/neat/graph/transformers/_rdfpath.py +42 -0
  22. cognite/neat/legacy/graph/extractors/_mock_graph_generator.py +1 -1
  23. cognite/neat/legacy/graph/loaders/_asset_loader.py +2 -2
  24. cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py +5 -2
  25. cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py +4 -1
  26. cognite/neat/legacy/graph/loaders/rdf_to_dms.py +3 -1
  27. cognite/neat/legacy/graph/transformations/query_generator/sparql.py +1 -1
  28. cognite/neat/legacy/graph/transformations/transformer.py +1 -1
  29. cognite/neat/legacy/rules/exporters/_rules2dms.py +8 -3
  30. cognite/neat/legacy/rules/exporters/_rules2graphql.py +1 -1
  31. cognite/neat/legacy/rules/exporters/_rules2ontology.py +2 -1
  32. cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py +3 -4
  33. cognite/neat/legacy/rules/importers/_dms2rules.py +4 -1
  34. cognite/neat/legacy/rules/importers/_graph2rules.py +5 -32
  35. cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py +1 -1
  36. cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py +2 -1
  37. cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py +1 -1
  38. cognite/neat/legacy/rules/models/raw_rules.py +1 -1
  39. cognite/neat/rules/analysis/_asset.py +15 -0
  40. cognite/neat/rules/analysis/_base.py +1 -1
  41. cognite/neat/rules/analysis/_information.py +40 -12
  42. cognite/neat/rules/exporters/_rules2dms.py +1 -1
  43. cognite/neat/rules/exporters/_rules2ontology.py +2 -1
  44. cognite/neat/rules/importers/_dms2rules.py +3 -1
  45. cognite/neat/rules/importers/_inference2rules.py +1 -5
  46. cognite/neat/rules/importers/_owl2rules/_owl2classes.py +1 -1
  47. cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +2 -1
  48. cognite/neat/rules/importers/_owl2rules/_owl2properties.py +1 -1
  49. cognite/neat/rules/issues/spreadsheet.py +35 -0
  50. cognite/neat/rules/models/_rdfpath.py +17 -21
  51. cognite/neat/rules/models/asset/_validation.py +38 -1
  52. cognite/neat/rules/models/dms/_exporter.py +7 -3
  53. cognite/neat/rules/models/dms/_schema.py +5 -4
  54. cognite/neat/rules/models/entities.py +26 -8
  55. cognite/neat/rules/models/information/_validation.py +1 -1
  56. cognite/neat/utils/__init__.py +0 -3
  57. cognite/neat/utils/auth.py +47 -28
  58. cognite/neat/utils/auxiliary.py +141 -1
  59. cognite/neat/utils/cdf/__init__.py +0 -0
  60. cognite/neat/utils/{cdf_classes.py → cdf/data_classes.py} +122 -2
  61. cognite/neat/utils/{cdf_loaders → cdf/loaders}/_data_modeling.py +37 -0
  62. cognite/neat/utils/{cdf_loaders → cdf/loaders}/_ingestion.py +2 -1
  63. cognite/neat/utils/collection_.py +18 -0
  64. cognite/neat/utils/rdf_.py +165 -0
  65. cognite/neat/utils/text.py +4 -0
  66. cognite/neat/utils/time_.py +17 -0
  67. cognite/neat/utils/upload.py +13 -1
  68. cognite/neat/workflows/_exceptions.py +5 -5
  69. cognite/neat/workflows/base.py +1 -1
  70. cognite/neat/workflows/steps/lib/current/rules_validator.py +2 -2
  71. cognite/neat/workflows/steps/lib/legacy/graph_extractor.py +1 -1
  72. cognite/neat/workflows/steps/lib/legacy/graph_loader.py +1 -1
  73. cognite/neat/workflows/steps/lib/legacy/rules_exporter.py +1 -1
  74. cognite/neat/workflows/steps/lib/legacy/rules_importer.py +1 -1
  75. {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/METADATA +2 -2
  76. {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/RECORD +81 -81
  77. cognite/neat/utils/cdf.py +0 -59
  78. cognite/neat/utils/cdf_loaders/data_classes.py +0 -121
  79. cognite/neat/utils/exceptions.py +0 -41
  80. cognite/neat/utils/utils.py +0 -429
  81. /cognite/neat/utils/{cdf_loaders → cdf/loaders}/__init__.py +0 -0
  82. /cognite/neat/utils/{cdf_loaders → cdf/loaders}/_base.py +0 -0
  83. {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/LICENSE +0 -0
  84. {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/WHEEL +0 -0
  85. {cognite_neat-0.87.0.dist-info → cognite_neat-0.87.3.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from collections.abc import (
3
+ Hashable,
3
4
  ItemsView,
4
5
  Iterable,
5
6
  Iterator,
@@ -8,11 +9,18 @@ from collections.abc import (
8
9
  MutableMapping,
9
10
  ValuesView,
10
11
  )
12
+ from dataclasses import dataclass
11
13
  from typing import Any, TypeVar, cast, final
12
14
 
13
15
  import pandas as pd
14
16
  import yaml
15
- from cognite.client.data_classes._base import T_CogniteResource
17
+ from cognite.client import CogniteClient
18
+ from cognite.client.data_classes._base import (
19
+ CogniteResourceList,
20
+ T_CogniteResource,
21
+ WriteableCogniteResource,
22
+ WriteableCogniteResourceList,
23
+ )
16
24
  from cognite.client.data_classes.data_modeling import (
17
25
  ContainerApply,
18
26
  ContainerId,
@@ -29,7 +37,119 @@ from cognite.client.utils._pandas_helpers import (
29
37
  convert_nullable_int_cols,
30
38
  )
31
39
 
32
- T_ID = TypeVar("T_ID")
40
+ # The Table, TableWrite data classes in the Cognite-SDK lacks the database attribute.
41
+ # This is a problem when creating the RawTableLoader that needs the data class to be able to create, update, retrieve
42
+ # and delete tables.
43
+ # This is a reimplemented version of the Table, TableWrite data classes with the database attribute added.
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class RawTableID:
48
+ table: str
49
+ database: str
50
+
51
+ def as_tuple(self) -> tuple[str, str]:
52
+ return self.database, self.table
53
+
54
+
55
+ class RawTableCore(WriteableCogniteResource["RawTableWrite"], ABC):
56
+ """A NoSQL database table to store customer data
57
+
58
+ Args:
59
+ name (str | None): Unique name of the table
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ name: str | None = None,
65
+ database: str | None = None,
66
+ ) -> None:
67
+ self.name = name
68
+ self.database = database
69
+
70
+ def as_id(self) -> RawTableID:
71
+ if self.name is None or self.database is None:
72
+ raise ValueError("name and database are required to create a TableID")
73
+ return RawTableID(table=self.name, database=self.database)
74
+
75
+
76
+ class RawTable(RawTableCore):
77
+ """A NoSQL database table to store customer data.
78
+ This is the reading version of the Table class, which is used when retrieving a table.
79
+
80
+ Args:
81
+ name (str | None): Unique name of the table
82
+ created_time (int | None): Time the table was created.
83
+ cognite_client (CogniteClient | None): The client to associate with this object.
84
+ """
85
+
86
+ def __init__(
87
+ self,
88
+ name: str | None = None,
89
+ database: str | None = None,
90
+ created_time: int | None = None,
91
+ cognite_client: CogniteClient | None = None,
92
+ ) -> None:
93
+ super().__init__(name, database)
94
+ self.created_time = created_time
95
+ self._cognite_client = cast("CogniteClient", cognite_client)
96
+
97
+ self._db_name: str | None = None
98
+
99
+ def as_write(self) -> "RawTableWrite":
100
+ """Returns this Table as a TableWrite"""
101
+ if self.name is None or self.database is None:
102
+ raise ValueError("name and database are required to create a Table")
103
+ return RawTableWrite(name=self.name, database=self.database)
104
+
105
+
106
+ class RawTableWrite(RawTableCore):
107
+ """A NoSQL database table to store customer data
108
+ This is the writing version of the Table class, which is used when creating a table.
109
+
110
+ Args:
111
+ name (str): Unique name of the table
112
+ """
113
+
114
+ def __init__(
115
+ self,
116
+ name: str,
117
+ database: str,
118
+ ) -> None:
119
+ super().__init__(name, database)
120
+
121
+ @classmethod
122
+ def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None = None) -> "RawTableWrite":
123
+ return cls(resource["name"], resource["database"])
124
+
125
+ def as_write(self) -> "RawTableWrite":
126
+ """Returns this TableWrite instance."""
127
+ return self
128
+
129
+
130
+ class RawTableWriteList(CogniteResourceList[RawTableWrite]):
131
+ _RESOURCE = RawTableWrite
132
+
133
+ def as_ids(self) -> list[RawTableID]:
134
+ """Returns this TableWriteList as a list of TableIDs"""
135
+ return [table.as_id() for table in self.data]
136
+
137
+
138
+ class RawTableList(
139
+ WriteableCogniteResourceList[RawTableWrite, RawTable],
140
+ ):
141
+ _RESOURCE = RawTable
142
+
143
+ def as_write(self) -> RawTableWriteList:
144
+ """Returns this TableList as a TableWriteList"""
145
+ return RawTableWriteList([table.as_write() for table in self.data])
146
+
147
+ def as_ids(self) -> list[RawTableID]:
148
+ """Returns this TableList as a list of TableIDs"""
149
+ return [table.as_id() for table in self.data]
150
+
151
+
152
+ T_ID = TypeVar("T_ID", bound=Hashable)
33
153
 
34
154
 
35
155
  # Inheriting from dict as we are extending it,
@@ -3,6 +3,7 @@ from graphlib import TopologicalSorter
3
3
  from typing import Any, Literal, cast
4
4
 
5
5
  from cognite.client import CogniteClient
6
+ from cognite.client.data_classes import filters
6
7
  from cognite.client.data_classes._base import (
7
8
  T_CogniteResourceList,
8
9
  T_WritableCogniteResource,
@@ -95,6 +96,42 @@ class SpaceLoader(DataModelingLoader[str, SpaceApply, Space, SpaceApplyList, Spa
95
96
  ids = [cast(Space | SpaceApply, item).space for item in ids]
96
97
  return self.client.data_modeling.spaces.delete(cast(SequenceNotStr[str], ids))
97
98
 
99
+ def clean(self, space: str) -> None:
100
+ """Deletes all data in a space.
101
+
102
+ This means all nodes, edges, views, containers, and data models located in the given space.
103
+
104
+ Args:
105
+ client: Connected CogniteClient
106
+ space: The space to delete.
107
+
108
+ """
109
+ edges = self.client.data_modeling.instances.list(
110
+ "edge", limit=-1, filter=filters.Equals(["edge", "space"], space)
111
+ )
112
+ if edges:
113
+ instances = self.client.data_modeling.instances.delete(edges=edges.as_ids())
114
+ print(f"Deleted {len(instances.edges)} edges")
115
+ nodes = self.client.data_modeling.instances.list(
116
+ "node", limit=-1, filter=filters.Equals(["node", "space"], space)
117
+ )
118
+ if nodes:
119
+ instances = self.client.data_modeling.instances.delete(nodes=nodes.as_ids())
120
+ print(f"Deleted {len(instances.nodes)} nodes")
121
+ views = self.client.data_modeling.views.list(limit=-1, space=space)
122
+ if views:
123
+ deleted_views = self.client.data_modeling.views.delete(views.as_ids())
124
+ print(f"Deleted {len(deleted_views)} views")
125
+ containers = self.client.data_modeling.containers.list(limit=-1, space=space)
126
+ if containers:
127
+ deleted_containers = self.client.data_modeling.containers.delete(containers.as_ids())
128
+ print(f"Deleted {len(deleted_containers)} containers")
129
+ if data_models := self.client.data_modeling.data_models.list(limit=-1, space=space):
130
+ deleted_data_models = self.client.data_modeling.data_models.delete(data_models.as_ids())
131
+ print(f"Deleted {len(deleted_data_models)} data models")
132
+ deleted_space = self.client.data_modeling.spaces.delete(space)
133
+ print(f"Deleted space {deleted_space}")
134
+
98
135
 
99
136
  class ViewLoader(DataModelingLoader[ViewId, ViewApply, View, ViewApplyList, ViewList]):
100
137
  resource_name = "views"
@@ -15,8 +15,9 @@ from cognite.client.data_classes import (
15
15
  from cognite.client.exceptions import CogniteAPIError
16
16
  from cognite.client.utils.useful_types import SequenceNotStr
17
17
 
18
+ from cognite.neat.utils.cdf.data_classes import RawTable, RawTableID, RawTableList, RawTableWrite, RawTableWriteList
19
+
18
20
  from ._base import ResourceLoader
19
- from .data_classes import RawTable, RawTableID, RawTableList, RawTableWrite, RawTableWriteList
20
21
 
21
22
 
22
23
  class TransformationLoader(
@@ -0,0 +1,18 @@
1
+ from collections import Counter
2
+ from collections.abc import Sequence
3
+ from typing import TypeVar
4
+
5
+ T_Element = TypeVar("T_Element")
6
+
7
+
8
+ def remove_none_elements_from_set(s: set[T_Element]) -> set[T_Element]:
9
+ return {x for x in s if x is not None}
10
+
11
+
12
+ def most_occurring_element(list_of_elements: list[T_Element]) -> T_Element:
13
+ counts = Counter(list_of_elements)
14
+ return counts.most_common(1)[0][0]
15
+
16
+
17
+ def chunker(sequence: Sequence[T_Element], chunk_size: int) -> list[Sequence[T_Element]]:
18
+ return [sequence[i : i + chunk_size] for i in range(0, len(sequence), chunk_size)]
@@ -0,0 +1,165 @@
1
+ import re
2
+ from typing import Any, Literal, TypeAlias, cast, overload
3
+
4
+ from pydantic import HttpUrl, TypeAdapter, ValidationError
5
+ from rdflib import Literal as RdfLiteral
6
+ from rdflib import Namespace, URIRef
7
+
8
+ Triple: TypeAlias = tuple[URIRef, URIRef, RdfLiteral | URIRef]
9
+
10
+
11
+ @overload
12
+ def remove_namespace_from_uri(
13
+ *URI: URIRef | str,
14
+ special_separator: str = "#_",
15
+ validation: Literal["full", "prefix"] = "prefix",
16
+ ) -> str: ...
17
+
18
+
19
+ @overload
20
+ def remove_namespace_from_uri(
21
+ *URI: tuple[URIRef | str, ...],
22
+ special_separator: str = "#_",
23
+ validation: Literal["full", "prefix"] = "prefix",
24
+ ) -> tuple[str, ...]: ...
25
+
26
+
27
+ def remove_namespace_from_uri(
28
+ *URI: URIRef | str | tuple[URIRef | str, ...],
29
+ special_separator: str = "#_",
30
+ validation: Literal["full", "prefix"] = "prefix",
31
+ ) -> tuple[str, ...] | str:
32
+ """Removes namespace from URI
33
+
34
+ Args
35
+ URI: URIRef | str
36
+ URI of an entity
37
+ special_separator : str
38
+ Special separator to use instead of # or / if present in URI
39
+ Set by default to "#_" which covers special client use case
40
+ validation: str
41
+ Validation type to use for URI. If set to "full", URI will be validated using pydantic
42
+ If set to "prefix", only check if URI starts with http or https will be made
43
+
44
+ Returns
45
+ Entities id without namespace
46
+
47
+ Examples:
48
+
49
+ >>> remove_namespace_from_uri("http://www.example.org/index.html#section2")
50
+ 'section2'
51
+ >>> remove_namespace_from_uri("http://www.example.org/index.html#section2", "http://www.example.org/index.html#section3")
52
+ ('section2', 'section3')
53
+ """
54
+ if isinstance(URI, str | URIRef):
55
+ uris = (URI,)
56
+ elif isinstance(URI, tuple):
57
+ # Assume that all elements in the tuple are of the same type following type hint
58
+ uris = cast(tuple[URIRef | str, ...], URI)
59
+ else:
60
+ raise TypeError(f"URI must be of type URIRef or str, got {type(URI)}")
61
+
62
+ output = []
63
+ for u in uris:
64
+ if validation == "full":
65
+ try:
66
+ _ = TypeAdapter(HttpUrl).validate_python(u)
67
+ output.append(u.split(special_separator if special_separator in u else "#" if "#" in u else "/")[-1])
68
+ except ValidationError:
69
+ output.append(str(u))
70
+ else:
71
+ if u.lower().startswith("http"):
72
+ output.append(u.split(special_separator if special_separator in u else "#" if "#" in u else "/")[-1])
73
+ else:
74
+ output.append(str(u))
75
+
76
+ return tuple(output) if len(output) > 1 else output[0]
77
+
78
+
79
+ def get_namespace(URI: URIRef, special_separator: str = "#_") -> str:
80
+ """Removes namespace from URI
81
+
82
+ Parameters
83
+ ----------
84
+ URI : URIRef
85
+ URI of an entity
86
+ special_separator : str
87
+ Special separator to use instead of # or / if present in URI
88
+ Set by default to "#_" which covers special client use case
89
+
90
+ Returns
91
+ -------
92
+ str
93
+ Entity id without namespace
94
+ """
95
+ if special_separator in URI:
96
+ return URI.split(special_separator)[0] + special_separator
97
+ elif "#" in URI:
98
+ return URI.split("#")[0] + "#"
99
+ else:
100
+ return "/".join(URI.split("/")[:-1]) + "/"
101
+
102
+
103
+ def as_neat_compliant_uri(uri: URIRef) -> URIRef:
104
+ namespace = get_namespace(uri)
105
+ id_ = remove_namespace_from_uri(uri)
106
+ compliant_uri = re.sub(r"[^a-zA-Z0-9-_.]", "", id_)
107
+ return URIRef(f"{namespace}{compliant_uri}")
108
+
109
+
110
+ def convert_rdflib_content(content: RdfLiteral | URIRef | dict | list) -> Any:
111
+ if isinstance(content, RdfLiteral) or isinstance(content, URIRef):
112
+ return content.toPython()
113
+ elif isinstance(content, dict):
114
+ return {key: convert_rdflib_content(value) for key, value in content.items()}
115
+ elif isinstance(content, list):
116
+ return [convert_rdflib_content(item) for item in content]
117
+ else:
118
+ return content
119
+
120
+
121
+ def uri_to_short_form(URI: URIRef, prefixes: dict[str, Namespace]) -> str | URIRef:
122
+ """Returns the short form of a URI if its namespace is present in the prefixes dict,
123
+ otherwise returns the URI itself
124
+
125
+ Args:
126
+ URI: URI to be converted to form prefix:entityName
127
+ prefixes: dict of prefixes
128
+
129
+ Returns:
130
+ shortest form of the URI if its namespace is present in the prefixes dict,
131
+ otherwise returns the URI itself
132
+ """
133
+ uris: set[str | URIRef] = {URI}
134
+ for prefix, namespace in prefixes.items():
135
+ if URI.startswith(namespace):
136
+ uris.add(f"{prefix}:{URI.replace(namespace, '')}")
137
+ return min(uris, key=len)
138
+
139
+
140
+ def _traverse(hierarchy: dict, graph: dict, names: list[str]) -> dict:
141
+ """traverse the graph and return the hierarchy"""
142
+ for name in names:
143
+ hierarchy[name] = _traverse({}, graph, graph[name])
144
+ return hierarchy
145
+
146
+
147
+ def get_inheritance_path(child: Any, child_parent: dict[Any, list[Any]]) -> list:
148
+ """Returns the inheritance path for a given child
149
+
150
+ Args:
151
+ child: Child class
152
+ child_parent: Dictionary of child to parent relationship
153
+
154
+ Returns:
155
+ Inheritance path for a given child
156
+
157
+ !!! note "No Circular Inheritance"
158
+ This method assumes that the child_parent dictionary is a tree and does not contain any cycles.
159
+ """
160
+ path = []
161
+ if child in child_parent:
162
+ path.extend(child_parent[child])
163
+ for parent in child_parent[child]:
164
+ path.extend(get_inheritance_path(parent, child_parent))
165
+ return path
@@ -102,3 +102,7 @@ def to_snake(string: str) -> str:
102
102
  else:
103
103
  words = pattern.findall(string)
104
104
  return "_".join(map(str.lower, words))
105
+
106
+
107
+ def replace_non_alphanumeric_with_underscore(text: str) -> str:
108
+ return re.sub(r"\W+", "_", text)
@@ -0,0 +1,17 @@
1
+ import sys
2
+ from datetime import datetime
3
+
4
+ if sys.version_info >= (3, 11):
5
+ from datetime import UTC
6
+ else:
7
+ from datetime import timezone
8
+
9
+ UTC = timezone.utc
10
+
11
+
12
+ def epoch_now_ms() -> int:
13
+ return int((datetime.now(UTC) - datetime(1970, 1, 1, tzinfo=UTC)).total_seconds() * 1000)
14
+
15
+
16
+ def datetime_utc_now():
17
+ return datetime.now(UTC)
@@ -36,6 +36,7 @@ class UploadResultList(NeatList[UploadResultCore]): ...
36
36
  @dataclass
37
37
  class UploadResult(UploadResultCore, Generic[T_ID]):
38
38
  created: set[T_ID] = field(default_factory=set)
39
+ upserted: set[T_ID] = field(default_factory=set)
39
40
  deleted: set[T_ID] = field(default_factory=set)
40
41
  changed: set[T_ID] = field(default_factory=set)
41
42
  unchanged: set[T_ID] = field(default_factory=set)
@@ -53,12 +54,21 @@ class UploadResult(UploadResultCore, Generic[T_ID]):
53
54
 
54
55
  @property
55
56
  def success(self) -> int:
56
- return len(self.created) + len(self.deleted) + len(self.changed) + len(self.unchanged) + len(self.skipped)
57
+ return (
58
+ len(self.created)
59
+ + len(self.deleted)
60
+ + len(self.changed)
61
+ + len(self.upserted)
62
+ + len(self.unchanged)
63
+ + len(self.skipped)
64
+ )
57
65
 
58
66
  def dump(self, aggregate: bool = True) -> dict[str, Any]:
59
67
  output = super().dump(aggregate)
60
68
  if self.created:
61
69
  output["created"] = len(self.created) if aggregate else list(self.created)
70
+ if self.upserted:
71
+ output["upserted"] = len(self.upserted) if aggregate else list(self.upserted)
62
72
  if self.deleted:
63
73
  output["deleted"] = len(self.deleted) if aggregate else list(self.deleted)
64
74
  if self.changed:
@@ -69,6 +79,8 @@ class UploadResult(UploadResultCore, Generic[T_ID]):
69
79
  output["skipped"] = len(self.skipped) if aggregate else list(self.skipped)
70
80
  if self.failed_created:
71
81
  output["failed_created"] = len(self.failed_created) if aggregate else list(self.failed_created)
82
+ if self.failed_upserted:
83
+ output["failed_upserted"] = len(self.failed_upserted) if aggregate else list(self.failed_upserted)
72
84
  if self.failed_changed:
73
85
  output["failed_changed"] = len(self.failed_changed) if aggregate else list(self.failed_changed)
74
86
  if self.failed_deleted:
@@ -1,7 +1,7 @@
1
- from cognite.neat.utils.exceptions import NeatError
1
+ from cognite.neat.exceptions import NeatException
2
2
 
3
3
 
4
- class InvalidStepOutputException(NeatError):
4
+ class InvalidStepOutputException(NeatException):
5
5
  type_ = "invalid_step_output"
6
6
  code = 400
7
7
  description = "The step output is invalid."
@@ -12,7 +12,7 @@ class InvalidStepOutputException(NeatError):
12
12
  super().__init__(self.message)
13
13
 
14
14
 
15
- class ConfigurationNotSet(NeatError):
15
+ class ConfigurationNotSet(NeatException):
16
16
  type_ = "configuration_not_set"
17
17
  code = 400
18
18
  description = "The configuration is not set."
@@ -29,13 +29,13 @@ class ConfigurationNotSet(NeatError):
29
29
  return self.message
30
30
 
31
31
 
32
- class StepNotInitialized(NeatError):
32
+ class StepNotInitialized(NeatException):
33
33
  def __init__(self, step_name: str):
34
34
  self.message = f"Step {step_name} has not been initialized."
35
35
  super().__init__(self.message)
36
36
 
37
37
 
38
- class StepFlowContextNotInitialized(NeatError):
38
+ class StepFlowContextNotInitialized(NeatException):
39
39
  def __init__(self, step_name: str):
40
40
  self.message = f"Step {step_name} requires flow context which is missing."
41
41
  super().__init__(self.message)
@@ -13,7 +13,7 @@ from prometheus_client import Gauge
13
13
 
14
14
  from cognite.neat.app.monitoring.metrics import NeatMetricsCollector
15
15
  from cognite.neat.config import Config
16
- from cognite.neat.utils.utils import retry_decorator
16
+ from cognite.neat.utils.auxiliary import retry_decorator
17
17
  from cognite.neat.workflows import cdf_store, utils
18
18
  from cognite.neat.workflows._exceptions import ConfigurationNotSet, InvalidStepOutputException
19
19
  from cognite.neat.workflows.cdf_store import CdfStore
@@ -9,7 +9,7 @@ from cognite.neat.rules.issues import IssueList
9
9
  from cognite.neat.rules.issues.dms import MissingContainerError, MissingSpaceError, MissingViewError
10
10
  from cognite.neat.rules.issues.formatters import FORMATTER_BY_NAME
11
11
  from cognite.neat.rules.models import DMSRules, SchemaCompleteness
12
- from cognite.neat.utils import cdf_loaders
12
+ from cognite.neat.utils.cdf.loaders import ViewLoader
13
13
  from cognite.neat.workflows._exceptions import StepNotInitialized
14
14
  from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
15
15
  from cognite.neat.workflows.steps.data_contracts import MultiRuleData
@@ -69,7 +69,7 @@ class ValidateRulesAgainstCDF(Step):
69
69
  retrieved_containers = cdf_client.data_modeling.containers.retrieve(missing_containers).as_write()
70
70
  # Converting read format of views to write format requires to account for parents (implements)
71
71
  # Thus we use the loader to convert the views to write format.
72
- view_loader = cdf_loaders.ViewLoader(cdf_client)
72
+ view_loader = ViewLoader(cdf_client)
73
73
  retrieved_views = [
74
74
  view_loader.as_write(view) for view in cdf_client.data_modeling.views.retrieve(missing_views)
75
75
  ]
@@ -14,7 +14,7 @@ from cognite.neat.legacy.graph.extractors._mock_graph_generator import (
14
14
  generate_triples as generate_mock_triples,
15
15
  )
16
16
  from cognite.neat.legacy.rules.exporters._rules2triples import get_instances_as_triples
17
- from cognite.neat.utils.utils import create_sha256_hash
17
+ from cognite.neat.utils.auxiliary import create_sha256_hash
18
18
  from cognite.neat.workflows._exceptions import StepNotInitialized
19
19
  from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
20
20
  from cognite.neat.workflows.steps.data_contracts import (
@@ -26,7 +26,7 @@ from cognite.neat.legacy.graph.loaders.core.rdf_to_relationships import (
26
26
  from cognite.neat.legacy.graph.loaders.rdf_to_dms import upload_edges, upload_nodes
27
27
  from cognite.neat.legacy.graph.loaders.validator import validate_asset_hierarchy
28
28
  from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType
29
- from cognite.neat.utils.utils import generate_exception_report
29
+ from cognite.neat.utils.auxiliary import generate_exception_report
30
30
  from cognite.neat.workflows._exceptions import StepFlowContextNotInitialized, StepNotInitialized
31
31
  from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
32
32
  from cognite.neat.workflows.steps.data_contracts import (
@@ -12,7 +12,7 @@ from cognite.neat.legacy.rules import exporters
12
12
  from cognite.neat.legacy.rules.exporters._rules2dms import DMSSchemaComponents
13
13
  from cognite.neat.legacy.rules.exporters._rules2graphql import GraphQLSchema
14
14
  from cognite.neat.legacy.rules.exporters._rules2ontology import Ontology
15
- from cognite.neat.utils.utils import generate_exception_report
15
+ from cognite.neat.utils.auxiliary import generate_exception_report
16
16
  from cognite.neat.workflows._exceptions import StepNotInitialized
17
17
  from cognite.neat.workflows.model import FlowMessage, StepExecutionStatus
18
18
  from cognite.neat.workflows.steps.data_contracts import CogniteClient, DMSSchemaComponentsData, RulesData
@@ -13,7 +13,7 @@ from cognite.neat.legacy.rules import exporters, importers
13
13
  from cognite.neat.legacy.rules.models.rdfpath import TransformationRuleType
14
14
  from cognite.neat.legacy.rules.models.rules import Class, Classes, Metadata, Properties, Property, Rules
15
15
  from cognite.neat.legacy.rules.models.value_types import ValueType
16
- from cognite.neat.utils.utils import generate_exception_report
16
+ from cognite.neat.utils.auxiliary import generate_exception_report
17
17
  from cognite.neat.workflows import utils
18
18
  from cognite.neat.workflows._exceptions import StepNotInitialized
19
19
  from cognite.neat.workflows.cdf_store import CdfStore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.87.0
3
+ Version: 0.87.3
4
4
  Summary: Knowledge graph transformation
5
5
  Home-page: https://cognite-neat.readthedocs-hosted.com/
6
6
  License: Apache-2.0
@@ -21,7 +21,7 @@ Provides-Extra: oxi
21
21
  Provides-Extra: service
22
22
  Requires-Dist: PyYAML
23
23
  Requires-Dist: backports.strenum (>=1.2,<2.0) ; python_version < "3.11"
24
- Requires-Dist: cognite-sdk (>=7.52.0,<8.0.0)
24
+ Requires-Dist: cognite-sdk (>=7.54.6,<8.0.0)
25
25
  Requires-Dist: deepdiff
26
26
  Requires-Dist: exceptiongroup (>=1.1.3,<2.0.0) ; python_version < "3.11"
27
27
  Requires-Dist: fastapi (>=0,<1) ; extra == "service" or extra == "all"