Skip to main content

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 Horizontal bars preview
Stat Cards Stat cards preview
Blue Parallelogram Bands Blue parallelograms preview
Arc Circles Arc circles preview
Staggered Keywords Staggered keywords preview
Key Messages Key messages preview

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 properties establish shared context, while element-level let blocks create isolated overlays that run after any repeat bindings. Values never bleed across scope boundaries unless you intentionally rebind them. When let bindings reuse a base key, the let binding takes precedence for dependent expressions.

  • Deterministic evaluation. Element let bindings 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 the canvas size here (width, height) along with reusable constants such as palette, margin, or font_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 a type, optional attribute map, optional text, optional let, and optional children. Elements render once unless a repeat block is present.

  • schema (optional): JSON Schema definition for the expected dataset shape. Describe the root collection (usually an array) as well as nested iterables like values, 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}"
}
  • items references the collection to iterate (any dotted path resolved via the current context, such as items or items[0].points).
  • as names 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__).
  • let inside repeat defines per-iteration bindings evaluated before the element's own let block, allowing shared loop-derived values to be reused.
  • Element let injects 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 properties block.
  • Use repeat to make iteration explicit; push derived per-loop values into an element's let block so they stay scoped to that element.
  • Inline expressions handle quick maths ({__index__ * 10}) while element let bindings 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 ./skills or copy skills/create-infogroove into 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

infogroove-0.5.2.tar.gz (64.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

infogroove-0.5.2-py3-none-any.whl (26.2 kB view details)

Uploaded Python 3

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

Hashes for infogroove-0.5.2.tar.gz
Algorithm Hash digest
SHA256 d078f8ecccdec61e32716ec263f6b9252cd384ce34ea4a0e45e12015f2c051d1
MD5 6404cfd2893eb1979acbc0dea8759ce5
BLAKE2b-256 ee0c244c0ceda5fac2fefd5c419d6871d38d17acbabc4481acb789d277277e49

See more details on using hashes here.

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

Hashes for infogroove-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 37d666135150a0162f637f4f048c65090f19215240356c0757cdca3f3d61dde6
MD5 1ee2d4505da86f7e595b2506648ea85d
BLAKE2b-256 763c57a17e3b80f7cdb16b00444ca490e22af84db9c57f598bd585f6af423125

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page