Skip to content

feat: activity calendar heatmap (GitHub-style) #27

@jschloman

Description

@jschloman

Summary

Add a full-year calendar heatmap (GitHub contribution graph style) to the Overview tab, showing daily activity intensity across all loaded data sources. Works for scrobbles, check-ins, fitness minutes, films watched — or a combined total.

Depends on: #23 (theme foundation)

Library

Use plotly-calplot (or its successor plotly-calheatmap if the former is unavailable):

pip install plotly-calplot

Implementation

from plotly_calplot import calplot

fig = calplot(
    activity_df,          # columns: date (datetime), value (int)
    x="date",
    y="value",
    dark_theme=True,
    colorscale=[[0, "#141c2f"], [0.3, "#312e81"], [0.7, "#6366f1"], [1.0, "#22d3ee"]],
    years_title_gap=20,
    space_between_plots=0.08,
    month_lines_color="#2d3a52",
    month_lines_width=2,
)
st.plotly_chart(fig, use_container_width=True)

Source selector

Sidebar radio (visible only when multiple sources loaded):

  • "All activity" — sum across all sources per day
  • "Music" — scrobble count per day
  • "Check-ins" — check-ins per day
  • "Fitness" — active minutes per day

Data prep

Add get_daily_activity(df, source="all") -> pd.DataFrame to analysis_utils.py. Returns a DataFrame with date (datetime, midnight UTC) and value (int) columns, with zero-filled gaps for days with no activity so the calendar renders completely.

Fallback

If plotly-calplot is unavailable, implement using Altair's mark_rect() with week (column) × day-of-week (row) encoding as a fallback. Document the alternative in a comment.

Acceptance criteria

  • Calendar heatmap renders for a full year with the indigo→cyan colorscale
  • Source selector switches the metric being displayed
  • Days with no activity shown in card background color (not transparent)
  • Hover tooltip shows date and activity count
  • get_daily_activity() covered by unit tests
  • ruff check ., mypy, and pytest --cov-fail-under=80 all pass

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions