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

@@ -51,7 +51,14 @@ class CDFLoader(BaseLoader[T_Output]):
51
51
  def load_into_cdf(
52
52
  self, client: CogniteClient, dry_run: bool = False, check_client: bool = True
53
53
  ) -> UploadResultList:
54
- return UploadResultList(self.load_into_cdf_iterable(client, dry_run, check_client))
54
+ upload_result_by_name: dict[str, UploadResult] = {}
55
+ for upload_result in self.load_into_cdf_iterable(client, dry_run, check_client):
56
+ if last_result := upload_result_by_name.get(upload_result.name):
57
+ upload_result_by_name[upload_result.name] = last_result.merge(upload_result)
58
+ else:
59
+ upload_result_by_name[upload_result.name] = upload_result
60
+
61
+ return UploadResultList(upload_result_by_name.values())
55
62
 
56
63
  def load_into_cdf_iterable(
57
64
  self, client: CogniteClient, dry_run: bool = False, check_client: bool = True
@@ -1,3 +1,4 @@
1
+ import itertools
1
2
  import json
2
3
  from collections.abc import Iterable, Sequence
3
4
  from pathlib import Path
@@ -295,9 +296,24 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
295
296
  dry_run: bool,
296
297
  read_issues: IssueList,
297
298
  ) -> Iterable[UploadResult]:
299
+ nodes: list[dm.NodeApply] = []
300
+ edges: list[dm.EdgeApply] = []
301
+ source_by_node_id: dict[dm.NodeId, str] = {}
302
+ source_by_edge_id: dict[dm.EdgeId, str] = {}
303
+ for item in items:
304
+ if isinstance(item, dm.NodeApply):
305
+ nodes.append(item)
306
+ if item.sources:
307
+ source_by_node_id[item.as_id()] = item.sources[0].source.external_id
308
+ else:
309
+ source_by_node_id[item.as_id()] = "node"
310
+ elif isinstance(item, dm.EdgeApply):
311
+ edges.append(item)
312
+ if item.sources:
313
+ source_by_edge_id[item.as_id()] = item.sources[0].source.external_id
314
+ else:
315
+ source_by_edge_id[item.as_id()] = "edge"
298
316
  try:
299
- nodes = [item for item in items if isinstance(item, dm.NodeApply)]
300
- edges = [item for item in items if isinstance(item, dm.EdgeApply)]
301
317
  upserted = client.data_modeling.instances.apply(
302
318
  nodes,
303
319
  edges,
@@ -312,19 +328,23 @@ class DMSLoader(CDFLoader[dm.InstanceApply]):
312
328
  result.created.update(item.as_id() for item in e.successful)
313
329
  yield result
314
330
  else:
315
- for instance_type, instances in {
316
- "Nodes": upserted.nodes,
317
- "Edges": upserted.edges,
318
- }.items():
319
- result = UploadResult[InstanceId](name=instance_type, issues=read_issues)
320
- for instance in instances: # type: ignore[attr-defined]
321
- if instance.was_modified and instance.created_time == instance.last_updated_time:
322
- result.created.add(instance.as_id())
323
- elif instance.was_modified:
324
- result.changed.add(instance.as_id())
325
- else:
326
- result.unchanged.add(instance.as_id())
327
- yield result
331
+ for instances, ids_by_source in [
332
+ (upserted.nodes, source_by_node_id),
333
+ (upserted.edges, source_by_edge_id),
334
+ ]:
335
+ for name, subinstances in itertools.groupby(
336
+ sorted(instances, key=lambda i: ids_by_source[i.as_id()]), # type: ignore[call-overload, index, attr-defined]
337
+ key=lambda i: ids_by_source[i.as_id()], # type: ignore[index]
338
+ ):
339
+ result = UploadResult(name=name, issues=read_issues)
340
+ for instance in subinstances: # type: ignore[attr-defined]
341
+ if instance.was_modified and instance.created_time == instance.last_updated_time:
342
+ result.created.add(instance.as_id())
343
+ elif instance.was_modified:
344
+ result.changed.add(instance.as_id())
345
+ else:
346
+ result.unchanged.add(instance.as_id())
347
+ yield result
328
348
 
329
349
 
330
350
  def _get_field_value_types(cls, info):
@@ -42,6 +42,27 @@ class Queries:
42
42
  for res in list(self.graph.query(query_statement))
43
43
  ]
44
44
 
45
+ @property
46
+ def types(self) -> dict[URIRef, str]:
47
+ """Types and their short form in the graph"""
48
+ query = """SELECT DISTINCT ?type
49
+ WHERE {?s a ?type .}"""
50
+ return {type_: remove_namespace_from_uri(cast(URIRef, type_)) for (type_,) in list(self.graph.query(query))} # type: ignore[misc, index, arg-type]
51
+
52
+ def type_uri(self, type_: str) -> list[URIRef]:
53
+ """Get the URIRef of a type"""
54
+ return [k for k, v in self.types.items() if v == type_]
55
+
56
+ @property
57
+ def properties(self) -> dict[URIRef, str]:
58
+ query = """SELECT DISTINCT ?property
59
+ WHERE {?s ?property ?o . FILTER(?property != rdf:type)}"""
60
+ return {type_: remove_namespace_from_uri(cast(URIRef, type_)) for (type_,) in list(self.graph.query(query))} # type: ignore[misc, index, arg-type]
61
+
62
+ def property_uri(self, property_: str) -> list[URIRef]:
63
+ """Get the URIRef of a type"""
64
+ return [k for k, v in self.properties.items() if v == property_]
65
+
45
66
  def list_instances_ids_of_class(self, class_uri: URIRef, limit: int = -1) -> list[URIRef]:
46
67
  """Get instances ids for a given class
47
68
 
@@ -101,7 +122,7 @@ class Queries:
101
122
  # We cannot include the RDF.type in case there is a neat:type property
102
123
  return [remove_namespace_from_uri(list(triple)) for triple in result if triple[1] != RDF.type] # type: ignore[misc, index, arg-type]
103
124
 
104
- def types_with_property(self, property_uri: URIRef) -> list[URIRef]:
125
+ def type_with_property(self, type_: URIRef, property_uri: URIRef) -> bool:
105
126
  """Check if a property exists in the graph store
106
127
 
107
128
  Args:
@@ -110,8 +131,8 @@ class Queries:
110
131
  Returns:
111
132
  True if property exists, False otherwise
112
133
  """
113
- query = f"SELECT DISTINCT ?t WHERE {{ ?s <{property_uri}> ?o ; a ?t}} Limit 1"
114
- return cast(list[URIRef], [t[0] for t in self.graph.query(query)]) # type: ignore[index]
134
+ query = f"SELECT ?o WHERE {{ ?s a <{type_}> ; <{property_uri}> ?o .}} Limit 1"
135
+ return bool(list(self.graph.query(query)))
115
136
 
116
137
  def has_namespace(self, namespace: Namespace) -> bool:
117
138
  """Check if a namespace exists in the graph store
@@ -250,7 +271,7 @@ class Queries:
250
271
  result = self.graph.query(query)
251
272
 
252
273
  # We cannot include the RDF.type in case there is a neat:type property
253
- return [remove_namespace_from_uri(cast(ResultRow, triple)) for triple in result if triple[1] != RDF.type] # type: ignore[misc, index]
274
+ return [remove_namespace_from_uri(cast(ResultRow, triple)) for triple in result if triple[1] != RDF.type] # type: ignore[misc, index, arg-type]
254
275
  else:
255
276
  warnings.warn(
256
277
  "No rules found for the graph store, returning empty list.",
@@ -3,12 +3,14 @@ as some helper classes to handle them like NeatIssueList"""
3
3
 
4
4
  from ._base import (
5
5
  DefaultWarning,
6
+ FutureResult,
6
7
  IssueList,
7
8
  MultiValueError,
8
9
  NeatError,
9
10
  NeatIssue,
10
11
  NeatIssueList,
11
12
  NeatWarning,
13
+ catch_issues,
12
14
  catch_warnings,
13
15
  )
14
16
 
@@ -21,4 +23,6 @@ __all__ = [
21
23
  "IssueList",
22
24
  "MultiValueError",
23
25
  "catch_warnings",
26
+ "catch_issues",
27
+ "FutureResult",
24
28
  ]
@@ -18,6 +18,7 @@ from cognite.client.data_classes.data_modeling import (
18
18
  PropertyId,
19
19
  ViewId,
20
20
  )
21
+ from pydantic import ValidationError
21
22
  from pydantic_core import ErrorDetails
22
23
 
23
24
  from cognite.neat._utils.spreadsheet import SpreadsheetRead
@@ -398,6 +399,10 @@ class NeatIssueList(list, Sequence[T_NeatIssue], ABC):
398
399
  """Return all the warnings in this list."""
399
400
  return type(self)([issue for issue in self if isinstance(issue, NeatWarning)]) # type: ignore[misc]
400
401
 
402
+ def has_error_type(self, error_type: type[NeatError]) -> bool:
403
+ """Return True if this list contains any errors of the given type."""
404
+ return any(isinstance(issue, error_type) for issue in self)
405
+
401
406
  def as_errors(self, operation: str = "Operation failed") -> ExceptionGroup:
402
407
  """Return an ExceptionGroup with all the errors in this list."""
403
408
  return ExceptionGroup(
@@ -473,3 +478,46 @@ def catch_warnings(
473
478
  finally:
474
479
  if warning_logger and issues is not None:
475
480
  issues.extend([warning_cls.from_warning(warning) for warning in warning_logger]) # type: ignore[misc]
481
+
482
+
483
+ class FutureResult:
484
+ def __init__(self) -> None:
485
+ self._result: Literal["success", "failure", "pending"] = "pending"
486
+
487
+ @property
488
+ def result(self) -> Literal["success", "failure", "pending"]:
489
+ return self._result
490
+
491
+
492
+ @contextmanager
493
+ def catch_issues(
494
+ issues: IssueList,
495
+ error_cls: type[NeatError] = NeatError,
496
+ warning_cls: type[NeatWarning] = NeatWarning,
497
+ error_args: dict[str, Any] | None = None,
498
+ ) -> Iterator[FutureResult]:
499
+ """This is an internal help function to handle issues and warnings.
500
+
501
+ Args:
502
+ issues: The issues list to append to.
503
+ error_cls: The class used to convert errors to issues.
504
+ warning_cls: The class used to convert warnings to issues.
505
+
506
+ Returns:
507
+ FutureResult: A future result object that can be used to check the result of the context manager.
508
+ """
509
+ with catch_warnings(issues, warning_cls):
510
+ future_result = FutureResult()
511
+ try:
512
+ yield future_result
513
+ except ValidationError as e:
514
+ issues.extend(error_cls.from_pydantic_errors(e.errors(), **(error_args or {})))
515
+ future_result._result = "failure"
516
+ except MultiValueError as e:
517
+ issues.extend(e.errors)
518
+ future_result._result = "failure"
519
+ except NeatError as e:
520
+ issues.append(e)
521
+ future_result._result = "failure"
522
+ else:
523
+ future_result._result = "success"
@@ -19,10 +19,12 @@ class NeatTypeError(NeatError, TypeError):
19
19
 
20
20
  @dataclass(unsafe_hash=True)
21
21
  class RegexViolationError(NeatError, ValueError):
22
- """Value, {value} failed regex, {regex}, validation. Make sure that the name follows the regex pattern."""
22
+ """Value, {value} in {location} failed regex, {regex}, validation.
23
+ Make sure that the name follows the regex pattern."""
23
24
 
24
25
  value: str
25
26
  regex: str
27
+ location: str
26
28
 
27
29
 
28
30
  @dataclass(unsafe_hash=True)
@@ -76,13 +76,17 @@ NamespaceType = Annotated[
76
76
  PrefixType = Annotated[
77
77
  str,
78
78
  StringConstraints(pattern=PREFIX_COMPLIANCE_REGEX),
79
- _custom_error(lambda _, value: RegexViolationError(value, PREFIX_COMPLIANCE_REGEX)),
79
+ _custom_error(
80
+ lambda _, value: RegexViolationError(value, PREFIX_COMPLIANCE_REGEX, "prefix entry in metadata"),
81
+ ),
80
82
  ]
81
83
 
82
84
  DataModelExternalIdType = Annotated[
83
85
  str,
84
86
  StringConstraints(pattern=DATA_MODEL_COMPLIANCE_REGEX),
85
- _custom_error(lambda _, value: RegexViolationError(value, DATA_MODEL_COMPLIANCE_REGEX)),
87
+ _custom_error(
88
+ lambda _, value: RegexViolationError(value, DATA_MODEL_COMPLIANCE_REGEX, "external ID entry in metadata")
89
+ ),
86
90
  ]
87
91
 
88
92
 
@@ -90,15 +94,15 @@ VersionType = Annotated[
90
94
  str,
91
95
  BeforeValidator(str),
92
96
  StringConstraints(pattern=VERSION_COMPLIANCE_REGEX),
93
- _custom_error(lambda _, value: RegexViolationError(value, VERSION_COMPLIANCE_REGEX)),
97
+ _custom_error(lambda _, value: RegexViolationError(value, VERSION_COMPLIANCE_REGEX, "version entry in metadata")),
94
98
  ]
95
99
 
96
100
 
97
- def _external_id_validation_factory(entity_type: EntityTypes):
101
+ def _external_id_validation_factory(entity_type: EntityTypes, location: str):
98
102
  def _external_id_validation(value: str) -> str:
99
103
  compiled_regex = PATTERNS.entity_pattern(entity_type)
100
104
  if not compiled_regex.match(value):
101
- raise RegexViolationError(value, compiled_regex.pattern)
105
+ raise RegexViolationError(value, compiled_regex.pattern, location)
102
106
  if PATTERNS.more_than_one_alphanumeric.search(value):
103
107
  warnings.warn(
104
108
  RegexViolationWarning(
@@ -116,36 +120,40 @@ def _external_id_validation_factory(entity_type: EntityTypes):
116
120
 
117
121
  SpaceType = Annotated[
118
122
  str,
119
- AfterValidator(_external_id_validation_factory(EntityTypes.space)),
123
+ AfterValidator(_external_id_validation_factory(EntityTypes.space, "space entry in metadata")),
120
124
  ]
121
125
 
122
126
  InformationPropertyType = Annotated[
123
127
  str,
124
- AfterValidator(_external_id_validation_factory(EntityTypes.information_property)),
128
+ AfterValidator(_external_id_validation_factory(EntityTypes.information_property, "Property column in properties")),
125
129
  ]
126
130
  DmsPropertyType = Annotated[
127
131
  str,
128
- AfterValidator(_external_id_validation_factory(EntityTypes.dms_property)),
132
+ AfterValidator(_external_id_validation_factory(EntityTypes.dms_property, "Property column in properties")),
129
133
  ]
130
134
 
131
135
 
132
- def _entity_validation(value: Entities) -> Entities:
136
+ def _entity_validation(value: Entities, location: str) -> Entities:
133
137
  suffix_regex = PATTERNS.entity_pattern(value.type_)
134
138
  if not suffix_regex.match(value.suffix):
135
- raise RegexViolationError(str(value), suffix_regex.pattern)
139
+ raise RegexViolationError(str(value), suffix_regex.pattern, location)
136
140
  return value
137
141
 
138
142
 
139
- ClassEntityType = Annotated[ClassEntity, AfterValidator(_entity_validation)]
140
- ViewEntityType = Annotated[ViewEntity, AfterValidator(_entity_validation)]
141
- ContainerEntityType = Annotated[ContainerEntity, AfterValidator(_entity_validation)]
143
+ ClassEntityType = Annotated[ClassEntity, AfterValidator(lambda v: _entity_validation(v, "the Class column"))]
144
+ ViewEntityType = Annotated[ViewEntity, AfterValidator(lambda v: _entity_validation(v, "the View column"))]
145
+ ContainerEntityType = Annotated[
146
+ ContainerEntity, AfterValidator(lambda v: _entity_validation(v, "the Container column"))
147
+ ]
142
148
 
143
149
 
144
- def _multi_value_type_validation(value: MultiValueTypeInfo) -> MultiValueTypeInfo:
150
+ def _multi_value_type_validation(value: MultiValueTypeInfo, location: str) -> MultiValueTypeInfo:
145
151
  for type_ in value.types:
146
152
  if isinstance(type_, ClassEntity):
147
- _entity_validation(type_)
153
+ _entity_validation(type_, location)
148
154
  return value
149
155
 
150
156
 
151
- MultiValueTypeType = Annotated[MultiValueTypeInfo, AfterValidator(_multi_value_type_validation)]
157
+ MultiValueTypeType = Annotated[
158
+ MultiValueTypeInfo, AfterValidator(lambda v: _multi_value_type_validation(v, "the Value Type column"))
159
+ ]
@@ -157,21 +157,6 @@ class DMSPostValidation:
157
157
  )
158
158
  )
159
159
 
160
- # This sets the container definition for all the properties where it is not defined.
161
- # This allows the user to define the container only once.
162
- value_type = next(iter(value_types))
163
- list_definition = next(iter(list_definitions)) if list_definitions else None
164
- nullable_definition = next(iter(nullable_definitions)) if nullable_definitions else None
165
- default_definition = next(iter(default_definitions)) if default_definitions else None
166
- index_definition = next(iter(index_definitions)).split(",") if index_definitions else None
167
- constraint_definition = next(iter(constraint_definitions)).split(",") if constraint_definitions else None
168
- for _, prop in properties:
169
- prop.value_type = value_type
170
- prop.is_list = prop.is_list or list_definition
171
- prop.nullable = prop.nullable or nullable_definition
172
- prop.default = prop.default or default_definition
173
- prop.index = prop.index or index_definition
174
- prop.constraint = prop.constraint or constraint_definition
175
160
  self.issue_list.extend(errors)
176
161
 
177
162
  def _referenced_views_and_containers_are_existing_and_proper_size(self) -> None:
@@ -1,11 +1,7 @@
1
1
  from abc import ABC
2
- from collections.abc import Iterator
3
- from contextlib import contextmanager
4
2
  from typing import Any, Literal
5
3
 
6
- from pydantic import ValidationError
7
-
8
- from cognite.neat._issues import IssueList, MultiValueError, NeatError, NeatWarning, catch_warnings
4
+ from cognite.neat._issues import IssueList, NeatError, NeatWarning, catch_issues
9
5
  from cognite.neat._issues.errors import NeatTypeError
10
6
  from cognite.neat._rules._shared import (
11
7
  InputRules,
@@ -45,7 +41,7 @@ class VerificationTransformer(RulesTransformer[T_InputRules, T_VerifiedRules], A
45
41
  if isinstance(rules, ReadRules):
46
42
  error_args = rules.read_context
47
43
  verified_rules: T_VerifiedRules | None = None
48
- with _catch_issues(issues, NeatError, NeatWarning, error_args) as future:
44
+ with catch_issues(issues, NeatError, NeatWarning, error_args) as future:
49
45
  rules_cls = self._get_rules_cls(in_)
50
46
  verified_rules = rules_cls.model_validate(in_.dump()) # type: ignore[assignment]
51
47
 
@@ -92,46 +88,3 @@ class VerifyAnyRules(VerificationTransformer[InputRules, VerifiedRules]):
92
88
  return DomainRules
93
89
  else:
94
90
  raise NeatTypeError(f"Unsupported rules type: {type(in_)}")
95
-
96
-
97
- class _FutureResult:
98
- def __init__(self) -> None:
99
- self._result: Literal["success", "failure", "pending"] = "pending"
100
-
101
- @property
102
- def result(self) -> Literal["success", "failure", "pending"]:
103
- return self._result
104
-
105
-
106
- @contextmanager
107
- def _catch_issues(
108
- issues: IssueList,
109
- error_cls: type[NeatError] = NeatError,
110
- warning_cls: type[NeatWarning] = NeatWarning,
111
- error_args: dict[str, Any] | None = None,
112
- ) -> Iterator[_FutureResult]:
113
- """This is an internal help function to handle issues and warnings.
114
-
115
- Args:
116
- issues: The issues list to append to.
117
- error_cls: The class used to convert errors to issues.
118
- warning_cls: The class used to convert warnings to issues.
119
-
120
- Returns:
121
- FutureResult: A future result object that can be used to check the result of the context manager.
122
- """
123
- with catch_warnings(issues, warning_cls):
124
- future_result = _FutureResult()
125
- try:
126
- yield future_result
127
- except ValidationError as e:
128
- issues.extend(error_cls.from_pydantic_errors(e.errors(), **(error_args or {})))
129
- future_result._result = "failure"
130
- except MultiValueError as e:
131
- issues.extend(e.errors)
132
- future_result._result = "failure"
133
- except NeatError as e:
134
- issues.append(e)
135
- future_result._result = "failure"
136
- else:
137
- future_result._result = "success"
@@ -5,9 +5,10 @@ from cognite.client import CogniteClient
5
5
  from cognite.client import data_modeling as dm
6
6
 
7
7
  from cognite.neat import _version
8
- from cognite.neat._issues import IssueList
8
+ from cognite.neat._issues import IssueList, catch_issues
9
+ from cognite.neat._issues.errors import RegexViolationError
9
10
  from cognite.neat._rules import importers
10
- from cognite.neat._rules._shared import ReadRules
11
+ from cognite.neat._rules._shared import ReadRules, VerifiedRules
11
12
  from cognite.neat._rules.importers._rdf._base import DEFAULT_NON_EXISTING_NODE_TYPE
12
13
  from cognite.neat._rules.models import DMSRules
13
14
  from cognite.neat._rules.models.data_types import AnyURI
@@ -15,10 +16,12 @@ from cognite.neat._rules.models.entities._single_value import UnknownEntity
15
16
  from cognite.neat._rules.models.information._rules import InformationRules
16
17
  from cognite.neat._rules.models.information._rules_input import InformationInputRules
17
18
  from cognite.neat._rules.transformers import ConvertToRules, VerifyAnyRules
19
+ from cognite.neat._rules.transformers._converters import ConversionTransformer
18
20
  from cognite.neat._store._provenance import (
19
21
  INSTANCES_ENTITY,
20
22
  Change,
21
23
  )
24
+ from cognite.neat._utils.auth import _CLIENT_NAME
22
25
 
23
26
  from ._inspect import InspectAPI
24
27
  from ._prepare import PrepareAPI
@@ -49,6 +52,8 @@ class NeatSession:
49
52
  self.show = ShowAPI(self._state)
50
53
  self.set = SetAPI(self._state, verbose)
51
54
  self.inspect = InspectAPI(self._state)
55
+ if self._client is not None and self._client._config is not None:
56
+ self._client._config.client_name = _CLIENT_NAME
52
57
  if load_engine != "skip" and (engine_version := load_neat_engine(client, load_engine)):
53
58
  print(f"Neat Engine {engine_version} loaded.")
54
59
 
@@ -85,35 +90,52 @@ class NeatSession:
85
90
  print("You can inspect the issues with the .inspect.issues(...) method.")
86
91
  return output.issues
87
92
 
88
- def convert(self, target: Literal["dms", "information"]) -> None:
93
+ def convert(self, target: Literal["dms", "information"]) -> IssueList:
89
94
  start = datetime.now(timezone.utc)
90
- if target == "dms":
91
- source_id, info_rules = self._state.data_model.last_verified_information_rules
92
- converter = ConvertToRules(DMSRules)
93
- converted_rules = converter.transform(info_rules).rules
94
- elif target == "information":
95
- source_id, dms_rules = self._state.data_model.last_verified_dms_rules
96
- converter = ConvertToRules(InformationRules)
97
- converted_rules = converter.transform(dms_rules).rules
98
- else:
99
- raise NeatSessionError(f"Target {target} not supported.")
95
+ issues = IssueList()
96
+ converter: ConversionTransformer | None = None
97
+ converted_rules: VerifiedRules | None = None
98
+ with catch_issues(issues):
99
+ if target == "dms":
100
+ source_id, info_rules = self._state.data_model.last_verified_information_rules
101
+ converter = ConvertToRules(DMSRules)
102
+ converted_rules = converter.transform(info_rules).rules
103
+ elif target == "information":
104
+ source_id, dms_rules = self._state.data_model.last_verified_dms_rules
105
+ converter = ConvertToRules(InformationRules)
106
+ converted_rules = converter.transform(dms_rules).rules
107
+ else:
108
+ # Session errors are not caught by the catch_issues context manager
109
+ raise NeatSessionError(f"Target {target} not supported.")
110
+
100
111
  end = datetime.now(timezone.utc)
112
+ if issues:
113
+ self._state.data_model.issue_lists.append(issues)
101
114
 
102
- # Provenance
103
- change = Change.from_rules_activity(
104
- converted_rules,
105
- converter.agent,
106
- start,
107
- end,
108
- f"Converted data model {source_id} to {converted_rules.id_}",
109
- self._state.data_model.provenance.source_entity(source_id)
110
- or self._state.data_model.provenance.target_entity(source_id),
111
- )
115
+ if converted_rules is not None and converter is not None:
116
+ # Provenance
117
+ change = Change.from_rules_activity(
118
+ converted_rules,
119
+ converter.agent,
120
+ start,
121
+ end,
122
+ f"Converted data model {source_id} to {converted_rules.id_}",
123
+ self._state.data_model.provenance.source_entity(source_id)
124
+ or self._state.data_model.provenance.target_entity(source_id),
125
+ )
126
+
127
+ self._state.data_model.write(converted_rules, change)
112
128
 
113
- self._state.data_model.write(converted_rules, change)
129
+ if self._verbose and not issues.has_errors:
130
+ print(f"Rules converted to {target}")
131
+ else:
132
+ print("Conversion failed.")
133
+ if issues:
134
+ print("You can inspect the issues with the .inspect.issues(...) method.")
135
+ if issues.has_error_type(RegexViolationError):
136
+ print("You can use .prepare. to try to fix the issues")
114
137
 
115
- if self._verbose:
116
- print(f"Rules converted to {target}")
138
+ return issues
117
139
 
118
140
  def infer(
119
141
  self,
@@ -173,8 +173,18 @@ class InspectUploadOutcome:
173
173
  continue
174
174
  lines.append(f"#### {key}")
175
175
  if isinstance(value, list):
176
- for v in value:
177
- lines.append(f" * {v}")
176
+ total = len(value)
177
+ for i, v in enumerate(value):
178
+ if key in ["created", "updated", "changed"]:
179
+ if i < 50:
180
+ lines.append(f" * {v}")
181
+ elif i == 50 and total > 50:
182
+ lines.append(f" * ... {total-50} more")
183
+ elif i == 50 and total == 50:
184
+ lines.append(f" * {v}")
185
+ else:
186
+ lines.append(f" * {v}")
187
+
178
188
  else:
179
189
  lines.append(f" * {value}")
180
190
 
@@ -12,7 +12,7 @@ from cognite.neat._rules.transformers import ReduceCogniteModel, ToCompliantEnti
12
12
  from cognite.neat._store._provenance import Change
13
13
 
14
14
  from ._state import SessionState
15
- from .exceptions import intercept_session_exceptions
15
+ from .exceptions import NeatSessionError, intercept_session_exceptions
16
16
 
17
17
 
18
18
  @intercept_session_exceptions
@@ -32,18 +32,18 @@ class InstancePrepareAPI:
32
32
 
33
33
  def make_connection_on_exact_match(
34
34
  self,
35
- source: tuple[URIRef, URIRef],
36
- target: tuple[URIRef, URIRef],
37
- connection: URIRef | None = None,
35
+ source: tuple[str, str],
36
+ target: tuple[str, str],
37
+ connection: str | None = None,
38
38
  limit: int | None = 100,
39
39
  ) -> None:
40
40
  """Make connection on exact match.
41
41
 
42
42
  Args:
43
- source: The source of the connection. A tuple of (rdf type, property) where
43
+ source: The source of the connection. A tuple of (type, property) where
44
44
  where property is the property that should be matched on the source
45
45
  to make the connection with the target.
46
- target: The target of the connection. A tuple of (rdf type, property) where
46
+ target: The target of the connection. A tuple of (type, property) where
47
47
  where property is the property that should be matched on the target
48
48
  to make the connection with the source.
49
49
 
@@ -51,11 +51,19 @@ class InstancePrepareAPI:
51
51
  will be made by lowercasing the target type.
52
52
  limit: The maximum number of connections to make. If None, all connections
53
53
 
54
+ !!! note "Make Connection on Exact Match"
55
+ This method will make a connection between the source and target based on the exact match:
56
+ (SourceType)-[sourceProperty]->(sourceValue) == (TargetType)-[targetProperty]->(targetValue)
57
+
58
+ The connection will be made by creating a new property on the source type that will contain the
59
+ target value, as follows:
60
+ (SourceType)-[connection]->(TargetType)
61
+
54
62
 
55
63
  """
56
64
 
57
- subject_type, subject_predicate = source
58
- object_type, object_predicate = target
65
+ subject_type, subject_predicate = self._get_type_and_property_uris(*source)
66
+ object_type, object_predicate = self._get_type_and_property_uris(*target)
59
67
 
60
68
  transformer = MakeConnectionOnExactMatch(
61
69
  subject_type,
@@ -68,6 +76,24 @@ class InstancePrepareAPI:
68
76
 
69
77
  self._state.instances.store.transform(transformer)
70
78
 
79
+ def _get_type_and_property_uris(self, type_: str, property_: str) -> tuple[URIRef, URIRef]:
80
+ type_uri = self._state.instances.store.queries.type_uri(type_)
81
+ property_uri = self._state.instances.store.queries.property_uri(property_)
82
+
83
+ if not type_uri:
84
+ raise NeatSessionError(f"Type {type_} does not exist in the graph.")
85
+ elif len(type_uri) > 1:
86
+ raise NeatSessionError(f"{type_} has multiple ids found in the graph: {','.join(type_uri)}.")
87
+
88
+ if not property_uri:
89
+ raise NeatSessionError(f"Property {property_} does not exist in the graph.")
90
+ elif len(type_uri) > 1:
91
+ raise NeatSessionError(f"{property_} has multiple ids found in the graph: {','.join(property_uri)}.")
92
+
93
+ if not self._state.instances.store.queries.type_with_property(type_uri[0], property_uri[0]):
94
+ raise NeatSessionError(f"Property {property_} is not defined for type {type_}. Cannot make connection")
95
+ return type_uri[0], property_uri[0]
96
+
71
97
 
72
98
  @intercept_session_exceptions
73
99
  class DataModelPrepareAPI:
@@ -2,11 +2,12 @@ from datetime import datetime, timezone
2
2
 
3
3
  from cognite.client import data_modeling as dm
4
4
 
5
+ from cognite.neat._constants import COGNITE_MODELS
5
6
  from cognite.neat._rules.transformers import SetIDDMSModel
6
7
  from cognite.neat._store._provenance import Change
7
8
 
8
9
  from ._state import SessionState
9
- from .exceptions import intercept_session_exceptions
10
+ from .exceptions import NeatSessionError, intercept_session_exceptions
10
11
 
11
12
 
12
13
  @intercept_session_exceptions
@@ -20,6 +21,12 @@ class SetAPI:
20
21
  if res := self._state.data_model.last_verified_dms_rules:
21
22
  source_id, rules = res
22
23
 
24
+ if rules.metadata.as_data_model_id() in COGNITE_MODELS:
25
+ raise NeatSessionError(
26
+ "Cannot change the data model ID of a Cognite Data Model in NeatSession"
27
+ " due to temporarily issue with the reverse direct relation interpretation"
28
+ )
29
+
23
30
  start = datetime.now(timezone.utc)
24
31
  transformer = SetIDDMSModel(new_model_id)
25
32
  output = transformer.transform(rules)
@@ -27,15 +34,16 @@ class SetAPI:
27
34
 
28
35
  # Provenance
29
36
  change = Change.from_rules_activity(
30
- output,
37
+ output.rules,
31
38
  transformer.agent,
32
39
  start,
33
40
  end,
34
41
  "Changed data model id",
35
- self._state.data_model.provenance.source_entity(source_id),
42
+ self._state.data_model.provenance.source_entity(source_id)
43
+ or self._state.data_model.provenance.target_entity(source_id),
36
44
  )
37
45
 
38
- self._state.data_model.write(output, change)
46
+ self._state.data_model.write(output.rules, change)
39
47
  if self._verbose:
40
48
  print(f"Data model ID set to {new_model_id}")
41
49
  else:
@@ -2,12 +2,14 @@ from pathlib import Path
2
2
  from typing import Any, Literal, overload
3
3
 
4
4
  from cognite.client import CogniteClient
5
+ from cognite.client.data_classes.data_modeling import SpaceApply
5
6
 
6
7
  from cognite.neat._graph import loaders
7
8
  from cognite.neat._issues import IssueList, catch_warnings
8
9
  from cognite.neat._rules import exporters
9
- from cognite.neat._session._wizard import space_wizard
10
- from cognite.neat._utils.upload import UploadResultCore
10
+ from cognite.neat._rules._constants import PATTERNS
11
+ from cognite.neat._rules._shared import VerifiedRules
12
+ from cognite.neat._utils.upload import UploadResultCore, UploadResultList
11
13
 
12
14
  from ._state import SessionState
13
15
  from .exceptions import NeatSessionError, intercept_session_exceptions
@@ -23,9 +25,24 @@ class ToAPI:
23
25
  def excel(
24
26
  self,
25
27
  io: Any,
28
+ model: Literal["dms", "information", "logical", "physical"] | None,
26
29
  ) -> None:
30
+ """Export the verified data model to Excel.
31
+
32
+ Args:
33
+ io: The file path or file-like object to write the Excel file to.
34
+ model: The format of the data model to export. Defaults to None.
35
+ """
27
36
  exporter = exporters.ExcelExporter()
28
- exporter.export_to_file(self._state.data_model.last_verified_rule[1], Path(io))
37
+ rules: VerifiedRules
38
+ if model == "information" or model == "logical":
39
+ rules = self._state.data_model.last_verified_information_rules[1]
40
+ elif model == "dms" or model == "physical":
41
+ rules = self._state.data_model.last_verified_dms_rules[1]
42
+ else:
43
+ rules = self._state.data_model.last_verified_rule[1]
44
+
45
+ exporter.export_to_file(rules, Path(io))
29
46
  return None
30
47
 
31
48
  @overload
@@ -50,19 +67,29 @@ class CDFToAPI:
50
67
  self._state = state
51
68
  self._verbose = verbose
52
69
 
53
- def instances(self, space: str | None = None):
70
+ def instances(self, space: str | None = None) -> UploadResultList:
54
71
  if not self._client:
55
72
  raise NeatSessionError("No CDF client provided!")
56
73
 
74
+ space = space or f"{self._state.data_model.last_verified_dms_rules[1].metadata.space}_instances"
75
+
76
+ if space and space == self._state.data_model.last_verified_dms_rules[1].metadata.space:
77
+ raise NeatSessionError("Space for instances must be different from the data model space.")
78
+ elif not PATTERNS.space_compliance.match(str(space)):
79
+ raise NeatSessionError("Please provide a valid space name. {PATTERNS.space_compliance.pattern}")
80
+
81
+ if not self._client.data_modeling.spaces.retrieve(space):
82
+ self._client.data_modeling.spaces.apply(SpaceApply(space=space))
83
+
57
84
  loader = loaders.DMSLoader.from_rules(
58
85
  self._state.data_model.last_verified_dms_rules[1],
59
86
  self._state.instances.store,
60
- space_wizard(space=space),
87
+ instance_space=space,
61
88
  )
62
89
  result = loader.load_into_cdf(self._client)
63
90
  self._state.instances.outcome.append(result)
64
- print("You can inspect the details with the .inspect.instances.outcome(...) method.")
65
- return loader.load_into_cdf(self._client)
91
+ print("You can inspect the details with the .inspect.outcome.instances(...) method.")
92
+ return result
66
93
 
67
94
  def data_model(
68
95
  self,
@@ -41,7 +41,7 @@ def get_cognite_client(env_file_name: str) -> CogniteClient:
41
41
  with suppress(KeyError, FileNotFoundError, TypeError):
42
42
  variables = _from_dotenv(repo_root / env_file_name)
43
43
  client = variables.get_client()
44
- print("Found .env file in repository root. Loaded variables from .env file.")
44
+ print(f"Found {env_file_name} file in repository root. Loaded variables from {env_file_name} file.")
45
45
  return client
46
46
  variables = _prompt_user()
47
47
  if repo_root and _env_in_gitignore(repo_root, env_file_name):
@@ -50,13 +50,14 @@ def get_cognite_client(env_file_name: str) -> CogniteClient:
50
50
 
51
51
  env_file = repo_root / env_file_name
52
52
  answer = Prompt.ask(
53
- "Do you store the variables in an .env file in the repository root for easy reuse?", choices=["y", "n"]
53
+ f"Do you store the variables in an {env_file_name} file in the repository root for easy reuse?",
54
+ choices=["y", "n"],
54
55
  )
55
56
  if env_file.exists():
56
57
  answer = Prompt.ask(f"{env_file} already exists. Overwrite?", choices=["y", "n"])
57
58
  if answer == "y":
58
59
  env_file.write_text(variables.create_env_file())
59
- print("Created .env file in repository root.")
60
+ print(f"Created {env_file_name} file in repository root.")
60
61
 
61
62
  return variables.get_client()
62
63
 
@@ -4,6 +4,7 @@ from functools import total_ordering
4
4
  from typing import Any, Generic
5
5
 
6
6
  from cognite.neat._issues import IssueList
7
+ from cognite.neat._issues.errors import NeatValueError
7
8
  from cognite.neat._shared import T_ID, NeatList, NeatObject
8
9
 
9
10
 
@@ -110,3 +111,22 @@ class UploadResult(UploadResultCore, Generic[T_ID]):
110
111
  continue
111
112
  lines.append(f"{key}: {value}")
112
113
  return f"{self.name.title()}: {', '.join(lines)}"
114
+
115
+ def merge(self, other: "UploadResult[T_ID]") -> "UploadResult[T_ID]":
116
+ if self.name != other.name:
117
+ raise NeatValueError(f"Cannot merge UploadResults with different names: {self.name} and {other.name}")
118
+ return UploadResult(
119
+ name=self.name,
120
+ error_messages=self.error_messages + other.error_messages,
121
+ issues=IssueList(self.issues + other.issues),
122
+ created=self.created.union(other.created),
123
+ upserted=self.upserted.union(other.upserted),
124
+ deleted=self.deleted.union(other.deleted),
125
+ changed=self.changed.union(other.changed),
126
+ unchanged=self.unchanged.union(other.unchanged),
127
+ skipped=self.skipped.union(other.skipped),
128
+ failed_created=self.failed_created.union(other.failed_created),
129
+ failed_upserted=self.failed_upserted.union(other.failed_upserted),
130
+ failed_changed=self.failed_changed.union(other.failed_changed),
131
+ failed_deleted=self.failed_deleted.union(other.failed_deleted),
132
+ )
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.97.1"
1
+ __version__ = "0.97.3"
2
2
  __engine__ = "^1.0.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cognite-neat
3
- Version: 0.97.1
3
+ Version: 0.97.3
4
4
  Summary: Knowledge graph transformation
5
5
  Home-page: https://cognite-neat.readthedocs-hosted.com/
6
6
  License: Apache-2.0
@@ -101,11 +101,11 @@ cognite/neat/_graph/extractors/_iodd.py,sha256=nMSLmtgvxLrQJMA5NECF1OCp4Bcv0Vq0W
101
101
  cognite/neat/_graph/extractors/_mock_graph_generator.py,sha256=yEqQdbnRQjBXVQIEVGP_B_Gqu4qi_1koqpMjw8XRTA0,15409
102
102
  cognite/neat/_graph/extractors/_rdf_file.py,sha256=WHsVAtfRhMdtjTTN0et3pAFMm2PTCTyHOJgPSVqsqi4,1688
103
103
  cognite/neat/_graph/loaders/__init__.py,sha256=TbeJqifd16JLOglPVNOeb6pN_w060UYag50KquBM_r0,769
104
- cognite/neat/_graph/loaders/_base.py,sha256=ms_nntW7RKhO751yS2w9-zGIoAYDRecY9-RaE6Krrcw,3600
104
+ cognite/neat/_graph/loaders/_base.py,sha256=tjplRd-vbWhWyys0Ll3KgHR3F3ETlP_dXJ3e8F8w15M,3984
105
105
  cognite/neat/_graph/loaders/_rdf2asset.py,sha256=OMfTVbVEx6yAUlds7c2mVcgnBV0Cq5DiQ7joW3-yy0Y,17525
106
- cognite/neat/_graph/loaders/_rdf2dms.py,sha256=oIpm35JMlb_VR38QZqpQo3TULU_c0aGqD1CrYEaEHyY,14977
106
+ cognite/neat/_graph/loaders/_rdf2dms.py,sha256=kR5yjXS5VDHMUFff5WSTrZRqKSzX53SlEbcwHMVh8J8,15951
107
107
  cognite/neat/_graph/queries/__init__.py,sha256=BgDd-037kvtWwAoGAy8eORVNMiZ5-E9sIV0txIpeaN4,50
108
- cognite/neat/_graph/queries/_base.py,sha256=NuDG8H4wC7R4Py8kpgiWEbfAVZajV3L2ivaHDyYa8Iw,12714
108
+ cognite/neat/_graph/queries/_base.py,sha256=FhRCUQ-jpVdtC6qdBWqTlh6JP29BigQJoyACkrr5bBM,13700
109
109
  cognite/neat/_graph/queries/_construct.py,sha256=CW8uHtXXACUXDj1AcEjROXtvoiuyx0CTgZ0bURY5Neo,7213
110
110
  cognite/neat/_graph/queries/_shared.py,sha256=K3svLkvw-DWPZUwIwpJRjPKg5UIRKFCn5jBLpuJjiHc,5330
111
111
  cognite/neat/_graph/transformers/__init__.py,sha256=CdlG9Bk--bLyO5S8jJOkOriZQwJtukXj7oRPXfKKJSY,984
@@ -115,11 +115,11 @@ cognite/neat/_graph/transformers/_iodd.py,sha256=yH-BvVQUswM8RmV2VvOPQAgwudhBJdx
115
115
  cognite/neat/_graph/transformers/_prune_graph.py,sha256=fWZ4sMBf3XqoYC5nOU75Gl6XwscRe-wGYT-GiBFmcK8,5277
116
116
  cognite/neat/_graph/transformers/_rdfpath.py,sha256=jTqrfoEn8cnQjM4u-d2rDo-VRvT9FJUzbWhMwXbk-WI,4157
117
117
  cognite/neat/_graph/transformers/_value_type.py,sha256=JorH-AgDXVZUkG_GCcwn51Mw0M2WIOV834t0kF_Nwvo,2614
118
- cognite/neat/_issues/__init__.py,sha256=BZu2sZ6SiAbvyzH4yGz87pAiVFpextgQCTPUx_Zti9E,480
119
- cognite/neat/_issues/_base.py,sha256=YH9wDnqzZjjzhduihFTPh4GxG-DB-eOvOlG4uzZ5DOA,17485
118
+ cognite/neat/_issues/__init__.py,sha256=IEZBpvL88hdghX7JgndhxcZcxreZowuoQFIXuSeIKDs,556
119
+ cognite/neat/_issues/_base.py,sha256=ks4-3bbholOw0VaacazbaX4ViMh0EcS6KPND_srLCbE,19180
120
120
  cognite/neat/_issues/errors/__init__.py,sha256=sOfHXOh9BfBEKoHFsneB5pCq5s7wJNhzwIEUKUr7HI4,2160
121
121
  cognite/neat/_issues/errors/_external.py,sha256=AuV2PyJcGjYxEnuwmi3zfYWPCF-yr4w39Uy0lQpeoqo,1619
122
- cognite/neat/_issues/errors/_general.py,sha256=Afsp2OpP8lb5J9JrEDLlBWtU36Mx9AxtKaDxTFqRVKs,808
122
+ cognite/neat/_issues/errors/_general.py,sha256=zwEoaygHA2Nt9vCwiveDuzJsqZYIaX0BoUYkwJkQ4jU,844
123
123
  cognite/neat/_issues/errors/_properties.py,sha256=cr29pDs-Cc_kyRJjCk-9bS-HXV6naF27AOv3TSNbQdQ,2787
124
124
  cognite/neat/_issues/errors/_resources.py,sha256=SbiojpJ2J9Dk3NKRN0FoiN-vy14LXmEJCJM8xu_gTzQ,3964
125
125
  cognite/neat/_issues/errors/_workflow.py,sha256=m_Hlsvl5A1Oy7P3IROnz-4_do8_orZ1Pr1IHqsMyEys,971
@@ -177,7 +177,7 @@ cognite/neat/_rules/models/__init__.py,sha256=wB6Vo3hs9WRtQjHuXR_2HN4jot8ZJE0OiE
177
177
  cognite/neat/_rules/models/_base_input.py,sha256=qJrxobZLqpc28adEUJTKdJ8hDUZ67SVDFkUJnGjcPOc,6647
178
178
  cognite/neat/_rules/models/_base_rules.py,sha256=Yf4BE5XRSxFRbElrJcKzFwrGT_pOkT_mc_EKpr2-F1Q,13489
179
179
  cognite/neat/_rules/models/_rdfpath.py,sha256=hqUMZCMeI8ESdJltu7FifuUhna5JNN_Heup2aYkV56Y,11882
180
- cognite/neat/_rules/models/_types.py,sha256=jNzz7oO9uKIMqkjJ6kLEvRb83NERW0ZXURLvbPjgViA,4719
180
+ cognite/neat/_rules/models/_types.py,sha256=6kljU9hREz1jllrBlIh7_jr-BLk6aUsINc-iDqPLKWI,5158
181
181
  cognite/neat/_rules/models/asset/__init__.py,sha256=Z2tQEABW-q66bmHNcxMuIxPmYQBcGdiSZt7fHGe01dQ,363
182
182
  cognite/neat/_rules/models/asset/_rules.py,sha256=KAGwnqvNcGGde6xaUElfkc3BxZZ6obzaqwXhZQ_CQ5o,4188
183
183
  cognite/neat/_rules/models/asset/_rules_input.py,sha256=rEiW460fwF-yXZLKF5HR-M0qOLLPmpaac0bJUm2t5TE,3597
@@ -188,7 +188,7 @@ cognite/neat/_rules/models/dms/_exporter.py,sha256=gCATniEtjhCOpofnY4jjTRnkm1s84
188
188
  cognite/neat/_rules/models/dms/_rules.py,sha256=3_cn4wIzQDwfSmIwCFRKDJLhvuXxUrz4ompDLtT3Fx0,21445
189
189
  cognite/neat/_rules/models/dms/_rules_input.py,sha256=8tOJ9XNX9qBW-PEKAyCJ1mvG1BgrEbJBk7VYTTKG_D0,11813
190
190
  cognite/neat/_rules/models/dms/_schema.py,sha256=HSmSDvOm5S0x4Vb9tH9Jvd5i9tXiiM08E_Sdu6q_iA8,50783
191
- cognite/neat/_rules/models/dms/_validation.py,sha256=LxufRBRUCqvO1yztbk01O5dcX4N4Elf75JHqxt6p8WM,18398
191
+ cognite/neat/_rules/models/dms/_validation.py,sha256=63cS1troy3CBpfeIst0AtpKGYS1-IiZEbh_6Y8Pv3bQ,17256
192
192
  cognite/neat/_rules/models/domain.py,sha256=zzmurr1caPZaz6hoUwveCHlSM-bDf9Rt-S1m5HWOfRk,4295
193
193
  cognite/neat/_rules/models/entities/__init__.py,sha256=QD-h79HhjqCsgscNU5kuf1ieRCE94dOfpujLuzYbtHk,1469
194
194
  cognite/neat/_rules/models/entities/_constants.py,sha256=ToiLaaF-hGLPfn3AsKIIrfB4ZdTk4cY1RjM9gA1Qjkg,288
@@ -209,16 +209,16 @@ cognite/neat/_rules/transformers/_base.py,sha256=jmgcSFWOPvrbfme0kUwXi1_3Bvxwif1
209
209
  cognite/neat/_rules/transformers/_converters.py,sha256=DmUP7HKdjNRJ98Bis0s-leYK92a5eSbJMZdLH4HMZww,44141
210
210
  cognite/neat/_rules/transformers/_mapping.py,sha256=RWHKPMaP3JdeCNvoDGu9ZGHxfyeIgapYEBRoargnd2Q,6797
211
211
  cognite/neat/_rules/transformers/_pipelines.py,sha256=-E5Hgitnr6ee8R9_3sqtjmWIPJ0w1xaLErG6Fo6ExVU,2603
212
- cognite/neat/_rules/transformers/_verification.py,sha256=Jdy9dpjVxu5Hz__4phXU-44fWbKvL0vLWaq4gm5dVI8,4466
212
+ cognite/neat/_rules/transformers/_verification.py,sha256=8p3cpVTfBkEeRLZSFJh_tby8ScRJodjSvrt2-_JBwQ4,2885
213
213
  cognite/neat/_session/__init__.py,sha256=fxQ5URVlUnmEGYyB8Baw7IDq-uYacqkigbc4b-Pr9Fw,58
214
- cognite/neat/_session/_base.py,sha256=LLCEHWrU9l8vUX2HptQtBPiFnDlZNWzMhrOJKshQCUc,7348
215
- cognite/neat/_session/_inspect.py,sha256=GoYrsyLkd7JSCZ21VAl5zIehhQ3-bXiV7zzyWSl3xaw,6488
216
- cognite/neat/_session/_prepare.py,sha256=AIJj28wCbZizbiCeE5wfB4RYZbyoc3bu6ESYgfb9AaY,9673
214
+ cognite/neat/_session/_base.py,sha256=NHyESFlD3N1aUW6oJ3emuUSklopQUXeXblUk9-eje8U,8540
215
+ cognite/neat/_session/_inspect.py,sha256=42mWP2GbQPxsM8-Xt-jY3_LHH0VNikkX8I2t1D_tj5I,7058
216
+ cognite/neat/_session/_prepare.py,sha256=RnKetAX305PwdHQwOag1NHxmU1UTyAQMTAzQAw7MxLw,11219
217
217
  cognite/neat/_session/_read.py,sha256=d6O-ymlVx4o0mjKDIlrHOXiTOZxrdxhEX6mieLLETEI,9461
218
- cognite/neat/_session/_set.py,sha256=iSr086hrAOA8ctj2PlselDcHmkHsCDvcnr_OETjQN-A,1452
218
+ cognite/neat/_session/_set.py,sha256=zNhHI-7WON3NUXCc1Ajo2ti8l5kPYmHeJ8MxJ_QLIJk,1927
219
219
  cognite/neat/_session/_show.py,sha256=-f1-f6vJKdpZODYuFv6TFmO1BuELUldfCqZqeLvr0NE,14161
220
220
  cognite/neat/_session/_state.py,sha256=rqKHkikagO1pf_fKpY-LZI1X5R_v6AyYpV72_3eSduM,5783
221
- cognite/neat/_session/_to.py,sha256=xq4cCyMWsbd7Oc55BxffGgUobMDT63jsToK2VTjNiDQ,3802
221
+ cognite/neat/_session/_to.py,sha256=eMEw-25oGRJ-UFv57pCDUGtL_oNi4QharwhDl9DFu80,5181
222
222
  cognite/neat/_session/_wizard.py,sha256=Rdld2eZ-Q5BYbmAwW8FlfAYebdlw_P6L6V2WSDk-dHI,1306
223
223
  cognite/neat/_session/engine/__init__.py,sha256=aeI5pzljU5n1B-SVu3LwjYVsN1tSVhnJj-4ddflEo4U,120
224
224
  cognite/neat/_session/engine/_import.py,sha256=1QxA2_EK613lXYAHKQbZyw2yjo5P9XuiX4Z6_6-WMNQ,169
@@ -230,7 +230,7 @@ cognite/neat/_store/__init__.py,sha256=G-VG_YwfRt1kuPao07PDJyZ3w_0-eguzLUM13n-Z_
230
230
  cognite/neat/_store/_base.py,sha256=nKfXaQmtW535aDQZs_EIhFiBnghazZkeuyMen2THxm0,13611
231
231
  cognite/neat/_store/_provenance.py,sha256=cLOuZrIgb5mZwL1KvMeL0ODew92QyEvpri62CXhXz_w,8690
232
232
  cognite/neat/_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
233
- cognite/neat/_utils/auth.py,sha256=hyDnVBUbbgVANzayxbh9uTWlYb924hmzPYkVwwLfDIg,13241
233
+ cognite/neat/_utils/auth.py,sha256=UbFNq4BIqxc1459xJtI1FZz91K5XEMkfY5cfpBHyUHU,13301
234
234
  cognite/neat/_utils/auxiliary.py,sha256=kKPOPtwHj-RYerVCTQ_Rukn0Zkc6FNBhgQ1zOTAeRs8,5717
235
235
  cognite/neat/_utils/cdf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
236
  cognite/neat/_utils/cdf/data_classes.py,sha256=XdPsHfmE3G-4o1AT_rNRiT916kWRoUpOMfgCyYjI0xU,10461
@@ -245,9 +245,9 @@ cognite/neat/_utils/reader/_base.py,sha256=PECrAlJqKDlyFzAlBBLfKjyOEyJSgN0sUfjbK
245
245
  cognite/neat/_utils/spreadsheet.py,sha256=LI0c7dlW0zXHkHw0NvB-gg6Df6cDcE3FbiaHBYLXdzQ,2714
246
246
  cognite/neat/_utils/text.py,sha256=PvTEsEjaTu8SE8yYaKUrce4msboMj933dK7-0Eey_rE,3652
247
247
  cognite/neat/_utils/time_.py,sha256=O30LUiDH9TdOYz8_a9pFqTtJdg8vEjC3qHCk8xZblG8,345
248
- cognite/neat/_utils/upload.py,sha256=qd8NA46fMvhCIDh66WNj8MiXv1ewDiFjvy59pSFkmG0,4604
248
+ cognite/neat/_utils/upload.py,sha256=iWKmsQgw4EHLv-11NjYu7zAj5LtqTAfNa87a1kWeuaU,5727
249
249
  cognite/neat/_utils/xml_.py,sha256=FQkq84u35MUsnKcL6nTMJ9ajtG9D5i1u4VBnhGqP2DQ,1710
250
- cognite/neat/_version.py,sha256=obJ4L48mncYyAOX7r86bANjiuo8sNuMcnRMnXjUcUq8,45
250
+ cognite/neat/_version.py,sha256=x_rAP_QXrbMfEieOF7mnEF3qFbN1SzK4mMGniMYJCAo,45
251
251
  cognite/neat/_workflows/__init__.py,sha256=S0fZq7kvoqDKodHu1UIPsqcpdvXoefUWRPt1lqeQkQs,420
252
252
  cognite/neat/_workflows/base.py,sha256=O1pcmfbme2gIVF2eOGrKZSUDmhZc8L9rI8UfvLN2YAM,26839
253
253
  cognite/neat/_workflows/cdf_store.py,sha256=3pebnATPo6In4-1srpa3wzstynTOi3T6hwFX5uaie4c,18050
@@ -276,8 +276,8 @@ cognite/neat/_workflows/tasks.py,sha256=dr2xuIb8P5e5e9p_fjzRlvDbKsre2xGYrkc3wnRx
276
276
  cognite/neat/_workflows/triggers.py,sha256=u69xOsaTtM8_WD6ZeIIBB-XKwvlZmPHAsZQh_TnyHcM,7073
277
277
  cognite/neat/_workflows/utils.py,sha256=gKdy3RLG7ctRhbCRwaDIWpL9Mi98zm56-d4jfHDqP1E,453
278
278
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
- cognite_neat-0.97.1.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
280
- cognite_neat-0.97.1.dist-info/METADATA,sha256=CUzzYAntpELjv-uak04D5lH33bYK_0wLI_uPoKjixOI,9657
281
- cognite_neat-0.97.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
282
- cognite_neat-0.97.1.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
283
- cognite_neat-0.97.1.dist-info/RECORD,,
279
+ cognite_neat-0.97.3.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
280
+ cognite_neat-0.97.3.dist-info/METADATA,sha256=ohCmVgNuYqPUXNKeJk9sFrE9yRuxwjbroUmNoUhLj-k,9657
281
+ cognite_neat-0.97.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
282
+ cognite_neat-0.97.3.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
283
+ cognite_neat-0.97.3.dist-info/RECORD,,