{"spec_id":"heatmap-calendar","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nheatmap-calendar: Basic Calendar Heatmap\nLibrary: altair 6.2.2 | Python 3.13.14\nQuality: 94/100 | Updated: 2026-07-23\n\"\"\"\n\nimport os\nimport sys\n\n\n# The file is named altair.py; remove its own directory from sys.path so\n# `import altair` resolves to the library, not this script.\n_HERE = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if not p or os.path.abspath(p) != _HERE]\nos.chdir(_HERE)  # saves (plot-*.png, plot-*.html) land in the implementations dir\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\n\n\n# Theme-adaptive chrome tokens (Imprint palette)\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# Data - create one year of daily activity data (GitHub-style contribution graph)\nnp.random.seed(42)\n\n# Generate dates for one year, dropping a short break to exercise the\n# spec's \"handle missing dates gracefully\" requirement (those days simply\n# have no row, so mark_rect leaves the cell blank/neutral).\nstart_date = pd.Timestamp(\"2024-01-01\")\nend_date = pd.Timestamp(\"2024-12-31\")\nmissing_dates = pd.date_range(\"2024-07-04\", \"2024-07-08\", freq=\"D\")\ndates = pd.date_range(start=start_date, end=end_date, freq=\"D\").difference(missing_dates)\n\n# Generate realistic activity values (commits/contributions)\n# More activity on weekdays, less on weekends, with some variation\nvalues = []\nfor date in dates:\n    weekday = date.weekday()\n    # Base activity: higher on weekdays\n    if weekday < 5:  # Weekday\n        base = np.random.choice([0, 2, 5, 8, 12], p=[0.2, 0.25, 0.3, 0.15, 0.1])\n    else:  # Weekend\n        base = np.random.choice([0, 1, 3, 5], p=[0.5, 0.25, 0.15, 0.1])\n    # Add some noise\n    value = max(0, base + np.random.randint(-1, 2))\n    values.append(value)\n\n# Create DataFrame\ndf = pd.DataFrame({\"date\": dates, \"value\": values})\n\n# Extract calendar components\ndf[\"weekday\"] = df[\"date\"].dt.weekday  # 0=Monday, 6=Sunday\ndf[\"month\"] = df[\"date\"].dt.month\ndf[\"month_name\"] = df[\"date\"].dt.strftime(\"%b\")\n\n# Create week number that's continuous across the year\ndf[\"week_of_year\"] = (df[\"date\"] - start_date).dt.days // 7\n\n# Map weekday numbers to names (for y-axis)\nweekday_names = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\"]\ndf[\"weekday_name\"] = df[\"weekday\"].map(lambda x: weekday_names[x])\n\n# Create month labels for x-axis (first week of each month)\nmonth_labels = df.groupby(\"month\").agg({\"week_of_year\": \"min\", \"month_name\": \"first\"}).reset_index()\n# Assign to top row (Monday) for positioning\nmonth_labels[\"weekday_name\"] = \"Mon\"\n\n# Call out the single busiest day of the year (design storytelling: focal point)\npeak_day = df.loc[[df[\"value\"].idxmax()]]\npeak_date_str = peak_day[\"date\"].dt.strftime(\"%b %-d\").iloc[0]\npeak_value = int(peak_day[\"value\"].iloc[0])\n\n# Plot - calendar heatmap. Sequential imprint_seq (brand green -> blue) for\n# the single-polarity contribution counts.\nheatmap = (\n    alt.Chart(df)\n    .mark_rect()\n    .encode(\n        x=alt.X(\"week_of_year:O\", title=\"\", axis=alt.Axis(labels=False, ticks=False, domain=False)),\n        y=alt.Y(\n            \"weekday_name:O\",\n            title=\"Weekday\",\n            sort=weekday_names,\n            axis=alt.Axis(labelFontSize=12, titleFontSize=11, domain=False, ticks=False),\n        ),\n        color=alt.Color(\n            \"value:Q\",\n            scale=alt.Scale(range=[\"#009E73\", \"#4467A3\"], domain=[0, 15]),\n            legend=alt.Legend(title=\"Contributions\", titleFontSize=10, labelFontSize=10, values=[0, 5, 10, 15]),\n        ),\n        tooltip=[\n            alt.Tooltip(\"date:T\", title=\"Date\", format=\"%Y-%m-%d\"),\n            alt.Tooltip(\"value:Q\", title=\"Contributions\"),\n            alt.Tooltip(\"weekday_name:N\", title=\"Day\"),\n        ],\n    )\n)\n\n# Month labels as a text layer at the top\nmonth_text = (\n    alt.Chart(month_labels)\n    .mark_text(fontSize=12, align=\"left\", baseline=\"bottom\", dy=-8, fontWeight=\"bold\", color=INK)\n    .encode(x=alt.X(\"week_of_year:O\"), y=alt.Y(\"weekday_name:O\", sort=weekday_names), text=\"month_name:N\")\n)\n\n# Highlight ring around the year's busiest day - draws the eye to a focal point\npeak_highlight = (\n    alt.Chart(peak_day)\n    .mark_rect(filled=False, stroke=INK, strokeWidth=2)\n    .encode(x=alt.X(\"week_of_year:O\"), y=alt.Y(\"weekday_name:O\", sort=weekday_names))\n)\n\n# Combine heatmap, month labels, and peak-day highlight\n# Title fontsize scaled from the 16px default: round(16 * 67/74) = 14\ntitle_text = \"Daily Contributions 2024 · heatmap-calendar · python · altair · anyplot.ai\"\nchart = (\n    alt.layer(heatmap, month_text, peak_highlight)\n    .properties(\n        width=620,\n        height=320,\n        background=PAGE_BG,\n        title=alt.Title(\n            title_text,\n            fontSize=14,\n            anchor=\"start\",\n            offset=20,\n            subtitle=f\"Outlined cell marks the busiest day: {peak_date_str} ({peak_value} contributions)\",\n            subtitleFontSize=11,\n            subtitleColor=INK_SOFT,\n        ),\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_axis(domainColor=INK_SOFT, tickColor=INK_SOFT, grid=False, labelColor=INK_SOFT, titleColor=INK)\n    .configure_title(color=INK)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\n# Save PNG, then pad (never crop) up to the exact canonical canvas\nchart.save(f\"plot-{THEME}.png\", scale_factor=4.0)\n\nTW, TH = 3200, 1800\n_img = Image.open(f\"plot-{THEME}.png\").convert(\"RGB\")\n_w, _h = _img.size\nif _w > TW or _h > TH:\n    raise SystemExit(\n        f\"altair vl-convert produced {_w}x{_h}, exceeds target {TW}x{TH}. \"\n        f\"Shrink chart .properties(width=, height=) values and re-render.\"\n    )\nif _w < TW or _h < TH:\n    _canvas = Image.new(\"RGB\", (TW, TH), PAGE_BG)\n    _canvas.paste(_img, ((TW - _w) // 2, (TH - _h) // 2))\n    _canvas.save(f\"plot-{THEME}.png\")\n\nchart.save(f\"plot-{THEME}.html\")\n"}