{"spec_id":"gantt-dependencies","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ngantt-dependencies: Gantt Chart with Dependencies\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-06-02\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent this file from shadowing the installed plotly package\n_this_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _this_dir]\n\nimport pandas as pd\nimport plotly.graph_objects as go\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Imprint palette — theme-adaptive chrome\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\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint categorical palette — positions 1–5, canonical order\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data — Software Development Project with phases and dependencies\ntasks = [\n    # Requirements Phase\n    {\n        \"task\": \"Requirements Gathering\",\n        \"start\": \"2024-03-01\",\n        \"end\": \"2024-03-08\",\n        \"group\": \"Requirements\",\n        \"depends_on\": [],\n    },\n    {\n        \"task\": \"Requirements Analysis\",\n        \"start\": \"2024-03-08\",\n        \"end\": \"2024-03-12\",\n        \"group\": \"Requirements\",\n        \"depends_on\": [\"Requirements Gathering\"],\n    },\n    {\n        \"task\": \"Requirements Sign-off\",\n        \"start\": \"2024-03-12\",\n        \"end\": \"2024-03-14\",\n        \"group\": \"Requirements\",\n        \"depends_on\": [\"Requirements Analysis\"],\n    },\n    # Design Phase\n    {\n        \"task\": \"System Architecture\",\n        \"start\": \"2024-03-14\",\n        \"end\": \"2024-03-20\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"Requirements Sign-off\"],\n    },\n    {\n        \"task\": \"Database Design\",\n        \"start\": \"2024-03-20\",\n        \"end\": \"2024-03-26\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"System Architecture\"],\n    },\n    {\n        \"task\": \"UI/UX Design\",\n        \"start\": \"2024-03-20\",\n        \"end\": \"2024-03-28\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"System Architecture\"],\n    },\n    {\n        \"task\": \"Design Review\",\n        \"start\": \"2024-03-28\",\n        \"end\": \"2024-03-30\",\n        \"group\": \"Design\",\n        \"depends_on\": [\"Database Design\", \"UI/UX Design\"],\n    },\n    # Development Phase\n    {\n        \"task\": \"Backend API Development\",\n        \"start\": \"2024-03-30\",\n        \"end\": \"2024-04-15\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"Design Review\"],\n    },\n    {\n        \"task\": \"Frontend Development\",\n        \"start\": \"2024-03-30\",\n        \"end\": \"2024-04-16\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"Design Review\"],\n    },\n    {\n        \"task\": \"Database Implementation\",\n        \"start\": \"2024-03-30\",\n        \"end\": \"2024-04-08\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"Design Review\"],\n    },\n    {\n        \"task\": \"Integration\",\n        \"start\": \"2024-04-16\",\n        \"end\": \"2024-04-22\",\n        \"group\": \"Development\",\n        \"depends_on\": [\"Backend API Development\", \"Frontend Development\", \"Database Implementation\"],\n    },\n    # Testing Phase\n    {\n        \"task\": \"Unit Testing\",\n        \"start\": \"2024-04-08\",\n        \"end\": \"2024-04-20\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Database Implementation\"],\n    },\n    {\n        \"task\": \"Integration Testing\",\n        \"start\": \"2024-04-22\",\n        \"end\": \"2024-04-30\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Integration\"],\n    },\n    {\n        \"task\": \"User Acceptance Testing\",\n        \"start\": \"2024-04-30\",\n        \"end\": \"2024-05-07\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Integration Testing\"],\n    },\n    {\n        \"task\": \"Bug Fixes\",\n        \"start\": \"2024-05-01\",\n        \"end\": \"2024-05-10\",\n        \"group\": \"Testing\",\n        \"depends_on\": [\"Unit Testing\"],\n    },\n    # Deployment Phase\n    {\n        \"task\": \"Deployment Preparation\",\n        \"start\": \"2024-05-07\",\n        \"end\": \"2024-05-10\",\n        \"group\": \"Deployment\",\n        \"depends_on\": [\"User Acceptance Testing\"],\n    },\n    {\n        \"task\": \"Production Deployment\",\n        \"start\": \"2024-05-10\",\n        \"end\": \"2024-05-12\",\n        \"group\": \"Deployment\",\n        \"depends_on\": [\"Deployment Preparation\", \"Bug Fixes\"],\n    },\n    {\n        \"task\": \"Post-Deployment Review\",\n        \"start\": \"2024-05-12\",\n        \"end\": \"2024-05-14\",\n        \"group\": \"Deployment\",\n        \"depends_on\": [\"Production Deployment\"],\n    },\n]\n\ndf = pd.DataFrame(tasks)\ndf[\"start\"] = pd.to_datetime(df[\"start\"])\ndf[\"end\"] = pd.to_datetime(df[\"end\"])\n\n# Group colors — Imprint palette canonical order\ngroup_order = [\"Requirements\", \"Design\", \"Development\", \"Testing\", \"Deployment\"]\ngroup_colors = dict(zip(group_order, IMPRINT_PALETTE, strict=False))\n\n# Compute critical path by backward traversal from project end\ntask_deps = {row[\"task\"]: row[\"depends_on\"] for _, row in df.iterrows()}\ntask_ends = {row[\"task\"]: row[\"end\"] for _, row in df.iterrows()}\n\nall_predecessors = {dep for deps in task_deps.values() for dep in deps}\nterminal_tasks = [t for t in task_deps if t not in all_predecessors]\nlast_task = max(terminal_tasks, key=lambda t: task_ends[t])\n\ncritical_path = set()\ncritical_edges = set()\ncurrent = last_task\nwhile current:\n    critical_path.add(current)\n    deps = task_deps[current]\n    if not deps:\n        break\n    binding = max(deps, key=lambda d: task_ends[d])\n    critical_edges.add((binding, current))\n    current = binding\n\n# Build ordered task list (group headers + indented tasks)\ngroups_agg = df.groupby(\"group\").agg({\"start\": \"min\", \"end\": \"max\"}).reset_index()\n\nordered_items = []\ntask_y_labels = {}\nfor group in group_order:\n    ordered_items.append({\"label\": f\"▼ {group}\", \"is_group\": True, \"group\": group})\n    for _, row in df[df[\"group\"] == group].sort_values(\"start\").iterrows():\n        label = f\"   {row['task']}\"\n        ordered_items.append({\"label\": label, \"is_group\": False, \"task\": row})\n        task_y_labels[row[\"task\"]] = label\n\ny_categories = [item[\"label\"] for item in ordered_items]\n\n# Create figure\nfig = go.Figure()\n\n# Draw bars\nfor group in group_order:\n    color = group_colors[group]\n    g = groups_agg[groups_agg[\"group\"] == group].iloc[0]\n    dur_ms = (g[\"end\"] - g[\"start\"]).total_seconds() * 1000\n\n    # Group summary bar (wider, shown in legend)\n    fig.add_trace(\n        go.Bar(\n            y=[f\"▼ {group}\"],\n            x=[dur_ms],\n            base=[g[\"start\"].isoformat()],\n            orientation=\"h\",\n            name=group,\n            legendgroup=group,\n            marker={\"color\": color, \"line\": {\"width\": 0}},\n            width=0.7,\n            hovertemplate=(\n                f\"<b>{group} Phase</b><br>\"\n                f\"Start: {g['start'].strftime('%b %d, %Y')}<br>\"\n                f\"End: {g['end'].strftime('%b %d, %Y')}<extra></extra>\"\n            ),\n        )\n    )\n\n    # Individual task bars (narrower, hidden from legend)\n    for _, task in df[df[\"group\"] == group].sort_values(\"start\").iterrows():\n        is_cp = task[\"task\"] in critical_path\n        label = task_y_labels[task[\"task\"]]\n        dur_ms_task = (task[\"end\"] - task[\"start\"]).total_seconds() * 1000\n        dur_days = (task[\"end\"] - task[\"start\"]).days\n\n        fig.add_trace(\n            go.Bar(\n                y=[label],\n                x=[dur_ms_task],\n                base=[task[\"start\"].isoformat()],\n                orientation=\"h\",\n                showlegend=False,\n                legendgroup=group,\n                marker={\n                    \"color\": color,\n                    \"opacity\": 0.95 if is_cp else 0.6,\n                    \"line\": {\"width\": 1.5 if is_cp else 0, \"color\": \"rgba(0,0,0,0.25)\" if is_cp else \"rgba(0,0,0,0)\"},\n                },\n                width=0.45,\n                hovertemplate=(\n                    f\"<b>{task['task']}</b>\" + (\" ⚡ Critical Path\" if is_cp else \"\") + f\"<br>Phase: {group}<br>\"\n                    f\"Start: {task['start'].strftime('%b %d, %Y')}<br>\"\n                    f\"End: {task['end'].strftime('%b %d, %Y')}<br>\"\n                    f\"Duration: {dur_days} days<extra></extra>\"\n                ),\n            )\n        )\n\n# Dependency arrows (critical path edges highlighted in Imprint matte-red)\nCP_RED = \"#AE3030\"\nfor _, task in df.iterrows():\n    for dep in task[\"depends_on\"]:\n        if dep in task_y_labels:\n            pred = df[df[\"task\"] == dep].iloc[0]\n            is_cp_edge = (dep, task[\"task\"]) in critical_edges\n\n            fig.add_annotation(\n                x=task[\"start\"].isoformat(),\n                y=task_y_labels[task[\"task\"]],\n                ax=pred[\"end\"].isoformat(),\n                ay=task_y_labels[dep],\n                xref=\"x\",\n                yref=\"y\",\n                axref=\"x\",\n                ayref=\"y\",\n                showarrow=True,\n                arrowhead=3,\n                arrowsize=1.3,\n                arrowwidth=2.5 if is_cp_edge else 1.3,\n                arrowcolor=CP_RED if is_cp_edge else INK_SOFT,\n                opacity=0.9 if is_cp_edge else 0.35,\n            )\n\n# Layout\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font=dict(color=INK),\n    title=dict(\n        text=\"gantt-dependencies · python · plotly · anyplot.ai\", font=dict(size=16, color=INK), x=0.5, xanchor=\"center\"\n    ),\n    xaxis=dict(\n        title=dict(text=\"Timeline (2024)\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        type=\"date\",\n        tickformat=\"%b %d\",\n        gridcolor=GRID,\n        showgrid=True,\n        dtick=7 * 24 * 60 * 60 * 1000,\n        tickangle=0,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n        mirror=False,\n    ),\n    yaxis=dict(\n        tickfont=dict(size=10, color=INK_SOFT),\n        categoryorder=\"array\",\n        categoryarray=y_categories[::-1],\n        showgrid=False,\n        linecolor=INK_SOFT,\n        mirror=False,\n    ),\n    barmode=\"overlay\",\n    legend=dict(\n        title=dict(text=\"Project Phases\", font=dict(size=10, color=INK)),\n        font=dict(size=10, color=INK_SOFT),\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n        orientation=\"h\",\n        yanchor=\"bottom\",\n        y=1.02,\n        xanchor=\"center\",\n        x=0.5,\n        itemwidth=30,\n    ),\n    margin=dict(l=160, r=40, t=80, b=90),\n)\n\n# Dependency type legend (bottom-right annotation)\nfig.add_annotation(\n    x=0.99,\n    y=-0.13,\n    xref=\"paper\",\n    yref=\"paper\",\n    text=\"━━▶ Critical Path\",\n    showarrow=False,\n    font=dict(size=9, color=CP_RED),\n    xanchor=\"right\",\n)\nfig.add_annotation(\n    x=0.99,\n    y=-0.19,\n    xref=\"paper\",\n    yref=\"paper\",\n    text=\"──▶ Dependency (finish-to-start)\",\n    showarrow=False,\n    font=dict(size=9, color=INK_SOFT),\n    xanchor=\"right\",\n)\n\n# Save — landscape 3200×1800\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}