StrictDoc Documentation
strictdoc/export/html/generators/traceability_matrix.py
Source file coverage
Path:
strictdoc/export/html/generators/traceability_matrix.py
Lines:
111
Non-empty lines:
96
Non-empty lines covered with requirements:
96 / 96 (100.0%)
Functions:
2
Functions covered by requirements:
2 / 2 (100.0%)
1
"""
2
@relation(SDOC-SRS-112, scope=file)
3
"""
4
 
5
from typing import Dict, Optional
6
 
7
from markupsafe import Markup
8
 
9
from strictdoc.backend.sdoc.models.document import SDocDocument
10
from strictdoc.backend.sdoc.models.document_grammar import DocumentGrammar
11
from strictdoc.core.project_config import ProjectConfig
12
from strictdoc.core.traceability_index import TraceabilityIndex
13
from strictdoc.export.html.generators.view_objects.traceability_matrix_view_object import (
14
    TraceabilityMatrixViewObject,
15
)
16
from strictdoc.export.html.html_templates import HTMLTemplates
17
from strictdoc.export.html.renderers.link_renderer import LinkRenderer
18
from strictdoc.export.html.renderers.markup_renderer import MarkupRenderer
19
 
20
 
21
class TraceabilityMatrixHTMLGenerator:
22
    @staticmethod
23
    def export(
24
        *,
25
        project_config: ProjectConfig,
26
        traceability_index: TraceabilityIndex,
27
        html_templates: HTMLTemplates,
28
    ) -> Markup:
29
        assert isinstance(html_templates, HTMLTemplates)
30
 
31
        known_relations: Dict[str, Dict[Optional[str], bool]] = {
32
            "Parent": {},
33
            "Child": {},
34
            "File": {},
35
        }
36
 
37
        discovered_relation_types = set()
38
 
39
        document_: SDocDocument
40
        for document_ in traceability_index.document_tree.document_list:
41
            assert document_.grammar is not None
42
            document_grammar: DocumentGrammar = document_.grammar
43
            for grammar_element_ in document_grammar.elements:
44
                for relation_ in grammar_element_.relations:
45
                    discovered_relation_types.add(relation_.relation_type)
46
 
47
                    bucket = known_relations[relation_.relation_type]
48
 
49
                    if relation_.relation_role not in bucket:
50
                        bucket[relation_.relation_role] = True
51
 
52
        for relation_type_ in list(known_relations.keys()):
53
            if relation_type_ not in discovered_relation_types:
54
                del known_relations[relation_type_]
55
 
56
        #
57
        # Validate that all config-provided relation tuples are actually present
58
        # in the existing documents.
59
        # A typical config entry may look like this:
60
        # [                                                   # noqa: ERA001
61
        #     ("Parent", None),                               # noqa: ERA001
62
        #     ("Parent", "Refines"),                          # noqa: ERA001
63
        #     ("Parent", "REQUIREMENT_FOR"),                  # noqa: ERA001
64
        #     ("File", None)                                  # noqa: ERA001
65
        # ]                                                   # noqa: ERA001
66
        #
67
        config_relation_tuples = (
68
            project_config.traceability_matrix_relation_columns
69
            if project_config.traceability_matrix_relation_columns is not None
70
            else []
71
        )
72
        for config_relation_tuple_ in config_relation_tuples:
73
            assert config_relation_tuple_[0] in known_relations, (
74
                config_relation_tuple_,
75
                known_relations,
76
            )
77
            bucket = known_relations[config_relation_tuple_[0]]
78
            assert config_relation_tuple_[1] in bucket
79
 
80
        #
81
        # After the config values have been validated, merge both the
82
        # config-provided list of relation tuples with the list that was
83
        # discovered from the existing documents.
84
        #
85
        known_relations_list = list(config_relation_tuples)
86
        for relation_type_, bucket_ in known_relations.items():
87
            for relation_role_, _ in bucket_.items():
88
                relation_tuple = (relation_type_, relation_role_)
89
                if relation_tuple not in known_relations_list:
90
                    known_relations_list.append(relation_tuple)
91
 
92
        link_renderer = LinkRenderer(
93
            root_path="", static_path=project_config.dir_for_sdoc_assets
94
        )
95
        markup_renderer = MarkupRenderer.create(
96
            "RST",
97
            traceability_index,
98
            link_renderer,
99
            html_templates,
100
            project_config,
101
            None,
102
        )
103
 
104
        view_object = TraceabilityMatrixViewObject(
105
            traceability_index=traceability_index,
106
            project_config=project_config,
107
            link_renderer=link_renderer,
108
            markup_renderer=markup_renderer,
109
            known_relations_list=known_relations_list,
110
        )
111
        return view_object.render_screen(html_templates.jinja_environment())