def generate_dashboard(): """ Generate the static html dashboard :return: """ _now = datetime.datetime.now() _file = f'{system.config["export_path"]}index.html' output.println(f'{output.Subject.INFO} Generating dashboard {output.CliFormat.BLACK}{_file}{output.CliFormat.ENDC}') html = open(_file, "w") _rendered_html = render_template('index.html.j2', logo=render_logo(), date=_now.strftime("%Y-%m-%dT%H:%M:%SZ"), assets_css=render_assets(ASSETS_CSS, TAG_CSS), assets_js=render_assets(ASSETS_JS, TAG_JS), items=render_items(), title=system.config['title'], description=system.config['description'], version=info.__version__, homepage=info.__homepage__, lighthouse_version=system.config['lighthouse']['version'] ) html.write(_rendered_html) html.close()
def run_command(command, return_output=False, allow_fail=False): """ Run local command :param command: String Shell command :param return_output: Boolean Return shell command output :param allow_fail: Boolean :return: """ output.println( f'{output.Subject.DEBUG}{output.Subject.CMD} {output.CliFormat.BLACK}{command}{output.CliFormat.ENDC}', verbose_only=True) res = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) out, err = res.communicate() if res.wait() != 0 and err.decode() != '': _error_message = f'{output.Subject.ERROR} {err.decode()}' if allow_fail: output.println(_error_message) return False else: sys.exit(_error_message) if return_output: return out.decode() return True
def print_footer(): """ Printing console footer :return: """ if 'errors' in system.config: output.println(f'{output.Subject.WARNING} Errors occurred during execution, see console output for more information') else: output.println(f'{output.Subject.OK} Successfully fetched lighthouse data')
def clear_data(): """ Clear the performance data and the lighthouse garden dashboard :return: """ output.println(f'{output.Subject.INFO} Clear data') _file_path = f'{config["export_path"]}index.html' _dir_path = utility.get_data_dir() if os.path.isfile(_file_path): os.remove(_file_path) shutil.rmtree(_dir_path)
def check_lighthouse_version(): """ Check sshpass version :return: """ _version = run_command('lighthouse --version', return_output=True) if _version: output.println( f'{output.Subject.INFO} lighthouse version {_version}', verbose_only=True ) config['lighthouse']['version'] = _version
def add_value_to_history(target, result): """ Adding a new result value to the history data of a specific target :param target: Dict :param result: Dict :return: """ _data = get_data(target, '.history') while len(_data) > system.config['keep_history']: output.println(f'{output.Subject.INFO} Cleaning history value', verbose_only=True) utility.remove_file(f'{utility.get_export_dir()}{_data[0]["report"]}') del _data[0] _data.append(result) set_data(target, _data, '.history')
def lighthouse(target): """ Fetch the lighthouse data of a specific target :param target: Dict :return: """ output.println(f'{output.Subject.INFO} Fetching performance data for {info.get_target_name(target)}', verbose_only=True) _output_name = generate_output_name(target) _result = system.run_command( f'lighthouse {target["url"]} {build_options(_output_name)}', allow_fail=True ) if not _result: output.println(f'{output.Subject.ERROR} > {info.get_target_name(target)} ...') system.config['errors'] = True return _output_name
def extend_html_report_with_info(result, file_name): """ Extend the existing lighthouse html report with an additional info box :param result: Dict :param file_name: String :return: """ if os.path.isfile(file_name): with open(file_name, "r") as read_file: _html = read_file.readlines() _info = render.render_template('partials/info.html.j2', title=result['title'], url=result['url'], date=result['date'], logo=render.render_logo() ) _html[-2] = f'{_info}\n</body>\n</html>' del(_html[-1]) with open(file_name, 'w') as file: output.println(f'{output.Subject.INFO} Extend html report with info box', verbose_only=True) file.writelines(_html)
def check_config(additional_config={}): """ Checking configuration information by file or dictionary :param additional_config: Dictionary :return: """ global config if config['config_file_path'] is None and additional_config == {}: sys.exit(f'{output.Subject.ERROR} Configuration is missing') if additional_config: config.update(additional_config) _config_file_path = config['config_file_path'] if not _config_file_path is None: if os.path.isfile(_config_file_path): with open(_config_file_path, 'r') as read_file: config.update(json.load(read_file)) output.println( f'{output.Subject.INFO} Loading host configuration {output.CliFormat.BLACK}{_config_file_path}{output.CliFormat.ENDC}', verbose_only=True) else: sys.exit(f'{output.Subject.ERROR} Local configuration not found: {config["config_file_path"]}')
def generate_badges(target): """ Generate the badges for the various metrics of a specific target :param target: Dict :return: """ output.println(f'{output.Subject.INFO} Generating badges', verbose_only=True) badges = { 'performance': { 'label': 'performance', 'value': database.get_data(target)['performance'], }, 'accessibility': { 'label': 'accessibility', 'value': database.get_data(target)['accessibility'], }, 'best-practices': { 'label': 'best-practices', 'value': database.get_data(target)['best-practices'], }, 'seo': { 'label': 'seo', 'value': database.get_data(target)['seo'], }, 'average': { 'label': 'performance', 'value': database.get_data(target)['average']['value'] } } for key, value in badges.items(): generate_badge( target=target, layout=value, attribute=key )
def fetch_data(): """ Fetch the lighthouse data of all given targets :return: """ output.println(f'{output.Subject.INFO} Checking export path', verbose_only=True) system.check_path(f'{system.config["export_path"]}/{system.config["data_dir"]}') output.println(f'{output.Subject.INFO} Starting to process targets') for target in system.config['targets']: _output_name = lighthouse(target) _result = interpreter.get_result_by_report_file(target, _output_name) if _result: database.set_data(target, _result) output.println(f'{output.Subject.OK} > {info.get_target_name(target)} ... {_result["performance"]}') utility.extend_html_report_with_info(_result, f'{utility.get_data_dir()}{_output_name}.report.html') generate_badges(target) else: utility.remove_file(f'{utility.get_data_dir()}{_output_name}.report.html') utility.remove_file(f'{utility.get_data_dir()}{_output_name}.report.json')
def get_result_by_report_file(target, file_name): """ Generate the result by a given lighthouse report file :param target: Dict :param file_name: String :return: """ output.println(f'{output.Subject.INFO} Processing result of report', verbose_only=True) _report_path = f'{utility.get_data_dir()}{file_name}.report.json' _report = None if os.path.isfile(_report_path): with open(_report_path, 'r') as read_file: _report = json.load(read_file) else: sys.exit(f'{output.Subject.ERROR} Report file not found: {_report_path}') if not isinstance(_report, dict): sys.exit(f'{output.Subject.ERROR} Report not readable') if _report['categories']['performance']['score']: _performance = int(round(_report['categories']['performance']['score'] * 100)) else: output.println(f'{output.Subject.ERROR} Missing performance score', verbose_only=True) return None _result = defaultdict(lambda: defaultdict(dict)) _result = { 'title': target['title'], 'url': target['url'], 'performance': _performance, 'report': f'{utility.get_data_dir(absolute_path=False)}{file_name}.report.html', 'link': f'#{target["identifier"]}', 'date': '{:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now()) } # additional metrics if 'accessibility' in _report['categories'] and _report['categories']['accessibility']['score']: _result['accessibility'] = int(round(_report['categories']['accessibility']['score'] * 100)) if 'best-practices' in _report['categories'] and _report['categories']['best-practices']['score']: _result['best-practices'] = int(round(_report['categories']['best-practices']['score'] * 100)) if 'seo' in _report['categories'] and _report['categories']['seo']['score']: _result['seo'] = int(round(_report['categories']['seo']['score'] * 100)) database.add_value_to_history(target, _result) # audits _result['audits'] = {} if _report['audits']['first-contentful-paint']: _result['audits']['first-contentful-paint'] = _report['audits']['first-contentful-paint']['displayValue'] if _report['audits']['largest-contentful-paint']: _result['audits']['largest-contentful-paint'] = _report['audits']['largest-contentful-paint'][ 'displayValue'] if _report['audits']['speed-index']: _result['audits']['speed-index'] = _report['audits']['speed-index']['displayValue'] if _report['audits']['total-blocking-time']: _result['audits']['total-blocking-time'] = _report['audits']['total-blocking-time']['displayValue'] if _report['audits']['interactive']: _result['audits']['interactive'] = _report['audits']['interactive']['displayValue'] if _report['audits']['cumulative-layout-shift']: _result['audits']['cumulative-layout-shift'] = _report['audits']['cumulative-layout-shift']['displayValue'] # average _result['average'] = { 'value': database.get_average_by_attribute(target, 'performance'), 'min': database.get_average_peak(target, 'performance', True), 'max': database.get_average_peak(target, 'performance', False), 'trend': get_trend(target, 'performance') } return _result