{"spec_id":"timeline-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\ntimeline-basic: Event Timeline\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-05-11\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    ggsize,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\nfrom lets_plot.export import ggsave\n\n\nLetsPlot.setup_html()\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\n# Okabe-Ito palette (first series always #009E73)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data - Software project milestones (10 events for better readability)\nevents = pd.DataFrame(\n    {\n        \"date\": pd.to_datetime(\n            [\n                \"2024-01-15\",\n                \"2024-02-28\",\n                \"2024-04-01\",\n                \"2024-05-15\",\n                \"2024-06-20\",\n                \"2024-07-25\",\n                \"2024-09-01\",\n                \"2024-10-10\",\n                \"2024-11-15\",\n                \"2024-12-20\",\n            ]\n        ),\n        \"event\": [\n            \"Project Kickoff\",\n            \"Requirements Done\",\n            \"Design Review\",\n            \"Alpha Release\",\n            \"Beta Testing\",\n            \"Feature Freeze\",\n            \"Release Candidate\",\n            \"Security Audit\",\n            \"Docs Complete\",\n            \"v1.0 Launch\",\n        ],\n        \"category\": [\n            \"Planning\",\n            \"Planning\",\n            \"Design\",\n            \"Development\",\n            \"Testing\",\n            \"Development\",\n            \"Release\",\n            \"Testing\",\n            \"Release\",\n            \"Release\",\n        ],\n    }\n)\n\n# Convert dates to numeric for plotting and create alternating y positions\nevents[\"date_num\"] = (events[\"date\"] - events[\"date\"].min()).dt.days\nevents[\"y_offset\"] = [1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0, 1.0, -1.0]\nevents[\"label_y\"] = [1.5, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5, -1.5, 1.5, -1.5]\n\n# Create month breaks for x-axis\nmonth_breaks = [0, 31, 59, 90, 121, 152, 182, 213, 244, 274, 305, 335]\nmonth_labels = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\n# Create plot\nplot = (\n    ggplot(events)\n    # Timeline axis (horizontal line at y=0)\n    + geom_hline(yintercept=0, color=INK_SOFT, size=1.5)\n    # Vertical connectors from axis to event points\n    + geom_segment(\n        mapping=aes(x=\"date_num\", xend=\"date_num\", yend=\"y_offset\", color=\"category\"), y=0, size=1.2, alpha=0.7\n    )\n    # Event points\n    + geom_point(mapping=aes(x=\"date_num\", y=\"y_offset\", color=\"category\"), size=6, alpha=0.9)\n    # Event labels (positioned at label_y)\n    + geom_text(mapping=aes(x=\"date_num\", y=\"label_y\", label=\"event\"), size=10, color=INK)\n    # Color scale using Okabe-Ito palette\n    + scale_color_manual(values=IMPRINT, name=\"Phase\")\n    # Axis configuration - use simple month labels with padding\n    + scale_x_continuous(name=\"Month (2024)\", breaks=month_breaks, labels=month_labels, limits=[-15, 365])\n    + scale_y_continuous(limits=[-2.2, 2.2])\n    # Labels\n    + labs(title=\"timeline-basic · letsplot · anyplot.ai\", y=\"\")\n    # Theme\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        plot_title=element_text(size=24, color=INK),\n        axis_title_x=element_text(size=20, color=INK),\n        axis_text_x=element_text(size=16, color=INK_SOFT),\n        axis_text_y=element_blank(),\n        axis_ticks_y=element_blank(),\n        axis_line_x=element_line(color=INK_SOFT, size=0.5),\n        axis_line_y=element_blank(),\n        legend_title=element_text(size=18, color=INK),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_position=\"right\",\n        panel_grid_major_y=element_blank(),\n        panel_grid_minor=element_blank(),\n    )\n    + ggsize(1600, 900)\n)\n\n# Save as PNG (scale 3x for 4800x2700 px) and HTML\nggsave(plot, filename=f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, filename=f\"plot-{THEME}.html\", path=\".\")\n"}