wily\commands\report.py
-- -- --- --- --- --- --- --- ------- ------- ------- | """
-- -- --- --- --- --- --- --- ------- ------- ------- | Report command.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | The report command gives a table of metrics for a specified list of files.
-- -- --- --- --- --- --- --- ------- ------- ------- | Will compare the values between revisions and highlight changes in green/red.
-- -- --- --- --- --- --- --- ------- ------- ------- | """
-- -- --- --- --- --- --- --- ------- ------- ------- | import tabulate
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | from pathlib import Path
-- -- --- --- --- --- --- --- ------- ------- ------- | from shutil import copytree
-- -- --- --- --- --- --- --- ------- ------- ------- | from string import Template
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily import logger, format_date, format_revision, MAX_MESSAGE_WIDTH
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.helper.custom_enums import ReportFormat
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.operators import resolve_metric_as_tuple, MetricType
-- -- --- --- --- --- --- --- ------- ------- ------- | from wily.state import State
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | def report(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | config,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | path,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | metrics,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | n,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | output,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | include_message=False,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | format=ReportFormat.CONSOLE,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | console_format=None,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | ):
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | """
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | Show information about the cache and runtime.
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param config: The configuration
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type config: :class:`wily.config.WilyConfig`
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param path: The path to the file
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type path: ``str``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param metrics: Name of the metric to report on
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type metrics: ``str``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param n: Number of items to list
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type n: ``int``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param output: Output path
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type output: ``Path``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param include_message: Include revision messages
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type include_message: ``bool``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param format: Output format
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type format: ``ReportFormat``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :param console_format: Grid format style for tabulate
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | :type console_format: ``str``
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | """
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | logger.debug("Running report command")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | logger.info(f"-----------History for {metrics}------------")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | data = []
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | metric_metas = []
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | for metric in metrics:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | operator, metric = resolve_metric_as_tuple(metric)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | key = metric.name
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | operator = operator.name
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | # Set the delta colors depending on the metric type
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if metric.measure == MetricType.AimHigh:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | good_color = 32
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | bad_color = 31
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | elif metric.measure == MetricType.AimLow:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | good_color = 31
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | bad_color = 32
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | elif metric.measure == MetricType.Informational:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | good_color = 33
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | bad_color = 33
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | metric_meta = {
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | "key": key,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | "operator": operator,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | "good_color": good_color,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | "bad_color": bad_color,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | "title": metric.description,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | "type": metric.type,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | }
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | metric_metas.append(metric_meta)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | state = State(config)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | for archiver in state.archivers:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | history = state.index[archiver].revisions[:n][::-1]
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | last = {}
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | for rev in history:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | vals = []
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | for meta in metric_metas:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | try:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | logger.debug(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | f"Fetching metric {meta['key']} for {meta['operator']} in {path}"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | val = rev.get(config, archiver, meta["operator"], path, meta["key"])
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | last_val = last.get(meta["key"], None)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | # Measure the difference between this value and the last
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if meta["type"] in (int, float):
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if last_val:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | delta = val - last_val
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | delta = 0
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | last[meta["key"]] = val
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | # TODO : Measure ranking increases/decreases for str types?
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | delta = 0
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if delta == 0:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | delta_col = delta
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | elif delta < 0:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | delta_col = f"\u001b[{meta['good_color']}m{delta:n}\u001b[0m"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | delta_col = f"\u001b[{meta['bad_color']}m+{delta:n}\u001b[0m"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if meta["type"] in (int, float):
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | k = f"{val:n} ({delta_col})"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | k = f"{val}"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | except KeyError as e:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | k = f"Not found {e}"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | vals.append(k)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if include_message:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | data.append(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | (
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | format_revision(rev.revision.key),
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | rev.revision.message[:MAX_MESSAGE_WIDTH],
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | rev.revision.author_name,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | format_date(rev.revision.date),
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | *vals,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | data.append(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | (
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | format_revision(rev.revision.key),
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | rev.revision.author_name,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | format_date(rev.revision.date),
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | *vals,
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | descriptions = [meta["title"] for meta in metric_metas]
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if include_message:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | headers = ("Revision", "Message", "Author", "Date", *descriptions)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | headers = ("Revision", "Author", "Date", *descriptions)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if format == ReportFormat.HTML:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | if output.is_file and output.suffix == ".html":
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_path = output.parents[0]
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_output = output
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_path = output
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_output = output.joinpath("index.html")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_path.mkdir(exist_ok=True, parents=True)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | templates_dir = (Path(__file__).parents[1] / "templates").resolve()
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_template = Template((templates_dir / "report_template.html").read_text())
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | table_headers = "".join([f"<th>{header}</th>" for header in headers])
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | table_content = ""
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | for line in data[::-1]:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | table_content += "<tr>"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | for element in line:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | element = element.replace("[32m", "<span class='green-color'>")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | element = element.replace("[31m", "<span class='red-color'>")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | element = element.replace("[33m", "<span class='orange-color'>")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | element = element.replace("[0m", "</span>")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | table_content += f"<td>{element}</td>"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | table_content += "</tr>"
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | report_template = report_template.safe_substitute(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | headers=table_headers, content=table_content
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | with report_output.open("w") as output:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | output.write(report_template)
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | try:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | copytree(str(templates_dir / "css"), str(report_path / "css"))
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | except FileExistsError:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | pass
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 |
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | logger.info(f"wily report was saved to {report_path}")
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | else:
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | print(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | tabulate.tabulate(
-- 24 008 029 021 039 037 060 0312.57 1681.40 0005.38 | headers=headers, tabular_data=data[::-1], tablefmt=console_format
-- -- 008 029 021 039 037 060 0312.57 1681.40 0005.38 | )
-- -- --- --- --- --- --- --- ------- ------- ------- | )