Source code for robota_common_errors.report

"""Generates a HTML report of common errors for a git repo."""
import argparse
import datetime
from loguru import logger
import os
import pathlib
from typing import List

import dateutil.parser
import jinja2

import robota_core.config_readers as config_readers
from robota_core.data_server import DataServer

from robota_common_errors.common_errors import get_error_descriptions, CommonError
from robota_common_errors.common_errors import assess_common_errors
from robota_common_errors.output_templates.build_webpages import build_webpages


[docs]def count_errors(common_errors: List[CommonError]) -> int: """Count how many different common error types were detected.""" error_count = 0 for error in common_errors: if error.detail_titles: error_count += 1 return error_count
[docs]def identify_common_errors(robota_config: dict, start: datetime.datetime, end: datetime.datetime) -> List[CommonError]: """The main function which gets the common errors.""" # Set up data source data_server = DataServer(robota_config, start, end) # Obtain info on common errors from YAML files common_errors = get_error_descriptions(robota_config) # Identify common errors. return assess_common_errors(data_server, common_errors)
[docs]def output_html_report(common_errors: List[CommonError], data_source_info: dict, common_error_summary: dict, output_dir: str): # Construct the marking report based on determined performance update_template(common_errors, data_source_info, common_error_summary) # Build the webpages copying from templates to the live directory template_dir = pathlib.Path(__file__).parent / "output_templates" build_webpages(template_dir, pathlib.Path(output_dir))
[docs]def update_template(common_errors: List[CommonError], data_source_info: dict, common_error_summary: dict): """Produce the HTML report by writing the marking results to a HTML template. :param common_errors: A list of common errors and feedback on them. :param data_source_info: A dictionary of information about the data sources that were used. :param common_error_summary: A dictionary of stats about the common errors that is printed in the report. """ logger.info("Writing common error report.") template_loader = jinja2.FileSystemLoader( searchpath=f"robota_common_errors/output_templates/") template_env = jinja2.Environment(loader=template_loader, trim_blocks=True, lstrip_blocks=True, undefined=jinja2.StrictUndefined) jinja_template = template_env.get_template("common_error_report/report_template.html") rendered_page = jinja_template.render(common_errors=common_errors, common_error_summary=common_error_summary, data_source_info=data_source_info) # prepare the output directory output_directory = pathlib.Path("webpages/").resolve() os.makedirs(output_directory, exist_ok=True) # Write the generated report to a file output_name = "common-error-report.html" report_path = output_directory / pathlib.Path(output_name) with open(report_path, "w", encoding='utf-8') as result_html: result_html.write(rendered_page) logger.info(f"Common error report written to {report_path}.")
[docs]def summarise_data_sources(robota_config: dict, start: datetime.datetime, end: datetime.datetime) -> dict: data_source_summary = {"start": start, "end": end} desired_sources = ["issues", "remote_provider", "repository", "ci"] for source in desired_sources: source_info = config_readers.get_data_source_info(robota_config, source) if source_info: source_info.pop("token", None) source_info.pop("username", None) data_source_summary[source] = source_info return data_source_summary
[docs]def summarise_common_errors(common_errors: List[CommonError]): error_summary = {"num_tested_errors": len(common_errors), "num_detected_errors": 0} for error in common_errors: if error.count_errors() > 1: error_summary["num_detected_errors"] += 1 return error_summary
[docs]def run_html_error_report(start: str, end: str, config_path: str, output_dir: str, substitution_variables: dict): """Get common errors and output them in the form of a HTML report.""" start = dateutil.parser.parse(start) end = dateutil.parser.parse(end) # Get robota config from local config file robota_config = config_readers.get_robota_config(config_path, substitution_variables) common_errors = identify_common_errors(robota_config, start, end) data_source_info = summarise_data_sources(robota_config, start, end) common_error_summary = summarise_common_errors(common_errors) output_html_report(common_errors, data_source_info, common_error_summary, output_dir)
if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-c", "--config_path", help="The path to the robota-config.yaml file.", default="robota-config.yaml") parser.add_argument("-o", "--output_dir", help="The output directory to place the report.", default="webpages/") parser.add_argument("-s", "--start", help="The start date of the first commit to consider in YYYY-MM-DD format.", default="2020-01-01") parser.add_argument("-e", "--end", help="The end date of the last commit to consider in YYYY-MM-DD format.", default=datetime.date.today().isoformat()) parsed, unknown_args = parser.parse_known_args() KNOWN_ARGS = vars(parsed) if len(unknown_args) % 2 != 0: raise SyntaxError("Malformed command line arguments. " "Each flag must be followed by a single argument.") while unknown_args: flag = unknown_args.pop(0).lstrip("-") value = unknown_args.pop(0) KNOWN_ARGS[flag] = value run_html_error_report(KNOWN_ARGS.pop("start"), KNOWN_ARGS.pop("end"), KNOWN_ARGS.pop("config_path"), KNOWN_ARGS.pop("output_dir"), KNOWN_ARGS)