def index(config, include_message=False): """ Show information about the cache and runtime. :param config: The wily configuration :type config: :namedtuple:`wily.config.WilyConfig` :param include_message: Include revision messages :type include_message: ``bool`` """ state = State(config=config) logger.debug("Running show command") logger.info("--------Configuration---------") logger.info(f"Path: {config.path}") logger.info(f"Archiver: {config.archiver}") logger.info(f"Operators: {config.operators}") logger.info("") logger.info("-----------History------------") data = [] for archiver in state.archivers: for rev in state.index[archiver].revisions: if include_message: data.append( ( format_revision(rev.revision.key), rev.revision.author_name, rev.revision.message[:MAX_MESSAGE_WIDTH], format_date(rev.revision.date), ) ) else: data.append( ( format_revision(rev.revision.key), rev.revision.author_name, format_date(rev.revision.date), ) ) if include_message: headers = ("Revision", "Author", "Message", "Date") else: headers = ("Revision", "Author", "Date") print( tabulate.tabulate( headers=headers, tabular_data=data, tablefmt=DEFAULT_GRID_STYLE ) )
def report( config, path, metrics, n, output, include_message=False, format=ReportFormat.CONSOLE, console_format=None, ): """ Show information about the cache and runtime. :param config: The configuration :type config: :class:`wily.config.WilyConfig` :param path: The path to the file :type path: ``str`` :param metrics: Name of the metric to report on :type metrics: ``str`` :param n: Number of items to list :type n: ``int`` :param output: Output path :type output: ``Path`` :param include_message: Include revision messages :type include_message: ``bool`` :param format: Output format :type format: ``ReportFormat`` :param console_format: Grid format style for tabulate :type console_format: ``str`` """ logger.debug("Running report command") logger.info(f"-----------History for {metrics}------------") data = [] metric_metas = [] for metric in metrics: operator, metric = resolve_metric_as_tuple(metric) key = metric.name operator = operator.name # Set the delta colors depending on the metric type if metric.measure == MetricType.AimHigh: good_color = 32 bad_color = 31 elif metric.measure == MetricType.AimLow: good_color = 31 bad_color = 32 elif metric.measure == MetricType.Informational: good_color = 33 bad_color = 33 metric_meta = { "key": key, "operator": operator, "good_color": good_color, "bad_color": bad_color, "title": metric.description, "type": metric.type, } metric_metas.append(metric_meta) state = State(config) for archiver in state.archivers: # We have to do it backwards to get the deltas between releases history = state.index[archiver].revisions[:n][::-1] last = {} for rev in history: vals = [] for meta in metric_metas: try: logger.debug( f"Fetching metric {meta['key']} for {meta['operator']} in {path}" ) val = rev.get(config, archiver, meta["operator"], path, meta["key"]) last_val = last.get(meta["key"], None) # Measure the difference between this value and the last if meta["type"] in (int, float): if last_val: delta = val - last_val else: delta = 0 last[meta["key"]] = val else: # TODO : Measure ranking increases/decreases for str types? delta = 0 if delta == 0: delta_col = delta elif delta < 0: delta_col = f"\u001b[{meta['good_color']}m{delta:n}\u001b[0m" else: delta_col = f"\u001b[{meta['bad_color']}m+{delta:n}\u001b[0m" if meta["type"] in (int, float): k = f"{val:n} ({delta_col})" else: k = f"{val}" except KeyError as e: k = f"Not found {e}" vals.append(k) if include_message: data.append(( format_revision(rev.revision.key), rev.revision.message[:MAX_MESSAGE_WIDTH], rev.revision.author_name, format_date(rev.revision.date), *vals, )) else: data.append(( format_revision(rev.revision.key), rev.revision.author_name, format_date(rev.revision.date), *vals, )) descriptions = [meta["title"] for meta in metric_metas] if include_message: headers = ("Revision", "Message", "Author", "Date", *descriptions) else: headers = ("Revision", "Author", "Date", *descriptions) if format == ReportFormat.HTML: if output.is_file and output.suffix == ".html": report_path = output.parents[0] report_output = output else: report_path = output report_output = output.joinpath("index.html") report_path.mkdir(exist_ok=True, parents=True) templates_dir = (Path(__file__).parents[1] / "templates").resolve() report_template = Template( (templates_dir / "report_template.html").read_text()) table_headers = "".join([f"<th>{header}</th>" for header in headers]) table_content = "" for line in data[::-1]: table_content += "<tr>" for element in line: element = element.replace("[32m", "<span class='green-color'>") element = element.replace("[31m", "<span class='red-color'>") element = element.replace("[33m", "<span class='orange-color'>") element = element.replace("[0m", "</span>") table_content += f"<td>{element}</td>" table_content += "</tr>" report_template = report_template.safe_substitute( headers=table_headers, content=table_content) with report_output.open("w") as output: output.write(report_template) try: copytree(str(templates_dir / "css"), str(report_path / "css")) except FileExistsError: pass logger.info(f"wily report was saved to {report_path}") else: print( # But it still makes more sense to show the newest at the top, so reverse again tabulate.tabulate(headers=headers, tabular_data=data[::-1], tablefmt=console_format))
def report(config, path, metrics, n, include_message=False): """ Show information about the cache and runtime. :param config: The configuration :type config: :class:`wily.config.WilyConfig` :param path: The path to the file :type path: ``str`` :param metrics: Name of the metric to report on :type metrics: ``str`` :param n: Number of items to list :type n: ``int`` :param include_message: Include revision messages :type include_message: ``bool`` """ logger.debug("Running report command") logger.info(f"-----------History for {metrics}------------") data = [] metric_metas = [] for metric in metrics: operator, key = metric.split(".") metric = resolve_metric(metric) # Set the delta colors depending on the metric type if metric.measure == MetricType.AimHigh: good_color = 32 bad_color = 31 elif metric.measure == MetricType.AimLow: good_color = 31 bad_color = 32 elif metric.measure == MetricType.Informational: good_color = 33 bad_color = 33 metric_meta = { "key": key, "operator": operator, "good_color": good_color, "bad_color": bad_color, "title": metric.description, "type": metric.type, } metric_metas.append(metric_meta) state = State(config) for archiver in state.archivers: # We have to do it backwards to get the deltas between releases history = state.index[archiver].revisions[:n][::-1] last = {} for rev in history: vals = [] for meta in metric_metas: try: logger.debug( f"Fetching metric {meta['key']} for {meta['operator']} in {path}" ) val = rev.get(config, archiver, meta["operator"], path, meta["key"]) last_val = last.get(meta["key"], None) # Measure the difference between this value and the last if meta["type"] in (int, float): if last_val: delta = val - last_val else: delta = 0 last[meta["key"]] = val else: # TODO : Measure ranking increases/decreases for str types? delta = 0 if delta == 0: delta_col = delta elif delta < 0: delta_col = f"\u001b[{meta['good_color']}m{delta:n}\u001b[0m" else: delta_col = f"\u001b[{meta['bad_color']}m+{delta:n}\u001b[0m" if meta["type"] in (int, float): k = f"{val:n} ({delta_col})" else: k = f"{val}" except KeyError as e: k = f"Not found {e}" vals.append(k) if include_message: data.append(( format_revision(rev.revision.key), rev.revision.message[:MAX_MESSAGE_WIDTH], rev.revision.author_name, format_date(rev.revision.date), *vals, )) else: data.append(( format_revision(rev.revision.key), rev.revision.author_name, format_date(rev.revision.date), *vals, )) descriptions = [meta["title"] for meta in metric_metas] if include_message: headers = ("Revision", "Message", "Author", "Date", *descriptions) else: headers = ("Revision", "Author", "Date", *descriptions) print( # But it still makes more sense to show the newest at the top, so reverse again tabulate.tabulate(headers=headers, tabular_data=data[::-1], tablefmt=DEFAULT_GRID_STYLE))
def report( config: WilyConfig, path: Path, metrics: str, n: int, output: Path, include_message: bool = False, format: ReportFormat = ReportFormat.CONSOLE, console_format: str = None, ) -> None: """ Show information about the cache and runtime. :param config: The configuration :type config: :class:`wily.config.WilyConfig` :param path: The path to the file :type path: ``str`` :param metrics: Name of the metric to report on :type metrics: ``str`` :param n: Number of items to list :type n: ``int`` :param output: Output path :type output: ``Path`` :param include_message: Include revision messages :type include_message: ``bool`` :param format: Output format :type format: ``ReportFormat`` :param console_format: Grid format style for tabulate :type console_format: ``str`` """ logger.debug("Running report command") logger.info(f"-----------History for {metrics}------------") data = [] metric_metas = [] for metric in metrics: operator, metric = resolve_metric_as_tuple(metric) # Set the delta colors depending on the metric type metric_meta = { "key": metric.name, "operator": operator.name, "title": metric.description, "type": metric.type, "measure": metric.measure, } metric_metas.append(metric_meta) state = State(config) for archiver in state.archivers: history = state.index[archiver].revisions[:n][::-1] last = {} for rev in history: vals = [] for meta in metric_metas: try: logger.debug( f"Fetching metric {meta['key']} for {meta['operator']} in {path}" ) val = rev.get(config, archiver, meta["operator"], path, meta["key"]) last_val = last.get(meta["key"], None) # Measure the difference between this value and the last if meta["type"] in (int, float): delta = val - last_val if last_val else 0 change = delta elif last_val: delta = ord(last_val) - ord( val) if last_val != val else 1 change = last_val else: delta = 1 change = val last[meta["key"]] = val if delta == 0: delta_col = delta elif delta < 0: delta_col = _plant_delta_color( BAD_COLORS[meta["measure"]], change) else: delta_col = _plant_delta_color( GOOD_COLORS[meta["measure"]], change) k = _plant_delta(val, delta_col) except KeyError as e: k = f"Not found {e}" vals.append(k) if include_message: data.append(( format_revision(rev.revision.key), rev.revision.message[:MAX_MESSAGE_WIDTH], rev.revision.author_name, format_date(rev.revision.date), *vals, )) else: data.append(( format_revision(rev.revision.key), rev.revision.author_name, format_date(rev.revision.date), *vals, )) descriptions = [meta["title"] for meta in metric_metas] if include_message: headers = (_("Revision"), _("Message"), _("Author"), _("Date"), *descriptions) else: headers = (_("Revision"), _("Author"), _("Date"), *descriptions) if format in FORMAT_MAP: FORMAT_MAP[format](path, output, data, headers) return print( tabulate.tabulate(headers=headers, tabular_data=data[::-1], tablefmt=console_format))