StrictDoc Documentation
strictdoc/core/actions/export_action.py
Source file coverage
Path:
strictdoc/core/actions/export_action.py
Lines:
322
Non-empty lines:
289
Non-empty lines covered with requirements:
289 / 289 (100.0%)
Functions:
6
Functions covered by requirements:
6 / 6 (100.0%)
1
import os
2
import sys
3
from pathlib import Path
4
from typing import Optional
5
 
6
from strictdoc.backend.excel.export.excel_generator import ExcelGenerator
7
from strictdoc.backend.markdown.writer import SDMarkdownWriter
8
from strictdoc.backend.reqif.reqif_export import ReqIFExport
9
from strictdoc.backend.sdoc.errors.document_tree_error import DocumentTreeError
10
from strictdoc.backend.sdoc.models.document import SDocDocument
11
from strictdoc.backend.sdoc.writer import SDWriter
12
from strictdoc.core.project_config import ProjectConfig
13
from strictdoc.core.traceability_index import TraceabilityIndex
14
from strictdoc.core.traceability_index_builder import TraceabilityIndexBuilder
15
from strictdoc.export.doxygen.doxygen_generator import DoxygenGenerator
16
from strictdoc.export.html.document_type import DocumentType
17
from strictdoc.export.html.html_generator import HTMLGenerator
18
from strictdoc.export.html.html_templates import HTMLTemplates
19
from strictdoc.export.json.json_generator import JSONGenerator
20
from strictdoc.export.rst.document_rst_generator import DocumentRSTGenerator
21
from strictdoc.export.spdx.spdx_generator import SPDXGenerator
22
from strictdoc.features.diff_and_changelog.change_generator import (
23
    ChangeGenerator,
24
)
25
from strictdoc.features.html2pdf.html2pdf_generator import HTML2PDFGenerator
26
from strictdoc.helpers.parallelizer import NullParallelizer, Parallelizer
27
from strictdoc.helpers.timing import timing_decorator
28
 
29
 
30
class ExportAction:
31
    def __init__(
32
        self,
33
        project_config: ProjectConfig,
34
        parallelizer: Parallelizer,
35
    ):
36
        assert parallelizer
37
        self.project_config: ProjectConfig = project_config
38
        self.parallelizer = parallelizer
39
        self.traceability_index: TraceabilityIndex = self.build_index()
40
 
41
    @timing_decorator("Parse SDoc project tree")
42
    def build_index(self) -> TraceabilityIndex:
43
        try:
44
            traceability_index: TraceabilityIndex = (
45
                TraceabilityIndexBuilder.create(
46
                    project_config=self.project_config,
47
                    parallelizer=self.parallelizer,
48
                )
49
            )
50
        except DocumentTreeError as exc:
51
            print(exc.to_print_message())  # noqa: T201
52
            sys.exit(1)
53
        self.traceability_index = traceability_index
54
        return traceability_index
55
 
56
    @timing_decorator("Export SDoc")
57
    def export(self) -> None:
58
        assert self.traceability_index is not None, (
59
            "The index must be built at this point."
60
        )
61
        assert self.project_config.export_formats is not None, (
62
            "The export_formats must not be None."
63
        )
64
 
65
        if (
66
            "html" in self.project_config.export_formats
67
            or "html2pdf" in self.project_config.export_formats
68
        ):
69
            is_small_project = self.traceability_index.is_small_project()
70
 
71
            html_templates = HTMLTemplates.create(
72
                project_config=self.project_config,
73
                enable_caching=not is_small_project,
74
                strictdoc_last_update=self.traceability_index.strictdoc_last_update,
75
            )
76
 
77
            # The bundle document is generated only when the option is provided.
78
            traceability_index_copy: Optional[TraceabilityIndex] = None
79
            bundle_document: Optional[SDocDocument] = None
80
            if self.project_config.generate_bundle_document:
81
                traceability_index_copy, bundle_document = (
82
                    self.traceability_index.clone_to_bundle_document(
83
                        self.project_config
84
                    )
85
                )
86
 
87
            if "html" in self.project_config.export_formats:
88
                html_generator = HTMLGenerator(
89
                    self.project_config, html_templates
90
                )
91
                html_generator.export_complete_tree(
92
                    traceability_index=self.traceability_index,
93
                    parallelizer=self.parallelizer,
94
                )
95
                if self.project_config.generate_bundle_document:
96
                    assert bundle_document is not None, (
97
                        "bundle_document must not be None."
98
                    )
99
                    assert traceability_index_copy is not None, (
100
                        "traceability_index_copy must not be None."
101
                    )
102
                    html_generator.export_single_document(
103
                        document=bundle_document,
104
                        traceability_index=traceability_index_copy,
105
                        specific_documents=(DocumentType.DOCUMENT,),
106
                    )
107
 
108
            if "html" in self.project_config.export_formats:
109
                if (
110
                    diff_git_revisions := self.project_config.diff_git_revisions
111
                ) is not None:
112
                    ChangeGenerator().generate_from_revisions(
113
                        diff_git_revisions,
114
                        project_config=self.project_config,
115
                        html_templates=html_templates,
116
                    )
117
                if (
118
                    diff_dir_revisions := self.project_config.diff_dir_revisions
119
                ) is not None:
120
                    ChangeGenerator().generate_from_paths(
121
                        diff_dir_revisions[0],
122
                        diff_dir_revisions[1],
123
                        project_config=self.project_config,
124
                        html_templates=html_templates,
125
                    )
126
 
127
            # @relation(SDOC-SRS-51, scope=range_start)
128
            if "html2pdf" in self.project_config.export_formats:
129
                output_html2pdf_root = os.path.join(
130
                    self.project_config.output_dir, "html2pdf"
131
                )
132
                Path(output_html2pdf_root).mkdir(parents=True, exist_ok=True)
133
                HTML2PDFGenerator.export_tree(
134
                    self.project_config,
135
                    self.traceability_index,
136
                    html_templates,
137
                    output_html2pdf_root,
138
                )
139
 
140
                if self.project_config.generate_bundle_document:
141
                    assert traceability_index_copy is not None, (
142
                        "traceability_index_copy must not be None."
143
                    )
144
                    HTML2PDFGenerator.export_tree(
145
                        self.project_config,
146
                        traceability_index_copy,
147
                        html_templates,
148
                        output_html2pdf_root,
149
                        flat_assets=True,
150
                    )
151
            # @relation(SDOC-SRS-51, scope=range_end)
152
 
153
        if "rst" in self.project_config.export_formats:
154
            output_rst_root = os.path.join(
155
                self.project_config.output_dir, "rst"
156
            )
157
            Path(output_rst_root).mkdir(parents=True, exist_ok=True)
158
            DocumentRSTGenerator.export_tree(
159
                self.traceability_index, output_rst_root
160
            )
161
 
162
        if "excel" in self.project_config.export_formats:
163
            output_excel_root = f"{self.project_config.output_dir}/excel"
164
            ExcelGenerator.export_tree(
165
                self.traceability_index,
166
                output_excel_root,
167
                project_config=self.project_config,
168
            )
169
 
170
        if "reqif-sdoc" in self.project_config.export_formats:
171
            output_reqif_root = f"{self.project_config.output_dir}/reqif"
172
            ReqIFExport.export(
173
                project_config=self.project_config,
174
                traceability_index=self.traceability_index,
175
                output_reqif_root=output_reqif_root,
176
                reqifz=False,
177
            )
178
 
179
        if "reqifz-sdoc" in self.project_config.export_formats:
180
            output_reqif_root = f"{self.project_config.output_dir}/reqif"
181
            ReqIFExport.export(
182
                project_config=self.project_config,
183
                traceability_index=self.traceability_index,
184
                output_reqif_root=output_reqif_root,
185
                reqifz=True,
186
            )
187
 
188
        if "sdoc" in self.project_config.export_formats:
189
            self.export_sdoc()
190
 
191
        if "markdown" in self.project_config.export_formats:
192
            self.export_markdown()
193
 
194
        if "doxygen" in self.project_config.export_formats:
195
            output_doxygen_root = os.path.join(
196
                self.project_config.output_dir, "doxygen"
197
            )
198
            doxygen_generator = DoxygenGenerator(
199
                project_config=self.project_config
200
            )
201
            doxygen_generator.export(
202
                traceability_index=self.traceability_index,
203
                path_to_output_dir=output_doxygen_root,
204
            )
205
 
206
        if "spdx" in self.project_config.export_formats:
207
            output_dot_root = os.path.join(
208
                self.project_config.output_dir, "spdx"
209
            )
210
            Path(output_dot_root).mkdir(parents=True, exist_ok=True)
211
            SPDXGenerator().export_tree(
212
                self.project_config, self.traceability_index, output_dot_root
213
            )
214
 
215
        if "json" in self.project_config.export_formats:
216
            output_json_root = os.path.join(
217
                self.project_config.output_dir, "json"
218
            )
219
            Path(output_json_root).mkdir(parents=True, exist_ok=True)
220
            JSONGenerator().export_tree(
221
                self.traceability_index, self.project_config, output_json_root
222
            )
223
 
224
    def export_sdoc(self) -> None:
225
        assert self.project_config.input_paths
226
        try:
227
            traceability_index: TraceabilityIndex = (
228
                TraceabilityIndexBuilder.create(
229
                    project_config=self.project_config,
230
                    parallelizer=NullParallelizer(),
231
                )
232
            )
233
        except DocumentTreeError as exc:
234
            print(exc.to_print_message())  # noqa: T201
235
            sys.exit(1)
236
        else:
237
            assert traceability_index.document_tree
238
 
239
        writer = SDWriter(
240
            self.project_config, node_filter=traceability_index.node_filter
241
        )
242
 
243
        output_base_dir = (
244
            self.project_config.output_dir
245
            if self.project_config.output_dir is not None
246
            else os.path.join(os.getcwd(), "output")
247
        )
248
        output_dir = os.path.join(output_base_dir, "sdoc")
249
        for document in traceability_index.document_tree.document_list:
250
            assert document.meta
251
            assert document.meta.document_filename_base
252
            assert document.meta.input_doc_dir_rel_path
253
            output, fragments_dict = writer.write_with_fragments(
254
                document,
255
            )
256
 
257
            path_to_output_file_dir: str = os.path.join(
258
                output_dir, document.meta.input_doc_dir_rel_path.relative_path
259
            )
260
            Path(path_to_output_file_dir).mkdir(parents=True, exist_ok=True)
261
            path_to_output_file = os.path.join(
262
                path_to_output_file_dir, document.meta.document_filename_base
263
            )
264
            path_to_output_file += ".sdoc"
265
            with open(path_to_output_file, "w", encoding="utf8") as file:
266
                file.write(output)
267
 
268
            for fragment_path_, fragment_content_ in fragments_dict.items():
269
                path_to_output_fragment = os.path.join(
270
                    path_to_output_file_dir, fragment_path_
271
                )
272
                with open(
273
                    path_to_output_fragment, "w", encoding="utf8"
274
                ) as file_:
275
                    file_.write(fragment_content_)
276
 
277
    def export_markdown(self) -> None:
278
        assert self.project_config.input_paths
279
        try:
280
            traceability_index: TraceabilityIndex = (
281
                TraceabilityIndexBuilder.create(
282
                    project_config=self.project_config,
283
                    parallelizer=NullParallelizer(),
284
                )
285
            )
286
        except DocumentTreeError as exc:
287
            print(exc.to_print_message())  # noqa: T201
288
            sys.exit(1)
289
        else:
290
            assert traceability_index.document_tree
291
 
292
        writer = SDMarkdownWriter()
293
 
294
        output_base_dir = (
295
            self.project_config.output_dir
296
            if self.project_config.output_dir is not None
297
            else os.path.join(os.getcwd(), "output")
298
        )
299
        output_dir = os.path.join(output_base_dir, "markdown")
300
 
301
        for document in traceability_index.document_tree.document_list:
302
            assert document.meta
303
            assert document.meta.document_filename_base
304
            assert document.meta.input_doc_dir_rel_path
305
 
306
            output = writer.write(document)
307
 
308
            path_to_output_file_dir: str = os.path.join(
309
                output_dir, document.meta.input_doc_dir_rel_path.relative_path
310
            )
311
            Path(path_to_output_file_dir).mkdir(parents=True, exist_ok=True)
312
 
313
            path_to_output_file = os.path.join(
314
                path_to_output_file_dir,
315
                document.meta.document_filename_base + ".md",
316
            )
317
            with open(
318
                path_to_output_file,
319
                "w",
320
                encoding="utf8",
321
            ) as file:
322
                file.write(output)