def graph(config, path, metrics, output=None, x_axis=None, changes=True, text=False):
logger.debug("<STR_LIT>")<EOL>data = []<EOL>state = State(config)<EOL>abs_path = config.path / pathlib.Path(path)<EOL>if x_axis is None:<EOL><INDENT>x_axis = "<STR_LIT>"<EOL><DEDENT>else:<EOL><INDENT>x_operator, x_key = metric_parts(x_axis)<EOL><DEDENT>if abs_path.is_dir():<EOL><INDENT>paths = [<EOL>p.relative_to(config.path) for p in pathlib.Path(abs_path).glob("<STR_LIT>")<EOL>]<EOL><DEDENT>else:<EOL><INDENT>paths = [path]<EOL><DEDENT>operator, key = metric_parts(metrics[<NUM_LIT:0>])<EOL>if len(metrics) == <NUM_LIT:1>:  <EOL><INDENT>z_axis = None<EOL><DEDENT>else:<EOL><INDENT>z_axis = resolve_metric(metrics[<NUM_LIT:1>])<EOL>z_operator, z_key = metric_parts(metrics[<NUM_LIT:1>])<EOL><DEDENT>for path in paths:<EOL><INDENT>x = []<EOL>y = []<EOL>z = []<EOL>labels = []<EOL>last_y = None<EOL>for rev in state.index[state.default_archiver].revisions:<EOL><INDENT>labels.append(f"<STR_LIT>")<EOL>try:<EOL><INDENT>val = rev.get(config, state.default_archiver, operator, str(path), key)<EOL>if val != last_y or not changes:<EOL><INDENT>y.append(val)<EOL>if z_axis:<EOL><INDENT>z.append(<EOL>rev.get(<EOL>config,<EOL>state.default_archiver,<EOL>z_operator,<EOL>str(path),<EOL>z_key,<EOL>)<EOL>)<EOL><DEDENT>if x_axis == "<STR_LIT>":<EOL><INDENT>x.append(format_datetime(rev.revision.date))<EOL><DEDENT>else:<EOL><INDENT>x.append(<EOL>rev.get(<EOL>config,<EOL>state.default_archiver,<EOL>x_operator,<EOL>str(path),<EOL>x_key,<EOL>)<EOL>)<EOL><DEDENT><DEDENT>last_y = val<EOL><DEDENT>except KeyError:<EOL><INDENT>pass<EOL><DEDENT><DEDENT>trace = go.Scatter(<EOL>x=x,<EOL>y=y,<EOL>mode="<STR_LIT>" if text else "<STR_LIT>",<EOL>name=f"<STR_LIT>",<EOL>ids=state.index[state.default_archiver].revision_keys,<EOL>text=labels,<EOL>marker=dict(<EOL>size=<NUM_LIT:0> if z_axis is None else z,<EOL>color=list(range(len(y))),<EOL>),<EOL>xcalendar="<STR_LIT>",<EOL>hoveron="<STR_LIT>",<EOL>)<EOL>data.append(trace)<EOL><DEDENT>if output:<EOL><INDENT>filename = output<EOL>auto_open = False<EOL><DEDENT>else:<EOL><INDENT>filename = "<STR_LIT>"<EOL>auto_open = True<EOL><DEDENT>y_metric = resolve_metric(metrics[<NUM_LIT:0>])<EOL>title = f"<STR_LIT>"<EOL>plotly.offline.plot(<EOL>{<EOL>"<STR_LIT:data>": data,<EOL>"<STR_LIT>": go.Layout(<EOL>title=title,<EOL>xaxis={"<STR_LIT:title>": x_axis},<EOL>yaxis={"<STR_LIT:title>": y_metric.description},<EOL>),<EOL>},<EOL>auto_open=auto_open,<EOL>filename=filename,<EOL>)<EOL>
Graph information about the cache and runtime.

:param config: The configuration.
:type  config: :class:`wily.config.WilyConfig`

:param path: The path to the files.
:type  path: ``list``

:param metrics: The Y and Z-axis metrics to report on.
:type  metrics: ``tuple``

:param output: Save report to specified path instead of opening browser.
:type  output: ``str``