Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

examples: JSON example: print nested sections and requirements #156

Merged
merged 1 commit into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions reqif/experimental/reqif_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from enum import Enum
from typing import Any, Dict, Optional

from reqif.models.reqif_spec_object import ReqIFSpecObject
from reqif.models.reqif_spec_object_type import (
ReqIFSpecObjectType,
SpecAttributeDefinition,
)
from reqif.reqif_bundle import ReqIFBundle


class ReqIFSchema:
class ReqIFSchemaType(Enum):
POLARION = 1
DEFAULT = 2

def __init__(self, reqif_bundle: ReqIFBundle):
data_type_definitions: Dict[str, Any] = {}
spec_object_type_attributes: Dict[str, SpecAttributeDefinition] = {}
spec_object_type_names: Dict[str, str] = {}
detected_section_spec_type: Optional[ReqIFSpecObjectType] = None
detected_chapter_name_attribute: Optional[
SpecAttributeDefinition
] = None
detected_schema_type: ReqIFSchema.ReqIFSchemaType = (
ReqIFSchema.ReqIFSchemaType.DEFAULT
)

# First, collect iterate over all spec object types and collect all
# attributes which are essentially the requirements fields.
assert reqif_bundle.core_content is not None
assert reqif_bundle.core_content.req_if_content is not None
assert reqif_bundle.core_content.req_if_content.spec_types is not None
for spec_type in reqif_bundle.core_content.req_if_content.spec_types:
if not isinstance(spec_type, ReqIFSpecObjectType):
continue

assert spec_type.long_name is not None
if spec_type.long_name == "Heading":
detected_section_spec_type = spec_type
detected_schema_type = ReqIFSchema.ReqIFSchemaType.POLARION

spec_object_type_names[spec_type.identifier] = spec_type.long_name

assert spec_type.attribute_definitions is not None
for attribute_definition in spec_type.attribute_definitions:
if attribute_definition.long_name == "ReqIF.ChapterName":
detected_chapter_name_attribute = attribute_definition
spec_object_type_attributes[
attribute_definition.identifier
] = attribute_definition

assert reqif_bundle.core_content.req_if_content.data_types is not None
for data_type in reqif_bundle.core_content.req_if_content.data_types:
data_type_definitions[data_type.identifier] = data_type

self.data_type_definitions: Dict[str, Any] = data_type_definitions
self.spec_object_type_attributes: Dict[
str, SpecAttributeDefinition
] = spec_object_type_attributes
self.spec_object_type_names: Dict[str, str] = spec_object_type_names
self.reqif_bundle: ReqIFBundle = reqif_bundle
self.detected_heading_spec_type = detected_section_spec_type
self.detected_chapter_name_attribute: Optional[
SpecAttributeDefinition
] = detected_chapter_name_attribute
self.detected_schema_type: ReqIFSchema.ReqIFSchemaType = (
detected_schema_type
)

def is_spec_object_a_heading(self, spec_object: ReqIFSpecObject):
if self.detected_heading_spec_type is not None:
return (
spec_object.spec_object_type
== self.detected_heading_spec_type.identifier
)
if self.detected_chapter_name_attribute is not None:
return (
self.detected_chapter_name_attribute.identifier
in spec_object.attribute_map
)

# If neither a Heading/Section spec type nor ReqIF.ChapterName attribute
# could be detected, assume that all spec objects in the ReqIF file are
# simply requirements.
return False
11 changes: 9 additions & 2 deletions reqif/helpers/lxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from itertools import chain

from lxml import etree
from lxml.etree import tostring
from lxml.etree import _Comment, tostring
from lxml.html import fragment_fromstring


Expand Down Expand Up @@ -208,9 +208,16 @@ def lxml_strip_namespace_from_xml(root_xml, full=False):
# Remove an XML namespace URI in the element's name but keep the
# namespaces in the HTML content as found in the
# <ATTRIBUTE-VALUE-XHTML> of ReqIF XML.
if not full and "http://www.w3.org/1999/xhtml" in elem.tag:
if lxml_is_comment_node(elem) or (
not full and "http://www.w3.org/1999/xhtml" in elem.tag
):
continue
elem.tag = etree.QName(elem).localname
# Remove unused namespace declarations
etree.cleanup_namespaces(root_xml)
return root_xml


def lxml_is_comment_node(xml_node):
# FIXME: Accessing a "_"-marked Comment class of lxml is not great.
return isinstance(xml_node, _Comment)
10 changes: 9 additions & 1 deletion reqif/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
from lxml import etree
from lxml.etree import DocInfo

from reqif.helpers.lxml import lxml_strip_namespace_from_xml
from reqif.helpers.lxml import (
lxml_is_comment_node,
lxml_strip_namespace_from_xml,
)
from reqif.models.error_handling import (
ReqIFMissingTagException,
ReqIFSchemaError,
Expand Down Expand Up @@ -276,6 +279,9 @@ def _parse_reqif_content(
spec_type = RelationGroupTypeParser.parse(
xml_spec_object_type_xml
)
elif lxml_is_comment_node(xml_spec_object_type_xml):
# Skip comments for now.
continue
else:
raise NotImplementedError(
xml_spec_object_type_xml
Expand Down Expand Up @@ -318,6 +324,8 @@ def _parse_reqif_content(
if xml_spec_objects is not None:
spec_objects = []
for xml_spec_object in xml_spec_objects:
if lxml_is_comment_node(xml_spec_object):
continue
spec_object = SpecObjectParser.parse(xml_spec_object)
spec_objects.append(spec_object)
spec_objects_lookup[spec_object.identifier] = spec_object
Expand Down
3 changes: 3 additions & 0 deletions reqif/parsers/attribute_value_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from reqif.helpers.lxml import (
lxml_convert_children_from_reqif_ns_xhtml_string,
lxml_is_comment_node,
lxml_stringify_children,
lxml_stringify_namespaced_children,
)
Expand Down Expand Up @@ -96,6 +97,8 @@ def parse_attribute_values(

attributes: List[SpecObjectAttribute] = []
for attribute_xml in xml_attribute_values:
if lxml_is_comment_node(attribute_xml):
continue
if attribute_xml.tag == "ATTRIBUTE-VALUE-STRING":
attribute_value = attribute_xml.attrib["THE-VALUE"]
attribute_definition_ref = attribute_xml[0][0].text
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"documents": [
{
"name": "Test Export",
"nodes": [
{
"node_type": "Heading",
"level": 1,
"fields": {
"ReqIF.ChapterName": "Section 1",
"ReqIF.Text": "Section text..."
},
"nodes": [
{
"node_type": "System Requirement",
"level": 2,
"fields": {
"ReqIF.ForeignCreatedBy": "[email protected]",
"ReqIF.ForeignID": "LOREM-818",
"Status": "Draft",
"ReqIF.ForeignCreatedOn": "2023-03-15T10:46:58.611Z",
"ReqIF.ChapterName": "SW: Lorem Ipsum",
"ReqIF.Text": "\n <xhtml:div>The Lorem Ipsum shall do something.</xhtml:div>\n "
},
"nodes": []
}
]
}
]
}
],
"fields": [
"ReqIF.ChapterName",
"ReqIF.ForeignCreatedBy",
"ReqIF.ForeignCreatedOn",
"ReqIF.ForeignID",
"ReqIF.Text",
"Status"
]
}
Loading
Loading