cognite-neat 0.103.0__py3-none-any.whl → 0.104.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (64) hide show
  1. cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
  2. cognite/neat/_graph/transformers/_base.py +109 -1
  3. cognite/neat/_graph/transformers/_classic_cdf.py +4 -0
  4. cognite/neat/_graph/transformers/_prune_graph.py +103 -47
  5. cognite/neat/_graph/transformers/_rdfpath.py +41 -17
  6. cognite/neat/_graph/transformers/_value_type.py +119 -154
  7. cognite/neat/_issues/_base.py +35 -8
  8. cognite/neat/_issues/warnings/_resources.py +1 -1
  9. cognite/neat/_rules/_shared.py +18 -34
  10. cognite/neat/_rules/exporters/_base.py +28 -2
  11. cognite/neat/_rules/exporters/_rules2dms.py +4 -0
  12. cognite/neat/_rules/exporters/_rules2excel.py +11 -0
  13. cognite/neat/_rules/exporters/_rules2instance_template.py +4 -0
  14. cognite/neat/_rules/exporters/_rules2ontology.py +13 -1
  15. cognite/neat/_rules/exporters/_rules2yaml.py +4 -0
  16. cognite/neat/_rules/importers/_base.py +9 -0
  17. cognite/neat/_rules/importers/_dms2rules.py +17 -5
  18. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +5 -2
  19. cognite/neat/_rules/importers/_rdf/_base.py +10 -8
  20. cognite/neat/_rules/importers/_rdf/_imf2rules.py +4 -0
  21. cognite/neat/_rules/importers/_rdf/_inference2rules.py +7 -0
  22. cognite/neat/_rules/importers/_rdf/_owl2rules.py +4 -0
  23. cognite/neat/_rules/importers/_spreadsheet2rules.py +17 -8
  24. cognite/neat/_rules/importers/_yaml2rules.py +21 -7
  25. cognite/neat/_rules/models/_base_input.py +1 -1
  26. cognite/neat/_rules/models/_base_rules.py +5 -0
  27. cognite/neat/_rules/models/dms/_rules.py +4 -0
  28. cognite/neat/_rules/models/dms/_rules_input.py +9 -0
  29. cognite/neat/_rules/models/dms/_validation.py +2 -0
  30. cognite/neat/_rules/models/entities/_single_value.py +25 -5
  31. cognite/neat/_rules/models/information/_rules.py +4 -0
  32. cognite/neat/_rules/models/information/_rules_input.py +9 -0
  33. cognite/neat/_rules/models/mapping/_classic2core.py +2 -5
  34. cognite/neat/_rules/transformers/__init__.py +5 -4
  35. cognite/neat/_rules/transformers/_base.py +41 -65
  36. cognite/neat/_rules/transformers/_converters.py +149 -62
  37. cognite/neat/_rules/transformers/_mapping.py +17 -12
  38. cognite/neat/_rules/transformers/_verification.py +50 -37
  39. cognite/neat/_session/_base.py +32 -121
  40. cognite/neat/_session/_inspect.py +3 -3
  41. cognite/neat/_session/_mapping.py +17 -105
  42. cognite/neat/_session/_prepare.py +36 -254
  43. cognite/neat/_session/_read.py +11 -130
  44. cognite/neat/_session/_set.py +6 -30
  45. cognite/neat/_session/_show.py +49 -30
  46. cognite/neat/_session/_state.py +49 -107
  47. cognite/neat/_session/_to.py +42 -31
  48. cognite/neat/_shared.py +23 -2
  49. cognite/neat/_store/_provenance.py +3 -82
  50. cognite/neat/_store/_rules_store.py +372 -10
  51. cognite/neat/_store/exceptions.py +23 -0
  52. cognite/neat/_utils/graph_transformations_report.py +36 -0
  53. cognite/neat/_utils/io_.py +11 -0
  54. cognite/neat/_utils/rdf_.py +8 -0
  55. cognite/neat/_utils/spreadsheet.py +5 -4
  56. cognite/neat/_version.py +1 -1
  57. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +7 -7
  58. cognite/neat/_workflows/steps/lib/current/rules_importer.py +24 -99
  59. {cognite_neat-0.103.0.dist-info → cognite_neat-0.104.0.dist-info}/METADATA +1 -1
  60. {cognite_neat-0.103.0.dist-info → cognite_neat-0.104.0.dist-info}/RECORD +63 -61
  61. cognite/neat/_rules/transformers/_pipelines.py +0 -70
  62. {cognite_neat-0.103.0.dist-info → cognite_neat-0.104.0.dist-info}/LICENSE +0 -0
  63. {cognite_neat-0.103.0.dist-info → cognite_neat-0.104.0.dist-info}/WHEEL +0 -0
  64. {cognite_neat-0.103.0.dist-info → cognite_neat-0.104.0.dist-info}/entry_points.txt +0 -0
@@ -1,20 +1,382 @@
1
- from cognite.neat._issues import IssueList
1
+ import hashlib
2
+ from collections import defaultdict
3
+ from collections.abc import Callable, Hashable
4
+ from dataclasses import dataclass
5
+ from datetime import datetime, timezone
6
+ from functools import partial
7
+ from pathlib import Path
8
+ from typing import Any, cast
9
+
10
+ import rdflib
11
+
12
+ from cognite.neat._client import NeatClient
13
+ from cognite.neat._constants import DEFAULT_NAMESPACE
14
+ from cognite.neat._issues import IssueList, catch_issues
15
+ from cognite.neat._issues.errors import NeatValueError
16
+ from cognite.neat._rules._shared import ReadRules, Rules, T_VerifiedRules, VerifiedRules
2
17
  from cognite.neat._rules.exporters import BaseExporter
18
+ from cognite.neat._rules.exporters._base import CDFExporter, T_Export
3
19
  from cognite.neat._rules.importers import BaseImporter
20
+ from cognite.neat._rules.models import DMSRules, InformationRules
21
+ from cognite.neat._rules.models._base_input import InputRules
4
22
  from cognite.neat._rules.transformers import RulesTransformer
23
+ from cognite.neat._utils.upload import UploadResultList
24
+
25
+ from ._provenance import EMPTY_ENTITY, UNKNOWN_AGENT, Activity, Agent, Change, Entity, Provenance
26
+ from .exceptions import EmptyStore, InvalidInputOperation
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class ModelEntity(Entity):
31
+ result: Rules | None = None
5
32
 
6
- from ._provenance import Provenance
33
+ @property
34
+ def display_name(self) -> str:
35
+ if self.result is None:
36
+ return "Failed"
37
+ if isinstance(self.result, ReadRules):
38
+ if self.result.rules is None:
39
+ return "FailedRead"
40
+ return self.result.rules.display_type_name()
41
+ else:
42
+ return self.result.display_type_name()
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class OutcomeEntity(Entity):
47
+ result: UploadResultList | Path | str | None = None
7
48
 
8
49
 
9
50
  class NeatRulesStore:
10
- def __init__(self):
11
- self._provenance = Provenance()
51
+ def __init__(self) -> None:
52
+ self.provenance = Provenance()
53
+ self.exports_by_source_entity_id: dict[rdflib.URIRef, list[Change]] = defaultdict(list)
54
+ self.pruned_by_source_entity_id: dict[rdflib.URIRef, list[Provenance]] = defaultdict(list)
55
+ self._last_outcome: UploadResultList | None = None
56
+ self._iteration_by_id: dict[Hashable, int] = {}
57
+
58
+ def calculate_provenance_hash(self, shorten: bool = True) -> str:
59
+ sha256_hash = hashlib.sha256()
60
+ for change in self.provenance:
61
+ for id_ in [change.agent.id_, change.activity.id_, change.target_entity.id_, change.source_entity.id_]:
62
+ sha256_hash.update(str(id_).encode("utf-8"))
63
+ calculated_hash = sha256_hash.hexdigest()
64
+ if shorten:
65
+ return calculated_hash[:8]
66
+ return calculated_hash
67
+
68
+ def import_(self, importer: BaseImporter) -> IssueList:
69
+ agent = importer.agent
70
+ source_entity = Entity(
71
+ was_attributed_to=UNKNOWN_AGENT,
72
+ id_=importer.source_uri,
73
+ )
74
+ return self._do_activity(importer.to_rules, agent, source_entity, importer.description)[1]
75
+
76
+ def transform(self, *transformer: RulesTransformer) -> IssueList:
77
+ if not self.provenance:
78
+ raise EmptyStore()
79
+
80
+ all_issues = IssueList()
81
+ for item in transformer:
82
+ last_change = self.provenance[-1]
83
+ source_entity = last_change.target_entity
84
+ if not isinstance(source_entity, ModelEntity):
85
+ # Todo: Provenance should be of an entity type
86
+ raise ValueError("Bug in neat: The last entity in the provenance is not a model entity.")
87
+ transformer_input = source_entity.result
88
+
89
+ if not item.is_valid_input(transformer_input):
90
+ raise InvalidInputOperation(expected=item.transform_type_hint(), got=type(transformer_input))
91
+
92
+ transform_issues = self._do_activity(
93
+ partial(item.transform, rules=transformer_input),
94
+ item.agent,
95
+ source_entity,
96
+ item.description,
97
+ )[1]
98
+ all_issues.extend(transform_issues)
99
+ return all_issues
100
+
101
+ def export(self, exporter: BaseExporter[T_VerifiedRules, T_Export]) -> T_Export:
102
+ last_change = self.provenance[-1]
103
+ source_entity = last_change.target_entity
104
+ if not isinstance(source_entity, ModelEntity):
105
+ # Todo: Provenance should be of an entity type
106
+ raise ValueError("Bug in neat: The last entity in the provenance is not a model entity.")
107
+ expected_types = exporter.source_types()
108
+ if not any(isinstance(source_entity.result, type_) for type_ in expected_types):
109
+ raise InvalidInputOperation(expected=expected_types, got=type(source_entity.result))
110
+
111
+ agent = exporter.agent
112
+ start = datetime.now(timezone.utc)
113
+ issue_list = IssueList()
114
+ with catch_issues(issue_list) as _:
115
+ # Validate the type of the result
116
+ result = exporter.export(source_entity.result) # type: ignore[arg-type]
117
+ end = datetime.now(timezone.utc)
118
+ target_id = DEFAULT_NAMESPACE["export-result"]
119
+ activity = Activity(
120
+ was_associated_with=agent,
121
+ ended_at_time=end,
122
+ started_at_time=start,
123
+ used=source_entity,
124
+ )
125
+ target_entity = OutcomeEntity(
126
+ was_attributed_to=agent,
127
+ was_generated_by=activity,
128
+ result=type(result).__name__,
129
+ issues=issue_list,
130
+ id_=target_id,
131
+ )
132
+ change = Change(
133
+ agent=agent,
134
+ activity=activity,
135
+ target_entity=target_entity,
136
+ description=exporter.description,
137
+ source_entity=source_entity,
138
+ )
139
+ self.exports_by_source_entity_id[source_entity.id_].append(change)
140
+ return result
141
+
142
+ def export_to_file(self, exporter: BaseExporter, path: Path) -> None:
143
+ last_change = self.provenance[-1]
144
+ source_entity = last_change.target_entity
145
+ if not isinstance(source_entity, ModelEntity):
146
+ # Todo: Provenance should be of an entity type
147
+ raise ValueError("Bug in neat: The last entity in the provenance is not a model entity.")
148
+ expected_types = exporter.source_types()
149
+ if not any(isinstance(source_entity.result, type_) for type_ in expected_types):
150
+ raise InvalidInputOperation(expected=expected_types, got=type(source_entity.result))
151
+ target_id = DEFAULT_NAMESPACE[path.name]
152
+ agent = exporter.agent
153
+ start = datetime.now(timezone.utc)
154
+ issue_list = IssueList()
155
+ with catch_issues(issue_list) as _:
156
+ exporter.export_to_file(source_entity.result, path)
157
+ end = datetime.now(timezone.utc)
158
+
159
+ activity = Activity(
160
+ was_associated_with=agent,
161
+ ended_at_time=end,
162
+ started_at_time=start,
163
+ used=source_entity,
164
+ )
165
+ target_entity = OutcomeEntity(
166
+ was_attributed_to=agent,
167
+ was_generated_by=activity,
168
+ result=path,
169
+ issues=issue_list,
170
+ id_=target_id,
171
+ )
172
+ change = Change(
173
+ agent=agent,
174
+ activity=activity,
175
+ target_entity=target_entity,
176
+ description=exporter.description,
177
+ source_entity=source_entity,
178
+ )
179
+ self.exports_by_source_entity_id[source_entity.id_].append(change)
180
+
181
+ def export_to_cdf(self, exporter: CDFExporter, client: NeatClient, dry_run: bool) -> UploadResultList:
182
+ last_change = self.provenance[-1]
183
+ source_entity = last_change.target_entity
184
+ if not isinstance(source_entity, ModelEntity):
185
+ # Todo: Provenance should be of an entity type
186
+ raise ValueError("Bug in neat: The last entity in the provenance is not a model entity.")
187
+ expected_types = exporter.source_types()
188
+ if not any(isinstance(source_entity.result, type_) for type_ in expected_types):
189
+ raise InvalidInputOperation(expected=expected_types, got=type(source_entity.result))
190
+
191
+ agent = exporter.agent
192
+ start = datetime.now(timezone.utc)
193
+ target_id = DEFAULT_NAMESPACE["upload-result"]
194
+ issue_list = IssueList()
195
+ result: UploadResultList | None = None
196
+ with catch_issues(issue_list) as _:
197
+ result = exporter.export_to_cdf(source_entity.result, client, dry_run)
198
+ end = datetime.now(timezone.utc)
199
+
200
+ activity = Activity(
201
+ was_associated_with=agent,
202
+ ended_at_time=end,
203
+ started_at_time=start,
204
+ used=source_entity,
205
+ )
206
+ target_entity = OutcomeEntity(
207
+ was_attributed_to=agent,
208
+ was_generated_by=activity,
209
+ result=result,
210
+ issues=issue_list,
211
+ id_=target_id,
212
+ )
213
+ change = Change(
214
+ agent=agent,
215
+ activity=activity,
216
+ target_entity=target_entity,
217
+ description=exporter.description,
218
+ source_entity=source_entity,
219
+ )
220
+ self.exports_by_source_entity_id[source_entity.id_].append(change)
221
+ self._last_outcome = result
222
+ return result
223
+
224
+ def prune_until_compatible(self, transformer: RulesTransformer) -> list[Change]:
225
+ """Prune the provenance until the last successful entity is compatible with the transformer.
226
+
227
+ Args:
228
+ transformer: The transformer to check compatibility with.
229
+
230
+ Returns:
231
+ The changes that were pruned.
232
+ """
233
+ pruned_candidates: list[Change] = []
234
+ for change in reversed(self.provenance):
235
+ if not isinstance(change.target_entity, ModelEntity):
236
+ continue
237
+ if not transformer.is_valid_input(change.target_entity.result):
238
+ pruned_candidates.append(change)
239
+ else:
240
+ break
241
+ else:
242
+ raise NeatValueError("No compatible entity found in the provenance.")
243
+ if not pruned_candidates:
244
+ return []
245
+ self.provenance = self.provenance[: -len(pruned_candidates)]
246
+ pruned_candidates.reverse()
247
+ self.pruned_by_source_entity_id[self.provenance[-1].target_entity.id_].append(Provenance(pruned_candidates))
248
+ return pruned_candidates
249
+
250
+ def _export(self, action: Callable[[Any], Any], agent: Agent, description: str) -> Any:
251
+ last_entity: ModelEntity | None = None
252
+ for change in reversed(self.provenance):
253
+ if isinstance(change.target_entity, ModelEntity) and isinstance(change.target_entity.result, DMSRules):
254
+ last_entity = change.target_entity
255
+ break
256
+ if last_entity is None:
257
+ raise NeatValueError("No verified DMS rules found in the provenance.")
258
+ rules = last_entity.result
259
+ result, _ = self._do_activity(lambda: action(rules), agent, last_entity, description)
260
+ return result
261
+
262
+ def _do_activity(
263
+ self, action: Callable[[], Rules | None], agent: Agent, source_entity: Entity, description: str
264
+ ) -> tuple[Any, IssueList]:
265
+ start = datetime.now(timezone.utc)
266
+ issue_list = IssueList()
267
+ result: Rules | None = None
268
+ with catch_issues(issue_list) as _:
269
+ result = action()
270
+ end = datetime.now(timezone.utc)
271
+
272
+ activity = Activity(
273
+ was_associated_with=agent,
274
+ ended_at_time=end,
275
+ started_at_time=start,
276
+ used=source_entity,
277
+ )
278
+ target_entity = ModelEntity(
279
+ was_attributed_to=agent,
280
+ was_generated_by=activity,
281
+ result=result,
282
+ issues=issue_list,
283
+ id_=self._create_id(result),
284
+ )
285
+ change = Change(
286
+ agent=agent,
287
+ activity=activity,
288
+ target_entity=target_entity,
289
+ description=description,
290
+ source_entity=source_entity,
291
+ )
292
+
293
+ self.provenance.append(change)
294
+ return result, issue_list
295
+
296
+ def _create_id(self, result: Any) -> rdflib.URIRef:
297
+ identifier: rdflib.URIRef
298
+ if result is None:
299
+ identifier = EMPTY_ENTITY.id_
300
+ elif isinstance(result, ReadRules):
301
+ if result.rules is None:
302
+ identifier = EMPTY_ENTITY.id_
303
+ else:
304
+ identifier = result.rules.metadata.identifier
305
+ elif isinstance(result, VerifiedRules):
306
+ identifier = result.metadata.identifier
307
+ else:
308
+ identifier = DEFAULT_NAMESPACE["unknown-entity"]
309
+
310
+ if identifier not in self._iteration_by_id:
311
+ self._iteration_by_id[identifier] = 1
312
+ return identifier
313
+ self._iteration_by_id[identifier] += 1
314
+ return identifier + f"/Iteration_{self._iteration_by_id[identifier]}"
315
+
316
+ def get_last_entity(self) -> ModelEntity:
317
+ if not self.provenance:
318
+ raise NeatValueError("No entity found in the provenance.")
319
+ return cast(ModelEntity, self.provenance[-1].target_entity)
320
+
321
+ def get_last_successful_entity(self) -> ModelEntity:
322
+ for change in reversed(self.provenance):
323
+ if isinstance(change.target_entity, ModelEntity) and change.target_entity.result:
324
+ return change.target_entity
325
+ raise NeatValueError("No successful entity found in the provenance.")
326
+
327
+ @property
328
+ def has_unverified_rules(self) -> bool:
329
+ return any(
330
+ isinstance(change.target_entity, ModelEntity)
331
+ and isinstance(change.target_entity.result, ReadRules)
332
+ and change.target_entity.result.rules is not None
333
+ for change in self.provenance
334
+ )
335
+
336
+ @property
337
+ def has_verified_rules(self) -> bool:
338
+ return any(
339
+ isinstance(change.target_entity, ModelEntity)
340
+ and isinstance(change.target_entity.result, DMSRules | InformationRules)
341
+ for change in self.provenance
342
+ )
343
+
344
+ @property
345
+ def last_unverified_rule(self) -> InputRules:
346
+ for change in reversed(self.provenance):
347
+ if (
348
+ isinstance(change.target_entity, ModelEntity)
349
+ and isinstance(change.target_entity.result, ReadRules)
350
+ and change.target_entity.result.rules is not None
351
+ ):
352
+ return change.target_entity.result.rules
353
+
354
+ raise NeatValueError("No unverified rule found in the provenance.")
355
+
356
+ @property
357
+ def last_verified_rule(self) -> DMSRules | InformationRules:
358
+ for change in reversed(self.provenance):
359
+ if isinstance(change.target_entity, ModelEntity) and isinstance(
360
+ change.target_entity.result, DMSRules | InformationRules
361
+ ):
362
+ return change.target_entity.result
363
+ raise NeatValueError("No verified rule found in the provenance.")
12
364
 
13
- def write(self, importer: BaseImporter) -> IssueList:
14
- raise NotImplementedError()
365
+ @property
366
+ def last_verified_dms_rules(self) -> DMSRules:
367
+ for change in reversed(self.provenance):
368
+ if isinstance(change.target_entity, ModelEntity) and isinstance(change.target_entity.result, DMSRules):
369
+ return change.target_entity.result
370
+ raise NeatValueError("No verified DMS rules found in the provenance.")
15
371
 
16
- def transform(self, transformer: RulesTransformer) -> IssueList:
17
- raise NotImplementedError()
372
+ @property
373
+ def last_issues(self) -> IssueList:
374
+ if not self.provenance:
375
+ raise NeatValueError("No issues found in the provenance.")
376
+ return self.provenance[-1].target_entity.issues
18
377
 
19
- def read(self, exporter: BaseExporter) -> IssueList:
20
- raise NotImplementedError()
378
+ @property
379
+ def last_outcome(self) -> UploadResultList:
380
+ if self._last_outcome is not None:
381
+ return self._last_outcome
382
+ raise NeatValueError("No outcome found in the provenance.")
@@ -0,0 +1,23 @@
1
+ """These are special exceptions that are used by the store to signal invalid transformers"""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ class NeatStoreError(Exception):
7
+ """Base class for all exceptions in the store module"""
8
+
9
+ ...
10
+
11
+
12
+ @dataclass
13
+ class InvalidInputOperation(NeatStoreError, RuntimeError):
14
+ """Raised when an invalid operation is attempted"""
15
+
16
+ expected: tuple[type, ...]
17
+ got: type
18
+
19
+
20
+ class EmptyStore(NeatStoreError, RuntimeError):
21
+ """Raised when the store is empty"""
22
+
23
+ ...
@@ -0,0 +1,36 @@
1
+ from abc import ABC
2
+ from dataclasses import dataclass
3
+ from typing import Any
4
+
5
+ from cognite.neat._shared import NeatList, NeatObject
6
+
7
+
8
+ @dataclass
9
+ class GraphTransformationResult(NeatObject, ABC):
10
+ name: str
11
+ affected_nodes_count: int | None = None
12
+ added: int | None = None
13
+ removed: int | None = None
14
+ skipped: int | None = None
15
+ modified: int | None = None
16
+
17
+ def dump(self, aggregate: bool = True) -> dict[str, Any]:
18
+ output: dict[str, Any] = {"name": self.name}
19
+ if self.added:
20
+ output["added"] = self.added
21
+ if self.removed:
22
+ output["removed"] = self.removed
23
+ if self.skipped:
24
+ output["skipped"] = self.skipped
25
+ if self.affected_nodes_count:
26
+ output["affected nodes"] = self.affected_nodes_count
27
+ if self.modified:
28
+ output["modified instances"] = self.modified
29
+ return output
30
+
31
+
32
+ class GraphTransformationResultList(NeatList[GraphTransformationResult]):
33
+ def _repr_html_(self) -> str:
34
+ df = self.to_pandas().fillna(0)
35
+ df = df.style.format({column: "{:,.0f}".format for column in df.select_dtypes(include="number").columns})
36
+ return df._repr_html_() # type: ignore[attr-defined]
@@ -0,0 +1,11 @@
1
+ import re
2
+
3
+ # Spaces are allowed, but we replace them as well
4
+ _ILLEGAL_CHARACTERS = re.compile(r"[<>:\"/\\|?*\s]")
5
+
6
+
7
+ def to_directory_compatible(text: str) -> str:
8
+ """Convert a string to be compatible with directory names on all platforms"""
9
+ cleaned = _ILLEGAL_CHARACTERS.sub("_", text)
10
+ # Replace multiple underscores with a single one
11
+ return re.sub(r"_+", "_", cleaned)
@@ -257,3 +257,11 @@ def remove_instance_ids_in_batch(graph: Graph, instance_ids: Iterable[URIRef], b
257
257
  check_commit()
258
258
 
259
259
  check_commit(force_commit=True)
260
+
261
+
262
+ def uri_display_name(thing: URIRef) -> str:
263
+ if "https://cognitedata.com/dms/data-model/" in thing:
264
+ return "DMS(" + ",".join(thing.replace("https://cognitedata.com/dms/data-model/", "").split("/")) + ")"
265
+ elif "http://purl.org/cognite/neat/data-model/" in thing:
266
+ return "NEAT(" + ",".join(thing.replace("http://purl.org/cognite/neat/data-model/", "").split("/")) + ")"
267
+ return remove_namespace_from_uri(thing)
@@ -14,7 +14,7 @@ class SpreadsheetRead:
14
14
  such that the error/warning messages are accurate.
15
15
  """
16
16
 
17
- header_row: int = 0
17
+ header_row: int = 1
18
18
  empty_rows: list[int] = field(default_factory=list)
19
19
  is_one_indexed: bool = True
20
20
 
@@ -22,13 +22,13 @@ class SpreadsheetRead:
22
22
  self.empty_rows = sorted(self.empty_rows)
23
23
 
24
24
  def adjusted_row_number(self, row_no: int) -> int:
25
- output = row_no + self.header_row + (1 if self.is_one_indexed else 0)
25
+ output = row_no
26
26
  for empty_row in self.empty_rows:
27
27
  if empty_row <= output:
28
28
  output += 1
29
29
  else:
30
30
  break
31
- return output
31
+ return output + self.header_row + (1 if self.is_one_indexed else 0)
32
32
 
33
33
 
34
34
  @overload
@@ -71,7 +71,8 @@ def read_individual_sheet(
71
71
  raw["Value Type"] = raw["Value Type"].replace(float("nan"), "#N/A")
72
72
  output = raw.replace(float("nan"), None).to_dict(orient="records")
73
73
  if return_read_info:
74
- return output, SpreadsheetRead(header_row=skiprows, empty_rows=empty_rows, is_one_indexed=True)
74
+ # If no rows are skipped, row 1 is the header row.
75
+ return output, SpreadsheetRead(header_row=skiprows + 1, empty_rows=empty_rows, is_one_indexed=True)
75
76
  return output
76
77
 
77
78
 
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.103.0"
1
+ __version__ = "0.104.0"
2
2
  __engine__ = "^2.0.3"
@@ -89,7 +89,7 @@ class DeleteDataModelFromCDF(Step):
89
89
  if isinstance(input_rules, DMSRules):
90
90
  dms_rules = input_rules
91
91
  elif isinstance(input_rules, InformationRules):
92
- dms_rules = InformationToDMS().transform(input_rules).rules
92
+ dms_rules = InformationToDMS().transform(input_rules)
93
93
  else:
94
94
  raise NotImplementedError(f"Unsupported rules type {type(input_rules)}")
95
95
 
@@ -201,7 +201,7 @@ class RulesToDMS(Step):
201
201
  if isinstance(input_rules, DMSRules):
202
202
  dms_rules = input_rules
203
203
  elif isinstance(input_rules, InformationRules):
204
- dms_rules = InformationToDMS().transform(input_rules).rules
204
+ dms_rules = InformationToDMS().transform(input_rules)
205
205
  else:
206
206
  raise NotImplementedError(f"Unsupported rules type {type(input_rules)}")
207
207
 
@@ -324,12 +324,12 @@ class RulesToExcel(Step):
324
324
  ...
325
325
  elif output_role is RoleTypes.dms:
326
326
  if isinstance(rule_instance, InformationRules):
327
- rule_instance = InformationToDMS().transform(rule_instance).rules
327
+ rule_instance = InformationToDMS().transform(rule_instance)
328
328
  else:
329
329
  raise NotImplementedError(f"Role {output_role} is not supported for {type(rules).__name__} rules")
330
330
  elif output_role is RoleTypes.information:
331
331
  if isinstance(rule_instance, DMSRules):
332
- rule_instance = DMSToInformation().transform(rule_instance).rules
332
+ rule_instance = DMSToInformation().transform(rule_instance)
333
333
  else:
334
334
  raise NotImplementedError(f"Role {output_role} is not supported for {type(rules).__name__} rules")
335
335
  else:
@@ -394,7 +394,7 @@ class RulesToOntology(Step):
394
394
 
395
395
  input_rules = rules.information or rules.dms
396
396
  if isinstance(input_rules, DMSRules):
397
- info_rules = DMSToInformation().transform(input_rules).rules
397
+ info_rules = DMSToInformation().transform(input_rules)
398
398
  elif isinstance(input_rules, InformationRules):
399
399
  info_rules = input_rules
400
400
  else:
@@ -453,7 +453,7 @@ class RulesToSHACL(Step):
453
453
 
454
454
  input_rules = rules.information or rules.dms
455
455
  if isinstance(input_rules, DMSRules):
456
- info_rules = DMSToInformation().transform(input_rules).rules
456
+ info_rules = DMSToInformation().transform(input_rules)
457
457
  elif isinstance(input_rules, InformationRules):
458
458
  info_rules = input_rules
459
459
  else:
@@ -511,7 +511,7 @@ class RulesToSemanticDataModel(Step):
511
511
  storage_path.parent.mkdir(parents=True, exist_ok=True)
512
512
  input_rules = rules.information or rules.dms
513
513
  if isinstance(input_rules, DMSRules):
514
- info_rules = DMSToInformation().transform(input_rules).rules
514
+ info_rules = DMSToInformation().transform(input_rules)
515
515
  elif isinstance(input_rules, InformationRules):
516
516
  info_rules = input_rules
517
517
  else: