plots: standardise across DVC/Studio/VS Code (#9931)

* standardise plot data output

* add data required for new anchors to renderer properties

* feed dvc-render updates into API output

* update --json --split api with new data types (send split anchor definitions as strings)

* accomodate zoom and pan anchor

* switch anchor_revs to revs_with_datapoints

* accomodate height and width anchors

* drop terrible idea of holding all data as strings

* fix plot having multiple x fields

* move inferred properties off vega converter class
This commit is contained in:
Matt Seddon
2023-12-06 11:25:13 +11:00
committed by GitHub
parent 1aed230cd0
commit 5bb72b36cb
12 changed files with 488 additions and 406 deletions
+93 -58
View File
@@ -11,7 +11,7 @@ from bs4 import BeautifulSoup
from funcy import first
from dvc.cli import main
from dvc.render import REVISION_FIELD, VERSION_FIELD
from dvc.render import ANCHOR_DEFINITIONS, FILENAME, REVISION
JSON_OUT = "vis_data"
@@ -102,6 +102,9 @@ def verify_vega(
html_result,
json_result,
split_json_result,
title,
x_label,
y_label,
):
if isinstance(versions, str):
versions = [versions]
@@ -111,27 +114,60 @@ def verify_vega(
assert j[0]["type"] == "vega"
assert set(j[0]["revisions"]) == set(versions)
assert json_result[0]["datapoints"] == split_json_result[0]["datapoints"]
assert set(versions) == set(json_result[0]["datapoints"].keys())
assert (
json_result[0]["content"]["data"]["values"]
== split_json_result[0][ANCHOR_DEFINITIONS]["<DVC_METRIC_DATA>"]
)
assert set(versions) == set(json_result[0]["revisions"])
assert json_result[0]["content"]["data"]["values"]
assert html_result["data"]["values"]
assert split_json_result[0]["content"]["data"]["values"] == "<DVC_METRIC_DATA>"
def _assert_templates_equal(html_template, filled_template, split_template):
# besides data, json and split json should be equal
path = ["data", "values"]
content_str = json.dumps(split_json_result[0]["content"])
assert "<DVC_METRIC_DATA>" in content_str
assert "<DVC_METRIC_X_LABEL>" in content_str
assert "<DVC_METRIC_Y_LABEL>" in content_str
def _assert_templates_equal(
html_template, filled_template, split_template, title, x_label, y_label
):
# besides split anchors, json and split json should be equal
paths = [["data", "values"], ["encoding", "color"]]
tmp1 = deepcopy(html_template)
tmp2 = deepcopy(filled_template)
tmp3 = deepcopy(split_template)
dpath.set(tmp1, path, {})
dpath.set(tmp2, path, {})
dpath.set(tmp3, path, {})
tmp3 = json.loads(
json.dumps(split_template)
.replace('"<DVC_METRIC_PLOT_HEIGHT>"', "300")
.replace('"<DVC_METRIC_PLOT_WIDTH>"', "300")
.replace("<DVC_METRIC_TITLE>", title)
.replace("<DVC_METRIC_X_LABEL>", x_label)
.replace("<DVC_METRIC_Y_LABEL>", y_label)
.replace(
'"<DVC_METRIC_ZOOM_AND_PAN>"',
json.dumps(
{
"name": "grid",
"select": "interval",
"bind": "scales",
}
),
)
)
for path in paths:
dpath.set(tmp1, path, {})
dpath.set(tmp2, path, {})
dpath.set(tmp3, path, {})
assert tmp1 == tmp2 == tmp3
_assert_templates_equal(
html_result, json_result[0]["content"], split_json_result[0]["content"]
html_result,
json_result[0]["content"],
split_json_result[0]["content"],
title,
x_label,
y_label,
)
@@ -193,17 +229,13 @@ def test_repo_with_plots(tmp_dir, scm, dvc, capsys, run_copy_metrics, repo_with_
] == _update_datapoints(
linear_v1,
{
VERSION_FIELD: {
"revision": "workspace",
"filename": "linear.json",
"field": "y",
},
REVISION: "workspace",
},
)
assert html_result["linear.json"]["data"]["values"] == _update_datapoints(
linear_v1,
{
REVISION_FIELD: "workspace",
REVISION: "workspace",
},
)
assert json_data["confusion.json"][0]["content"]["data"][
@@ -211,27 +243,29 @@ def test_repo_with_plots(tmp_dir, scm, dvc, capsys, run_copy_metrics, repo_with_
] == _update_datapoints(
confusion_v1,
{
VERSION_FIELD: {
"revision": "workspace",
"filename": "confusion.json",
"field": "actual",
},
REVISION: "workspace",
},
)
assert html_result["confusion.json"]["data"]["values"] == _update_datapoints(
confusion_v1,
{
REVISION_FIELD: "workspace",
REVISION: "workspace",
},
)
verify_image(tmp_dir, "workspace", "image.png", image_v1, html_path, json_data)
for plot in ["linear.json", "confusion.json"]:
for plot, title, x_label, y_label in [
("linear.json", "linear", "x", "y"),
("confusion.json", "confusion matrix", "predicted", "actual"),
]:
verify_vega(
"workspace",
html_result[plot],
json_data[plot],
split_json_data[plot],
title,
x_label,
y_label,
)
verify_vega_props("confusion.json", json_data, **confusion_props)
@@ -250,12 +284,18 @@ def test_repo_with_plots(tmp_dir, scm, dvc, capsys, run_copy_metrics, repo_with_
verify_image(tmp_dir, "workspace", "image.png", image_v2, html_path, json_data)
verify_image(tmp_dir, "HEAD", "image.png", image_v1, html_path, json_data)
for plot in ["linear.json", "confusion.json"]:
for plot, title, x_label, y_label in [
("linear.json", "linear", "x", "y"),
("confusion.json", "confusion matrix", "predicted", "actual"),
]:
verify_vega(
["HEAD", "workspace"],
html_result[plot],
json_data[plot],
split_json_data[plot],
title,
x_label,
y_label,
)
verify_vega_props("confusion.json", json_data, **confusion_props)
path = tmp_dir / "subdir"
@@ -277,31 +317,23 @@ def test_repo_with_plots(tmp_dir, scm, dvc, capsys, run_copy_metrics, repo_with_
] == _update_datapoints(
linear_v2,
{
VERSION_FIELD: {
"revision": "workspace",
"filename": "../linear.json",
"field": "y",
},
REVISION: "workspace",
},
) + _update_datapoints(
linear_v1,
{
VERSION_FIELD: {
"revision": "HEAD",
"filename": "../linear.json",
"field": "y",
},
REVISION: "HEAD",
},
)
assert html_result["../linear.json"]["data"]["values"] == _update_datapoints(
linear_v2,
{
REVISION_FIELD: "workspace",
REVISION: "workspace",
},
) + _update_datapoints(
linear_v1,
{
REVISION_FIELD: "HEAD",
REVISION: "HEAD",
},
)
assert json_data["../confusion.json"][0]["content"]["data"][
@@ -309,43 +341,38 @@ def test_repo_with_plots(tmp_dir, scm, dvc, capsys, run_copy_metrics, repo_with_
] == _update_datapoints(
confusion_v2,
{
VERSION_FIELD: {
"revision": "workspace",
"filename": "../confusion.json",
"field": "actual",
},
REVISION: "workspace",
},
) + _update_datapoints(
confusion_v1,
{
VERSION_FIELD: {
"revision": "HEAD",
"filename": "../confusion.json",
"field": "actual",
},
REVISION: "HEAD",
},
)
assert html_result["../confusion.json"]["data"]["values"] == _update_datapoints(
confusion_v2,
{
REVISION_FIELD: "workspace",
REVISION: "workspace",
},
) + _update_datapoints(
confusion_v1,
{
REVISION_FIELD: "HEAD",
REVISION: "HEAD",
},
)
for plot in [
"../linear.json",
"../confusion.json",
for plot, title, x_label, y_label in [
("../linear.json", "linear", "x", "y"),
("../confusion.json", "confusion matrix", "predicted", "actual"),
]:
verify_vega(
["HEAD", "workspace"],
html_result[plot],
json_data[plot],
split_json_data[plot],
title,
x_label,
y_label,
)
verify_image(
path,
@@ -434,7 +461,7 @@ def test_repo_with_config_plots(tmp_dir, capsys, repo_with_config_plots):
repo_state = repo_with_config_plots()
plots = next(repo_state)
html_path, _, __ = call(capsys)
html_path, _, split_json_result = call(capsys)
assert os.path.exists(html_path)
html_result = extract_vega_specs(
@@ -444,20 +471,28 @@ def test_repo_with_config_plots(tmp_dir, capsys, repo_with_config_plots):
"confusion_train_vs_test",
],
)
ble = _update_datapoints(
plots["data"]["linear_train.json"],
{
REVISION_FIELD: "linear_train.json",
REVISION: "workspace",
FILENAME: "linear_train.json",
},
) + _update_datapoints(
plots["data"]["linear_test.json"],
{
REVISION_FIELD: "linear_test.json",
REVISION: "workspace",
FILENAME: "linear_test.json",
},
)
assert html_result["linear_train_vs_test"]["data"]["values"] == ble
# TODO check json results once vscode is able to handle flexible plots
assert (
split_json_result["data"]["linear_train_vs_test"][0][ANCHOR_DEFINITIONS][
"<DVC_METRIC_DATA>"
]
== ble
)
@pytest.mark.vscode
@@ -466,7 +501,7 @@ def test_repo_with_dvclive_plots(tmp_dir, capsys, repo_with_dvclive_plots):
for s in ("show", "diff"):
_, json_result, split_json_result = call(capsys, subcommand=s)
expected_result = {
expected_result: Dict[str, Dict[str, list[str]]] = {
"data": {
"dvclive/plots/metrics/metric.tsv": [],
},
+54 -76
View File
@@ -1,109 +1,87 @@
from dvc.render import REVISION_FIELD, REVISIONS_KEY, SRC_FIELD, TYPE_KEY, VERSION_FIELD
import json
import pytest
from dvc.render import ANCHOR_DEFINITIONS, FILENAME, REVISION, REVISIONS, SRC, TYPE_KEY
from dvc.render.convert import to_json
def test_to_json_vega(mocker):
vega_renderer = mocker.MagicMock()
vega_renderer.TYPE = "vega"
vega_renderer.get_revs.return_value = ["bar", "foo"]
vega_renderer.get_filled_template.return_value = {"this": "is vega"}
vega_renderer.datapoints = [
{
"x": 1,
"y": 2,
VERSION_FIELD: {"revision": "foo"},
"filename": "foo.json",
},
{
"x": 2,
"y": 1,
VERSION_FIELD: {"revision": "bar"},
"filename": "foo.json",
},
]
result = to_json(vega_renderer)
assert result[0] == {
TYPE_KEY: vega_renderer.TYPE,
REVISIONS_KEY: ["bar", "foo"],
REVISIONS: ["bar", "foo"],
"content": {"this": "is vega"},
"datapoints": {
"foo": [
{
"x": 1,
"y": 2,
"filename": "foo.json",
VERSION_FIELD: {"revision": "foo"},
},
],
"bar": [
{
"x": 2,
"y": 1,
"filename": "foo.json",
VERSION_FIELD: {"revision": "bar"},
},
],
},
}
vega_renderer.get_filled_template.assert_called()
@pytest.mark.vscode
def test_to_json_vega_split(mocker):
revs = ["bar", "foo"]
content = json.dumps(
{
"this": "is split vega",
"encoding": {"color": "<DVC_METRIC_COLOR>"},
"data": {"values": "<DVC_METRIC_DATA>"},
}
)
anchor_definitions = {
"<DVC_METRIC_COLOR>": {
"field": "rev",
"scale": {
"domain": revs,
"range": ["#ff0000", "#00ff00"],
},
},
"<DVC_METRIC_DATA>": [
{
"x": 1,
"y": 2,
REVISION: "foo",
FILENAME: "foo.json",
},
{
"x": 2,
"y": 1,
REVISION: "bar",
FILENAME: "foo.json",
},
],
}
vega_renderer = mocker.MagicMock()
vega_renderer.TYPE = "vega"
vega_renderer.get_filled_template.return_value = {"this": "is split vega"}
vega_renderer.datapoints = [
{
"x": 1,
"y": 2,
VERSION_FIELD: {"revision": "foo"},
"filename": "foo.json",
},
{
"x": 2,
"y": 1,
VERSION_FIELD: {"revision": "bar"},
"filename": "foo.json",
},
]
vega_renderer.get_partial_filled_template.return_value = (
content,
{ANCHOR_DEFINITIONS: anchor_definitions},
)
vega_renderer.get_revs.return_value = ["bar", "foo"]
result = to_json(vega_renderer, split=True)
assert result[0] == {
ANCHOR_DEFINITIONS: anchor_definitions,
TYPE_KEY: vega_renderer.TYPE,
REVISIONS_KEY: ["bar", "foo"],
"content": {"this": "is split vega"},
"datapoints": {
"foo": [
{
"x": 1,
"y": 2,
"filename": "foo.json",
VERSION_FIELD: {"revision": "foo"},
}
],
"bar": [
{
"x": 2,
"y": 1,
"filename": "foo.json",
VERSION_FIELD: {"revision": "bar"},
}
],
},
REVISIONS: revs,
"content": content,
}
vega_renderer.get_filled_template.assert_called_with(
as_string=False, skip_anchors=["data"]
)
vega_renderer.get_partial_filled_template.assert_called_once()
def test_to_json_image(mocker):
image_renderer = mocker.MagicMock()
image_renderer.TYPE = "image"
image_renderer.datapoints = [
{SRC_FIELD: "contentfoo", REVISION_FIELD: "foo"},
{SRC_FIELD: "contentbar", REVISION_FIELD: "bar"},
{SRC: "contentfoo", REVISION: "foo"},
{SRC: "contentbar", REVISION: "bar"},
]
result = to_json(image_renderer)
assert result[0] == {
"url": image_renderer.datapoints[0].get(SRC_FIELD),
REVISIONS_KEY: [image_renderer.datapoints[0].get(REVISION_FIELD)],
"url": image_renderer.datapoints[0].get(SRC),
REVISIONS: [image_renderer.datapoints[0].get(REVISION)],
TYPE_KEY: image_renderer.TYPE,
}
+10 -10
View File
@@ -1,4 +1,4 @@
from dvc.render import REVISION_FIELD, SRC_FIELD
from dvc.render import FILENAME, REVISION, SRC
from dvc.render.converter.image import ImageConverter
@@ -8,9 +8,9 @@ def test_image_converter_no_out():
datapoints, _ = converter.flat_datapoints("r")
assert datapoints[0] == {
REVISION_FIELD: "r",
"filename": "image.png",
SRC_FIELD: converter._encode_image(b"content"),
REVISION: "r",
FILENAME: "image.png",
SRC: converter._encode_image(b"content"),
}
@@ -21,9 +21,9 @@ def test_image_converter_with_out(tmp_dir):
datapoints, _ = converter.flat_datapoints("r")
assert datapoints[0] == {
REVISION_FIELD: "r",
"filename": "image.png",
SRC_FIELD: str(tmp_dir / "foo" / "r_image.png"),
REVISION: "r",
FILENAME: "image.png",
SRC: str(tmp_dir / "foo" / "r_image.png"),
}
assert (tmp_dir / "foo" / "r_image.png").read_bytes() == b"content"
@@ -37,9 +37,9 @@ def test_image_converter_with_slash_in_revision(tmp_dir):
datapoints, _ = converter.flat_datapoints("feature/r")
assert datapoints[0] == {
REVISION_FIELD: "feature/r",
"filename": "image.png",
SRC_FIELD: str(tmp_dir / "foo" / "feature_r_image.png"),
REVISION: "feature/r",
FILENAME: "image.png",
SRC: str(tmp_dir / "foo" / "feature_r_image.png"),
}
assert (tmp_dir / "foo" / "feature_r_image.png").read_bytes() == b"content"
+14 -12
View File
@@ -1,9 +1,13 @@
import pytest
from funcy import set_in
from dvc.render import VERSION_FIELD
from dvc.render import FIELD, FILENAME, REVISION
from dvc.render.converter.vega import VegaConverter
from dvc.render.match import PlotsData, _squash_plots_properties, match_defs_renderers
from dvc.render.match import (
PlotsData,
_squash_plots_properties,
match_defs_renderers,
)
@pytest.mark.parametrize(
@@ -158,25 +162,23 @@ def test_match_renderers(M):
renderer = renderer_with_errors[0]
assert renderer.datapoints == [
{
VERSION_FIELD: {
"revision": "v1",
"filename": "file.json",
"field": "y",
},
REVISION: "v1",
FILENAME: "file.json",
FIELD: "y",
"x": 1,
"y": 1,
},
{
VERSION_FIELD: {
"revision": "v1",
"filename": "file.json",
"field": "y",
},
REVISION: "v1",
FILENAME: "file.json",
FIELD: "y",
"x": 2,
"y": 2,
},
]
assert renderer.properties == {
"anchors_y_definitions": [{FILENAME: "file.json", FIELD: "y"}],
"revs_with_datapoints": ["v1"],
"title": "plot_id_1",
"x": "x",
"y": "y",
+232 -135
View File
@@ -3,7 +3,7 @@ from collections import OrderedDict
import pytest
from dvc.exceptions import DvcException
from dvc.render import VERSION_FIELD
from dvc.render import FIELD, FILENAME, REVISION
from dvc.render.converter.vega import FieldNotFoundError, VegaConverter, _lists
@@ -35,23 +35,25 @@ def test_finding_lists(dictionary, expected_result):
{
"v": 1,
"step": 0,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"v": 2,
"step": 1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
],
{"x": "step", "y": "v", "x_label": "step", "y_label": "v"},
{
"anchors_y_definitions": [{FILENAME: "f", FIELD: "v"}],
"x": "step",
"y": "v",
"x_label": "step",
"y_label": "v",
},
id="default_x_y",
),
pytest.param(
@@ -61,23 +63,25 @@ def test_finding_lists(dictionary, expected_result):
{
"v": 1,
"v2": 0.1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"v": 2,
"v2": 0.2,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
],
{"x": "v", "y": "v2", "x_label": "v", "y_label": "v2"},
{
"anchors_y_definitions": [{FILENAME: "f", FIELD: "v2"}],
"x": "v",
"y": "v2",
"x_label": "v",
"y_label": "v2",
},
id="choose_x_y",
),
pytest.param(
@@ -99,23 +103,25 @@ def test_finding_lists(dictionary, expected_result):
{
"v": 1,
"v2": 0.1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"v": 2,
"v2": 0.2,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
],
{"x": "v", "y": "v2", "x_label": "x", "y_label": "y"},
{
"anchors_y_definitions": [{FILENAME: "f", FIELD: "v2"}],
"x": "v",
"y": "v2",
"x_label": "x",
"y_label": "y",
},
id="find_in_nested_structure",
),
pytest.param(
@@ -123,44 +129,36 @@ def test_finding_lists(dictionary, expected_result):
{"y": {"f": ["v", "v2"]}},
[
{
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
"dvc_inferred_y_value": 1,
"v": 1,
"v2": 0.1,
"step": 0,
},
{
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
"dvc_inferred_y_value": 2,
"v": 2,
"v2": 0.2,
"step": 1,
},
{
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
"dvc_inferred_y_value": 0.1,
"v2": 0.1,
"v": 1,
"step": 0,
},
{
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
"v": 2,
"v2": 0.2,
"dvc_inferred_y_value": 0.2,
@@ -168,6 +166,10 @@ def test_finding_lists(dictionary, expected_result):
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v"},
{FILENAME: "f", FIELD: "v2"},
],
"x": "step",
"y": "dvc_inferred_y_value",
"y_label": "y",
@@ -189,47 +191,43 @@ def test_finding_lists(dictionary, expected_result):
"z": 3,
"v": 1,
"step": 0,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"dvc_inferred_y_value": 2,
"z": 4,
"step": 1,
"v": 2,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"dvc_inferred_y_value": 3,
"v": 1,
"z": 3,
"step": 0,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "z",
},
REVISION: "r",
FILENAME: "f",
FIELD: "z",
},
{
"dvc_inferred_y_value": 4,
"v": 2,
"z": 4,
"step": 1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "z",
},
REVISION: "r",
FILENAME: "f",
FIELD: "z",
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v"},
{FILENAME: "f", FIELD: "z"},
],
"x": "step",
"y": "dvc_inferred_y_value",
"y_label": "y",
@@ -247,32 +245,35 @@ def test_finding_lists(dictionary, expected_result):
{
"v": 1,
"v2": 0.1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"v": 2,
"v2": 0.2,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"v": 3,
"v2": 0.3,
VERSION_FIELD: {
"revision": "r",
"filename": "f2",
"field": "v2",
},
REVISION: "r",
FILENAME: "f2",
FIELD: "v2",
},
],
{"x": "v", "y": "v2", "x_label": "v", "y_label": "v2"},
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v2"},
{FILENAME: "f2", FIELD: "v2"},
],
"x": "v",
"y": "v2",
"x_label": "v",
"y_label": "v2",
},
id="multi_file_json",
),
pytest.param(
@@ -284,47 +285,43 @@ def test_finding_lists(dictionary, expected_result):
"v": 1,
"v2": 0.1,
"step": 0,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"dvc_inferred_y_value": 2,
"v": 2,
"v2": 0.2,
"step": 1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"dvc_inferred_y_value": 0.1,
"v": 1,
"v2": 0.1,
"step": 0,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"dvc_inferred_y_value": 0.2,
"v": 2,
"v2": 0.2,
"step": 1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v"},
{FILENAME: "f", FIELD: "v2"},
],
"x": "step",
"y": "dvc_inferred_y_value",
"x_label": "step",
@@ -344,35 +341,34 @@ def test_finding_lists(dictionary, expected_result):
"v": 1,
"v2": 0.1,
"v3": 0.01,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"dvc_inferred_y_value": 0.01,
"v": 1,
"v2": 0.1,
"v3": 0.01,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v3",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v3",
},
{
"dvc_inferred_y_value": 0.1,
"v": 1,
"v2": 0.1,
VERSION_FIELD: {
"revision": "r",
"filename": "f2",
"field": "v2",
},
REVISION: "r",
FILENAME: "f2",
FIELD: "v2",
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v2"},
{FILENAME: "f", FIELD: "v3"},
{FILENAME: "f2", FIELD: "v2"},
],
"x": "v",
"y": "dvc_inferred_y_value",
"x_label": "v",
@@ -390,23 +386,23 @@ def test_finding_lists(dictionary, expected_result):
{
"v": 1,
"v2": 0.1,
VERSION_FIELD: {
"revision": "r",
"filename": "f",
"field": "v2",
},
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"v": 1,
"v2": 0.1,
VERSION_FIELD: {
"revision": "r",
"filename": "f2",
"field": "v2",
},
REVISION: "r",
FILENAME: "f2",
FIELD: "v2",
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v2"},
{FILENAME: "f2", FIELD: "v2"},
],
"x": "v",
"y": "v2",
"x_label": "v",
@@ -414,6 +410,107 @@ def test_finding_lists(dictionary, expected_result):
},
id="multi_file_y_same_prefix",
),
pytest.param(
{
"f": {"metric": [{"x1": 1, "v": 0.1}]},
"f2": {"metric": [{"x2": 100, "v": 0.1}]},
},
{"y": {"f": ["v"], "f2": ["v"]}, "x": {"f": "x1", "f2": "x2"}},
[
{
"x1": 1,
"v": 0.1,
"dvc_inferred_x_value": 1,
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"x2": 100,
"v": 0.1,
"dvc_inferred_x_value": 100,
REVISION: "r",
FILENAME: "f2",
FIELD: "v",
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v"},
{FILENAME: "f2", FIELD: "v"},
],
"x": "dvc_inferred_x_value",
"y": "v",
"x_label": "x",
"y_label": "v",
},
id="multiple_x_fields",
),
pytest.param(
{
"f": {
"metric": [
{"v": 1, "v2": 0.1, "x1": 100},
{"v": 2, "v2": 0.2, "x1": 1000},
]
},
"f2": {"metric": [{"x2": -2}, {"x2": -4}]},
},
{"y": ["v", "v2"], "x": {"f": "x1", "f2": "x2"}},
[
{
"dvc_inferred_x_value": 100,
"dvc_inferred_y_value": 1,
"v": 1,
"v2": 0.1,
"x1": 100,
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"dvc_inferred_x_value": 1000,
"dvc_inferred_y_value": 2,
"v": 2,
"v2": 0.2,
"x1": 1000,
REVISION: "r",
FILENAME: "f",
FIELD: "v",
},
{
"dvc_inferred_x_value": -2,
"dvc_inferred_y_value": 0.1,
"v": 1,
"v2": 0.1,
"x1": 100,
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
{
"dvc_inferred_x_value": -4,
"dvc_inferred_y_value": 0.2,
"v": 2,
"v2": 0.2,
"x1": 1000,
REVISION: "r",
FILENAME: "f",
FIELD: "v2",
},
],
{
"anchors_y_definitions": [
{FILENAME: "f", FIELD: "v"},
{FILENAME: "f", FIELD: "v2"},
],
"x": "dvc_inferred_x_value",
"y": "dvc_inferred_y_value",
"x_label": "x",
"y_label": "y",
},
id="y_list_x_dict",
),
],
)
def test_convert(