-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
Draw graph in HTML for a specific metric.
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
TODO: Add multiple lines for multiple files
-- -- --- --- --- --- --- --- ------- ------- ------- |
"""
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from pathlib import Path
-- -- --- --- --- --- --- --- ------- ------- ------- |
from typing import Optional, Tuple, Union
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
import plotly.graph_objs as go
-- -- --- --- --- --- --- --- ------- ------- ------- |
import plotly.offline
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily import format_datetime, logger
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.config.types import WilyConfig
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.operators import resolve_metric, resolve_metric_as_tuple
-- -- --- --- --- --- --- --- ------- ------- ------- |
from wily.state import State
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
def metric_parts(metric):
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
"""Convert a metric name into the operator and metric names."""
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
operator, met = resolve_metric_as_tuple(metric)
-- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 |
return operator.name, met.name
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- -- --- --- --- --- --- --- ------- ------- ------- |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
def graph(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
config: WilyConfig,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
path: str,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
metrics: Union[Tuple[str], Tuple[str, str]],
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
output: Optional[str] = None,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x_axis: Optional[str] = None,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
changes: bool = True,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
text: bool = False,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
aggregate: bool = False,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
) -> None:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
"""
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
Graph information about the cache and runtime.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param config: The configuration.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param path: The path to the files.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param metrics: The Y and Z-axis metrics to report on.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param output: Save report to specified path instead of opening browser.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param x_axis: Name of metric for x-axis or "history".
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param changes: Only graph changes.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param text: Show commit message inline in graph.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
:param aggregate: Aggregate values for graph.
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
"""
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
logger.debug("Running graph command")
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
data = []
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
state = State(config)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if x_axis is None:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x_axis = "history"
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x_operator = x_key = ""
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
else:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x_operator, x_key = metric_parts(x_axis)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
y_metric = resolve_metric(metrics[0])
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
title = f"{x_axis.capitalize()} of {y_metric.description} for {path}{' aggregated' if aggregate else ''}"
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if not aggregate:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
tracked_files = set()
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
for rev in state.index[state.default_archiver].revisions:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
tracked_files.update(rev.revision.tracked_files)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
paths = {
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
tracked_file
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
for tracked_file in tracked_files
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if tracked_file.startswith(path)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
} or {path}
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
else:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
paths = {path}
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
operator, key = metric_parts(metrics[0])
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if len(metrics) == 1: # only y-axis
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z_axis = z_operator = z_key = ""
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
else:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z_axis = resolve_metric(metrics[1])
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z_operator, z_key = metric_parts(metrics[1])
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
for path_ in paths:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
current_path = str(Path(path_))
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x = []
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
y = []
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z = []
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
labels = []
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
last_y = None
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
for rev in state.index[state.default_archiver].revisions:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
try:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
val = rev.get(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
config, state.default_archiver, operator, current_path, key
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if val != last_y or not changes:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
y.append(val)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if z_axis:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z.append(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
rev.get(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
config,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
state.default_archiver,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z_operator,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
current_path,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
z_key,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if x_axis == "history":
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x.append(format_datetime(rev.revision.date))
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
else:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x.append(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
rev.get(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
config,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
state.default_archiver,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x_operator,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
current_path,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x_key,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
labels.append(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
f"{rev.revision.author_name} <br>{rev.revision.message}"
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
last_y = val
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
except KeyError:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
# missing data
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
pass
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
# Create traces
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
trace = go.Scatter(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
x=x,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
y=y,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
mode="lines+markers+text" if text else "lines+markers",
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
name=f"{path_}",
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
ids=state.index[state.default_archiver].revision_keys,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
text=labels,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
marker={
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
"size": 0 if z_axis is None else z,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
"color": list(range(len(y))),
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
# "colorscale": "Viridis",
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
},
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
xcalendar="gregorian",
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
hoveron="points+fills",
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
) # type: ignore
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
data.append(trace)
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
if output:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
filename = output
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
auto_open = False
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
else:
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
filename = "wily-report.html"
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
auto_open = True
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
plotly.offline.plot(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
{
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
"data": data,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
"layout": go.Layout(
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
title=title,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
xaxis={"title": x_axis},
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
yaxis={"title": y_metric.description},
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
), # type: ignore
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
},
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
auto_open=auto_open,
-- 19 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
filename=filename,
-- -- 005 014 009 016 019 025 0106.20 0303.42 0002.86 |
)