{"spec_id":"burndown-sprint","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nburndown-sprint: Agile Sprint Burndown Chart\nLibrary: matplotlib 3.11.0 | Python 3.13.13\nQuality: 89/100 | Created: 2026-06-14\n\"\"\"\n\nimport sys\n\n\n# Prevent this file from shadowing the installed matplotlib package when run\n# from its own directory (sys.path[0] would otherwise resolve to this file).\nsys.path.pop(0)\n\nimport os\n\nimport matplotlib.dates as mdates\nimport matplotlib.pyplot as plt\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette positions used\nBRAND = \"#009E73\"  # brand green — actual remaining (first series, always)\nBLUE = \"#4467A3\"  # blue — ideal burndown reference line\nRED = \"#AE3030\"  # matte red — scope change / behind-schedule indicator\n\n# Data — 10-working-day sprint: Mon Jan 6 to Fri Jan 17 2025\n# Committed scope: 40 story points; scope change (+10) occurs on Thu Jan 9\nsprint_dates = pd.bdate_range(\"2025-01-06\", \"2025-01-17\")  # 10 business days\nremaining = np.array([40, 38, 34, 44, 40, 36, 28, 20, 12, 4])\nideal = np.linspace(40, 0, len(sprint_dates))\n\nscope_change_date = sprint_dates[3]  # Jan 9: remaining jumps 34 → 44 (+10 scope)\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Weekend background band (Sat–Sun between the two working weeks)\nax.axvspan(pd.Timestamp(\"2025-01-11\"), pd.Timestamp(\"2025-01-13\"), color=INK, alpha=0.04, zorder=0)\nax.text(pd.Timestamp(\"2025-01-12\"), 2, \"wknd\", fontsize=8, ha=\"center\", va=\"bottom\", color=INK_MUTED, rotation=90)\n\n# Behind-schedule fill: region where actual remaining exceeds the ideal target\nax.fill_between(sprint_dates, remaining, ideal, where=(remaining > ideal), color=RED, alpha=0.07, step=\"post\", zorder=1)\n\n# Ideal burndown — straight diagonal reference from committed scope to zero\nax.plot(sprint_dates, ideal, color=BLUE, linewidth=1.8, linestyle=\"--\", alpha=0.75, label=\"Ideal burndown\", zorder=2)\n\n# Actual remaining — step series (work completes in discrete daily chunks)\nax.step(sprint_dates, remaining, where=\"post\", color=BRAND, linewidth=2.8, label=\"Actual remaining\", zorder=3)\nax.scatter([sprint_dates[-1]], [remaining[-1]], color=BRAND, s=80, zorder=4, edgecolors=PAGE_BG, linewidth=1.0)\n\n# Scope change: vertical dotted line + label above the spike\nax.axvline(scope_change_date, color=RED, linewidth=1.2, linestyle=\":\", alpha=0.85, zorder=4)\nax.text(\n    scope_change_date + pd.Timedelta(hours=8),\n    47.5,\n    \"+10 pts scope\",\n    fontsize=7,\n    color=RED,\n    ha=\"left\",\n    va=\"bottom\",\n    bbox={\"facecolor\": ELEVATED_BG, \"edgecolor\": RED, \"alpha\": 0.88, \"linewidth\": 0.7, \"boxstyle\": \"round,pad=0.3\"},\n    zorder=5,\n)\n\n# \"Behind schedule\" label at peak deviation — Jan 13 (Mon), midpoint between step and ideal\nax.text(\n    sprint_dates[5] + pd.Timedelta(hours=12),\n    (remaining[5] + ideal[5]) / 2,\n    \"behind schedule\",\n    fontsize=7,\n    color=RED,\n    alpha=0.55,\n    ha=\"center\",\n    va=\"center\",\n    rotation=90,\n)\n\n# Axis limits and labels\nax.set_xlim(sprint_dates[0] - pd.Timedelta(hours=12), sprint_dates[-1] + pd.Timedelta(hours=12))\nax.set_ylim(-1, 55)\nax.set_ylabel(\"Remaining Story Points\", fontsize=10, color=INK)\nax.set_xlabel(\"Sprint Day  ·  Jan 6 – 17 2025\", fontsize=10, color=INK)\n\n# X-axis ticks: Mon, Wed, Fri\nax.xaxis.set_major_locator(mdates.WeekdayLocator(byweekday=[0, 2, 4]))\nax.xaxis.set_major_formatter(mdates.DateFormatter(\"%a %d\"))\nplt.setp(ax.get_xticklabels(), rotation=0, ha=\"center\", fontsize=8, color=INK_SOFT)\nax.tick_params(axis=\"y\", labelsize=8, labelcolor=INK_SOFT)\nax.tick_params(axis=\"x\", colors=INK_SOFT)\n\n# Grid and spines\nax.yaxis.grid(True, alpha=0.12, linewidth=0.7, color=INK)\nax.set_axisbelow(True)\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\n\n# Title\ntitle = \"burndown-sprint · python · matplotlib · anyplot.ai\"\nax.set_title(title, fontsize=12, fontweight=\"medium\", color=INK, pad=10)\n\n# Legend\nleg = ax.legend(fontsize=8, loc=\"upper right\")\nif leg:\n    leg.get_frame().set_facecolor(ELEVATED_BG)\n    leg.get_frame().set_edgecolor(INK_SOFT)\n    plt.setp(leg.get_texts(), color=INK_SOFT)\n\nfig.subplots_adjust(left=0.10, right=0.97, top=0.92, bottom=0.12)\n\n# Save\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\nplt.close()\n"}