mirror of
https://github.com/aykhans/AzSuicideDataVisualization.git
synced 2025-04-22 10:28:02 +00:00
312 lines
9.1 KiB
Python
312 lines
9.1 KiB
Python
import hashlib
|
|
import os
|
|
import json
|
|
import random
|
|
import collections
|
|
from operator import itemgetter
|
|
import warnings
|
|
|
|
import jinja2
|
|
|
|
from docutils import nodes
|
|
from docutils.statemachine import ViewList
|
|
from docutils.parsers.rst import Directive
|
|
from docutils.parsers.rst.directives import flag
|
|
|
|
from sphinx.util.nodes import nested_parse_with_titles
|
|
|
|
from .utils import (
|
|
get_docstring_and_rest,
|
|
prev_this_next,
|
|
create_thumbnail,
|
|
create_generic_image,
|
|
)
|
|
from altair.utils.execeval import eval_block
|
|
from altair.examples import iter_examples
|
|
|
|
|
|
EXAMPLE_MODULE = "altair.examples"
|
|
|
|
|
|
GALLERY_TEMPLATE = jinja2.Template(
|
|
"""
|
|
.. This document is auto-generated by the altair-gallery extension. Do not modify directly.
|
|
|
|
.. _{{ gallery_ref }}:
|
|
|
|
{{ title }}
|
|
{% for char in title %}-{% endfor %}
|
|
|
|
This gallery contains a selection of examples of the plots Altair can create.
|
|
|
|
Some may seem fairly complicated at first glance, but they are built by combining a simple set of declarative building blocks.
|
|
|
|
Many draw upon sample datasets compiled by the `Vega <https://vega.github.io/vega/>`_ project. To access them yourself, install `vega_datasets <https://github.com/altair-viz/vega_datasets>`_.
|
|
|
|
.. code-block:: none
|
|
|
|
$ pip install vega_datasets
|
|
|
|
{% for grouper, group in examples %}
|
|
|
|
.. _gallery-category-{{ grouper }}:
|
|
|
|
{{ grouper }}
|
|
{% for char in grouper %}~{% endfor %}
|
|
|
|
.. raw:: html
|
|
|
|
<span class="gallery">
|
|
{% for example in group %}
|
|
<a class="imagegroup" href="{{ example.name }}.html">
|
|
<span class="image" alt="{{ example.title }}" style="background-image: url({{ image_dir }}/{{ example.name }}-thumb.png);"></span>
|
|
<span class="image-title">{{ example.title }}</span>
|
|
</a>
|
|
{% endfor %}
|
|
</span>
|
|
|
|
<div style='clear:both;'></div>
|
|
|
|
.. toctree::
|
|
:hidden:
|
|
{% for example in group %}
|
|
{{ example.name }}
|
|
{%- endfor %}
|
|
|
|
{% endfor %}
|
|
"""
|
|
)
|
|
|
|
MINIGALLERY_TEMPLATE = jinja2.Template(
|
|
"""
|
|
.. raw:: html
|
|
|
|
<div id="showcase">
|
|
<div class="examples">
|
|
{% for example in examples %}
|
|
<a class="preview" href="{{ gallery_dir }}/{{ example.name }}.html" style="background-image: url({{ image_dir }}/{{ example.name }}-thumb.png)"></a>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
"""
|
|
)
|
|
|
|
|
|
EXAMPLE_TEMPLATE = jinja2.Template(
|
|
"""
|
|
.. This document is auto-generated by the altair-gallery extension. Do not modify directly.
|
|
|
|
.. _gallery_{{ name }}:
|
|
|
|
{{ docstring }}
|
|
|
|
.. altair-plot::
|
|
{% if code_below %}:code-below:{% endif %}
|
|
|
|
{{ code | indent(4) }}
|
|
|
|
.. toctree::
|
|
:hidden:
|
|
"""
|
|
)
|
|
|
|
|
|
def save_example_pngs(examples, image_dir, make_thumbnails=True):
|
|
"""Save example pngs and (optionally) thumbnails"""
|
|
if not os.path.exists(image_dir):
|
|
os.makedirs(image_dir)
|
|
|
|
# store hashes so that we know whether images need to be generated
|
|
hash_file = os.path.join(image_dir, "_image_hashes.json")
|
|
|
|
if os.path.exists(hash_file):
|
|
with open(hash_file) as f:
|
|
hashes = json.load(f)
|
|
else:
|
|
hashes = {}
|
|
|
|
for example in examples:
|
|
filename = example["name"] + ".png"
|
|
image_file = os.path.join(image_dir, filename)
|
|
|
|
example_hash = hashlib.md5(example["code"].encode()).hexdigest()
|
|
hashes_match = hashes.get(filename, "") == example_hash
|
|
|
|
if hashes_match and os.path.exists(image_file):
|
|
print("-> using cached {}".format(image_file))
|
|
else:
|
|
# the file changed or the image file does not exist. Generate it.
|
|
print("-> saving {}".format(image_file))
|
|
chart = eval_block(example["code"])
|
|
try:
|
|
chart.save(image_file)
|
|
hashes[filename] = example_hash
|
|
except ImportError:
|
|
warnings.warn("Unable to save image: using generic image")
|
|
create_generic_image(image_file)
|
|
|
|
with open(hash_file, "w") as f:
|
|
json.dump(hashes, f)
|
|
|
|
if make_thumbnails:
|
|
params = example.get("galleryParameters", {})
|
|
thumb_file = os.path.join(image_dir, example["name"] + "-thumb.png")
|
|
create_thumbnail(image_file, thumb_file, **params)
|
|
|
|
# Save hashes so we know whether we need to re-generate plots
|
|
with open(hash_file, "w") as f:
|
|
json.dump(hashes, f)
|
|
|
|
|
|
def populate_examples(**kwds):
|
|
"""Iterate through Altair examples and extract code"""
|
|
|
|
examples = sorted(iter_examples(), key=itemgetter("name"))
|
|
|
|
for example in examples:
|
|
docstring, category, code, lineno = get_docstring_and_rest(example["filename"])
|
|
example.update(kwds)
|
|
if category is None:
|
|
category = "other charts"
|
|
example.update(
|
|
{
|
|
"docstring": docstring,
|
|
"title": docstring.strip().split("\n")[0],
|
|
"code": code,
|
|
"category": category.title(),
|
|
"lineno": lineno,
|
|
}
|
|
)
|
|
|
|
return examples
|
|
|
|
|
|
class AltairMiniGalleryDirective(Directive):
|
|
has_content = False
|
|
|
|
option_spec = {
|
|
"size": int,
|
|
"names": str,
|
|
"indices": lambda x: list(map(int, x.split())),
|
|
"shuffle": flag,
|
|
"seed": int,
|
|
"titles": bool,
|
|
"width": str,
|
|
}
|
|
|
|
def run(self):
|
|
size = self.options.get("size", 15)
|
|
names = [name.strip() for name in self.options.get("names", "").split(",")]
|
|
indices = self.options.get("indices", [])
|
|
shuffle = "shuffle" in self.options
|
|
seed = self.options.get("seed", 42)
|
|
titles = self.options.get("titles", False)
|
|
width = self.options.get("width", None)
|
|
|
|
env = self.state.document.settings.env
|
|
app = env.app
|
|
|
|
gallery_dir = app.builder.config.altair_gallery_dir
|
|
|
|
examples = populate_examples()
|
|
|
|
if names:
|
|
if len(names) < size:
|
|
raise ValueError(
|
|
"altair-minigallery: if names are specified, "
|
|
"the list must be at least as long as size."
|
|
)
|
|
mapping = {example["name"]: example for example in examples}
|
|
examples = [mapping[name] for name in names]
|
|
else:
|
|
if indices:
|
|
examples = [examples[i] for i in indices]
|
|
if shuffle:
|
|
random.seed(seed)
|
|
random.shuffle(examples)
|
|
if size:
|
|
examples = examples[:size]
|
|
|
|
include = MINIGALLERY_TEMPLATE.render(
|
|
image_dir="/_static",
|
|
gallery_dir=gallery_dir,
|
|
examples=examples,
|
|
titles=titles,
|
|
width=width,
|
|
)
|
|
|
|
# parse and return documentation
|
|
result = ViewList()
|
|
for line in include.split("\n"):
|
|
result.append(line, "<altair-minigallery>")
|
|
node = nodes.paragraph()
|
|
node.document = self.state.document
|
|
nested_parse_with_titles(self.state, result, node)
|
|
|
|
return node.children
|
|
|
|
|
|
def main(app):
|
|
gallery_dir = app.builder.config.altair_gallery_dir
|
|
target_dir = os.path.join(app.builder.srcdir, gallery_dir)
|
|
image_dir = os.path.join(app.builder.srcdir, "_images")
|
|
|
|
gallery_ref = app.builder.config.altair_gallery_ref
|
|
gallery_title = app.builder.config.altair_gallery_title
|
|
examples = populate_examples(gallery_ref=gallery_ref, code_below=True)
|
|
|
|
if not os.path.exists(target_dir):
|
|
os.makedirs(target_dir)
|
|
|
|
examples = sorted(examples, key=lambda x: x["title"])
|
|
examples_toc = collections.OrderedDict(
|
|
{
|
|
"Simple Charts": [],
|
|
"Bar Charts": [],
|
|
"Line Charts": [],
|
|
"Area Charts": [],
|
|
"Circular Plots": [],
|
|
"Scatter Plots": [],
|
|
"Histograms": [],
|
|
"Maps": [],
|
|
"Interactive Charts": [],
|
|
"Case Studies": [],
|
|
"Other Charts": [],
|
|
}
|
|
)
|
|
for d in examples:
|
|
examples_toc[d["category"]].append(d)
|
|
|
|
# Write the gallery index file
|
|
with open(os.path.join(target_dir, "index.rst"), "w") as f:
|
|
f.write(
|
|
GALLERY_TEMPLATE.render(
|
|
title=gallery_title,
|
|
examples=examples_toc.items(),
|
|
image_dir="/_static",
|
|
gallery_ref=gallery_ref,
|
|
)
|
|
)
|
|
|
|
# save the images to file
|
|
save_example_pngs(examples, image_dir)
|
|
|
|
# Write the individual example files
|
|
for prev_ex, example, next_ex in prev_this_next(examples):
|
|
if prev_ex:
|
|
example["prev_ref"] = "gallery_{name}".format(**prev_ex)
|
|
if next_ex:
|
|
example["next_ref"] = "gallery_{name}".format(**next_ex)
|
|
target_filename = os.path.join(target_dir, example["name"] + ".rst")
|
|
with open(os.path.join(target_filename), "w", encoding="utf-8") as f:
|
|
f.write(EXAMPLE_TEMPLATE.render(example))
|
|
|
|
|
|
def setup(app):
|
|
app.connect("builder-inited", main)
|
|
app.add_css_file("altair-gallery.css")
|
|
app.add_config_value("altair_gallery_dir", "gallery", "env")
|
|
app.add_config_value("altair_gallery_ref", "example-gallery", "env")
|
|
app.add_config_value("altair_gallery_title", "Example Gallery", "env")
|
|
app.add_directive_to_domain("py", "altair-minigallery", AltairMiniGalleryDirective)
|