Goコミュニティのイベントカレンダーを作りました

Goコミュニティのイベントカレンダーを公開しました。リポジトリgithub.com/yebis0942/go-event-calendar-jpです。

connpassで運営されているGo言語のコミュニティのイベント開催情報をまとめてiCalendar形式で出力します。Webブラウザでカレンダーを表示したり、Googleカレンダーに登録したりできます。

以下のような用途に使えると思います。

  • 面白そうなGoイベントがないか探す
  • 最近のGoイベントを眺めて、トレンドを把握する
  • Goイベントを企画する際の日程調整の参考にする

実装にはconnpass API v2を利用しています。コミュニティ・個人向けの利用を申請して、無償でAPIキーを発行していただきました。ありがとうございます。v2からは認証のための固定IPアドレスが不要になり、運用が楽になったのもありがたいところです。

構成

システム構成は以下の通りです。地域.rbカレンダーをかなり参考にさせていただきました。

  • iCalendar生成サーバー
    • connpass API v2でイベントを収集して、iCalendar形式に整形して返します
      • 現時点では、設定ファイルに記載したconnpassグループのイベントのみを対象としています
    • Cloudflare Workersにデプロイしている
  • Googleカレンダー
  • GitHub Pages

構成について考えたことメモ

  • GitHub Actionsでscheduleトリガーを使って定期的にiCalendarファイルを生成してGitHub Pagesで配信すればいいかも?
    • この方法ならCloudflare Workersによるサーバー実装を省けるので、実装は簡単になります。ただし、利用規約ではGitHub Actionsの用途はUse for Development and Testingに限られています*1。この方法はその用途からやや外れているようにも思えたので、この方法は選ばないことにしました。
  • Cloudflare Workersを使うなら、HTMLの配信にGitHub Pagesを使わなくてもいいのでは?
    • バックエンドにCloudflare Workersを使い続けるか分からなかったので、ユーザー向けのURLはGitHub Pagesに固定することにしました。ただし、iCalendarファイルを配信するURLが変わると、GoogleカレンダーのURLは変わってしまいます。

今後の課題

  • Go以外のイベントも開催するconnpassグループの対応
    • 企業が運営するグループや、GDG(Google Developer Group)のグループなどは、Go以外のイベントも開催しているため、現在は掲載対象にしていません
    • また、グループに所属していないイベントなども掲載できていません
  • connpass以外のイベントの掲載
    • 現状ではほとんどのイベントはconnpassに登録されているように思うので、それほど大きな問題ではないかもしれません(他のイベントサイトを捕捉できていないだけかも)
  • 過去の開催イベントの永続的な保存
  • GitHub Actionによる自動デプロイ
    • 現在は手元でwasmをビルドしてpnpm run deployしています

先行事例

golang.jp ブログ

2019年から2025年3月まで、毎月の日本国内のGoのイベントの開催情報が投稿されていました。

人手で収集されているので信頼性が高く、長期間に渡ってほぼ一貫したフォーマットで投稿されているため、アーカイブとしての価値も高いです。たとえば、新型コロナウィルスの流行が始まった2020年3月の状況や、その後のオンライン開催への移行の経過なども克明に追うことができます。

地域.rbカレンダー

こちらは各地のRubyコミュニティのイベントが掲載されています。2019年から運用されている老舗カレンダー。基本的なシステム構成をそのままいただきました。この場を借りて感謝申し上げます。

以下の点も参考にさせていただきました。

  • 今日を起点に前後3ヶ月のイベントを収集する
    • 自分の初期実装では未来のイベントだけ収集していましたが、たしかに過去のイベントを参照したいケースは多いはず
  • 埋め込みのGoogleカレンダーには祝日のカレンダーも表示する
    • これがないと平日開催なのか祝日開催なのか区別がつかなくて困る

ということで

先行サービスを参考にしながら、Goコミュニティのイベントカレンダーを作りました。まだ不十分なところもありますが、Goコミュニティのイベント情報の把握に少しでも役立てば幸いです。掲載されていないconnpassグループなどがあればGitHubのissueなどでお知らせください。

ソースコードに効率的にアクセスする(git grep + peco, helm plugins)

これまでhelmとシェルの補完機能で生きてきたけど、ファイル数が100を超えるようなプロジェクトだとそれではしんどくなってくる。もう少し規模の大きいコードベース向けの既存の解決策をいくつか紹介する。

git grep (またはgit ls-files) + pecoで絞り込む

Goソースをgrep → pecoで選択 → vimで該当行番号にジャンプした状態で開くワンライナー - Qiitaが参考になる。ファイルパスを**/*.goから**/*.pmなどに変えればPerlソースにも使えるし、後述するようにawkの部分を変えるとEmacsでも使える。使ってる様子

git find foobarのように使えるシェルスクリプト版もあります。

git findコマンドを作った - あんパン

仕組み

  • git grep: Gitで管理しているファイルからgrepする。
    • 検索対象はカレントディレクトリ以下になる。つまり/lib/Bananaにいる状態では/lib/Plackや/scriptなどのファイルは検索されない。
    • grepのオプションが通らないことがある。
  • peco: ターミナル上で汎用的なインクリメンタル検索をするためのツールpeco/peco - GitHubGIFデモを見るのが早い。

これらを組み合わせると、grepして見つかった行を選択したら、そのファイルのその部分を開いたエディタが立ち上がるということができる。

pecoのインストール

Installation - Mac OS X / Homebrew

Macを使っていてHomebrewを入れていれば二行のコマンドを入れるだけで30秒以内に使えるようになる。

その他

EmacsPerlのファイルを開く場合はこんな感じ。/Applications/Emacs.app/Contents/MacOS/bin/emacsclient -n $(git grep -n "$1" -- "**/*.pl" "**/*.pm" "**/*.t" | grep -v "[0-9]:\s*//" | peco | awk -F ":" '{print "+"$2" "$1}')

ファイルパスだけを使って絞り込みたいときにはgit ls-filesが使える。

Emacs

シェルを使わずにEmacsだけでアクセスするためのelispを紹介する。helmプラグインがメイン。

ファイルパスから開く

helm-ls-gitというelispもあるが、anything-git-projectの helm版. - Life is very shortはmodified filesを上のほうに表示してくれたりするので(と書いたけど、helm-ls-gitにも同様の機能があった)、僕はこちらを使うことにした。

git grepから開く

yasuyk/helm-git-grepがよさそう。package-installからインストールできた。

helmを使ってない人はemacs内でgit grepする方法 - $shibayu36->blog;が便利なのでは。

todo

ソートアルゴリズムの勉強2

ソートアルゴリズムの勉強 - yebis0942’s blogの続き。

マージソートのマージする部分で、「配列Aと配列Bのどちらも空ではないなら」という条件文にするべきところを「配列Aと配列Bのどちらかが空ではないなら」と書いていたのが原因だった。コードを慣れないC++で書いているので、自分の言語に対する知識が間違っているのか、ロジックの理解に問題があるのかはっきりしなくて難しい。しかし自分がそれなりに習熟しているのはスクリプト言語ばかりで、それでソートとかを書いてもアルゴリズムの勉強をしているという気分が出ない。

vector<int> mergesort(vector<int> v) {
  if (v.size() == 1) return v;

  if (v.size() == 2) {
    if (v[0] > v[1]) swap(v[0], v[1]);
    return v;
  }

  vector<int> a, b;
  for (int i = 0; i < v.size(); i++) {
    if (i <= v.size() / 2)
      a.push_back(v[i]);
    else
      b.push_back(v[i]);
  }

  auto a_sorted = mergesort(a);
  auto b_sorted = mergesort(b);
  int a_sorted_idx = 0;
  int b_sorted_idx = 0;
  vector<int> sorted;

  while (a_sorted_idx < a_sorted.size() && b_sorted_idx < b_sorted.size()) { // ここを || にしていた。
    if (a_sorted[a_sorted_idx] > b_sorted[b_sorted_idx]) {
      sorted.push_back(b_sorted[b_sorted_idx++]);
    } else {
      sorted.push_back(a_sorted[a_sorted_idx++]);
    }
  }

  while (a_sorted_idx < a.size())
    sorted.push_back(a_sorted[a_sorted_idx++]);

  while (b_sorted_idx < b.size())
    sorted.push_back(b_sorted[b_sorted_idx++]);

  return sorted;
}

ソートアルゴリズムの勉強

アルゴリズムとかの分野は必要になったときに泥縄で勉強しようとしてもなかなか身に付かなさそう。ということで

アルゴリズムを学ぼう

アルゴリズムを学ぼう

を読んでる。

バブルソート、選択ソート、挿入ソートという基本的なソートを実装したりして、次にマージソートに取りかかったけど、マージがうまく実装できなくて悩んでいる。