自分のGitHubリポジトリへのPR数とコミット数を集計する

# はじめに
特定期間の自身の活動を振り返るため、自分のGitHubレポジトリに対するコミット数とPR数を集計するプログラムを作りました。

# コード

```
pip install PyGithub pydantic-settings pandas tqdm
```

```Python:git_count.py
from collections import defaultdict

import pandas as pd
from github import Github
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from tqdm import tqdm


# 環境変数から設定を取得するクラス
class Settings(BaseSettings):
    GITHUB_TOKEN: str = Field(
        default="",
        description="GitHubのPersonal Access Token。ContentsとPull Requestsのread権限が必要",
    )
    username: str = Field(default="", description="GitHubのユーザー名")
    start_month: str = Field(default="1900-01", description="合計期間の開始月")
    end_month: str = Field(default="2100-01", description="合計期間の終了月")

    model_config = SettingsConfigDict(
        env_file=".env",
        case_sensitive=False,
        cli_parse_args=True,
        extra="allow",
    )


# 設定を読み込む
def main():
    settings = Settings()
    print(settings)

    # GitHub APIの初期化
    GITHUB_TOKEN = settings.GITHUB_TOKEN
    USERNAME = settings.username
    # 合計期間の開始月と終了月
    start_month = settings.start_month
    end_month = settings.end_month

    g = Github(GITHUB_TOKEN)
    user = g.get_user(USERNAME)

    # 各月ごとのコミット数とマージされたPR数を保持する辞書
    commit_data = defaultdict(int)
    pr_data = defaultdict(int)

    # ユーザー所有の各リポジトリに対して処理を行う
    for repo in tqdm(user.get_repos()):
        # 自分が作成したコミットを取得
        commits = repo.get_commits()
        for commit in commits:
            commit_date = commit.commit.author.date  # コミット日時
            month_str = commit_date.strftime("%Y-%m")
            commit_data[month_str] += 1

        # 閉じたプルリクエストを取得
        pulls = repo.get_pulls(state="closed")
        for pr in pulls:
            closed_date = pr.closed_at
            if closed_date is not None:
                month_str = closed_date.strftime("%Y-%m")
                pr_data[month_str] += 1

    # 辞書のデータを pandas DataFrame に変換し、月ごとにソート
    commit_df = pd.DataFrame(
        list(commit_data.items()), columns=["Month", "Commits"]
    ).sort_values("Month")
    pr_df = pd.DataFrame(
        list(pr_data.items()), columns=["Month", "Merged PRs"]
    ).sort_values("Month")

    print("=== コミット数（月ごと） ===")
    print(commit_df)
    print("\n=== マージ済みPR数（月ごと） ===")
    print(pr_df)

    # コミット数の合計を計算
    total_commits = commit_df[
        (commit_df["Month"] >= start_month) & (commit_df["Month"] <= end_month)
    ]["Commits"].sum()

    # マージ済みPR数の合計を計算
    total_prs = pr_df[(pr_df["Month"] >= start_month) & (pr_df["Month"] <= end_month)][
        "Merged PRs"
    ].sum()

    print(f"\n=== 期間({start_month}-{end_month})の合計 ===")
    print(f"コミット数合計: {total_commits}")
    print(f"マージ済みPR数合計: {total_prs}")


if __name__ == "__main__":
    main()
```


githubユーザ名を与えて実行すると、月ごとのコミット数、PR数と、指定した区間の合計数が出力されます。数値はダミーです。

```
% python git_count.py --username USERNAME --start_month 2024-10 --end_month 2025-03
=== コミット数（月ごと） ===
Month  Commits
53  2018-05        5
15  2018-08        7
14  2018-09        4
52  2018-10       20
61  2018-11       12
..      …      …
27  2024-11       18
55  2024-12        8
54  2025-01       10
18  2025-02       90
17  2025-03      150

[65 rows x 2 columns]

⸻

=== マージ済みPR数（月ごと） ===
Month  Merged PRs
4  2024-05           3
0  2024-10          12
3  2024-11           5
5  2024-12           2
2  2025-02          10
1  2025-03          30

⸻

=== 期間(2024/10-2025/03)の合計 ===
コミット数合計: 446
マージ済みPR数合計: 59
```

# 解説

## 設定値の取得
pydantic-settingを使うことで、環境変数またはコマンドライン引数から必要な情報を取得します。両方で指定される場合はコマンドライン引数が優先されます。

```Python
# 環境変数から設定を取得するクラス
class Settings(BaseSettings):
    GITHUB_TOKEN: str = Field(
        default="",
        description="GitHubのPersonal Access Token。ContentsとPull Requestsのread権限が必要",
    )
    username: str = Field(default="", description="GitHubのユーザー名")
    start_month: str = Field(default="1900-01", description="合計期間の開始月")
    end_month: str = Field(default="2100-01", description="合計期間の終了月")

    model_config = SettingsConfigDict(
        env_file=".env",
        case_sensitive=False,
        cli_parse_args=True,
        extra="allow",
    )
```

## コミット数とPR数の取得

自分のすべてのリポジトリに対して、コミットとPRを取得し、月単位で合計します。

```Python
    # ユーザー所有の各リポジトリに対して処理を行う
    for repo in tqdm(user.get_repos()):
        # 自分が作成したコミットを取得
        commits = repo.get_commits()
        for commit in commits:
            commit_date = commit.commit.author.date  # コミット日時
            month_str = commit_date.strftime("%Y-%m")
            commit_data[month_str] += 1

        # 閉じたプルリクエストを取得
        pulls = repo.get_pulls(state="closed")
        for pr in pulls:
            closed_date = pr.closed_at
            if closed_date is not None:
                month_str = closed_date.strftime("%Y-%m")
                pr_data[month_str] += 1
```

なおauthorは限定していません。
もし複数人で開発していて自分のみの貢献を知りたい場合は`commits = repo.get_commits(author=USERNAME)`、`pr.user.login == USERNAME`などを利用して絞るとよいです。