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

cognite/neat/_alpha.py CHANGED
@@ -22,3 +22,4 @@ class ExperimentalFlags:
22
22
  aml_read = ExperimentalFeatureWarning("aml_read")
23
23
  csv_read = ExperimentalFeatureWarning("csv_read")
24
24
  to_ontology = ExperimentalFeatureWarning("to_ontology")
25
+ extension = ExperimentalFeatureWarning("extension")
@@ -154,8 +154,12 @@ class RulesAnalysis:
154
154
  def _include_ancestors(parents_by_class: dict[T_Hashable, set[T_Hashable]]) -> None:
155
155
  # Topological sort to ensure that classes include all ancestors
156
156
  for class_entity in list(TopologicalSorter(parents_by_class).static_order()):
157
+ if class_entity not in parents_by_class:
158
+ continue
157
159
  parents_by_class[class_entity] |= {
158
- grand_parent for parent in parents_by_class[class_entity] for grand_parent in parents_by_class[parent]
160
+ grand_parent
161
+ for parent in parents_by_class[class_entity]
162
+ for grand_parent in parents_by_class.get(parent, set())
159
163
  }
160
164
 
161
165
  def properties_by_class(
@@ -1,8 +1,9 @@
1
+ import warnings
1
2
  from collections import defaultdict
2
3
  from collections.abc import Collection, Iterable, Sequence
3
4
  from datetime import datetime
4
5
  from pathlib import Path
5
- from typing import Literal, cast
6
+ from typing import Literal
6
7
 
7
8
  from cognite.client import data_modeling as dm
8
9
  from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier
@@ -16,8 +17,13 @@ from cognite.client.data_classes.data_modeling.data_types import Enum as DMSEnum
16
17
  from cognite.client.data_classes.data_modeling.views import (
17
18
  MultiEdgeConnectionApply,
18
19
  MultiReverseDirectRelationApply,
20
+ ReverseDirectRelation,
21
+ SingleEdgeConnection,
19
22
  SingleEdgeConnectionApply,
23
+ SingleReverseDirectRelation,
20
24
  SingleReverseDirectRelationApply,
25
+ View,
26
+ ViewProperty,
21
27
  ViewPropertyApply,
22
28
  )
23
29
  from cognite.client.utils import ms_to_datetime
@@ -28,11 +34,13 @@ from cognite.neat._issues import IssueList, MultiValueError, NeatIssue, catch_is
28
34
  from cognite.neat._issues.errors import (
29
35
  FileTypeUnexpectedError,
30
36
  NeatValueError,
37
+ PropertyTypeNotSupportedError,
31
38
  ResourceMissingIdentifierError,
32
39
  ResourceRetrievalError,
33
40
  )
34
41
  from cognite.neat._issues.warnings import (
35
42
  MissingCogniteClientWarning,
43
+ NeatValueWarning,
36
44
  PropertyNotFoundWarning,
37
45
  PropertyTypeNotSupportedWarning,
38
46
  ResourceNotFoundWarning,
@@ -45,7 +53,7 @@ from cognite.neat._rules.models import (
45
53
  DMSInputRules,
46
54
  DMSSchema,
47
55
  )
48
- from cognite.neat._rules.models.data_types import DataType, Enum
56
+ from cognite.neat._rules.models.data_types import DataType, Enum, String
49
57
  from cognite.neat._rules.models.dms import (
50
58
  DMSInputContainer,
51
59
  DMSInputEnum,
@@ -63,6 +71,10 @@ from cognite.neat._rules.models.entities import (
63
71
  ReverseConnectionEntity,
64
72
  ViewEntity,
65
73
  )
74
+ from cognite.neat._rules.models.information import (
75
+ InformationInputClass,
76
+ InformationInputProperty,
77
+ )
66
78
 
67
79
 
68
80
  class DMSImporter(BaseImporter[DMSInputRules]):
@@ -332,19 +344,27 @@ class DMSImporter(BaseImporter[DMSInputRules]):
332
344
  )
333
345
  return None
334
346
 
335
- value_type = self._get_value_type(prop, view_entity, prop_id, enum_collection_by_container_property)
347
+ container_property = (
348
+ self._get_container_property_definition(prop) if isinstance(prop, dm.MappedPropertyApply) else None
349
+ )
350
+ value_type = self._get_value_type(prop, container_property, enum_collection_by_container_property)
336
351
  if value_type is None:
352
+ self.issue_list.append(
353
+ PropertyTypeNotSupportedWarning(view_entity.as_id(), "view", prop_id, type(prop).__name__)
354
+ )
337
355
  return None
356
+ if isinstance(value_type, ViewEntity) and value_type.as_id() not in self._all_views_by_id:
357
+ self.issue_list.append(ResourceUnknownWarning(prop.source, "view", view_entity.as_id(), "view"))
338
358
 
339
359
  return DMSInputProperty(
340
360
  description=prop.description,
341
361
  name=prop.name,
342
362
  connection=self._get_connection_type(prop),
343
363
  value_type=str(value_type),
344
- min_count=self._get_min_count(prop),
345
- max_count=self._get_max_count(prop),
364
+ min_count=self._get_min_count(prop, container_property),
365
+ max_count=self._get_max_count(prop, container_property),
346
366
  immutable=self._get_immutable(prop),
347
- default=self._get_default(prop),
367
+ default=self._get_default(prop, container_property),
348
368
  container=(
349
369
  str(ContainerEntity.from_id(prop.container)) if isinstance(prop, dm.MappedPropertyApply) else None
350
370
  ),
@@ -357,7 +377,7 @@ class DMSImporter(BaseImporter[DMSInputRules]):
357
377
  constraint=self._get_constraint(prop, prop_id),
358
378
  )
359
379
 
360
- def _container_prop_unsafe(self, prop: dm.MappedPropertyApply) -> dm.ContainerProperty:
380
+ def _get_container_property_definition(self, prop: dm.MappedPropertyApply) -> dm.ContainerProperty:
361
381
  """This method assumes you have already checked that the container with property exists."""
362
382
  return self._all_containers_by_id[prop.container].properties[prop.container_property_identifier]
363
383
 
@@ -372,40 +392,48 @@ class DMSImporter(BaseImporter[DMSInputRules]):
372
392
  elif isinstance(prop, SingleReverseDirectRelationApply | MultiReverseDirectRelationApply):
373
393
  return ReverseConnectionEntity(property=prop.through.property)
374
394
  elif isinstance(prop, dm.MappedPropertyApply) and isinstance(
375
- self._container_prop_unsafe(prop).type, dm.DirectRelation
395
+ self._get_container_property_definition(prop).type, dm.DirectRelation
376
396
  ):
377
397
  return "direct"
378
398
  else:
379
399
  return None
380
400
 
401
+ @classmethod
381
402
  def _get_value_type(
382
- self,
383
- prop: ViewPropertyApply,
384
- view_entity: ViewEntity,
385
- prop_id: str,
386
- enum_collection_by_container_property: dict[tuple[dm.ContainerId, str], str],
403
+ cls,
404
+ prop: ViewPropertyApply | ViewProperty,
405
+ container_property: dm.ContainerProperty | None = None,
406
+ enum_collection_by_container_property: dict[tuple[dm.ContainerId, str], str] | None = None,
387
407
  ) -> DataType | ViewEntity | DMSUnknownEntity | None:
388
408
  if isinstance(
389
409
  prop,
390
410
  SingleEdgeConnectionApply
391
411
  | MultiEdgeConnectionApply
392
412
  | SingleReverseDirectRelationApply
393
- | MultiReverseDirectRelationApply,
413
+ | MultiReverseDirectRelationApply
414
+ | SingleEdgeConnection
415
+ | dm.MultiEdgeConnection
416
+ | SingleReverseDirectRelation
417
+ | dm.MultiReverseDirectRelation,
394
418
  ):
395
419
  return ViewEntity.from_id(prop.source)
396
- elif isinstance(prop, dm.MappedPropertyApply):
397
- container_prop = self._container_prop_unsafe(cast(dm.MappedPropertyApply, prop))
398
- if isinstance(container_prop.type, dm.DirectRelation):
420
+ elif isinstance(prop, dm.MappedPropertyApply | dm.MappedProperty):
421
+ if isinstance(prop, dm.MappedPropertyApply):
422
+ if container_property is None:
423
+ raise ValueError("container property must be provided when prop is a MappedProperty")
424
+ prop_type = container_property.type
425
+ else:
426
+ prop_type = prop.type
427
+ if isinstance(prop_type, dm.DirectRelation):
399
428
  if prop.source is None:
400
429
  return DMSUnknownEntity()
401
- elif prop.source not in self._all_views_by_id:
402
- self.issue_list.append(ResourceUnknownWarning(prop.source, "view", view_entity.as_id(), "view"))
403
- return ViewEntity.from_id(prop.source)
404
430
  else:
405
431
  return ViewEntity.from_id(prop.source)
406
- elif isinstance(container_prop.type, PropertyTypeWithUnit) and container_prop.type.unit:
407
- return DataType.load(f"{container_prop.type._type}(unit={container_prop.type.unit.external_id})")
408
- elif isinstance(container_prop.type, DMSEnum):
432
+ elif isinstance(prop_type, PropertyTypeWithUnit) and prop_type.unit:
433
+ return DataType.load(f"{prop_type._type}(unit={prop_type.unit.external_id})")
434
+ elif isinstance(prop_type, DMSEnum):
435
+ if enum_collection_by_container_property is None:
436
+ return String()
409
437
  collection = enum_collection_by_container_property.get(
410
438
  (prop.container, prop.container_property_identifier)
411
439
  )
@@ -415,30 +443,46 @@ class DMSImporter(BaseImporter[DMSInputRules]):
415
443
  f"BUG in Neat: Enum for {prop.container}.{prop.container_property_identifier} not found."
416
444
  )
417
445
 
418
- return Enum(collection=ClassEntity(suffix=collection), unknownValue=container_prop.type.unknown_value)
446
+ return Enum(collection=ClassEntity(suffix=collection), unknownValue=prop_type.unknown_value)
419
447
  else:
420
- return DataType.load(container_prop.type._type)
448
+ return DataType.load(prop_type._type)
421
449
  else:
422
- self.issue_list.append(
423
- PropertyTypeNotSupportedWarning[dm.ViewId](view_entity.as_id(), "view", prop_id, type(prop).__name__)
424
- )
425
450
  return None
426
451
 
427
- def _get_min_count(self, prop: ViewPropertyApply) -> int | None:
452
+ @classmethod
453
+ def _get_min_count(
454
+ cls,
455
+ prop: ViewPropertyApply | ViewProperty,
456
+ container_property: dm.ContainerProperty | None = None,
457
+ ) -> int | None:
428
458
  if isinstance(prop, dm.MappedPropertyApply):
429
- return int(not self._container_prop_unsafe(prop).nullable)
459
+ if container_property is None:
460
+ raise ValueError("container_property must be provided when prop is a MappedPropertyApply")
461
+ return int(not container_property.nullable)
462
+ elif isinstance(prop, dm.MappedProperty):
463
+ return int(not prop.nullable)
430
464
  else:
431
465
  return None
432
466
 
433
467
  def _get_immutable(self, prop: ViewPropertyApply) -> bool | None:
434
468
  if isinstance(prop, dm.MappedPropertyApply):
435
- return self._container_prop_unsafe(prop).immutable
469
+ return self._get_container_property_definition(prop).immutable
436
470
  else:
437
471
  return None
438
472
 
439
- def _get_max_count(self, prop: ViewPropertyApply) -> int | float | None:
440
- if isinstance(prop, dm.MappedPropertyApply):
441
- prop_type = self._container_prop_unsafe(prop).type
473
+ @classmethod
474
+ def _get_max_count(
475
+ cls,
476
+ prop: ViewPropertyApply | ViewProperty,
477
+ container_property: dm.ContainerProperty | None = None,
478
+ ) -> int | float | None:
479
+ if isinstance(prop, dm.MappedPropertyApply | dm.MappedProperty):
480
+ if isinstance(prop, dm.MappedPropertyApply):
481
+ if container_property is None:
482
+ raise ValueError("get_container must be provided when prop is a MappedPropertyApply")
483
+ prop_type = container_property.type
484
+ else:
485
+ prop_type = prop.type
442
486
  if isinstance(prop_type, ListablePropertyType):
443
487
  if prop_type.is_list is False:
444
488
  return 1
@@ -450,17 +494,41 @@ class DMSImporter(BaseImporter[DMSInputRules]):
450
494
  return DMS_PRIMITIVE_LIST_DEFAULT_LIMIT
451
495
  else:
452
496
  return 1
453
- elif isinstance(prop, MultiEdgeConnectionApply | MultiReverseDirectRelationApply):
497
+ elif isinstance(
498
+ prop,
499
+ MultiEdgeConnectionApply
500
+ | MultiReverseDirectRelationApply
501
+ | dm.MultiEdgeConnection
502
+ | dm.MultiReverseDirectRelation,
503
+ ):
454
504
  return float("inf")
455
- elif isinstance(prop, SingleEdgeConnectionApply | SingleReverseDirectRelationApply):
505
+ elif isinstance(
506
+ prop,
507
+ SingleEdgeConnectionApply
508
+ | SingleReverseDirectRelationApply
509
+ | SingleEdgeConnection
510
+ | SingleReverseDirectRelation,
511
+ ):
456
512
  return 1
457
513
  else:
458
- # Unknown type.
514
+ warnings.warn(
515
+ NeatValueWarning(f"Unknown property type {type(prop)}. Assuming max count is inf"), stacklevel=2
516
+ )
459
517
  return None
460
518
 
461
- def _get_default(self, prop: ViewPropertyApply) -> str | None:
462
- if isinstance(prop, dm.MappedPropertyApply):
463
- default = self._container_prop_unsafe(prop).default_value
519
+ @classmethod
520
+ def _get_default(
521
+ cls,
522
+ prop: ViewPropertyApply | ViewProperty,
523
+ container_property: dm.ContainerProperty | None = None,
524
+ ) -> str | None:
525
+ if isinstance(prop, dm.MappedPropertyApply | dm.MappedProperty):
526
+ if isinstance(prop, dm.MappedPropertyApply):
527
+ if container_property is None:
528
+ raise ValueError("container_property must be provided when prop is a MappedPropertyApply")
529
+ default = container_property.default_value
530
+ else:
531
+ default = prop.default_value
464
532
  if default is not None:
465
533
  return str(default)
466
534
  return None
@@ -559,3 +627,43 @@ class DMSImporter(BaseImporter[DMSInputRules]):
559
627
  )
560
628
  )
561
629
  return enum_by_container_property
630
+
631
+ @classmethod
632
+ def as_information_input_property(
633
+ cls, entity: ClassEntity, prop_id: str, view_property: ViewProperty
634
+ ) -> InformationInputProperty:
635
+ if not isinstance(view_property, dm.MappedProperty | dm.EdgeConnection | ReverseDirectRelation):
636
+ raise PropertyTypeNotSupportedError(
637
+ dm.ViewId(str(entity.prefix), str(entity.suffix), entity.version),
638
+ "view",
639
+ prop_id,
640
+ type(view_property).__name__,
641
+ )
642
+
643
+ value_type = cls._get_value_type(view_property)
644
+ if value_type is None:
645
+ raise NeatValueError(f"Failed to get value type for {entity} property {prop_id}")
646
+
647
+ return InformationInputProperty(
648
+ class_=entity,
649
+ property_=prop_id,
650
+ value_type=str(value_type),
651
+ name=view_property.name,
652
+ description=view_property.description,
653
+ min_count=cls._get_min_count(view_property),
654
+ max_count=cls._get_max_count(view_property),
655
+ default=cls._get_default(view_property),
656
+ )
657
+
658
+ @classmethod
659
+ def as_information_input_class(cls, view: View) -> InformationInputClass:
660
+ return InformationInputClass(
661
+ class_=ClassEntity(prefix=view.space, suffix=view.external_id, version=view.version),
662
+ name=view.name,
663
+ description=view.description,
664
+ implements=[
665
+ ClassEntity(prefix=parent.space, suffix=parent.external_id, version=parent.version)
666
+ for parent in view.implements or []
667
+ ]
668
+ or None,
669
+ )
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass, field
2
2
  from datetime import datetime
3
- from typing import Any
3
+ from typing import Any, cast
4
4
 
5
5
  import pandas as pd
6
6
  from cognite.client import data_modeling as dm
@@ -103,6 +103,9 @@ class InformationInputProperty(InputComponent[InformationProperty]):
103
103
  output["Value Type"] = load_value_type(self.value_type, default_prefix)
104
104
  return output
105
105
 
106
+ def copy(self, update: dict[str, Any], default_prefix: str) -> "InformationInputProperty":
107
+ return cast(InformationInputProperty, type(self)._load({**self.dump(default_prefix), **update}))
108
+
106
109
 
107
110
  @dataclass
108
111
  class InformationInputClass(InputComponent[InformationClass]):
@@ -1,6 +1,7 @@
1
1
  from ._base import RulesTransformer, VerifiedRulesTransformer
2
2
  from ._converters import (
3
3
  AddClassImplements,
4
+ AddCogniteProperties,
4
5
  ChangeViewPrefix,
5
6
  ClassicPrepareCore,
6
7
  ConversionTransformer,
@@ -29,6 +30,7 @@ from ._verification import VerifyAnyRules, VerifyDMSRules, VerifyInformationRule
29
30
 
30
31
  __all__ = [
31
32
  "AddClassImplements",
33
+ "AddCogniteProperties",
32
34
  "AsParentPropertyId",
33
35
  "ChangeViewPrefix",
34
36
  "ClassicPrepareCore",
@@ -6,10 +6,12 @@ from collections import Counter, defaultdict
6
6
  from collections.abc import Collection, Mapping
7
7
  from datetime import date, datetime
8
8
  from functools import cached_property
9
+ from graphlib import CycleError, TopologicalSorter
9
10
  from typing import Any, ClassVar, Literal, TypeVar, cast, overload
10
11
 
12
+ from cognite.client import data_modeling as dm
11
13
  from cognite.client.data_classes import data_modeling as dms
12
- from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier, ViewId
14
+ from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier, View, ViewId
13
15
  from cognite.client.utils.useful_types import SequenceNotStr
14
16
  from pydantic import ValidationError
15
17
  from rdflib import Namespace
@@ -25,8 +27,10 @@ from cognite.neat._constants import (
25
27
  DMS_RESERVED_PROPERTIES,
26
28
  get_default_prefixes_and_namespaces,
27
29
  )
28
- from cognite.neat._issues.errors import NeatValueError
29
- from cognite.neat._issues.warnings import NeatValueWarning
30
+ from cognite.neat._issues import IssueList
31
+ from cognite.neat._issues._factory import from_pydantic_errors
32
+ from cognite.neat._issues.errors import CDFMissingClientError, NeatValueError
33
+ from cognite.neat._issues.warnings import NeatValueWarning, PropertyOverwritingWarning
30
34
  from cognite.neat._issues.warnings._models import (
31
35
  SolutionModelBuildOnTopOfCDMWarning,
32
36
  )
@@ -60,9 +64,16 @@ from cognite.neat._rules.models.entities import (
60
64
  UnknownEntity,
61
65
  ViewEntity,
62
66
  )
63
- from cognite.neat._rules.models.information import InformationClass, InformationMetadata, InformationProperty
67
+ from cognite.neat._rules.models.information import (
68
+ InformationClass,
69
+ InformationInputClass,
70
+ InformationInputProperty,
71
+ InformationMetadata,
72
+ InformationProperty,
73
+ )
64
74
  from cognite.neat._utils.rdf_ import get_inheritance_path
65
- from cognite.neat._utils.text import NamingStandardization, title, to_camel_case, to_words
75
+ from cognite.neat._utils.spreadsheet import SpreadsheetRead
76
+ from cognite.neat._utils.text import NamingStandardization, humanize_collection, title, to_camel_case, to_words
66
77
 
67
78
  from ._base import RulesTransformer, T_VerifiedIn, T_VerifiedOut, VerifiedRulesTransformer
68
79
  from ._verification import VerifyDMSRules
@@ -522,13 +533,17 @@ class InformationToDMS(ConversionTransformer[InformationRules, DMSRules]):
522
533
  """Converts InformationRules to DMSRules."""
523
534
 
524
535
  def __init__(
525
- self, ignore_undefined_value_types: bool = False, reserved_properties: Literal["error", "warning"] = "error"
536
+ self,
537
+ ignore_undefined_value_types: bool = False,
538
+ reserved_properties: Literal["error", "warning"] = "error",
539
+ client: NeatClient | None = None,
526
540
  ):
527
541
  self.ignore_undefined_value_types = ignore_undefined_value_types
528
542
  self.reserved_properties = reserved_properties
543
+ self.client = client
529
544
 
530
545
  def transform(self, rules: InformationRules) -> DMSRules:
531
- return _InformationRulesConverter(rules).as_dms_rules(
546
+ return _InformationRulesConverter(rules, self.client).as_dms_rules(
532
547
  self.ignore_undefined_value_types, self.reserved_properties
533
548
  )
534
549
 
@@ -1347,9 +1362,10 @@ class MergeInformationRules(VerifiedRulesTransformer[InformationRules, Informati
1347
1362
  class _InformationRulesConverter:
1348
1363
  _start_or_end_node: ClassVar[frozenset[str]] = frozenset({"endNode", "end_node", "startNode", "start_node"})
1349
1364
 
1350
- def __init__(self, information: InformationRules):
1365
+ def __init__(self, information: InformationRules, client: NeatClient | None = None):
1351
1366
  self.rules = information
1352
1367
  self.property_count_by_container: dict[ContainerEntity, int] = defaultdict(int)
1368
+ self.client = client
1353
1369
 
1354
1370
  def as_dms_rules(
1355
1371
  self, ignore_undefined_value_types: bool = False, reserved_properties: Literal["error", "warning"] = "error"
@@ -1388,9 +1404,29 @@ class _InformationRulesConverter:
1388
1404
  and (prop.property_ == "endNode" or prop.property_ == "end_node")
1389
1405
  and isinstance(prop.value_type, ClassEntity)
1390
1406
  }
1407
+ ancestors_by_view: dict[ViewEntity, set[ViewEntity]] = {}
1408
+ parents_by_class = RulesAnalysis(self.rules).parents_by_class(
1409
+ include_ancestors=True, include_different_space=True
1410
+ )
1411
+ for cls_, parents in parents_by_class.items():
1412
+ view_type = cls_.as_view_entity(default_space, default_version)
1413
+ parent_views = {parent.as_view_entity(default_space, default_version) for parent in parents}
1414
+ if view_type in ancestors_by_view:
1415
+ ancestors_by_view[view_type].update(parent_views)
1416
+ else:
1417
+ ancestors_by_view[view_type] = parent_views
1418
+
1419
+ cognite_properties, cognite_containers, cognite_views = self._get_cognite_components()
1420
+ for view in cognite_views:
1421
+ if view in ancestors_by_view:
1422
+ ancestors_by_view[view].update(cognite_views[view])
1423
+ else:
1424
+ ancestors_by_view[view] = cognite_views[view]
1391
1425
 
1392
1426
  properties_by_class: dict[ClassEntity, list[DMSProperty]] = defaultdict(list)
1393
- referenced_containers: dict[ContainerEntity, Counter[ClassEntity]] = defaultdict(Counter)
1427
+ used_containers: dict[ContainerEntity, Counter[ClassEntity]] = defaultdict(Counter)
1428
+ used_cognite_containers: dict[ContainerEntity, DMSContainer] = {}
1429
+
1394
1430
  for prop in self.rules.properties:
1395
1431
  if ignore_undefined_value_types and isinstance(prop.value_type, UnknownEntity):
1396
1432
  continue
@@ -1403,17 +1439,34 @@ class _InformationRulesConverter:
1403
1439
  warnings.warn(NeatValueWarning(f"{msg} Skipping..."), stacklevel=2)
1404
1440
  continue
1405
1441
 
1406
- dms_property = self._as_dms_property(
1407
- prop,
1408
- default_space,
1409
- default_version,
1410
- edge_classes,
1411
- edge_value_types_by_class_property_pair,
1412
- end_node_by_edge,
1413
- )
1442
+ if cognite_property := self._find_cognite_property(
1443
+ prop.property_, parents_by_class[prop.class_], cognite_properties
1444
+ ):
1445
+ dms_property = self._customize_cognite_property(
1446
+ prop,
1447
+ cognite_property,
1448
+ prop.class_,
1449
+ default_space,
1450
+ default_version,
1451
+ ancestors_by_view,
1452
+ )
1453
+ if dms_property.container:
1454
+ if dms_property.container not in used_cognite_containers:
1455
+ used_cognite_containers[dms_property.container] = cognite_containers[dms_property.container]
1456
+ else:
1457
+ # Not matching any parent.
1458
+ dms_property = self._as_dms_property(
1459
+ prop,
1460
+ default_space,
1461
+ default_version,
1462
+ edge_classes,
1463
+ edge_value_types_by_class_property_pair,
1464
+ end_node_by_edge,
1465
+ )
1466
+ if dms_property.container:
1467
+ used_containers[dms_property.container][prop.class_] += 1
1468
+
1414
1469
  properties_by_class[prop.class_].append(dms_property)
1415
- if dms_property.container:
1416
- referenced_containers[dms_property.container][prop.class_] += 1
1417
1470
 
1418
1471
  views: list[DMSView] = []
1419
1472
 
@@ -1433,11 +1486,11 @@ class _InformationRulesConverter:
1433
1486
  existing_containers: set[ContainerEntity] = set()
1434
1487
 
1435
1488
  containers: list[DMSContainer] = []
1436
- for container_entity, class_entities in referenced_containers.items():
1489
+ for container_entity, class_entities in used_containers.items():
1437
1490
  if container_entity in existing_containers:
1438
1491
  continue
1439
1492
  constrains = self._create_container_constraint(
1440
- class_entities, default_space, class_by_entity, referenced_containers
1493
+ class_entities, default_space, class_by_entity, used_containers
1441
1494
  )
1442
1495
  most_used_class_entity = class_entities.most_common(1)[0][0]
1443
1496
  class_ = class_by_entity[most_used_class_entity]
@@ -1458,6 +1511,9 @@ class _InformationRulesConverter:
1458
1511
  )
1459
1512
  containers.append(container)
1460
1513
 
1514
+ if used_cognite_containers:
1515
+ containers.extend(used_cognite_containers.values())
1516
+
1461
1517
  dms_rules = DMSRules(
1462
1518
  metadata=dms_metadata,
1463
1519
  properties=SheetList[DMSProperty]([prop for prop_set in properties_by_class.values() for prop in prop_set]),
@@ -1469,6 +1525,35 @@ class _InformationRulesConverter:
1469
1525
 
1470
1526
  return dms_rules
1471
1527
 
1528
+ def _get_cognite_components(
1529
+ self,
1530
+ ) -> tuple[
1531
+ dict[tuple[ClassEntity, str], DMSProperty],
1532
+ dict[ContainerEntity, DMSContainer],
1533
+ dict[ViewEntity, set[ViewEntity]],
1534
+ ]:
1535
+ cognite_concepts = self._get_cognite_concepts()
1536
+ cognite_properties: dict[tuple[ClassEntity, str], DMSProperty] = {}
1537
+ cognite_containers: dict[ContainerEntity, DMSContainer] = {}
1538
+ cognite_views: dict[ViewEntity, set[ViewEntity]] = {}
1539
+ if cognite_concepts:
1540
+ if self.client is None:
1541
+ raise CDFMissingClientError(
1542
+ f"Cannot convert {self.rules.metadata.as_data_model_id()}. Missing Cognite Client."
1543
+ f"This is required as the data model is referencing cognite concepts in the implements"
1544
+ f"{humanize_collection(cognite_concepts)}"
1545
+ )
1546
+ cognite_rules = self._get_cognite_dms_rules(cognite_concepts, self.client)
1547
+
1548
+ cognite_properties = {
1549
+ (dms_prop.view.as_class(), dms_prop.view_property): dms_prop for dms_prop in cognite_rules.properties
1550
+ }
1551
+ cognite_containers = {container.container: container for container in cognite_rules.containers or []}
1552
+ cognite_views = RulesAnalysis(dms=cognite_rules).implements_by_view(
1553
+ include_ancestors=True, include_different_space=True
1554
+ )
1555
+ return cognite_properties, cognite_containers, cognite_views
1556
+
1472
1557
  @staticmethod
1473
1558
  def _create_container_constraint(
1474
1559
  class_entities: Counter[ClassEntity],
@@ -1563,6 +1648,74 @@ class _InformationRulesConverter:
1563
1648
 
1564
1649
  return dms_property
1565
1650
 
1651
+ @staticmethod
1652
+ def _customize_cognite_property(
1653
+ prop: InformationProperty,
1654
+ cognite_prop: DMSProperty,
1655
+ class_: ClassEntity,
1656
+ default_space: str,
1657
+ default_version: str,
1658
+ ancestors_by_view: dict[ViewEntity, set[ViewEntity]],
1659
+ ) -> DMSProperty:
1660
+ """Customize the cognite property to match the information property.
1661
+ This means updating the name and description of the cognite property with the information property.
1662
+ In addition, the value type can be updated given that the value type is matches the cognite property value
1663
+ type or in the case of a View Value type a derivative of the cognite property value type.
1664
+
1665
+ Args:
1666
+ prop: Information property
1667
+ cognite_prop: Cognite property
1668
+ class_: Class entity
1669
+ default_space: The default space
1670
+ default_version: The default version
1671
+ ancestors_by_view: Ancestors by view
1672
+
1673
+ Returns:
1674
+ DMSProperty: The customized cognite property
1675
+
1676
+ """
1677
+ value_type: DataType | ViewEntity | DMSUnknownEntity = cognite_prop.value_type
1678
+ if isinstance(prop.value_type, DataType) and prop.value_type != value_type:
1679
+ warnings.warn(
1680
+ PropertyOverwritingWarning(prop.property_, "property", "value type", (str(prop.value_type),)),
1681
+ stacklevel=2,
1682
+ )
1683
+ elif isinstance(prop.value_type, DataType):
1684
+ # User set the same value type as core concept.
1685
+ pass
1686
+ elif isinstance(prop.value_type, ClassEntity) and isinstance(cognite_prop.value_type, ViewEntity):
1687
+ view_type = prop.value_type.as_view_entity(default_space, default_version)
1688
+ ancestors = ancestors_by_view.get(view_type, set())
1689
+ if view_type == cognite_prop.value_type or cognite_prop.value_type in ancestors:
1690
+ value_type = view_type
1691
+ else:
1692
+ warnings.warn(
1693
+ NeatValueWarning(
1694
+ f"Invalid Value Type. The view {view_type} must implement "
1695
+ f"{humanize_collection(ancestors, bind_word='or')} "
1696
+ f"to be used as the Value Type in the {prop.class_!s}.{prop.property_}. "
1697
+ f"Skipping..."
1698
+ ),
1699
+ stacklevel=2,
1700
+ )
1701
+ else:
1702
+ warnings.warn(
1703
+ NeatValueWarning(
1704
+ f"Invalid Value Type. The {prop.value_type} is not supported as {prop.class_} implements"
1705
+ f"a cognite concepts. Will skip this, and use the {cognite_prop.value_type} instead."
1706
+ ),
1707
+ stacklevel=2,
1708
+ )
1709
+
1710
+ return cognite_prop.model_copy(
1711
+ update={
1712
+ "view": class_.as_view_entity(default_space, default_version),
1713
+ "name": prop.name or cognite_prop.name,
1714
+ "description": prop.description or cognite_prop.description,
1715
+ "value_type": value_type,
1716
+ }
1717
+ )
1718
+
1566
1719
  @staticmethod
1567
1720
  def _get_connection(
1568
1721
  prop: InformationProperty,
@@ -1703,6 +1856,48 @@ class _InformationRulesConverter:
1703
1856
 
1704
1857
  return data_types.String()
1705
1858
 
1859
+ def _get_cognite_concepts(self) -> set[ClassEntity]:
1860
+ return {cls_.class_ for cls_ in self.rules.classes if str(cls_.class_.prefix) in COGNITE_SPACES} | {
1861
+ parent
1862
+ for cls_ in self.rules.classes
1863
+ for parent in cls_.implements or []
1864
+ if str(parent.prefix) in COGNITE_SPACES
1865
+ }
1866
+
1867
+ @staticmethod
1868
+ def _get_cognite_dms_rules(concepts: set[ClassEntity], client: NeatClient) -> DMSRules:
1869
+ view_ids = [dm.ViewId(str(cls_.prefix), cls_.suffix, cls_.version) for cls_ in concepts]
1870
+ views = client.loaders.views.retrieve(view_ids, format="read", include_connected=True, include_ancestor=True)
1871
+ spaces = Counter(view.space for view in views)
1872
+ space = spaces.most_common(1)[0][0]
1873
+ model: dm.DataModel[dm.View] = dm.DataModel(
1874
+ space=space,
1875
+ external_id="CognitePlaceholderModel",
1876
+ version="v1",
1877
+ is_global=False,
1878
+ last_updated_time=1,
1879
+ created_time=1,
1880
+ name=None,
1881
+ description="This model is constructed to hold all properties/views/containers that are referenced"
1882
+ "by the data model being converted to DMS. This model is not meant to be used for any other"
1883
+ "purpose.",
1884
+ views=list(views),
1885
+ )
1886
+ unverified = DMSImporter.from_data_model(client, model).to_rules()
1887
+ if unverified.rules is None:
1888
+ raise NeatValueError("Failed to create CogniteConcepts")
1889
+ return unverified.rules.as_verified_rules()
1890
+
1891
+ @staticmethod
1892
+ def _find_cognite_property(
1893
+ property_: str, parents: set[ClassEntity], cognite_properties: dict[tuple[ClassEntity, str], DMSProperty]
1894
+ ) -> DMSProperty | None:
1895
+ """Find the parent class that has the property in the cognite properties"""
1896
+ for parent in parents:
1897
+ if (parent, property_) in cognite_properties:
1898
+ return cognite_properties[(parent, property_)]
1899
+ return None
1900
+
1706
1901
 
1707
1902
  class _DMSRulesConverter:
1708
1903
  def __init__(self, dms: DMSRules, instance_namespace: Namespace | None = None) -> None:
@@ -2017,3 +2212,131 @@ class SubsetInformationRules(VerifiedRulesTransformer[InformationRules, Informat
2017
2212
  return InformationRules.model_validate(subsetted_rules)
2018
2213
  except ValidationError as e:
2019
2214
  raise NeatValueError(f"Cannot subset rules: {e}") from e
2215
+
2216
+
2217
+ class AddCogniteProperties(RulesTransformer[ReadRules[InformationInputRules], ReadRules[InformationInputRules]]):
2218
+ """This transformer looks at the implements of the classes and adds all properties
2219
+ from the parent (and ancestors) classes that are not already included in the data model.
2220
+
2221
+ Args:
2222
+ client: The client is used to look up the properties of the parent classes.
2223
+
2224
+ """
2225
+
2226
+ def __init__(self, client: NeatClient) -> None:
2227
+ self._client = client
2228
+
2229
+ @property
2230
+ def description(self) -> str:
2231
+ """Get the description of the transformer."""
2232
+ return "Add Cognite properties for all concepts that implements a Cognite concept."
2233
+
2234
+ def transform(self, rules: ReadRules[InformationInputRules]) -> ReadRules[InformationInputRules]:
2235
+ input_ = rules.rules
2236
+ if input_ is None:
2237
+ raise NeatValueError("Rule read failed. Cannot add cognite properties to None rules.")
2238
+
2239
+ default_space = input_.metadata.space
2240
+ default_version = input_.metadata.version
2241
+
2242
+ dependencies_by_class = self._get_dependencies_by_class(input_.classes, rules.read_context, default_space)
2243
+ properties_by_class = self._get_properties_by_class(input_.properties, rules.read_context, default_space)
2244
+ cognite_implements_concepts = self._get_cognite_concepts(dependencies_by_class)
2245
+ views_by_class_entity = self._get_views_by_class(cognite_implements_concepts, default_space, default_version)
2246
+
2247
+ for class_entity, view in views_by_class_entity.items():
2248
+ for prop_id, view_prop in view.properties.items():
2249
+ if prop_id in properties_by_class[class_entity]:
2250
+ continue
2251
+ properties_by_class[class_entity][prop_id] = DMSImporter.as_information_input_property(
2252
+ class_entity, prop_id, view_prop
2253
+ )
2254
+
2255
+ try:
2256
+ topological_order = TopologicalSorter(dependencies_by_class).static_order()
2257
+ except CycleError as e:
2258
+ raise NeatValueError(f"Cycle detected in the class hierarchy: {e}") from e
2259
+
2260
+ new_properties: list[InformationInputProperty] = input_.properties.copy()
2261
+ for class_entity in topological_order:
2262
+ if class_entity not in dependencies_by_class:
2263
+ continue
2264
+ for parent in dependencies_by_class[class_entity]:
2265
+ for prop in properties_by_class[parent].values():
2266
+ if prop.property_ not in properties_by_class[class_entity]:
2267
+ new_prop = prop.copy(update={"Class": class_entity}, default_prefix=default_space)
2268
+ new_properties.append(new_prop)
2269
+ properties_by_class[class_entity][prop.property_] = new_prop
2270
+
2271
+ new_classes: list[InformationInputClass] = input_.classes.copy()
2272
+ existing_classes = {cls.class_ for cls in input_.classes}
2273
+ for class_entity, view in views_by_class_entity.items():
2274
+ if class_entity not in existing_classes:
2275
+ new_classes.append(DMSImporter.as_information_input_class(view))
2276
+ existing_classes.add(class_entity)
2277
+
2278
+ return ReadRules(
2279
+ rules=InformationInputRules(
2280
+ metadata=input_.metadata,
2281
+ properties=new_properties,
2282
+ classes=new_classes,
2283
+ prefixes=input_.prefixes,
2284
+ ),
2285
+ read_context={},
2286
+ )
2287
+
2288
+ @staticmethod
2289
+ def _get_properties_by_class(
2290
+ properties: list[InformationInputProperty], read_context: dict[str, SpreadsheetRead], default_space: str
2291
+ ) -> dict[ClassEntity, dict[str, InformationInputProperty]]:
2292
+ issues = IssueList()
2293
+ properties_by_class: dict[ClassEntity, dict[str, InformationInputProperty]] = defaultdict(dict)
2294
+ for prop in properties:
2295
+ try:
2296
+ dumped = prop.dump(default_prefix=default_space)
2297
+ except ValidationError as e:
2298
+ issues.extend(from_pydantic_errors(e.errors(), read_context))
2299
+ continue
2300
+ class_entity = cast(ClassEntity, dumped["Class"])
2301
+ properties_by_class[class_entity][prop.property_] = prop
2302
+ if issues.has_errors:
2303
+ raise issues.as_errors(operation="Reading properties")
2304
+ return properties_by_class
2305
+
2306
+ @staticmethod
2307
+ def _get_dependencies_by_class(
2308
+ classes: list[InformationInputClass], read_context: dict[str, SpreadsheetRead], default_space: str
2309
+ ) -> dict[ClassEntity, set[ClassEntity]]:
2310
+ dependencies_by_class: dict[ClassEntity, set[ClassEntity]] = {}
2311
+ issues = IssueList()
2312
+ for raw in classes:
2313
+ try:
2314
+ dumped = raw.dump(default_prefix=default_space)
2315
+ except ValidationError as e:
2316
+ issues.extend(from_pydantic_errors(e.errors(), read_context))
2317
+ continue
2318
+ class_entity = cast(ClassEntity, dumped["Class"])
2319
+ implements = cast(list[ClassEntity] | None, dumped["Implements"])
2320
+ dependencies_by_class[class_entity] = set(implements or [])
2321
+ if issues.has_errors:
2322
+ raise issues.as_errors(operation="Reading classes")
2323
+ return dependencies_by_class
2324
+
2325
+ @staticmethod
2326
+ def _get_cognite_concepts(dependencies_by_class: dict[ClassEntity, set[ClassEntity]]) -> set[ClassEntity]:
2327
+ cognite_implements_concepts = {
2328
+ dependency
2329
+ for dependencies in dependencies_by_class.values()
2330
+ for dependency in dependencies
2331
+ if dependency.prefix in COGNITE_SPACES
2332
+ }
2333
+ if not cognite_implements_concepts:
2334
+ raise NeatValueError("None of the classes implement Cognite Core concepts.")
2335
+ return cognite_implements_concepts
2336
+
2337
+ def _get_views_by_class(
2338
+ self, classes: set[ClassEntity], default_space: str, default_version: str
2339
+ ) -> dict[ClassEntity, View]:
2340
+ view_ids = [class_.as_view_entity(default_space, default_version).as_id() for class_ in classes]
2341
+ views = self._client.loaders.views.retrieve(view_ids, include_ancestor=True, include_connected=True)
2342
+ return {ClassEntity(prefix=view.space, suffix=view.external_id, version=view.version): view for view in views}
@@ -168,7 +168,7 @@ class NeatSession:
168
168
  self._state._raise_exception_if_condition_not_met(
169
169
  "Convert to physical", has_dms_rules=False, has_information_rules=True
170
170
  )
171
- converter = InformationToDMS(reserved_properties=reserved_properties)
171
+ converter = InformationToDMS(reserved_properties=reserved_properties, client=self._state.client)
172
172
 
173
173
  issues = self._state.rule_transform(converter)
174
174
 
@@ -1,16 +1,24 @@
1
- from typing import Literal
1
+ from pathlib import Path
2
+ from typing import Any, Literal
2
3
 
3
4
  from cognite.client.data_classes.data_modeling import DataModelIdentifier
4
5
 
5
- from cognite.neat._issues import IssueList
6
+ from cognite.neat._alpha import ExperimentalFlags
7
+ from cognite.neat._issues import IssueList, catch_issues
8
+ from cognite.neat._rules._shared import ReadRules
9
+ from cognite.neat._rules.exporters import ExcelExporter
10
+ from cognite.neat._rules.importers import ExcelImporter
11
+ from cognite.neat._rules.models import InformationInputRules
6
12
  from cognite.neat._rules.models.dms import DMSValidation
7
13
  from cognite.neat._rules.transformers import (
14
+ AddCogniteProperties,
8
15
  IncludeReferenced,
9
16
  ToDataProductModel,
10
17
  ToEnterpriseModel,
11
18
  ToSolutionModel,
12
19
  VerifiedRulesTransformer,
13
20
  )
21
+ from cognite.neat._utils.reader import NeatReader, PathReader
14
22
 
15
23
  from ._state import SessionState
16
24
  from .exceptions import NeatSessionError, session_class_wrapper
@@ -165,3 +173,52 @@ class TemplateAPI:
165
173
  if last_rules and not issues.has_errors:
166
174
  self._state.last_reference = last_rules
167
175
  return issues
176
+
177
+ def extension(self, io: Any, output: str | Path | None = None) -> IssueList:
178
+ """Creates a template for an extension of a Cognite model.
179
+
180
+ The input is a spreadsheet of a conceptual model in which the concepts are defined
181
+ and marked with the Cognite concept they are extending. For example, if you have a pump
182
+ in the Classes sheet you will see
183
+ ```
184
+ Class: Pump
185
+ Implements: cdf_cdm:CogniteAsset(version=v1)
186
+ ```
187
+ The output will be a spreadsheet in which all the properties from the Cognite concept model
188
+ is added to the spreadsheet. In the example above, the pump concept will have all
189
+ the properties it inherits from the CogniteAsset concept added to the Properties spreadsheet.
190
+
191
+
192
+ Args:
193
+ io: The input spreadsheet.
194
+ output: The output spreadsheet. If None, the output will be the same
195
+ as the input with `_extension` added to the name.
196
+ """
197
+ ExperimentalFlags.extension.warn()
198
+ reader = NeatReader.create(io)
199
+ path = reader.materialize_path()
200
+ if output is None:
201
+ if isinstance(reader, PathReader):
202
+ output_path = path.with_name(f"{path.stem}_extension{path.suffix}")
203
+ else:
204
+ # The source is not a file, for example, a URL or a stream.
205
+ output_path = Path.cwd() / f"{path.stem}_extension{path.suffix}"
206
+ else:
207
+ output_path = Path(output)
208
+
209
+ with catch_issues() as issues:
210
+ read: ReadRules[InformationInputRules] = ExcelImporter(path).to_rules()
211
+ if read.rules is not None:
212
+ # If rules are None there will be issues that are already caught.
213
+ if not isinstance(read.rules, InformationInputRules):
214
+ raise NeatSessionError(f"The input {reader.name} must contain an InformationInputRules object. ")
215
+ if self._state.client is None:
216
+ raise NeatSessionError("Client must be set in the session to run the extension.")
217
+ modified = AddCogniteProperties(self._state.client).transform(read)
218
+ if modified.rules is not None:
219
+ # If rules are None there will be issues that are already caught.
220
+ info = modified.rules.as_verified_rules()
221
+
222
+ ExcelExporter(styling="maximal").export_to_file(info, output_path)
223
+ issues.action = "Created extension template"
224
+ return issues
@@ -192,7 +192,29 @@ def replace_non_alphanumeric_with_underscore(text: str) -> str:
192
192
  return re.sub(r"\W+", "_", text)
193
193
 
194
194
 
195
- def humanize_collection(collection: Collection[Any], /, *, sort: bool = True) -> str:
195
+ def humanize_collection(collection: Collection[Any], /, *, sort: bool = True, bind_word: str = "and") -> str:
196
+ """Convert a collection of items to a human-readable string.
197
+
198
+ Args:
199
+ collection: The collection of items to convert.
200
+ sort: Whether to sort the collection before converting. Default is True.
201
+ bind_word: The word to use to bind the last two items. Default is "and".
202
+
203
+ Returns:
204
+ A human-readable string of the collection.
205
+
206
+ Examples:
207
+ >>> humanize_collection(["b", "c", "a"])
208
+ 'a, b and c'
209
+ >>> humanize_collection(["b", "c", "a"], sort=False)
210
+ 'b, c and a'
211
+ >>> humanize_collection(["a", "b"])
212
+ 'a and b'
213
+ >>> humanize_collection(["a"])
214
+ 'a'
215
+ >>> humanize_collection([])
216
+
217
+ """
196
218
  if not collection:
197
219
  return ""
198
220
  elif len(collection) == 1:
@@ -204,7 +226,7 @@ def humanize_collection(collection: Collection[Any], /, *, sort: bool = True) ->
204
226
  else:
205
227
  sequence = list(strings)
206
228
 
207
- return f"{', '.join(sequence[:-1])} and {sequence[-1]}"
229
+ return f"{', '.join(sequence[:-1])} {bind_word} {sequence[-1]}"
208
230
 
209
231
 
210
232
  class NamingStandardization:
cognite/neat/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.117.12"
1
+ __version__ = "0.119.0"
2
2
  __engine__ = "^2.0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cognite-neat
3
- Version: 0.117.12
3
+ Version: 0.119.0
4
4
  Summary: Knowledge graph transformation
5
5
  License: Apache-2.0
6
6
  Author: Nikola Vasiljevic
@@ -1,5 +1,5 @@
1
1
  cognite/neat/__init__.py,sha256=AXLMIF5t8RmjOpSaIlfqT8ltbPc__Tb8nAVbc1pkE0Q,176
2
- cognite/neat/_alpha.py,sha256=AVw3YiSwpOiV3fSoTbwTxd-p7WGlS-zgf4TPZCT_9vE,1163
2
+ cognite/neat/_alpha.py,sha256=LvvLIpOO5OBC-fIXkhe-ghEF1uSeEYoWvwT6-ajrhQw,1219
3
3
  cognite/neat/_client/__init__.py,sha256=RQ7MwL8mwGqGHokRzsPqO3XStDzmI4-c12_gz1UPJ74,177
4
4
  cognite/neat/_client/_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  cognite/neat/_client/_api/data_modeling_loaders.py,sha256=LsB8k2erMZ3BJ_wLMIBhav7ykimLkV2QJjnbeC2xikE,39790
@@ -77,7 +77,7 @@ cognite/neat/_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
77
77
  cognite/neat/_rules/_constants.py,sha256=XRcbXDyVlm9WkcMyJJUi8SfpLDWZTqERU7RMgZo3TSk,5922
78
78
  cognite/neat/_rules/_shared.py,sha256=6pl0qCp-2xn5CW6i6opD_6mVuXzblLpoc5323UNVvmY,1356
79
79
  cognite/neat/_rules/analysis/__init__.py,sha256=0Y7KiXpzrwOIU7GVlC3zlizj1TXe6sodAJzMh1rTZLE,62
80
- cognite/neat/_rules/analysis/_base.py,sha256=t9NBxZoM95_7UqOa9coWzsH0Kv6neKcb0dtebTV2KnI,23434
80
+ cognite/neat/_rules/analysis/_base.py,sha256=D6jjMlgwMpXsgwF1WSWtiU6QTJfocHlkHdmSUtf_IWE,23555
81
81
  cognite/neat/_rules/catalog/__init__.py,sha256=dwDB8b-5GKZuwVyPuiwsH0EaK2FY9-wJrkTjKoL8KE4,250
82
82
  cognite/neat/_rules/catalog/classic_model.xlsx,sha256=YkocpkKypizjsWYwOdn5yzIz_BSl8T8SQLxgm4GIjLQ,15014
83
83
  cognite/neat/_rules/catalog/hello_world_pump.xlsx,sha256=E63t5U1PQLIoUfXp1mEuhuq8I2TGKovZlEfIhO5bevw,23322
@@ -92,7 +92,7 @@ cognite/neat/_rules/exporters/_rules2yaml.py,sha256=ggaPR8FO8PwZk1_nhwb5wVHk_C4s
92
92
  cognite/neat/_rules/exporters/_validation.py,sha256=DVJGdNNU2WtAFgUg0h4SWVhveRErEPOcYdT65y5toV0,682
93
93
  cognite/neat/_rules/importers/__init__.py,sha256=KiTNglSK6OVCdvPiGmO2dTKEGQJWT5Yxa6XWqm7YClQ,1408
94
94
  cognite/neat/_rules/importers/_base.py,sha256=yLrrOgjtkSPfLACmtRXGlHmaeGkk0kGnowregWbIPUo,1940
95
- cognite/neat/_rules/importers/_dms2rules.py,sha256=DzlNZHjGLGvXylTxVBkGt4sKOuIPS5_WhTnYGh9Eldc,23393
95
+ cognite/neat/_rules/importers/_dms2rules.py,sha256=RyqRT7O-iEIxMeuDlVt2DcA8FQFm3Op2YvAWMTKLJjo,27524
96
96
  cognite/neat/_rules/importers/_dtdl2rules/__init__.py,sha256=CNR-sUihs2mnR1bPMKs3j3L4ds3vFTsrl6YycExZTfU,68
97
97
  cognite/neat/_rules/importers/_dtdl2rules/_unit_lookup.py,sha256=wW4saKva61Q_i17guY0dc4OseJDQfqHy_QZBtm0OD6g,12134
98
98
  cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py,sha256=jZJEMD_RDhKK4berSfNKypfhASTMhXW3GYXHyanWTPc,11715
@@ -125,18 +125,18 @@ cognite/neat/_rules/models/entities/_types.py,sha256=df9rnXJJKciv2Bp-Ve2q4xdEJt6
125
125
  cognite/neat/_rules/models/entities/_wrapped.py,sha256=L8ujfOHLQxkUzuJWHgdpPUCaHbjcCdDUiKsyApS7McU,7756
126
126
  cognite/neat/_rules/models/information/__init__.py,sha256=lV7l8RTfWBvN_DFkzea8OzADjq0rZGgWe_0Fiwtfje0,556
127
127
  cognite/neat/_rules/models/information/_rules.py,sha256=eb1_uunWmEnZV0rnuGq7-GyDICJU5yvGrzQNHSGoUKE,13561
128
- cognite/neat/_rules/models/information/_rules_input.py,sha256=Pk6DNwDfTUr5DbbuIaFj0ICS9XmwqbhYlaz1d96ouDk,6037
128
+ cognite/neat/_rules/models/information/_rules_input.py,sha256=zNw8zAuidGqGxnI9Ze_KrEur94dq9d0X-NhIS5ox7r4,6244
129
129
  cognite/neat/_rules/models/information/_validation.py,sha256=8o1rQ8lGY5yXP-ZiDX64opx7mHZGofHSxr-6972xEmI,12170
130
130
  cognite/neat/_rules/models/mapping/__init__.py,sha256=T68Hf7rhiXa7b03h4RMwarAmkGnB-Bbhc1H07b2PyC4,100
131
131
  cognite/neat/_rules/models/mapping/_classic2core.py,sha256=AhLWoXk4PlBVA6SgBtY9dcLcpqEMaYcsiEatI19miPk,1211
132
132
  cognite/neat/_rules/models/mapping/_classic2core.yaml,sha256=ei-nuivNWVW9HmvzDBKIPF6ZdgaMq64XHw_rKm0CMxg,22584
133
- cognite/neat/_rules/transformers/__init__.py,sha256=vp6OQnRUGdIrPVDOsY_tM2KBzis-IldaoVkAQEFgvVM,1563
133
+ cognite/neat/_rules/transformers/__init__.py,sha256=icjtZq6Hc122fEj9AGEcDB8QuF2JL6dWe4utne73naQ,1617
134
134
  cognite/neat/_rules/transformers/_base.py,sha256=9LnjKbYIf9238PQXedkTZsMXAyEND6glUD187iEaHfc,2783
135
- cognite/neat/_rules/transformers/_converters.py,sha256=cCMvSrk6LPMyA3k9-4j0qCNi9FdsWRhSifKnr8j4UmQ,89385
135
+ cognite/neat/_rules/transformers/_converters.py,sha256=pel46GyIBVKeYb_GoflHc0uq5pVPayc2ePrIBwNb0fk,104666
136
136
  cognite/neat/_rules/transformers/_mapping.py,sha256=QVd96vMF9sGailN5hB22KGdmOU8GzFwZ7IPMD0aVCYY,18237
137
137
  cognite/neat/_rules/transformers/_verification.py,sha256=coZjoLqdfS8yrPP62T_xEKrDZc9ETbPWrLuwEVBSLZQ,4370
138
138
  cognite/neat/_session/__init__.py,sha256=fxQ5URVlUnmEGYyB8Baw7IDq-uYacqkigbc4b-Pr9Fw,58
139
- cognite/neat/_session/_base.py,sha256=7NRswQdQOdB-2Cb-WavzzKZDyEOpJBCcO18KTCGu9Tw,12317
139
+ cognite/neat/_session/_base.py,sha256=WrX4g-pf4GnnYpusSoNkmKyjH_IfB9ZWfXw34THMqy0,12344
140
140
  cognite/neat/_session/_collector.py,sha256=ipYlwBBgjF53adjDW_D1U39mLEviB3UOVSGgeqHkEuc,4228
141
141
  cognite/neat/_session/_drop.py,sha256=gOkDAnddASpFxYxkPjlTyhkpNfnmDEj94GRI8tnHFR0,4167
142
142
  cognite/neat/_session/_explore.py,sha256=hrL0ASLtEXLlZn0dgDsKNySO10qEMBT8cE8mti2lOws,1561
@@ -149,7 +149,7 @@ cognite/neat/_session/_set.py,sha256=dCZ5zEmNAw8tiqOGT7-EigSXOIGlfVP2ldA7nmC8LJ8
149
149
  cognite/neat/_session/_show.py,sha256=2lnkud996ouwf6-aKGvU0cU0ttfMeQ3vcb__g_7Yko4,10539
150
150
  cognite/neat/_session/_state.py,sha256=6b0BSXm7m5-OcsF_O0A_7ec9tt8oPqH8zHImyEhVQQA,6313
151
151
  cognite/neat/_session/_subset.py,sha256=4RFGC8apNLnRSKcoDh95ksXA7zLy8vGLxEV5U0H3Hoc,2687
152
- cognite/neat/_session/_template.py,sha256=A0mx65fs-07eJcRk46LklRBmTUtO181EvOwsz74G4go,7538
152
+ cognite/neat/_session/_template.py,sha256=oR11KXyEGUGKjKthsehK7-3xI2n3_3ZAsRQ66wHXs8o,10458
153
153
  cognite/neat/_session/_to.py,sha256=S7209afgOlLWbsKOA0Z4lAwT57gzLmSDaUWpUaWjJJY,19118
154
154
  cognite/neat/_session/_wizard.py,sha256=9idlzhZy54h2Iwupe9iXKX3RDb5jJQuBZFEouni50L0,1476
155
155
  cognite/neat/_session/engine/__init__.py,sha256=D3MxUorEs6-NtgoICqtZ8PISQrjrr4dvca6n48bu_bI,120
@@ -173,14 +173,14 @@ cognite/neat/_utils/rdf_.py,sha256=eCN1HBuL4NvPwE2WTnkz-6evnECQ8PveLqYAcpBfA9E,9
173
173
  cognite/neat/_utils/reader/__init__.py,sha256=fPkrNB_9hLB7CyHTCFV_xEbIfOMqUQzNly5JN33-QfM,146
174
174
  cognite/neat/_utils/reader/_base.py,sha256=PpD7OKVPTMHF6mqBY7L3XcUw3nv-drEbOzBoSSildlM,5412
175
175
  cognite/neat/_utils/spreadsheet.py,sha256=Jo1ib9RgbHkAoekO5Yu1-_nF_O6pXhsyBGNgInQwkts,5378
176
- cognite/neat/_utils/text.py,sha256=qy7lgMdRjzxSYkL8teAnWsq6T5baS_QcezHLK007_7M,7791
176
+ cognite/neat/_utils/text.py,sha256=9T0mzcNn6J9X8DriNntLN5ThCXOWbU1BYareukbLT7A,8507
177
177
  cognite/neat/_utils/time_.py,sha256=7ayUm0OWZm1JDmy32E4ip8WRr2o0GLwrHwJA8sJ43Z4,357
178
178
  cognite/neat/_utils/upload.py,sha256=xWtM6mFuD2QYQHaZ7zCAuGptbEpPIxcH-raWQu93-Ug,5845
179
179
  cognite/neat/_utils/xml_.py,sha256=FQkq84u35MUsnKcL6nTMJ9ajtG9D5i1u4VBnhGqP2DQ,1710
180
- cognite/neat/_version.py,sha256=UiPaIrCQ2Dtlfchy7Yn9lrRzE0x1d8c6iWHSSVJ1chY,47
180
+ cognite/neat/_version.py,sha256=TJTHDgA0OrwVmqyL7--NeoKjn6AUK1SpOFTw5WFlYms,46
181
181
  cognite/neat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
- cognite_neat-0.117.12.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
183
- cognite_neat-0.117.12.dist-info/METADATA,sha256=CrA6mTX8srnTM6XDzWoLx632BTbkkucd4lIpQ2IvNWg,5422
184
- cognite_neat-0.117.12.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
185
- cognite_neat-0.117.12.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
186
- cognite_neat-0.117.12.dist-info/RECORD,,
182
+ cognite_neat-0.119.0.dist-info/LICENSE,sha256=W8VmvFia4WHa3Gqxq1Ygrq85McUNqIGDVgtdvzT-XqA,11351
183
+ cognite_neat-0.119.0.dist-info/METADATA,sha256=83ednd4awU-v4LrLtW-WNmIV9LE0HAe0C_hkKQup5wo,5421
184
+ cognite_neat-0.119.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
185
+ cognite_neat-0.119.0.dist-info/entry_points.txt,sha256=SsQlnl8SNMSSjE3acBI835JYFtsIinLSbVmHmMEXv6E,51
186
+ cognite_neat-0.119.0.dist-info/RECORD,,