StrictDoc Documentation
strictdoc/backend/sdoc/models/document_config.py
Source file coverage
Path:
strictdoc/backend/sdoc/models/document_config.py
Lines:
234
Non-empty lines:
204
Non-empty lines covered with requirements:
204 / 204 (100.0%)
Functions:
17
Functions covered by requirements:
17 / 17 (100.0%)
1
"""
2
@relation(SDOC-SRS-110, SDOC-SRS-151, scope=file)
3
"""
4
 
5
from typing import TYPE_CHECKING, Any, List, Optional, Tuple
6
 
7
from strictdoc.backend.sdoc.constants import SDocMarkup
8
from strictdoc.backend.sdoc.models.inline_link import InlineLink
9
from strictdoc.helpers.auto_described import auto_described
10
 
11
if TYPE_CHECKING:
12
    from strictdoc.backend.sdoc.models.document import SDocDocument
13
 
14
 
15
@auto_described
16
class DocumentCustomMetadataKeyValuePair:
17
    def __init__(
18
        self,
19
        *,
20
        parent: Optional["DocumentCustomMetadata"] = None,
21
        key: Optional[str],
22
        parts: List[Any],
23
    ) -> None:
24
        self.parent: Optional[DocumentCustomMetadata] = parent
25
        self.key = key
26
        self.parts: List[Any] = parts
27
        for part in parts:
28
            if isinstance(part, InlineLink):
29
                part.parent = self
30
 
31
    def get_text_value(self) -> str:
32
        text = ""
33
        for part in self.parts:
34
            if isinstance(part, str):
35
                text += part
36
            elif isinstance(part, InlineLink):
37
                text += "[LINK: "
38
                text += part.link
39
                text += "]"
40
            else:
41
                raise NotImplementedError(part)  # pragma: no cover
42
        return text
43
 
44
    def get_owning_node(self) -> "SDocDocument":
45
        assert self.parent is not None
46
        assert self.parent.parent is not None
47
        document = self.parent.parent.parent
48
        assert document is not None
49
        return document
50
 
51
 
52
@auto_described
53
class DocumentCustomMetadata:
54
    def __init__(
55
        self,
56
        *,
57
        parent: Optional["DocumentConfig"] = None,
58
        entries: List[DocumentCustomMetadataKeyValuePair],
59
    ) -> None:
60
        self.parent: Optional[DocumentConfig] = parent
61
        self.entries: List[DocumentCustomMetadataKeyValuePair] = entries
62
 
63
 
64
@auto_described
65
class DocumentConfig:
66
    @staticmethod
67
    def default_config(document: Optional["SDocDocument"]) -> "DocumentConfig":
68
        return DocumentConfig(
69
            parent=document,
70
            version=None,
71
            date=None,
72
            uid=None,
73
            classification=None,
74
            requirement_prefix=None,
75
            root=None,
76
            enable_mid=None,
77
            relation_field=None,
78
            markup=None,
79
            auto_levels=None,
80
            layout=None,
81
            requirement_style=None,
82
            requirement_in_toc=None,
83
            default_view=None,
84
            custom_metadata=None,
85
        )
86
 
87
    def __init__(
88
        self,
89
        *,
90
        parent: Optional["SDocDocument"],
91
        version: Optional[str],
92
        date: Optional[str],
93
        uid: Optional[str],
94
        classification: Optional[str],
95
        requirement_prefix: Optional[str],
96
        root: Optional[str],
97
        enable_mid: Optional[str],
98
        relation_field: Optional[str],
99
        markup: Optional[str],
100
        auto_levels: Optional[str],
101
        layout: Optional[str],
102
        requirement_style: Optional[str],
103
        requirement_in_toc: Optional[str],
104
        default_view: Optional[str],
105
        custom_metadata: Optional[DocumentCustomMetadata],
106
        view_style_tag: Optional[str] = None,
107
        node_in_toc_tag: Optional[str] = None,
108
    ) -> None:
109
        self.parent = parent
110
        self.version: Optional[str] = version
111
        self.date: Optional[str] = date
112
 
113
        meaningful_uid: Optional[str] = None
114
        if uid is not None and len(uid) > 0:
115
            meaningful_uid = uid
116
        self.uid: Optional[str] = meaningful_uid
117
 
118
        self.classification: Optional[str] = classification
119
        self.requirement_prefix: Optional[str] = requirement_prefix
120
 
121
        self.root: Optional[bool] = (
122
            True if root == "True" else (False if root == "False" else None)
123
        )
124
        self.enable_mid: Optional[bool] = (
125
            True
126
            if enable_mid == "True"
127
            else (False if root == "False" else None)
128
        )
129
        self.relation_field: Optional[str] = (
130
            relation_field
131
            if relation_field is not None and len(relation_field) > 0
132
            else None
133
        )
134
 
135
        self.markup: Optional[str] = markup
136
        self.auto_levels: bool = auto_levels is None or auto_levels == "On"
137
 
138
        if layout is not None:
139
            if len(layout) > 0:
140
                assert layout in ("Default", "Website")
141
            else:
142
                layout = None
143
        self.layout: Optional[str] = layout
144
 
145
        # Possible requirement styles:
146
        # Simple, Table, Rows.
147
        self.requirement_style: Optional[str] = requirement_style
148
        self.view_style_tag: Optional[str] = view_style_tag
149
 
150
        self.requirement_in_toc: Optional[str] = requirement_in_toc
151
        self.node_in_toc_tag: Optional[str] = node_in_toc_tag
152
 
153
        self.default_view: Optional[str] = default_view
154
        self.ng_auto_levels_specified = auto_levels is not None
155
 
156
        self.ng_line_start: int = 0
157
        self.ng_col_start: int = 0
158
 
159
        self.custom_metadata: Optional[DocumentCustomMetadata] = custom_metadata
160
 
161
    def get_markup(self) -> str:
162
        if self.markup is None:
163
            return SDocMarkup.RST
164
        return self.markup
165
 
166
    def get_requirement_style_mode(self) -> str:
167
        if (
168
            self.requirement_style is None
169
            or self.requirement_style == "Narrative"
170
        ):
171
            return "narrative"
172
        if self.requirement_style == "Plain":
173
            return "plain"
174
        if self.requirement_style in (
175
            "Inline",
176
            "Simple",
177
        ):
178
            return "simple"
179
        if self.requirement_style == "Table":
180
            return "table"
181
        if self.requirement_style == "Zebra":
182
            return "zebra"
183
        raise NotImplementedError(self.requirement_style)
184
 
185
    def is_requirement_in_toc(self) -> bool:
186
        return (
187
            self.requirement_in_toc is None or self.requirement_in_toc == "True"
188
        )
189
 
190
    def get_prefix(self) -> str:
191
        if self.requirement_prefix is not None:
192
            return self.requirement_prefix
193
        return "REQ-"
194
 
195
    def get_relation_field(self) -> str:
196
        return self.relation_field or "UID"
197
 
198
    def has_meta(self) -> bool:
199
        # TODO: When OPTIONS are not provided to a document, the self.number and
200
        # self.version are both None. Otherwise, they become empty strings "".
201
        # This issue might deserve a bug report to TextX.
202
        return (
203
            (self.uid is not None and len(self.uid) > 0)
204
            or (self.version is not None and len(self.version) > 0)
205
            or (
206
                self.classification is not None and len(self.classification) > 0
207
            )
208
        )
209
 
210
    def has_custom_metadata(self) -> bool:
211
        return len(self.get_custom_metadata()) > 0
212
 
213
    def get_custom_metadata(self) -> List[Tuple[str, str]]:
214
        if (
215
            self.custom_metadata is not None
216
            and self.custom_metadata.entries is not None
217
        ):
218
            reserved_keys = {
219
                key
220
                for key, value in (
221
                    ("UID", self.uid),
222
                    ("VERSION", self.version),
223
                    ("DATE", self.date),
224
                    ("CLASSIFICATION", self.classification),
225
                )
226
                if value is not None and len(value) > 0
227
            }
228
            return [
229
                (entry.key, entry.get_text_value())
230
                for entry in self.custom_metadata.entries
231
                if entry.key is not None
232
                and entry.key.upper() not in reserved_keys
233
            ]
234
        return []