Test Failed
Pull Request — master (#173)
by Jan
02:32
created

oval_graph.oval_node.oval_node.OvalNode.__init__()   A

Complexity

Conditions 4

Size

Total Lines 40
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 40
ccs 0
cts 17
cp 0
rs 9.45
c 0
b 0
f 0
cc 4
nop 2
crap 20
1
"""
2
    This file contains a OvalNode class, which is one node of OVAL tree
3
"""
4
5
import oval_graph.oval_node.evaluate as evaluate
6
7
8
class OvalNode():
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
9
    """The Oval Node object is one node of the OVAL tree.
10
       The graphic representation of the OVAL tree is the OVAL graph.
11
12
    Attributes:
13
        node_id (str): id of node
14
        node_type (str): type node
15
        value (str): value of node for operator and, or, one etc... and for value true,
16
        false, error etc...
17
        negation (bool): value indicating whether the node is negated
18
        comment (str): some comment about node
19
        tag (str): tag specifies if the node represents OVAL test,
20
        OVAL definition or XCCDF rule
21
        test_result_details (dict|None): information about test
22
        children ([OvalNode]): children of node
23
    """
24
25
    def __init__(self, **kwargs):
26
        """This metode construct OvalNode and validate values of parameteres.
27
28
        Required args:
29
            node_id (str|int): identifies node
30
            node_type (str): type of node (value or operator)
31
            value (str): value of node
32
33
        Optional args:
34
            negation (bool): value indicating whether the node is negated (empty eq False)
35
            comment (str): text about node (empty eq None)
36
            tag (str): tag specifies if the node represents OVAL test,
37
            OVAL definition or XCCDF rule (empty eq None)
38
            test_result_details (dict|None): information about test (empty eq None)
39
            children ([OvalNode]): array of children of node (empty eq empty array)
40
41
        Raises:
42
            KeyError, TypeError, ValueError
43
        """
44
        try:
45
            self.node_id = kwargs['node_id']
46
            self.node_type = self._validate_type(kwargs['node_type'])
47
            self.value = self._validate_value(self.node_type, kwargs['value'])
48
49
            self._check_missing_children_for_operator(
50
                kwargs.get('children', None))
51
            self.negation = self._validate_negation(
52
                kwargs.get('negation', False))
53
        except KeyError:
54
            raise Exception("Missing required argument!")
0 ignored issues
show
introduced by
Consider explicitly re-raising using the 'from' keyword
Loading history...
55
56
        self.comment = kwargs.get('comment', None)
57
        self.tag = kwargs.get('tag', None)
58
        self.test_result_details = kwargs.get('test_result_details', None)
59
60
        input_children = kwargs.get('children', None)
61
        self.children = []
62
        if input_children:
63
            for child in input_children:
64
                self.add_child(child)
65
66
    @staticmethod
67
    def _validate_negation(input_negation):
68
        if not isinstance(input_negation, bool):
69
            raise TypeError("Wrong value of negation argument!")
70
        return input_negation
71
72
    @staticmethod
73
    def _validate_type(input_node_type):
74
        node_type = input_node_type.lower()
75
        if node_type not in ("value", "operator"):
76
            raise TypeError("Wrong value of node_type argument!")
77
        return node_type
78
79
    @staticmethod
80
    def _validate_value(input_node_type, input_value):
81
        value = input_value.lower()
82
83
        allowed_values = [
84
            "true",
85
            "false",
86
            "error",
87
            "unknown",
88
            "noteval",
89
            "notappl"]
90
        allowed_operators = ["or", "and", "one", "xor"]
91
92
        if input_node_type == "value" and value not in allowed_values:
93
            raise TypeError(
94
                "Wrong value of argument value for value node!")
95
96
        if input_node_type == "operator" and value not in allowed_operators:
97
            raise TypeError(
98
                "Wrong value of argument value for operator node!")
99
100
        return value
101
102
    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...
103
        if children is None and self.node_type == "operator":
104
            raise ValueError(
105
                "The operator node must have a child!")
106
107
    def __repr__(self):
108
        return self.value
109
110
    def add_child(self, node):
111
        """Adds a OvalNode (node) as child of OvalNode.
112
113
        Args:
114
            node (OvalNode): New node
115
116
        Raises:
117
            ValueError
118
        """
119
        if self.node_type == "operator":
120
            assert isinstance(node, OvalNode)
121
            self.children.append(node)
122
            return
123
        raise ValueError(
124
            "The value node cannot contain any child!")
125
126
    def _get_result_counts(self):
127
        result = {
128
            'true_cnt': 0,
129
            'false_cnt': 0,
130
            'error_cnt': 0,
131
            'unknown_cnt': 0,
132
            'noteval_cnt': 0,
133
            'notappl_cnt': 0
134
        }
135
136
        for child in self.children:
137
            if child.value == 'true' and not child.negation:
138
                result['true_cnt'] += 1
139
            elif child.value == 'true' and child.negation:
140
                result['false_cnt'] += 1
141
            elif child.value == 'false' and not child.negation:
142
                result['false_cnt'] += 1
143
            elif child.value == 'false' and child.negation:
144
                result['true_cnt'] += 1
145
            else:
146
                if child.node_type == "operator":
147
                    result[child.evaluate_tree() + "_cnt"] += 1
148
                else:
149
                    result[child.value + "_cnt"] += 1
150
        return result
151
152
    def evaluate_tree(self):
153
        """Evaluates the entire OVAL tree.
154
155
        Returns:
156
            str. return values::
157
158
                true
159
                false
160
                error
161
                unknown
162
                noteval
163
                notappl
164
        """
165
        result = self._get_result_counts()
166
        out_result = None
167
        if evaluate.is_notapp_result(result):
168
            out_result = "notappl"
169
        else:
170
            if self.value == "or":
171
                out_result = evaluate.oval_operator_or(result)
172
            elif self.value == "and":
173
                out_result = evaluate.oval_operator_and(result)
174
            elif self.value == "one":
175
                out_result = evaluate.oval_operator_one(result)
176
            elif self.value == "xor":
177
                out_result = evaluate.oval_operator_xor(result)
178
179
        if out_result == 'true' and self.negation:
180
            out_result = 'false'
181
        elif out_result == 'false' and self.negation:
182
            out_result = 'true'
183
184
        return out_result
185
186
    def save_tree_to_dict(self):
187
        """Converts the entire OVAL tree to dict.
188
189
        Returns:
190
            dict. Dictionary representing OVAL tree
191
        """
192
        if not self.children:
193
            return {
194
                'node_id': self.node_id,
195
                'type': self.node_type,
196
                'value': self.value,
197
                'negation': self.negation,
198
                'comment': self.comment,
199
                'tag': self.tag,
200
                'test_result_details': self.test_result_details,
201
                'child': None
202
            }
203
        return {
204
            'node_id': self.node_id,
205
            'type': self.node_type,
206
            'value': self.value,
207
            'negation': self.negation,
208
            'comment': self.comment,
209
            'tag': self.tag,
210
            'test_result_details': self.test_result_details,
211
            'child': [child.save_tree_to_dict() for child in self.children]
212
        }
213
214
    def find_node_with_id(self, node_id):
215
        """Finds an OVAL tree node by ID.
216
217
        Args:
218
            node_id (str): ID of node
219
220
        Returns:
221
            OvalNode or None.
222
        """
223
        if self.node_id == node_id:
224
            return self
225
        for child in self.children:
226
            if child.node_id == node_id:
227
                return child
228
        for child in self.children:
229
            if child.children != []:
230
                return child.find_node_with_id(node_id)
231
        return None
232
233
    def add_to_tree(self, node_id, new_node):
234
        """Adds a OvalNode (new_node) below OvalNode with node_id.
235
236
        Args:
237
            node_id (str): ID of node
238
            newNode (OvalNode): New node
239
240
        Returns:
241
            bool. The return values::
242
243
                true -- Success!
244
                false -- Not found.
245
        """
246
        node = self.find_node_with_id(node_id)
247
        if node is not None:
248
            node.add_child(new_node)
249
            return True
250
        return False
251
252
    def change_tree_value(self, node_id, value):
253
        """Changes the value of OvalNode with node_id.
254
255
        Args:
256
            node_id (str): ID of node
257
            value (str): value of node::
258
259
                true
260
                false
261
                error
262
                unknown
263
                noteval
264
                notappl
265
266
        Returns:
267
            bool. The return values::
268
269
                true -- Success!
270
                false -- Not found.
271
        """
272
        node = self.find_node_with_id(node_id)
273
        if node is not None:
274
            self._validate_value(node.node_type, value)
275
            node.value = value
276
            return True
277
        return False
278
279
280
def restore_dict_to_tree(dict_of_tree):
281
    """Converts the dict of OVAL tree to OvalNode.
282
283
    Args:
284
        dict_of_tree (str): dict of OVAL tree
285
286
    Returns:
287
        OvalNode.
288
    """
289
    if dict_of_tree["child"] is None:
290
        return OvalNode(
291
            node_id=dict_of_tree["node_id"],
292
            node_type=dict_of_tree["type"],
293
            value=dict_of_tree["value"],
294
            negation=dict_of_tree["negation"],
295
            comment=dict_of_tree["comment"],
296
            tag=dict_of_tree["tag"],
297
            test_result_details=dict_of_tree["test_result_details"])
298
    return OvalNode(
299
        node_id=dict_of_tree["node_id"],
300
        node_type=dict_of_tree["type"],
301
        value=dict_of_tree["value"],
302
        negation=dict_of_tree["negation"],
303
        comment=dict_of_tree["comment"],
304
        tag=dict_of_tree["tag"],
305
        test_result_details=dict_of_tree["test_result_details"],
306
        children=[restore_dict_to_tree(i) for i in dict_of_tree["child"]])
307