cognite-neat 0.98.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 (103) hide show
  1. cognite/neat/_client/__init__.py +4 -0
  2. cognite/neat/_client/_api/data_modeling_loaders.py +585 -0
  3. cognite/neat/_client/_api/schema.py +111 -0
  4. cognite/neat/_client/_api_client.py +17 -0
  5. cognite/neat/_client/data_classes/__init__.py +0 -0
  6. cognite/neat/{_utils/cdf/data_classes.py → _client/data_classes/data_modeling.py} +8 -135
  7. cognite/neat/_client/data_classes/schema.py +495 -0
  8. cognite/neat/_constants.py +27 -4
  9. cognite/neat/_graph/_shared.py +14 -15
  10. cognite/neat/_graph/extractors/_classic_cdf/_assets.py +14 -154
  11. cognite/neat/_graph/extractors/_classic_cdf/_base.py +154 -7
  12. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +25 -14
  13. cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +17 -92
  14. cognite/neat/_graph/extractors/_classic_cdf/_events.py +13 -162
  15. cognite/neat/_graph/extractors/_classic_cdf/_files.py +15 -179
  16. cognite/neat/_graph/extractors/_classic_cdf/_labels.py +32 -100
  17. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +27 -178
  18. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +14 -139
  19. cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +15 -173
  20. cognite/neat/_graph/extractors/_rdf_file.py +6 -7
  21. cognite/neat/_graph/loaders/_rdf2dms.py +2 -2
  22. cognite/neat/_graph/queries/_base.py +17 -1
  23. cognite/neat/_graph/transformers/_classic_cdf.py +74 -147
  24. cognite/neat/_graph/transformers/_prune_graph.py +1 -1
  25. cognite/neat/_graph/transformers/_rdfpath.py +1 -1
  26. cognite/neat/_issues/_base.py +26 -17
  27. cognite/neat/_issues/errors/__init__.py +4 -2
  28. cognite/neat/_issues/errors/_external.py +7 -0
  29. cognite/neat/_issues/errors/_properties.py +2 -7
  30. cognite/neat/_issues/errors/_resources.py +1 -1
  31. cognite/neat/_issues/warnings/__init__.py +8 -0
  32. cognite/neat/_issues/warnings/_external.py +16 -0
  33. cognite/neat/_issues/warnings/_properties.py +16 -0
  34. cognite/neat/_issues/warnings/_resources.py +26 -2
  35. cognite/neat/_issues/warnings/user_modeling.py +4 -4
  36. cognite/neat/_rules/_constants.py +8 -11
  37. cognite/neat/_rules/analysis/_base.py +8 -4
  38. cognite/neat/_rules/exporters/_base.py +3 -4
  39. cognite/neat/_rules/exporters/_rules2dms.py +33 -46
  40. cognite/neat/_rules/importers/__init__.py +1 -3
  41. cognite/neat/_rules/importers/_base.py +1 -1
  42. cognite/neat/_rules/importers/_dms2rules.py +6 -29
  43. cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
  44. cognite/neat/_rules/importers/_rdf/_base.py +34 -11
  45. cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
  46. cognite/neat/_rules/importers/_rdf/_inference2rules.py +43 -35
  47. cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
  48. cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
  49. cognite/neat/_rules/models/__init__.py +1 -1
  50. cognite/neat/_rules/models/_base_rules.py +22 -12
  51. cognite/neat/_rules/models/dms/__init__.py +4 -2
  52. cognite/neat/_rules/models/dms/_exporter.py +45 -48
  53. cognite/neat/_rules/models/dms/_rules.py +20 -17
  54. cognite/neat/_rules/models/dms/_rules_input.py +52 -8
  55. cognite/neat/_rules/models/dms/_validation.py +391 -119
  56. cognite/neat/_rules/models/entities/_single_value.py +32 -4
  57. cognite/neat/_rules/models/information/__init__.py +2 -0
  58. cognite/neat/_rules/models/information/_rules.py +0 -67
  59. cognite/neat/_rules/models/information/_validation.py +9 -9
  60. cognite/neat/_rules/models/mapping/__init__.py +2 -3
  61. cognite/neat/_rules/models/mapping/_classic2core.py +36 -146
  62. cognite/neat/_rules/models/mapping/_classic2core.yaml +343 -0
  63. cognite/neat/_rules/transformers/__init__.py +2 -2
  64. cognite/neat/_rules/transformers/_converters.py +110 -11
  65. cognite/neat/_rules/transformers/_mapping.py +105 -30
  66. cognite/neat/_rules/transformers/_pipelines.py +1 -1
  67. cognite/neat/_rules/transformers/_verification.py +31 -3
  68. cognite/neat/_session/_base.py +24 -8
  69. cognite/neat/_session/_drop.py +35 -0
  70. cognite/neat/_session/_inspect.py +17 -5
  71. cognite/neat/_session/_mapping.py +39 -0
  72. cognite/neat/_session/_prepare.py +219 -23
  73. cognite/neat/_session/_read.py +49 -12
  74. cognite/neat/_session/_to.py +8 -5
  75. cognite/neat/_session/exceptions.py +4 -0
  76. cognite/neat/_store/_base.py +27 -24
  77. cognite/neat/_utils/rdf_.py +34 -5
  78. cognite/neat/_version.py +1 -1
  79. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +5 -88
  80. cognite/neat/_workflows/steps/lib/current/rules_importer.py +3 -14
  81. cognite/neat/_workflows/steps/lib/current/rules_validator.py +6 -7
  82. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/METADATA +3 -3
  83. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/RECORD +87 -92
  84. cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
  85. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
  86. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
  87. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
  88. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
  89. cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
  90. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
  91. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
  92. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
  93. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
  94. cognite/neat/_rules/models/dms/_schema.py +0 -1101
  95. cognite/neat/_rules/models/mapping/_base.py +0 -131
  96. cognite/neat/_utils/cdf/loaders/__init__.py +0 -25
  97. cognite/neat/_utils/cdf/loaders/_base.py +0 -54
  98. cognite/neat/_utils/cdf/loaders/_data_modeling.py +0 -339
  99. cognite/neat/_utils/cdf/loaders/_ingestion.py +0 -167
  100. /cognite/neat/{_utils/cdf → _client/_api}/__init__.py +0 -0
  101. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/LICENSE +0 -0
  102. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/WHEEL +0 -0
  103. {cognite_neat-0.98.0.dist-info → cognite_neat-0.99.1.dist-info}/entry_points.txt +0 -0
@@ -1,29 +0,0 @@
1
- from cognite.neat._rules.importers._rdf._shared import make_metadata_compliant
2
- from cognite.neat._rules.models import RoleTypes
3
-
4
-
5
- def parse_imf_metadata(space: str = "pcaimf") -> dict:
6
- """Provide hardcoded IMF metadata to dict.
7
-
8
- Returns:
9
- Dictionary containing IMF metadata
10
-
11
- !!! note "Compliant IMF metadata"
12
- The current RDF provide IMF types as SHACL, but there are not any metadata describing
13
- the actual content.
14
-
15
- """
16
-
17
- raw_metadata = {
18
- "role": RoleTypes.information,
19
- "space": space,
20
- "external_id": "imf_types",
21
- "version": None,
22
- "created": None,
23
- "updated": None,
24
- "name": "IMF Types",
25
- "description": "IMF - types",
26
- "creator": None,
27
- }
28
-
29
- return make_metadata_compliant(raw_metadata)
@@ -1,130 +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_imf_to_properties(graph: Graph, language: str = "en") -> list[dict]:
13
- """Parse IMF elements from RDF-graph and extract properties to pandas dataframe.
14
-
15
- Args:
16
- graph: Graph containing imf elements
17
- language: Language to use for parsing, by default "en"
18
-
19
- Returns:
20
- List of dictionaries containing properties extracted from IMF elements
21
-
22
- !!! note "IMF Compliance"
23
- The IMF elements are expressed in RDF, primarily using SHACL and OWL. To ensure
24
- that the resulting properties are compliant with CDF, similar validation checks
25
- as in the OWL ontology importer are applied.
26
-
27
- For the IMF-types more of the compliance logic is placed directly in the SPARQL
28
- query. Among these are the creation of class and property names not starting
29
- with a number, ensuring property types as well as default cardinality boundraries.
30
-
31
- IMF-attributes are considered both classes and properties. This kind of punning
32
- is necessary to capture additional information carried by attributes. They carry,
33
- among other things, a set of relationsships to reference terms, units of measure,
34
- and qualifiers that together make up the meaning of the attribute. These references
35
- are listed as additional properties with default values.
36
- """
37
-
38
- query = """
39
- SELECT DISTINCT ?class ?property ?name ?description ?valueType ?minCount ?maxCount ?default ?propertyType
40
- WHERE
41
- {
42
- # Finding IMF-blocks and terminals
43
- {
44
- VALUES ?classType { imf:BlockType imf:TerminalType }
45
- ?imfClass a ?classType ;
46
- sh:property ?propertyShape .
47
- ?propertyShape sh:path ?imfProperty .
48
-
49
- OPTIONAL { ?imfProperty skos:prefLabel ?name . }
50
- OPTIONAL { ?imfProperty skos:definition | skos:description ?description . }
51
- OPTIONAL { ?imfProperty rdfs:range ?range . }
52
- OPTIONAL { ?imfProperty a ?type . }
53
- OPTIONAL { ?propertyShape sh:minCount ?minCardinality} .
54
- OPTIONAL { ?propertyShape sh:maxCount ?maxCardinality} .
55
- OPTIONAL { ?propertyShape sh:hasValue ?defualt . }
56
- OPTIONAL { ?propertyShape sh:class | sh:qualifiedValueShape/sh:class ?valueShape .}
57
- }
58
- UNION
59
- # Finding the IMF-attribute types
60
- {
61
- ?imfClass a imf:AttributeType ;
62
- ?imfProperty ?default .
63
-
64
- # The following information is used to describe the attribute when it is connected to a block or a terminal
65
- # and not duplicated here.
66
- # Note: Bug in PCA has lead to the use non-existing term skos:description. This will be replaced
67
- # with the correct skos:definition in the near future, so both terms are included here.
68
- FILTER(?imfProperty != rdf:type && ?imfProperty != skos:prefLabel &&
69
- ?imfProperty != skos:defintion && ?imfProperty != skos:description)
70
- }
71
-
72
- # Finding the last segment of the class IRI
73
- BIND(STR(?imfClass) AS ?classString)
74
- BIND(REPLACE(?classString, "^.*[/#]([^/#]*)$", "$1") AS ?tempClassSegment)
75
- BIND(REPLACE(?tempClassSegment, "-", "_") AS ?classSegment)
76
- BIND(IF(CONTAINS(?classString, "imf/"), CONCAT("IMF_", ?classSegment) , ?classSegment) AS ?class)
77
-
78
-
79
- # Finding the last segment of the property IRI
80
- BIND(STR(?imfProperty) AS ?propertyString)
81
- BIND(REPLACE(?propertyString, "^.*[/#]([^/#]*)$", "$1") AS ?tempPropertySegment)
82
- BIND(REPLACE(?tempPropertySegment, "-", "_") AS ?propertySegment)
83
- BIND(IF(CONTAINS(?propertyString, "imf/"), CONCAT("IMF_", ?propertySegment) , ?propertySegment) AS ?property)
84
-
85
- # Set the value type for the property based on sh:class, sh:qualifiedValueType or rdfs:range
86
- BIND(IF(BOUND(?valueShape), ?valueShape, IF(BOUND(?range) , ?range , ?valueShape)) AS ?valueIriType)
87
-
88
- # Finding the last segment of value types
89
- BIND(STR(?valueIriType) AS ?valueTypeString)
90
- BIND(REPLACE(?valueTypeString, "^.*[/#]([^/#]*)$", "$1") AS ?tempValueTypeSegment)
91
- BIND(REPLACE(?tempValueTypeSegment, "-", "_") AS ?valueTypeSegment)
92
- BIND(IF(CONTAINS(?valueTypeString, "imf/"), CONCAT("IMF_", ?valueTypeSegment) , ?valueTypeSegment)
93
- AS ?valueType)
94
-
95
- # Helper variable to set owl datatype- or object-property if this is not already set.
96
- BIND(IF( EXISTS {?imfProperty a ?tempPropertyType . FILTER(?tempPropertyType = owl:DatatypeProperty) },
97
- owl:DatatypeProperty,
98
- owl:ObjectProperty
99
- )
100
- AS ?propertyType)
101
-
102
- # Assert cardinality values if they do not exist
103
- BIND(IF(BOUND(?minCardinality), ?minCardinality, 0) AS ?minCount)
104
- BIND(IF(BOUND(?maxCardinality), ?maxCardinality, 1) AS ?maxCount)
105
-
106
- # Rebind the IRI of the IMF-attribute to the ?reference variable to align with dataframe column headers
107
- # This is solely for readability, the ?imfClass could have been returnered directly instead of ?reference
108
- BIND(?imfProperty AS ?reference)
109
-
110
- FILTER (!isBlank(?property))
111
- FILTER (!bound(?class) || !isBlank(?class))
112
- FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
113
- FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
114
- }
115
- """
116
-
117
- raw_df = parse_raw_properties_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language)))))
118
- if raw_df.empty:
119
- return []
120
-
121
- # group values and clean up
122
- processed_df = clean_up_properties(raw_df)
123
-
124
- # make compliant
125
- processed_df = make_properties_compliant(processed_df, importer="IMF")
126
-
127
- # drop column _property_type, which was a helper column:
128
- processed_df.drop(columns=["_property_type"], inplace=True)
129
-
130
- return processed_df.to_dict(orient="records")
@@ -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)