StrictDoc Documentation
strictdoc/core/transforms/update_grammar_element.py
Source file coverage
Path:
strictdoc/core/transforms/update_grammar_element.py
Lines:
132
Non-empty lines:
110
Non-empty lines covered with requirements:
110 / 110 (100.0%)
Functions:
3
Functions covered by requirements:
3 / 3 (100.0%)
1
"""
2
@relation(SDOC-SRS-56, scope=file)
3
"""
4
 
5
from collections import OrderedDict
6
from typing import Dict, List, Optional, Set, Tuple
7
 
8
from strictdoc.backend.sdoc.models.document import SDocDocument
9
from strictdoc.backend.sdoc.models.grammar_element import (
10
    GrammarElement,
11
    GrammarElementField,
12
)
13
from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField
14
from strictdoc.core.traceability_index import (
15
    TraceabilityIndex,
16
)
17
from strictdoc.export.html.form_objects.grammar_element_form_object import (
18
    GrammarElementFormObject,
19
)
20
from strictdoc.helpers.cast import assert_cast
21
 
22
 
23
class UpdateGrammarElementCommand:
24
    def __init__(
25
        self,
26
        *,
27
        form_object: GrammarElementFormObject,
28
        document: SDocDocument,
29
        traceability_index: TraceabilityIndex,
30
    ):
31
        self.form_object: GrammarElementFormObject = form_object
32
        self.document: SDocDocument = document
33
        self.traceability_index: TraceabilityIndex = traceability_index
34
 
35
    def perform(self) -> None:
36
        form_object: GrammarElementFormObject = self.form_object
37
        document: SDocDocument = self.document
38
        assert document.grammar is not None
39
 
40
        existing_element: GrammarElement = document.grammar.get_element_by_mid(
41
            form_object.element_mid
42
        )
43
 
44
        grammar_fields: Dict[str, GrammarElementField] = {}
45
        for grammar_field in existing_element.fields:
46
            grammar_fields[grammar_field.mid] = grammar_field
47
 
48
        # Prepare fields that could have been renamed by the user has just saved the form.
49
        renamed_fields_lookup = {}
50
        for field in form_object.fields:
51
            if field.field_mid not in grammar_fields:
52
                continue
53
            existing_field = grammar_fields[field.field_mid]
54
            if field.field_name != existing_field.title:
55
                renamed_fields_lookup[field.field_name] = existing_field.title
56
 
57
        #
58
        # Convert the form object to an updated grammar element.
59
        #
60
        assert document.grammar is not None
61
        updated_element: GrammarElement = (
62
            form_object.convert_to_grammar_element(document.grammar)
63
        )
64
 
65
        #
66
        # Compare if anything was changed in the new grammar.
67
        #
68
        document_grammar_field_names = updated_element.get_field_titles()
69
 
70
        existing_requirement_element = document.grammar.elements_by_type[
71
            existing_element.tag
72
        ]
73
 
74
        document.grammar.update_element(existing_element, updated_element)
75
 
76
        document_iterator = self.traceability_index.document_iterators[document]
77
 
78
        for node, _ in document_iterator.all_content():
79
            if not isinstance(node, SDocNode):
80
                continue
81
 
82
            requirement: SDocNode = assert_cast(node, SDocNode)
83
            if requirement.node_type != updated_element.tag:
84
                continue
85
 
86
            requirement_field_names = list(
87
                requirement.ordered_fields_lookup.keys()
88
            )
89
 
90
            # Rewrite requirement fields because some fields could have been
91
            # renamed.
92
            new_ordered_fields_lookup: OrderedDict[str, List[SDocNodeField]] = (
93
                OrderedDict()
94
            )
95
 
96
            for document_grammar_field_name in document_grammar_field_names:
97
                # We need to find a previous field name in case the field was
98
                # renamed.
99
                previous_field_name = renamed_fields_lookup.get(
100
                    document_grammar_field_name, document_grammar_field_name
101
                )
102
 
103
                # If the field does not exist in the grammar fields anymore,
104
                # delete the requirement field.
105
                if previous_field_name not in requirement_field_names:
106
                    continue
107
 
108
                previous_fields: List[SDocNodeField] = (
109
                    requirement.ordered_fields_lookup[previous_field_name]
110
                )
111
                for previous_field in previous_fields:
112
                    previous_field.field_name = document_grammar_field_name
113
 
114
                new_ordered_fields_lookup[document_grammar_field_name] = (
115
                    previous_fields
116
                )
117
 
118
            registered_relation_types: Set[Tuple[str, Optional[str]]] = set()
119
            for relation in existing_requirement_element.relations:
120
                registered_relation_types.add(
121
                    (relation.relation_type, relation.relation_role)
122
                )
123
            if len(requirement.relations) > 0:
124
                new_relations = []
125
                for requirement_relation_ in requirement.relations:
126
                    if (
127
                        requirement_relation_.ref_type,
128
                        requirement_relation_.role,
129
                    ) in registered_relation_types:
130
                        new_relations.append(requirement_relation_)
131
                requirement.relations = new_relations
132
            requirement.ordered_fields_lookup = new_ordered_fields_lookup