{"spec_id":"area-cumulative-flow","library":"altair","language":"python","code":"\"\"\" anyplot.ai\narea-cumulative-flow: Cumulative Flow Diagram for Workflow Analytics\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 90/100 | Created: 2026-05-07\n\"\"\"\n\nimport os\nimport sys\n\n\nsys.path.pop(0)  # remove script dir so 'import altair' finds the installed package\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data — 90-day software delivery Kanban board\nnp.random.seed(42)\nn_days = 90\ndates = pd.date_range(\"2024-01-02\", periods=n_days, freq=\"D\")\n\n# Cumulative items that have reached each stage (monotonically non-decreasing)\n# Workflow order: Backlog → Analysis → Development → Testing → Done\ndone = np.cumsum(np.random.poisson(0.75, n_days))\n\ntesting_cum = done + np.round(np.linspace(4, 8, n_days) + np.random.normal(0, 0.8, n_days))\ntesting_cum = np.maximum.accumulate(testing_cum.clip(0).astype(int))\ntesting_cum = np.maximum(testing_cum, done)\n\n# Development is the bottleneck — band widens over time\ndev_cum = testing_cum + np.round(np.linspace(8, 18, n_days) + np.random.normal(0, 1.2, n_days))\ndev_cum = np.maximum.accumulate(dev_cum.clip(0).astype(int))\ndev_cum = np.maximum(dev_cum, testing_cum)\n\nanalysis_cum = dev_cum + np.round(np.linspace(4, 7, n_days) + np.random.normal(0, 0.8, n_days))\nanalysis_cum = np.maximum.accumulate(analysis_cum.clip(0).astype(int))\nanalysis_cum = np.maximum(analysis_cum, dev_cum)\n\nbacklog_cum = analysis_cum + np.round(np.linspace(12, 16, n_days) + np.random.normal(0, 1.0, n_days))\nbacklog_cum = np.maximum.accumulate(backlog_cum.clip(0).astype(int))\nbacklog_cum = np.maximum(backlog_cum, analysis_cum)\n\n# Decompose into WIP per stage (stacked from bottom: Done → Backlog)\nstages = [\"Done\", \"Testing\", \"Development\", \"Analysis\", \"Backlog\"]\nwip_arrays = [done, testing_cum - done, dev_cum - testing_cum, analysis_cum - dev_cum, backlog_cum - analysis_cum]\n\nrecords = []\nfor stage, wip in zip(stages, wip_arrays, strict=True):\n    for i, d in enumerate(dates):\n        records.append({\"date\": d, \"stage\": stage, \"wip\": int(wip[i])})\n\ndf = pd.DataFrame(records)\ndf[\"stack_order\"] = df[\"stage\"].map({s: i for i, s in enumerate(stages)})\n\n# Plot\nbase = (\n    alt.Chart(df)\n    .mark_area(interpolate=\"monotone\")\n    .encode(\n        x=alt.X(\"date:T\", title=\"Date\", axis=alt.Axis(format=\"%b %Y\", tickCount=6)),\n        y=alt.Y(\"wip:Q\", stack=\"zero\", title=\"Cumulative Items\"),\n        color=alt.Color(\n            \"stage:N\",\n            scale=alt.Scale(domain=stages, range=IMPRINT),\n            legend=alt.Legend(title=\"Workflow Stage\", orient=\"top-left\"),\n        ),\n        order=alt.Order(\"stack_order:Q\"),\n    )\n    .properties(\n        width=1600,\n        height=900,\n        title=alt.Title(\"area-cumulative-flow · altair · anyplot.ai\", fontSize=28),\n        background=PAGE_BG,\n    )\n)\n\nchart = (\n    base.configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_axis(\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        gridOpacity=0.10,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        labelFontSize=18,\n        titleFontSize=22,\n    )\n    .configure_title(color=INK, fontSize=28)\n    .configure_legend(\n        fillColor=ELEVATED_BG,\n        strokeColor=INK_SOFT,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n        titleFontSize=18,\n        labelFontSize=16,\n    )\n)\n\n# Save\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}