Commit 7c90fb8a authored by Torben Miny's avatar Torben Miny
Browse files

Merge branch fix/annotatedRelationshipElement_annotation with...

Merge branch fix/annotatedRelationshipElement_annotation with refs/heads/master into refs/merge-requests/38/train
parents 6b2d997b b903020a
Pipeline #286099 passed with stage
in 3 minutes and 6 seconds
...@@ -562,17 +562,9 @@ class AASFromJsonDecoder(json.JSONDecoder): ...@@ -562,17 +562,9 @@ class AASFromJsonDecoder(json.JSONDecoder):
kind=cls._get_kind(dct)) kind=cls._get_kind(dct))
cls._amend_abstract_attributes(ret, dct) cls._amend_abstract_attributes(ret, dct)
if 'annotation' in dct: if 'annotation' in dct:
for annotation_data in _get_ts(dct, 'annotation', list): for element in _get_ts(dct, "annotation", list):
try: if _expect_type(element, model.DataElement, str(ret), cls.failsafe):
ret.annotation.add(cls._construct_aas_reference(annotation_data, model.DataElement)) ret.annotation.add(element)
except (KeyError, TypeError) as e:
error_message = \
"Error while trying to convert JSON object into annotation Reference for {}: {} >>> {}".format(
ret, e, pprint.pformat(dct, depth=2, width=2 ** 14, compact=True))
if cls.failsafe:
logger.error(error_message, exc_info=e)
else:
raise type(e)(error_message) from e
return ret return ret
@classmethod @classmethod
......
...@@ -588,11 +588,11 @@ def _construct_submodel_element(element: etree.Element, failsafe: bool, **kwargs ...@@ -588,11 +588,11 @@ def _construct_submodel_element(element: etree.Element, failsafe: bool, **kwargs
"submodelElementCollection": _construct_submodel_element_collection "submodelElementCollection": _construct_submodel_element_collection
}.items()} }.items()}
if element.tag not in submodel_elements: if element.tag not in submodel_elements:
return _construct_data_element(element, failsafe, abstract_element="submodel element", **kwargs) return _construct_data_element(element, failsafe, abstract_class_name="submodel element", **kwargs)
return submodel_elements[element.tag](element, failsafe, **kwargs) return submodel_elements[element.tag](element, failsafe, **kwargs)
def _construct_data_element(element: etree.Element, failsafe: bool, abstract_element: str = "data element", def _construct_data_element(element: etree.Element, failsafe: bool, abstract_class_name: str = "data element",
**kwargs: Any) -> model.DataElement: **kwargs: Any) -> model.DataElement:
data_elements: Dict[str, Callable[..., model.DataElement]] = {NS_AAS + k: v for k, v in { data_elements: Dict[str, Callable[..., model.DataElement]] = {NS_AAS + k: v for k, v in {
"blob": _construct_blob, "blob": _construct_blob,
...@@ -603,7 +603,7 @@ def _construct_data_element(element: etree.Element, failsafe: bool, abstract_ele ...@@ -603,7 +603,7 @@ def _construct_data_element(element: etree.Element, failsafe: bool, abstract_ele
"referenceElement": _construct_reference_element, "referenceElement": _construct_reference_element,
}.items()} }.items()}
if element.tag not in data_elements: if element.tag not in data_elements:
raise KeyError(_element_pretty_identifier(element) + f" is not a valid {abstract_element}!") raise KeyError(_element_pretty_identifier(element) + f" is not a valid {abstract_class_name}!")
return data_elements[element.tag](element, failsafe, **kwargs) return data_elements[element.tag](element, failsafe, **kwargs)
...@@ -631,17 +631,13 @@ def _construct_operation_variable(element: etree.Element, _failsafe: bool, **_kw ...@@ -631,17 +631,13 @@ def _construct_operation_variable(element: etree.Element, _failsafe: bool, **_kw
def _construct_annotated_relationship_element(element: etree.Element, failsafe: bool, **_kwargs: Any) \ def _construct_annotated_relationship_element(element: etree.Element, failsafe: bool, **_kwargs: Any) \
-> model.AnnotatedRelationshipElement: -> model.AnnotatedRelationshipElement:
annotated_relationship_element = model.AnnotatedRelationshipElement( annotated_relationship_element = _construct_relationship_element_internal(
_child_text_mandatory(element, NS_AAS + "idShort"), element, failsafe, object_class=model.AnnotatedRelationshipElement
_child_construct_mandatory(element, NS_AAS + "first", _construct_referable_reference),
_child_construct_mandatory(element, NS_AAS + "second", _construct_referable_reference),
kind=_get_modeling_kind(element)
) )
annotations = _get_child_mandatory(element, NS_AAS + "annotations") for data_element in _get_child_mandatory(element, NS_AAS + "annotations"):
for data_element_ref in _failsafe_construct_multiple(annotations.findall(NS_AAS + "reference"), constructed = _failsafe_construct(data_element, _construct_data_element, failsafe)
_construct_data_element_reference, failsafe): if constructed is not None:
annotated_relationship_element.annotation.add(data_element_ref) annotated_relationship_element.annotation.add(constructed)
_amend_abstract_attributes(annotated_relationship_element, element, failsafe)
return annotated_relationship_element return annotated_relationship_element
...@@ -793,7 +789,15 @@ def _construct_reference_element(element: etree.Element, failsafe: bool, **_kwar ...@@ -793,7 +789,15 @@ def _construct_reference_element(element: etree.Element, failsafe: bool, **_kwar
def _construct_relationship_element(element: etree.Element, failsafe: bool, **_kwargs: Any) \ def _construct_relationship_element(element: etree.Element, failsafe: bool, **_kwargs: Any) \
-> model.RelationshipElement: -> model.RelationshipElement:
relationship_element = model.RelationshipElement( return _construct_relationship_element_internal(element, failsafe, model.RelationshipElement, **_kwargs)
RE = TypeVar("RE", bound=model.RelationshipElement)
def _construct_relationship_element_internal(element: etree.Element, failsafe: bool,
object_class: Type[RE], **_kwargs: Any) -> RE:
relationship_element = object_class(
_child_text_mandatory(element, NS_AAS + "idShort"), _child_text_mandatory(element, NS_AAS + "idShort"),
_child_construct_mandatory(element, NS_AAS + "first", _construct_referable_reference), _child_construct_mandatory(element, NS_AAS + "first", _construct_referable_reference),
_child_construct_mandatory(element, NS_AAS + "second", _construct_referable_reference), _child_construct_mandatory(element, NS_AAS + "second", _construct_referable_reference),
......
...@@ -746,8 +746,8 @@ def annotated_relationship_element_to_xml(obj: model.AnnotatedRelationshipElemen ...@@ -746,8 +746,8 @@ def annotated_relationship_element_to_xml(obj: model.AnnotatedRelationshipElemen
et_annotated_relationship_element = relationship_element_to_xml(obj, tag) et_annotated_relationship_element = relationship_element_to_xml(obj, tag)
et_annotations = _generate_element(name=NS_AAS+"annotations") et_annotations = _generate_element(name=NS_AAS+"annotations")
if obj.annotation: if obj.annotation:
for reference in obj.annotation: for data_element in obj.annotation:
et_annotations.append(reference_to_xml(reference)) et_annotations.append(data_element_to_xml(data_element))
et_annotated_relationship_element.append(et_annotations) et_annotated_relationship_element.append(et_annotations)
return et_annotated_relationship_element return et_annotated_relationship_element
......
...@@ -360,14 +360,15 @@ class AASDataChecker(DataChecker): ...@@ -360,14 +360,15 @@ class AASDataChecker(DataChecker):
:return: :return:
""" """
self.check_relationship_element_equal(object_, expected_value) self.check_relationship_element_equal(object_, expected_value)
self.check_contained_element_length(object_, 'annotation', model.AASReference, self.check_contained_element_length(object_, 'annotation', model.DataElement,
len(expected_value.annotation)) len(expected_value.annotation))
for expected_ref in expected_value.annotation: for expected_data_element in expected_value.annotation:
ref = self._find_reference(expected_ref, object_.annotation) self.check(
if self.check(ref is not None, 'Annotated Reference {} must exist'.format(repr(expected_ref))): object_.annotation.get(expected_data_element.id_short) is not None,
self._check_reference_equal(ref, expected_ref) # type: ignore 'Annotation {} must exist'.format(repr(expected_data_element))
)
found_elements = self._find_extra_reference(object_.annotation, expected_value.annotation) found_elements = self._find_extra_elements_by_id_short(object_.annotation, expected_value.annotation)
self.check(found_elements == set(), 'Annotated Reference {} must not have extra ' self.check(found_elements == set(), 'Annotated Reference {} must not have extra '
'references'.format(repr(object_)), 'references'.format(repr(object_)),
value=found_elements) value=found_elements)
......
...@@ -433,11 +433,16 @@ def create_example_submodel() -> model.Submodel: ...@@ -433,11 +433,16 @@ def create_example_submodel() -> model.Submodel:
value='ExampleProperty2', value='ExampleProperty2',
id_type=model.KeyType.IDSHORT),), id_type=model.KeyType.IDSHORT),),
model.Property), model.Property),
annotation={model.AASReference((model.Key(type_=model.KeyElements.PROPERTY, annotation={model.Property(id_short="ExampleAnnotatedProperty",
local=True, value_type=model.datatypes.String,
value='ExampleProperty3', value='exampleValue',
id_type=model.KeyType.IDSHORT),), parent=None),
model.Property)}, model.Range(id_short="ExampleAnnotatedRange",
value_type=model.datatypes.Integer,
min_=1,
max_=5,
parent=None)
},
category='PARAMETER', category='PARAMETER',
description={'en-us': 'Example AnnotatedRelationshipElement object', description={'en-us': 'Example AnnotatedRelationshipElement object',
'de': 'Beispiel AnnotatedRelationshipElement Element'}, 'de': 'Beispiel AnnotatedRelationshipElement Element'},
......
...@@ -199,11 +199,16 @@ def create_example_submodel() -> model.Submodel: ...@@ -199,11 +199,16 @@ def create_example_submodel() -> model.Submodel:
value='ExampleProperty', value='ExampleProperty',
id_type=model.KeyType.IDSHORT),), id_type=model.KeyType.IDSHORT),),
model.Property), model.Property),
annotation={model.AASReference((model.Key(type_=model.KeyElements.PROPERTY, annotation={model.Property(id_short="ExampleAnnotatedProperty",
local=True, value_type=model.datatypes.String,
value='ExampleProperty', value='exampleValue',
id_type=model.KeyType.IDSHORT),), parent=None),
model.Property)}, model.Range(id_short="ExampleAnnotatedRange",
value_type=model.datatypes.Integer,
min_=1,
max_=5,
parent=None)
},
category='PARAMETER', category='PARAMETER',
description={'en-us': 'Example AnnotatedRelationshipElement object', description={'en-us': 'Example AnnotatedRelationshipElement object',
'de': 'Beispiel AnnotatedRelationshipElement Element'}, 'de': 'Beispiel AnnotatedRelationshipElement Element'},
......
...@@ -681,7 +681,7 @@ class RelationshipElement(SubmodelElement): ...@@ -681,7 +681,7 @@ class RelationshipElement(SubmodelElement):
self.second: base.AASReference = second self.second: base.AASReference = second
class AnnotatedRelationshipElement(RelationshipElement): class AnnotatedRelationshipElement(RelationshipElement, base.Namespace):
""" """
An annotated relationship element is a relationship element that can be annotated with additional data elements. An annotated relationship element is a relationship element that can be annotated with additional data elements.
...@@ -692,7 +692,7 @@ class AnnotatedRelationshipElement(RelationshipElement): ...@@ -692,7 +692,7 @@ class AnnotatedRelationshipElement(RelationshipElement):
id_short: str, id_short: str,
first: base.AASReference, first: base.AASReference,
second: base.AASReference, second: base.AASReference,
annotation: Optional[Set[base.AASReference[DataElement]]] = None, annotation: Optional[Iterable[DataElement]] = None,
category: Optional[str] = None, category: Optional[str] = None,
description: Optional[base.LangStringSet] = None, description: Optional[base.LangStringSet] = None,
parent: Optional[base.Namespace] = None, parent: Optional[base.Namespace] = None,
...@@ -703,7 +703,7 @@ class AnnotatedRelationshipElement(RelationshipElement): ...@@ -703,7 +703,7 @@ class AnnotatedRelationshipElement(RelationshipElement):
Initializer of AnnotatedRelationshipElement Initializer of AnnotatedRelationshipElement
:param id_short: Identifying string of the element within its name space. (from base.Referable) :param id_short: Identifying string of the element within its name space. (from base.Referable)
:param annotation: Unordered list of annotations that hold for the relationship between to elements :param annotation: Unordered list of annotations that hold for the relationship between two elements
:param category: The category is a value that gives further meta information w.r.t. to the class of the element. :param category: The category is a value that gives further meta information w.r.t. to the class of the element.
It affects the expected existence of attributes and the applicability of constraints. It affects the expected existence of attributes and the applicability of constraints.
(from base.Referable) (from base.Referable)
...@@ -721,7 +721,10 @@ class AnnotatedRelationshipElement(RelationshipElement): ...@@ -721,7 +721,10 @@ class AnnotatedRelationshipElement(RelationshipElement):
""" """
super().__init__(id_short, first, second, category, description, parent, semantic_id, qualifier, kind) super().__init__(id_short, first, second, category, description, parent, semantic_id, qualifier, kind)
self.annotation: Set[base.AASReference[DataElement]] = set() if annotation is None else annotation if annotation is None:
self.annotation: base.NamespaceSet[DataElement] = base.NamespaceSet(self)
else:
self.annotation = base.NamespaceSet(self, annotation)
class OperationVariable: class OperationVariable:
......
...@@ -141,7 +141,6 @@ class ComplianceToolTest(unittest.TestCase): ...@@ -141,7 +141,6 @@ class ComplianceToolTest(unittest.TestCase):
def test_json_create_example(self) -> None: def test_json_create_example(self) -> None:
file_path = os.path.join(os.path.dirname(aas.compliance_tool.__file__), 'cli.py') file_path = os.path.join(os.path.dirname(aas.compliance_tool.__file__), 'cli.py')
test_file_path = os.path.join(os.path.dirname(__file__), 'files')
file, filename = tempfile.mkstemp(suffix=".json") file, filename = tempfile.mkstemp(suffix=".json")
os.close(file) os.close(file)
...@@ -202,7 +201,6 @@ class ComplianceToolTest(unittest.TestCase): ...@@ -202,7 +201,6 @@ class ComplianceToolTest(unittest.TestCase):
def test_xml_create_example(self) -> None: def test_xml_create_example(self) -> None:
file_path = os.path.join(os.path.dirname(aas.compliance_tool.__file__), 'cli.py') file_path = os.path.join(os.path.dirname(aas.compliance_tool.__file__), 'cli.py')
test_file_path = os.path.join(os.path.dirname(__file__), 'files')
file, filename = tempfile.mkstemp(suffix=".xml") file, filename = tempfile.mkstemp(suffix=".xml")
os.close(file) os.close(file)
......
...@@ -197,21 +197,19 @@ class AASDataCheckerTest(unittest.TestCase): ...@@ -197,21 +197,19 @@ class AASDataCheckerTest(unittest.TestCase):
id_type=model.KeyType.IDSHORT),), id_type=model.KeyType.IDSHORT),),
model.Property), model.Property),
annotation={ annotation={
model.AASReference((model.Key(type_=model.KeyElements.PROPERTY, model.Property(id_short="ExampleAnnotatedProperty",
local=True, value_type=model.datatypes.String,
value='ExampleProperty', value='exampleValue',
id_type=model.KeyType.IDSHORT),), parent=None)
model.Property)
}) })
checker = AASDataChecker(raise_immediately=False) checker = AASDataChecker(raise_immediately=False)
checker.check_annotated_relationship_element_equal(rel1, rel2) checker.check_annotated_relationship_element_equal(rel1, rel2)
self.assertEqual(2, sum(1 for _ in checker.failed_checks)) self.assertEqual(2, sum(1 for _ in checker.failed_checks))
checker_iterator = iter(checker.failed_checks) checker_iterator = iter(checker.failed_checks)
self.assertEqual("FAIL: AnnotatedRelationshipElement[test] must contain 1 AASReferences " self.assertEqual("FAIL: AnnotatedRelationshipElement[test] must contain 1 DataElements "
"(count=0)", "(count=0)",
repr(next(checker_iterator))) repr(next(checker_iterator)))
self.assertEqual("FAIL: Annotated Reference AASReference(type=Property, key=(Key(local=True, id_type=IDSHORT, " self.assertEqual("FAIL: Annotation Property[test / ExampleAnnotatedProperty] must exist ()",
"value=ExampleProperty),)) must exist ()",
repr(next(checker_iterator))) repr(next(checker_iterator)))
def test_submodel_checker(self): def test_submodel_checker(self):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment