はじめに
ういっすういっすういっすー!✌️nikkieです。
10/15, 16のPyCon JP 2021、お疲れさまでした!
今年は座長としてがっつり関わりましたが、実は裏でけっこう手を動かしてコードも書いていました。
アウトプットのリハビリも兼ねて、PyCon JP 2021を支えた技術面のアウトプットをざっと紹介します。
目次
- はじめに
- 目次
- プロポーザルレビューアプリ(Django)
- connpassのイベント作成操作自動化(helium)
- ウェブサイトのタイムテーブル用に、sessionize APIの返り値をパース
- Discordでの受付Bot:mogirin
- おまけ:30日後に始まる #pyconjp
- 終わりに
プロポーザルレビューアプリ(Django)
レビューモードです(データはfactory_boyによるダミーデータです)
2020に引き続き、プロポーザルをレビューするためのアプリをDjangoで作りました。
2021での技術的なアップデートは以下です。
項目 | 2020 | 2021 |
---|---|---|
DB(postgres) | ローカルマシン内 | Dockerイメージ |
Sign in with Slack | django-slack-oauth1 | django-allauth1 |
プロポーザルデータ投入 | JSONファイルをgit commitしてherokuにだけpush | django-rest-frameworkでエンドポイント作成 |
テストデータ | - | factory_boyでランダムに作成 |
django-allauthは罠が多く、いろんなところでハマったのですが、なんとかSign in with Slackを実装しました(詳細はまたの機会に)。
django-rest-frameworkも初めて使いましたが、akiyokoさんの『現場で使える Django REST Framework の教科書』にめちゃくちゃ助けられました。
ちなみに実装時期は7月で、金曜ロードショーの細田守祭を流しながら毎週金曜の夜に実装してました。
おおかみこどもを聴きながら、DRFの教科書を真似てエンドポイントを実装できた瞬間はすごい気持ちよかった1ですし、バケモノの子を聴きながら、Sign in with Slackでハマった箇所に対処していました。
伸びしろはテストコードですね。
Djangoのテストに書き慣れていないから、テストを書こうとすると遅くなり、限られた実装時間の中ではテストを書かないことを2年とも選びました2。
この夏に出たc-bataさんの『実践Django』はテスト駆動のアプローチなので、真似てみるのはいい練習になりそうです。
connpassのイベント作成操作自動化(helium)
9月の #pycharity (や他の勉強会でのLT)で聞かれた方もいるかもしれませんが、繰り返しconnpassでイベントを作るので、ブラウザ操作自動化ライブラリ helium を使って自動化しました。
PyCon JPスタッフのmtgは毎回connpassを使っており、出席者の確認やカレンダー連携、外からも活動の様子が分かると利点も大きいと感じています。
一方、「毎週connpassイベントを作るのが面倒」というスタッフの声もありました。
そこでイベント作成はnikkieに集約し、自動化スクリプトで作っています。
この自動化で得たノウハウはmogirin(後述)にも活きています。
次の技術的な挑戦としては、PyCon JP SlackにBotないしはカスタムコマンドとして導入し、スタッフ誰でも簡単にconnpassイベントを作れるようにすることですね。
ウェブサイトのタイムテーブル用に、sessionize APIの返り値をパース
2020同様3、sessionizeからトークのデータを取得し、CSVに整形するプログラムを実装しました。
今年はsessionizeのAll
というエンドポイントを使ったのですが、返り値がやんちゃで、実装はけっこう大変でした(もっと単純な返り値が返るAPIの設計を強くお願いしたいです🙏)。
スピーカー、ルーム、プロポーザルの項目(前提知識など)のIDと値を対応させるdictをいくつも書くことになり、「coreモジュールが大きすぎる(返り値のパースの仕方を知りすぎている)」と頭を悩ませながら実装していました。
# coreモジュールが知りすぎていると感じた一部 def create_speaker_id_map(speaker_data): return {d["id"]: Speaker(d["fullName"], d["bio"]) for d in speaker_data} speaker_id_map = create_speaker_id_map(data["speakers"]) # スピーカー作成処理(Before) # [speaker_id_map[speaker_id] for speaker_id in session["speakers"]]
最終的にはファクトリ(作り方を知っているモノくらいの意味です)のクラスを用意し、それらにスピーカーなどを作らせることで、大量のdictはcoreから各ファクトリの属性に移すことができ、個人的にはスッキリした実装にできたと思っています。
# coreモジュールはSpeakerFactoryを作ってあとはお任せ speaker_factory = SpeakerFactory.from_(data["speakers"]) # スピーカー作成処理(After) # [speaker_factory.create(speaker_id) for speaker_id in session["speakers"]]
Webサイトのビルドでは0.1.1
を使っているのですが、0.2.0
ではCSVに出力するフィールドを指定できるようにしました。
これにより、例えば、タイトル、スピーカー名、ルームのようにフィールドを絞ってCSVを作れるようになりました!
$ python -m pyconjp_domains timetable talk_titles.csv --fields id title $ python -m pyconjp_domains timetable interval_slide.csv \ --fields day 'slot_number AS no' room title 'speaker_names AS name' id
(動作させるには、環境変数 ENDPOINT_ID
の指定が必要です。スタッフに共有しています)
0.2.0
により、似たようなスクリプトを何個も書かずに済んでとても便利でした。
Webサイトのタイムテーブル以外では、Discordのテキストチャンネル名や幕間に表示したスライドにも使っています。
Discordでの受付Bot:mogirin
PyCon JP 2021のオンライン会場、Discordには、受付(チケットのもぎり)をするBot、mogirinがいました。
こちらの実装にも関わっています。
- 参加者はconnpassの受付票で受付番号を確認(1234567のような7桁の数字)
- Discordの受付チャンネルで
@mogirin 1234567
と呼び出す - mogirinは受付番号一覧のスプレッドシートを参照し、まだ受付されていない受付番号であれば、シートを受付済みに更新し、会場が見えるようにattendeeのロールを付与する
mogirin自体は他のスタッフが迅速に動作検証してくださった曳光弾コードがあったので、私はそのコードをherokuで動くようにエンジニアリングしただけです。
一方、mogirinが参照する受付番号一覧スプレッドシートの更新は、技術的に背伸びが必要なストレッチゴールでした。
- 参加者の受付番号はconnpassから参加者情報のCSVとしてダウンロード(ブラウザ操作自動化)
- CSVの中から、差分をスプレッドシートに追記
- これを定期的に動かして受付番号一覧スプレッドシートを更新する
結論を言えば、Dockerイメージにして、CircleCIでスケジュール実行しています。
https://hub.docker.com/r/pyconjp/mogirin-db-synchronizer
connpassから参加者情報CSVをダウンロードする部分はheliumを使いました。
Firefoxが立ち上がるDockerイメージにheliumをインストールして動かしています。
ブラウザの表示言語の設定やDockerの中で立ち上がったブラウザのデバッグなど、けっこう大変だったのですが、これはまたの機会にアウトプットしたいですね。
なお、このあたりは10月頭にワクチン副反応と闘いながら実装していました。
ブラウザ自動操作でconnpassから参加者情報CSVをダウンロードというのは、どれくらいの頻度でやっていいのか手探りで悩ましかったです(ログインを伴うので、robots.txtは当てにできないと考えていました)。
会期直前は1時間おきに動かしていたのですが、例えば、10:30に動いた後、10:40にチケットを買った方は「すぐ受付したい」はずですが、自動化した範囲では11:30まで待ってもらうしかありません。
「運用でカバー」というやつで、待っている参加者に気づいたらスタッフにスプレッドシートを更新してもらい、回していました4。
(このあたりはconnpassという外部に参加者データを保持している制約と考えています。他のPyConのWebサイトみたいに、ウェブサイトでチケットの決済までできると、内部のデータベースをmogirinから参照することで、待ち時間やスタッフの作業なく受付できたはずです。もしくは、connpassのAPI公開が待たれます🙏)
おまけ:30日後に始まる #pyconjp
観測範囲ではワニに続き47歳さんが話題ですが、#pycharityのhayaoさんの発表「日常生活で使うPython」5に影響を受け、カウントダウンしました。
これをやることで @pyconjapan
のフォロワーも増えるかなと思っていたのですが、この企画がフォロワーを集める要因には、単に続ける以外の要素が大きいということですね。
漫画がなかったからか・・(←違う)
# お祭りの後 $ python -m xdays_journey -5 days, 0:00:00
終わりに
PyCon JP 2020では、「Code for PyCon JP」という発表(15分トーク)をしました。
2021はブラウザ操作中心に自動化スクリプトを追加、mogirinが参照するスプレッドシートの更新処理の実装と、振り返ってみるとめちゃくちゃ(2020の倍くらい?)手を動かしてましたね。
1スタッフとしてできることはかなり広がったと思いますし(10x Staff?)、手を動かす中で身についたと感じるトピックもいくつもあります。
技術で(も)支えたおかげで、毎日Contributionは継続しています!(2020/05/02から始めて500日突破しました)
今後の方向性としては、どれくらいの配分にするかは考慮中ですが、技術面メイン(DX担当?)で引き続きPyCon JPスタッフには関わる予定です。
目下のところは、mogirinのDockerイメージを利用して、connpass操作を代行するSlack Appを作ってみたいなと思います。
今回のアウトプットについて「他のカンファレンス・勉強会で使いたいのですが、ここはどうやったらいいですか?」といった質問・相談は大歓迎です。
リポジトリのIssueやTwitter @ftnext までお気軽にどうぞ!
(技術的な伸びしろがあれば、Pull requestで提案していただくのも嬉しいです)
私の場合は、Pythonを書いてスタッフの作業を進めたいシーンがいくつもあったので実装しています。
これくらい作れないとPyCon JPのスタッフはできないということではありません。
「やりかたは自由」6ですので、スタッフに興味ある方は気楽に構えて飛び込んでみてください!
-
Django 3.0向けに自分でパッチ当てたversionです。Slack側の仕様が変わったようで、もう動かないのではないかと思っています https://github.com/ftnext/django-slack-oauth↩
-
2020年積み残し解消:django-allauthを使ったソーシャルアカウント(GitHub・Slack)連携、素振りの記 - nikkie-ftnextの日記 での素振りが活きました!↩
-
テストを書かないというジャッジをするのは構いません。でもそれは、スタートアップだからでもスピード優先だからでもない。自分達が未熟だからで、そこには向き合うべきだと考えます。 「スタートアップだからテストを書かない」は正しいか - An Epicurean より(そのとおりで、今年一番しびれたエントリです)↩
-
セッションデータはsessionizeというサービスで管理しています。API経由でセッションデータを取得することができるので、ビルド時にセッションデータを取得し、フロントエンド側で利用しやすいように整形しています。 PyCon JP Blog: PyCon JP 2020 システムチーム振り返り【技術編】より↩
-
会期中にふと、mogirinが参照したスプレッドシートに受付番号がなかったことを契機に、スプレッドシート更新を起動するようにできたことに気づきました↩
-
Python for Everyday - Speaker Deck 、ちょっとした工夫の共有、とてもよかったです。なお、#pycharityの資料は https://pyconjp.connpass.com/event/218154/presentation/ にまとまっています↩
-
https://pyconjp.atlassian.net/wiki/spaces/pyconjp/pages/1019183576#%E3%82%84%E3%82%8A%E3%81%8B%E3%81%9F%E3%81%AF%E8%87%AA%E7%94%B1 参照。「よりよいやり方」がコードを書いて楽をすることだと思ったので書きました(タイダデスネー)↩