Programmable infographic generation powered by sympy and svg.py
Project description
Infogroove
Infogroove converts declarative template definitions into SVG infographics using sympy for formula evaluation and svg.py for SVG generation.
Quick Start
Create the virtual environment and install dependencies with uv:
uv sync
Render any of the bundled examples (each lives in its own subdirectory):
uv run infogroove -f examples/horizontal-bars/def.json -i examples/horizontal-bars/data.json -o examples/horizontal-bars/horizontal-bars.svg
uv run infogroove -f examples/stat-cards/def.json -i examples/stat-cards/data.json -o examples/stat-cards/stat-cards.svg
uv run infogroove -f examples/blue-parallelograms/def.json -i examples/blue-parallelograms/data.json -o examples/blue-parallelograms/blue-parallelograms.svg
uv run infogroove -f examples/arc-circles/def.json -i examples/arc-circles/data.json -o examples/arc-circles/arc-circles.svg
uv run infogroove -f examples/staggered-keywords/def.json -i examples/staggered-keywords/data.json -o examples/staggered-keywords/staggered-keywords.svg
Render instantly with uvx (no local install required):
uvx infogroove -f /path/to/def.json -i /path/to/data.json -o /path/to/output.svg
Omit -o to print the SVG to stdout:
uvx infogroove -f /path/to/def.json -i /path/to/data.json > output.svg
Running Tests
Install development dependencies and execute the test suite with pytest:
uv sync --extra dev
uv run --extra dev pytest
To measure coverage locally you can add the --cov flag:
uv run --extra dev pytest --cov=infogroove --cov=tests
Example Gallery
| Template | Preview |
|---|---|
| Horizontal Bars | |
| Stat Cards | |
| Blue Parallelogram Bands | |
| Arc Circles | |
| Staggered Keywords | |
| Key Messages |
Template Overview
A template definition is a JSON document with these top-level keys. The design aims to keep templates declarative and predictable:
-
Explicit scopes. Global
propertiesestablish shared context, while element-levelletblocks create isolated overlays that run after any repeat bindings. Values never bleed across scope boundaries unless you intentionally rebind them. Whenletbindings reuse a base key, theletbinding takes precedence for dependent expressions. -
Deterministic evaluation. Element
letbindings resolve lazily the first time they are referenced. Cycles are detected and reported early, preventing runaway recursion and making intent obvious. -
Composable building blocks. Elements remain small, nested structures. Complex layouts emerge from combining scoped bindings and child trees rather than inventing a verbose DSL.
-
properties: Global assignments evaluated before rendering begins. Provide thecanvassize here (width,height) along with reusable constants such aspalette,margin, orfont_family. Values are injected into the rendering context as-is, so strings like"Inter, Arial, sans-serif"remain literal. -
template: A list of element descriptors. Each descriptor has atype, optional attribute map, optionaltext, optionallet, and optionalchildren. Elements render once unless arepeatblock is present. -
schema(optional): JSON Schema definition for the expected dataset shape. Describe the root collection (usually anarray) as well as nested iterables likevalues,points, or other custom series so input data can be validated before rendering. Templates typically expect the top-level payload to be an object (for example{"items": [...]}), but you are free to choose any shape that satisfies the schema.
Each element may declare its own let block. These bindings evaluate against
the current context (including repeat helpers) and the results become available
to the element's attributes and its children.
Evaluation rules for let bindings:
- If the string contains
{...}placeholders, placeholder expansion is used. - If the string is exactly one placeholder (for example
"{value}"), the raw expression result is returned (number, list, object, etc.). - Otherwise the string is treated as a full expression and evaluated.
- To force a literal string, wrap it in quotes (for example
"'blue'").
The repeat block explicitly controls iteration:
{
"type": "text",
"repeat": {
"items": "items",
"as": "row",
"let": {
"row_x": "__index__ * 24"
}
},
"let": {
"label": "row.label",
"x": "__index__ * 24"
},
"attributes": {"x": "{x}", "y": "40"},
"text": "{label}"
}
itemsreferences the collection to iterate (any dotted path resolved via the current context, such asitemsoritems[0].points).asnames the current element. Use the reserved helpers (e.g.__index__,__count__) inside expressions when you need positional data; when the iterated item is a mapping, those helpers are also exposed on the alias (for example,row.__index__).letinsiderepeatdefines per-iteration bindings evaluated before the element's ownletblock, allowing shared loop-derived values to be reused.- Element
letinjects per-iteration bindings scoped to that element. Expressions can reference the current item, previously declared loop bindings, and globals.
During iteration, Infogroove also injects reserved helpers such as __index__,
__first__, __last__, __count__, and __total__ for convenience.
Placeholder syntax supports both {path.to.value} lookups and inline
expressions such as {__index__ * 10} or {canvas.width / 2}. Attribute and
text values are always strings, so expressions only run inside {...}
placeholders. For example, "width": "1 + 2" stays literal, while
"width": "{1 + 2}" evaluates to 3.
Expressions are evaluated by sympy first and then a restricted AST fallback.
Only a safe subset of Python is permitted; see the template spec for the exact
rules and allowed helpers: docs/SPEC.md.
Identifiers are resolved exactly as written. Prefer a consistent casing convention within datasets and templates to avoid ambiguity.
Randomness is opt-in. To enable random values, provide random_seed (or a
custom RNG) under properties; expressions can then call Math.random() or
random.random() for deterministic sequences.
CLI Options
uv run infogroove --help
Key flags:
-f, --template: Path to the template definition JSON file (e.g.def.json).-i, --input: JSON file containing an array of data objects.-o, --output: Destination SVG path or-for stdout (default:-).
Programmatic Usage
Infogroove exposes a loader for integrating templates directly into Python applications:
from infogroove.loader import load
with open("examples/arc-circles/def.json", encoding="utf-8") as fh:
infographic = load(fh)
data = [{"label": "Alpha", "value": 3}]
svg_markup = infographic.render(data)
Prefer infogroove.loader.load for file objects and infogroove.loader.loads
when the template definition is already in memory as a string. Both helpers
return an InfogrooveRenderer, exposing the parsed template via the
template property for metadata inspection.
When you already have the JSON structure as a Python mapping, instantiate an
infographic directly with the Infogroove factory:
from infogroove import Infogroove
infographic = Infogroove({
"properties": {
"canvas": {"width": 200, "height": 40},
"gap": 10,
},
"template": [
{
"type": "circle",
"attributes": {"cx": "{__index__ * gap}", "cy": "20", "r": "5"},
"repeat": {"items": "data", "as": "item"}
}
],
})
svg_inline = infographic.render([{}] * 10)
Developing Templates
- Keep shared constants (including canvas dimensions) under the top-level
propertiesblock. - Use
repeatto make iteration explicit; push derived per-loop values into an element'sletblock so they stay scoped to that element. - Inline expressions handle quick maths (
{__index__ * 10}) while elementletbindings are ideal for shared or multi-step calculations. - Let bindings resolve lazily, so the order you declare keys does not matter.
However, circular definitions (e.g.
total: "max",max: "total") will be rejected with a clear error. Break cycles by lifting shared calculations into a new binding or restructuring the dependency chain.
Agent Skills (Claude, Codex, Gemini)
This repository includes an Agent Skills package at skills/create-infogroove and a
pre-built archive at skills/create-infogroove.skill.
Most Agent Skills-compatible assistants (Claude, Codex, Gemini, etc.) support one of these installation modes:
- Import the archive: use the agent's Skills/Extensions UI or CLI to import
skills/create-infogroove.skill. - Register the folder: point the agent's skills path to
./skillsor copyskills/create-infogrooveinto the agent's configured skills directory.
After installation, ask the agent to create or update an infographic; it will generate
def.json, data.json, and a rendered SVG under ./infographics/<name>/ by default.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file infogroove-0.5.2.tar.gz.
File metadata
- Download URL: infogroove-0.5.2.tar.gz
- Upload date:
- Size: 64.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d078f8ecccdec61e32716ec263f6b9252cd384ce34ea4a0e45e12015f2c051d1
|
|
| MD5 |
6404cfd2893eb1979acbc0dea8759ce5
|
|
| BLAKE2b-256 |
ee0c244c0ceda5fac2fefd5c419d6871d38d17acbabc4481acb789d277277e49
|
File details
Details for the file infogroove-0.5.2-py3-none-any.whl.
File metadata
- Download URL: infogroove-0.5.2-py3-none-any.whl
- Upload date:
- Size: 26.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
37d666135150a0162f637f4f048c65090f19215240356c0757cdca3f3d61dde6
|
|
| MD5 |
1ee2d4505da86f7e595b2506648ea85d
|
|
| BLAKE2b-256 |
763c57a17e3b80f7cdb16b00444ca490e22af84db9c57f598bd585f6af423125
|