mkdocs-charts-plugin icon indicating copy to clipboard operation
mkdocs-charts-plugin copied to clipboard

Helper function for `altair` exports

Open timvink opened this issue 3 years ago • 1 comments

altair has good support for exporting vega schemas (https://altair-viz.github.io/user_guide/saving_charts.html?highlight=to_).

Of course you could just use chart.save('chart.svg') and insert that into a mkdocs page. But you want to want update the data later on and use the same chart.

Manually copy pasting the schema to a file is a hassle. Write a helper function to:

save_mkdocs_chart(chart, name="", docs_folder="../docs", json_path="docs/assets/charts/", data_path="docs/assets/charts/data")

The docs_folders could be auto-detected by traversing upward, finding mkdocs.yml and reading the docs_dir entry (if not present then docs/).

The json_path is relative to the docs_folder and can have a default "docs/assets/charts/"

The data_path is relative to the docs_folder and can have a "docs/assets/charts/data" default. This will only work if we can access the data object in the altair chart instance, which we can then write to .csv.

timvink avatar Dec 08 '21 15:12 timvink

This might need to be tweaked somewhat, but I created this helper function that roughly follows the logic you're suggesting here:

import json
from pathlib import Path

from altair import Chart
from pydantic import BaseModel, DirectoryPath, validator


class ChartParams(BaseModel):
    output_dir: DirectoryPath
    data_file: str
    plot_name: str

    @validator("data_file")
    def validate_data_file(cls, val, values):
        if "output_dir" in values:
            full_path = values["output_dir"].absolute() / val
            if not full_path.exists():
                raise ValueError("data_file does not exist")
        return val

    @validator("plot_name")
    def validate_plot_name(cls, val):
        if not val.endswith("json"):
            raise ValueError(f"plot_name must end with .json")
        return val

    @property
    def plot_path(self):
        return self.output_dir / self.plot_name


def save_chart(chart: Chart, output_dir: Path, data_file: str = "results.csv", plot_name: str = "plot.json") -> None:
    params = ChartParams(output_dir=output_dir, data_file=data_file, plot_name=plot_name)

    # Convert the plot to JSON
    plot = json.loads(chart.to_json())

    del plot["datasets"]
    plot["data"] = {"url": data_file}
    with open(params.plot_path, "w") as fo:
        json.dump(plot, fo)

This assumes you're storing the plot in the same directory as the CSV file containing the data.

ewellinger avatar Jun 01 '23 16:06 ewellinger