{"spec_id":"timeline-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ntimeline-basic: Event Timeline\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-11\n\"\"\"\n\nfrom datetime import date\n\nimport cairosvg\nimport pygal\nfrom pygal.style import Style\n\n\n# Data - Software project milestones\nevents = [\n    (date(2024, 1, 15), \"Project Kickoff\", \"Planning\"),\n    (date(2024, 2, 10), \"Requirements Complete\", \"Planning\"),\n    (date(2024, 3, 20), \"Architecture Design\", \"Design\"),\n    (date(2024, 4, 25), \"Development Start\", \"Development\"),\n    (date(2024, 6, 15), \"Alpha Release\", \"Development\"),\n    (date(2024, 8, 1), \"Beta Release\", \"Testing\"),\n    (date(2024, 9, 10), \"User Acceptance\", \"Testing\"),\n    (date(2024, 10, 20), \"Production Launch\", \"Deployment\"),\n]\n\n# Sort events by date\nevents = sorted(events, key=lambda x: x[0])\n\n# Category colors mapped to pygal color indices\ncategories = [\"Planning\", \"Design\", \"Development\", \"Testing\", \"Deployment\"]\ncategory_colors = {\n    \"Planning\": \"#306998\",\n    \"Design\": \"#FFD43B\",\n    \"Development\": \"#4ECDC4\",\n    \"Testing\": \"#FF6B6B\",\n    \"Deployment\": \"#45B7D1\",\n}\n\n# Custom style for large canvas (4800x2700)\ncustom_style = Style(\n    background=\"white\",\n    plot_background=\"white\",\n    foreground=\"#333333\",\n    foreground_strong=\"#333333\",\n    foreground_subtle=\"#666666\",\n    colors=tuple(category_colors[c] for c in categories),\n    title_font_size=60,\n    label_font_size=32,\n    major_label_font_size=32,\n    legend_font_size=36,\n    value_font_size=28,\n    tooltip_font_size=28,\n)\n\n# Reference date for x-axis positioning\nreference_date = date(2024, 1, 1)\n\n# Create XY chart - using native pygal scatter capabilities\nchart = pygal.XY(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"Software Project Milestones · timeline-basic · pygal · pyplots.ai\",\n    show_legend=True,\n    legend_at_bottom=False,\n    legend_box_size=32,\n    x_title=\"Month (2024)\",\n    y_title=\"Project Phase\",\n    show_dots=True,\n    dots_size=18,\n    stroke=False,\n    show_x_guides=True,\n    show_y_guides=True,\n    margin=120,\n    x_labels_major_every=1,\n    truncate_legend=-1,\n)\n\n# Custom x-axis labels for months\nchart.x_labels = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\"]\nchart.x_labels_major = chart.x_labels\n\n# Y positions for alternating above/below layout - spread vertically to use more canvas\ny_positions = {\n    0: 4,  # Above\n    1: 1,  # Below\n    2: 4,  # Above\n    3: 1,  # Below\n    4: 4,  # Above\n    5: 1,  # Below\n    6: 4,  # Above\n    7: 1,  # Below\n}\n\n# Group events by category for legend\ncategory_data = {cat: [] for cat in categories}\n\nfor i, (event_date, event_name, category) in enumerate(events):\n    # X position: days since Jan 1, scaled to month\n    days = (event_date - reference_date).days\n    x_val = days / 30.44  # Average days per month\n\n    # Y position for alternating layout\n    y_val = y_positions[i]\n\n    # Add data point with label\n    category_data[category].append({\"value\": (x_val, y_val), \"label\": f\"{event_name}\\n{event_date.strftime('%b %d')}\"})\n\n# Add each category as a series\nfor category in categories:\n    if category_data[category]:\n        chart.add(category, category_data[category])\n\n# Set y range to maximize vertical usage (values 0-5 with padding)\nchart.range = (0, 5)\n\n# Render base SVG\nsvg_string = chart.render().decode(\"utf-8\")\n\n# Add a horizontal timeline axis in the middle (y=2.5) as visual anchor\n# Calculate plot area based on pygal defaults with our margins\nPLOT_LEFT = 400\nPLOT_RIGHT = 4550\nPLOT_TOP = 300\nPLOT_BOTTOM = 2200\nPLOT_HEIGHT = PLOT_BOTTOM - PLOT_TOP\n\n# Timeline at y=2.5 (middle of 0-5 range)\ntimeline_y = PLOT_TOP + PLOT_HEIGHT * (1 - 2.5 / 5)  # Invert because SVG y goes down\n\n# Create timeline axis elements\ntimeline_svg = f\"\"\"\n<g class=\"timeline-axis\">\n  <line x1=\"{PLOT_LEFT}\" y1=\"{timeline_y:.0f}\" x2=\"{PLOT_RIGHT}\" y2=\"{timeline_y:.0f}\"\n        stroke=\"#999999\" stroke-width=\"4\" stroke-dasharray=\"15,8\"/>\n  <text x=\"{PLOT_LEFT - 80}\" y=\"{timeline_y + 10:.0f}\" font-family=\"Consolas, sans-serif\"\n        font-size=\"28\" fill=\"#666666\" text-anchor=\"end\">Timeline</text>\n</g>\n\"\"\"\n\n# Inject timeline before </svg>\nsvg_output = svg_string.replace(\"</svg>\", f\"{timeline_svg}\\n</svg>\")\n\n# Save outputs\nwith open(\"plot.html\", \"w\") as f:\n    f.write(svg_output)\n\ncairosvg.svg2png(bytestring=svg_output.encode(), write_to=\"plot.png\")\n"}