Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "data-toolkit",
"version": "0.8.1",
"version": "0.8.2",
"description": "Extract, tidy, reconcile, analyse, visualise and convert messy data into clean, validated, audit-ready tables and insight briefs \u2014 computed by a deterministic engine that runs on your machine, with no third-party services. From Phronesis Applied.",
"author": {
"name": "Phronesis Applied",
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 0.8.2 — 2026-07-19

**Post-#21 merge hygiene** — two leftovers from landing the analyse plan-surface
PR after the viz/filter work:

- **`analysis-plan.schema.json`**: drop the duplicate `allOf` rule that required
`column` for `numeric_summary` / `outliers_iqr` / `currency_mix` a second time
(the #21 rule that also covers `distribution` remains).
- **`blocks_from_analysis`**: render `histogram`, `stacked_bar`, and
`scatter_chart`. `suggest_blocks_from_analysis` already proposed the first two
for `distribution` / `pivot`; the convenience helper raised
`unsupported block type` instead of drawing them. Agent runtime was unaffected
(`suggest` → `_viz_block`).

## 0.8.1 — 2026-07-19

**Follow-ups to the viz/filter PR (#25)** — closing the plan-surface and handoff
Expand Down
2 changes: 1 addition & 1 deletion schemas/analysis-plan.schema.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://data-toolkit.local/schemas/analysis-plan.schema.json","title":"Data Analyse operations","type":"array","items":{"type":"object","required":["op"],"properties":{"op":{"enum":["numeric_summary","outliers_iqr","breakdown","period_series","ageing","currency_mix","concentration","pivot","distribution","trend","percentile","cohort","correlation_matrix","rolling","gini","seasonality","join_on","compare_series","filter_rows"]},"name":{"type":"string"},"column":{"type":"string","minLength":1},"by":{"type":"string","minLength":1},"value":{"type":"string"},"top":{"type":"integer","minimum":1},"top_n":{"type":"integer","minimum":1},"k":{"type":["number","string"]},"cap":{"type":"integer","minimum":1},"date_col":{"type":"string","minLength":1},"grain":{"enum":["month","quarter","year"]},"dayfirst":{"type":"boolean"},"as_of":{"type":"string","minLength":1},"buckets":{"type":"array","minItems":1,"items":{"type":"integer","minimum":0}},"rows_col":{"type":"string","minLength":1},"cols_col":{"type":"string","minLength":1},"aggfunc":{"enum":["sum","count","mean"]},"q":{"oneOf":[{"type":"number","minimum":0,"maximum":1},{"type":"array","minItems":1,"items":{"type":"number","minimum":0,"maximum":1}}]},"id_col":{"type":"string","minLength":1},"columns":{"type":"array","minItems":2,"items":{"type":"string","minLength":1}},"window":{"type":"integer","minimum":1},"func":{"enum":["mean","sum","median"]},"on":{"oneOf":[{"type":"string","minLength":1},{"type":"array","minItems":1,"items":{"type":"string","minLength":1}}]},"how":{"enum":["inner","left"]},"a_value":{"type":"string","minLength":1},"b_value":{"type":"string","minLength":1},"a_label":{"type":"string","minLength":1},"b_label":{"type":"string","minLength":1},"left":{"type":"object","required":["date_col"],"properties":{"date_col":{"type":"string","minLength":1},"value":{"type":"string"},"dayfirst":{"type":"boolean"}},"additionalProperties":false},"right":{"type":"object","required":["date_col"],"properties":{"date_col":{"type":"string","minLength":1},"value":{"type":"string"},"dayfirst":{"type":"boolean"}},"additionalProperties":false},"filters":{"type":"array","minItems":1,"items":{"type":"object","required":["op"],"properties":{"col":{"type":"string","minLength":1},"column":{"type":"string","minLength":1},"op":{"type":"string"},"value":{},"values":{"type":"array"},"lo":{},"hi":{}},"additionalProperties":false}}},"allOf":[{"if":{"properties":{"op":{"enum":["numeric_summary","outliers_iqr","currency_mix","distribution"]}},"required":["op"]},"then":{"required":["column"]}},{"if":{"properties":{"op":{"const":"breakdown"}},"required":["op"]},"then":{"required":["by"]}},{"if":{"properties":{"op":{"const":"period_series"}},"required":["op"]},"then":{"required":["date_col"]}},{"if":{"properties":{"op":{"const":"ageing"}},"required":["op"]},"then":{"required":["date_col","as_of"]}},{"if":{"properties":{"op":{"enum":["concentration","gini"]}},"required":["op"]},"then":{"anyOf":[{"required":["column"]},{"required":["by"]}]}},{"if":{"properties":{"op":{"const":"pivot"}},"required":["op"]},"then":{"required":["rows_col","cols_col"]}},{"if":{"properties":{"op":{"enum":["trend","rolling","seasonality"]}},"required":["op"]},"then":{"required":["date_col"]}},{"if":{"properties":{"op":{"const":"rolling"}},"required":["op"]},"then":{"required":["window"]}},{"if":{"properties":{"op":{"const":"seasonality"}},"required":["op"]},"then":{"properties":{"grain":{"enum":["month","quarter"]}}}},{"if":{"properties":{"op":{"const":"percentile"}},"required":["op"]},"then":{"required":["column","q"]}},{"if":{"properties":{"op":{"const":"cohort"}},"required":["op"]},"then":{"required":["id_col","date_col"]}},{"if":{"properties":{"op":{"const":"correlation_matrix"}},"required":["op"]},"then":{"required":["columns"]}},{"if":{"properties":{"op":{"const":"join_on"}},"required":["op"]},"then":{"required":["on"]}},{"if":{"properties":{"op":{"const":"compare_series"}},"required":["op"]},"then":{"anyOf":[{"required":["date_col","a_value","b_value"]},{"required":["left","right"]}]}},{"if":{"properties":{"op":{"enum":["numeric_summary","outliers_iqr","currency_mix"]}},"required":["op"]},"then":{"required":["column"]}},{"if":{"properties":{"op":{"const":"filter_rows"}},"required":["op"]},"then":{"required":["filters"]}}],"additionalProperties":false}}
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://data-toolkit.local/schemas/analysis-plan.schema.json","title":"Data Analyse operations","type":"array","items":{"type":"object","required":["op"],"properties":{"op":{"enum":["numeric_summary","outliers_iqr","breakdown","period_series","ageing","currency_mix","concentration","pivot","distribution","trend","percentile","cohort","correlation_matrix","rolling","gini","seasonality","join_on","compare_series","filter_rows"]},"name":{"type":"string"},"column":{"type":"string","minLength":1},"by":{"type":"string","minLength":1},"value":{"type":"string"},"top":{"type":"integer","minimum":1},"top_n":{"type":"integer","minimum":1},"k":{"type":["number","string"]},"cap":{"type":"integer","minimum":1},"date_col":{"type":"string","minLength":1},"grain":{"enum":["month","quarter","year"]},"dayfirst":{"type":"boolean"},"as_of":{"type":"string","minLength":1},"buckets":{"type":"array","minItems":1,"items":{"type":"integer","minimum":0}},"rows_col":{"type":"string","minLength":1},"cols_col":{"type":"string","minLength":1},"aggfunc":{"enum":["sum","count","mean"]},"q":{"oneOf":[{"type":"number","minimum":0,"maximum":1},{"type":"array","minItems":1,"items":{"type":"number","minimum":0,"maximum":1}}]},"id_col":{"type":"string","minLength":1},"columns":{"type":"array","minItems":2,"items":{"type":"string","minLength":1}},"window":{"type":"integer","minimum":1},"func":{"enum":["mean","sum","median"]},"on":{"oneOf":[{"type":"string","minLength":1},{"type":"array","minItems":1,"items":{"type":"string","minLength":1}}]},"how":{"enum":["inner","left"]},"a_value":{"type":"string","minLength":1},"b_value":{"type":"string","minLength":1},"a_label":{"type":"string","minLength":1},"b_label":{"type":"string","minLength":1},"left":{"type":"object","required":["date_col"],"properties":{"date_col":{"type":"string","minLength":1},"value":{"type":"string"},"dayfirst":{"type":"boolean"}},"additionalProperties":false},"right":{"type":"object","required":["date_col"],"properties":{"date_col":{"type":"string","minLength":1},"value":{"type":"string"},"dayfirst":{"type":"boolean"}},"additionalProperties":false},"filters":{"type":"array","minItems":1,"items":{"type":"object","required":["op"],"properties":{"col":{"type":"string","minLength":1},"column":{"type":"string","minLength":1},"op":{"type":"string"},"value":{},"values":{"type":"array"},"lo":{},"hi":{}},"additionalProperties":false}}},"allOf":[{"if":{"properties":{"op":{"enum":["numeric_summary","outliers_iqr","currency_mix","distribution"]}},"required":["op"]},"then":{"required":["column"]}},{"if":{"properties":{"op":{"const":"breakdown"}},"required":["op"]},"then":{"required":["by"]}},{"if":{"properties":{"op":{"const":"period_series"}},"required":["op"]},"then":{"required":["date_col"]}},{"if":{"properties":{"op":{"const":"ageing"}},"required":["op"]},"then":{"required":["date_col","as_of"]}},{"if":{"properties":{"op":{"enum":["concentration","gini"]}},"required":["op"]},"then":{"anyOf":[{"required":["column"]},{"required":["by"]}]}},{"if":{"properties":{"op":{"const":"pivot"}},"required":["op"]},"then":{"required":["rows_col","cols_col"]}},{"if":{"properties":{"op":{"enum":["trend","rolling","seasonality"]}},"required":["op"]},"then":{"required":["date_col"]}},{"if":{"properties":{"op":{"const":"rolling"}},"required":["op"]},"then":{"required":["window"]}},{"if":{"properties":{"op":{"const":"seasonality"}},"required":["op"]},"then":{"properties":{"grain":{"enum":["month","quarter"]}}}},{"if":{"properties":{"op":{"const":"percentile"}},"required":["op"]},"then":{"required":["column","q"]}},{"if":{"properties":{"op":{"const":"cohort"}},"required":["op"]},"then":{"required":["id_col","date_col"]}},{"if":{"properties":{"op":{"const":"correlation_matrix"}},"required":["op"]},"then":{"required":["columns"]}},{"if":{"properties":{"op":{"const":"join_on"}},"required":["op"]},"then":{"required":["on"]}},{"if":{"properties":{"op":{"const":"compare_series"}},"required":["op"]},"then":{"anyOf":[{"required":["date_col","a_value","b_value"]},{"required":["left","right"]}]}},{"if":{"properties":{"op":{"const":"filter_rows"}},"required":["op"]},"then":{"required":["filters"]}}],"additionalProperties":false}}
15 changes: 15 additions & 0 deletions skills/data-visualise/scripts/viz.py
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,21 @@ def _render(spec: dict) -> str:
show_last=spec.get("show_last", True), unit=spec.get("unit", ""))
if kind == "waterfall":
return waterfall(spec.get("steps", []), title=spec.get("title"), unit=spec.get("unit", ""))
if kind == "scatter_chart":
return scatter_chart(spec.get("x", []), spec.get("y", []),
title=spec.get("title"),
x_label=spec.get("x_label"), y_label=spec.get("y_label"),
unit_x=spec.get("unit_x", ""),
unit_y=spec.get("unit_y", spec.get("unit", "")),
labels=spec.get("labels"),
trend_line=spec.get("trend_line", False))
if kind == "histogram":
return histogram(spec.get("values", []), bins=spec.get("bins", 10),
title=spec.get("title"), unit=spec.get("unit", ""))
if kind == "stacked_bar":
return stacked_bar(spec.get("data", {}), title=spec.get("title"),
unit=spec.get("unit", ""),
legend=spec.get("legend", True))
if kind == "section":
return section(spec.get("title", ""), *[_render(c) for c in spec.get("blocks", [])])
if kind == "grid":
Expand Down
6 changes: 6 additions & 0 deletions tests/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,10 @@ def test_suggest_blocks_proposes_stacked_bar_for_pivot():
block_types = [b["type"] for b in specs[0]["blocks"]]
assert "heatmap" in block_types
assert "stacked_bar" in block_types, f"expected stacked_bar, got {block_types}"
# suggest proposes it; blocks_from_analysis must actually render it (merge
# follow-up: the convenience helper lagged _viz_block / suggest).
html = viz.blocks_from_analysis(analysis)
assert any("<rect" in h for h in html)


def test_suggest_blocks_proposes_histogram_for_distribution_when_values_carried():
Expand All @@ -758,6 +762,8 @@ def test_suggest_blocks_proposes_histogram_for_distribution_when_values_carried(
specs = viz.suggest_blocks_from_analysis(analysis)
block_types = [b["type"] for b in specs[0]["blocks"]]
assert "histogram" in block_types, f"expected histogram, got {block_types}"
html = viz.blocks_from_analysis(analysis)
assert any("value" in h and "<rect" in h for h in html)
# without values, no histogram is proposed
analysis2 = {"results": [{"op": "distribution", "name": "x", "result": {
"skewness": 1.2, "kurtosis": 2.1, "classification": "right-skewed"}}]}
Expand Down
Loading