Passed
Push — master ( b5a080...903f35 )
by Jan
06:45 queued 01:03
created

oval_graph.oval_tree.oval_node   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Test Coverage

Coverage 67.65%

Importance

Changes 0
Metric Value
wmc 47
eloc 118
dl 0
loc 179
rs 8.64
c 0
b 0
f 0
ccs 69
cts 102
cp 0.6765

12 Methods

Rating   Name   Duplication   Size   Complexity  
A OvalNode.__init__() 0 37 3
A OvalNode.__repr__() 0 2 1
A OvalNode.add_child() 0 7 2
A OvalNode._validate_negation() 0 5 2
A OvalNode._validate_type() 0 6 2
A OvalNode._check_missing_children_for_operator() 0 4 3
A OvalNode._validate_value() 0 13 5
C OvalNode._get_result_counts() 0 17 11
C OvalNode.evaluate_tree() 0 22 10
A OvalNode.change_value_of_node() 0 7 2
A OvalNode.add_child_to_node() 0 6 2
A OvalNode.find_node_with_id() 0 8 4

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