mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-02 07:18:25 +01:00
plots: introduce support for images (#6431)
* plots: support images initial * plots: data: unify PlotsData * plots: data conversion refactor * plots: renderers: abstract functionality * plots: uncommenting tests * plots: self implement grouping * plots: rename group * plots: extract rendering tests * plots: move rendering tests to unit * plots: add image rendering tests * plots: dont use dpath to find vega plot * plots: test find_vega * plots: test render function * plots: move rendering out of repo package * plots: refactoring * plots: self review refactor
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import pytest
|
||||
|
||||
from dvc.render.html import HTML, PAGE_HTML, MissingPlaceholderError
|
||||
|
||||
CUSTOM_PAGE_HTML = """<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>TITLE</title>
|
||||
<script type="text/javascript" src="vega"></script>
|
||||
<script type="text/javascript" src="vega-lite"></script>
|
||||
<script type="text/javascript" src="vega-embed"></script>
|
||||
</head>
|
||||
<body>
|
||||
{plot_divs}
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"template,page_elements,expected_page",
|
||||
[
|
||||
(None, ["content"], PAGE_HTML.format(plot_divs="content")),
|
||||
(
|
||||
CUSTOM_PAGE_HTML,
|
||||
["content"],
|
||||
CUSTOM_PAGE_HTML.format(plot_divs="content"),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_html(tmp_dir, template, page_elements, expected_page):
|
||||
page = HTML(template)
|
||||
page.elements = page_elements
|
||||
|
||||
result = page.embed()
|
||||
|
||||
assert result == expected_page
|
||||
|
||||
|
||||
def test_no_placeholder(tmp_dir):
|
||||
template = "<head></head><body></body>"
|
||||
|
||||
with pytest.raises(MissingPlaceholderError):
|
||||
HTML(template)
|
||||
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from dvc.render.image import ImageRenderer
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"extension, matches",
|
||||
(
|
||||
(".csv", False),
|
||||
(".json", False),
|
||||
(".tsv", False),
|
||||
(".yaml", False),
|
||||
(".jpg", True),
|
||||
(".gif", True),
|
||||
(".jpeg", True),
|
||||
(".png", True),
|
||||
),
|
||||
)
|
||||
def test_matches(extension, matches):
|
||||
filename = "file" + extension
|
||||
data = {
|
||||
"HEAD": {"data": {filename: {}}},
|
||||
"v1": {"data": {filename: {}}},
|
||||
}
|
||||
assert ImageRenderer.matches(data) == matches
|
||||
|
||||
|
||||
def test_render(tmp_dir):
|
||||
data = {"workspace": {"data": {"file.jpg": {"data": b"content"}}}}
|
||||
|
||||
page_dir = os.path.join("some", "path")
|
||||
html = ImageRenderer(data).generate_html(page_dir)
|
||||
|
||||
assert (tmp_dir / page_dir).is_dir()
|
||||
image_file = tmp_dir / page_dir / "static" / "workspace_file.jpg"
|
||||
assert image_file.is_file()
|
||||
|
||||
with open(image_file, "rb") as fobj:
|
||||
assert fobj.read() == b"content"
|
||||
|
||||
assert "<p>file.jpg</p>" in html
|
||||
assert (
|
||||
f'<img src="{os.path.join("static", "workspace_file.jpg")}">' in html
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
import os
|
||||
|
||||
from dvc.render.utils import find_vega, render
|
||||
|
||||
|
||||
def assert_website_has_image(page_path, revision, filename, image_content):
|
||||
index_path = page_path / "index.html"
|
||||
assert index_path.is_file()
|
||||
index_content = index_path.read_text()
|
||||
|
||||
resources_filename = f"{revision}_{filename.replace(os.sep, '_')}"
|
||||
image_path = page_path / "static" / resources_filename
|
||||
assert image_path.is_file()
|
||||
|
||||
img_html = f'<img src="{os.path.join("static", resources_filename)}">'
|
||||
assert img_html in index_content
|
||||
with open(image_path, "rb") as fobj:
|
||||
assert fobj.read() == image_content
|
||||
|
||||
|
||||
def test_render(tmp_dir, dvc):
|
||||
data = {
|
||||
"HEAD": {
|
||||
"data": {
|
||||
"file.json": {
|
||||
"data": [{"y": 5}, {"y": 6}],
|
||||
"props": {"fields": {"y"}},
|
||||
},
|
||||
os.path.join("sub", "other_file.jpg"): {"data": b"content"},
|
||||
}
|
||||
},
|
||||
"v2": {
|
||||
"data": {
|
||||
"file.json": {
|
||||
"data": [{"y": 3}, {"y": 5}],
|
||||
"props": {"fields": {"y"}},
|
||||
},
|
||||
"other_file.jpg": {"data": b"content2"},
|
||||
}
|
||||
},
|
||||
"v1": {
|
||||
"data": {
|
||||
"some.csv": {
|
||||
"data": [{"y": 2}, {"y": 3}],
|
||||
"props": {"fields": {"y"}},
|
||||
},
|
||||
"another.gif": {"data": b"content3"},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
render(dvc, data, path=os.path.join("results", "dir"))
|
||||
page_path = tmp_dir / "results" / "dir"
|
||||
index_path = page_path / "index.html"
|
||||
|
||||
assert index_path.is_file()
|
||||
assert_website_has_image(
|
||||
page_path, "HEAD", os.path.join("sub", "other_file.jpg"), b"content"
|
||||
)
|
||||
assert_website_has_image(page_path, "v2", "other_file.jpg", b"content2")
|
||||
assert_website_has_image(page_path, "v1", "another.gif", b"content3")
|
||||
|
||||
index_content = index_path.read_text()
|
||||
file_vega = find_vega(dvc, data, "file.json")
|
||||
some_vega = find_vega(dvc, data, "some.csv")
|
||||
assert file_vega in index_content.strip()
|
||||
assert some_vega in index_content.strip()
|
||||
@@ -0,0 +1,436 @@
|
||||
import json
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
|
||||
import pytest
|
||||
|
||||
from dvc.render.utils import find_vega, group_by_filename
|
||||
from dvc.render.vega import (
|
||||
INDEX_FIELD,
|
||||
REVISION_FIELD,
|
||||
VegaRenderer,
|
||||
_find_data,
|
||||
_lists,
|
||||
)
|
||||
from dvc.repo.plots.template import (
|
||||
BadTemplateError,
|
||||
NoFieldInDataError,
|
||||
TemplateNotFoundError,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"dictionary, expected_result",
|
||||
[
|
||||
({}, []),
|
||||
({"x": ["a", "b", "c"]}, [["a", "b", "c"]]),
|
||||
(
|
||||
OrderedDict([("x", {"y": ["a", "b"]}), ("z", {"w": ["c", "d"]})]),
|
||||
[["a", "b"], ["c", "d"]],
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_finding_lists(dictionary, expected_result):
|
||||
result = _lists(dictionary)
|
||||
|
||||
assert list(result) == expected_result
|
||||
|
||||
|
||||
def test_find_data_in_dict(tmp_dir):
|
||||
m1 = [{"accuracy": 1, "loss": 2}, {"accuracy": 3, "loss": 4}]
|
||||
m2 = [{"x": 1}, {"x": 2}]
|
||||
dmetric = OrderedDict([("t1", m1), ("t2", m2)])
|
||||
|
||||
assert _find_data(dmetric) == m1
|
||||
assert _find_data(dmetric, fields={"x"}) == m2
|
||||
|
||||
|
||||
def test_group_plots_data():
|
||||
error = FileNotFoundError()
|
||||
data = {
|
||||
"v2": {
|
||||
"data": {
|
||||
"file.json": {"data": [{"y": 2}, {"y": 3}], "props": {}},
|
||||
"other_file.jpg": {"data": "content"},
|
||||
}
|
||||
},
|
||||
"v1": {
|
||||
"data": {"file.json": {"data": [{"y": 4}, {"y": 5}], "props": {}}}
|
||||
},
|
||||
"workspace": {
|
||||
"data": {
|
||||
"file.json": {"error": error, "props": {}},
|
||||
"other_file.jpg": {"data": "content2"},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
results = group_by_filename(data)
|
||||
assert {
|
||||
"v2": {
|
||||
"data": {
|
||||
"file.json": {"data": [{"y": 2}, {"y": 3}], "props": {}},
|
||||
}
|
||||
},
|
||||
"v1": {
|
||||
"data": {"file.json": {"data": [{"y": 4}, {"y": 5}], "props": {}}}
|
||||
},
|
||||
"workspace": {
|
||||
"data": {
|
||||
"file.json": {"error": error, "props": {}},
|
||||
}
|
||||
},
|
||||
} in results
|
||||
assert {
|
||||
"v2": {
|
||||
"data": {
|
||||
"other_file.jpg": {"data": "content"},
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"data": {
|
||||
"other_file.jpg": {"data": "content2"},
|
||||
}
|
||||
},
|
||||
} in results
|
||||
|
||||
|
||||
def test_one_column(tmp_dir, scm, dvc):
|
||||
props = {
|
||||
"x_label": "x_title",
|
||||
"y_label": "y_title",
|
||||
"title": "mytitle",
|
||||
}
|
||||
data = {
|
||||
"workspace": {
|
||||
"data": {
|
||||
"file.json": {"data": [{"val": 2}, {"val": 3}], "props": props}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["title"] == "mytitle"
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"val": 2, INDEX_FIELD: 0, REVISION_FIELD: "workspace"},
|
||||
{"val": 3, INDEX_FIELD: 1, REVISION_FIELD: "workspace"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
|
||||
assert plot_content["encoding"]["y"]["field"] == "val"
|
||||
assert plot_content["encoding"]["x"]["title"] == "x_title"
|
||||
assert plot_content["encoding"]["y"]["title"] == "y_title"
|
||||
|
||||
|
||||
def test_multiple_columns(tmp_dir, scm, dvc):
|
||||
metric = [
|
||||
OrderedDict([("first_val", 100), ("second_val", 100), ("val", 2)]),
|
||||
OrderedDict([("first_val", 200), ("second_val", 300), ("val", 3)]),
|
||||
]
|
||||
|
||||
data = {
|
||||
"workspace": {"data": {"file.json": {"data": metric, "props": {}}}}
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{
|
||||
"val": 2,
|
||||
INDEX_FIELD: 0,
|
||||
REVISION_FIELD: "workspace",
|
||||
"first_val": 100,
|
||||
"second_val": 100,
|
||||
},
|
||||
{
|
||||
"val": 3,
|
||||
INDEX_FIELD: 1,
|
||||
REVISION_FIELD: "workspace",
|
||||
"first_val": 200,
|
||||
"second_val": 300,
|
||||
},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
|
||||
assert plot_content["encoding"]["y"]["field"] == "val"
|
||||
|
||||
|
||||
def test_choose_axes(tmp_dir, scm, dvc):
|
||||
metric = [
|
||||
OrderedDict([("first_val", 100), ("second_val", 100), ("val", 2)]),
|
||||
OrderedDict([("first_val", 200), ("second_val", 300), ("val", 3)]),
|
||||
]
|
||||
|
||||
props = {"x": "first_val", "y": "second_val"}
|
||||
|
||||
data = {
|
||||
"workspace": {"data": {"file.json": {"data": metric, "props": props}}}
|
||||
}
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{
|
||||
"val": 2,
|
||||
REVISION_FIELD: "workspace",
|
||||
"first_val": 100,
|
||||
"second_val": 100,
|
||||
},
|
||||
{
|
||||
"val": 3,
|
||||
REVISION_FIELD: "workspace",
|
||||
"first_val": 200,
|
||||
"second_val": 300,
|
||||
},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == "first_val"
|
||||
assert plot_content["encoding"]["y"]["field"] == "second_val"
|
||||
|
||||
|
||||
def test_confusion(tmp_dir, dvc):
|
||||
confusion_matrix = [
|
||||
{"predicted": "B", "actual": "A"},
|
||||
{"predicted": "A", "actual": "A"},
|
||||
]
|
||||
props = {"template": "confusion", "x": "predicted", "y": "actual"}
|
||||
|
||||
data = {
|
||||
"workspace": {
|
||||
"data": {"file.json": {"data": confusion_matrix, "props": props}}
|
||||
}
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"predicted": "B", "actual": "A", REVISION_FIELD: "workspace"},
|
||||
{"predicted": "A", "actual": "A", REVISION_FIELD: "workspace"},
|
||||
]
|
||||
assert plot_content["spec"]["transform"][0]["groupby"] == [
|
||||
"actual",
|
||||
"predicted",
|
||||
]
|
||||
assert plot_content["spec"]["encoding"]["x"]["field"] == "predicted"
|
||||
assert plot_content["spec"]["encoding"]["y"]["field"] == "actual"
|
||||
|
||||
|
||||
def test_multiple_revs_default(tmp_dir, scm, dvc):
|
||||
metric_1 = [{"y": 2}, {"y": 3}]
|
||||
metric_2 = [{"y": 3}, {"y": 5}]
|
||||
metric_3 = [{"y": 5}, {"y": 6}]
|
||||
|
||||
data = {
|
||||
"HEAD": {
|
||||
"data": {
|
||||
"file.json": {"data": metric_3, "props": {"fields": {"y"}}}
|
||||
}
|
||||
},
|
||||
"v2": {
|
||||
"data": {
|
||||
"file.json": {"data": metric_2, "props": {"fields": {"y"}}}
|
||||
}
|
||||
},
|
||||
"v1": {
|
||||
"data": {
|
||||
"file.json": {"data": metric_1, "props": {"fields": {"y"}}}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"y": 5, INDEX_FIELD: 0, REVISION_FIELD: "HEAD"},
|
||||
{"y": 6, INDEX_FIELD: 1, REVISION_FIELD: "HEAD"},
|
||||
{"y": 3, INDEX_FIELD: 0, REVISION_FIELD: "v2"},
|
||||
{"y": 5, INDEX_FIELD: 1, REVISION_FIELD: "v2"},
|
||||
{"y": 2, INDEX_FIELD: 0, REVISION_FIELD: "v1"},
|
||||
{"y": 3, INDEX_FIELD: 1, REVISION_FIELD: "v1"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
|
||||
assert plot_content["encoding"]["y"]["field"] == "y"
|
||||
|
||||
|
||||
def test_metric_missing(tmp_dir, scm, dvc, caplog):
|
||||
|
||||
metric = [{"y": 2}, {"y": 3}]
|
||||
data = {
|
||||
"v2": {"data": {"file.json": {"data": metric, "props": {}}}},
|
||||
"workspace": {
|
||||
"data": {"file.json": {"error": FileNotFoundError(), "props": {}}}
|
||||
},
|
||||
}
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"y": 2, INDEX_FIELD: 0, REVISION_FIELD: "v2"},
|
||||
{"y": 3, INDEX_FIELD: 1, REVISION_FIELD: "v2"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
|
||||
assert plot_content["encoding"]["y"]["field"] == "y"
|
||||
|
||||
|
||||
def test_custom_template(tmp_dir, scm, dvc, custom_template):
|
||||
metric = [{"a": 1, "b": 2}, {"a": 2, "b": 3}]
|
||||
props = {"template": os.fspath(custom_template), "x": "a", "y": "b"}
|
||||
data = {
|
||||
"workspace": {"data": {"file.json": {"data": metric, "props": props}}}
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"a": 1, "b": 2, REVISION_FIELD: "workspace"},
|
||||
{"a": 2, "b": 3, REVISION_FIELD: "workspace"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == "a"
|
||||
assert plot_content["encoding"]["y"]["field"] == "b"
|
||||
|
||||
|
||||
def test_raise_on_no_template(tmp_dir, dvc):
|
||||
metric = [{"val": 2}, {"val": 3}]
|
||||
props = {"template": "non_existing_template.json"}
|
||||
data = {
|
||||
"workspace": {"data": {"file.json": {"data": metric, "props": props}}}
|
||||
}
|
||||
|
||||
with pytest.raises(TemplateNotFoundError):
|
||||
VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
|
||||
def test_bad_template(tmp_dir, dvc):
|
||||
metric = [{"val": 2}, {"val": 3}]
|
||||
tmp_dir.gen("template.json", json.dumps({"a": "b", "c": "d"}))
|
||||
props = {"template": "template.json"}
|
||||
data = {
|
||||
"workspace": {"data": {"file.json": {"data": metric, "props": props}}}
|
||||
}
|
||||
|
||||
with pytest.raises(BadTemplateError):
|
||||
VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
|
||||
def test_plot_choose_columns(tmp_dir, scm, dvc, custom_template):
|
||||
metric = [{"a": 1, "b": 2, "c": 3}, {"a": 2, "b": 3, "c": 4}]
|
||||
props = {
|
||||
"template": os.fspath(custom_template),
|
||||
"fields": {"b", "c"},
|
||||
"x": "b",
|
||||
"y": "c",
|
||||
}
|
||||
data = {
|
||||
"workspace": {"data": {"file.json": {"data": metric, "props": props}}}
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
plot_content = json.loads(plot_string)
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"b": 2, "c": 3, REVISION_FIELD: "workspace"},
|
||||
{"b": 3, "c": 4, REVISION_FIELD: "workspace"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == "b"
|
||||
assert plot_content["encoding"]["y"]["field"] == "c"
|
||||
|
||||
|
||||
def test_plot_default_choose_column(tmp_dir, scm, dvc):
|
||||
metric = [{"a": 1, "b": 2, "c": 3}, {"a": 2, "b": 3, "c": 4}]
|
||||
data = {
|
||||
"workspace": {
|
||||
"data": {"file.json": {"data": metric, "props": {"fields": {"b"}}}}
|
||||
}
|
||||
}
|
||||
|
||||
plot_string = VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
plot_content = json.loads(plot_string)
|
||||
|
||||
assert plot_content["data"]["values"] == [
|
||||
{INDEX_FIELD: 0, "b": 2, REVISION_FIELD: "workspace"},
|
||||
{INDEX_FIELD: 1, "b": 3, REVISION_FIELD: "workspace"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
|
||||
assert plot_content["encoding"]["y"]["field"] == "b"
|
||||
|
||||
|
||||
def test_raise_on_wrong_field(tmp_dir, scm, dvc):
|
||||
metric = [{"val": 2}, {"val": 3}]
|
||||
data = {
|
||||
"workspace": {
|
||||
"data": {"file.json": {"data": metric, "props": {"x": "no_val"}}}
|
||||
}
|
||||
}
|
||||
|
||||
with pytest.raises(NoFieldInDataError):
|
||||
VegaRenderer(data, dvc.plots.templates).get_vega()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"extension, matches",
|
||||
(
|
||||
(".csv", True),
|
||||
(".json", True),
|
||||
(".tsv", True),
|
||||
(".yaml", True),
|
||||
(".jpg", False),
|
||||
(".gif", False),
|
||||
(".jpeg", False),
|
||||
(".png", False),
|
||||
),
|
||||
)
|
||||
def test_matches(extension, matches):
|
||||
filename = "file" + extension
|
||||
data = {
|
||||
"HEAD": {"data": {filename: {}}},
|
||||
"v1": {"data": {filename: {}}},
|
||||
}
|
||||
assert VegaRenderer.matches(data) == matches
|
||||
|
||||
|
||||
def test_find_vega(tmp_dir, dvc):
|
||||
data = {
|
||||
"HEAD": {
|
||||
"data": {
|
||||
"file.json": {
|
||||
"data": [{"y": 5}, {"y": 6}],
|
||||
"props": {"fields": {"y"}},
|
||||
},
|
||||
"other_file.jpg": {"data": b"content"},
|
||||
}
|
||||
},
|
||||
"v2": {
|
||||
"data": {
|
||||
"file.json": {
|
||||
"data": [{"y": 3}, {"y": 5}],
|
||||
"props": {"fields": {"y"}},
|
||||
},
|
||||
"other_file.jpg": {"data": b"content2"},
|
||||
}
|
||||
},
|
||||
"v1": {
|
||||
"data": {
|
||||
"file.json": {
|
||||
"data": [{"y": 2}, {"y": 3}],
|
||||
"props": {"fields": {"y"}},
|
||||
},
|
||||
"another.gif": {"data": b"content2"},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
plot_content = json.loads(find_vega(dvc, data, "file.json"))
|
||||
|
||||
assert plot_content["data"]["values"] == [
|
||||
{"y": 5, INDEX_FIELD: 0, REVISION_FIELD: "HEAD"},
|
||||
{"y": 6, INDEX_FIELD: 1, REVISION_FIELD: "HEAD"},
|
||||
{"y": 3, INDEX_FIELD: 0, REVISION_FIELD: "v2"},
|
||||
{"y": 5, INDEX_FIELD: 1, REVISION_FIELD: "v2"},
|
||||
{"y": 2, INDEX_FIELD: 0, REVISION_FIELD: "v1"},
|
||||
{"y": 3, INDEX_FIELD: 1, REVISION_FIELD: "v1"},
|
||||
]
|
||||
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
|
||||
assert plot_content["encoding"]["y"]["field"] == "y"
|
||||
Reference in New Issue
Block a user