Passed
Push — master ( eb090f...f3512f )
by Matěj
03:08 queued 11s
created

graph.oval_graph.OvalNode._count_max_y()   A

Complexity

Conditions 3

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 7
ccs 6
cts 6
cp 1
rs 10
c 0
b 0
f 0
cc 3
nop 2
crap 3
1
'''
2
    Modules form my lib and for create ID
3
'''
4 1
import graph.xml_parser
5 1
import graph.evaluate
6 1
import uuid
7 1
import collections
8 1
import re
9
10
'''
11
    This module contains methods and classes for
12
    constructing and controlling an oval tree.
13
'''
14
15
16 1
class OvalNode():
17
    '''
18
    The OvalNode object is one node of oval graph.
19
20
    Args:
21
        node_id (str|int): identifies node
22
        input_node_type (str): type of node (value or operator)
23
        input_value (str): value of node
24
        children ([OvalNode]): array of children of node
25
26
    Attributes:
27
        node_id (str): id of node
28
        node_type (str): type node
29
        value (str): value of node for operator and,
30
        or, one etc... and for value true, false, error etc...
31
        children ([OvalNode]): children of node
32
    '''
33
34 1
    def __init__(
35
            self,
36
            node_id,
37
            input_node_type,
38
            input_value,
39
            input_negation,
40
            children=None):
41 1
        self.node_id = node_id
42 1
        if isinstance(input_negation, bool):
43 1
            self.negation = input_negation
44
        else:
45 1
            raise ValueError("err- negation si bool (only True or False)")
46 1
        value = input_value.lower()
47 1
        node_type = input_node_type.lower()
48 1
        if node_type == "value" or node_type == "operator":
49 1
            self.node_type = node_type
50
        else:
51 1
            raise ValueError("err- unknown type")
52 1
        allowed_operators = [
53
            "or",
54
            "and",
55
            "one",
56
            "xor"]
57 1
        allowed_values = [
58
            "true",
59
            "false",
60
            "error",
61
            "unknown",
62
            "noteval",
63
            "notappl"]
64 1
        if self.node_type == "value":
65 1
            if value in allowed_values:
66 1
                self.value = value
67
            else:
68 1
                raise ValueError("err- unknown value")
69 1
        if self.node_type == "operator":
70 1
            if value in allowed_operators:
71 1
                self.value = value
72
            else:
73 1
                raise ValueError("err- unknown operator")
74 1
        self.children = []
75 1
        if children is not None:
76 1
            for child in children:
77 1
                self.add_child(child)
78
        else:
79 1
            if self.node_type == "operator":
80 1
                raise ValueError('err- OR, XOR, ONE, AND have child!')
81
82 1
    def __repr__(self):
83 1
        return self.value
84
85 1
    def add_child(self, node):
86 1
        if self.node_type == "operator":
87 1
            assert isinstance(node, OvalNode)
88 1
            self.children.append(node)
89
        else:
90 1
            self.children = None
91 1
            raise ValueError(
92
                "err- true, false, error, unknown. noteval, notappl have not child!")
93
94 1
    def _get_result_counts(self):
95 1
        result = {
96
            'true_cnt': 0,
97
            'false_cnt': 0,
98
            'error_cnt': 0,
99
            'unknown_cnt': 0,
100
            'noteval_cnt': 0,
101
            'notappl_cnt': 0
102
        }
103
104 1
        for child in self.children:
105 1
            if child.value == 'true':
106 1
                if child.negation:
107
                    result['false_cnt'] += 1
108
                else:
109 1
                    result['true_cnt'] += 1
110 1
            elif child.value == 'false':
111 1
                if child.negation:
112 1
                    result['true_cnt'] += 1
113
                else:
114 1
                    result['false_cnt'] += 1
115 1
            elif child.value == 'error':
116 1
                result['error_cnt'] += 1
117 1
            elif child.value == 'unknown':
118 1
                result['unknown_cnt'] += 1
119 1
            elif child.value == 'noteval':
120 1
                result['noteval_cnt'] += 1
121 1
            elif child.value == 'notappl':
122 1
                result['notappl_cnt'] += 1
123
            else:
124 1
                if self.node_type == "operator":
125 1
                    result[child.evaluate_tree() + "_cnt"] += 1
126 1
        return result
127
128 1
    def evaluate_tree(self):
129 1
        result = self._get_result_counts()
130 1
        out_result = None
131 1
        if graph.evaluate.is_notapp_result(result):
132 1
            out_result = "notappl"
133
        else:
134 1
            if self.value == "or":
135 1
                out_result = graph.evaluate.oval_operator_or(result)
136 1
            elif self.value == "and":
137 1
                out_result = graph.evaluate.oval_operator_and(result)
138 1
            elif self.value == "one":
139 1
                out_result = graph.evaluate.oval_operator_one(result)
140 1
            elif self.value == "xor":
141 1
                out_result = graph.evaluate.oval_operator_xor(result)
142
143 1
        if out_result == 'true' and self.negation:
144
            out_result = 'false'
145 1
        elif out_result == 'false' and self.negation:
146 1
            out_result = 'true'
147
148 1
        return out_result
149
150 1
    def save_tree_to_dict(self):
151 1
        if not self.children:
152 1
            return {
153
                'node_id': self.node_id,
154
                'type': self.node_type,
155
                'value': self.value,
156
                'negation': self.negation,
157
                'child': None
158
            }
159 1
        return {
160
            'node_id': self.node_id,
161
            'type': self.node_type,
162
            'value': self.value,
163
            'negation': self.negation,
164
            'child': [child.save_tree_to_dict() for child in self.children]
165
        }
166
167 1
    def find_node_with_ID(self, node_id):
168 1
        if self.node_id == node_id:
169 1
            return self
170
        else:
171 1
            for child in self.children:
172 1
                if child.node_id == node_id:
173 1
                    return child
174 1
            for child in self.children:
175 1
                if child.children != []:
176 1
                    return child.find_node_with_ID(node_id)
177
178 1
    def add_to_tree(self, node_id, newNode):
179 1
        self.find_node_with_ID(node_id).add_child(newNode)
180
181 1
    def change_tree_value(self, node_id, value):
182 1
        self.find_node_with_ID(node_id).value = value
183
184
    # Methods for interpreting oval tree with SigmaJS
185
186 1
    def _get_label(self):
187 1
        if self.node_type == 'value':
188 1
            return re.sub(
189
                '(oval:ssg-test_|oval:ssg-)|(:def:1|:tst:1)', '', str(self.node_id))
190
        else:
191 1
            if str(self.node_id).startswith('xccdf_org'):
192 1
                return re.sub(
193
                    '(xccdf_org.ssgproject.content_)', '', str(
194
                        self.node_id))
195 1
            return self.value
196
197 1
    def _get_node_color(self):
198 1
        value = self.evaluate_tree()
199 1
        if value is None:
200 1
            if self.value == 'true' and self.negation:
201
                value = 'false'
202 1
            elif self.value == 'false' and self.negation:
203
                value = 'true'
204
            else:
205 1
                value = self.value
206 1
        VALUE_TO_COLOR = {
207
            "true": "#00ff00",
208
            "false": "#ff0000",
209
            "error": "#000000",
210
            "unknown": "#000000",
211
            "noteval": "#000000",
212
            "notappl": "#000000"
213
        }
214 1
        return VALUE_TO_COLOR[value]
215
216 1
    def _get_node_title(self):
217 1
        value = self.evaluate_tree()
218 1
        if value is None:
219 1
            value = self.value
220 1
        if value == 'true' or value == 'false':
221 1
            return self.node_id
222 1
        return str(self.node_id) + ' ' + self.value
223
224 1
    def _create_node(self, x, y):
225
        # print(self.evaluate_tree(),self.value)
226 1
        return {
227
            'id': self.node_id,
228
            'label': self._get_label(),
229
            'url': 'null',
230
            'text': 'null',
231
            'title': self._get_node_title(),
232
            "x": x,
233
            "y": y,
234
            "size": 3,
235
            "color": self._get_node_color()}
236
237 1
    def _create_edge(self, id_source, id_target, target_node):
238 1
        return {
239
            "id": str(uuid.uuid4()),
240
            "source": id_source,
241
            "target": id_target,
242
            "color": self._get_color_edge(target_node)
243
        }
244
245 1
    def _get_color_edge(self, target_node):
246 1
        return target_node['color']
247
248 1
    def create_list_of_id(self, array_of_ids=None):
249 1
        if array_of_ids is None:
250 1
            array_of_ids = []
251 1
            array_of_ids.append(self.node_id)
252 1
        for child in self.children:
253 1
            if child.node_type != "operator":
254 1
                array_of_ids.append(child.node_id)
255
            else:
256 1
                array_of_ids.append(child.node_id)
257 1
                child.create_list_of_id(array_of_ids)
258 1
        return array_of_ids
259
260 1
    def _remove_Duplication(self, graph_data):
261 1
        array_of_ids = self.create_list_of_id()
262 1
        out = dict(nodes=[], edges=graph_data['edges'])
263 1
        duplicate_ids = [item for item, count in collections.Counter(
264
            array_of_ids).items() if count > 1]
265
266 1
        for node in graph_data['nodes']:
267 1
            if node['id'] not in duplicate_ids:
268 1
                out['nodes'].append(node)
269
270 1
        for id in duplicate_ids:
271 1
            for node in graph_data['nodes']:
272 1
                if node['id'] == id:
273 1
                    out['nodes'].append(node)
274 1
                    break
275 1
        return out
276
277 1
    def _fix_graph(self, preprocessed_graph_data):
278 1
        for node in preprocessed_graph_data['nodes']:
279 1
            for node1 in preprocessed_graph_data['nodes']:
280 1
                if node['x'] == node1['x'] and node['y'] == node1['y']:
281 1
                    node['x'] = node['x'] - 1
282 1
        return preprocessed_graph_data
283
284 1
    def _help_to_sigma_dict(self, x, y, preprocessed_graph_data=None):
285 1
        if preprocessed_graph_data is None:
286 1
            preprocessed_graph_data = dict(nodes=[], edges=[])
287 1
            preprocessed_graph_data['nodes'].append(self._create_node(x, y))
288 1
        y_row = y + 1
289 1
        x_row = x
290 1
        for node in self.children:
291 1
            preprocessed_graph_data['nodes'].append(
292
                node._create_node(x_row, y_row))
293 1
            preprocessed_graph_data['edges'].append(node._create_edge(
294
                self.node_id, node.node_id, preprocessed_graph_data['nodes'][-1]))
295 1
            x_row = x_row + 1
296 1
            if node.children is not None:
297 1
                preprocessed_graph_data = node._help_to_sigma_dict(
298
                    x_row + 1, y_row + 1, preprocessed_graph_data)
299 1
        return self._fix_graph(preprocessed_graph_data)
300
301 1
    def _count_max_y(self, out):
302 1
        max_y = 0
303
304 1
        for node in out['nodes']:
305 1
            if max_y < node['y']:
306 1
                max_y = node['y']
307 1
        return max_y
308
309 1
    def _create_nodes_in_rows(self, rows):
310 1
        nodes_in_rows = dict()
311
312 1
        for i in range(rows + 1):
313 1
            nodes_in_rows[i] = []
314 1
        return nodes_in_rows
315
316 1
    def _push_nodes_to_nodes_in_row(self, out, nodes_in_rows):
317 1
        for node in out['nodes']:
318 1
            nodes_in_rows[node['y']].append(node)
319
320 1
    def _remove_empty_rows(self, nodes_in_rows, max_y):
321 1
        for row in range(max_y + 1):
322 1
            if not nodes_in_rows[row]:
323 1
                del nodes_in_rows[row]
324
325 1
    def _move_rows(self, nodes_in_rows):
326 1
        count = 0
327 1
        nodes_in_rows1 = dict()
328
329 1
        for row in nodes_in_rows:
330 1
            nodes_in_rows1[count] = nodes_in_rows[row]
331 1
            for node in nodes_in_rows1[count]:
332 1
                node['y'] = count
333 1
            count += 1
334 1
        return nodes_in_rows1
335
336 1
    def _create_positions(self, nodes_in_rows):
337 1
        positions = []
338 1
        for row in nodes_in_rows:
339 1
            len_of_row = len(nodes_in_rows[row])
340 1
            if len_of_row > 1:
341 1
                if (len_of_row % 2) == 1:
342 1
                    len_of_row += 1
343
344 1
                for i in range((int(-(len_of_row / 2))) * 2,
345
                               (int(+(len_of_row / 2)) + 1) * 2, 2):
346 1
                    positions.append(i)
347
348 1
                if len_of_row == 2:
349 1
                    positions.remove(0)
350
351 1
                if len(nodes_in_rows[row]) < len(positions):
352 1
                    positions.pop()
353 1
                    if len(nodes_in_rows[row]) < len(positions):
354 1
                        positions.pop(0)
355
356 1
                count = 0
357
358 1
                for pos in positions:
359 1
                    nodes_in_rows[row][count]['x'] = pos
360 1
                    count += 1
361 1
                positions = []
362
            else:
363 1
                nodes_in_rows[row][0]['x'] = 0
364
365 1
        return positions
366
367 1
    def _convert_nodes_in_rows_to_nodes(self, nodes_in_rows):
368 1
        nodes = []
369 1
        for row in nodes_in_rows:
370 1
            for node in nodes_in_rows[row]:
371 1
                nodes.append(node)
372 1
        return nodes
373
374 1
    def _change_position(self, positions, nodes_in_rows):
375 1
        x = 0.6
376 1
        up_and_down = True
377 1
        down = False
378 1
        down_row = False
379 1
        save_x = 0
380 1
        continue_move = False
381
382 1
        for row in nodes_in_rows:
383 1
            for node in nodes_in_rows[row]:
384 1
                if (len(node['label']) > 6
385
                        and len(node['label']) < 40
386
                        or continue_move):
387 1
                    if up_and_down:
388 1
                        node['y'] = node['y'] + (0.6 * x)
389 1
                        up_and_down = False
390
                    else:
391 1
                        up_and_down = True
392 1
                    continue_move = True
393 1
                elif len(node['label']) > 30:
394 1
                    node['y'] = node['y'] + (0.6 * x)
395 1
                    x += 0.6
396 1
                    save_x = x
397 1
                    down = True
398
                else:
399 1
                    if down:
400 1
                        node['y'] = node['y'] + (0.6 * save_x)
401
402 1
                    if down_row:
403 1
                        node['y'] = node['y'] + (0.6 * save_x) - 0.7
404 1
            if down:
405 1
                down = False
406 1
                down_row = True
407 1
            continue_move = False
408 1
            x = 0.6
409
410 1
    def _sort(self, array):
411 1
        less = []
412 1
        equal = []
413 1
        greater = []
414
415 1
        if len(array) > 1:
416 1
            pivot = array[0]['x']
417 1
            for node in array:
418 1
                if node['x'] < pivot:
419
                    less.append(node)
420 1
                if node['x'] == pivot:
421 1
                    equal.append(node)
422 1
                if node['x'] > pivot:
423 1
                    greater.append(node)
424 1
            return self._sort(less) + equal + self._sort(greater)
425
        else:
426 1
            return array
427
428 1
    def _sort_nodes(self, nodes_in_rows):
429 1
        for row in nodes_in_rows:
430 1
            nodes_in_rows[row] = self._sort(nodes_in_rows[row])
431
432 1
    def _center_graph(self, out):
433 1
        max_y = self._count_max_y(out)
434 1
        nodes_in_rows = self._create_nodes_in_rows(max_y)
435 1
        self._push_nodes_to_nodes_in_row(out, nodes_in_rows)
436 1
        self._remove_empty_rows(nodes_in_rows, max_y)
437 1
        nodes_in_rows = self._move_rows(nodes_in_rows)
438 1
        self._sort_nodes(nodes_in_rows)
439 1
        positions = self._create_positions(nodes_in_rows)
440 1
        self._change_position(positions, nodes_in_rows)
441 1
        out['nodes'] = self._convert_nodes_in_rows_to_nodes(nodes_in_rows)
442 1
        return out
443
444 1
    def to_sigma_dict(self, x, y):
445 1
        return self._center_graph(
446
            self._remove_Duplication(
447
                self._help_to_sigma_dict(
448
                    x, y)))
449
450
451 1
def build_nodes_form_xml(xml_src, rule_id):
452 1
    parser = graph.xml_parser.xml_parser(xml_src)
453 1
    return parser.get_oval_graph(rule_id)
454
455
456 1
def restore_dict_to_tree(dict_of_tree):
457 1
    if dict_of_tree["child"] is None:
458 1
        return OvalNode(
459
            dict_of_tree["node_id"],
460
            dict_of_tree["type"],
461
            dict_of_tree["value"],
462
            dict_of_tree["negation"])
463 1
    return OvalNode(
464
        dict_of_tree["node_id"],
465
        dict_of_tree["type"],
466
        dict_of_tree["value"],
467
        dict_of_tree["negation"],
468
        [restore_dict_to_tree(i) for i in dict_of_tree["child"]])
469