{"spec_id":"gantt-dependencies","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ngantt-dependencies: Gantt Chart with Dependencies\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib.patches import FancyArrowPatch\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\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 categorical palette — canonical order, first series always #009E73\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data: Software Development Project with Dependencies\ntasks_data = [\n    # Requirements Phase\n    {\n        \"task\": \"Requirements Gathering\",\n        \"start\": \"2024-01-01\",\n        \"end\": \"2024-01-14\",\n        \"group\": \"Requirements\",\n        \"depends_on\": [],\n    },\n    {\n        \"task\": \"Requirements Review\",\n        \"start\": \"2024-01-15\",\n        \"end\": \"2024-01-21\",\n        \"group\": \"Requirements\",\n        \"depends_on\": [\"Requirements Gathering\"],\n    },\n    {\n        \"task\": \"Requirements Sign-off\",\n        \"start\": \"2024-01-22\",\n        \"end\": \"2024-01-25\",\n        \"group\": \"Requirements\",\n        \"depends_on\": [\"Requirements Review\"],\n    },\n    # Design Phase\n    {\n        \"task\": \"System Architecture\",\n        \"start\": \"2024-01-26\",\n        \"end\": \"2024-02-08\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"Requirements Sign-off\"],\n    },\n    {\n        \"task\": \"Database Design\",\n        \"start\": \"2024-02-09\",\n        \"end\": \"2024-02-20\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"System Architecture\"],\n    },\n    {\n        \"task\": \"UI/UX Design\",\n        \"start\": \"2024-02-09\",\n        \"end\": \"2024-02-22\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"System Architecture\"],\n    },\n    {\n        \"task\": \"Design Review\",\n        \"start\": \"2024-02-23\",\n        \"end\": \"2024-02-28\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"Database Design\", \"UI/UX Design\"],\n    },\n    # Development Phase\n    {\n        \"task\": \"Backend Core\",\n        \"start\": \"2024-02-29\",\n        \"end\": \"2024-03-18\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"Design Review\"],\n    },\n    {\n        \"task\": \"API Development\",\n        \"start\": \"2024-03-19\",\n        \"end\": \"2024-04-05\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"Backend Core\"],\n    },\n    {\n        \"task\": \"Frontend Components\",\n        \"start\": \"2024-04-06\",\n        \"end\": \"2024-04-22\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"UI/UX Design\", \"API Development\"],\n    },\n    {\n        \"task\": \"Integration\",\n        \"start\": \"2024-04-23\",\n        \"end\": \"2024-05-06\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"API Development\", \"Frontend Components\"],\n    },\n    # Testing Phase\n    {\n        \"task\": \"Unit Testing\",\n        \"start\": \"2024-03-19\",\n        \"end\": \"2024-04-05\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Backend Core\"],\n    },\n    {\n        \"task\": \"Integration Testing\",\n        \"start\": \"2024-05-07\",\n        \"end\": \"2024-05-17\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Integration\", \"Unit Testing\"],\n    },\n    {\n        \"task\": \"UAT\",\n        \"start\": \"2024-05-20\",\n        \"end\": \"2024-05-31\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Integration Testing\"],\n    },\n    {\"task\": \"Bug Fixes\", \"start\": \"2024-06-03\", \"end\": \"2024-06-14\", \"group\": \"Testing\", \"depends_on\": [\"UAT\"]},\n]\n\ndf = pd.DataFrame(tasks_data)\ndf[\"start\"] = pd.to_datetime(df[\"start\"])\ndf[\"end\"] = pd.to_datetime(df[\"end\"])\n\nref_date = df[\"start\"].min()\ndf[\"start_num\"] = (df[\"start\"] - ref_date).dt.days\ndf[\"end_num\"] = (df[\"end\"] - ref_date).dt.days\n\n# Y positions: group header row, then individual task rows\ngroups = list(df[\"group\"].unique())\nphase_colors = {group: IMPRINT_PALETTE[i] for i, group in enumerate(groups)}\n\ny_positions = {}\ntask_to_y = {}\ny_counter = 0\n\nfor group in groups:\n    y_positions[group] = y_counter\n    y_counter += 1\n    for task in df[df[\"group\"] == group][\"task\"].tolist():\n        task_to_y[task] = y_counter\n        y_counter += 1\n\ndf[\"y_pos\"] = df[\"task\"].map(task_to_y)\n\n# Canvas: 3200 × 1800 px (landscape 16:9) — bbox_inches must stay default (None)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Build lineplot data: two endpoints per task (sns.lineplot + units draws one bar per task)\nline_data = []\nfor _, row in df.iterrows():\n    line_data.extend(\n        [\n            {\"x\": row[\"start_num\"], \"y\": row[\"y_pos\"], \"task\": row[\"task\"], \"group\": row[\"group\"]},\n            {\"x\": row[\"end_num\"], \"y\": row[\"y_pos\"], \"task\": row[\"task\"], \"group\": row[\"group\"]},\n        ]\n    )\n\nline_df = pd.DataFrame(line_data)\n\n# Task bars via sns.lineplot with units — idiomatic seaborn approach for Gantt charts\nsns.lineplot(\n    data=line_df,\n    x=\"x\",\n    y=\"y\",\n    hue=\"group\",\n    units=\"task\",\n    estimator=None,\n    palette=phase_colors,\n    linewidth=10,\n    alpha=0.85,\n    ax=ax,\n    legend=False,\n)\n\n# Phase aggregate bars: semi-transparent span across all tasks in each group\nfor group in groups:\n    g_df = df[df[\"group\"] == group]\n    ax.plot(\n        [g_df[\"start_num\"].min(), g_df[\"end_num\"].max()],\n        [y_positions[group], y_positions[group]],\n        color=phase_colors[group],\n        linewidth=8,\n        alpha=0.35,\n        solid_capstyle=\"round\",\n    )\n\n# Phase completion milestones via sns.scatterplot — diamond markers sized for 3200×1800\nmilestone_df = pd.DataFrame(\n    [{\"x\": df[df[\"group\"] == g][\"end_num\"].max(), \"y\": y_positions[g], \"group\": g} for g in groups]\n)\n\nsns.scatterplot(\n    data=milestone_df,\n    x=\"x\",\n    y=\"y\",\n    hue=\"group\",\n    palette=phase_colors,\n    marker=\"D\",\n    s=280,\n    edgecolor=PAGE_BG,\n    linewidth=1.5,\n    zorder=6,\n    ax=ax,\n    legend=False,\n)\n\n# Dependency arrows: predecessor end → successor start, theme-adaptive ink color\n# Cross-phase arrows use larger arc radius to reduce clutter in busy transition zones\nfor _, row in df.iterrows():\n    if row[\"depends_on\"]:\n        for dep in row[\"depends_on\"]:\n            dep_row = df[df[\"task\"] == dep].iloc[0]\n            same_phase = dep_row[\"group\"] == row[\"group\"]\n            rad = 0.15 if same_phase else 0.3\n            arrow = FancyArrowPatch(\n                (dep_row[\"end_num\"], task_to_y[dep]),\n                (row[\"start_num\"], task_to_y[row[\"task\"]]),\n                connectionstyle=f\"arc3,rad={rad}\",\n                arrowstyle=\"->,head_width=0.4,head_length=0.3\",\n                color=INK_SOFT,\n                linewidth=1.2,\n                alpha=0.8,\n                zorder=5,\n            )\n            ax.add_patch(arrow)\n\n# Y-axis: hierarchical labels with bold group headers\nall_labels = []\nall_y = []\nfor group in groups:\n    all_labels.append(f\"▪ {group}\")\n    all_y.append(y_positions[group])\n    for task in df[df[\"group\"] == group][\"task\"].tolist():\n        all_labels.append(f\"   {task}\")\n        all_y.append(task_to_y[task])\n\nax.set_yticks(all_y)\nax.set_yticklabels(all_labels, fontsize=7.5)\nax.invert_yaxis()\n\nfor label in ax.get_yticklabels():\n    if label.get_text().startswith(\"▪\"):\n        label.set_fontweight(\"bold\")\n        label.set_fontsize(9)\n\n# X-axis: biweekly date ticks\nmax_day = int(df[\"end_num\"].max()) + 7\ndate_ticks = np.arange(0, max_day, 14)\ndate_labels = [(ref_date + pd.Timedelta(days=int(d))).strftime(\"%b %d\") for d in date_ticks]\nax.set_xticks(date_ticks)\nax.set_xticklabels(date_labels, fontsize=8, rotation=45, ha=\"right\")\nax.set_xlim(-2, max_day)\n\n# Title, subtitle, and axis labels\nax.set_title(\"gantt-dependencies · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=22)\nax.annotate(\n    \"Software Development Project  ·  Q1–Q2 2024\",\n    xy=(0.5, 1.02),\n    xycoords=\"axes fraction\",\n    ha=\"center\",\n    va=\"bottom\",\n    fontsize=9,\n    color=INK_SOFT,\n    style=\"italic\",\n)\nax.set_xlabel(\"Project Timeline (2024)\", fontsize=10, color=INK)\nax.set_ylabel(\"Tasks by Phase\", fontsize=10, color=INK)\n\n# Subtle vertical-only grid\nax.grid(True, axis=\"x\", alpha=0.15, linewidth=0.8, color=INK)\nax.grid(False, axis=\"y\")\nax.set_axisbelow(True)\n\n# Legend: phase color patches, dotted divider, then dependency arrow indicator\nlegend_patches = [mpatches.Patch(color=phase_colors[g], alpha=0.85, label=g) for g in groups]\ndivider = plt.Line2D([], [], linewidth=0.5, color=INK_SOFT, linestyle=\":\", alpha=0.6, label=\" \")\narrow_legend = plt.Line2D([0], [0], color=INK_SOFT, linewidth=1.5, marker=\">\", markersize=7, label=\"Dependency\")\nax.legend(\n    handles=legend_patches + [divider, arrow_legend],\n    loc=\"upper right\",\n    fontsize=7.5,\n    framealpha=0.9,\n    title=\"Phases & Flow\",\n    title_fontsize=8,\n    handlelength=1.5,\n    labelspacing=0.35,\n)\n\n# Remove left spine for cleaner look; keep bottom for time axis reference\nsns.despine(left=True, bottom=False, ax=ax)\n\n# Manual padding — avoids bbox_inches=\"tight\" canvas drift; bottom raised for x-label clearance\nfig.subplots_adjust(left=0.25, right=0.97, top=0.89, bottom=0.17)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}