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
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 successorplotly-calheatmapif the former is unavailable):Implementation
Source selector
Sidebar radio (visible only when multiple sources loaded):
Data prep
Add
get_daily_activity(df, source="all") -> pd.DataFrametoanalysis_utils.py. Returns a DataFrame withdate(datetime, midnight UTC) andvalue(int) columns, with zero-filled gaps for days with no activity so the calendar renders completely.Fallback
If
plotly-calplotis unavailable, implement using Altair'smark_rect()withweek(column) ×day-of-week(row) encoding as a fallback. Document the alternative in a comment.Acceptance criteria
get_daily_activity()covered by unit testsruff check .,mypy, andpytest --cov-fail-under=80all pass