1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHubの草を表示するNotion用のウィジェットを作ってみた

Last updated at Posted at 2025-01-21

はじめに

Notion上でGitHubの草(コントリビューション)を確認できると便利だと思い、ウィジェットを作成してみました。

作ったもの

img_2.jpg.png

デモページになります。

iPhoneのウィジェットを参考にして作ってみました。
img_1.png

実装

1. GitHubのコントリビューションを取得する

GitHubのコントリビューション数を取得するため、以下のURLからスクレイピングを行います。

https://github.com/users/<UserName>/contributions

HTML構造は以下のようになっていました。

<td tabindex="0" 
    data-ix="{id?}" 
    aria-selected="false" 
    aria-describedby="contribution-graph-legend-level-{level}" 
    style="width: 10px" 
    data-date="{date}" 
    id="contribution-day-component-{row}-{col}" 
    data-level="{contribution-level}" 
    role="gridcell" 
    data-view-component="true" 
    class="ContributionCalendar-day">
</td>
<tool-tip id="tooltip-{id}" 
    for="contribution-day-component-{row}-{col}" 
    popover="manual" 
    data-direction="n" 
    data-type="label" 
    data-view-component="true" 
    class="sr-only position-absolute">
    {contribution-text}
</tool-tip>

これらから

  • <td> 要素のdata-date
  • <tool-tip> 要素の{contribution-text}の整数部分

を取得します。特に、{contribution-text}はその日のコントリビューションがないとNo contributions on {month} {day}.となるので場合分けします。また、日付の順番がぐちゃぐちゃなので、日付について昇順でソートします。

以下にPythonコードを示します。

python robot.py
import csv
import requests
from bs4 import BeautifulSoup

user_name = 'username'
url = "https://github.com/users/" + user_name + "/contributions"

response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")
days = soup.find_all("td", {"class": "ContributionCalendar-day"})

contributions = []
for day in days:
    date = day.get("data-date")
    
    tooltip = day.find_next("tool-tip")
    if tooltip:
        tooltip_text = tooltip.text.strip()
        if "No contributions" in tooltip_text:
            cnt = 0
        else:
            cnt = int(tooltip_text.split()[0])
    else:
        cnt = 0
    
    if date:
        contributions.append((date, cnt))

contributions.sort(key=lambda x: x[0])

2. Webサイトを作成する

コントリビューショングラフの色分けには以下の基準を使用します。

  • レベル0:コントリビューションなし
  • レベル1: $(0, \frac{最大値}{4}]$
  • レベル2: $(\frac{最大値}{4}, \frac{最大値}{2}]$
  • レベル3: $(\frac{最大値}{2}, \frac{3 *最大値}{4}]$
  • レベル4: $(\frac{3*最大値}{4},最大値]$

また、描画する日数を計算しなければいけません。例えば今日が月曜日だとすると7×6+2で合計44日間のデータが必要です。

img_3.png

datetimeモジュールのweekdayメソッドを使うと日付の曜日を数値で取得することができます。月曜日の場合は0を返すので工夫しました。

以下にFlaskの処理例を示します。

python app.py
@app.route('/')
def index():
    today = datetime.now()
    # 曜日
    if today.weekday() == 0:
        dow = 5
    elif today.weekday() == 6:
        dow = 6
    else:
        dow = 5 % today.weekday()
    size = 49 # 7の倍数
    contributions, _ = load_csv(file_path)
    
    # カレンダー
    calendar_data = []
    for i in range(size - dow):
        day = today - timedelta(days=size - dow - i - 1)
        date_str = day.strftime("%Y-%m-%d")
        count = contributions.get(date_str, 0)
        calendar_data.append({"day": day.day, "color": get_color(count)})

    show_data = [[] for _ in range(len(calendar_data) // 7 + 1)]
    for i in range(len(calendar_data)):
        show_data[i // 7].append(calendar_data[i])

    return render_template('index.html',calendar=show_data)

続いてフロント側の処理も書いていきます。このとき以下のようにWebサイトの中央にウィジェットとして表示したいものが来るようにします。

img_4.png

コントリビューションレベルに応じたカラーコードは以下の通りです。

レベル カラーコード
0 #2b2b2b
1 #0e4429
2 #006d32
3 #26a641
4 #39d353

また、Notionの背景色とそろえておく必要があります。

モード カラーコード
ライト #ffffff
ダーク #191919

3. AzureWebAppsでデプロイする

今回、Azureにデプロイする際にGitHub Actionsを使いたいのでGitHubにコードを上げます。アプリケーションのコードを以下のような構成で整理し、GitHubにアップロードします。このとき、app.py(またはapplication.py)とrequirements.txtはルートディレクトリに置かなければいけません。HTMLはtemplatesフォルダに、CSSはstaticフォルダに置きます。

project/
├── app.py                # メインのアプリケーションコード
├── requirements.txt      # 必要なPythonライブラリを記載
├── static/               # CSSや画像など静的ファイル
│   ├── styles.css
├── templates/            # HTMLファイル
│   ├── index.html
└── .github/
    └── workflows/        # GitHub Actionsの設定ファイル
        └── deploy.yml

Azureポータルにアクセスし、必要であればリソースグループを新規作成します。リソースグループは、アプリケーションや関連リソースをまとめて管理するための単位です。

img_5.png

App ServiseからWebアプリを新規作成します。ランタイムスタックにPythonを指定します。デプロイの項目は作成完了後に設定が可能になります。

img_6.jpg

作成後、デプロイセンターに移動します。デプロイオプションでGitHubを選択し、自分のリポジトリとブランチを指定します。

img_7.png

保存をクリックするとデプロイが開始されGitHubリポジトリのActionsタブで確認することができます。

img_8.png

4. GitHub Actionsで定期実行する

スクレイピングで取得した最新のデータをWebサイトに反映させる必要があるので、GitHub Actionsを用いてスクレイピングをスケージュールトリガーで実行します。

GitHub Actionsのワークフローを定義するyamlファイルは、リポジトリのルートディレクトリに.githubフォルダを作成し、その中にworkflowsというフォルダを作成し、その中にyamlファイルを作成します。

on:以降にアクショントリガーを設定します。

on:
  workflow_dispatch:
  schedule:
    # JST: 0, 6, 12, 18 -> UTC: 15, 21, 3, 9
    - cron: "0 15 * * *"
    - cron: "0 21 * * *"
    - cron: "0 3 * * *"
    - cron: "0 9 * * *"

workflow_dispatchは手動でワークフローを起動するためのトリガーです。テストするのに便利なので設定しておきます。

schedulecron式を使用して定期的な実行のタイミングを指定します。
Cron式は以下のような構造になっています

曜日
0 - 59 0 - 23(UTC時間) 1 - 31 1 - 12 0 - 7(0,7が日曜)

注意したいのが、時間はUTC時間であることです。UTC(協定世界時)はUTC = JST - 9時間で表現されます。現在はUTCに置き換わりつつありますが、GMTと同じ時間を示します。ここでは、毎日0,6,12,18時に更新したいので、"0 15,21,3,9 * * *”と指定しています。

最後にGitHub Actionsでの書き込み権限を設定します。SettingsタブからActionsGeneralを選択します。Workflow permissionsRead and write permissionsに変更することで書き込み権限を与えられます。

img_9.png

5. Notionに埋め込む

デプロイが完了し、Webサイトが問題なく表示されたらNotionに埋め込みます。

サイトのURLをNotionに張り付けて「埋め込み」を選択すれば完成です。

img_10.png

おわりに

GitHub Actionsで更新しているのでコントリビューションが汚染されてしまうことに気づき現在は自動更新を停止しています。解決策として、別のユーザでコミットを行う方法を検討中です。ここまで読んでいただきありがとうございました。

参考にしたサイト等

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?