Skip to content
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
120 changes: 73 additions & 47 deletions strictdoc/export/json/json_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,12 @@ def export_tree(
path_output_json_file = os.path.join(output_json_root, "index.json")
project_tree_json = json.dumps(project_tree_dict, indent=4)
with open(path_output_json_file, "w") as output_json_file:
output_json_file.write(project_tree_json)
output_json_file.write(project_tree_json + "\n")

@classmethod
def _write_document(cls, document: SDocDocument) -> Dict[str, Any]:
document_dict: Dict[str, Any] = {
"TITLE": document.title,
"PREFIX": None,
JSONKey.GRAMMAR: {"ELEMENTS": []},
JSONKey.OPTIONS: {},
JSONKey.NODES: [],
"_NODE_TYPE": "DOCUMENT",
}

if document.mid_permanent or document.config.enable_mid:
Expand Down Expand Up @@ -120,6 +116,8 @@ def _write_document(cls, document: SDocDocument) -> Dict[str, Any]:
or requirement_in_toc is not None
or default_view is not None
):
document_dict[JSONKey.OPTIONS] = {}

if enable_mid is not None:
document_dict[JSONKey.OPTIONS]["ENABLE_MID"] = (
True if enable_mid else False
Expand Down Expand Up @@ -148,9 +146,14 @@ def _write_document(cls, document: SDocDocument) -> Dict[str, Any]:
default_view
)

document_dict["TITLE"] = document.title

#
# Grammar.
#

document_dict[JSONKey.GRAMMAR] = {"ELEMENTS": []}

assert document.grammar is not None
document_grammar: DocumentGrammar = document.grammar

Expand Down Expand Up @@ -189,49 +192,24 @@ def _write_node(
document: SDocDocument,
level_stack: Tuple[int, ...],
) -> Dict[str, Any]:
def get_level_string_(
node_: Union[SDocNode, SDocSection, SDocDocument],
) -> str:
return (
""
if node_.ng_resolved_custom_level == "None"
else (
node_.ng_resolved_custom_level
if node_.ng_resolved_custom_level is not None
else ".".join(map(str, level_stack))
)
)

if isinstance(node, SDocSection):
section_dict: Dict[str, Any] = cls._write_section(
node, document, get_level_string_(node)
node, document, level_stack
)

current_number = 0
for subnode_ in node.section_contents:
if subnode_.ng_resolved_custom_level is None:
current_number += 1

section_subnode_dict: Dict[str, Any] = cls._write_node(
subnode_, document, level_stack + (current_number,)
)
section_dict[JSONKey.NODES].append(section_subnode_dict)

return section_dict

elif isinstance(node, SDocNode):
# FIXME: Print composite nodes.
# https://github.com/strictdoc-project/strictdoc/issues/2180
subnode_dict = cls._write_requirement(
node=node,
document=document,
level_string=get_level_string_(node),
level_stack=level_stack,
)
return subnode_dict

elif isinstance(node, SDocDocument):
node_dict: Dict[str, Any] = cls._write_included_document(
node, get_level_string_(node)
node,
level_stack=level_stack,
)

current_number = 0
Expand Down Expand Up @@ -259,32 +237,35 @@ def get_level_string_(

@classmethod
def _write_included_document(
cls, node: SDocDocument, level_string: str
cls,
node: SDocDocument,
level_stack: Tuple[int, ...],
) -> Dict[str, Any]:
node_dict: Dict[str, Any] = {
"_TOC": level_string,
"TYPE": "SECTION",
"_TOC": cls._get_level_string(node, level_stack=level_stack),
"_NODE_TYPE": "SECTION",
"TITLE": node.reserved_title,
JSONKey.NODES: [],
}
return node_dict

@classmethod
def _write_section(
cls, section: SDocSection, document: SDocDocument, level_string: str
cls,
section: SDocSection,
document: SDocDocument,
level_stack: Tuple[int, ...],
) -> Dict[str, Any]:
assert isinstance(section, (SDocSection, SDocDocument))
node_dict: Dict[str, Any] = {
"_TOC": level_string,
"TYPE": "SECTION",
"TITLE": str(section.title),
JSONKey.NODES: [],
"_TOC": cls._get_level_string(section, level_stack),
"_NODE_TYPE": "SECTION",
}

if section.mid_permanent or document.config.enable_mid:
node_dict["MID"] = section.reserved_mid

if section.uid:
if section.reserved_uid is not None:
node_dict["UID"] = section.uid

if (
Expand All @@ -296,15 +277,31 @@ def _write_section(
if section.requirement_prefix is not None:
node_dict["PREFIX"] = section.requirement_prefix

node_dict["TITLE"] = str(section.title)
node_dict[JSONKey.NODES] = []

current_number = 0
for subnode_ in section.section_contents:
if subnode_.ng_resolved_custom_level is None:
current_number += 1

section_subnode_dict: Dict[str, Any] = cls._write_node(
subnode_, document, level_stack + (current_number,)
)
node_dict[JSONKey.NODES].append(section_subnode_dict)

return node_dict

@classmethod
def _write_requirement(
cls, node: SDocNode, document: SDocDocument, level_string: str
cls,
node: SDocNode,
document: SDocDocument,
level_stack: Tuple[int, ...],
) -> Dict[str, Any]:
node_dict: Dict[str, Any] = {
"_TOC": level_string,
"TYPE": node.node_type,
"_TOC": cls._get_level_string(node, level_stack),
"_NODE_TYPE": node.node_type,
}

if node.mid_permanent or document.config.enable_mid:
Expand All @@ -324,6 +321,19 @@ def _write_requirement(
if len(node.relations) > 0:
node_dict["RELATIONS"] = cls._write_requirement_relations(node)

if node.is_composite:
node_dict[JSONKey.NODES] = []

current_number = 0
for subnode_ in node.section_contents:
if subnode_.ng_resolved_custom_level is None:
current_number += 1

section_subnode_dict: Dict[str, Any] = cls._write_node(
subnode_, document, level_stack + (current_number,)
)
node_dict[JSONKey.NODES].append(section_subnode_dict)

return node_dict

@classmethod
Expand Down Expand Up @@ -381,3 +391,19 @@ def _write_requirement_relations(node: SDocNode) -> List[Dict[str, str]]:
relations_list.append(relation_dict)

return relations_list

@classmethod
def _get_level_string(
cls,
node_: Union[SDocNode, SDocSection, SDocDocument],
level_stack: Tuple[int, ...],
) -> str:
return (
""
if node_.ng_resolved_custom_level == "None"
else (
node_.ng_resolved_custom_level
if node_.ng_resolved_custom_level is not None
else ".".join(map(str, level_stack))
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ CHECK: Step 'Export SDoc' took
RUN: cat %S/Output/json/index.json | filecheck %s --check-prefix=CHECK-JSON

CHECK-JSON: "_TOC": "3",
CHECK-JSON: "TYPE": "SECTION",
CHECK-JSON: "_NODE_TYPE": "SECTION",
CHECK-JSON: "TITLE": "Dummy Software Requirements Specification #2 FRAGMENT",
CHECK-JSON: "_TOC": "3.1",
CHECK-JSON: "TYPE": "REQUIREMENT",
CHECK-JSON: "_NODE_TYPE": "REQUIREMENT",
CHECK-JSON: "UID": "LREQ-3",
CHECK-JSON: "TITLE": "Dummy low-level requirement #3"
CHECK-JSON: "STATEMENT": "System ABC shall do 3."

CHECK-JSON-NOT: "_TOC": "1",
CHECK-JSON-NOT: "TYPE": "REQUIREMENT",
CHECK-JSON-NOT: "_NODE_TYPE": "REQUIREMENT",
CHECK-JSON-NOT: "UID": "LREQ-3",
CHECK-JSON-NOT: "TITLE": "Dummy low-level requirement #3",
CHECK-JSON-NOT: "STATEMENT": "System ABC shall do 3."
Expand All @@ -22,16 +22,16 @@ RUN: %strictdoc export %S --formats=json --included-documents --output-dir Outpu
RUN: cat %S/Output/json/index.json | filecheck %s --check-prefix=CHECK-JSON-INCLUDED

CHECK-JSON-INCLUDED: "_TOC": "3",
CHECK-JSON-INCLUDED: "TYPE": "SECTION",
CHECK-JSON-INCLUDED: "_NODE_TYPE": "SECTION",
CHECK-JSON-INCLUDED: "TITLE": "Dummy Software Requirements Specification #2 FRAGMENT",
CHECK-JSON-INCLUDED: "_TOC": "3.1",
CHECK-JSON-INCLUDED: "TYPE": "REQUIREMENT",
CHECK-JSON-INCLUDED: "_NODE_TYPE": "REQUIREMENT",
CHECK-JSON-INCLUDED: "UID": "LREQ-3",
CHECK-JSON-INCLUDED: "TITLE": "Dummy low-level requirement #3"
CHECK-JSON-INCLUDED: "STATEMENT": "System ABC shall do 3."

CHECK-JSON-INCLUDED: "_TOC": "1",
CHECK-JSON-INCLUDED: "TYPE": "REQUIREMENT",
CHECK-JSON-INCLUDED: "_NODE_TYPE": "REQUIREMENT",
CHECK-JSON-INCLUDED: "UID": "LREQ-3",
CHECK-JSON-INCLUDED: "TITLE": "Dummy low-level requirement #3",
CHECK-JSON-INCLUDED: "STATEMENT": "System ABC shall do 3."
49 changes: 49 additions & 0 deletions tests/integration/features/json/03_section/input1.sdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[DOCUMENT]
MID: 82becec5ace54321b135ed27d5a73c76
TITLE: Dummy Software Requirements Specification #1
OPTIONS:
ENABLE_MID: True

[GRAMMAR]
ELEMENTS:
- TAG: REQUIREMENT
FIELDS:
- TITLE: MID
TYPE: String
REQUIRED: False
- TITLE: UID
TYPE: String
REQUIRED: False
- TITLE: TITLE
TYPE: String
REQUIRED: False
- TITLE: STATEMENT
TYPE: String
REQUIRED: False
RELATIONS:
- TYPE: Parent
- TYPE: File

[[SECTION]]
MID: sect1
TITLE: High-level section

[REQUIREMENT]
MID: c165cc52af20417e9199e30a4e17f138
UID: REQ-1
TITLE: Dummy high-level requirement #1
STATEMENT: System ABC shall do 1.

[REQUIREMENT]
MID: 74dddc4c55ac44ae83dc0f73ef8ed2aa
UID: REQ-2
TITLE: Dummy high-level requirement #2
STATEMENT: System ABC shall do 2.

[REQUIREMENT]
MID: fcbce0c486ac4d3189a7aa1dd646399e
UID: REQ-3
TITLE: Dummy high-level requirement #3
STATEMENT: System ABC shall do 3.

[[/SECTION]]
64 changes: 64 additions & 0 deletions tests/integration/features/json/03_section/input2.sdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[DOCUMENT]
MID: 3c39b5bdf690402682538b418558c738
TITLE: Dummy Software Requirements Specification #2
OPTIONS:
ENABLE_MID: True

[GRAMMAR]
ELEMENTS:
- TAG: REQUIREMENT
FIELDS:
- TITLE: MID
TYPE: String
REQUIRED: False
- TITLE: UID
TYPE: String
REQUIRED: False
- TITLE: TITLE
TYPE: String
REQUIRED: False
- TITLE: STATEMENT
TYPE: String
REQUIRED: False
RELATIONS:
- TYPE: Parent
- TYPE: File

[[SECTION]]
MID: sect2
TITLE: Low-level section

[REQUIREMENT]
MID: 95e3198bbbdf44d691daa869efc79323
UID: LREQ-1
TITLE: Dummy low-level requirement #1
STATEMENT: System ABC shall do 1.
RELATIONS:
- TYPE: Parent
VALUE: REQ-1
- TYPE: File
VALUE: file.py

[REQUIREMENT]
MID: 70c97ad5b2e543b7b9a1015265cd9445
UID: LREQ-2
TITLE: Dummy low-level requirement #2
STATEMENT: System ABC shall do 2.
RELATIONS:
- TYPE: Parent
VALUE: REQ-2
- TYPE: File
VALUE: file.py

[REQUIREMENT]
MID: 44720087baf5489cb88c32cd284585e2
UID: LREQ-3
TITLE: Dummy low-level requirement #3
STATEMENT: System ABC shall do 3.
RELATIONS:
- TYPE: Parent
VALUE: REQ-3
- TYPE: File
VALUE: file.py

[[/SECTION]]
4 changes: 4 additions & 0 deletions tests/integration/features/json/03_section/strictdoc.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[project]
title = "Untitled Project"

features = []
Loading
Loading