Skip to content

Commit ea47a53

Browse files
committed
Merge branch 'main' into 2.1.x
2 parents 98bbae3 + f6924d5 commit ea47a53

File tree

91 files changed

+1580
-940
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+1580
-940
lines changed

CONTRIBUTING.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ It is based on the Semantic Aspect Meta Model (SAMM) and supports its use.
1616
# Contributing Source Code (using GitHub)
1717

1818
* We use this GitHub repository to track issues and feature requests.
19-
* For general discussions of the ESMF, modeling questions etc. we use
20-
the [community forum](https://www.eclipse.org/forums/index.php/f/617/).
2119
* For discussions specific to development, the preferred way is
2220
the [developer mailing list](https://accounts.eclipse.org/mailing-list/esmf-dev).
2321

core/esmf-aspect-meta-model-python/README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ https://projects.eclipse.org/projects/dt.esmf).
99
* [Download SAMM files](#download-samm-files)
1010
* [Download SAMM release](#download-samm-release)
1111
* [Download SAMM branch](#download-samm-branch)
12+
* [Input data handler usage](#input-data-handler-usage)
1213
* [Aspect Meta Model Loader usage](#aspect-meta-model-loader-usage)
1314
* [Samm Units](#samm-units)
1415
* [SAMM CLI wrapper class](#samm-cli-wrapper-class)
@@ -84,14 +85,47 @@ poetry run download-samm-branch
8485
```
8586
Link to all branches: [SAMM Releases](https://github.com/eclipse-esmf/esmf-semantic-aspect-meta-model/branches)
8687

88+
## Input data handler usage
89+
90+
The InputHandler is a general-purpose class designed for loading input data into an RDF graph.
91+
It easily accommodates different input sources such as local files (.ttl) or direct data strings containing
92+
RDF formatted data.
93+
94+
```python
95+
from esmf_aspect_meta_model_python.resolver.handler import InputHandler
96+
97+
# Instantiating the Handler
98+
# The InputHandler requires a path or data string upon instantiation, which defines the source of RDF data
99+
# local file
100+
model_path = "path/to/local/file/AspectName.ttl"
101+
handler = InputHandler(model_path)
102+
graph, aspect_urn = handler.get_rdf_graph()
103+
104+
# returns a tuple containing the RDF graph and the aspect URN derived from the provided data source
105+
```
106+
107+
```python
108+
from esmf_aspect_meta_model_python.resolver.handler import InputHandler
109+
110+
# Alternatively, if you have RDF data in a string format, you can directly pass it as follows:
111+
rdf_data_string = "your RDF data string here"
112+
handler = InputHandler(rdf_data_string)
113+
graph, aspect_urn = handler.get_rdf_graph()
114+
```
115+
87116
## Aspect Meta Model Loader usage
88117

89118
An Aspect of the Meta Model can be loaded as follows:
90119
```python
91120
from esmf_aspect_meta_model_python import AspectLoader
121+
from esmf_aspect_meta_model_python.resolver.handler import InputHandler
122+
123+
model_path = "absolute/path/to/turtle.ttl"
124+
handler = InputHandler(model_path)
125+
graph, aspect_urn = handler.get_rdf_graph()
92126

93127
loader = AspectLoader()
94-
model_elements = loader.load_aspect_model("absolute/path/to/turtle.ttl")
128+
model_elements = loader.load_aspect_model(graph, aspect_urn)
95129
aspect = model_elements[0]
96130

97131
# or you can provide an Aspect URN

core/esmf-aspect-meta-model-python/esmf_aspect_meta_model_python/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,4 @@
8787
)
8888
from .loader.aspect_loader import AspectLoader
8989
from .loader.samm_graph import SAMMGraph
90+
from .resolver.handler import InputHandler

core/esmf-aspect-meta-model-python/esmf_aspect_meta_model_python/loader/aspect_loader.py

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
#
1010
# SPDX-License-Identifier: MPL-2.0
1111

12-
from pathlib import Path
13-
from typing import Union
12+
from typing import List
1413

15-
from esmf_aspect_meta_model_python.loader.default_element_cache import DefaultElementCache
14+
from rdflib import Graph, URIRef
15+
16+
from esmf_aspect_meta_model_python.base.aspect import Aspect
1617
from esmf_aspect_meta_model_python.loader.samm_graph import SAMMGraph
1718

1819

@@ -24,55 +25,37 @@ class AspectLoader:
2425
The default cache strategy ignores inline defined elements.
2526
"""
2627

27-
def __init__(self) -> None:
28-
self._cache = DefaultElementCache()
29-
self._graph = SAMMGraph()
30-
31-
def get_graph(self) -> SAMMGraph:
32-
"""Get SAMM graph.
28+
def __init__(self):
29+
self.graph = None
3330

34-
:return: parsed SAMM Aspect model Graph.
31+
def load_aspect_model(self, rdf_graph: Graph, aspect_urn: URIRef | str = "") -> List[Aspect]:
3532
"""
36-
return self._graph
37-
38-
def get_samm_version(self) -> str:
39-
"""Get SAMM version of the graph."""
40-
return self._graph.get_samm_version()
33+
Creates a python object(s) to represent the Aspect model graph.
4134
42-
@staticmethod
43-
def convert_file_path(file_path: Union[str, Path]) -> str:
44-
"""Convert file_path to the string.
45-
46-
:param file_path: path to model file
47-
"""
48-
if isinstance(file_path, Path):
49-
file_path = str(file_path)
35+
This function takes an RDF graph and a URN for an Aspect node and converts it into
36+
a set of structured and connected Python objects that represents the Aspect model graph. The output is a
37+
list of Python objects derived from the RDF graph centered around the specified Aspect node.
5038
51-
tmp_path = Path(file_path)
52-
if not tmp_path.exists():
53-
raise FileNotFoundError(f"Could not found the file {tmp_path}")
39+
Args:
40+
rdf_graph (RDFGraph): The RDF graph from which to create the model.
41+
aspect_urn (str): The URN identifier for the main Aspect node in the RDF graph.
5442
55-
return file_path
43+
Returns:
44+
list: A list of Python objects that represent the Aspect elements of the Aspect model graph.
5645
57-
def _reset_graph(self):
58-
"""Reset graph and cache data."""
59-
if self._graph:
60-
self._graph = SAMMGraph()
46+
Examples:
47+
# Assuming 'graph' is a predefined RDFGraph object and 'aspect_urn' is defined:
48+
aspect_model = create_aspect_model_graph(graph, "urn:example:aspectNode")
49+
print(aspect_model) # This prints the list of Python objects.
6150
62-
if self._cache:
63-
self._cache = DefaultElementCache()
64-
65-
def load_aspect_model(self, file_path: Union[Path, str]):
66-
"""Load aspect model to RDF GRAPH.
67-
68-
Create an aspect object with all the including properties and operations with the turtle file
69-
70-
:param file_path: path to the turtle file. Can be either a string or a Path object
71-
:return: instance of the aspect
51+
Notes:
52+
It's crucial that the aspect_urn corresponds to a valid Aspect node within the RDF graph;
53+
otherwise, the function may not perform as expected.
7254
"""
73-
file_path = self.convert_file_path(file_path)
74-
self._reset_graph()
75-
_ = self._graph.parse(file_path)
76-
loaded_aspect_model = self._graph.to_python()
55+
self.graph = SAMMGraph(graph=rdf_graph)
56+
loaded_aspect_model = self.graph.to_python(aspect_urn)
57+
58+
# Add check that loaded_aspect_model is not empty
59+
# Add check that aspect_urn is ref to an Aspect node
7760

7861
return loaded_aspect_model
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright (c) 2023 Robert Bosch Manufacturing Solutions GmbH
2+
#
3+
# See the AUTHORS file(s) distributed with this work for additional
4+
# information regarding authorship.
5+
#
6+
# This Source Code Form is subject to the terms of the Mozilla Public
7+
# License, v. 2.0. If a copy of the MPL was not distributed with this
8+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
9+
#
10+
# SPDX-License-Identifier: MPL-2.0
11+
12+
from typing import Any, Dict, Optional
13+
14+
import rdflib # type: ignore
15+
16+
from rdflib.term import Node
17+
18+
from esmf_aspect_meta_model_python.base.characteristics.state import State
19+
from esmf_aspect_meta_model_python.impl.characteristics.default_state import DefaultState
20+
from esmf_aspect_meta_model_python.loader.instantiator.constants import DATA_TYPE_ERROR_MSG
21+
from esmf_aspect_meta_model_python.loader.instantiator.enumeration_instantiator import EnumerationInstantiator
22+
from esmf_aspect_meta_model_python.loader.rdf_helper import RdfHelper
23+
from esmf_aspect_meta_model_python.vocabulary.SAMM import SAMM
24+
from esmf_aspect_meta_model_python.vocabulary.SAMMC import SAMMC
25+
26+
27+
class StateInstantiator(EnumerationInstantiator):
28+
def _create_instance(self, element_node: Node) -> State:
29+
data_type = self._get_data_type(element_node)
30+
if data_type is None:
31+
raise TypeError(DATA_TYPE_ERROR_MSG)
32+
33+
meta_model_base_attributes = self._get_base_attributes(element_node)
34+
value_collection_node = self._aspect_graph.value(
35+
subject=element_node,
36+
predicate=self._sammc.get_urn(SAMMC.values),
37+
)
38+
value_nodes = RdfHelper.get_rdf_list_values(value_collection_node, self._aspect_graph)
39+
values = [self.__to_state_node_value(value_node) for value_node in value_nodes]
40+
41+
defaultValue = self._aspect_graph.value(
42+
subject=element_node,
43+
predicate=self._sammc.get_urn(SAMMC.default_value),
44+
)
45+
default = self.__to_state_node_value(defaultValue)
46+
47+
return DefaultState(meta_model_base_attributes, data_type, values, default)
48+
49+
def __to_state_node_value(self, value_node: Optional[Node]) -> Dict:
50+
"""
51+
This method instantiates one possible value of a state.
52+
:param value_node: Node of the Graph that represents one state value.
53+
The Argument can either be a Literal or a URIRef.
54+
- If value_node is a Literal it will represent e.g. a string or an integer value
55+
- If value_node is a URIRef it will represent a value of a ComplexType
56+
:return: the one generated value of the state
57+
"""
58+
if isinstance(value_node, rdflib.Literal):
59+
# value represents a simple data type
60+
return value_node.toPython()
61+
62+
elif isinstance(value_node, rdflib.URIRef):
63+
# value represents a complex data type
64+
value = {}
65+
value_node_properties = self._aspect_graph.predicate_objects(value_node)
66+
for property_urn, property_value in value_node_properties:
67+
if property_urn != rdflib.RDF.type and isinstance(property_urn, str):
68+
property_name = property_urn.split("#")[1]
69+
actual_value: Optional[Any]
70+
if self.__is_collection_value(property_urn):
71+
actual_value = self.__instantiate_enum_collection(property_value)
72+
else:
73+
actual_value = self.__to_state_node_value(property_value)
74+
value[property_name] = actual_value
75+
76+
value_node_name = value_node.split("#")[1]
77+
value_key = self._samm.get_urn(SAMM.name).toPython()
78+
value[value_key] = value_node_name # type: ignore
79+
return value
80+
81+
else:
82+
# illegal node type for state value (e.g., Blank Node)
83+
raise TypeError(
84+
f"Every value of an state must either be a Literal (string, int, etc.) or "
85+
f"a URI reference to a ComplexType. Values of type {type(value_node).__name__} are not allowed"
86+
)

0 commit comments

Comments
 (0)