Passed
Push — master ( 0b8121...67de79 )
by Matěj
04:37 queued 11s
created

Client.open_results_in_web_browser()   A

Complexity

Conditions 3

Size

Total Lines 9
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.7085

Importance

Changes 0
Metric Value
cc 3
eloc 9
nop 2
dl 0
loc 9
ccs 4
cts 7
cp 0.5714
crap 3.7085
rs 9.95
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 subprocess
7 1
from datetime import datetime
8 1
import sys
9
10 1
from .xml_parser import XmlParser
11 1
from .exceptions import NotChecked
12 1
from .__init__ import __version__
13
14
15 1
class Client():
16 1
    def __init__(self, args):
17 1
        self.parser = None
18 1
        self.MESSAGES = self._get_message()
19 1
        self.arg = self.parse_arguments(args)
20 1
        self.hide_passing_tests = self.arg.hide_passing_tests
21 1
        self.source_filename = self.arg.source_filename
22 1
        self.rule_name = self.arg.rule_id
23 1
        self.out = self.arg.output
24 1
        self.verbose = self.arg.verbose
25
26 1
        self.parts = self.get_src('parts')
27 1
        self.START_OF_FILE_NAME = 'graph-of-'
28 1
        self.date = str(datetime.now().strftime("-%d_%m_%Y-%H_%M_%S"))
29 1
        self.isatty = sys.stdout.isatty()
30
31 1
        self.all_rules = self.arg.all
32 1
        self.all_in_one = None
33 1
        self.display_html = None
34
35 1
        self.show_failed_rules = False
36 1
        self.show_not_selected_rules = False
37
38 1
        self.xml_parser = None
39
40 1
        self.html_builder = None
41
42 1
        self._set_attributes()
43
44 1
        self.web_browsers = []
45
46 1
    def _set_attributes(self):
47 1
        self.xml_parser = self.xml_parser = XmlParser(self.source_filename)
48
49 1
    def _get_message(self):
50 1
        MESSAGES = {
51
            'description': '',
52
            'source_filename': '',
53
        }
54 1
        return MESSAGES
55
56 1
    def print_red_text(self, text):
57
        CRED = '\033[91m'
58
        CEND = '\033[0m'
59
        print(CRED + str(text) + CEND)
60
61 1
    def run_gui_and_return_answers(self):
62 1
        if self.isatty:
63 1
            if self.all_rules:
64
                return self._get_rules()
65
            else:
66 1
                try:
67 1
                    import inquirer
68 1
                    return inquirer.prompt(self.get_questions())
69 1
                except ImportError:
70 1
                    print(self.get_selection_rules())
71 1
                    return None
72
        else:
73 1
            return self._get_rules()
74
75 1
    def _get_rules(self):
76 1
        return {
77
            'rules': self._get_only_fail_rule(
78
                self.search_rules_id())} if self.show_failed_rules else {
79
            'rules': self.search_rules_id()}
80
81 1
    def get_list_of_matched_rules(self):
82 1
        return self._get_only_fail_rule(
83
            self.search_rules_id()) if self.show_failed_rules else self.search_rules_id()
84
85 1
    def get_list_of_lines(self):
86 1
        lines = ['== The Rule ID regular expressions ==']
87 1
        for rule in self.get_list_of_matched_rules():
88 1
            lines.append("^" + rule + "$")
89 1
        if self.show_not_selected_rules:
90 1
            for line in self.get_lines_of_wanted_not_selected_rules():
91 1
                lines.append(line)
92 1
        lines.append(
93
            "Interactive rule selection is not available,"
94
            " because inquirer is not installed."
95
            " Copy id of the rule you want to visualize and"
96
            " paste it into a command with regular"
97
            " expression characters(^$).\n"
98
            "Alternatively, use the --all or --all-in-one arguments.")
99 1
        return lines
100
101 1
    def get_selection_rules(self):
102 1
        return "\n".join(self.get_list_of_lines())
103
104 1
    def get_lines_of_wanted_not_selected_rules(self):
105 1
        out = []
106 1
        out.append('== The not selected rule IDs ==')
107 1
        for rule in self._get_wanted_rules_from_array_of_IDs(
108
                self.xml_parser.notselected_rules):
109 1
            out.append(rule + '(Not selected)')
110 1
        return out
111
112 1
    def get_choices(self):
113 1
        if self.show_not_selected_rules:
114 1
            print("\n".join(self.get_lines_of_wanted_not_selected_rules()))
115 1
        return self.get_list_of_matched_rules()
116
117 1
    def get_questions(self):
118 1
        choices = self.get_choices()
119 1
        from inquirer.questions import Checkbox as checkbox
120 1
        questions = [
121
            checkbox(
122
                'rules',
123
                message=(
124
                    "= The Rules IDs = (move - UP and DOWN arrows,"
125
                    " select - SPACE or LEFT and RIGHT arrows, submit - ENTER)"),
126
                choices=choices,
127
            ),
128
        ]
129 1
        return questions
130
131 1
    def _get_only_fail_rule(self, rules):
132 1
        return list(
133
            filter(
134
                lambda rule: self.xml_parser.used_rules[rule]['result'] == 'fail',
135
                rules))
136
137 1
    def _get_wanted_rules_from_array_of_IDs(self, rules):
138 1
        return [
139
            x for x in rules if re.search(
140
                self.rule_name, x)]
141
142 1
    def search_rules_id(self):
143 1
        return self._check_rules_id(
144
            self._get_wanted_rules_from_array_of_IDs(
145
                self.xml_parser.used_rules.keys()),
146
            self._get_wanted_rules_from_array_of_IDs(
147
                self.xml_parser.notselected_rules))
148
149 1
    def _check_rules_id(self, rules, notselected_rules):
150 1
        if len(notselected_rules) and not rules:
151 1
            raise ValueError(
152
                ('Rule(s) "{}" was not selected, '
153
                 "so there are no results. The rule is"
154
                 ' "notselected" because it'
155
                 " wasn't a part of the executed profile"
156
                 " and therefore it wasn't evaluated "
157
                 "during the scan.")
158
                .format(notselected_rules))
159 1
        elif not notselected_rules and not rules:
160 1
            raise ValueError('404 rule "{}" not found!'.format(self.rule_name))
161
        else:
162 1
            return rules
163
164 1
    def get_save_src(self, rule):
165 1
        if self.out is not None:
166 1
            os.makedirs(self.out, exist_ok=True)
167 1
            return os.path.join(
168
                self.out,
169
                self.START_OF_FILE_NAME + rule + '.html')
170 1
        return os.path.join(
171
            tempfile.gettempdir(),
172
            self.START_OF_FILE_NAME + rule + '.html')
173
174 1
    def get_src(self, src):
175 1
        _dir = os.path.dirname(os.path.realpath(__file__))
176 1
        return str(os.path.join(_dir, src))
177
178 1
    def _build_and_save_html(self, dict_oval_trees, src, rules, out_src):
179 1
        self.html_builder.save_html(dict_oval_trees, src, rules)
180 1
        out_src.append(src)
181
182 1
    def open_results_in_web_browser(self, paths_to_results):
183 1
        if self.display_html:
184 1
            try:
185 1
                self.web_browsers.append(subprocess.Popen(
186
                    ["firefox", *paths_to_results]))
187
            except BaseException:
188
                default_web_browser_name = webbrowser.get().name
189
                self.web_browsers.append(subprocess.Popen(
190
                    [default_web_browser_name, *paths_to_results]))
191
192 1
    def kill_web_browsers(self):
193 1
        for web_browser in self.web_browsers:
194 1
            web_browser.kill()
195
196 1
    def _prepare_data(self, rules, dict_oval_trees, out_src):
197 1
        for rule in rules['rules']:
198 1
            try:
199 1
                self._put_to_dict_oval_trees(dict_oval_trees, rule)
200 1
                if not self.all_in_one:
201 1
                    self._build_and_save_html(
202
                        dict_oval_trees, self._get_src_for_one_graph(
203
                            rule), dict(
204
                            rules=[rule]), out_src)
205 1
                    dict_oval_trees = {}
206 1
            except NotChecked as error:
207
                self.print_red_text(error)
208 1
        if self.all_in_one:
209
            self._build_and_save_html(
210
                dict_oval_trees, self.get_save_src(
211
                    'rules' + self.date), rules, out_src)
212
213 1
    def prepare_data(self, rules):
214 1
        out_src = []
215 1
        oval_tree_dict = dict()
216 1
        self._prepare_data(rules, oval_tree_dict, out_src)
217 1
        self.open_results_in_web_browser(out_src)
218 1
        return out_src
219
220 1
    def parse_arguments(self, args):
221 1
        self.prepare_parser()
222 1
        return self.parser.parse_args(args)
223
224 1
    def prepare_args_when_output_is_html(self):
225 1
        self.parser.add_argument(
226
            '-i',
227
            '--all-in-one',
228
            action="store_true",
229
            default=False,
230
            help="Processes all rules into one file.")
231 1
        self.parser.add_argument(
232
            '-d',
233
            '--display',
234
            action="store_true",
235
            default=False,
236
            help="Enables opening a web browser with a graph, when is used --output.")
237
238 1
    def prepare_args_when_user_can_list_in_rules(self):
239 1
        self.parser.add_argument(
240
            '--show-failed-rules',
241
            action="store_true",
242
            default=False,
243
            help="Show only FAILED rules")
244 1
        self.parser.add_argument(
245
            '--show-not-selected-rules',
246
            action="store_true",
247
            default=False,
248
            help="Show notselected rules. These rules will not be visualized.")
249
250 1
    def prepare_parser(self):
251 1
        self.parser = argparse.ArgumentParser(
252
            prog='oval-graph',
253
            description=self.MESSAGES.get('description'))
254 1
        self.parser.add_argument(
255
            '--version',
256
            action='version',
257
            version='%(prog)s ' + __version__)
258 1
        self.parser.add_argument(
259
            '-a',
260
            '--all',
261
            action="store_true",
262
            default=False,
263
            help="Process all matched rules.")
264 1
        self.parser.add_argument(
265
            '--hide-passing-tests',
266
            action="store_true",
267
            default=False,
268
            help=(
269
                "Do not display passing tests for better orientation in"
270
                " graphs that contain a large amount of nodes."))
271 1
        self.parser.add_argument(
272
            '-v',
273
            '--verbose',
274
            action="store_true",
275
            default=False,
276
            help="Displays details about the results of the running command.")
277 1
        self.parser.add_argument(
278
            '-o',
279
            '--output',
280
            action="store",
281
            default=None,
282
            help='The file where to save output.')
283 1
        self.parser.add_argument(
284
            "source_filename",
285
            help=self.MESSAGES.get('source_filename'))
286 1
        self.parser.add_argument(
287
            "rule_id", help=(
288
                "Rule ID to be visualized. A part from the full rule ID"
289
                " a part of the ID or a regular expression can be used."
290
                " If brackets are used in the regular expression "
291
                "the regular expression must be quoted."))
292