plots: load templates from package (#6550)

* plots: vega: make interactive linear default plot

* plots: stop dumping default templates

* plots: move templates to package initial

* plots: update default plots schema

* plots: write defaults on init

* templates: migrate to importlib.resources

* fixup

* fixup
This commit is contained in:
Paweł Redzyński
2021-10-14 08:40:31 +02:00
committed by GitHub
parent b8478a8b04
commit a1b146669c
15 changed files with 656 additions and 512 deletions
+2
View File
@@ -1 +1,3 @@
include LICENSE
recursive-include requirements *.txt
recursive-include dvc/repo/plots/templates *.json
+1 -1
View File
@@ -1,7 +1,7 @@
import os.path
from typing import Dict, List
import dpath
import dpath.util
def get_files(data: Dict) -> List:
+1 -1
View File
@@ -147,7 +147,7 @@ class VegaRenderer(Renderer):
def get_vega(self) -> Optional[str]:
props = self._squash_props()
template = self.templates.load(props.get("template") or "default")
template = self.templates.load(props.get("template", None))
if not props.get("x") and template.has_anchor("x"):
props["append_index"] = True
+1 -1
View File
@@ -161,7 +161,7 @@ class Plots:
props = props or {}
template = props.get("template")
if template:
self.templates.get_template(template)
self.templates.load(template)
(out,) = self.repo.find_outs_by_path(path)
if not out.plot and unset is not None:
+91 -489
View File
@@ -1,15 +1,23 @@
import json
import os
from typing import Any, Dict, Optional
from typing import TYPE_CHECKING, Iterable, Optional
try:
import importlib_resources
except ImportError:
import importlib.resources as importlib_resources # type: ignore[no-redef]
from funcy import cached_property
from dvc.exceptions import DvcException
if TYPE_CHECKING:
from dvc.types import StrPath
class TemplateNotFoundError(DvcException):
def __init__(self, path):
super().__init__(f"Template '{path}' not found.")
def __init__(self, name):
super().__init__(f"Template '{name}' not found.")
class BadTemplateError(DvcException):
@@ -29,25 +37,9 @@ class Template:
EXTENSION = ".json"
ANCHOR = "<DVC_METRIC_{}>"
DEFAULT_CONTENT: Optional[Dict[str, Any]] = None
DEFAULT_NAME: Optional[str] = None
def __init__(self, content=None, name=None):
if content:
self.content = content
else:
self.content = (
json.dumps(
self.DEFAULT_CONTENT,
indent=self.INDENT,
separators=self.SEPARATORS,
)
+ "\n"
)
self.name = name or self.DEFAULT_NAME
assert self.content and self.name
self.filename = self.name + self.EXTENSION
def __init__(self, content, name):
self.content = content
self.name = name
def render(self, data, props=None):
props = props or {}
@@ -55,7 +47,7 @@ class Template:
if self._anchor_str("data") not in self.content:
anchor = self.anchor("data")
raise BadTemplateError(
f"Template '{self.filename}' is not using '{anchor}' anchor"
f"Template '{self.name}' is not using '{anchor}' anchor"
)
if props.get("x"):
@@ -106,466 +98,86 @@ class Template:
raise NoFieldInDataError(field)
class DefaultTemplate(Template):
DEFAULT_NAME = "default"
DEFAULT_CONTENT = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"values": Template.anchor("data")},
"title": Template.anchor("title"),
"width": 300,
"height": 300,
"mark": {"type": "line"},
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
"title": Template.anchor("x_label"),
},
"y": {
"field": Template.anchor("y"),
"type": "quantitative",
"title": Template.anchor("y_label"),
"scale": {"zero": False},
},
"color": {"field": "rev", "type": "nominal"},
},
}
class ConfusionTemplate(Template):
DEFAULT_NAME = "confusion"
DEFAULT_CONTENT = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"values": Template.anchor("data")},
"title": Template.anchor("title"),
"facet": {"field": "rev", "type": "nominal"},
"spec": {
"transform": [
{
"aggregate": [{"op": "count", "as": "xy_count"}],
"groupby": [Template.anchor("y"), Template.anchor("x")],
},
{
"impute": "xy_count",
"groupby": ["rev", Template.anchor("y")],
"key": Template.anchor("x"),
"value": 0,
},
{
"impute": "xy_count",
"groupby": ["rev", Template.anchor("x")],
"key": Template.anchor("y"),
"value": 0,
},
{
"joinaggregate": [
{"op": "max", "field": "xy_count", "as": "max_count"}
],
"groupby": [],
},
{
"calculate": "datum.xy_count / datum.max_count",
"as": "percent_of_max",
},
],
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "nominal",
"sort": "ascending",
"title": Template.anchor("x_label"),
},
"y": {
"field": Template.anchor("y"),
"type": "nominal",
"sort": "ascending",
"title": Template.anchor("y_label"),
},
},
"layer": [
{
"mark": "rect",
"width": 300,
"height": 300,
"encoding": {
"color": {
"field": "xy_count",
"type": "quantitative",
"title": "",
"scale": {"domainMin": 0, "nice": True},
}
},
},
{
"mark": "text",
"encoding": {
"text": {"field": "xy_count", "type": "quantitative"},
"color": {
"condition": {
"test": "datum.percent_of_max > 0.5",
"value": "white",
},
"value": "black",
},
},
},
],
},
}
class NormalizedConfusionTemplate(Template):
DEFAULT_NAME = "confusion_normalized"
DEFAULT_CONTENT = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"values": Template.anchor("data")},
"title": Template.anchor("title"),
"facet": {"field": "rev", "type": "nominal"},
"spec": {
"transform": [
{
"aggregate": [{"op": "count", "as": "xy_count"}],
"groupby": [Template.anchor("y"), Template.anchor("x")],
},
{
"impute": "xy_count",
"groupby": ["rev", Template.anchor("y")],
"key": Template.anchor("x"),
"value": 0,
},
{
"impute": "xy_count",
"groupby": ["rev", Template.anchor("x")],
"key": Template.anchor("y"),
"value": 0,
},
{
"joinaggregate": [
{"op": "sum", "field": "xy_count", "as": "sum_y"}
],
"groupby": [Template.anchor("y")],
},
{
"calculate": "datum.xy_count / datum.sum_y",
"as": "percent_of_y",
},
],
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "nominal",
"sort": "ascending",
"title": Template.anchor("x_label"),
},
"y": {
"field": Template.anchor("y"),
"type": "nominal",
"sort": "ascending",
"title": Template.anchor("y_label"),
},
},
"layer": [
{
"mark": "rect",
"width": 300,
"height": 300,
"encoding": {
"color": {
"field": "percent_of_y",
"type": "quantitative",
"title": "",
"scale": {"domain": [0, 1]},
}
},
},
{
"mark": "text",
"encoding": {
"text": {
"field": "percent_of_y",
"type": "quantitative",
"format": ".2f",
},
"color": {
"condition": {
"test": "datum.percent_of_y > 0.5",
"value": "white",
},
"value": "black",
},
},
},
],
},
}
class ScatterTemplate(Template):
DEFAULT_NAME = "scatter"
DEFAULT_CONTENT = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"values": Template.anchor("data")},
"title": Template.anchor("title"),
"width": 300,
"height": 300,
"layer": [
{
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
"title": Template.anchor("x_label"),
},
"y": {
"field": Template.anchor("y"),
"type": "quantitative",
"title": Template.anchor("y_label"),
"scale": {"zero": False},
},
"color": {"field": "rev", "type": "nominal"},
},
"layer": [
{"mark": "point"},
{
"selection": {
"label": {
"type": "single",
"nearest": True,
"on": "mouseover",
"encodings": ["x"],
"empty": "none",
"clear": "mouseout",
}
},
"mark": "point",
"encoding": {
"opacity": {
"condition": {
"selection": "label",
"value": 1,
},
"value": 0,
}
},
},
],
},
{
"transform": [{"filter": {"selection": "label"}}],
"layer": [
{
"encoding": {
"text": {
"type": "quantitative",
"field": Template.anchor("y"),
},
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
},
"y": {
"field": Template.anchor("y"),
"type": "quantitative",
},
},
"layer": [
{
"mark": {
"type": "text",
"align": "left",
"dx": 5,
"dy": -5,
},
"encoding": {
"color": {
"type": "nominal",
"field": "rev",
}
},
}
],
}
],
},
],
}
class SmoothLinearTemplate(Template):
DEFAULT_NAME = "smooth"
DEFAULT_CONTENT = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"values": Template.anchor("data")},
"title": Template.anchor("title"),
"mark": {"type": "line"},
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
"title": Template.anchor("x_label"),
},
"y": {
"field": Template.anchor("y"),
"type": "quantitative",
"title": Template.anchor("y_label"),
"scale": {"zero": False},
},
"color": {"field": "rev", "type": "nominal"},
},
"transform": [
{
"loess": Template.anchor("y"),
"on": Template.anchor("x"),
"groupby": ["rev"],
"bandwidth": 0.3,
}
],
}
class LinearTemplate(Template):
DEFAULT_NAME = "linear"
DEFAULT_CONTENT = {
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"values": Template.anchor("data")},
"title": Template.anchor("title"),
"width": 300,
"height": 300,
"layer": [
{
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
"title": Template.anchor("x_label"),
},
"y": {
"field": Template.anchor("y"),
"type": "quantitative",
"title": Template.anchor("y_label"),
"scale": {"zero": False},
},
"color": {"field": "rev", "type": "nominal"},
},
"layer": [
{"mark": "line"},
{
"selection": {
"label": {
"type": "single",
"nearest": True,
"on": "mouseover",
"encodings": ["x"],
"empty": "none",
"clear": "mouseout",
}
},
"mark": "point",
"encoding": {
"opacity": {
"condition": {
"selection": "label",
"value": 1,
},
"value": 0,
}
},
},
],
},
{
"transform": [{"filter": {"selection": "label"}}],
"layer": [
{
"mark": {"type": "rule", "color": "gray"},
"encoding": {
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
}
},
},
{
"encoding": {
"text": {
"type": "quantitative",
"field": Template.anchor("y"),
},
"x": {
"field": Template.anchor("x"),
"type": "quantitative",
},
"y": {
"field": Template.anchor("y"),
"type": "quantitative",
},
},
"layer": [
{
"mark": {
"type": "text",
"align": "left",
"dx": 5,
"dy": -5,
},
"encoding": {
"color": {
"type": "nominal",
"field": "rev",
}
},
}
],
},
],
},
],
}
class PlotTemplates:
TEMPLATES_DIR = "plots"
TEMPLATES = [
DefaultTemplate,
LinearTemplate,
ConfusionTemplate,
NormalizedConfusionTemplate,
ScatterTemplate,
SmoothLinearTemplate,
]
PKG_TEMPLATES_DIR = "templates"
@cached_property
def templates_dir(self):
return os.path.join(self.dvc_dir, self.TEMPLATES_DIR)
def get_template(self, path):
if os.path.exists(path):
return path
@staticmethod
def _find(templates, template_name):
for template in templates:
if (
template_name == template
or template_name + ".json" == template
):
return template
return None
if self.dvc_dir and os.path.exists(self.dvc_dir):
t_path = os.path.join(self.templates_dir, path)
if os.path.exists(t_path):
return t_path
def _find_in_project(self, name: str) -> Optional["StrPath"]:
if os.path.exists(name):
return name
all_templates = [
if os.path.exists(self.templates_dir):
templates = [
os.path.join(root, file)
for root, _, files in os.walk(self.templates_dir)
for file in files
]
matches = [
template
for template in all_templates
if os.path.splitext(template)[0] == t_path
]
if matches:
assert len(matches) == 1
return matches[0]
found = self._find(templates, name)
if found:
return os.path.join(self.templates_dir, found)
return None
raise TemplateNotFoundError(path)
@staticmethod
def _get_templates() -> Iterable[str]:
if (
importlib_resources.files(__package__)
.joinpath(PlotTemplates.PKG_TEMPLATES_DIR)
.is_dir()
):
entries = (
importlib_resources.files(__package__)
.joinpath(PlotTemplates.PKG_TEMPLATES_DIR)
.iterdir()
)
return [entry.name for entry in entries]
return []
@staticmethod
def _load_from_pkg(name):
templates = PlotTemplates._get_templates()
found = PlotTemplates._find(templates, name)
if found:
return (
(
importlib_resources.files(__package__)
/ PlotTemplates.PKG_TEMPLATES_DIR
/ found
)
.read_bytes()
.decode("utf-8")
)
return None
def load(self, name: str = None) -> Template:
if name is not None:
template_path = self._find_in_project(name)
if template_path:
with open(template_path, "r") as fd:
content = fd.read()
return Template(content, name)
else:
name = "linear"
content = self._load_from_pkg(name)
if content:
return Template(content, name)
raise TemplateNotFoundError(name)
def __init__(self, dvc_dir):
self.dvc_dir = dvc_dir
@@ -574,24 +186,14 @@ class PlotTemplates:
from dvc.utils.fs import makedirs
makedirs(self.templates_dir, exist_ok=True)
for t in self.TEMPLATES:
self._dump(t())
def _dump(self, template):
path = os.path.join(self.templates_dir, template.filename)
with open(path, "w") as fd:
fd.write(template.content)
def load(self, name):
try:
path = self.get_template(name)
with open(path) as fd:
content = fd.read()
return Template(content, name=name)
except TemplateNotFoundError:
for template in self.TEMPLATES:
if template.DEFAULT_NAME == name:
return template()
raise
templates = self._get_templates()
for template in templates:
content = (
importlib_resources.files(__package__)
.joinpath(PlotTemplates.PKG_TEMPLATES_DIR)
.joinpath(template)
.read_text()
)
with open(os.path.join(self.templates_dir, template), "w") as fd:
fd.write(content)
+107
View File
@@ -0,0 +1,107 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": "<DVC_METRIC_DATA>"
},
"title": "<DVC_METRIC_TITLE>",
"facet": {
"field": "rev",
"type": "nominal"
},
"spec": {
"transform": [
{
"aggregate": [
{
"op": "count",
"as": "xy_count"
}
],
"groupby": [
"<DVC_METRIC_Y>",
"<DVC_METRIC_X>"
]
},
{
"impute": "xy_count",
"groupby": [
"rev",
"<DVC_METRIC_Y>"
],
"key": "<DVC_METRIC_X>",
"value": 0
},
{
"impute": "xy_count",
"groupby": [
"rev",
"<DVC_METRIC_X>"
],
"key": "<DVC_METRIC_Y>",
"value": 0
},
{
"joinaggregate": [
{
"op": "max",
"field": "xy_count",
"as": "max_count"
}
],
"groupby": []
},
{
"calculate": "datum.xy_count / datum.max_count",
"as": "percent_of_max"
}
],
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "nominal",
"sort": "ascending",
"title": "<DVC_METRIC_X_LABEL>"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "nominal",
"sort": "ascending",
"title": "<DVC_METRIC_Y_LABEL>"
}
},
"layer": [
{
"mark": "rect",
"width": 300,
"height": 300,
"encoding": {
"color": {
"field": "xy_count",
"type": "quantitative",
"title": "",
"scale": {
"domainMin": 0,
"nice": true
}
}
}
},
{
"mark": "text",
"encoding": {
"text": {
"field": "xy_count",
"type": "quantitative"
},
"color": {
"condition": {
"test": "datum.percent_of_max > 0.5",
"value": "white"
},
"value": "black"
}
}
}
]
}
}
@@ -0,0 +1,112 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": "<DVC_METRIC_DATA>"
},
"title": "<DVC_METRIC_TITLE>",
"facet": {
"field": "rev",
"type": "nominal"
},
"spec": {
"transform": [
{
"aggregate": [
{
"op": "count",
"as": "xy_count"
}
],
"groupby": [
"<DVC_METRIC_Y>",
"<DVC_METRIC_X>"
]
},
{
"impute": "xy_count",
"groupby": [
"rev",
"<DVC_METRIC_Y>"
],
"key": "<DVC_METRIC_X>",
"value": 0
},
{
"impute": "xy_count",
"groupby": [
"rev",
"<DVC_METRIC_X>"
],
"key": "<DVC_METRIC_Y>",
"value": 0
},
{
"joinaggregate": [
{
"op": "sum",
"field": "xy_count",
"as": "sum_y"
}
],
"groupby": [
"<DVC_METRIC_Y>"
]
},
{
"calculate": "datum.xy_count / datum.sum_y",
"as": "percent_of_y"
}
],
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "nominal",
"sort": "ascending",
"title": "<DVC_METRIC_X_LABEL>"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "nominal",
"sort": "ascending",
"title": "<DVC_METRIC_Y_LABEL>"
}
},
"layer": [
{
"mark": "rect",
"width": 300,
"height": 300,
"encoding": {
"color": {
"field": "percent_of_y",
"type": "quantitative",
"title": "",
"scale": {
"domain": [
0,
1
]
}
}
}
},
{
"mark": "text",
"encoding": {
"text": {
"field": "percent_of_y",
"type": "quantitative",
"format": ".2f"
},
"color": {
"condition": {
"test": "datum.percent_of_y > 0.5",
"value": "white"
},
"value": "black"
}
}
}
]
}
}
+116
View File
@@ -0,0 +1,116 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": "<DVC_METRIC_DATA>"
},
"title": "<DVC_METRIC_TITLE>",
"width": 300,
"height": 300,
"layer": [
{
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative",
"title": "<DVC_METRIC_X_LABEL>"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "quantitative",
"title": "<DVC_METRIC_Y_LABEL>",
"scale": {
"zero": false
}
},
"color": {
"field": "rev",
"type": "nominal"
}
},
"layer": [
{
"mark": "line"
},
{
"selection": {
"label": {
"type": "single",
"nearest": true,
"on": "mouseover",
"encodings": [
"x"
],
"empty": "none",
"clear": "mouseout"
}
},
"mark": "point",
"encoding": {
"opacity": {
"condition": {
"selection": "label",
"value": 1
},
"value": 0
}
}
}
]
},
{
"transform": [
{
"filter": {
"selection": "label"
}
}
],
"layer": [
{
"mark": {
"type": "rule",
"color": "gray"
},
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative"
}
}
},
{
"encoding": {
"text": {
"type": "quantitative",
"field": "<DVC_METRIC_Y>"
},
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "quantitative"
}
},
"layer": [
{
"mark": {
"type": "text",
"align": "left",
"dx": 5,
"dy": -5
},
"encoding": {
"color": {
"type": "nominal",
"field": "rev"
}
}
}
]
}
]
}
]
}
+104
View File
@@ -0,0 +1,104 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": "<DVC_METRIC_DATA>"
},
"title": "<DVC_METRIC_TITLE>",
"width": 300,
"height": 300,
"layer": [
{
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative",
"title": "<DVC_METRIC_X_LABEL>"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "quantitative",
"title": "<DVC_METRIC_Y_LABEL>",
"scale": {
"zero": false
}
},
"color": {
"field": "rev",
"type": "nominal"
}
},
"layer": [
{
"mark": "point"
},
{
"selection": {
"label": {
"type": "single",
"nearest": true,
"on": "mouseover",
"encodings": [
"x"
],
"empty": "none",
"clear": "mouseout"
}
},
"mark": "point",
"encoding": {
"opacity": {
"condition": {
"selection": "label",
"value": 1
},
"value": 0
}
}
}
]
},
{
"transform": [
{
"filter": {
"selection": "label"
}
}
],
"layer": [
{
"encoding": {
"text": {
"type": "quantitative",
"field": "<DVC_METRIC_Y>"
},
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "quantitative"
}
},
"layer": [
{
"mark": {
"type": "text",
"align": "left",
"dx": 5,
"dy": -5
},
"encoding": {
"color": {
"type": "nominal",
"field": "rev"
}
}
}
]
}
]
}
]
}
+31
View File
@@ -0,0 +1,31 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": "<DVC_METRIC_DATA>"
},
"title": "<DVC_METRIC_TITLE>",
"width": 300,
"height": 300,
"mark": {
"type": "line"
},
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative",
"title": "<DVC_METRIC_X_LABEL>"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "quantitative",
"title": "<DVC_METRIC_Y_LABEL>",
"scale": {
"zero": false
}
},
"color": {
"field": "rev",
"type": "nominal"
}
}
}
+39
View File
@@ -0,0 +1,39 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": "<DVC_METRIC_DATA>"
},
"title": "<DVC_METRIC_TITLE>",
"mark": {
"type": "line"
},
"encoding": {
"x": {
"field": "<DVC_METRIC_X>",
"type": "quantitative",
"title": "<DVC_METRIC_X_LABEL>"
},
"y": {
"field": "<DVC_METRIC_Y>",
"type": "quantitative",
"title": "<DVC_METRIC_Y_LABEL>",
"scale": {
"zero": false
}
},
"color": {
"field": "rev",
"type": "nominal"
}
},
"transform": [
{
"loess": "<DVC_METRIC_Y>",
"on": "<DVC_METRIC_X>",
"groupby": [
"rev"
],
"bandwidth": 0.3
}
]
}
+1
View File
@@ -64,6 +64,7 @@ install_requires =
psutil>=5.8.0
pydot>=1.2.4
importlib-metadata>=1.4; python_version < '3.8'
importlib-resources>=5.2.2; python_version < '3.9'
flatten_dict>=0.4.1,<1
tabulate>=0.8.7
pygtrie>=2.3.2
+11 -2
View File
@@ -161,10 +161,19 @@ def pytest_configure(config):
@pytest.fixture()
def custom_template(tmp_dir, dvc):
import shutil
try:
import importlib_resources
except ImportError:
import importlib.resources as importlib_resources
content = (
importlib_resources.files("dvc.repo.plots")
/ "templates"
/ "simple.json"
).read_text()
template = tmp_dir / "custom_template.json"
shutil.copy(tmp_dir / ".dvc" / "plots" / "default.json", template)
template.write_text(content)
return template
+6 -2
View File
@@ -63,5 +63,9 @@ def test_render(tmp_dir, dvc):
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()
def clean(txt: str) -> str:
return txt.replace("\n", "").replace("\r", "").replace(" ", "")
assert clean(file_vega) in clean(index_content)
assert clean(some_vega) in clean(index_content)
+33 -16
View File
@@ -3,6 +3,7 @@ import os
from collections import OrderedDict
import pytest
from funcy import first
from dvc.render.utils import find_vega, group_by_filename
from dvc.render.vega import (
@@ -117,10 +118,12 @@ def test_one_column(tmp_dir, scm, dvc):
{"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"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == INDEX_FIELD
)
assert first(plot_content["layer"])["encoding"]["y"]["field"] == "val"
assert first(plot_content["layer"])["encoding"]["x"]["title"] == "x_title"
assert first(plot_content["layer"])["encoding"]["y"]["title"] == "y_title"
def test_multiple_columns(tmp_dir, scm, dvc):
@@ -152,8 +155,10 @@ def test_multiple_columns(tmp_dir, scm, dvc):
"second_val": 300,
},
]
assert plot_content["encoding"]["x"]["field"] == INDEX_FIELD
assert plot_content["encoding"]["y"]["field"] == "val"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == INDEX_FIELD
)
assert first(plot_content["layer"])["encoding"]["y"]["field"] == "val"
def test_choose_axes(tmp_dir, scm, dvc):
@@ -184,8 +189,12 @@ def test_choose_axes(tmp_dir, scm, dvc):
"second_val": 300,
},
]
assert plot_content["encoding"]["x"]["field"] == "first_val"
assert plot_content["encoding"]["y"]["field"] == "second_val"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == "first_val"
)
assert (
first(plot_content["layer"])["encoding"]["y"]["field"] == "second_val"
)
def test_confusion(tmp_dir, dvc):
@@ -250,8 +259,10 @@ def test_multiple_revs_default(tmp_dir, scm, dvc):
{"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"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == INDEX_FIELD
)
assert first(plot_content["layer"])["encoding"]["y"]["field"] == "y"
def test_metric_missing(tmp_dir, scm, dvc, caplog):
@@ -270,8 +281,10 @@ def test_metric_missing(tmp_dir, scm, dvc, caplog):
{"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"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == INDEX_FIELD
)
assert first(plot_content["layer"])["encoding"]["y"]["field"] == "y"
def test_custom_template(tmp_dir, scm, dvc, custom_template):
@@ -353,8 +366,10 @@ def test_plot_default_choose_column(tmp_dir, scm, dvc):
{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"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == INDEX_FIELD
)
assert first(plot_content["layer"])["encoding"]["y"]["field"] == "b"
def test_raise_on_wrong_field(tmp_dir, scm, dvc):
@@ -432,5 +447,7 @@ def test_find_vega(tmp_dir, dvc):
{"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"
assert (
first(plot_content["layer"])["encoding"]["x"]["field"] == INDEX_FIELD
)
assert first(plot_content["layer"])["encoding"]["y"]["field"] == "y"