From 475575cbcc14ad6c3c0b1a42ad6e7686de02a963 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Tue, 14 Jun 2022 12:53:35 +0200 Subject: [PATCH 1/2] Add TableRenderer. Make `tabulate` optional dependency. Add table styles in `PAGE_HTML`. Remove `with_metrics` method from HTML. --- pyproject.toml | 4 ++++ setup.cfg | 9 ++++++--- src/dvc_render/html.py | 23 +++++------------------ src/dvc_render/table.py | 33 +++++++++++++++++++++++++++++++++ tests/test_html.py | 4 +++- tests/test_table.py | 15 +++++++++++++++ 6 files changed, 66 insertions(+), 22 deletions(-) create mode 100644 src/dvc_render/table.py create mode 100644 tests/test_table.py diff --git a/pyproject.toml b/pyproject.toml index 199bd25..2932d77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,10 @@ warn_redundant_casts = true warn_unreachable = true files = ["src", "tests"] +[[tool.mypy.overrides]] +module = "tabulate" +ignore_missing_imports = true + [tool.pylint.message_control] enable = ["c-extension-no-member", "no-else-return"] disable = ["missing-module-docstring", "missing-class-docstring", "invalid-name", "R0801"] diff --git a/setup.cfg b/setup.cfg index 05a611d..eacffba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,11 +23,11 @@ zip_safe = False package_dir= =src packages = find: -install_requires= - funcy>=1.17 - tabulate>=0.8.7 + [options.extras_require] +table = + tabulate>=0.8.7 docs = mkdocs==1.3.0 mkdocs-gen-files==0.3.4 @@ -35,6 +35,8 @@ docs = mkdocs-section-index==0.3.4 mkdocstrings-python==0.7.0 tests = + %(table)s + funcy>=1.17 pytest==7.1.2 pytest-sugar==0.9.4 pytest-cov==3.0.0 @@ -43,6 +45,7 @@ tests = mypy==0.961 pytest-test-utils>=0.0.6 dev = + %(table)s %(tests)s %(docs)s diff --git a/src/dvc_render/html.py b/src/dvc_render/html.py index f55aed1..7ae1ac2 100644 --- a/src/dvc_render/html.py +++ b/src/dvc_render/html.py @@ -1,8 +1,6 @@ from pathlib import Path from typing import TYPE_CHECKING, Dict, List, Optional -import tabulate # type: ignore - from .exceptions import DvcRenderException if TYPE_CHECKING: @@ -15,6 +13,11 @@ {refresh_tag} DVC Plot {scripts} + {plot_divs} @@ -50,21 +53,6 @@ def __init__( if refresh_seconds is not None: self.refresh_tag = self.REFRESH_TAG.format(refresh_seconds) - def with_metrics(self, metrics: Dict[str, Dict]) -> "HTML": - "Adds metrics element." - header: List[str] = [] - rows: List[List[str]] = [] - - for _, rev_data in metrics.items(): - for _, data in rev_data.items(): - if not header: - header.extend(sorted(data.keys())) - - rows.append([data[key] for key in header]) - - self.elements.append(tabulate.tabulate(rows, header, tablefmt="html")) - return self - def with_scripts(self, scripts: str) -> "HTML": "Extend scripts element." if scripts not in self.scripts: @@ -108,7 +96,6 @@ def render_html( document = HTML(page_html, refresh_seconds=refresh_seconds) if metrics: - document.with_metrics(metrics) document.with_element("
") for renderer in renderers: diff --git a/src/dvc_render/table.py b/src/dvc_render/table.py new file mode 100644 index 0000000..3177104 --- /dev/null +++ b/src/dvc_render/table.py @@ -0,0 +1,33 @@ +from .base import Renderer + +try: + from tabulate import tabulate +except ImportError: + tabulate = None + + +class TableRenderer(Renderer): + """Renderer for tables.""" + + TYPE = "table" + DIV = """ +
+

{id}

+
+ {partial} +
+
""" + + SCRIPTS = "" + + EXTENSIONS = {".yml", ".yaml", ".json"} + + def partial_html(self, **kwargs) -> str: + # From list of dicts to dict of lists + data = { + k: [datapoint[k] for datapoint in self.datapoints] + for k in self.datapoints[0] + } + if tabulate is None: + raise ImportError(f"{self.__class__} requires `tabulate`.") + return tabulate(data, headers="keys", tablefmt="html") diff --git a/tests/test_html.py b/tests/test_html.py index 2e4aa18..c5ff59e 100644 --- a/tests/test_html.py +++ b/tests/test_html.py @@ -43,7 +43,9 @@ ( None, ["content"], - PAGE_HTML.format(plot_divs="content", refresh_tag="", scripts=""), + PAGE_HTML.replace("{plot_divs}", "content") + .replace("{scripts}", "") + .replace("{refresh_tag}", ""), ), ( CUSTOM_PAGE_HTML, diff --git a/tests/test_table.py b/tests/test_table.py new file mode 100644 index 0000000..c0ab785 --- /dev/null +++ b/tests/test_table.py @@ -0,0 +1,15 @@ +from dvc_render.table import TableRenderer + +# pylint: disable=missing-function-docstring + + +def test_render(): + datapoints = [ + {"foo": 1, "bar": 2}, + ] + html = TableRenderer(datapoints, "metrics.json").generate_html() + assert "

metrics_json

" in html + assert ' foo' in html + assert ' bar' in html + assert ' 1' in html + assert ' 2' in html From 538a1064c6aca1c633bed8cc3e7c570c14269ce5 Mon Sep 17 00:00:00 2001 From: David de la Iglesia Castro Date: Wed, 15 Jun 2022 13:36:45 +0200 Subject: [PATCH 2/2] Move `plot_` prefix to `vega`. --- src/dvc_render/base.py | 1 - src/dvc_render/vega.py | 2 ++ tests/test_parallel_coordinates.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dvc_render/base.py b/src/dvc_render/base.py index b299fd4..4cb26af 100644 --- a/src/dvc_render/base.py +++ b/src/dvc_render/base.py @@ -57,7 +57,6 @@ def generate_html(self, html_path=None) -> str: if partial: div_id = self.remove_special_chars(self.name) - div_id = f"plot_{div_id}" return self.DIV.format(id=div_id, partial=partial) return "" diff --git a/src/dvc_render/vega.py b/src/dvc_render/vega.py index 66da47c..a57b13f 100644 --- a/src/dvc_render/vega.py +++ b/src/dvc_render/vega.py @@ -33,6 +33,8 @@ class VegaRenderer(Renderer): EXTENSIONS = {".yml", ".yaml", ".json", ".csv", ".tsv"} def __init__(self, datapoints: List, name: str, **properties): + if name and not name.startswith("plot_"): + name = f"plot_{name}" super().__init__(datapoints, name, **properties) self.template = get_template( self.properties.get("template", None), diff --git a/tests/test_parallel_coordinates.py b/tests/test_parallel_coordinates.py index 3b8d8e2..423b734 100644 --- a/tests/test_parallel_coordinates.py +++ b/tests/test_parallel_coordinates.py @@ -144,7 +144,7 @@ def test_write_parallel_coordinates(tmp_dir): assert ParallelCoordinatesRenderer.SCRIPTS in html_text div = ParallelCoordinatesRenderer.DIV.format( - id="plot_pcp", partial=renderer.partial_html() + id="pcp", partial=renderer.partial_html() ) assert div in html_text