cognite-neat 0.99.0__py3-none-any.whl → 0.100.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (84) hide show
  1. cognite/neat/_client/_api/data_modeling_loaders.py +390 -116
  2. cognite/neat/_client/_api/schema.py +63 -2
  3. cognite/neat/_client/data_classes/data_modeling.py +4 -0
  4. cognite/neat/_client/data_classes/schema.py +2 -348
  5. cognite/neat/_constants.py +27 -4
  6. cognite/neat/_graph/extractors/_base.py +7 -0
  7. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +28 -18
  8. cognite/neat/_graph/loaders/_rdf2dms.py +52 -13
  9. cognite/neat/_graph/transformers/__init__.py +3 -3
  10. cognite/neat/_graph/transformers/_classic_cdf.py +135 -56
  11. cognite/neat/_issues/_base.py +26 -17
  12. cognite/neat/_issues/errors/__init__.py +4 -2
  13. cognite/neat/_issues/errors/_external.py +7 -0
  14. cognite/neat/_issues/errors/_properties.py +2 -7
  15. cognite/neat/_issues/errors/_resources.py +1 -1
  16. cognite/neat/_issues/warnings/__init__.py +6 -2
  17. cognite/neat/_issues/warnings/_external.py +9 -1
  18. cognite/neat/_issues/warnings/_resources.py +41 -2
  19. cognite/neat/_issues/warnings/user_modeling.py +4 -4
  20. cognite/neat/_rules/_constants.py +2 -6
  21. cognite/neat/_rules/analysis/_base.py +15 -5
  22. cognite/neat/_rules/analysis/_dms.py +20 -0
  23. cognite/neat/_rules/analysis/_information.py +22 -0
  24. cognite/neat/_rules/exporters/_base.py +3 -5
  25. cognite/neat/_rules/exporters/_rules2dms.py +190 -200
  26. cognite/neat/_rules/importers/__init__.py +1 -3
  27. cognite/neat/_rules/importers/_base.py +1 -1
  28. cognite/neat/_rules/importers/_dms2rules.py +3 -25
  29. cognite/neat/_rules/importers/_rdf/__init__.py +5 -0
  30. cognite/neat/_rules/importers/_rdf/_base.py +34 -11
  31. cognite/neat/_rules/importers/_rdf/_imf2rules.py +91 -0
  32. cognite/neat/_rules/importers/_rdf/_inference2rules.py +40 -7
  33. cognite/neat/_rules/importers/_rdf/_owl2rules.py +80 -0
  34. cognite/neat/_rules/importers/_rdf/_shared.py +138 -441
  35. cognite/neat/_rules/models/_base_rules.py +19 -0
  36. cognite/neat/_rules/models/_types.py +5 -0
  37. cognite/neat/_rules/models/dms/__init__.py +2 -0
  38. cognite/neat/_rules/models/dms/_exporter.py +247 -123
  39. cognite/neat/_rules/models/dms/_rules.py +7 -49
  40. cognite/neat/_rules/models/dms/_rules_input.py +8 -3
  41. cognite/neat/_rules/models/dms/_validation.py +421 -123
  42. cognite/neat/_rules/models/entities/_multi_value.py +3 -0
  43. cognite/neat/_rules/models/information/__init__.py +2 -0
  44. cognite/neat/_rules/models/information/_rules.py +17 -61
  45. cognite/neat/_rules/models/information/_rules_input.py +11 -2
  46. cognite/neat/_rules/models/information/_validation.py +107 -11
  47. cognite/neat/_rules/models/mapping/_classic2core.py +1 -1
  48. cognite/neat/_rules/models/mapping/_classic2core.yaml +8 -4
  49. cognite/neat/_rules/transformers/__init__.py +2 -1
  50. cognite/neat/_rules/transformers/_converters.py +163 -61
  51. cognite/neat/_rules/transformers/_mapping.py +132 -2
  52. cognite/neat/_rules/transformers/_pipelines.py +1 -1
  53. cognite/neat/_rules/transformers/_verification.py +29 -4
  54. cognite/neat/_session/_base.py +46 -60
  55. cognite/neat/_session/_mapping.py +105 -5
  56. cognite/neat/_session/_prepare.py +49 -14
  57. cognite/neat/_session/_read.py +50 -4
  58. cognite/neat/_session/_set.py +1 -0
  59. cognite/neat/_session/_to.py +38 -12
  60. cognite/neat/_session/_wizard.py +5 -0
  61. cognite/neat/_session/engine/_interface.py +3 -2
  62. cognite/neat/_session/exceptions.py +4 -0
  63. cognite/neat/_store/_base.py +79 -19
  64. cognite/neat/_utils/collection_.py +22 -0
  65. cognite/neat/_utils/rdf_.py +30 -4
  66. cognite/neat/_version.py +2 -2
  67. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +3 -91
  68. cognite/neat/_workflows/steps/lib/current/rules_importer.py +2 -16
  69. cognite/neat/_workflows/steps/lib/current/rules_validator.py +3 -5
  70. {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/METADATA +1 -1
  71. {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/RECORD +74 -82
  72. cognite/neat/_rules/importers/_rdf/_imf2rules/__init__.py +0 -3
  73. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +0 -86
  74. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +0 -29
  75. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +0 -130
  76. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2rules.py +0 -154
  77. cognite/neat/_rules/importers/_rdf/_owl2rules/__init__.py +0 -3
  78. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +0 -58
  79. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +0 -65
  80. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +0 -59
  81. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2rules.py +0 -39
  82. {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/LICENSE +0 -0
  83. {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/WHEEL +0 -0
  84. {cognite_neat-0.99.0.dist-info → cognite_neat-0.100.0.dist-info}/entry_points.txt +0 -0
@@ -1,13 +1,16 @@
1
+ from datetime import datetime
1
2
  from pathlib import Path
2
3
 
3
4
  from cognite.client import data_modeling as dm
4
- from rdflib import DC, DCTERMS, OWL, RDF, RDFS, SH, SKOS, XSD, Graph, Namespace, URIRef
5
+ from rdflib import Graph, Namespace, URIRef
5
6
 
7
+ from cognite.neat._constants import get_default_prefixes
6
8
  from cognite.neat._issues import IssueList
7
9
  from cognite.neat._issues.errors import FileReadError
8
10
  from cognite.neat._issues.errors._general import NeatValueError
9
11
  from cognite.neat._rules._shared import ReadRules
10
12
  from cognite.neat._rules.importers._base import BaseImporter
13
+ from cognite.neat._rules.models._base_rules import RoleTypes
11
14
  from cognite.neat._rules.models.data_types import AnyURI
12
15
  from cognite.neat._rules.models.entities import UnknownEntity
13
16
  from cognite.neat._rules.models.information import (
@@ -30,6 +33,13 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
30
33
  graph: Knowledge graph
31
34
  data_model_id: Data model id to be used for the imported rules
32
35
  space: CDF Space to be used for the imported rules
36
+ language: Language for description and human readable entity names
37
+
38
+
39
+
40
+ !!! note "Language"
41
+ Language is provided as ISO 639-1 code. If not provided, English will be used as default.
42
+
33
43
  """
34
44
 
35
45
  def __init__(
@@ -39,6 +49,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
39
49
  data_model_id: dm.DataModelId | tuple[str, str, str],
40
50
  max_number_of_instance: int,
41
51
  non_existing_node_type: UnknownEntity | AnyURI,
52
+ language: str,
42
53
  ) -> None:
43
54
  self.issue_list = issue_list
44
55
  self.graph = graph
@@ -48,6 +59,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
48
59
 
49
60
  self.max_number_of_instance = max_number_of_instance
50
61
  self.non_existing_node_type = non_existing_node_type
62
+ self.language = language
51
63
 
52
64
  @classmethod
53
65
  def from_graph_store(
@@ -56,6 +68,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
56
68
  data_model_id: (dm.DataModelId | tuple[str, str, str]) = DEFAULT_RDF_DATA_MODEL_ID,
57
69
  max_number_of_instance: int = -1,
58
70
  non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
71
+ language: str = "en",
59
72
  ):
60
73
  return cls(
61
74
  IssueList(title=f"{cls.__name__} issues"),
@@ -63,6 +76,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
63
76
  data_model_id=data_model_id,
64
77
  max_number_of_instance=max_number_of_instance,
65
78
  non_existing_node_type=non_existing_node_type,
79
+ language=language,
66
80
  )
67
81
 
68
82
  @classmethod
@@ -72,6 +86,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
72
86
  data_model_id: (dm.DataModelId | tuple[str, str, str]) = DEFAULT_RDF_DATA_MODEL_ID,
73
87
  max_number_of_instance: int = -1,
74
88
  non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
89
+ language: str = "en",
75
90
  ):
76
91
  issue_list = IssueList(title=f"{cls.__name__} issues")
77
92
 
@@ -82,15 +97,8 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
82
97
  issue_list.append(FileReadError(filepath, str(e)))
83
98
 
84
99
  # bind key namespaces
85
- graph.bind("owl", OWL)
86
- graph.bind("rdf", RDF)
87
- graph.bind("rdfs", RDFS)
88
- graph.bind("dcterms", DCTERMS)
89
- graph.bind("dc", DC)
90
- graph.bind("skos", SKOS)
91
- graph.bind("sh", SH)
92
- graph.bind("xsd", XSD)
93
- graph.bind("imf", "http://ns.imfid.org/imf#")
100
+ for prefix, namespace in get_default_prefixes().items():
101
+ graph.bind(prefix, namespace)
94
102
 
95
103
  return cls(
96
104
  issue_list,
@@ -98,6 +106,7 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
98
106
  data_model_id=data_model_id,
99
107
  max_number_of_instance=max_number_of_instance,
100
108
  non_existing_node_type=non_existing_node_type,
109
+ language=language,
101
110
  )
102
111
 
103
112
  def to_rules(
@@ -129,4 +138,18 @@ class BaseRDFImporter(BaseImporter[InformationInputRules]):
129
138
  prefixes: Dict of prefixes and namespaces
130
139
  """
131
140
  if Namespace(get_namespace(URI)) not in prefixes.values():
132
- prefixes[f"prefix-{len(prefixes)+1}"] = Namespace(get_namespace(URI))
141
+ prefixes[f"prefix_{len(prefixes)+1}"] = Namespace(get_namespace(URI))
142
+
143
+ @property
144
+ def _metadata(self) -> dict:
145
+ return {
146
+ "role": RoleTypes.information,
147
+ "space": self.data_model_id.space,
148
+ "external_id": self.data_model_id.external_id,
149
+ "version": self.data_model_id.version,
150
+ "created": datetime.now().replace(microsecond=0),
151
+ "updated": datetime.now().replace(microsecond=0),
152
+ "name": None,
153
+ "description": f"Data model imported using {type(self).__name__}",
154
+ "creator": "Neat",
155
+ }
@@ -0,0 +1,91 @@
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 parse_classes, parse_properties
6
+
7
+ CLASSES_QUERY = """
8
+ SELECT ?class_ ?name ?description ?implements
9
+ WHERE {{
10
+ VALUES ?type {{ imf:BlockType imf:TerminalType imf:AttributeType }}
11
+ ?class_ a ?type .
12
+
13
+ OPTIONAL {{?class_ rdfs:subClassOf ?parent }}.
14
+ OPTIONAL {{?class_ rdfs:label|skos:prefLabel ?name }}.
15
+ OPTIONAL {{?class_ rdfs:comment|skos:definition ?description}}.
16
+
17
+
18
+ # Add imf:Attribute as parent class when no parent is found
19
+ BIND(IF(!bound(?parent) && ?type = imf:AttributeType, imf:Attribute, ?parent) AS ?implements)
20
+
21
+ # FILTERS
22
+ FILTER (!isBlank(?class_))
23
+ FILTER (!bound(?implements) || !isBlank(?implements))
24
+
25
+ FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "{language}"))
26
+ FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "{language}"))
27
+ }}
28
+ """
29
+
30
+ PROPERTIES_QUERY = """
31
+ SELECT ?class_ ?property_ ?name ?description ?value_type ?min_count ?max_count ?default
32
+ WHERE
33
+ {{
34
+ # CASE 1: Handling Blocks and Terminals
35
+ {{
36
+ VALUES ?type {{ imf:BlockType imf:TerminalType }}
37
+ ?class_ a ?type ;
38
+ sh:property ?propertyShape .
39
+ ?propertyShape sh:path ?property_ .
40
+
41
+ OPTIONAL {{ ?property_ skos:prefLabel ?name . }}
42
+ OPTIONAL {{ ?property_ skos:definition ?description . }}
43
+ OPTIONAL {{ ?property_ rdfs:range ?range . }}
44
+
45
+ OPTIONAL {{ ?propertyShape sh:minCount ?min_count . }}
46
+ OPTIONAL {{ ?propertyShape sh:maxCount ?max_count . }}
47
+ OPTIONAL {{ ?propertyShape sh:hasValue ?default . }}
48
+ OPTIONAL {{ ?propertyShape sh:class | sh:qualifiedValueShape/sh:class ?valueShape . }}
49
+ }}
50
+
51
+ UNION
52
+
53
+ # CASE 2: Handling Attributes
54
+ {{
55
+ ?class_ a imf:AttributeType .
56
+ BIND(xsd:anyURI AS ?valueShape)
57
+ BIND(imf:predicate AS ?property_)
58
+ ?class_ ?property_ ?defaultURI .
59
+ BIND(STR(?defaultURI) AS ?default)
60
+
61
+ }}
62
+
63
+ # Set the value type for the property based on sh:class, sh:qualifiedValueType or rdfs:range
64
+ BIND(IF(BOUND(?valueShape), ?valueShape, IF(BOUND(?range) , ?range , ?valueShape)) AS ?value_type)
65
+
66
+ FILTER (!isBlank(?property_))
67
+ FILTER (!bound(?class_) || !isBlank(?class_))
68
+ FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "{language}"))
69
+ FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "{language}"))
70
+ }}
71
+ """
72
+
73
+
74
+ class IMFImporter(BaseRDFImporter):
75
+ """Convert IMF Types provided as SHACL shapes to Input Rules."""
76
+
77
+ def _to_rules_components(
78
+ self,
79
+ ) -> dict:
80
+ classes, issue_list = parse_classes(self.graph, CLASSES_QUERY, self.language, self.issue_list)
81
+ self.issue_list = issue_list
82
+ properties, issue_list = parse_properties(self.graph, PROPERTIES_QUERY, self.language, self.issue_list)
83
+ self.issue_list = issue_list
84
+
85
+ components = {
86
+ "Metadata": self._metadata,
87
+ "Classes": list(classes.values()) if classes else [],
88
+ "Properties": list(properties.values()) if properties else [],
89
+ }
90
+
91
+ return components
@@ -26,7 +26,14 @@ ORDERED_CLASSES_QUERY = """SELECT ?class (count(?s) as ?instances )
26
26
  WHERE { ?s a ?class . }
27
27
  group by ?class order by DESC(?instances)"""
28
28
 
29
- INSTANCES_OF_CLASS_QUERY = """SELECT ?s WHERE { ?s a <class> . }"""
29
+
30
+ INSTANCES_OF_CLASS_QUERY = """SELECT ?s ?propertyCount WHERE { ?s a <class> . BIND ('Unknown' as ?propertyCount) }"""
31
+
32
+
33
+ INSTANCES_OF_CLASS_RICHNESS_ORDERED_QUERY = """SELECT ?s (COUNT(?p) as ?propertyCount)
34
+ WHERE { ?s a <class> ; ?p ?o . }
35
+ GROUP BY ?s
36
+ ORDER BY DESC(?propertyCount)"""
30
37
 
31
38
  INSTANCE_PROPERTIES_DEFINITION = """SELECT ?property (count(?property) as ?occurrence) ?dataType ?objectType
32
39
  WHERE {<instance_id> ?property ?value .
@@ -69,8 +76,15 @@ class InferenceImporter(BaseRDFImporter):
69
76
  data_model_id: (dm.DataModelId | tuple[str, str, str]) = DEFAULT_INFERENCE_DATA_MODEL_ID,
70
77
  max_number_of_instance: int = -1,
71
78
  non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
79
+ language: str = "en",
72
80
  ) -> "InferenceImporter":
73
- return super().from_graph_store(store, data_model_id, max_number_of_instance, non_existing_node_type)
81
+ return super().from_graph_store(
82
+ store,
83
+ data_model_id,
84
+ max_number_of_instance,
85
+ non_existing_node_type,
86
+ language,
87
+ )
74
88
 
75
89
  @classmethod
76
90
  def from_file(
@@ -79,8 +93,15 @@ class InferenceImporter(BaseRDFImporter):
79
93
  data_model_id: (dm.DataModelId | tuple[str, str, str]) = DEFAULT_INFERENCE_DATA_MODEL_ID,
80
94
  max_number_of_instance: int = -1,
81
95
  non_existing_node_type: UnknownEntity | AnyURI = DEFAULT_NON_EXISTING_NODE_TYPE,
96
+ language: str = "en",
82
97
  ) -> "InferenceImporter":
83
- return super().from_file(filepath, data_model_id, max_number_of_instance, non_existing_node_type)
98
+ return super().from_file(
99
+ filepath,
100
+ data_model_id,
101
+ max_number_of_instance,
102
+ non_existing_node_type,
103
+ language,
104
+ )
84
105
 
85
106
  @classmethod
86
107
  def from_json_file(
@@ -88,6 +109,7 @@ class InferenceImporter(BaseRDFImporter):
88
109
  filepath: Path,
89
110
  data_model_id: (dm.DataModelId | tuple[str, str, str]) = DEFAULT_INFERENCE_DATA_MODEL_ID,
90
111
  max_number_of_instance: int = -1,
112
+ language: str = "en",
91
113
  ) -> "InferenceImporter":
92
114
  raise NotImplementedError("JSON file format is not supported yet.")
93
115
 
@@ -97,6 +119,7 @@ class InferenceImporter(BaseRDFImporter):
97
119
  filepath: Path,
98
120
  data_model_id: (dm.DataModelId | tuple[str, str, str]) = DEFAULT_INFERENCE_DATA_MODEL_ID,
99
121
  max_number_of_instance: int = -1,
122
+ language: str = "en",
100
123
  ) -> "InferenceImporter":
101
124
  raise NotImplementedError("YAML file format is not supported yet.")
102
125
 
@@ -141,13 +164,19 @@ class InferenceImporter(BaseRDFImporter):
141
164
 
142
165
  self._add_uri_namespace_to_prefixes(cast(URIRef, class_uri), prefixes)
143
166
 
167
+ instances_query = (
168
+ INSTANCES_OF_CLASS_QUERY if self.max_number_of_instance == -1 else INSTANCES_OF_CLASS_RICHNESS_ORDERED_QUERY
169
+ )
170
+
144
171
  # Infers all the properties of the class
145
172
  for class_id, class_definition in classes.items():
146
- for (instance,) in self.graph.query( # type: ignore[misc]
147
- INSTANCES_OF_CLASS_QUERY.replace("class", class_definition["uri"])
173
+ for ( # type: ignore[misc]
174
+ instance,
175
+ _,
176
+ ) in self.graph.query( # type: ignore[misc]
177
+ instances_query.replace("class", class_definition["uri"])
148
178
  if self.max_number_of_instance < 0
149
- else INSTANCES_OF_CLASS_QUERY.replace("class", class_definition["uri"])
150
- + f" LIMIT {self.max_number_of_instance}"
179
+ else instances_query.replace("class", class_definition["uri"]) + f" LIMIT {self.max_number_of_instance}"
151
180
  ):
152
181
  for property_uri, occurrence, data_type_uri, object_type_uri in self.graph.query( # type: ignore[misc]
153
182
  INSTANCE_PROPERTIES_DEFINITION.replace("instance_id", instance)
@@ -226,6 +255,10 @@ class InferenceImporter(BaseRDFImporter):
226
255
 
227
256
  # Create multi-value properties otherwise single value
228
257
  for property_ in properties.values():
258
+ # Removes non-existing node type from value type prior final conversion to string
259
+ if len(property_["value_type"]) > 1 and str(self.non_existing_node_type) in property_["value_type"]:
260
+ property_["value_type"].remove(str(self.non_existing_node_type))
261
+
229
262
  if len(property_["value_type"]) > 1:
230
263
  property_["value_type"] = " | ".join([str(t) for t in property_["value_type"]])
231
264
  else:
@@ -0,0 +1,80 @@
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 parse_classes, parse_properties
6
+
7
+ CLASSES_QUERY = """SELECT ?class_ ?name ?description ?implements
8
+ WHERE {{
9
+
10
+ ?class_ a owl:Class .
11
+ OPTIONAL {{?class_ rdfs:subClassOf ?implements }}.
12
+ OPTIONAL {{?class_ rdfs:label|skos:prefLabel ?name }}.
13
+ OPTIONAL {{?class_ rdfs:comment|skos:definition ?description}} .
14
+
15
+
16
+ FILTER (!isBlank(?class_))
17
+ FILTER (!bound(?implements) || !isBlank(?implements))
18
+
19
+ FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "{language}"))
20
+ FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "{language}"))
21
+
22
+ }}
23
+ """
24
+
25
+ PROPERTIES_QUERY = """
26
+
27
+ SELECT ?class_ ?property_ ?name ?description ?value_type ?minCount ?maxCount ?default
28
+ WHERE {{
29
+ ?property_ a ?property_Type.
30
+ FILTER (?property_Type IN (owl:ObjectProperty, owl:DatatypeProperty ) )
31
+ OPTIONAL {{?property_ rdfs:domain ?class_ }}.
32
+ OPTIONAL {{?property_ rdfs:range ?value_type }}.
33
+ OPTIONAL {{?property_ rdfs:label|skos:prefLabel ?name }}.
34
+ OPTIONAL {{?property_ rdfs:comment|skos:definition ?description}}.
35
+ OPTIONAL {{?property_ owl:maxCardinality ?maxCount}}.
36
+ OPTIONAL {{?property_ owl:minCardinality ?minCount}}.
37
+
38
+ # FILTERS
39
+ FILTER (!isBlank(?property_))
40
+ FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "{language}"))
41
+ FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "{language}"))
42
+ }}
43
+ """
44
+
45
+
46
+ class OWLImporter(BaseRDFImporter):
47
+ """Convert OWL ontology to tables/ transformation rules / Excel file.
48
+
49
+ Args:
50
+ filepath: Path to OWL ontology
51
+
52
+ !!! Note
53
+ OWL Ontologies are information models which completeness varies. As such, constructing functional
54
+ data model directly will often be impossible, therefore the produced Rules object will be ill formed.
55
+ To avoid this, neat will automatically attempt to make the imported rules compliant by adding default
56
+ values for missing information, attaching dangling properties to default containers based on the
57
+ property type, etc.
58
+
59
+ One has to be aware that NEAT will be opinionated about how to make the ontology
60
+ compliant, and that the resulting rules may not be what you expect.
61
+
62
+ """
63
+
64
+ def _to_rules_components(
65
+ self,
66
+ ) -> dict:
67
+ classes, issue_list = parse_classes(self.graph, CLASSES_QUERY, self.language, self.issue_list)
68
+ self.issue_list = issue_list
69
+
70
+ # NeatError
71
+ properties, issue_list = parse_properties(self.graph, PROPERTIES_QUERY, self.language, self.issue_list)
72
+ self.issue_list = issue_list
73
+
74
+ components = {
75
+ "Metadata": self._metadata,
76
+ "Classes": list(classes.values()) if classes else [],
77
+ "Properties": list(properties.values()) if properties else [],
78
+ }
79
+
80
+ return components