cognite-neat 0.99.0__py3-none-any.whl → 0.99.1__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 (62) hide show
  1. cognite/neat/_client/_api/data_modeling_loaders.py +77 -4
  2. cognite/neat/_client/_api/schema.py +63 -2
  3. cognite/neat/_client/data_classes/schema.py +2 -348
  4. cognite/neat/_constants.py +27 -4
  5. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +5 -5
  6. cognite/neat/_graph/loaders/_rdf2dms.py +2 -2
  7. cognite/neat/_graph/transformers/_classic_cdf.py +24 -13
  8. cognite/neat/_issues/_base.py +26 -17
  9. cognite/neat/_issues/errors/__init__.py +4 -2
  10. cognite/neat/_issues/errors/_external.py +7 -0
  11. cognite/neat/_issues/errors/_properties.py +2 -7
  12. cognite/neat/_issues/errors/_resources.py +1 -1
  13. cognite/neat/_issues/warnings/__init__.py +4 -2
  14. cognite/neat/_issues/warnings/_external.py +9 -1
  15. cognite/neat/_issues/warnings/_resources.py +26 -2
  16. cognite/neat/_issues/warnings/user_modeling.py +4 -4
  17. cognite/neat/_rules/_constants.py +2 -6
  18. cognite/neat/_rules/exporters/_rules2dms.py +4 -6
  19. cognite/neat/_rules/importers/__init__.py +1 -3
  20. cognite/neat/_rules/importers/_base.py +1 -1
  21. cognite/neat/_rules/importers/_dms2rules.py +3 -25
  22. cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
  23. cognite/neat/_rules/importers/_rdf/_base.py +34 -11
  24. cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
  25. cognite/neat/_rules/importers/_rdf/_inference2rules.py +18 -2
  26. cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
  27. cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
  28. cognite/neat/_rules/models/dms/__init__.py +2 -0
  29. cognite/neat/_rules/models/dms/_exporter.py +32 -30
  30. cognite/neat/_rules/models/dms/_rules.py +3 -45
  31. cognite/neat/_rules/models/dms/_validation.py +389 -122
  32. cognite/neat/_rules/models/information/__init__.py +2 -0
  33. cognite/neat/_rules/models/information/_rules.py +0 -59
  34. cognite/neat/_rules/models/information/_validation.py +9 -9
  35. cognite/neat/_rules/models/mapping/_classic2core.py +1 -1
  36. cognite/neat/_rules/models/mapping/_classic2core.yaml +8 -4
  37. cognite/neat/_rules/transformers/_pipelines.py +1 -1
  38. cognite/neat/_rules/transformers/_verification.py +29 -4
  39. cognite/neat/_session/_base.py +16 -41
  40. cognite/neat/_session/_prepare.py +6 -5
  41. cognite/neat/_session/_to.py +5 -2
  42. cognite/neat/_session/exceptions.py +4 -0
  43. cognite/neat/_utils/rdf_.py +6 -4
  44. cognite/neat/_version.py +1 -1
  45. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -88
  46. cognite/neat/_workflows/steps/lib/current/rules_importer.py +2 -16
  47. cognite/neat/_workflows/steps/lib/current/rules_validator.py +3 -5
  48. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/METADATA +1 -1
  49. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/RECORD +52 -60
  50. cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
  51. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
  52. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
  53. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
  54. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
  55. cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
  56. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
  57. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
  58. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
  59. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
  60. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/LICENSE +0 -0
  61. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/WHEEL +0 -0
  62. {cognite_neat-0.99.0.dist-info → cognite_neat-0.99.1.dist-info}/entry_points.txt +0 -0
@@ -1,154 +0,0 @@
1
- """This module performs importing of various formats to one of serializations for which
2
- there are loaders to TransformationRules pydantic class."""
3
-
4
- from cognite.neat._rules.importers._rdf._base import BaseRDFImporter
5
- from cognite.neat._rules.models.data_types import _XSD_TYPES
6
-
7
- from ._imf2classes import parse_imf_to_classes
8
- from ._imf2metadata import parse_imf_metadata
9
- from ._imf2properties import parse_imf_to_properties
10
-
11
-
12
- class IMFImporter(BaseRDFImporter):
13
- """Convert SHACL shapes to tables/ transformation rules / Excel file.
14
-
15
- Args:
16
- filepath: Path to RDF file containing the SHACL Shapes
17
-
18
- !!! Note
19
- Rewrite to fit the SHACL rules we apply
20
- OWL Ontologies are information models which completeness varies. As such, constructing functional
21
- data model directly will often be impossible, therefore the produced Rules object will be ill formed.
22
- To avoid this, neat will automatically attempt to make the imported rules compliant by adding default
23
- values for missing information, attaching dangling properties to default containers based on the
24
- property type, etc.
25
-
26
- One has to be aware that NEAT will be opinionated about how to make the ontology
27
- compliant, and that the resulting rules may not be what you expect.
28
-
29
- """
30
-
31
- def _to_rules_components(
32
- self,
33
- ) -> dict:
34
- components = {
35
- "Metadata": parse_imf_metadata(),
36
- "Classes": parse_imf_to_classes(self.graph),
37
- "Properties": parse_imf_to_properties(self.graph),
38
- }
39
-
40
- return make_components_compliant(components)
41
-
42
-
43
- def make_components_compliant(components: dict) -> dict:
44
- components = _add_missing_classes(components)
45
- components = _add_missing_value_types(components)
46
- components = _add_default_property_to_dangling_classes(components)
47
-
48
- return components
49
-
50
-
51
- def _add_missing_classes(components: dict[str, list[dict]]) -> dict:
52
- """Add missing classes to Classes.
53
-
54
- Args:
55
- tables: imported tables from owl ontology
56
-
57
- Returns:
58
- Updated tables with missing classes added to containers
59
- """
60
-
61
- missing_classes = {definition["Class"] for definition in components["Properties"]} - {
62
- definition["Class"] for definition in components["Classes"]
63
- }
64
-
65
- comment = (
66
- "Added by NEAT. "
67
- "This is a class that a domain of a property but was not defined in the ontology. "
68
- "It is added by NEAT to make the ontology compliant with CDF."
69
- )
70
-
71
- for class_ in missing_classes:
72
- components["Classes"].append({"Class": class_, "Comment": comment})
73
-
74
- return components
75
-
76
-
77
- def _add_missing_value_types(components: dict) -> dict:
78
- """Add properties to classes that do not have any properties defined to them
79
-
80
- Args:
81
- tables: imported tables from owl ontology
82
-
83
- Returns:
84
- Updated tables with missing properties added to containers
85
- """
86
-
87
- xsd_types = _XSD_TYPES
88
- candidate_value_types = {definition["Value Type"] for definition in components["Properties"]} - {
89
- definition["Class"] for definition in components["Classes"]
90
- }
91
-
92
- # to avoid issue of case sensitivity for xsd types
93
- value_types_lower = {v.lower() for v in candidate_value_types}
94
-
95
- xsd_types_lower = {x.lower() for x in xsd_types}
96
-
97
- # Create a mapping from lowercase strings to original strings
98
- value_types_mapping = {v.lower(): v for v in candidate_value_types}
99
-
100
- # Find the difference
101
- difference = value_types_lower - xsd_types_lower
102
-
103
- # Convert the difference back to the original case
104
- difference_original_case = {value_types_mapping[d] for d in difference}
105
-
106
- for class_ in difference_original_case:
107
- components["Classes"].append(
108
- {
109
- "Class": class_,
110
- "Comment": (
111
- "Added by NEAT. "
112
- "This is a class that a domain of a property but was not defined in the ontology. "
113
- "It is added by NEAT to make the ontology compliant with CDF."
114
- ),
115
- }
116
- )
117
-
118
- return components
119
-
120
-
121
- def _add_default_property_to_dangling_classes(components: dict[str, list[dict]]) -> dict:
122
- """Add missing classes to Classes.
123
-
124
- Args:
125
- tables: imported tables from owl ontology
126
-
127
- Returns:
128
- Updated tables with missing classes added to containers
129
- """
130
-
131
- dangling_classes = {
132
- definition["Class"] for definition in components["Classes"] if not definition.get("Parent Class", None)
133
- } - {definition["Class"] for definition in components["Properties"]}
134
-
135
- comment = (
136
- "Added by NEAT. "
137
- "This is property has been added to this class since otherwise it will create "
138
- "dangling classes in the ontology."
139
- )
140
-
141
- for class_ in dangling_classes:
142
- components["Properties"].append(
143
- {
144
- "Class": class_,
145
- "Property": "label",
146
- "Value Type": "string",
147
- "Comment": comment,
148
- "Min Count": 0,
149
- "Max Count": 1,
150
- "Reference": "http://www.w3.org/2000/01/rdf-schema#label",
151
- }
152
- )
153
-
154
- return components
@@ -1,3 +0,0 @@
1
- from ._owl2rules import OWLImporter
2
-
3
- __all__ = ["OWLImporter"]
@@ -1,58 +0,0 @@
1
- from typing import cast
2
-
3
- from rdflib import Graph
4
-
5
- from cognite.neat._rules.importers._rdf._shared import (
6
- clean_up_classes,
7
- make_classes_compliant,
8
- parse_raw_classes_dataframe,
9
- )
10
-
11
-
12
- def parse_owl_classes(graph: Graph, language: str = "en") -> list[dict]:
13
- """Parse owl classes from graph to pandas dataframe.
14
-
15
- Args:
16
- graph: Graph containing owl classes
17
- language: Language to use for parsing, by default "en"
18
-
19
- Returns:
20
- Dataframe containing owl classes
21
-
22
- !!! note "Compliant OWL classes"
23
- This makes the method very opinionated, but results in a compliant classes.
24
- """
25
-
26
- query = """
27
- SELECT ?class ?name ?description ?parentClass
28
- WHERE {
29
- ?class a owl:Class .
30
- OPTIONAL {?class rdfs:subClassOf ?parentClass }.
31
- OPTIONAL {?class rdfs:label ?name }.
32
- OPTIONAL {?class rdfs:comment ?description} .
33
- FILTER (!isBlank(?class))
34
- FILTER (!bound(?parentClass) || !isBlank(?parentClass))
35
- FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
36
- FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
37
- BIND(?class AS ?reference)
38
- }
39
- """
40
-
41
- # create raw dataframe
42
-
43
- raw_df = parse_raw_classes_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language)))))
44
- if raw_df.empty:
45
- return []
46
-
47
- # group values and clean up
48
- processed_df = clean_up_classes(raw_df)
49
-
50
- # make compliant
51
- processed_df = make_classes_compliant(processed_df, importer="OWL")
52
-
53
- # Make Parent Class list elements into string joined with comma
54
- processed_df["Implements"] = processed_df["Implements"].apply(
55
- lambda x: ", ".join(x) if isinstance(x, list) and x else None
56
- )
57
-
58
- return processed_df.dropna(axis=0, how="all").replace(float("nan"), None).to_dict(orient="records")
@@ -1,65 +0,0 @@
1
- from rdflib import Graph
2
-
3
- from cognite.neat._constants import DEFAULT_NAMESPACE
4
- from cognite.neat._rules.importers._rdf._shared import make_metadata_compliant
5
- from cognite.neat._rules.models import RoleTypes
6
- from cognite.neat._utils.collection_ import remove_none_elements_from_set
7
- from cognite.neat._utils.rdf_ import convert_rdflib_content
8
-
9
-
10
- def parse_owl_metadata(graph: Graph) -> dict:
11
- """Parse owl metadata from graph to dict.
12
-
13
- Args:
14
- graph: Graph containing owl metadata
15
-
16
- Returns:
17
- Dictionary containing owl metadata
18
-
19
- !!! note "Compliant OWL metadata"
20
- This makes the method very opinionated, but results in a compliant metadata.
21
-
22
-
23
- """
24
- # TODO: Move dataframe to dict representation
25
-
26
- query = f"""SELECT ?namespace ?prefix ?version ?created ?updated ?title ?description ?creator ?rights ?license
27
- WHERE {{
28
- ?namespace a owl:Ontology .
29
- OPTIONAL {{?namespace owl:versionInfo ?version }}.
30
- OPTIONAL {{?namespace dcterms:creator ?creator }}.
31
- OPTIONAL {{?namespace <{DEFAULT_NAMESPACE.prefix}> ?prefix }}.
32
- OPTIONAL {{?namespace dcterms:title|rdfs:label|skos:prefLabel ?title }}.
33
- OPTIONAL {{?namespace dcterms:modified ?updated }}.
34
- OPTIONAL {{?namespace dcterms:created ?created }}.
35
- OPTIONAL {{?namespace dcterms:description ?description }}.
36
- OPTIONAL {{?namespace dcterms:rights|dc:rights ?rights }}.
37
-
38
- OPTIONAL {{?namespace dcterms:license|dc:license ?license }}.
39
- FILTER (!isBlank(?namespace))
40
- FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
41
- FILTER (!bound(?title) || LANG(?title) = "" || LANGMATCHES(LANG(?title), "en"))
42
- }}
43
- """
44
-
45
- results = [{item for item in sublist} for sublist in list(zip(*graph.query(query), strict=True))]
46
-
47
- raw_metadata = convert_rdflib_content(
48
- {
49
- "role": RoleTypes.information,
50
- "space": results[1].pop(),
51
- "external_id": "OntologyBasedDataModel",
52
- "version": results[2].pop(),
53
- "created": results[3].pop(),
54
- "updated": results[4].pop(),
55
- "name": results[5].pop(),
56
- "description": results[6].pop(),
57
- "creator": (
58
- ", ".join(remove_none_elements_from_set(results[7]))
59
- if remove_none_elements_from_set(results[7])
60
- else None
61
- ),
62
- }
63
- )
64
-
65
- return make_metadata_compliant(raw_metadata)
@@ -1,59 +0,0 @@
1
- from typing import cast
2
-
3
- from rdflib import Graph
4
-
5
- from cognite.neat._rules.importers._rdf._shared import (
6
- clean_up_properties,
7
- make_properties_compliant,
8
- parse_raw_properties_dataframe,
9
- )
10
-
11
-
12
- def parse_owl_properties(graph: Graph, language: str = "en") -> list[dict]:
13
- """Parse owl properties from graph to pandas dataframe.
14
-
15
- Args:
16
- graph: Graph containing owl properties
17
- language: Language to use for parsing, by default "en"
18
-
19
- Returns:
20
- List of dictionaries containing owl properties
21
- """
22
-
23
- query = """
24
-
25
- SELECT ?class ?property ?name ?description ?type ?minCount ?maxCount ?default ?propertyType
26
- WHERE {
27
- ?property a ?propertyType.
28
- FILTER (?propertyType IN (owl:ObjectProperty, owl:DatatypeProperty ) )
29
- OPTIONAL {?property rdfs:domain ?class }.
30
- OPTIONAL {?property rdfs:range ?type }.
31
- OPTIONAL {?property rdfs:label ?name }.
32
- OPTIONAL {?property rdfs:comment ?description} .
33
- OPTIONAL {?property owl:maxCardinality ?maxCount} .
34
- OPTIONAL {?property owl:minCardinality ?minCount} .
35
- FILTER (!isBlank(?property))
36
- FILTER (!bound(?type) || !isBlank(?type))
37
- FILTER (!bound(?class) || !isBlank(?class))
38
- FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
39
- FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
40
- BIND(IF(bound(?minCount), ?minCount, 0) AS ?minCount)
41
- BIND(IF(bound(?maxCount), ?maxCount, 1) AS ?maxCount)
42
- BIND(?property AS ?reference)
43
- }
44
- """
45
-
46
- raw_df = parse_raw_properties_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language)))))
47
- if raw_df.empty:
48
- return []
49
-
50
- # group values and clean up
51
- processed_df = clean_up_properties(raw_df)
52
-
53
- # make compliant
54
- processed_df = make_properties_compliant(processed_df, importer="OWL")
55
-
56
- # drop column _property_type, which was a helper column:
57
- processed_df.drop(columns=["_property_type"], inplace=True)
58
-
59
- return processed_df.to_dict(orient="records")
@@ -1,39 +0,0 @@
1
- """This module performs importing of various formats to one of serializations for which
2
- there are loaders to TransformationRules pydantic class."""
3
-
4
- from cognite.neat._rules.importers._rdf._base import BaseRDFImporter
5
- from cognite.neat._rules.importers._rdf._shared import make_components_compliant
6
-
7
- from ._owl2classes import parse_owl_classes
8
- from ._owl2metadata import parse_owl_metadata
9
- from ._owl2properties import parse_owl_properties
10
-
11
-
12
- class OWLImporter(BaseRDFImporter):
13
- """Convert OWL ontology to tables/ transformation rules / Excel file.
14
-
15
- Args:
16
- filepath: Path to OWL ontology
17
-
18
- !!! Note
19
- OWL Ontologies are information models which completeness varies. As such, constructing functional
20
- data model directly will often be impossible, therefore the produced Rules object will be ill formed.
21
- To avoid this, neat will automatically attempt to make the imported rules compliant by adding default
22
- values for missing information, attaching dangling properties to default containers based on the
23
- property type, etc.
24
-
25
- One has to be aware that NEAT will be opinionated about how to make the ontology
26
- compliant, and that the resulting rules may not be what you expect.
27
-
28
- """
29
-
30
- def _to_rules_components(
31
- self,
32
- ) -> dict:
33
- components = {
34
- "Metadata": parse_owl_metadata(self.graph),
35
- "Classes": parse_owl_classes(self.graph),
36
- "Properties": parse_owl_properties(self.graph),
37
- }
38
-
39
- return make_components_compliant(components)