Passed
Push — master ( e33474...8e9b8f )
by Matěj
05:13 queued 10s
created

oval_graph.client.Client._prepare_data()   A

Complexity

Conditions 5

Size

Total Lines 16
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5.246

Importance

Changes 0
Metric Value
cc 5
eloc 16
nop 5
dl 0
loc 16
ccs 11
cts 14
cp 0.7856
crap 5.246
rs 9.1333
c 0
b 0
f 0
1 1
import re
2 1
import argparse
3 1
import tempfile
4 1
import os
5 1
import webbrowser
6 1
import json
7 1
import shutil
8 1
from datetime import datetime
9 1
import sys
10 1
from lxml import etree
11 1
from lxml.builder import ElementMaker, E
12 1
import lxml.html
13
14 1
from .xml_parser import XmlParser
15 1
from .exceptions import NotChecked
16
17
18 1
class Client():
19 1
    def __init__(self, args):
20 1
        self.parser = None
21 1
        self.MESSAGES = self._get_message()
22 1
        self.arg = self.parse_arguments(args)
23 1
        self.hide_passing_tests = self.arg.hide_passing_tests
24 1
        self.source_filename = self.arg.source_filename
25 1
        self.rule_name = self.arg.rule_id
26 1
        self.out = self.arg.output
27 1
        self.all_rules = self.arg.all
28 1
        self.all_in_one = None
29 1
        self.off_webbrowser = None
30 1
        self.isatty = sys.stdout.isatty()
31 1
        self.show_failed_rules = False
32 1
        self.show_not_selected_rules = False
33 1
        self.xml_parser = XmlParser(
34
            self.source_filename)
35 1
        self.parts = self.get_src('parts')
36 1
        self.START_OF_FILE_NAME = 'graph-of-'
37
38 1
    def _get_message(self):
39 1
        MESSAGES = {
40
            'description': '',
41
            '--output': '',
42
            'source_filename': '',
43
        }
44 1
        return MESSAGES
45
46 1
    def print_red_text(self, text):
47
        CRED = '\033[91m'
48
        CEND = '\033[0m'
49
        print(CRED + str(text) + CEND)
50
51 1
    def run_gui_and_return_answers(self):
52 1
        if self.isatty:
53 1
            if self.all_rules:
54
                return self._get_rules()
55
            else:
56 1
                try:
57 1
                    import inquirer
58 1
                    return inquirer.prompt(self.get_questions())
59 1
                except ImportError:
60 1
                    print(self.get_selection_rules())
61 1
                    return None
62
        else:
63 1
            return self._get_rules()
64
65 1
    def _get_rules(self):
66 1
        if self.show_failed_rules:
67 1
            return {'rules': self._get_only_fail_rule(self.search_rules_id())}
68
        else:
69 1
            return {'rules': self.search_rules_id()}
70
71 1
    def get_list_of_matched_rules(self):
72 1
        rules = self.search_rules_id()
73 1
        if self.show_failed_rules:
74 1
            rules = self._get_only_fail_rule(rules)
75 1
        return rules
76
77 1
    def get_list_of_lines(self):
78 1
        lines = ['== The Rule IDs ==']
79 1
        for rule in self.get_list_of_matched_rules():
80 1
            lines.append("'" + rule + r'\b' + "'")
81 1
        if self.show_not_selected_rules:
82 1
            for line in self.get_lines_of_wanted_not_selected_rules():
83 1
                lines.append(line)
84 1
        lines.append(
85
            "You haven't got installed inquirer lib. "
86
            "Please copy id rule with you want use and put it in command")
87 1
        return lines
88
89 1
    def get_selection_rules(self):
90 1
        return "\n".join(self.get_list_of_lines())
91
92 1
    def get_lines_of_wanted_not_selected_rules(self):
93 1
        out = []
94 1
        out.append('== The not selected rule IDs ==')
95 1
        for rule in self._get_wanted_rules_from_array_of_IDs(
96
                self.xml_parser.notselected_rules):
97 1
            out.append(rule + '(Not selected)')
98 1
        return out
99
100 1
    def get_choices(self):
101 1
        rules = self.search_rules_id()
102 1
        if self.show_failed_rules:
103 1
            rules = self._get_only_fail_rule(rules)
104 1
        choices = rules
105 1
        if self.show_not_selected_rules:
106 1
            print("\n".join(self.get_lines_of_wanted_not_selected_rules()))
107 1
        return choices
108
109 1
    def get_questions(self):
110 1
        choices = self.get_choices()
111 1
        from inquirer.questions import Checkbox as checkbox
112 1
        questions = [
113
            checkbox(
114
                'rules',
115
                message=(
116
                    "= The Rules IDs = (move - UP and DOWN arrows,"
117
                    " select - SPACE or LEFT and RIGHT arrows, submit - ENTER)"),
118
                choices=choices,
119
            ),
120
        ]
121 1
        return questions
122
123 1
    def _get_only_fail_rule(self, rules):
124 1
        return list(
125
            filter(
126
                lambda rule: self.xml_parser.used_rules[rule]['result'] == 'fail',
127
                rules))
128
129 1
    def _get_wanted_rules_from_array_of_IDs(self, rules):
130 1
        return [
131
            x for x in rules if re.search(
132
                self.rule_name, x)]
133
134 1
    def search_rules_id(self):
135 1
        rules = self._get_wanted_rules_from_array_of_IDs(
136
            self.xml_parser.used_rules.keys())
137 1
        notselected_rules = self._get_wanted_rules_from_array_of_IDs(
138
            self.xml_parser.notselected_rules)
139 1
        return self._check_rules_id(rules, notselected_rules)
140
141 1
    def _check_rules_id(self, rules, notselected_rules):
142 1
        if len(notselected_rules) and not rules:
143 1
            raise ValueError(
144
                ('Rule(s) "{}" was not selected, '
145
                 "so there are no results. The rule is"
146
                 ' "notselected" because it'
147
                 " wasn't a part of the executed profile"
148
                 " and therefore it wasn't evaluated "
149
                 "during the scan.")
150
                .format(notselected_rules))
151 1
        elif not notselected_rules and not rules:
152 1
            raise ValueError('404 rule "{}" not found!'.format(self.rule_name))
153
        else:
154 1
            return rules
155
156 1
    def save_html_and_open_html(
157
            self, dict_oval_trees, src, rules, out):
158 1
        self.save_html_report(dict_oval_trees, src)
159 1
        self.print_output_message_and_open_web_browser(
160
            src, self._format_rules_output(rules), out)
161
162 1
    def _format_rules_output(self, rules):
163 1
        out = ''
164 1
        for rule in rules['rules']:
165 1
            out += rule + '\n'
166 1
        return out
167
168 1
    def print_output_message_and_open_web_browser(self, src, rule, out):
169 1
        print('Rule(s) "{}" done!'.format(rule))
170 1
        out.append(src)
171 1
        self.open_web_browser(src)
172
173 1
    def open_web_browser(self, src):
174 1
        if not self.off_webbrowser:
175
            try:
176
                webbrowser.get('firefox').open_new_tab(src)
177
            except BaseException:
178
                webbrowser.open_new_tab(src)
179
180 1
    def get_src(self, src):
181 1
        _dir = os.path.dirname(os.path.realpath(__file__))
182 1
        FIXTURE_DIR = os.path.join(_dir, src)
183 1
        return str(FIXTURE_DIR)
184
185 1
    def _prepare_data(self, rules, dict_oval_trees, out, date):
186 1
        for rule in rules['rules']:
187 1
            try:
188 1
                self._put_to_dict_oval_trees(dict_oval_trees, rule)
189 1
                if not self.all_in_one:
190 1
                    src = self._get_src_for_one_graph(rule, date)
191 1
                    self.save_html_and_open_html(
192
                        dict_oval_trees, src, dict(rules=[rule]), out)
193 1
                    dict_oval_trees = {}
194 1
            except NotChecked as error:
195
                self.print_red_text(error)
196 1
        if self.all_in_one:
197
            src = self.get_save_src('rules' + date)
198
            self.save_html_and_open_html(
199
                dict_oval_trees, src, rules, out)
200 1
        return out
201
202 1
    def prepare_data(self, rules):
203 1
        out = []
204 1
        oval_tree_dict = dict()
205 1
        date = str(datetime.now().strftime("-%d_%m_%Y-%H_%M_%S"))
206 1
        out = self._prepare_data(rules, oval_tree_dict, out, date)
207 1
        return out
208
209 1
    def get_save_src(self, rule):
210 1
        if self.out is not None:
211 1
            os.makedirs(self.out, exist_ok=True)
212 1
            return os.path.join(
213
                self.out,
214
                self.START_OF_FILE_NAME + rule + '.html')
215 1
        return os.path.join(
216
            os.getcwd(),
217
            self.START_OF_FILE_NAME + rule + '.html')
218
219 1
    def _get_part(self, part):
220 1
        out = ''
221 1
        with open(os.path.join(self.parts, part), "r") as data_file:
222 1
            for line in data_file.readlines():
223 1
                out += line
224 1
            return out
225
226 1
    def _get_html_head(self):
227 1
        return E.head(
228
            E.title("OVAL TREE"),
229
            E.style(self._get_part('css.txt')),
230
            E.style(self._get_part('bootstrapStyle.txt')),
231
            E.style(self._get_part('jsTreeStyle.txt')),
232
            E.script(self._get_part('jQueryScript.txt')),
233
            E.script(self._get_part('bootstrapScript.txt')),
234
            E.script(self._get_part('jsTreeScript.txt')),
235
        )
236
237 1
    def _get_html_body(self, dict_of_rules):
238 1
        return E.body(
239
            E.script(self._get_script_graph_data(dict_of_rules)),
240
            self._get_titles_and_places_for_graph(dict_of_rules),
241
            E.div({'id': 'data'}),
242
            E.div({'id': 'modal', 'class': 'modal'},
243
                  E.div({'class': 'modal-content'},
244
                        E.span({'id': 'close', 'class': 'close'}, '×'),
245
                        E.div({'id': 'content'}),
246
                        )
247
                  ),
248
            E.script(self._get_part('script.js')),
249
        )
250
251 1
    def _get_html(self, dict_of_rules):
252 1
        M = ElementMaker(namespace=None,
253
                         nsmap={None: "http://www.w3.org/1999/xhtml"})
254 1
        html = M.html(
255
            self._get_html_head(),
256
            self._get_html_body(dict_of_rules))
257 1
        result = etree.tostring(
258
            html,
259
            xml_declaration=True,
260
            doctype=('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"'
261
                     ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'),
262
            encoding='utf-8',
263
            standalone=False,
264
            with_tail=False,
265
            method='html',
266
            pretty_print=True)
267 1
        return result.decode('UTF-8')
268
269 1
    def _get_script_graph_data(self, dict_of_rules):
270 1
        return (
271
            "var data_of_tree = " + str(
272
                json.dumps(
273
                    {
274
                        re.sub(
275
                            r'[\_\-\.]',
276
                            '',
277
                            k): v for k,
278
                        v in dict_of_rules.items()},
279
                    sort_keys=False,
280
                    indent=4)) + ";")
281
282 1
    def _get_titles_and_places_for_graph(self, dict_of_rules):
283 1
        out = ''
284 1
        for rule in dict_of_rules.keys():
285 1
            out += ('<h1>' +
286
                    rule +
287
                    '</h1><div id="' +
288
                    re.sub(r'[\_\-\.]', '', rule) +
289
                    '"></div>')
290 1
        return lxml.html.fromstring(out)
291
292 1
    def save_html_report(self, dict_of_rules, src):
293 1
        with open(src, "w+") as data_file:
294 1
            data_file.writelines(self._get_html(dict_of_rules))
295
296 1
    def parse_arguments(self, args):
297 1
        self.prepare_parser()
298 1
        args = self.parser.parse_args(args)
299 1
        return args
300
301 1
    def prepare_parser(self):
302 1
        self.parser = argparse.ArgumentParser(
303
            description=self.MESSAGES.get('description'))
304 1
        self.parser.add_argument(
305
            '--all',
306
            action="store_true",
307
            default=False,
308
            help="Process all matched rules.")
309 1
        self.parser.add_argument(
310
            '--hide-passing-tests',
311
            action="store_true",
312
            default=False,
313
            help=(
314
                "Do not display passing tests for better orientation in"
315
                " graphs that contain a large amount of nodes.(Not implemented)"))
316 1
        self.parser.add_argument(
317
            '-o',
318
            '--output',
319
            action="store",
320
            default=None,
321
            help=self.MESSAGES.get('--output'))
322 1
        self.parser.add_argument(
323
            "source_filename",
324
            help=self.MESSAGES.get('source_filename'))
325 1
        self.parser.add_argument(
326
            "rule_id", help=(
327
                "Rule ID to be visualized. A part from the full rule ID"
328
                " a part of the ID or a regular expression can be used."
329
                " If brackets are used in the regular expression "
330
                "the regular expression must be quoted."))
331