Passed
Pull Request — master (#173)
by Jan
04:56
created

OvalNode.evaluate_tree()   C

Complexity

Conditions 10

Size

Total Lines 21
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 10

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 21
ccs 18
cts 18
cp 1
rs 5.9999
c 0
b 0
f 0
cc 10
nop 1
crap 10

How to fix   Complexity   

Complexity

Complex classes like oval_graph.oval_tree.oval_node.OvalNode.evaluate_tree() 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 . import evaluate
2
3 1
ALLOWED_VALUES = ("true", "false", "error", "unknown", "noteval", "notappl")
4 1
ALLOWED_OPERATORS = ("or", "and", "one", "xor")
5
6
7 1
class OvalNode():
8
    """The Oval Node object is one node of the OVAL tree.
9
       The graphic representation of the OVAL tree is the OVAL graph.
10
11
    Attributes:
12
        node_id (str): id of node
13
        node_type (str): type node
14
        value (str): value of node for operator and, or, one etc... and for value true,
15
        false, error etc...
16
        negation (bool): value indicating whether the node is negated
17
        comment (str): some comment about node
18
        tag (str): tag specifies if the node represents OVAL test,
19
        OVAL definition or XCCDF rule
20
        test_result_details (dict|None): information about test
21
        children ([OvalNode]): children of node
22
    """
23
24 1
    def __init__(self, node_id, node_type, value, **kwargs):
25
        """This metode construct OvalNode and validate values of parameteres.
26
27
        Required args:
28
            node_id (str|int): identifies node
29
            node_type (str): type of node (value or operator)
30
            value (str): value of node
31
32
        Optional args:
33
            negation (bool): value indicating whether the node is negated (empty eq False)
34
            comment (str): text about node (empty eq None)
35
            tag (str): tag specifies if the node represents OVAL test,
36
            OVAL definition or XCCDF rule (empty eq None)
37
            test_result_details (dict|None): information about test (empty eq None)
38
            children ([OvalNode]): array of children of node (empty eq empty array)
39
40
        Raises:
41
            TypeError, ValueError
42
        """
43 1
        self.node_id = node_id
44 1
        self.node_type = self._validate_type(node_type)
45 1
        self.value = self._validate_value(self.node_type, value)
46
47 1
        self._check_missing_children_for_operator(
48
            kwargs.get('children', None))
49 1
        self.negation = self._validate_negation(
50
            kwargs.get('negation', False))
51
52 1
        self.comment = kwargs.get('comment', None)
53 1
        self.tag = kwargs.get('tag', None)
54 1
        self.test_result_details = kwargs.get('test_result_details', None)
55
56 1
        input_children = kwargs.get('children', None)
57 1
        self.children = []
58 1
        if input_children:
59 1
            for child in input_children:
60 1
                self.add_child(child)
61
62 1
    @staticmethod
63
    def _validate_negation(input_negation):
64 1
        if not isinstance(input_negation, bool):
65 1
            raise TypeError("Wrong value of negation argument!")
66 1
        return input_negation
67
68 1
    @staticmethod
69
    def _validate_type(input_node_type):
70 1
        node_type = input_node_type.lower()
71 1
        if node_type not in ("value", "operator"):
72 1
            raise TypeError("Wrong value of node_type argument!")
73 1
        return node_type
74
75 1
    @staticmethod
76
    def _validate_value(input_node_type, input_value):
77 1
        value = input_value.lower()
78
79 1
        if input_node_type == "value" and value not in ALLOWED_VALUES:
80 1
            raise TypeError(
81
                "Wrong value of argument value for value node!")
82
83 1
        if input_node_type == "operator" and value not in ALLOWED_OPERATORS:
84 1
            raise TypeError(
85
                "Wrong value of argument value for operator node!")
86
87 1
        return value
88
89 1
    def _check_missing_children_for_operator(self, children):
90 1
        if children is None and self.node_type == "operator":
91 1
            raise ValueError(
92
                "The operator node must have a child!")
93
94 1
    def __repr__(self):
95 1
        return self.value
96
97 1
    def add_child(self, node):
98 1
        if self.node_type == "operator":
99 1
            assert isinstance(node, OvalNode)
100 1
            self.children.append(node)
101 1
            return
102 1
        raise ValueError(
103
            "The value node cannot contain any child!")
104
105 1
    def _get_result_counts(self):
106 1
        result = {
107
            'true_cnt': 0,
108
            'false_cnt': 0,
109
            'error_cnt': 0,
110
            'unknown_cnt': 0,
111
            'noteval_cnt': 0,
112
            'notappl_cnt': 0
113
        }
114
115 1
        for child in self.children:
116 1
            if child.value == 'true' and not child.negation:
117 1
                result['true_cnt'] += 1
118 1
            elif child.value == 'true' and child.negation:
119 1
                result['false_cnt'] += 1
120 1
            elif child.value == 'false' and not child.negation:
121 1
                result['false_cnt'] += 1
122 1
            elif child.value == 'false' and child.negation:
123 1
                result['true_cnt'] += 1
124
            else:
125 1
                if child.node_type == "operator":
126 1
                    result[child.evaluate_tree() + "_cnt"] += 1
127
                else:
128 1
                    result[child.value + "_cnt"] += 1
129 1
        return result
130
131 1
    def evaluate_tree(self):
132 1
        result = self._get_result_counts()
133 1
        out_result = None
134 1
        if evaluate.is_notapp_result(result):
135 1
            out_result = "notappl"
136
        else:
137 1
            if self.value == "or":
138 1
                out_result = evaluate.oval_operator_or(result)
139 1
            elif self.value == "and":
140 1
                out_result = evaluate.oval_operator_and(result)
141 1
            elif self.value == "one":
142 1
                out_result = evaluate.oval_operator_one(result)
143 1
            elif self.value == "xor":
144 1
                out_result = evaluate.oval_operator_xor(result)
145
146 1
        if out_result == 'true' and self.negation:
147 1
            out_result = 'false'
148 1
        elif out_result == 'false' and self.negation:
149 1
            out_result = 'true'
150
151 1
        return out_result
152
153 1
    def find_node_with_id(self, node_id):
154 1
        if self.node_id == node_id:
155 1
            return self
156 1
        for child in self.children:
157 1
            if child.node_id == node_id:
158 1
                return child
159 1
        for child in self.children:
160 1
            if child.children != []:
161 1
                return child.find_node_with_id(node_id)
162
        return None
163
164 1
    def add_child_to_node(self, node_id, new_node):
165 1
        node = self.find_node_with_id(node_id)
166 1
        if node is not None:
167 1
            node.add_child(new_node)
168 1
            return True
169
        return False
170
171 1
    def change_value_of_node(self, node_id, value):
172 1
        node = self.find_node_with_id(node_id)
173 1
        if node is not None:
174 1
            self._validate_value(node.node_type, value)
175 1
            node.value = value
176 1
            return True
177
        return False
178