🎆

【文化祭】日比谷高校星陵祭2023のオンライン整理券システムを制作・運用しました!

2024/07/18に公開

2023年9月16・17日に行われた東京都立日比谷高等学校の文化祭である星陵祭のWebサイトおよび当日に使用されたオンライン整理券システムを制作しました。
実はこのシステムは2022年度に一度失敗しており、1年越しの実現となりました。2022年度についても記事にまとめていますのでそちらもぜひご覧ください。
https://zenn.dev/ekke/articles/ec07943d678340
もうすぐで2024年度の星陵祭が来ちゃうのでいい加減公開しないと

主な機能

整理券システム

日比谷高校の星陵祭では全学年全クラスが各教室でクラス劇を上演します。
(1公演50分) * (1日5公演) * (2日) = 全10公演
普通教室でやる都合上各公演の座席数は限られているので、観劇するための整理券をWebサイト上で配布する仕組みです。
2023年度は初の実施だったため、2022年度のようにサーバーが落ちたり不具合が発生したりしたら困るということで様子見として以下のような対応となりました。

  • 生徒と教員はオンラインで取得
  • 一般客は従来通り紙での配布
  • 保護者・家族はオンラインと紙のどちらも使うことが出来る(デジタルに不慣れな保護者・家族への対応策が万全には用意出来なかったため)
    当日配布されたパンフレットに掲載されたオンライン整理券システムの使い方のページ
    当日配布されたパンフレットに掲載されたオンライン整理券システムの使い方のページ

Webサイト

https://2023.seiryofes.com


トップページ

団体一覧ページ

今までは星陵祭企画委員会(以下「チーフ会」)の方々がWixなどのWebサイト制作ツールで作っていた星陵祭公式Webサイトをseiryofes.comに統合しました。
各クラスの演目の情報などが載っているWebサイトとオンライン整理券システムが別のページにあるのは使いづらいと考えたからです。
各クラスの個別ページが用意されており、生徒がサムネイル画像や演目の説明文を設定したりSNSアカウントへのリンクを追加したりできます。
開催スケジュールや学校へのアクセスといった基本的な情報もわかりやすく掲載しました。

インフォメーションボード

校内4か所にテレビ・プロジェクターを設置し地図や公演時間の情報などを表示しました。

剣道場(紙整理券の配布場所の1つ)に置かれたテレビ

実は本来は、紙整理券とオンライン整理券どちらを使うこともできるという対応になった保護者のために、紙整理券の配布場所でオンライン整理券の取得状況を表示する案でした。
なにせ結構直前に出た案だったので、とあふ[1]1日目の朝ギリギリまで健闘してくれて、開発環境では動作したのですが、後述するAsyncDataが原因で上手く動作しない問題を本番までに解決出来ませんでした。

背景

  • COVID-19の感染抑止
    • オンライン整理券システムの計画が本格的に始まった2021年度のきっかけは,COVID-19対策
    • しかし結局2023年度は、細かい制約はあり完全にコロナ禍以前とはいかなかったものの、COVID-19による大きな制約を受けずに星陵祭を開催することができたため、整理券システムにもCOVID-19を意識した機能はありません。
  • クラス劇整理券の効率的な配布
    • 星陵祭ではコロナ禍以前からクラス劇の整理券配布の混雑が悩みの種でした。まだ暑さが残る9月の体育館に5000人以上にも上る来場者が列をなし、そこで全クラス分の観劇整理券を配布する、というのはなかなか大変だったと聞いています。(写真などを入れる)
    • コロナはあくまできっかけで、長年の課題の解決策という認識
    • チーフ会の当日業務負担軽減
  • Webサイトとしての機能および内容充実・広報力の強化
    • 途中からそれも意識し出していたと思う
  • 映像配信のプラットフォーム
    • 当日2日間の全12公演ではすべてのクラスの劇を見ることは出来ず、キャストを担当する生徒の中には全く他クラスの劇を見られない人も出てきます。
    • 当日の公演を各クラスで撮影してアップロードし、後日生徒のみが見られるようにする仕組みです。
    • そもそも映像配信はCOVID-19により星陵祭が全く実施できなかった頃に始まっている。コロナ禍の数年間、Microsoft SharePointやStream,Teamsなどいろいろな形態を辿ってきた。

独自でプログラミングをして1から作ることにした理由

世の中には既にSaaSとして利用できる素晴らしいチケットシステムがたくさんあります。(teketなどはオケ部や合唱部がコンサートやるときにも使っていた。)
なぜわざわざ自作という茨の道を往ったのか。その理由はSaaSでは実現できない以下の仕様を満たすためです(調査不足で本当は出来る機能があったらすみません)
これはCOVID-19の影響で、企画時点では星陵祭が行われる9月にどのような情勢になっているかが全く読めなかったために星陵祭の開催方式が定まらず、様々な可能性を考慮した幅を持たせた設計にしたことにも起因しています。(以下に理由として挙げたが最終的には実装しなかった機能もいくつかある)

  • 入場制限の必要性

    • 実際に学校敷地に来場していないと整理券を取れないように
    • インターネット上の任意の人が(観劇する意思がないのに)整理券を取れてしまうと、各クラスで用意している座席数は結構少ないので致命的。整理券は売り切れているけどお客さんがいない、ということになったら悲惨。
  • 1人が同じ時間帯で複数クラスの整理券を重複して取ることを制限出来ない

    • 例) 11Rの1日目第1公演の整理券をすでに取っているのに、同じく1日目第1公演の12Rの整理券も取る。
    • いわゆる荒らし行為を防ぐ目的
    • 世の中のSaaSでは、ある1つの団体について同一人物が複数枚チケットを取ることを制限する機能はあるように思いますが、その制限を複数団体にまたがってかけることは出来ないように思われました。
  • 関連団体の部分に星陵祭とは全く関係のない団体が出てくることがなんか嫌

    • 下にスクロールしたりすると出てきますよね
  • Webページとの連携

    • 各団体の紹介やSNSリンク、動画配信などとのスムーズな連携を実現
  • 日比谷高校の星陵祭のニーズにぴったり合う独自機能

    • 投票機能との連携
      • 整理券を取って実際に観た劇にのみ投票が可能
      • 従来は、取った紙整理券をそのまま投票箱に入れることで、クラス劇への投票を兼ねていた
      • たしかに、実際に観ないと良しあしは判断できないですよね
      • 2023年度は実装予定でしたが、間に合いませんでした...
    • 入学を検討している中学生が優先的に整理券を取れる仕組みとかも考えた時ありました
  • 独自で作ってみたいという個人的な興味

    • これは否定できません
    • 後述する筑駒や麻布の例に刺激を受け自分もやってみたいと思いました。


生徒会室での会議の様子(2023年1月16日)

今後の後輩が、私たちが作ったシステムをより良く変えながら使っていくことはそりゃあとても嬉しいのはその通りですが、毎年毎年、本当に自作で行くメリットが上回るのかの検討はして欲しいと思います。SaaSで必要な要件が満たせるならそれが良いと思う。

制作の時系列

2022年9月

詳細は昨年の記事をぜひご覧ください。

チーフ会の要求する仕様とIT部隊の認識との間にズレがあるなど、チーフ会との連携不足も課題だと感じていたため2022年度の星陵祭が終わった直後からチーフ会整理券担当のS[2]と頻繁にしゃべることを意識していました。
またこの時期から、星陵祭チーフ会の下に所属していたIT部隊を「IT委員会」として独立した組織にする計画が進んでいきます。
IT部隊がIT委員会になるストーリーは日比谷高校雑誌委員会が発行している「いてふ台」の次の号に載せていただけるらしいのでそちらもぜひ読んでみてください。

2022年後半

そもそも一般のお客さんは来場できるのか、来場できるとして人数制限は必要なのか、等開催方式が揺れていました。
2022年度にサーバーが落ちてしまったことによる先生方の不安感は当然あったと思うので、オンライン整理券システムを正式に使うのか、従来通り全て紙による配布にするのか、というところからの議論でした。
ある程度2022年度のサーバー落ちの原因は分析出来ており今年は成功できるだろうという自信と、オンライン化によるメリットは大きいはずだという判断があったので地道な準備や説得を続けました。チーフ会委員長をはじめチーフ会の何人かが「オンラインにした方がいい」と言ってくれていたのも背中を押しました。

ところで2022年度は製作途中の画面を逐一先生方やチーフ会に見せる、ということをしていなかったのも良くなかった。2022年度の星陵祭直前or直後に、実際にスマホを操作してオンラインで整理券を取る流れを生徒会担当の先生に見せたら好感触だったのを覚えている。もっと早くやるべきでした。

2023年度に向けたコーディングを本格的に始めたのは2023年3月ごろからのようです
https://github.com/hibiya-itchief/2023-quaint-app/issues/95#issue-1606638125
↑今見ると見当違いなこともいくつか書いていて恥ずかしい

2023年4月 IT委員会 新入生募集大作戦

IT委員会は出来たばかりの組織であり、今後どうなっていくかは発足初年度である2023年にどれだけ新入生を集められるかにかかっていると考え、気合を入れた新入生募集を行いました。

  • 「IT」という文字をとにかく大きくして目に入るように
  • 当時の技術部のメンバー5人で1人1問ずつ作った情報やコンピュータに関する問題を載せ興味を引くように
    • 論理回路,二分探索,巡回セールスマン(のようななにか),ナップザック問題,などなど


パンフレットの全パターンはこちら(https://github.com/hibiya-itchief/2023-spring-problems/blob/main/problems.pdf)
ちなみに技術部員が作った解答のHTMLもすごく面白いのでぜひ見てみてください
https://hibiya-itchief.github.io/2023-spring-problems/nand-circuit/
↑特に好き 編集後記の「なかなか良いNAND(難度)でできたのではないでしょうか。」とか
https://hibiya-itchief.github.io/2023-spring-problems/detect-hensachi/
https://hibiya-itchief.github.io/2023-spring-problems/genkai-student/
https://hibiya-itchief.github.io/2023-spring-problems/syukudai-selection/
https://hibiya-itchief.github.io/2023-spring-problems/mysterious-symbol/

入学式の日(在校生は登校しなくてもいい日)に登校して、新入生の各机に配り生徒会室前や各教室の黒板にも貼らせてもらいました。
入学式の朝って多くの一年生は近くの人に話しかける勇気は出ず、配られてるいてふ台を読むかPTA会報を読むかしかやることがないと思う(私はそうでした。結構面白くてあっという間に時間すぎるのよね)ので、全5種類あるIT委員会のパンフレットを周りの人と持ち寄って話すきっかけになればいいかなとかも考えてましたねそういえば(実際どうだったのかは知らない)
TwitterやInstagram等にも掲載し、結構反響があったのが嬉しかったです(SNSでは専ら在校生からの反応がメインだった)
発案が結構直前だったため時間が無くQRコードだけ先に載せて紙を印刷してから解答HTMLを作ってアップロードしたりしてた。首の皮一枚すぎる。焦って作ったので実は問題にいくつか不備があります...お恥ずかしい。Twitterで在校生に指摘されたりもしましたね...

ちなみに、スマホの写真フォルダには2023年1月に↓の広告の写真が何種類も残っているので、おそらく当時の僕は↓の東急のUrban Hacksの広告にすごくわくわくして影響を受けて自分でも似たようなことをやってみたくなったのだと思います。

2023年4月26日 負荷テスト

いきなり星陵祭本番を迎えて、そこで初めてやってみて上手くいきませんでした、では困るということで生徒総会の後の時間を頂いて全校生徒に端末を操作してもらう形の負荷テストを行うことになりました。もちろんそれまでの開発段階でJMeter等の負荷テストツールを使った負荷テストは実施していたのですが、ツールではない実際の負荷ではまた事情が違うかもしれないというのと、2022年度のサーバー落ちで先生方には当然不安感があったと思うので全校生徒の前でちゃんと動作することを示すいいパフォーマンスの機会だな、とも考えていました。

しかし当日になって急遽ストップがかかります。委員会担当の先生には事前に実施する内容を伝えて連携を取っていたつもりですが、配布したプリントが全ての先生方に十分に周知出来ていなかったようで(?)急遽ストップがかかり僕とY委員長が体育館のステージに登壇し突然の中止を発表・謝罪しました。何重にでも先生方と連携を取ることの重要性を痛感した出来事でした


当時のUI(信号機状態の空席表示や「お気に入り機能(未実装)」など相当荒削り)

2023年5月15日「負荷テスト・再」

体育大会直前準備の時間を少しいただきリベンジ。

具体的な負荷テストの内容は、放送で指示をしながら、生徒1人の端末から、3人分,5人分,最終的には10人分のリクエストを同時に飛ばす、というもの。
保護者や一般客が利用して負荷が増えるという想定です。

あれほど気を付けてたのにmax_connectionsが小さい値になっていました。どこかのタイミングでDBサーバーを作り直したときに設定し直すのを忘れていました。500エラーが出て焦ったが、AzureのMySQLの設定の画面から変更すると、1分ほどで反映されました。まあすぐ変更出来るってことが分かって良かった!と捉えます。

5月負荷テストのAPIサーバーの負荷の様子
5月負荷テストのAPIサーバーの負荷の様子
5月負荷テストのDBサーバーの負荷の様子
5月負荷テストのDBサーバーの負荷の様子
12:20ごろDBのaborted connectionsが増加、500エラーの数が1400近くなり、max_connectionsの値が小さいことに気づく
12:23ごろから無事DBのコネクション数が増えて500エラーが収まる
最大瞬間風速は12:25分ごろ、35,000/分のHTTPリクエストを捌き、DBのクエリは20万/分超えている、DBのコネクション数は3000を超えている

この時は、MySQLサーバー側のmax_connectionsの値を上げて対応しましたが、本来は(DBから見た)クライアント側のsqlalchemyで同時接続数を制限しておく方が正しそうということも知りました。確かに前者のやり方では結局さらに負荷が増えたら同じことですもんね。
やはりRDB難しい。お恥ずかしながら僕は2022年度にサーバー落ちして初めてmax_connectionsの存在を知りました。FastAPI公式ドキュメントのデータベースとの接続の項には同時接続数についての注意は書かれていないように思うし、RDBを使ったアプリを作ってみよう、的なQiitaやZennの記事とかにも書かれているものって見たことないような気がします...どこでこういう情報を仕入れたらいいんだ...RDBについては何となく使うのではなくどういう仕組みなのかちゃんと勉強して理解してから使う必要がありそうです。業務でRDBを使う人にとっては当たり前すぎる内容なんでしょうかね

1人で10人分のリクエストを送るといういちばん高負荷として用意していたものまで問題なく終わり、先生方に負荷テストの結果について説明する資料を作成。負荷テストが上手くいったことが後押ししたのか、これ以降オンライン整理券制度についての検討が加速してより詳細なことが決まっていったような気がします。

生徒のみなさんからしたら明日体育大会なのに何をやらされているんだろうという感じだったでしょう。ご協力ありがとうございました。ちなみにその翌日の体育大会の様子はこちら↓
2022年度のサーバー落ちをいじられて3年次のクラスTシャツの背ネームは「サーバー復旧中」になりました。星陵祭本番もこのTシャツで臨みます。

9月7日

日比谷高校では夏休み明けに期末考査があるため星陵祭に向けて動けるのは考査が終わってからになります。もどかしい。そして時間が無い。
チーフ会顧問の先生にほぼほぼ完成した状態をお披露目しました。実際に先生のスマホを使って整理券を取る流れを操作していただいたところ、めちゃくちゃ褒めてくださった記憶があります。嬉しい。
前述の通り、初年度の様子見としてオンラインと紙を併用することになっていたため、オンラインで配布する座席数と、一般向けに紙で配布する座席数の配分を検討しました。
オンラインが売れ残り、紙が売り切れになってしまって、一般客が取りたいのに取れなくなっちゃうのでは?はたまたその逆は?など、前例がないゆえに想像するしかない難しさがありました。

体育棟1階で会議。エアコンが無くてとにかく暑かった

9月1?日 保護者用アカウントの差し込み印刷

保護者用のアカウントは生徒を通じて配布しました。事前にAzure ADの一括作成機能で作成した1人ひとり違う保護者用ユーザーの情報を、使い方などを記載したWordファイルに差し込み印刷しました。
IT委員会総務部の人たちが後は印刷するだけの状態にしてくれていたのですが、差し込み印刷に慣れておらずなかなかうまくいかず、差し込み印刷に熟達した化学科の先生に手伝ってもらいながら、チーフ会担当の先生とチーフ会整理券担当のS[2:1]と(単純に枚数も多かったので)20:00くらいまで居残りして作業しました。

9月13日 直前負荷テスト

5月にやった負荷テストを念のため直前にもう一度やる予定になっていました。
5月の時点でサーバーに問題が無いことは確かめられていたのでどちらかと言えば生徒の皆さんにオンライン整理券システムの使い方を周知する、というのがメインの内容でした。
5月の負荷テスト時点ではまだ先の話だった星陵祭が数日後に迫り、星陵祭ムードになっていたためか、Webページや整理券システムについてすごいーという声が聞こえて嬉しかったです。

その後、直前負荷テストが無事終了したことへの感謝と本番の成功を祈り校舎の裏にある日枝神社に参拝しました。

9月13日 心臓に悪すぎる実装ミス

保護者が、スマホを持たない子どもなどと一緒に観劇することを想定して、1つのアカウントで3人まで観劇できるような仕様でした。(ひと家庭に2つアカウントを配布したので合計6人まで)
上記を実現するために、DBの設計としてはTicketテーブルのpersonというフィールドで1枚の整理券で何人観劇出来るかを持っていました。

整理券が売り切れていないことを判定する条件は
「(その公演に登録されている座席数) > (現時点で整理券を取り観劇することになっている人数)」
となるはずですが、Ticketテーブルの「レコード数」をカウントする実装になっていたのです。
正しくはTicketテーブルの「全てのレコードのpersonフィールドを合計」しなければなりません。
https://github.com/hibiya-itchief/2023-quaint-api/issues/145
↑めちゃくちゃ焦っているissue
https://github.com/hibiya-itchief/2023-quaint-api/pull/148

家でAPIのソースコードを眺めながら最終確認してた時に気づいたのですが、正直なんでこれに気づけたのかわかりません。神様が味方してくれたんだと思っています。これに気づけなかったら、用意されている座席数以上に整理券を配布してしまうことになり、大惨事になってたと思います。

2022年度はほぼ全てのエンドポイントや関数に対してPytestでテストコードを書いていました。しかし、後述しますが認証方法をAzure ADに変更したことで2022年度のテストコードがそのままでは動かなくなり(今考えればFastAPIのdependency_overridesを1つ追加するだけでよかったのだが)、めんどくさくなりPytestをやめてしまったのです。テストコードをしっかり書いていれば、条件を勘違いした実装で突き進んでしまうことも防げたのでは無いかと思います(テストケースを書く時も条件を勘違いしてたらダメだが)。身をもってテストコードの大切さを実感しました。

9月15日 23:55〜 「ギリギリの公演情報入力」

オンラインと紙の枚数配分が最終決定したのは前日の23:55。家族用優先券に関しての訂正が前日まで相次いだためです。

※家族用優先券:保護者が自分の子どものクラスの公演は整理券取得競争に参加せずとも必ず観劇できることを確約するもの

Discordでチーフ会整理券担当のS[2:2]からExcelファイルが送られてきました。ほんとお疲れ様。来年はこの保護者アンケートもデジタル化して自動で集計→オンライン整理券システム反映に出来たらいいな。
そこからIT委員7名にバトンタッチしてシステムに「手作業で」入力。Excelファイルを手作業で写すだけでも苦行なのに、極めつけは実用を考えていない使いづらいUIに苦しみます(自分で作ったのに)

  • 1公演ずつでしか入力できない
    • (1日5公演) * (2日間) * (24クラス) = 240回 の単純作業の繰り返し
  • 公演時間を毎回手入力
    • 2022年度は予め用意された時間枠からしか選べない形式だったのを、2023年度は公演の時間帯を自由に設定できるように変更しました。地味な進化ポイント
    • クラス劇以外のHebe(体育館でやるステージ発表のこと)や部活動の発表などでも使うことが出来るように、というつもり
    • でもその機能追加が仇になった
    • プルダウンで第何公演かを選ぶようにすれば大分楽だった

というのもUIを作った当時は、各クラ代が自分のクラスの公演は自分たちで追加をするイメージでした。IT委員会が、しかも前日に徹夜して全クラス全公演まとめて追加するイメージではなかった...

2024年度はcsvファイルをもとに公演を一括作成する機能が実装されています!後輩ありがとう

9月16日 1日目

当日は保健室前に用意した相談ブースを拠点にして、IT委員が交代で常駐して、オンライン整理券システム使用者のトラブルに対応しました。

当日になって発覚した主な不具合は以下です。

  • 配布時間や座席数の入力ミスが一部あった。
    • 前日に徹夜して焦って入力したことによるヒューマンエラー
    • 34Rのサウンドオブミュージックを見ながら、ItsukiKigoshi[3]と修正をしました。
    • 大半は配布開始までに対応出来たが、21Rの公演が重複して登録されたまま配布が始まってしまい、「ごめんなさい整理券多く配りすぎちゃった追加で8人入れられますか?」
    • 改めてひどい。何とか対応してくれてとても助かりました
  • 売り切れていないのに売り切れと表示される公演がいくつかあった
    • このPRが原因そう
    • nuxt generateしてStatic Pageとしてデプロイする場合の、asyncData()が呼ばれるタイミングについて理解不足でした。
    • ただし、売り切れと表示されていても、売り切れていなければボタンを押せば取得は出来たので整理券の配布は行えた

このように不具合はありましたが、まあ!ギリ!致命傷とまではいかず、整理券を配布するという使命は果たせたのでは無いでしょうか。

1日目は何より周りの人たちが実際に使ってくれている様子が見れて感動していました。

9月17日 2日目


星陵祭2日目、星陵祭まであと-1日


朝起きたらフィードバックのGoogleフォームに送られていた。励まされた

2日目は一日中クラス劇のキャストだったりHebeというステージ発表にバンドで出たりしていたので、僕はあまりオンライン整理券システムにつきっきりになってはいませんでした。
つきっきりにならずともスムーズにトラブルなく運用されていました。

生徒が実際に使用している様子を撮影させてもらいました。


生徒が実際に使用している様子1

生徒が実際に使用している様子2

データの振り返り

  • 全部で 3225 枚の整理券が取られました
  • キャンセルされたのは 1478 枚です(これは↑には含めていません)
  • 1枚でも整理券をとった人のうち、1人当たりの平均取得枚数(=観劇した公演数)は3.1236
  • 一番多く整理券を取った人は8公演見ていました
  • 7公演見た人は結構いましたが8公演見てるのは1人だけでした
  • ちなみにキャンセル済みも枚数に含めると26枚取った人が居ます。何回キャンセルしたんですか
  • 各公演の売り切れるまでの時間ランキング
    • 1枚目が取られてから売り切れるまでの時間でランキングにしました
    • 一度売り切れてもキャンセルが出ると、売り切れでは無くなります。
    • キャンセルがどのタイミングでされたかという情報は記録していなかったので、キャンセルは配布開始直後の取得競争が穏やかになってから行われたと仮定して、結果的にはキャンセルされた整理券もカウントに含めて最初に売り切れるまでの時間でランキングにしました

(↓分析用に書いた慣れないSQL)

INSERT INTO RECORDS (event_id, group_id, eventname, created_at_diff)
SELECT 
    e.id AS event_id,
    e.group_id AS group_id,
    e.eventname AS eventname,
    TIMESTAMPDIFF(SECOND, MIN(t.created_at), MAX(t.created_at)) AS created_at_diff
FROM 
    events e
JOIN LATERAL (
    SELECT created_at
    FROM (
        SELECT *, SUM(person) OVER (ORDER BY created_at) AS running_total
        FROM (
            SELECT *
            FROM tickets
            WHERE event_id = e.id
        ) AS f
    ) AS g
    WHERE running_total <= e.ticket_stock
) AS t ON true

GROUP BY 
    e.id;

結果はこちら↓ created_at_diffの単位は秒です。
最短3秒で売り切れていることに驚きです。来年以降は場合によっては抽選とかも必要かもしれません。

続き






こういう情報は終わった直後に公開した方が盛り上がっただろうなー

技術構成

詳しくはそれぞれのGitHubやAPIドキュメントを見てください。
クラスや有志団体、部活といったseiryofes.comで管理する団体をGroup、上演時間帯や整理券の配布時間帯・配布数などを持つ公演をEvent、整理券をTicketというふうに命名しています。
GroupとEventが一対多、EventとTicketが一対多の関係ですね。

フレームワークの選定については、ひとつのフレームワークでフロントエンドもAPIも出来ちゃうようなやつもありますが、習得が大変そうだったのでフロントエンドとバックエンドを完全に分けて、やり取りはドキュメントで定義された形式のjsonで統一しました。

フロントエンド - Nuxt

  • 2022年度から引き続きNuxt 2を採用しました。
  • UIフレームワークにはVuetifyを使いました。
    • yarn create nuxt-app で選べた中で一番好きな見た目でした
    • 12カラムグリッドをなぜか(本当になぜか)敬遠してBootstrapを選ばなかった記憶があるのですが、結局<v-row>,<v-col>多用されてますね。
    • ただ途中からマテリアルデザインへの飽きがちょっとありました
    • 見やすくわかりやすいUIだと好評ではあったのですが、整理券システム部分以外のWebページとしての部分はもっと独自でcssを書いた方が完成度の高いものになったと思います。(他校の文化祭Webサイト、ほんとレベル高いですよね)
  • 2023年度の開発時にはNuxt 3が出てましたが、まだベータだったのとあまり情報が無かったので2022年度からの資産があったということもありNuxt2を続投しました。
  • 今振り返るとコンポーネントとかを全然使いこなせていなくてVueの理解が甘かったなーと思います。
  • 2023年度のフロントエンドはほとんどとあふ[1:1]が書いてくれました

バックエンド - FastAPI

https://github.com/hibiya-itchief/2023-quaint-api

  • 2022年度から引き続きFastAPIを採用しました。

  • 自動でSwagger UIのドキュメントが生成されるのが良い。

  • 1年次に情報の授業でPythonを習うので、Pythonで書くフレームワークを採用することで少しでも多くの人に開発に参加して欲しいという淡い期待はそのままです。

  • 実は以下のようなFastAPIから乗り換えたい要素もあったのですが、フロントエンドもそうですがせっかく2022年度で大枠は完成していたのでそれをなるべく活用することを優先しました。

    • pydanticバリデーションされるが、やはり型ヒントというだけあって、TypeScriptみたいにVSCodeの全力サポートはない。コーディング中に不正な型の値を代入しようとしても警告してくれない。
    • やっぱり個人的にはフロントエンドとバックエンドで型定義を共有できたら楽だなと思った
  • 動的に更新される画像(各団体のサムネイルなど)の保存に関してはOracleのS3互換のオブジェクトストレージを使いました(S3が5GBまでのところ10GBまで無料!嬉しい)

インフラ

フロントエンド

NuxtのSSG(Static Site Generation)機能で静的ページに書き出したものをCloudflare Pagesで配信しました。2022年度は良くわからないままAzure AppService上でSSR(Server Side Rendering)して配信していたため読み込みに時間がかかっていました(設定が良くなかったのだと思う)が、Cloudflareによる静的配信にしたところ爆速になりました。
2022年度の鯖落ちで負荷に対して過敏になっていた僕にとって、アクセス数に一切の制限が無かったのがCloudflare Pagesの決め手でした。GitHub Pagesなどはよくよく調べると月間100GBまでという転送量の制限があります(月間100GBなんて超えないだろ!心配しすぎだ、というのは置いておいて)

APIサーバー

Microsoft AzureのAppServiceというPaaSを使いました。73期の先輩からおすすめしてもらい2022年度から使っていました。
Premium v3 P2V3(4コア16GBメモリ $0.376/1時間) x 4台スケールアウト
以下は当日16・17日二日間のAPIサーバーのメトリクスです。メモリ使用率は20%、CPU使用率に至ってはピーク時でも10%以下、ほぼ1%程度で推移していて明らかに過剰も過剰すぎるスペックでしたね。昨年2022年度のサーバー落ちがトラウマで警戒しすぎでした。
APIの負荷

予算面の話

2022年度の記事に詳しく書いてありますが、実体のないものには支出出来ないという東京都の制限により、学校からチーフ会や各クラスなどに出る星陵祭用の予算は使えません。Azureで学生の認証をするともらえる200ドル分のクレジットを使う計画でした。
僕のクレジットは2022年度に使い切ってしまっていたので、ItsukiKigoshi[3:1]に提供していただきました。

技術的なアピールポイント

学校アカウントでのシームレスなログイン

都立校生には一人一つ「@metro.ed.jp」で終わる都が契約しているMicrosoftアカウントが与えられています。
ダメ元で自分が作ったAzure ADディレクトリに外部ユーザーとして招待してみたところ、拍子抜けするくらい普通に期待した動作をしてくれたので採用を決めました。
生徒はいつも使い慣れているアカウント名・パスワードでオンライン整理券システムも利用できるようになったので、便利に使ってもらえるとともに、2022年度に大変だったことの1つ、生徒向けアカウント配布という作業も削減できました。去年はログインのタイミングでサーバーが落ちたトラウマがあったので、ログイン情報をデータベースとかで持つ必要が無くなったのもよかったです。

metro.ed.jpのディレクトリから招待したユーザー

ログインフローはこんな感じ(後輩への説明に使った資料を流用)

フロントエンドではNuxt.jsのモジュールnuxt-authを使い、公式ドキュメントのOpenIDConnectの項の通りに設定するだけでトークンのリフレッシュ周りまで上手くいきました。

ライブラリのバグに遭遇

結局2023年度は一般のお客さんは従来通り紙での整理券配布になったのでAzure AD B2Cを使うことは無かったのですが、開発中にnuxt-authとAzure AD B2Cの組み合わせで起こるライブラリのバグを引きました。Azure AD B2Cから指定されるエンドポイントはOAuth2で定義されている各エンドポイントにクエリパラメータを付けた形になっています。/authorizeではなく/authorize?p=hogehogeのような感じ。nuxt-authモジュールは既にクエリパラメータがあるか無いかに関わらず必ず?を付けてから各種パラメータを&で結合してリクエストを飛ばす実装になっていたので、?がURLに2回登場することになり上手く動作しませんでした。

issue立ててPRも作ったんですがマージされていません。悲しい。patch-packageを使って上記のPRをパッチ適⽤して対応しました(後輩へ。もしバグなどがあったら上流とはソースが微妙に違うので注意してください)
ただ僕にとって初めてのOSS活動(なおマージはされていない)だったので良い経験になりました。OSSにPR出すような人は自分とは住む世界の違う人なのだと思っていましたが意外とこういう身近に使うライブラリに自分でも直せるようなバグ、言い換えればOSS貢献のチャンスが転がっていることを知れて良かったです。余談ですが後日、日本産分散型SNSのMisskeyに機能追加のPRをしてマージしていただき無事OSSデビューを果たしました。

Azure権限管理

Azure ActiveDirectoryではグループでユーザーを管理することができます。このAzure ADのグループを使ってseiryofes.comの権限管理を行いました。

IDトークンにAzure ADのグループの情報も含めて返すように設定します。API側ではグループのオブジェクトIDを持っておきます。それをもって管理者や各団体管理者(各団体の情報を編集できる)の権限があるかを判定するようにしました。

groups属性をjwtに含める設定

↓groupsという配列を得られる

{
  "aud": "f892d251-000c-4634-a0ec-9d296834ef5a",
  "iss": "https://login.microsoftonline.com/c799e15d-d9ce-48db-b48b-43895f012761/v2.0",
  "iat": 1712910019,
  "nbf": 1712910019,
  "exp": 1712913919,
  "groups": [
    "bc9f6b48-cb37-4c63-8b1f-c6e11f3fcf57",
    "432411a7-b903-45b6-89bd-db5c7184b1dc",
    "93bad3a6-815f-4689-acba-25a506eceb87"
  ],
  "idp": "https://sts.windows.net/8ebb1615-d2d8-4fef-ac59-c61ebfe5b4ca/",
  "name": "ekkekuru2",
  "oid": "2e59b4b0-1050-4511-8a94-69ebe347d200",
  "preferred_username": "ekke@ekke.jp",
  "sub": "DdC9WzH25VdU8v52JZDCn44nsE4cnqa7BLTWLzgt",
  "ver": "2.0",
  :
  (省略)
  :
}

ここで注意が必要なのはAzure ADのPotalで確認できる各ユーザーのオブジェクトIDはoidというプロパティに入っています。subじゃないんかい。各団体とその団体の代表者の紐づけはオブジェクトIDでおこなったのでちょっとハマりました。厄介なことに(?)Azure AD B2Cでは普通にsubに入ってるんですね。どうしてこのような仕様になっているのか調べておらずわかりません。

Azure ADのグループで権限を管理するというなかなか強引な方法ですね。果たしてこれは如何なものか、OAuth的にはスコープとか使うような気もしますが...

オープニングアニメーション


トップページにアクセスすると流れる動画です。
チーフ会整理券担当のS[2:3] (書道選択、達筆!!)がiPadで書いた、今年の星陵祭テーマである「祭徠」の書をGIMPで一画ごとに分けてAviUtlでアニメーションさせました。盛り上がりを演出します。
正直言うと別に全然いらないです。自己満。前述した、Webページとしての部分まで全てマテリアルデザインで統一してしまうことに個人的なつまらなさを感じたのでちょっと抗おうとした、という感じですね。他校の文化祭Webサイトはなんでそんなイケイケなデザインに出来るんですか!!?
毎回動画ファイルの読み込みが発生するのでそういう点でもユーザー体験は良くないです。

クラ代への権限

各クラスの星陵祭クラス代表には自分のクラスの個別ページの編集権限をつけました。
↓編集画面

ほんとは各クラ代が準備の様子や舞台の設計、裏設定などを自由に書くことが出来るブログ的な機能を作ろうという構想が(僕の頭の中に)ありました。
星陵祭のクラス劇って本当にすごくて各クラスのこだわりがいろんなところに詰まっているのでTwitterやInstagramよりももっと長文でこだわりを語って欲しかったんですよね...
僕は3年間クラ代をやったのですが、一つ上の代が3年生の時の星陵祭終了後に係分担などのノウハウをまとめたEvernoteを下級生のクラ代向けに共有してくれて、自分たちが3年の劇を作る時にすごく参考になったし読んでいて楽しかったのがこの構想のきっかけです。というかクラ代としてもこういう感じの振り返りをまとめたいですね。

あとがき

はじめに、オンライン整理券制度についてたくさん議論したチーフ会のみなさん、一緒にコーディングをしたり資料を作成したりしてくれたIT委員会技術部・総務部・広報部のみんな、そしてご協力いただいた先生方、本当にありがとうございました。
後日オンライン整理券すごかったねという風に声をかけてもらえるのも嬉しかったし、逆に生徒のみなさんや保護者の方々が何か特別なものではなく当たり前のものとして使っていたのも良かったです。特にコロナ前の整理券制度を知る先生方や保護者の方、劇のキャストのシフトがきつい生徒などからは感謝の言葉をいただき、より良い星陵祭を作ることにに少しでも貢献できたのだなあと感慨深かったです。
また、個人的には正直去年のリベンジという思いがあったことは否めません。様々な成功と失敗の分岐点があった中で、一応は成功と言える結果にほっとしています。
APIの設計からフロントエンドの実装,インフラまで携わって制作したサービスを、数千人規模の方に利用していただけるという貴重な経験ができました。
2022年度の失敗の後などは特にめげそうになることも多かったですが、とても楽しい2年間でした!

最後に、この記事が面白いと思っていただければ、いいねや僕のGitHubのフォローをしてただけるととても喜びます!励みになります!


学校新聞での嬉しいお言葉


ItsukiKigoshi

チーフ会整理券担当Sと

星陵祭2日目終了後に正面玄関付近に居たIT委員と 全員で集まって撮りたかった...

主に参考にさせていただいた記事

https://qiita.com/ret2home/items/9806d073122dd5e7e873

https://zenn.dev/su8ru/articles/cappuccino-system

見てくださっていたら嬉しいなあ

願わくば、この記事が他校で似たようなことをやろうとしている誰かの参考になれば幸いです。

脚注
  1. とあふ(hibiyahibiyahibiya):77期(2023年度当時高校2年生)。IT委員会2代目技術主任。 ↩︎ ↩︎

  2. チーフ会整理券担当S:77期(2023年度当時高校2年生)。星陵祭企画委員会(学校全体に関わる星陵祭の準備をする委員会。以下、「チーフ会」)の整理券担当。 ↩︎ ↩︎ ↩︎ ↩︎

  3. ItsukiKigoshi:75期(2023年度当時大学1年生)IT委員会の前身、チーフ会IT部隊の部隊長 ↩︎ ↩︎

Discussion