Passed
Pull Request — master (#173)
by Jan
05:28
created

OvalNode._get_result_counts()   C

Complexity

Conditions 11

Size

Total Lines 25
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 11

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 25
ccs 15
cts 15
cp 1
rs 5.4
c 0
b 0
f 0
cc 11
nop 1
crap 11

How to fix   Complexity   

Complexity

Complex classes like oval_graph.oval_tree.oval_node.OvalNode._get_result_counts() 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
"""
2
    This file contains a OvalNode class, which is one node of OVAL tree
3
"""
4
5 1
from .evaluate import (is_notapp_result, oval_operator_and, oval_operator_one,
6
                       oval_operator_or, oval_operator_xor)
7
8
9 1
class OvalNode():
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
10
    """The Oval Node object is one node of the OVAL tree.
11
       The graphic representation of the OVAL tree is the OVAL graph.
12
13
    Attributes:
14
        node_id (str): id of node
15
        node_type (str): type node
16
        value (str): value of node for operator and, or, one etc... and for value true,
17
        false, error etc...
18
        negation (bool): value indicating whether the node is negated
19
        comment (str): some comment about node
20
        tag (str): tag specifies if the node represents OVAL test,
21
        OVAL definition or XCCDF rule
22
        test_result_details (dict|None): information about test
23
        children ([OvalNode]): children of node
24
    """
25
26 1
    def __init__(self, **kwargs):
27
        """This metode construct OvalNode and validate values of parameteres.
28
29
        Required args:
30
            node_id (str|int): identifies node
31
            node_type (str): type of node (value or operator)
32
            value (str): value of node
33
34
        Optional args:
35
            negation (bool): value indicating whether the node is negated (empty eq False)
36
            comment (str): text about node (empty eq None)
37
            tag (str): tag specifies if the node represents OVAL test,
38
            OVAL definition or XCCDF rule (empty eq None)
39
            test_result_details (dict|None): information about test (empty eq None)
40
            children ([OvalNode]): array of children of node (empty eq empty array)
41
42
        Raises:
43
            KeyError, TypeError, ValueError
44
        """
45 1
        try:
46 1
            self.node_id = kwargs['node_id']
47 1
            self.node_type = self._validate_type(kwargs['node_type'])
48 1
            self.value = self._validate_value(self.node_type, kwargs['value'])
49
50 1
            self._check_missing_children_for_operator(
51
                kwargs.get('children', None))
52 1
            self.negation = self._validate_negation(
53
                kwargs.get('negation', False))
54 1
        except KeyError as key_error:
55 1
            raise Exception("Missing required argument!") from key_error
56
57 1
        self.comment = kwargs.get('comment', None)
58 1
        self.tag = kwargs.get('tag', None)
59 1
        self.test_result_details = kwargs.get('test_result_details', None)
60
61 1
        input_children = kwargs.get('children', None)
62 1
        self.children = []
63 1
        if input_children:
64 1
            for child in input_children:
65 1
                self.add_child(child)
66
67 1
    @staticmethod
68
    def _validate_negation(input_negation):
69 1
        if not isinstance(input_negation, bool):
70 1
            raise TypeError("Wrong value of negation argument!")
71 1
        return input_negation
72
73 1
    @staticmethod
74
    def _validate_type(input_node_type):
75 1
        node_type = input_node_type.lower()
76 1
        if node_type not in ("value", "operator"):
77 1
            raise TypeError("Wrong value of node_type argument!")
78 1
        return node_type
79
80 1
    @staticmethod
81
    def _validate_value(input_node_type, input_value):
82 1
        value = input_value.lower()
83
84 1
        allowed_values = [
85
            "true",
86
            "false",
87
            "error",
88
            "unknown",
89
            "noteval",
90
            "notappl"]
91 1
        allowed_operators = ["or", "and", "one", "xor"]
92
93 1
        if input_node_type == "value" and value not in allowed_values:
94 1
            raise TypeError(
95
                "Wrong value of argument value for value node!")
96
97 1
        if input_node_type == "operator" and value not in allowed_operators:
98 1
            raise TypeError(
99
                "Wrong value of argument value for operator node!")
100
101 1
        return value
102
103 1
    def _check_missing_children_for_operator(self, children):
0 ignored issues
show
Coding Style Naming introduced by
Method name "_check_missing_children_for_operator" doesn't conform to '[a-z_][a-z0-9_]2,30$' pattern ('[a-z_][a-z0-9_]2,30$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
104 1
        if children is None and self.node_type == "operator":
105 1
            raise ValueError(
106
                "The operator node must have a child!")
107
108 1
    def __repr__(self):
109 1
        return self.value
110
111 1
    def add_child(self, node):
112
        """Adds a OvalNode (node) as child of OvalNode.
113
114
        Args:
115
            node (OvalNode): New node
116
117
        Raises:
118
            ValueError
119
        """
120 1
        if self.node_type == "operator":
121 1
            assert isinstance(node, OvalNode)
122 1
            self.children.append(node)
123 1
            return
124 1
        raise ValueError(
125
            "The value node cannot contain any child!")
126
127 1
    def _get_result_counts(self):
128 1
        result = {
129
            'true_cnt': 0,
130
            'false_cnt': 0,
131
            'error_cnt': 0,
132
            'unknown_cnt': 0,
133
            'noteval_cnt': 0,
134
            'notappl_cnt': 0
135
        }
136
137 1
        for child in self.children:
138 1
            if child.value == 'true' and not child.negation:
139 1
                result['true_cnt'] += 1
140 1
            elif child.value == 'true' and child.negation:
141 1
                result['false_cnt'] += 1
142 1
            elif child.value == 'false' and not child.negation:
143 1
                result['false_cnt'] += 1
144 1
            elif child.value == 'false' and child.negation:
145 1
                result['true_cnt'] += 1
146
            else:
147 1
                if child.node_type == "operator":
148 1
                    result[child.evaluate_tree() + "_cnt"] += 1
149
                else:
150 1
                    result[child.value + "_cnt"] += 1
151 1
        return result
152
153 1
    def evaluate_tree(self):
154
        """Evaluates the entire OVAL tree.
155
156
        Returns:
157
            str. return values::
158
159
                true
160
                false
161
                error
162
                unknown
163
                noteval
164
                notappl
165
        """
166 1
        result = self._get_result_counts()
167 1
        out_result = None
168 1
        if is_notapp_result(result):
169 1
            out_result = "notappl"
170
        else:
171 1
            if self.value == "or":
172 1
                out_result = oval_operator_or(result)
173 1
            elif self.value == "and":
174 1
                out_result = oval_operator_and(result)
175 1
            elif self.value == "one":
176 1
                out_result = oval_operator_one(result)
177 1
            elif self.value == "xor":
178 1
                out_result = oval_operator_xor(result)
179
180 1
        if out_result == 'true' and self.negation:
181 1
            out_result = 'false'
182 1
        elif out_result == 'false' and self.negation:
183 1
            out_result = 'true'
184
185 1
        return out_result
186
187 1
    def find_node_with_id(self, node_id):
188
        """Finds an OVAL tree node by ID.
189
190
        Args:
191
            node_id (str): ID of node
192
193
        Returns:
194
            OvalNode or None.
195
        """
196 1
        if self.node_id == node_id:
197 1
            return self
198 1
        for child in self.children:
199 1
            if child.node_id == node_id:
200 1
                return child
201 1
        for child in self.children:
202 1
            if child.children != []:
203 1
                return child.find_node_with_id(node_id)
204
        return None
205
206 1
    def add_to_tree(self, node_id, new_node):
207
        """Adds a OvalNode (new_node) below OvalNode with node_id.
208
209
        Args:
210
            node_id (str): ID of node
211
            newNode (OvalNode): New node
212
213
        Returns:
214
            bool. The return values::
215
216
                true -- Success!
217
                false -- Not found.
218
        """
219 1
        node = self.find_node_with_id(node_id)
220 1
        if node is not None:
221 1
            node.add_child(new_node)
222 1
            return True
223
        return False
224
225 1
    def change_tree_value(self, node_id, value):
226
        """Changes the value of OvalNode with node_id.
227
228
        Args:
229
            node_id (str): ID of node
230
            value (str): value of node::
231
232
                true
233
                false
234
                error
235
                unknown
236
                noteval
237
                notappl
238
239
        Returns:
240
            bool. The return values::
241
242
                true -- Success!
243
                false -- Not found.
244
        """
245 1
        node = self.find_node_with_id(node_id)
246 1
        if node is not None:
247 1
            self._validate_value(node.node_type, value)
248 1
            node.value = value
249 1
            return True
250
        return False
251