diff --git a/strictdoc/backend/reqif/p01_sdoc/reqif_to_sdoc_converter.py b/strictdoc/backend/reqif/p01_sdoc/reqif_to_sdoc_converter.py index 2f129905a..8d0b559db 100644 --- a/strictdoc/backend/reqif/p01_sdoc/reqif_to_sdoc_converter.py +++ b/strictdoc/backend/reqif/p01_sdoc/reqif_to_sdoc_converter.py @@ -1,5 +1,5 @@ # mypy: disable-error-code="no-untyped-def,union-attr,operator" -from typing import Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Tuple, Union from reqif.models.reqif_data_type import ReqIFDataTypeDefinitionEnumeration from reqif.models.reqif_spec_object import ReqIFSpecObject @@ -34,7 +34,9 @@ GrammarElementFieldMultipleChoice, GrammarElementFieldSingleChoice, GrammarElementFieldString, + GrammarElementRelationParent, ) +from strictdoc.helpers.ordered_set import OrderedSet from strictdoc.helpers.string import ( create_safe_requirement_tag_string, ensure_newline, @@ -49,6 +51,12 @@ def __init__(self, *, enable_mid: bool, import_markup: Optional[str]): self.map_spec_object_type_identifier_to_grammar_node_tags: Dict[ str, GrammarElement ] = {} + self.map_source_target_pairs_to_spec_relation_types: Dict[ + Tuple[str, str], Any + ] = {} + self.unique_grammar_element_relations: Dict[ + GrammarElement, OrderedSet[Tuple[str, Optional[str]]] + ] = {} class P01_ReqIFToSDocConverter: @@ -69,6 +77,16 @@ def convert_reqif_bundle( ): return [] + for ( + spec_relation_ + ) in reqif_bundle.core_content.req_if_content.spec_relations: + spec_relation_type_ = reqif_bundle.lookup.get_spec_type_by_ref( + spec_relation_.relation_type_ref + ) + context.map_source_target_pairs_to_spec_relation_types[ + (spec_relation_.source, spec_relation_.target) + ] = spec_relation_type_ + documents: List[SDocDocument] = [] for ( specification @@ -131,7 +149,9 @@ def _create_document_from_reqif_specification( # StrictDoc document is not created with irrelevant grammar elements that # actually belong to other Specifications in this ReqIF bundle. # Using Dict as an ordered set. - spec_object_type_identifiers_used_by_this_document: Dict[str, None] = {} + spec_object_type_identifiers_used_by_this_document: OrderedSet[str] = ( + OrderedSet() + ) def node_converter_lambda( current_hierarchy_, @@ -140,9 +160,9 @@ def node_converter_lambda( spec_object = reqif_bundle.get_spec_object_by_ref( current_hierarchy_.spec_object ) - spec_object_type_identifiers_used_by_this_document[ + spec_object_type_identifiers_used_by_this_document.add( spec_object.spec_object_type - ] = None + ) is_section = P01_ReqIFToSDocConverter.is_spec_object_section( spec_object, @@ -181,7 +201,7 @@ def node_converter_lambda( elements: List[GrammarElement] = [] for ( spec_object_type_identifier_ - ) in spec_object_type_identifiers_used_by_this_document.keys(): + ) in spec_object_type_identifiers_used_by_this_document: spec_object_type: ReqIFSpecObjectType = ( reqif_bundle.lookup.get_spec_type_by_ref( spec_object_type_identifier_ @@ -194,7 +214,13 @@ def node_converter_lambda( spec_object_type_identifier_ ] ) + if len(grammar_element.relations) == 0: + grammar_element.relations = create_default_relations( + grammar_element + ) + elements.append(grammar_element) + grammar: DocumentGrammar if len(elements) > 0: grammar = DocumentGrammar(parent=document, elements=elements) @@ -288,9 +314,6 @@ def create_grammar_element_from_spec_object_type( fields=fields, relations=[], ) - requirement_element.relations = create_default_relations( - requirement_element - ) return requirement_element @staticmethod @@ -523,6 +546,45 @@ def create_requirement_from_spec_object( ) parent_refs: List[Reference] = [] for spec_object_parent in spec_object_parents: + spec_relation_type = ( + context.map_source_target_pairs_to_spec_relation_types[ + (spec_object.identifier, spec_object_parent) + ] + ) + + relation_role = ( + spec_relation_type.long_name + if spec_relation_type.long_name is not None + else None + ) + if relation_role == "Parent": + relation_role = None + + if ( + grammar_element + not in context.unique_grammar_element_relations + ): + context.unique_grammar_element_relations[ + grammar_element + ] = OrderedSet() + + if ( + "Parent", + relation_role, + ) not in context.unique_grammar_element_relations[ + grammar_element + ]: + context.unique_grammar_element_relations[ + grammar_element + ].add(("Parent", relation_role)) + grammar_element.relations.append( + GrammarElementRelationParent( + parent=grammar_element, + relation_type="Parent", + relation_role=relation_role, + ) + ) + parent_spec_object_parent = ( reqif_bundle.lookup.get_spec_object_by_ref( spec_object_parent @@ -535,7 +597,7 @@ def create_requirement_from_spec_object( parent_spec_object_parent.attribute_map[ foreign_key_id_or_none ].value, - role=None, + role=relation_role, ) ) if len(parent_refs) > 0: diff --git a/strictdoc/backend/reqif/p01_sdoc/sdoc_to_reqif_converter.py b/strictdoc/backend/reqif/p01_sdoc/sdoc_to_reqif_converter.py index 57a4e0234..f34b7a28f 100644 --- a/strictdoc/backend/reqif/p01_sdoc/sdoc_to_reqif_converter.py +++ b/strictdoc/backend/reqif/p01_sdoc/sdoc_to_reqif_converter.py @@ -3,7 +3,7 @@ import uuid from collections import defaultdict from enum import Enum -from typing import Dict, List +from typing import Dict, List, Optional, Tuple from reqif.models.reqif_core_content import ReqIFCoreContent from reqif.models.reqif_data_type import ( @@ -22,6 +22,7 @@ SpecAttributeDefinition, ) from reqif.models.reqif_spec_relation import ReqIFSpecRelation +from reqif.models.reqif_spec_relation_type import ReqIFSpecRelationType from reqif.models.reqif_specification import ReqIFSpecification from reqif.models.reqif_specification_type import ReqIFSpecificationType from reqif.models.reqif_types import SpecObjectAttributeType @@ -29,7 +30,6 @@ from reqif.reqif_bundle import ReqIFBundle from strictdoc.backend.reqif.sdoc_reqif_fields import ( - SDOC_SPEC_RELATION_PARENT_TYPE_SINGLETON, SDOC_SPECIFICATION_TYPE_SINGLETON, SDOC_TO_REQIF_FIELD_MAP, ReqIFChapterField, @@ -38,18 +38,22 @@ from strictdoc.backend.sdoc.models.document import SDocDocument from strictdoc.backend.sdoc.models.document_grammar import DocumentGrammar from strictdoc.backend.sdoc.models.node import SDocNode -from strictdoc.backend.sdoc.models.reference import ParentReqReference +from strictdoc.backend.sdoc.models.reference import ( + ChildReqReference, + ParentReqReference, + Reference, +) from strictdoc.backend.sdoc.models.section import SDocSection from strictdoc.backend.sdoc.models.type_system import ( GrammarElementField, GrammarElementFieldMultipleChoice, GrammarElementFieldSingleChoice, GrammarElementFieldString, - ReferenceType, ) from strictdoc.core.document_iterator import DocumentCachingIterator from strictdoc.core.document_tree import DocumentTree from strictdoc.helpers.cast import assert_cast +from strictdoc.helpers.ordered_set import OrderedSet from strictdoc.helpers.string import escape @@ -69,10 +73,15 @@ def __init__(self, *, multiline_is_xhtml: bool, enable_mid: bool): self.multiline_is_xhtml: bool = multiline_is_xhtml self.enable_mid: bool = enable_mid self.map_uid_to_spec_objects: Dict[str, ReqIFSpecObject] = {} - self.map_uid_to_parent_uids: Dict[str, List[str]] = {} + self.map_node_uids_to_their_relations: Dict[str, List[Reference]] = ( + defaultdict(list) + ) self.map_grammar_node_tags_to_spec_object_type: Dict[ SDocDocument, Dict[str, ReqIFSpecObjectType] ] = defaultdict(dict) + self.map_spec_relation_tuple_to_spec_relation_type: Dict[ + Tuple[str, Optional[str]], ReqIFSpecRelationType + ] = {} class P01_SDocToReqIFObjectConverter: @@ -334,18 +343,31 @@ def convert_document_tree( for ( requirement_id, - parent_uids, - ) in context.map_uid_to_parent_uids.items(): + node_relations_, + ) in context.map_node_uids_to_their_relations.items(): spec_object = context.map_uid_to_spec_objects[requirement_id] - for parent_uid in parent_uids: - parent_spec_object = context.map_uid_to_spec_objects[parent_uid] + for node_relation_ in node_relations_: + # For now, the File-relations are not supported. + if not isinstance( + node_relation_, (ParentReqReference, ChildReqReference) + ): + continue + related_node_uid = node_relation_.ref_uid + parent_spec_object = context.map_uid_to_spec_objects[ + related_node_uid + ] + spec_relation_type: ReqIFSpecRelationType = ( + context.map_spec_relation_tuple_to_spec_relation_type[ + (node_relation_.ref_type, node_relation_.role) + ] + ) spec_relations.append( ReqIFSpecRelation( xml_node=None, description=None, identifier=generate_unique_identifier("SPEC-RELATION"), last_change=None, - relation_type_ref=SDOC_SPEC_RELATION_PARENT_TYPE_SINGLETON, + relation_type_ref=spec_relation_type.identifier, source=spec_object.identifier, target=parent_spec_object.identifier, values_attribute=None, @@ -544,18 +566,10 @@ def _convert_requirement_to_spec_object( attributes.append(attribute) if requirement.reserved_uid is not None: - parent_references: List[str] = [] - for reference in requirement.relations: - if reference.ref_type != ReferenceType.PARENT: - continue - parent_reference: ParentReqReference = assert_cast( - reference, ParentReqReference - ) - parent_references.append(parent_reference.ref_uid) - - context.map_uid_to_parent_uids[requirement.reserved_uid] = ( - parent_references - ) + if len(requirement.relations) > 0: + context.map_node_uids_to_their_relations[ + requirement.reserved_uid + ] = requirement.relations spec_object_type: ReqIFSpecObjectType = ( context.map_grammar_node_tags_to_spec_object_type[ @@ -670,7 +684,31 @@ def _convert_document_grammar_to_spec_types( ] = section_spec_type spec_object_types.append(section_spec_type) - return spec_object_types + # Using dict as an ordered set. + spec_relation_tuples: OrderedSet = OrderedSet() + for element_ in grammar.elements: + for relation_ in element_.relations: + spec_relation_tuples.add( + (relation_.relation_type, relation_.relation_role) + ) + + spec_relation_types: List = [] + for spec_relation_tuple_ in spec_relation_tuples: + spec_relation_type_name = ( + spec_relation_tuple_[1] + if spec_relation_tuple_[1] is not None + else spec_relation_tuple_[0] + ) + spec_relation_type = ReqIFSpecRelationType( + identifier=generate_unique_identifier(spec_relation_type_name), + long_name=spec_relation_type_name, + ) + spec_relation_types.append(spec_relation_type) + context.map_spec_relation_tuple_to_spec_relation_type[ + spec_relation_tuple_ + ] = spec_relation_type + + return spec_object_types + spec_relation_types @classmethod def _create_section_spec_object_type( diff --git a/strictdoc/backend/reqif/sdoc_reqif_fields.py b/strictdoc/backend/reqif/sdoc_reqif_fields.py index 2a159dafd..ec0f2e8f3 100644 --- a/strictdoc/backend/reqif/sdoc_reqif_fields.py +++ b/strictdoc/backend/reqif/sdoc_reqif_fields.py @@ -62,7 +62,3 @@ class ReqIFChapterField: ] SDOC_SPECIFICATION_TYPE_SINGLETON = "SDOC_SPECIFICATION_TYPE_SINGLETON" - -SDOC_SPEC_RELATION_PARENT_TYPE_SINGLETON = ( - "SDOC_SPEC_RELATION_PARENT_TYPE_SINGLETON" -) diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/01_cli_option/sample.sdoc b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/01_cli_option/sample.sdoc index cc1a3c2bc..a35afd3bd 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/01_cli_option/sample.sdoc +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/01_cli_option/sample.sdoc @@ -37,7 +37,6 @@ ELEMENTS: REQUIRED: False RELATIONS: - TYPE: Parent - - TYPE: File [SECTION] MID: eece0e6eeb9f4dacbe1026a0ec818b4b diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/02_toml_option/sample.sdoc b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/02_toml_option/sample.sdoc index cc1a3c2bc..a35afd3bd 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/02_toml_option/sample.sdoc +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-enable-mid/02_toml_option/sample.sdoc @@ -37,7 +37,6 @@ ELEMENTS: REQUIRED: False RELATIONS: - TYPE: Parent - - TYPE: File [SECTION] MID: eece0e6eeb9f4dacbe1026a0ec818b4b diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/01_cli_option/sample.sdoc b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/01_cli_option/sample.sdoc index d1b9c6dc6..34625faa2 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/01_cli_option/sample.sdoc +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/01_cli_option/sample.sdoc @@ -33,7 +33,6 @@ ELEMENTS: REQUIRED: False RELATIONS: - TYPE: Parent - - TYPE: File [SECTION] TITLE: Section #1 diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/02_toml_option/sample.sdoc b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/02_toml_option/sample.sdoc index d1b9c6dc6..34625faa2 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/02_toml_option/sample.sdoc +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/--reqif-import-markup/02_toml_option/sample.sdoc @@ -33,7 +33,6 @@ ELEMENTS: REQUIRED: False RELATIONS: - TYPE: Parent - - TYPE: File [SECTION] TITLE: Section #1 diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/13_refs/sample.sdoc b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/70_relations/sample.sdoc similarity index 82% rename from tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/13_refs/sample.sdoc rename to tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/70_relations/sample.sdoc index 690a2dcb3..773ec2d67 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/13_refs/sample.sdoc +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/70_relations/sample.sdoc @@ -31,7 +31,8 @@ ELEMENTS: REQUIRED: False RELATIONS: - TYPE: Parent - - TYPE: File + - TYPE: Parent + ROLE: Traces [REQUIREMENT] UID: REQ-001 @@ -43,3 +44,11 @@ TITLE: Requirement #2 RELATIONS: - TYPE: Parent VALUE: REQ-001 + +[REQUIREMENT] +UID: REQ-003 +TITLE: Requirement #3 +RELATIONS: +- TYPE: Parent + VALUE: REQ-001 + ROLE: Traces diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/13_refs/test.itest b/tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/70_relations/test.itest similarity index 100% rename from tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/13_refs/test.itest rename to tests/integration/backend/reqif/profiles/p01_sdoc/end_to_end/70_relations/test.itest diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/output2/sample.sdoc b/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/output2/sample.sdoc index 3c27ba6f0..411e97f2b 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/output2/sample.sdoc +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/output2/sample.sdoc @@ -16,7 +16,6 @@ ELEMENTS: REQUIRED: False RELATIONS: - TYPE: Parent - - TYPE: File [REQUIREMENT_TYPE] UID: Anonymized-0f5a0a32-034a-aa17-65d0-7387eefa020a diff --git a/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/sample.reqif b/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/sample.reqif index 55e6bd41d..d7fd4d693 100644 --- a/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/sample.reqif +++ b/tests/integration/backend/reqif/profiles/p01_sdoc/examples/01_sample/sample.reqif @@ -56,6 +56,7 @@ +