{"spec_id":"bullet-basic","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nbullet-basic: Basic Bullet Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-29\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent self-import: this file is named altair.py; remove the script directory\n# from sys.path so `import altair` finds the installed package, not this file.\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if not p or os.path.abspath(p) != _this_dir]\n\nimport altair as alt\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Imprint palette — semantic mapping: green = above-target (good), red = below-target (loss)\nABOVE_COLOR = \"#009E73\"  # Imprint position 1, brand green\nBELOW_COLOR = \"#AE3030\"  # Imprint semantic anchor, matte red\n\n# Grayscale bands: theme-adaptive so bands read clearly against both backgrounds\nif THEME == \"light\":\n    BAND_COLORS = [\"#e8e8e8\", \"#c8c8c8\", \"#a0a0a0\"]\nelse:\n    BAND_COLORS = [\"#2e2e2e\", \"#3e3e3e\", \"#545454\"]\n\n# Data — KPI dashboard: mix of above-target and below-target results across four metrics\nmetrics = [\n    {\"metric\": \"Revenue ($K)\", \"actual\": 275, \"target\": 250, \"poor\": 150, \"satisfactory\": 200, \"good\": 300},\n    {\"metric\": \"Profit ($K)\", \"actual\": 85, \"target\": 100, \"poor\": 50, \"satisfactory\": 75, \"good\": 125},\n    {\"metric\": \"New Customers\", \"actual\": 320, \"target\": 300, \"poor\": 200, \"satisfactory\": 275, \"good\": 350},\n    {\"metric\": \"Satisfaction\", \"actual\": 4.2, \"target\": 4.5, \"poor\": 3.0, \"satisfactory\": 4.0, \"good\": 5.0},\n]\nmetric_order = [m[\"metric\"] for m in metrics]\n\n# Normalize to percentage-of-goal for fair cross-metric comparison\nrange_data = []\nfor m in metrics:\n    max_val = m[\"good\"]\n    poor_pct = (m[\"poor\"] / max_val) * 100\n    sat_pct = (m[\"satisfactory\"] / max_val) * 100\n    range_data.append({\"metric\": m[\"metric\"], \"start\": 0, \"end\": poor_pct, \"band\": \"Poor\"})\n    range_data.append({\"metric\": m[\"metric\"], \"start\": poor_pct, \"end\": sat_pct, \"band\": \"Satisfactory\"})\n    range_data.append({\"metric\": m[\"metric\"], \"start\": sat_pct, \"end\": 100, \"band\": \"Good\"})\n\ndf_ranges = pd.DataFrame(range_data)\n\ndf_actual = pd.DataFrame(\n    [\n        {\n            \"metric\": m[\"metric\"],\n            \"actual_pct\": (m[\"actual\"] / m[\"good\"]) * 100,\n            \"actual_raw\": m[\"actual\"],\n            \"above_target\": m[\"actual\"] >= m[\"target\"],\n        }\n        for m in metrics\n    ]\n)\n\ndf_target = pd.DataFrame(\n    [{\"metric\": m[\"metric\"], \"target_pct\": (m[\"target\"] / m[\"good\"]) * 100, \"target_raw\": m[\"target\"]} for m in metrics]\n)\n\n# Shared Y scale with tight padding for compact bullet rows\ny_scale = alt.Scale(paddingInner=0.22, paddingOuter=0.15)\n\n# Background qualitative ranges (grayscale per spec)\nranges_chart = (\n    alt.Chart(df_ranges)\n    .mark_bar()\n    .encode(\n        y=alt.Y(\n            \"metric:N\",\n            title=None,\n            sort=metric_order,\n            scale=y_scale,\n            axis=alt.Axis(labelFontSize=12, labelFontWeight=\"bold\"),\n        ),\n        x=alt.X(\n            \"start:Q\",\n            title=\"Performance (% of Goal)\",\n            scale=alt.Scale(domain=[0, 115]),\n            axis=alt.Axis(titleFontSize=12, labelFontSize=10, tickCount=6),\n        ),\n        x2=\"end:Q\",\n        color=alt.Color(\n            \"band:N\",\n            scale=alt.Scale(domain=[\"Poor\", \"Satisfactory\", \"Good\"], range=BAND_COLORS),\n            legend=alt.Legend(\n                title=\"Performance Band\", orient=\"bottom\", titleFontSize=10, labelFontSize=10, direction=\"horizontal\"\n            ),\n        ),\n        tooltip=[alt.Tooltip(\"metric:N\", title=\"Metric\"), alt.Tooltip(\"band:N\", title=\"Band\")],\n    )\n)\n\n# Actual value bars: Imprint green (above-target) or Imprint red (below-target)\nactual_chart = (\n    alt.Chart(df_actual)\n    .mark_bar(height=22)\n    .encode(\n        y=alt.Y(\"metric:N\", sort=metric_order, scale=y_scale),\n        x=alt.X(\"actual_pct:Q\"),\n        color=alt.condition(alt.datum.above_target, alt.value(ABOVE_COLOR), alt.value(BELOW_COLOR)),\n        tooltip=[\n            alt.Tooltip(\"metric:N\", title=\"Metric\"),\n            alt.Tooltip(\"actual_raw:Q\", title=\"Actual\"),\n            alt.Tooltip(\"actual_pct:Q\", title=\"% of Goal\", format=\".1f\"),\n        ],\n    )\n)\n\n# Target marker — theme-adaptive ink-color tick\ntarget_chart = (\n    alt.Chart(df_target)\n    .mark_tick(color=INK, thickness=4, size=56)\n    .encode(\n        y=alt.Y(\"metric:N\", sort=metric_order, scale=y_scale),\n        x=alt.X(\"target_pct:Q\"),\n        tooltip=[alt.Tooltip(\"metric:N\", title=\"Metric\"), alt.Tooltip(\"target_raw:Q\", title=\"Target\")],\n    )\n)\n\n# Value labels at end of each bar\nvalue_labels = (\n    alt.Chart(df_actual)\n    .mark_text(align=\"left\", dx=6, fontSize=12, fontWeight=\"bold\")\n    .encode(\n        y=alt.Y(\"metric:N\", sort=metric_order, scale=y_scale),\n        x=alt.X(\"actual_pct:Q\"),\n        text=alt.Text(\"actual_raw:Q\"),\n        color=alt.condition(alt.datum.above_target, alt.value(ABOVE_COLOR), alt.value(BELOW_COLOR)),\n    )\n)\n\n# Title — 43 chars, below 67-char baseline so no scaling needed\ntitle_str = \"bullet-basic · python · altair · anyplot.ai\"\ntitle_fs = round(16 * min(1.0, 67 / len(title_str)))\n\nchart = (\n    alt.layer(ranges_chart, actual_chart, target_chart, value_labels)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(title_str, fontSize=title_fs, color=INK, anchor=\"middle\"),\n    )\n    .configure_view(fill=PAGE_BG, stroke=None)\n    .configure_axis(domainColor=INK_SOFT, tickColor=INK_SOFT, grid=False, labelColor=INK_SOFT, titleColor=INK)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=None, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save PNG and HTML\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\nchart.save(f\"plot-{THEME}.html\")\n\n# Pad to exact 3200×1800 canvas (vl-convert can undershoot)\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}×{_h}, exceeds target {TW}×{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n"}