cognite-neat 0.108.0__py3-none-any.whl → 0.109.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/_constants.py +1 -1
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +8 -4
- cognite/neat/_graph/queries/_base.py +4 -0
- cognite/neat/_graph/transformers/__init__.py +3 -3
- cognite/neat/_graph/transformers/_base.py +4 -4
- cognite/neat/_graph/transformers/_classic_cdf.py +13 -13
- cognite/neat/_graph/transformers/_prune_graph.py +3 -3
- cognite/neat/_graph/transformers/_rdfpath.py +3 -4
- cognite/neat/_graph/transformers/_value_type.py +23 -16
- cognite/neat/_issues/errors/__init__.py +2 -0
- cognite/neat/_issues/errors/_external.py +8 -0
- cognite/neat/_issues/warnings/_resources.py +1 -1
- cognite/neat/_rules/exporters/_rules2yaml.py +1 -1
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +179 -118
- cognite/neat/_rules/models/_base_rules.py +9 -8
- cognite/neat/_rules/models/dms/_exporter.py +5 -4
- cognite/neat/_rules/transformers/__init__.py +4 -3
- cognite/neat/_rules/transformers/_base.py +6 -1
- cognite/neat/_rules/transformers/_converters.py +436 -361
- cognite/neat/_rules/transformers/_mapping.py +4 -4
- cognite/neat/_session/_base.py +71 -69
- cognite/neat/_session/_create.py +133 -0
- cognite/neat/_session/_drop.py +55 -1
- cognite/neat/_session/_fix.py +28 -0
- cognite/neat/_session/_inspect.py +19 -5
- cognite/neat/_session/_mapping.py +8 -8
- cognite/neat/_session/_prepare.py +3 -247
- cognite/neat/_session/_read.py +78 -4
- cognite/neat/_session/_set.py +34 -12
- cognite/neat/_session/_show.py +14 -41
- cognite/neat/_session/_state.py +48 -51
- cognite/neat/_session/_to.py +7 -3
- cognite/neat/_session/exceptions.py +7 -1
- cognite/neat/_store/_graph_store.py +14 -13
- cognite/neat/_store/_provenance.py +36 -20
- cognite/neat/_store/_rules_store.py +172 -293
- cognite/neat/_store/exceptions.py +40 -4
- cognite/neat/_utils/auth.py +4 -2
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/RECORD +44 -42
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.108.0.dist-info → cognite_neat-0.109.0.dist-info}/entry_points.txt +0 -0
|
@@ -7,10 +7,10 @@ from pathlib import Path
|
|
|
7
7
|
from typing import Any, ClassVar, cast
|
|
8
8
|
|
|
9
9
|
from cognite.client import data_modeling as dm
|
|
10
|
-
from rdflib import RDF, Graph, Namespace, URIRef
|
|
10
|
+
from rdflib import RDF, RDFS, Graph, Namespace, URIRef
|
|
11
11
|
from rdflib import Literal as RdfLiteral
|
|
12
12
|
|
|
13
|
-
from cognite.neat._constants import NEAT
|
|
13
|
+
from cognite.neat._constants import NEAT, get_default_prefixes_and_namespaces
|
|
14
14
|
from cognite.neat._issues import IssueList
|
|
15
15
|
from cognite.neat._issues.warnings import PropertyValueTypeUndefinedWarning
|
|
16
16
|
from cognite.neat._rules.analysis import InformationAnalysis
|
|
@@ -81,7 +81,7 @@ class InferenceImporter(BaseRDFImporter):
|
|
|
81
81
|
def from_graph_store(
|
|
82
82
|
cls,
|
|
83
83
|
store: NeatGraphStore,
|
|
84
|
-
data_model_id:
|
|
84
|
+
data_model_id: dm.DataModelId | tuple[str, str, str] = DEFAULT_INFERENCE_DATA_MODEL_ID,
|
|
85
85
|
max_number_of_instance: int = -1,
|
|
86
86
|
non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
|
|
87
87
|
language: str = "en",
|
|
@@ -194,6 +194,7 @@ class InferenceImporter(BaseRDFImporter):
|
|
|
194
194
|
INSTANCE_PROPERTIES_DEFINITION.replace("instance_id", instance)
|
|
195
195
|
): # type: ignore[misc]
|
|
196
196
|
# this is to skip rdf:type property
|
|
197
|
+
|
|
197
198
|
if property_uri == RDF.type:
|
|
198
199
|
continue
|
|
199
200
|
property_id = remove_namespace_from_uri(property_uri)
|
|
@@ -245,13 +246,8 @@ class InferenceImporter(BaseRDFImporter):
|
|
|
245
246
|
elif id_ in properties and definition["value_type"] not in properties[id_]["value_type"]:
|
|
246
247
|
properties[id_]["value_type"].add(definition["value_type"])
|
|
247
248
|
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
id_ in properties
|
|
251
|
-
and definition["value_type"] in properties[id_]["value_type"]
|
|
252
|
-
and properties[id_]["max_count"] != definition["max_count"]
|
|
253
|
-
):
|
|
254
|
-
properties[id_]["max_count"] = max(properties[id_]["max_count"], definition["max_count"])
|
|
249
|
+
# always update max_count with the upmost value
|
|
250
|
+
properties[id_]["max_count"] = max(properties[id_]["max_count"], definition["max_count"])
|
|
255
251
|
|
|
256
252
|
# Create multi-value properties otherwise single value
|
|
257
253
|
for property_ in properties.values():
|
|
@@ -292,11 +288,10 @@ class InferenceImporter(BaseRDFImporter):
|
|
|
292
288
|
# Internal helper class
|
|
293
289
|
@dataclass
|
|
294
290
|
class _ReadProperties:
|
|
295
|
-
|
|
296
|
-
subclass_uri: URIRef
|
|
291
|
+
type_uri: URIRef
|
|
297
292
|
property_uri: URIRef
|
|
298
|
-
|
|
299
|
-
|
|
293
|
+
value_type: URIRef
|
|
294
|
+
parent_uri: URIRef | None
|
|
300
295
|
max_occurrence: int
|
|
301
296
|
instance_count: int
|
|
302
297
|
|
|
@@ -315,7 +310,6 @@ class SubclassInferenceImporter(BaseRDFImporter):
|
|
|
315
310
|
Args:
|
|
316
311
|
issue_list: Issue list to store issues
|
|
317
312
|
graph: Knowledge graph
|
|
318
|
-
max_number_of_instance: Maximum number of instances to be used in inference
|
|
319
313
|
"""
|
|
320
314
|
|
|
321
315
|
overwrite_data_types: ClassVar[Mapping[URIRef, URIRef]] = {
|
|
@@ -323,35 +317,25 @@ class SubclassInferenceImporter(BaseRDFImporter):
|
|
|
323
317
|
data_types.Float.as_xml_uri_ref(): data_types.Double.as_xml_uri_ref(),
|
|
324
318
|
}
|
|
325
319
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
graph: Graph,
|
|
330
|
-
rules: InformationRules,
|
|
331
|
-
max_number_of_instance: int,
|
|
332
|
-
non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
|
|
333
|
-
) -> None:
|
|
334
|
-
super().__init__(
|
|
335
|
-
issue_list,
|
|
336
|
-
graph,
|
|
337
|
-
rules.metadata.as_data_model_id().as_tuple(), # type: ignore[arg-type]
|
|
338
|
-
max_number_of_instance,
|
|
339
|
-
non_existing_node_type,
|
|
340
|
-
language="en",
|
|
341
|
-
)
|
|
342
|
-
self._rules = rules
|
|
320
|
+
_ordered_class_query = """SELECT DISTINCT ?class (count(?s) as ?instances )
|
|
321
|
+
WHERE { ?s a ?class }
|
|
322
|
+
group by ?class order by DESC(?instances)"""
|
|
343
323
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
324
|
+
_type_parent_query = f"""SELECT ?parent ?type
|
|
325
|
+
WHERE {{ ?s a ?type .
|
|
326
|
+
?type <{RDFS.subClassOf}> ?parent }}"""
|
|
347
327
|
|
|
348
|
-
_properties_query = """SELECT DISTINCT ?property ?
|
|
328
|
+
_properties_query = """SELECT DISTINCT ?property ?valueType
|
|
349
329
|
WHERE {{
|
|
350
330
|
?s a <{type}> .
|
|
351
|
-
?s
|
|
352
|
-
|
|
353
|
-
BIND(
|
|
354
|
-
|
|
331
|
+
?s ?property ?object .
|
|
332
|
+
OPTIONAL {{ ?object a ?objectType }}
|
|
333
|
+
BIND(
|
|
334
|
+
IF(
|
|
335
|
+
isLiteral(?object), datatype(?object),
|
|
336
|
+
IF(BOUND(?objectType), ?objectType, <{unknown_type}>)
|
|
337
|
+
) AS ?valueType
|
|
338
|
+
)
|
|
355
339
|
}}"""
|
|
356
340
|
|
|
357
341
|
_max_occurrence_query = """SELECT (MAX(?count) AS ?maxCount)
|
|
@@ -360,130 +344,194 @@ class SubclassInferenceImporter(BaseRDFImporter):
|
|
|
360
344
|
SELECT ?subject (COUNT(?object) AS ?count)
|
|
361
345
|
WHERE {{
|
|
362
346
|
?subject a <{type}> .
|
|
363
|
-
?subject <{neat_type}> <{subtype}> .
|
|
364
347
|
?subject <{property}> ?object .
|
|
365
348
|
}}
|
|
366
349
|
GROUP BY ?subject
|
|
367
350
|
}}
|
|
368
351
|
}}"""
|
|
369
352
|
|
|
353
|
+
def __init__(
|
|
354
|
+
self,
|
|
355
|
+
issue_list: IssueList,
|
|
356
|
+
graph: Graph,
|
|
357
|
+
rules: InformationRules | None = None,
|
|
358
|
+
data_model_id: dm.DataModelId | tuple[str, str, str] | None = None,
|
|
359
|
+
non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
|
|
360
|
+
) -> None:
|
|
361
|
+
if sum([1 for v in [rules, data_model_id] if v is not None]) != 1:
|
|
362
|
+
raise ValueError("Exactly one of rules or data_model_id must be provided.")
|
|
363
|
+
if data_model_id is not None:
|
|
364
|
+
identifier = data_model_id
|
|
365
|
+
elif rules is not None:
|
|
366
|
+
identifier = rules.metadata.as_data_model_id().as_tuple() # type: ignore[assignment]
|
|
367
|
+
else:
|
|
368
|
+
raise ValueError("Exactly one of rules or data_model_id must be provided.")
|
|
369
|
+
super().__init__(issue_list, graph, identifier, -1, non_existing_node_type, language="en")
|
|
370
|
+
self._rules = rules
|
|
371
|
+
|
|
370
372
|
def _to_rules_components(
|
|
371
373
|
self,
|
|
372
374
|
) -> dict:
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
375
|
+
if self._rules:
|
|
376
|
+
prefixes = self._rules.prefixes.copy()
|
|
377
|
+
else:
|
|
378
|
+
prefixes = get_default_prefixes_and_namespaces()
|
|
379
|
+
|
|
380
|
+
parent_by_child = self._read_parent_by_child_from_graph()
|
|
381
|
+
read_properties = self._read_class_properties_from_graph(parent_by_child)
|
|
382
|
+
classes, properties = self._create_classes_properties(read_properties, prefixes)
|
|
383
|
+
|
|
384
|
+
if self._rules:
|
|
385
|
+
metadata = self._rules.metadata.model_dump()
|
|
386
|
+
default_space = self._rules.metadata.prefix
|
|
387
|
+
else:
|
|
388
|
+
metadata = self._default_metadata()
|
|
389
|
+
default_space = metadata["space"]
|
|
390
|
+
return {
|
|
391
|
+
"metadata": metadata,
|
|
392
|
+
"classes": [cls.dump(default_space) for cls in classes],
|
|
393
|
+
"properties": [prop.dump(default_space) for prop in properties],
|
|
394
|
+
"prefixes": prefixes,
|
|
395
|
+
}
|
|
376
396
|
|
|
397
|
+
def _create_classes_properties(
|
|
398
|
+
self, read_properties: list[_ReadProperties], prefixes: dict[str, Namespace]
|
|
399
|
+
) -> tuple[list[InformationInputClass], list[InformationInputProperty]]:
|
|
400
|
+
if self._rules:
|
|
401
|
+
existing_classes = {class_.class_.suffix: class_ for class_ in self._rules.classes}
|
|
402
|
+
else:
|
|
403
|
+
existing_classes = {}
|
|
377
404
|
classes: list[InformationInputClass] = []
|
|
378
405
|
properties: list[InformationInputProperty] = []
|
|
379
406
|
# Help for IDE
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
407
|
+
type_uri: URIRef
|
|
408
|
+
parent_uri: URIRef
|
|
409
|
+
for parent_uri, parent_class_properties_iterable in itertools.groupby(
|
|
410
|
+
sorted(read_properties, key=lambda x: x.parent_uri or NEAT.EmptyType),
|
|
411
|
+
key=lambda x: x.parent_uri or NEAT.EmptyType,
|
|
383
412
|
):
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
shared_property_uris = set.intersection(
|
|
387
|
-
*[
|
|
388
|
-
set(properties_by_property.keys())
|
|
389
|
-
for properties_by_property in properties_by_subclass_by_property.values()
|
|
390
|
-
]
|
|
413
|
+
properties_by_class_by_property = self._get_properties_by_class_by_property(
|
|
414
|
+
parent_class_properties_iterable
|
|
391
415
|
)
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if
|
|
395
|
-
|
|
416
|
+
|
|
417
|
+
parent_suffix: str | None = None
|
|
418
|
+
if parent_uri != NEAT.EmptyType:
|
|
419
|
+
shared_property_uris = set.intersection(
|
|
420
|
+
*[
|
|
421
|
+
set(properties_by_property.keys())
|
|
422
|
+
for properties_by_property in properties_by_class_by_property.values()
|
|
423
|
+
]
|
|
424
|
+
)
|
|
425
|
+
parent_suffix = remove_namespace_from_uri(parent_uri)
|
|
426
|
+
self._add_uri_namespace_to_prefixes(parent_uri, prefixes)
|
|
427
|
+
if parent_suffix not in existing_classes:
|
|
428
|
+
classes.append(InformationInputClass(class_=parent_suffix))
|
|
429
|
+
else:
|
|
430
|
+
classes.append(InformationInputClass.load(existing_classes[parent_suffix].model_dump()))
|
|
396
431
|
else:
|
|
397
|
-
|
|
432
|
+
shared_property_uris = set()
|
|
398
433
|
shared_properties: dict[URIRef, list[_ReadProperties]] = defaultdict(list)
|
|
399
|
-
for
|
|
400
|
-
|
|
401
|
-
self._add_uri_namespace_to_prefixes(
|
|
402
|
-
|
|
403
|
-
|
|
434
|
+
for type_uri, properties_by_property_uri in properties_by_class_by_property.items():
|
|
435
|
+
class_suffix = remove_namespace_from_uri(type_uri)
|
|
436
|
+
self._add_uri_namespace_to_prefixes(type_uri, prefixes)
|
|
437
|
+
|
|
438
|
+
if class_suffix not in existing_classes:
|
|
439
|
+
classes.append(
|
|
440
|
+
InformationInputClass(
|
|
441
|
+
class_=class_suffix,
|
|
442
|
+
implements=parent_suffix,
|
|
443
|
+
)
|
|
444
|
+
)
|
|
404
445
|
else:
|
|
405
|
-
classes.append(InformationInputClass.load(existing_classes[
|
|
446
|
+
classes.append(InformationInputClass.load(existing_classes[class_suffix].model_dump()))
|
|
406
447
|
for property_uri, read_properties in properties_by_property_uri.items():
|
|
407
448
|
if property_uri in shared_property_uris:
|
|
408
449
|
shared_properties[property_uri].extend(read_properties)
|
|
409
450
|
continue
|
|
410
451
|
properties.append(
|
|
411
|
-
self._create_property(read_properties,
|
|
452
|
+
self._create_property(read_properties, class_suffix, type_uri, property_uri, prefixes)
|
|
412
453
|
)
|
|
413
|
-
for property_uri, read_properties in shared_properties.items():
|
|
414
|
-
properties.append(
|
|
415
|
-
self._create_property(read_properties, class_suffix, class_uri, property_uri, prefixes)
|
|
416
|
-
)
|
|
417
454
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
455
|
+
if parent_suffix:
|
|
456
|
+
for property_uri, read_properties in shared_properties.items():
|
|
457
|
+
properties.append(
|
|
458
|
+
self._create_property(
|
|
459
|
+
read_properties, parent_suffix, read_properties[0].type_uri, property_uri, prefixes
|
|
460
|
+
)
|
|
461
|
+
)
|
|
462
|
+
return classes, properties
|
|
424
463
|
|
|
425
464
|
@staticmethod
|
|
426
|
-
def
|
|
427
|
-
|
|
465
|
+
def _get_properties_by_class_by_property(
|
|
466
|
+
parent_class_properties_iterable: Iterable[_ReadProperties],
|
|
428
467
|
) -> dict[URIRef, dict[URIRef, list[_ReadProperties]]]:
|
|
429
|
-
|
|
430
|
-
for
|
|
431
|
-
|
|
468
|
+
properties_by_class_by_property: dict[URIRef, dict[URIRef, list[_ReadProperties]]] = {}
|
|
469
|
+
for class_uri, class_properties_iterable in itertools.groupby(
|
|
470
|
+
sorted(parent_class_properties_iterable, key=lambda x: x.type_uri), key=lambda x: x.type_uri
|
|
432
471
|
):
|
|
433
|
-
|
|
434
|
-
for read_prop in
|
|
435
|
-
|
|
436
|
-
return
|
|
437
|
-
|
|
438
|
-
def _read_class_properties_from_graph(self) -> list[_ReadProperties]:
|
|
439
|
-
|
|
440
|
-
# Infers all the classes
|
|
441
|
-
for result_row in self.graph.query(self.
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
472
|
+
properties_by_class_by_property[class_uri] = defaultdict(list)
|
|
473
|
+
for read_prop in class_properties_iterable:
|
|
474
|
+
properties_by_class_by_property[class_uri][read_prop.property_uri].append(read_prop)
|
|
475
|
+
return properties_by_class_by_property
|
|
476
|
+
|
|
477
|
+
def _read_class_properties_from_graph(self, parent_by_child: dict[URIRef, URIRef]) -> list[_ReadProperties]:
|
|
478
|
+
count_by_type: dict[URIRef, int] = {}
|
|
479
|
+
# Infers all the classes in the graph
|
|
480
|
+
for result_row in self.graph.query(self._ordered_class_query):
|
|
481
|
+
type_uri, instance_count_literal = cast(tuple[URIRef, RdfLiteral], result_row)
|
|
482
|
+
count_by_type[type_uri] = instance_count_literal.toPython()
|
|
483
|
+
if self._rules:
|
|
484
|
+
analysis = InformationAnalysis(self._rules)
|
|
485
|
+
existing_class_properties = {
|
|
486
|
+
(class_entity.suffix, prop.property_)
|
|
487
|
+
for class_entity, properties in analysis.classes_with_properties(
|
|
488
|
+
consider_inheritance=True, allow_different_namespace=True
|
|
489
|
+
).items()
|
|
490
|
+
for prop in properties
|
|
491
|
+
}
|
|
492
|
+
else:
|
|
493
|
+
existing_class_properties = set()
|
|
450
494
|
properties_by_class_by_subclass: list[_ReadProperties] = []
|
|
451
|
-
for
|
|
452
|
-
property_query = self._properties_query.format(type=
|
|
453
|
-
class_suffix = remove_namespace_from_uri(
|
|
495
|
+
for type_uri, instance_count in count_by_type.items():
|
|
496
|
+
property_query = self._properties_query.format(type=type_uri, unknown_type=NEAT.UnknownType)
|
|
497
|
+
class_suffix = remove_namespace_from_uri(type_uri)
|
|
454
498
|
for result_row in self.graph.query(property_query):
|
|
455
|
-
property_uri,
|
|
456
|
-
if property_uri == RDF.type
|
|
499
|
+
property_uri, value_type_uri = cast(tuple[URIRef, URIRef], result_row)
|
|
500
|
+
if property_uri == RDF.type:
|
|
457
501
|
continue
|
|
458
502
|
property_str = remove_namespace_from_uri(property_uri)
|
|
459
503
|
if (class_suffix, property_str) in existing_class_properties:
|
|
460
504
|
continue
|
|
461
|
-
occurrence_query = self._max_occurrence_query.format(
|
|
462
|
-
type=class_uri, subtype=subclass_uri, property=property_uri, neat_type=NEAT.type
|
|
463
|
-
)
|
|
505
|
+
occurrence_query = self._max_occurrence_query.format(type=type_uri, property=property_uri)
|
|
464
506
|
max_occurrence = 1 # default value
|
|
465
|
-
|
|
466
|
-
if
|
|
467
|
-
max_occurrence_literal, *__ = cast(tuple[RdfLiteral, Any],
|
|
507
|
+
occurrence_row, *_ = list(self.graph.query(occurrence_query))
|
|
508
|
+
if occurrence_row:
|
|
509
|
+
max_occurrence_literal, *__ = cast(tuple[RdfLiteral, Any], occurrence_row)
|
|
468
510
|
max_occurrence = int(max_occurrence_literal.toPython())
|
|
469
511
|
properties_by_class_by_subclass.append(
|
|
470
512
|
_ReadProperties(
|
|
471
|
-
|
|
472
|
-
subclass_uri=subclass_uri,
|
|
513
|
+
type_uri=type_uri,
|
|
473
514
|
property_uri=property_uri,
|
|
474
|
-
|
|
475
|
-
|
|
515
|
+
parent_uri=parent_by_child.get(type_uri),
|
|
516
|
+
value_type=value_type_uri,
|
|
476
517
|
max_occurrence=max_occurrence,
|
|
477
518
|
instance_count=instance_count,
|
|
478
519
|
)
|
|
479
520
|
)
|
|
480
521
|
return properties_by_class_by_subclass
|
|
481
522
|
|
|
523
|
+
def _read_parent_by_child_from_graph(self) -> dict[URIRef, URIRef]:
|
|
524
|
+
parent_by_child: dict[URIRef, URIRef] = {}
|
|
525
|
+
for result_row in self.graph.query(self._type_parent_query):
|
|
526
|
+
parent_uri, child_uri = cast(tuple[URIRef, URIRef], result_row)
|
|
527
|
+
parent_by_child[child_uri] = parent_uri
|
|
528
|
+
return parent_by_child
|
|
529
|
+
|
|
482
530
|
def _create_property(
|
|
483
531
|
self,
|
|
484
532
|
read_properties: list[_ReadProperties],
|
|
485
533
|
class_suffix: str,
|
|
486
|
-
|
|
534
|
+
type_uri: URIRef,
|
|
487
535
|
property_uri: URIRef,
|
|
488
536
|
prefixes: dict[str, Namespace],
|
|
489
537
|
) -> InformationInputProperty:
|
|
@@ -497,17 +545,17 @@ class SubclassInferenceImporter(BaseRDFImporter):
|
|
|
497
545
|
property_=property_name,
|
|
498
546
|
max_count=first.max_occurrence,
|
|
499
547
|
value_type=value_type,
|
|
500
|
-
instance_source=(f"{uri_to_short_form(
|
|
548
|
+
instance_source=(f"{uri_to_short_form(type_uri, prefixes)}({uri_to_short_form(property_uri, prefixes)})"),
|
|
501
549
|
)
|
|
502
550
|
|
|
503
551
|
def _get_value_type(
|
|
504
552
|
self, read_properties: list[_ReadProperties], prefixes: dict[str, Namespace]
|
|
505
553
|
) -> str | UnknownEntity:
|
|
506
|
-
value_types = {prop.
|
|
507
|
-
prop.object_type for prop in read_properties if prop.object_type
|
|
508
|
-
}
|
|
554
|
+
value_types = {self.overwrite_data_types.get(prop.value_type, prop.value_type) for prop in read_properties}
|
|
509
555
|
if len(value_types) == 1:
|
|
510
556
|
uri_ref = value_types.pop()
|
|
557
|
+
if uri_ref == NEAT.UnknownType:
|
|
558
|
+
return UnknownEntity()
|
|
511
559
|
self._add_uri_namespace_to_prefixes(uri_ref, prefixes)
|
|
512
560
|
return remove_namespace_from_uri(uri_ref)
|
|
513
561
|
elif len(value_types) == 0:
|
|
@@ -515,3 +563,16 @@ class SubclassInferenceImporter(BaseRDFImporter):
|
|
|
515
563
|
for uri_ref in value_types:
|
|
516
564
|
self._add_uri_namespace_to_prefixes(uri_ref, prefixes)
|
|
517
565
|
return " | ".join(remove_namespace_from_uri(uri_ref) for uri_ref in value_types)
|
|
566
|
+
|
|
567
|
+
def _default_metadata(self) -> dict[str, Any]:
|
|
568
|
+
now = datetime.now(timezone.utc)
|
|
569
|
+
return InformationMetadata(
|
|
570
|
+
space=self.data_model_id.space,
|
|
571
|
+
external_id=self.data_model_id.external_id,
|
|
572
|
+
version=cast(str, self.data_model_id.version),
|
|
573
|
+
name="Inferred Model",
|
|
574
|
+
creator=["NEAT"],
|
|
575
|
+
created=now,
|
|
576
|
+
updated=now,
|
|
577
|
+
description="Inferred model from knowledge graph",
|
|
578
|
+
).model_dump()
|
|
@@ -301,6 +301,7 @@ class BaseRules(SchemaModel, ABC):
|
|
|
301
301
|
def dump(
|
|
302
302
|
self,
|
|
303
303
|
entities_exclude_defaults: bool = True,
|
|
304
|
+
sort: bool = False,
|
|
304
305
|
mode: Literal["python", "json"] = "python",
|
|
305
306
|
by_alias: bool = False,
|
|
306
307
|
exclude: IncEx | None = None,
|
|
@@ -317,6 +318,7 @@ class BaseRules(SchemaModel, ABC):
|
|
|
317
318
|
For example, given a class that is dumped as 'my_prefix:MyClass', if the prefix for the rules
|
|
318
319
|
set in metadata.prefix = 'my_prefix', then this class will be dumped as 'MyClass' when this flag is set.
|
|
319
320
|
Defaults to True.
|
|
321
|
+
sort: Whether to sort the entities in the output.
|
|
320
322
|
mode: The mode in which `to_python` should run.
|
|
321
323
|
If mode is 'json', the output will only contain JSON serializable types.
|
|
322
324
|
If mode is 'python', the output may contain non-JSON-serializable Python objects.
|
|
@@ -326,11 +328,12 @@ class BaseRules(SchemaModel, ABC):
|
|
|
326
328
|
exclude_unset: Whether to exclude fields that have not been explicitly set.
|
|
327
329
|
exclude_defaults: Whether to exclude fields that are set to their default value.
|
|
328
330
|
"""
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
value
|
|
331
|
+
if sort:
|
|
332
|
+
for field_name in self.model_fields.keys():
|
|
333
|
+
value = getattr(self, field_name)
|
|
334
|
+
# Ensure deterministic order of properties, classes, views, and so on
|
|
335
|
+
if isinstance(value, SheetList):
|
|
336
|
+
value.sort(key=lambda x: x._identifier())
|
|
334
337
|
|
|
335
338
|
context: dict[str, Any] = {}
|
|
336
339
|
if entities_exclude_defaults:
|
|
@@ -338,7 +341,7 @@ class BaseRules(SchemaModel, ABC):
|
|
|
338
341
|
|
|
339
342
|
exclude_input: IncEx | None = exclude
|
|
340
343
|
|
|
341
|
-
|
|
344
|
+
return self.model_dump(
|
|
342
345
|
mode=mode,
|
|
343
346
|
by_alias=by_alias,
|
|
344
347
|
exclude=exclude_input,
|
|
@@ -348,8 +351,6 @@ class BaseRules(SchemaModel, ABC):
|
|
|
348
351
|
context=context,
|
|
349
352
|
)
|
|
350
353
|
|
|
351
|
-
return output
|
|
352
|
-
|
|
353
354
|
|
|
354
355
|
class SheetRow(SchemaModel):
|
|
355
356
|
neatId: URIRefType | None = Field(
|
|
@@ -292,10 +292,11 @@ class _DMSExporter:
|
|
|
292
292
|
for container in containers:
|
|
293
293
|
container_id = container.as_id()
|
|
294
294
|
if not (container_properties := container_properties_by_id.get(container_id)):
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
if container_id.space not in COGNITE_SPACES:
|
|
296
|
+
warnings.warn(
|
|
297
|
+
EmptyContainerWarning(container_id),
|
|
298
|
+
stacklevel=2,
|
|
299
|
+
)
|
|
299
300
|
container_to_drop.add(container_id)
|
|
300
301
|
continue
|
|
301
302
|
for prop in container_properties:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from ._base import RulesTransformer
|
|
1
|
+
from ._base import RulesTransformer, VerifiedRulesTransformer
|
|
2
2
|
from ._converters import (
|
|
3
3
|
AddClassImplements,
|
|
4
4
|
ChangeViewPrefix,
|
|
@@ -6,12 +6,12 @@ from ._converters import (
|
|
|
6
6
|
ConversionTransformer,
|
|
7
7
|
ConvertToRules,
|
|
8
8
|
DMSToInformation,
|
|
9
|
+
DropModelViews,
|
|
9
10
|
IncludeReferenced,
|
|
10
11
|
InformationToDMS,
|
|
11
12
|
MergeDMSRules,
|
|
12
13
|
MergeInformationRules,
|
|
13
14
|
PrefixEntities,
|
|
14
|
-
ReduceCogniteModel,
|
|
15
15
|
SetIDDMSModel,
|
|
16
16
|
ToCompliantEntities,
|
|
17
17
|
ToDataProductModel,
|
|
@@ -30,13 +30,13 @@ __all__ = [
|
|
|
30
30
|
"ConversionTransformer",
|
|
31
31
|
"ConvertToRules",
|
|
32
32
|
"DMSToInformation",
|
|
33
|
+
"DropModelViews",
|
|
33
34
|
"IncludeReferenced",
|
|
34
35
|
"InformationToDMS",
|
|
35
36
|
"MapOneToOne",
|
|
36
37
|
"MergeDMSRules",
|
|
37
38
|
"MergeInformationRules",
|
|
38
39
|
"PrefixEntities",
|
|
39
|
-
"ReduceCogniteModel",
|
|
40
40
|
"RuleMapper",
|
|
41
41
|
"RulesTransformer",
|
|
42
42
|
"SetIDDMSModel",
|
|
@@ -45,6 +45,7 @@ __all__ = [
|
|
|
45
45
|
"ToEnterpriseModel",
|
|
46
46
|
"ToExtensionModel",
|
|
47
47
|
"ToSolutionModel",
|
|
48
|
+
"VerifiedRulesTransformer",
|
|
48
49
|
"VerifyAnyRules",
|
|
49
50
|
"VerifyDMSRules",
|
|
50
51
|
"VerifyInformationRules",
|
|
@@ -5,12 +5,14 @@ from types import UnionType
|
|
|
5
5
|
from typing import Generic, TypeVar, Union, get_args, get_origin
|
|
6
6
|
|
|
7
7
|
from cognite.neat._constants import DEFAULT_NAMESPACE
|
|
8
|
-
from cognite.neat._rules._shared import ReadRules, Rules
|
|
8
|
+
from cognite.neat._rules._shared import ReadRules, Rules, VerifiedRules
|
|
9
9
|
from cognite.neat._rules.models import DMSInputRules, InformationInputRules
|
|
10
10
|
from cognite.neat._store._provenance import Agent as ProvenanceAgent
|
|
11
11
|
|
|
12
12
|
T_RulesIn = TypeVar("T_RulesIn", bound=Rules)
|
|
13
13
|
T_RulesOut = TypeVar("T_RulesOut", bound=Rules)
|
|
14
|
+
T_VerifiedIn = TypeVar("T_VerifiedIn", bound=VerifiedRules)
|
|
15
|
+
T_VerifiedOut = TypeVar("T_VerifiedOut", bound=VerifiedRules)
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class RulesTransformer(ABC, Generic[T_RulesIn, T_RulesOut]):
|
|
@@ -62,3 +64,6 @@ class RulesTransformer(ABC, Generic[T_RulesIn, T_RulesOut]):
|
|
|
62
64
|
return ReadRules[DMSInputRules], ReadRules[InformationInputRules]
|
|
63
65
|
|
|
64
66
|
return (annotation,)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class VerifiedRulesTransformer(RulesTransformer[T_VerifiedIn, T_VerifiedOut], ABC): ...
|