Skip to content

Commit 718e1b5

Browse files
authored
Merge pull request #156 from strictdoc-project/stanislaw/examples
examples: JSON example: print nested sections and requirements
2 parents f725ad1 + 8f9e310 commit 718e1b5

13 files changed

Lines changed: 959 additions & 144 deletions

File tree

reqif/experimental/reqif_schema.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from enum import Enum
2+
from typing import Any, Dict, Optional
3+
4+
from reqif.models.reqif_spec_object import ReqIFSpecObject
5+
from reqif.models.reqif_spec_object_type import (
6+
ReqIFSpecObjectType,
7+
SpecAttributeDefinition,
8+
)
9+
from reqif.reqif_bundle import ReqIFBundle
10+
11+
12+
class ReqIFSchema:
13+
class ReqIFSchemaType(Enum):
14+
POLARION = 1
15+
DEFAULT = 2
16+
17+
def __init__(self, reqif_bundle: ReqIFBundle):
18+
data_type_definitions: Dict[str, Any] = {}
19+
spec_object_type_attributes: Dict[str, SpecAttributeDefinition] = {}
20+
spec_object_type_names: Dict[str, str] = {}
21+
detected_section_spec_type: Optional[ReqIFSpecObjectType] = None
22+
detected_chapter_name_attribute: Optional[
23+
SpecAttributeDefinition
24+
] = None
25+
detected_schema_type: ReqIFSchema.ReqIFSchemaType = (
26+
ReqIFSchema.ReqIFSchemaType.DEFAULT
27+
)
28+
29+
# First, collect iterate over all spec object types and collect all
30+
# attributes which are essentially the requirements fields.
31+
assert reqif_bundle.core_content is not None
32+
assert reqif_bundle.core_content.req_if_content is not None
33+
assert reqif_bundle.core_content.req_if_content.spec_types is not None
34+
for spec_type in reqif_bundle.core_content.req_if_content.spec_types:
35+
if not isinstance(spec_type, ReqIFSpecObjectType):
36+
continue
37+
38+
assert spec_type.long_name is not None
39+
if spec_type.long_name == "Heading":
40+
detected_section_spec_type = spec_type
41+
detected_schema_type = ReqIFSchema.ReqIFSchemaType.POLARION
42+
43+
spec_object_type_names[spec_type.identifier] = spec_type.long_name
44+
45+
assert spec_type.attribute_definitions is not None
46+
for attribute_definition in spec_type.attribute_definitions:
47+
if attribute_definition.long_name == "ReqIF.ChapterName":
48+
detected_chapter_name_attribute = attribute_definition
49+
spec_object_type_attributes[
50+
attribute_definition.identifier
51+
] = attribute_definition
52+
53+
assert reqif_bundle.core_content.req_if_content.data_types is not None
54+
for data_type in reqif_bundle.core_content.req_if_content.data_types:
55+
data_type_definitions[data_type.identifier] = data_type
56+
57+
self.data_type_definitions: Dict[str, Any] = data_type_definitions
58+
self.spec_object_type_attributes: Dict[
59+
str, SpecAttributeDefinition
60+
] = spec_object_type_attributes
61+
self.spec_object_type_names: Dict[str, str] = spec_object_type_names
62+
self.reqif_bundle: ReqIFBundle = reqif_bundle
63+
self.detected_heading_spec_type = detected_section_spec_type
64+
self.detected_chapter_name_attribute: Optional[
65+
SpecAttributeDefinition
66+
] = detected_chapter_name_attribute
67+
self.detected_schema_type: ReqIFSchema.ReqIFSchemaType = (
68+
detected_schema_type
69+
)
70+
71+
def is_spec_object_a_heading(self, spec_object: ReqIFSpecObject):
72+
if self.detected_heading_spec_type is not None:
73+
return (
74+
spec_object.spec_object_type
75+
== self.detected_heading_spec_type.identifier
76+
)
77+
if self.detected_chapter_name_attribute is not None:
78+
return (
79+
self.detected_chapter_name_attribute.identifier
80+
in spec_object.attribute_map
81+
)
82+
83+
# If neither a Heading/Section spec type nor ReqIF.ChapterName attribute
84+
# could be detected, assume that all spec objects in the ReqIF file are
85+
# simply requirements.
86+
return False

reqif/helpers/lxml.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from itertools import chain
44

55
from lxml import etree
6-
from lxml.etree import tostring
6+
from lxml.etree import _Comment, tostring
77
from lxml.html import fragment_fromstring
88

99

@@ -208,9 +208,16 @@ def lxml_strip_namespace_from_xml(root_xml, full=False):
208208
# Remove an XML namespace URI in the element's name but keep the
209209
# namespaces in the HTML content as found in the
210210
# <ATTRIBUTE-VALUE-XHTML> of ReqIF XML.
211-
if not full and "http://www.w3.org/1999/xhtml" in elem.tag:
211+
if lxml_is_comment_node(elem) or (
212+
not full and "http://www.w3.org/1999/xhtml" in elem.tag
213+
):
212214
continue
213215
elem.tag = etree.QName(elem).localname
214216
# Remove unused namespace declarations
215217
etree.cleanup_namespaces(root_xml)
216218
return root_xml
219+
220+
221+
def lxml_is_comment_node(xml_node):
222+
# FIXME: Accessing a "_"-marked Comment class of lxml is not great.
223+
return isinstance(xml_node, _Comment)

reqif/parser.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
from lxml import etree
99
from lxml.etree import DocInfo
1010

11-
from reqif.helpers.lxml import lxml_strip_namespace_from_xml
11+
from reqif.helpers.lxml import (
12+
lxml_is_comment_node,
13+
lxml_strip_namespace_from_xml,
14+
)
1215
from reqif.models.error_handling import (
1316
ReqIFMissingTagException,
1417
ReqIFSchemaError,
@@ -276,6 +279,9 @@ def _parse_reqif_content(
276279
spec_type = RelationGroupTypeParser.parse(
277280
xml_spec_object_type_xml
278281
)
282+
elif lxml_is_comment_node(xml_spec_object_type_xml):
283+
# Skip comments for now.
284+
continue
279285
else:
280286
raise NotImplementedError(
281287
xml_spec_object_type_xml
@@ -318,6 +324,8 @@ def _parse_reqif_content(
318324
if xml_spec_objects is not None:
319325
spec_objects = []
320326
for xml_spec_object in xml_spec_objects:
327+
if lxml_is_comment_node(xml_spec_object):
328+
continue
321329
spec_object = SpecObjectParser.parse(xml_spec_object)
322330
spec_objects.append(spec_object)
323331
spec_objects_lookup[spec_object.identifier] = spec_object

reqif/parsers/attribute_value_parser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from reqif.helpers.lxml import (
77
lxml_convert_children_from_reqif_ns_xhtml_string,
8+
lxml_is_comment_node,
89
lxml_stringify_children,
910
lxml_stringify_namespaced_children,
1011
)
@@ -96,6 +97,8 @@ def parse_attribute_values(
9697

9798
attributes: List[SpecObjectAttribute] = []
9899
for attribute_xml in xml_attribute_values:
100+
if lxml_is_comment_node(attribute_xml):
101+
continue
99102
if attribute_xml.tag == "ATTRIBUTE-VALUE-STRING":
100103
attribute_value = attribute_xml.attrib["THE-VALUE"]
101104
attribute_definition_ref = attribute_xml[0][0].text

tests/integration/examples/04_convert_reqif_to_json/expected/output.json

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"documents": [
3+
{
4+
"name": "Test Export",
5+
"nodes": [
6+
{
7+
"node_type": "Heading",
8+
"level": 1,
9+
"fields": {
10+
"ReqIF.ChapterName": "Section 1",
11+
"ReqIF.Text": "Section text..."
12+
},
13+
"nodes": [
14+
{
15+
"node_type": "System Requirement",
16+
"level": 2,
17+
"fields": {
18+
"ReqIF.ForeignCreatedBy": "redacted@mail.com",
19+
"ReqIF.ForeignID": "LOREM-818",
20+
"Status": "Draft",
21+
"ReqIF.ForeignCreatedOn": "2023-03-15T10:46:58.611Z",
22+
"ReqIF.ChapterName": "SW: Lorem Ipsum",
23+
"ReqIF.Text": "\n <xhtml:div>The Lorem Ipsum shall do something.</xhtml:div>\n "
24+
},
25+
"nodes": []
26+
}
27+
]
28+
}
29+
]
30+
}
31+
],
32+
"fields": [
33+
"ReqIF.ChapterName",
34+
"ReqIF.ForeignCreatedBy",
35+
"ReqIF.ForeignCreatedOn",
36+
"ReqIF.ForeignID",
37+
"ReqIF.Text",
38+
"Status"
39+
]
40+
}

0 commit comments

Comments
 (0)