Created
March 22, 2026 17:07
-
-
Save lemire/7342b4c02fe05144513c08b40bc69c23 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| import matplotlib.dates as mdates | |
| from datetime import datetime, timedelta | |
| username = "lemire" # use your GH username | |
| GITHUB_TOKEN = '' # put your GH token | |
| HEADERS = { | |
| 'Authorization': f'token {GITHUB_TOKEN}', | |
| 'Accept': 'application/vnd.github.v3+json' | |
| } | |
| # Fetch contribution calendar via GraphQL (weekly-ish granularity via days) | |
| all_weeks = [] | |
| for year in range(2016, 2027): | |
| from_date = f"{year}-01-01T00:00:00Z" | |
| to_date = f"{year}-12-31T23:59:59Z" | |
| query = """ | |
| query { | |
| user(login: "%s") { | |
| contributionsCollection(from: "%s", to: "%s") { | |
| contributionCalendar { | |
| weeks { | |
| contributionDays { | |
| contributionCount | |
| date | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| """ % (username, from_date, to_date) | |
| response = requests.post( | |
| "https://api.github.com/graphql", | |
| headers=HEADERS, | |
| json={"query": query} | |
| ).json() | |
| if "errors" in response: | |
| print(f"Erreurs GraphQL pour {year}:", response["errors"]) | |
| continue | |
| if "data" not in response or response["data"] is None: | |
| print(f"Erreur dans la réponse GraphQL pour {year}:", response) | |
| continue | |
| weeks = response["data"]["user"]["contributionsCollection"]["contributionCalendar"]["weeks"] | |
| all_weeks.extend(weeks) | |
| # Parse the data | |
| weeks = all_weeks | |
| data = [] | |
| for week in weeks: | |
| for day in week["contributionDays"]: | |
| if day["contributionCount"] > 0: | |
| data.append({ | |
| "date": datetime.fromisoformat(day["date"]), | |
| "count": day["contributionCount"] | |
| }) | |
| df = pd.DataFrame(data) | |
| df.set_index("date", inplace=True) | |
| # Aggregate to weekly (resample to week start) | |
| weekly = df.resample("W-MON")["count"].sum().fillna(0) | |
| # Plot | |
| plt.rcParams.update({'font.size': 24}) | |
| fig, ax = plt.subplots(figsize=(14, 6)) | |
| ax.bar(weekly.index, weekly.values, width=5, color="steelblue", alpha=0.6, label='Weekly commits') | |
| # Median before 2022 and after 2023 | |
| cutoff_before = pd.Timestamp('2022-01-01') | |
| cutoff_after = pd.Timestamp('2023-01-01') | |
| median_before = weekly[weekly.index < cutoff_before].median() | |
| median_after = weekly[weekly.index >= cutoff_after].median() | |
| ax.hlines(median_before, weekly.index[0], cutoff_before, colors='red', linewidth=2, | |
| label=f'Median before 2022: {median_before:.0f}') | |
| ax.hlines(median_after, cutoff_after, weekly.index[-1], colors='orange', linewidth=2, | |
| label=f'Median from 2023: {median_after:.0f}') | |
| ax.axvline(cutoff_before, color='gray', linewidth=1, linestyle='--') | |
| ax.axvline(cutoff_after, color='gray', linewidth=1, linestyle='--') | |
| ax.legend(fontsize=24) | |
| ax.xaxis.set_major_locator(mdates.YearLocator()) | |
| ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y')) | |
| ax.set_xlim(weekly.index[0], weekly.index[-1]) | |
| ax.spines['top'].set_visible(False) | |
| ax.spines['right'].set_visible(False) | |
| ax.set_title(f"Weekly commits – @{username}", fontsize=24) | |
| ax.set_xlabel("Year", fontsize=24) | |
| ax.set_ylabel("Number of commits", fontsize=24) | |
| ax.tick_params(labelsize=24) | |
| plt.tight_layout() | |
| plt.savefig('contributions.png', dpi=150) | |
| print("Graph saved to contributions.png") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment