Passed
Pull Request — master (#67)
by
unknown
06:27
created

CommandLineAPI._prepare_arguments()   B

Complexity

Conditions 1

Size

Total Lines 56
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 53
dl 0
loc 56
ccs 10
cts 10
cp 1
rs 8.5381
c 0
b 0
f 0
cc 1
nop 1
crap 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1 1
import argparse
2 1
import logging
3 1
from dataclasses import dataclass
4 1
from sys import exit as sys_exit
5 1
from sys import stdin, stdout
6
7 1
from lxml.etree import XMLSyntaxError
8
9 1
from . import __version__
10 1
from .html_report import ReportGenerator
11 1
from .old_html_report_style import OldOSCAPReportGenerator
12 1
from .scap_results_parser import SCAPResultsParser
13
14 1
DESCRIPTION = ("Generates an HTML report from an ARF (or XCCDF Result) file with results of "
15
               "a SCAP-compatible utility scan. Unless the --output option is specified "
16
               "the report will be written to the standard output.")
17 1
LOG_LEVES_DESCRIPTION = (
18
    "LOG LEVELS:\n"
19
    "\tDEBUG - Detailed information, typically of interest only for diagnosing problems.\n"
20
    "\tINFO - A confirmation that things are working as expected.\n"
21
    "\tWARING -  An indication that something unexpected happened, or a signal of"
22
    " a possible problem in the future. The software is still working as expected.\n"
23
    "\tERROR - Due to a more serious problems, the software has not been able to perform "
24
    "its function to the full extent.\n"
25
    "\tCRITICAL - A serious error, indicating that the program itself may be unable "
26
    "to continue operating.\n"
27
)
28 1
DEBUG_FLAGS_DESCRIPTION = (
29
    "DEBUG FLAGS:\n"
30
    "\tNO-MINIFY - The HTML report will not be minified.\n"
31
    "\tBUTTON-SHOW-ALL-RULES - Adds a button to the HTML report for expanding all rules.\n"
32
    "\tONLINE-CSS - Use the latest online version of Patternfly CSS/JS in the HTML report"
33
)
34
35 1
MASSAGE_FORMAT = '%(levelname)s: %(message)s'
36 1
EXPECTED_ERRORS = (XMLSyntaxError, )
37 1
EXIT_FAILURE_CODE = 1
38 1
EXIT_SUCCESS_CODE = 0
39
40
41 1
def prepare_parser():
42 1
    parser = argparse.ArgumentParser(
43
        prog="oscap-report",
44
        formatter_class=argparse.RawTextHelpFormatter,
45
        description=DESCRIPTION,
46
        add_help=False,
47
    )
48 1
    parser.add_argument(
49
        "--version",
50
        action="version",
51
        version="%(prog)s " + __version__,
52
        help="Show program's version number and exit.")
53 1
    parser.add_argument(
54
        '-h',
55
        '--help',
56
        action='help',
57
        default=argparse.SUPPRESS,
58
        help='Show this help message and exit.')
59 1
    parser.add_argument(
60
        'FILE',
61
        type=argparse.FileType("r"),
62
        nargs='?',
63
        default=stdin,
64
        help="ARF (XCCDF) file or stdin if not provided.")
65 1
    parser.add_argument(
66
        "-o",
67
        "--output",
68
        action="store",
69
        type=argparse.FileType("wb+", 0),
70
        default=stdout,
71
        help="write the report to a file instead of the standard output.")
72 1
    parser.add_argument(
73
        "--log-file",
74
        action="store",
75
        default=None,
76
        help="write the log to a file instead of stderr.")
77 1
    parser.add_argument(
78
        "--log-level",
79
        action="store",
80
        default="WARNING",
81
        choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
82
        help=(
83
            "write debug information to the log up to the LOG_LEVEL."
84
            f"\n{LOG_LEVES_DESCRIPTION}")
85
    )
86 1
    parser.add_argument(
87
        "-f",
88
        "--format",
89
        action="store",
90
        default="HTML",
91
        choices=["HTML", "OLD-STYLE-HTML"],
92
        help="FORMAT: %(choices)s (default: %(default)s)."
93
    )
94 1
    parser.add_argument(
95
        "-d",
96
        "--debug",
97
        action="store",
98
        nargs='+',
99
        default=[""],
100
        choices=["NO-MINIFY", "BUTTON-SHOW-ALL-RULES", "ONLINE-CSS"],
101
        help=(
102
            "extra HTML generation options for debugging"
103
            f"{DEBUG_FLAGS_DESCRIPTION}")
104
    )
105 1
    return parser
106
107
108 1
class CommandLineAPI():  # pylint: disable=R0902
109 1
    def __init__(self):
110 1
        self.arguments = prepare_parser().parse_args()
111 1
        self.log_file = self.arguments.log_file
112 1
        self.log_level = self.arguments.log_level
113 1
        self.debug_flags = self.arguments.debug
114 1
        self._setup_logging()
115 1
        logging.debug("Args: %s", self.arguments)
116 1
        self.report_file = self.arguments.FILE
117 1
        self.output_file = self.arguments.output
118 1
        self.output_format = self.arguments.format.upper()
119 1
        self.debug_setting = DebugSetting()
120
121 1
    def _setup_logging(self):
122 1
        logging.basicConfig(
123
            format=MASSAGE_FORMAT,
124
            filename=self.log_file,
125
            filemode='w',
126
            level=self.log_level.upper()
127
        )
128
129 1
    def generate_report(self, report_parser):
130 1
        logging.info("Generate report")
131 1
        if self.output_format == "OLD-STYLE-HTML":
132
            report_generator = OldOSCAPReportGenerator(report_parser)
133
            return report_generator.generate_html_report()
134 1
        report_generator = ReportGenerator(report_parser)
135
136 1
        self.debug_setting.update_settings_with_debug_flags(self.debug_flags)
137
138 1
        return report_generator.generate_html_report(self.debug_setting)
139
140 1
    def load_file(self):
141 1
        logging.info("Loading file: %s", self.report_file)
142 1
        return self.report_file.read().encode()
143
144 1
    def store_file(self, data):
145 1
        logging.info("Store report")
146 1
        if self.output_file.name == "<stdout>":
147
            logging.info("Output is stdout, converting bytes output to str")
148
            data = data.read().decode("utf-8")
149 1
        self.output_file.writelines(data)
150
151 1
    def close_files(self):
152 1
        logging.info("Close files")
153 1
        self.report_file.close()
154 1
        self.output_file.close()
155
156
157 1
@dataclass
158 1
class DebugSetting():
159 1
    no_minify: bool = False
160 1
    options_require_debug_script: tuple = ("BUTTON-SHOW-ALL-RULES", )
161 1
    include_debug_script: bool = False
162 1
    button_show_all_rules: bool = False
163 1
    use_online_css: bool = False
164
165 1
    def update_settings_with_debug_flags(self, debug_flags):
166 1
        for flag in debug_flags:
167 1
            if flag in self.options_require_debug_script:
168
                self.include_debug_script = True
169 1
            if flag == "NO-MINIFY":
170
                self.no_minify = True
171 1
            if flag == "BUTTON-SHOW-ALL-RULES":
172
                self.button_show_all_rules = True
173 1
            if flag == "ONLINE-CSS":
174
                self.use_online_css = True
175
176
177 1
def main():
178
    exit_code = EXIT_SUCCESS_CODE
179
    api = CommandLineAPI()
180
    arf_report = api.load_file()
181
182
    logging.info("Parse file")
183
    try:
184
        parser = SCAPResultsParser(arf_report)
185
186
        report = api.generate_report(parser)
187
188
        api.store_file(report)
189
    except EXPECTED_ERRORS as error:
190
        logging.fatal("%s", error)
191
        exit_code = EXIT_FAILURE_CODE
192
    api.close_files()
193
    sys_exit(exit_code)
194
195
196 1
if __name__ == '__main__':
197
    main()
198