Passed
Pull Request — master (#173)
by Jan
05:08 queued 01:33
created

oval_graph.oval_tree.oval_node   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 247
Duplicated Lines 0 %

Test Coverage

Coverage 97.12%

Importance

Changes 0
Metric Value
wmc 50
eloc 127
dl 0
loc 247
ccs 101
cts 104
cp 0.9712
rs 8.4
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A OvalNode.add_to_tree() 0 18 2
A OvalNode.__repr__() 0 2 1
C OvalNode._get_result_counts() 0 25 11
C OvalNode.evaluate_tree() 0 33 10
A OvalNode.add_child() 0 15 2
B OvalNode.find_node_with_id() 0 18 6
A OvalNode.change_tree_value() 0 26 2
A OvalNode._validate_negation() 0 5 2
A OvalNode.__init__() 0 40 4
A OvalNode._validate_type() 0 6 2
A OvalNode._check_missing_children_for_operator() 0 4 3
A OvalNode._validate_value() 0 22 5

How to fix   Complexity   

Complexity

Complex classes like oval_graph.oval_tree.oval_node often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1 1
from .evaluate import (is_notapp_result, oval_operator_and, oval_operator_one,
2
                       oval_operator_or, oval_operator_xor)
3
4
5 1
class OvalNode():
6
    """The Oval Node object is one node of the OVAL tree.
7
       The graphic representation of the OVAL tree is the OVAL graph.
8
9
    Attributes:
10
        node_id (str): id of node
11
        node_type (str): type node
12
        value (str): value of node for operator and, or, one etc... and for value true,
13
        false, error etc...
14
        negation (bool): value indicating whether the node is negated
15
        comment (str): some comment about node
16
        tag (str): tag specifies if the node represents OVAL test,
17
        OVAL definition or XCCDF rule
18
        test_result_details (dict|None): information about test
19
        children ([OvalNode]): children of node
20
    """
21
22 1
    def __init__(self, **kwargs):
23
        """This metode construct OvalNode and validate values of parameteres.
24
25
        Required args:
26
            node_id (str|int): identifies node
27
            node_type (str): type of node (value or operator)
28
            value (str): value of node
29
30
        Optional args:
31
            negation (bool): value indicating whether the node is negated (empty eq False)
32
            comment (str): text about node (empty eq None)
33
            tag (str): tag specifies if the node represents OVAL test,
34
            OVAL definition or XCCDF rule (empty eq None)
35
            test_result_details (dict|None): information about test (empty eq None)
36
            children ([OvalNode]): array of children of node (empty eq empty array)
37
38
        Raises:
39
            KeyError, TypeError, ValueError
40
        """
41 1
        try:
42 1
            self.node_id = kwargs['node_id']
43 1
            self.node_type = self._validate_type(kwargs['node_type'])
44 1
            self.value = self._validate_value(self.node_type, kwargs['value'])
45
46 1
            self._check_missing_children_for_operator(
47
                kwargs.get('children', None))
48 1
            self.negation = self._validate_negation(
49
                kwargs.get('negation', False))
50 1
        except KeyError as key_error:
51 1
            raise Exception("Missing required argument!") from key_error
52
53 1
        self.comment = kwargs.get('comment', None)
54 1
        self.tag = kwargs.get('tag', None)
55 1
        self.test_result_details = kwargs.get('test_result_details', None)
56
57 1
        input_children = kwargs.get('children', None)
58 1
        self.children = []
59 1
        if input_children:
60 1
            for child in input_children:
61 1
                self.add_child(child)
62
63 1
    @staticmethod
64
    def _validate_negation(input_negation):
65 1
        if not isinstance(input_negation, bool):
66 1
            raise TypeError("Wrong value of negation argument!")
67 1
        return input_negation
68
69 1
    @staticmethod
70
    def _validate_type(input_node_type):
71 1
        node_type = input_node_type.lower()
72 1
        if node_type not in ("value", "operator"):
73 1
            raise TypeError("Wrong value of node_type argument!")
74 1
        return node_type
75
76 1
    @staticmethod
77
    def _validate_value(input_node_type, input_value):
78 1
        value = input_value.lower()
79
80 1
        allowed_values = [
81
            "true",
82
            "false",
83
            "error",
84
            "unknown",
85
            "noteval",
86
            "notappl"]
87 1
        allowed_operators = ["or", "and", "one", "xor"]
88
89 1
        if input_node_type == "value" and value not in allowed_values:
90 1
            raise TypeError(
91
                "Wrong value of argument value for value node!")
92
93 1
        if input_node_type == "operator" and value not in allowed_operators:
94 1
            raise TypeError(
95
                "Wrong value of argument value for operator node!")
96
97 1
        return value
98
99 1
    def _check_missing_children_for_operator(self, children):
100 1
        if children is None and self.node_type == "operator":
101 1
            raise ValueError(
102
                "The operator node must have a child!")
103
104 1
    def __repr__(self):
105 1
        return self.value
106
107 1
    def add_child(self, node):
108
        """Adds a OvalNode (node) as child of OvalNode.
109
110
        Args:
111
            node (OvalNode): New node
112
113
        Raises:
114
            ValueError
115
        """
116 1
        if self.node_type == "operator":
117 1
            assert isinstance(node, OvalNode)
118 1
            self.children.append(node)
119 1
            return
120 1
        raise ValueError(
121
            "The value node cannot contain any child!")
122
123 1
    def _get_result_counts(self):
124 1
        result = {
125
            'true_cnt': 0,
126
            'false_cnt': 0,
127
            'error_cnt': 0,
128
            'unknown_cnt': 0,
129
            'noteval_cnt': 0,
130
            'notappl_cnt': 0
131
        }
132
133 1
        for child in self.children:
134 1
            if child.value == 'true' and not child.negation:
135 1
                result['true_cnt'] += 1
136 1
            elif child.value == 'true' and child.negation:
137 1
                result['false_cnt'] += 1
138 1
            elif child.value == 'false' and not child.negation:
139 1
                result['false_cnt'] += 1
140 1
            elif child.value == 'false' and child.negation:
141 1
                result['true_cnt'] += 1
142
            else:
143 1
                if child.node_type == "operator":
144 1
                    result[child.evaluate_tree() + "_cnt"] += 1
145
                else:
146 1
                    result[child.value + "_cnt"] += 1
147 1
        return result
148
149 1
    def evaluate_tree(self):
150
        """Evaluates the entire OVAL tree.
151
152
        Returns:
153
            str. return values::
154
155
                true
156
                false
157
                error
158
                unknown
159
                noteval
160
                notappl
161
        """
162 1
        result = self._get_result_counts()
163 1
        out_result = None
164 1
        if is_notapp_result(result):
165 1
            out_result = "notappl"
166
        else:
167 1
            if self.value == "or":
168 1
                out_result = oval_operator_or(result)
169 1
            elif self.value == "and":
170 1
                out_result = oval_operator_and(result)
171 1
            elif self.value == "one":
172 1
                out_result = oval_operator_one(result)
173 1
            elif self.value == "xor":
174 1
                out_result = oval_operator_xor(result)
175
176 1
        if out_result == 'true' and self.negation:
177 1
            out_result = 'false'
178 1
        elif out_result == 'false' and self.negation:
179 1
            out_result = 'true'
180
181 1
        return out_result
182
183 1
    def find_node_with_id(self, node_id):
184
        """Finds an OVAL tree node by ID.
185
186
        Args:
187
            node_id (str): ID of node
188
189
        Returns:
190
            OvalNode or None.
191
        """
192 1
        if self.node_id == node_id:
193 1
            return self
194 1
        for child in self.children:
195 1
            if child.node_id == node_id:
196 1
                return child
197 1
        for child in self.children:
198 1
            if child.children != []:
199 1
                return child.find_node_with_id(node_id)
200
        return None
201
202 1
    def add_to_tree(self, node_id, new_node):
203
        """Adds a OvalNode (new_node) below OvalNode with node_id.
204
205
        Args:
206
            node_id (str): ID of node
207
            newNode (OvalNode): New node
208
209
        Returns:
210
            bool. The return values::
211
212
                true -- Success!
213
                false -- Not found.
214
        """
215 1
        node = self.find_node_with_id(node_id)
216 1
        if node is not None:
217 1
            node.add_child(new_node)
218 1
            return True
219
        return False
220
221 1
    def change_tree_value(self, node_id, value):
222
        """Changes the value of OvalNode with node_id.
223
224
        Args:
225
            node_id (str): ID of node
226
            value (str): value of node::
227
228
                true
229
                false
230
                error
231
                unknown
232
                noteval
233
                notappl
234
235
        Returns:
236
            bool. The return values::
237
238
                true -- Success!
239
                false -- Not found.
240
        """
241 1
        node = self.find_node_with_id(node_id)
242 1
        if node is not None:
243 1
            self._validate_value(node.node_type, value)
244 1
            node.value = value
245 1
            return True
246
        return False
247