高校生が文化祭に3日でWeb受付システムを爆速単独開発することになった話

:::note warn
※この記事は、**1年ほど前に下書きを書いて以来放置**していたものです。
初心者だったこともあり、ごり押し実装が多く**参考になる点は少ない**かと思います。
また、記事が非常に長いです。
あくまでアクシデントエピソードとして読んでいただければ幸いです。
:::

こんにちは。しなもんです。
某校で文化祭が行われました。
その時に起きた地獄のデスマーチを紹介します。
皆様の参考（になるのか？）になれば幸いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/14bcf4e2-8ed6-1c70-4d8f-8aa8a16ead11.png)

# 経緯
クラスでカジノが行われることに。
着々と準備を進めていく中、文化祭三日前にして懸念点が。

**これ、人来すぎたらやばくね？**

文化祭でのカジノは一般的なものと異なり、
簡単に言うとテーブルゲームをし、ポイント（お金ではない）を奪い合うというものです。

しかし、テーブル数があまりにも少なく（3台）、
これでは文化祭で見込まれるお客さんの数は到底さばけないと思われる状態であることに気付きました。

待ち時間が発生するのはしょうがない...
お客さんが先にテーブルの予約をできるようにすれば...

**そうだ、回転寿司チェーンにある呼び出しのアレを作ろう！**

そうです。事前に受付をし、自分の番号を待って、呼ばれたら入店するアレです。

ということで、三日前からの怒涛のシステム開発が始まりました。

:::note alert
※情報科なので（？）、こんな感じで脳死開発を始めることがあります。同級生含め全員通常運転です。真似するべきではないです。
:::

# 本番三日前
午前が通常授業だったため午後から開発スタート。

## 環境構築
まずは環境構築しながら仕様を考えます。打ち合わせとか仕様書とか工数計算する暇ないです（し書いたこともないです）。
ざっくり考えたフローとしては

```
受付に設置したiPadをお客さんが操作し、予約
↓
バーコードが表示されるので、読み取ってもらう
↓
バーコード先のサイトで、番号と呼び出し状況、ルール説明などを表示
↓
先ほどとは別のスタッフ用iPadで、空席ができ次第番号を呼び出す
↓
呼び出されると、先ほどのバーコード先のサイトでお知らせ
↓
その場（廊下）にいれば入場、いなければ通知を送信
(このころはPWAを知らなかったので、単に呼び出された旨を表示するだけ)
```

となります。~~（iPad大量にある学校最高）~~
**（これが後々悲劇を生むことになります）**

とにかく完成を急いだので、
```
フレームワーク: Django（爆速構築しやすく、当時ハマっていた）
データベース: SQLite（Djangoでデフォルトで使える）
```
です。

https://www.djangoproject.com

Djangoはいいぞ。

公開インフラについては、いろんなサービスを運営してる自宅鯖と私用独自ドメインがあったので、それを使います。

**そのまま直でサーバー内で環境を構築し（絶対に真似しないでください）**、開発を始めました。

### 小話
この時の（物理的な）開発環境ですが、
* 9月の残暑が続く致死的爆熱教室
* 持ち込みのsurfaceでVPN（Tailscaleを崇めよ）で鯖にssh接続
* 同級生による装飾作業が行われる圧倒的騒音環境（でも楽しいからいい）
* サブモニター（たまたま現場に居合わせてしまった友人から借りたiPad）

でした。過酷。

## データベース構築
まずはデータベースのテーブルを考えました。
意外と脳内で仕様を完成させられたので、すぐできました。
Djangoではテーブルに自動的にプライマリーキーとしてIDが割り当てられるので、それをそのまま呼出番号とします。

```
・どのゲームで遊ぶか（カジノは3種のゲームを選べる。ゲームにIDを振り、数値で指定）
・その番号は呼び出し済みか（bool）
・その番号は入場済みか（bool）
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/ecf66d61-e2c0-58ae-6c4e-2e4ef2f41d49.png)

あとは、簡単に編集などを行えるようurl設計をしました。
```
・add/ 受付した際に新たに番号を発行
・call/ 指定した番号札を渡した人を呼び出す
・play/ 指定した番号札を持っている人を入場済みとする
```
後で集計に使うため、削除は基本しない方針としました。

***

ここまで進んだところで下校となりました。
ちょうどsurfaceのバッテリーも切れました。
~~surfaceくんほんとバッテリー少ないしusbハブ認識しないし（初期不良）なんなんですか~~

ここまでで一日終了です。
ちなみに家で作業はしたくない派です。

# 本番二日前
この日から授業が無く、丸一日フルで活動できます。やったぜ。

前日、surface君のバッテリーが一日持たないことが判明したこともあり、
PC教室で電源借りてめちゃくちゃ作業しました。

### 物理的な開発環境 v2.0
（物理的な）開発環境に、以下の変更が行われました。
* ~~昨日より~~とても静かな部屋に変更されました。
* 部屋に某ソシャゲのゲーム音が鳴り響くようになりました。（なぜかpc室に遊んでいる人がいた）
* 空冷がめちゃくちゃ強化されました。
* PCの変更はありません。（VPNでssh接続しているのでpcを変えられない）
* たまに友達がくるようになりました。（たのしい）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/59da7184-2f77-c8d4-c94f-6b3898d22e3f.png)


## ダッシュボードUI設計
まず登校直後からFigmaで画面を作りました。
これも時間がないので、「受け付けシステム」と調べて出てきた、
いくつかの製品のUIをいい感じに組み合わせて作りました。

具体的には、
* 受付用iPadのUI
* バーコード生成時のUI
* バーコード先のサイト
* 呼び出し用iPadのUI

などを設計しました。

実装時、時短のため**cssをhtmlに全部書き込めるtailwindcss**を使いました。慣れれば超便利。

https://tailwindcss.com

説明文などを加えつつ、いい感じに書けば完成です。

余談ですが、cssを何も書いてないhtmlだけの状態で
受付を担当する同級生達に操作方法を説明した時、
「ダッサwwwwwwww」と言われたのが割と刺さってます。

それは、本当に、そう。
どうせおぬしらもいつか生のhtmlをその手で書くときが来るんだよ！！！！！

## フローの完成
そんなこんなで

* 受付用iPad
* 呼び出し用iPad
* お客さんに通知を飛ばすサイト

が動くところまで完成しました。
疲れた～～～

せっかくなので、それぞれについて少し紹介したいと思います。
### 受付用iPad
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/85c282a3-2952-cc35-f9c2-a19add52869c.png)

ボタンを押すとQRコードが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/4f287750-5545-c465-6623-27d495751523.png)

### 呼び出し用iPad
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/faeb24fa-bfb6-9543-730c-aa919e438855.png)

ボタンを押すと自動で順番が選ばれ、呼び出されます。

### お客さんに通知を飛ばすサイト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/48a301f9-7af4-c625-7f20-2475e614d15c.png)

その後も文章を見直したり、cssを調整したりなど改良を重ねました。

## 事件勃発（システム変更）
あらかた完成したので、クラスへ持っていき、カジノの担当者たちと説明と打ち合わせをしました。

が、**フローに解釈違いがあった**ようでした。

```text:私の認識
受付に設置したiPadをお客さんが操作し、予約
↓
空席ができ入場
↓
予約時に設定したテーブルに座り、ゲーム開始
↓
ゲーム終了後退出
```
つまり、1予約1ゲームということです。
この方法では大量に人数を回すことができ、沢山のお客さんにカジノを体験してもらうことができます。

（一方で、別のゲームも遊びたい場合はもう一度予約して入場を待たなければいけないというデメリットがあり、先述した通りポイント制であったことも含め、1ゲームで如何にポイントを稼ぐかという、初心者にはランキング上位に上がることが厳しいシステムでした。）

一方のカジノ担当者の認識はこうです。
```text:担当者の認識
受付に設置したiPadをお客さんが操作し、予約
↓
キャパに余裕があれば入場
↓
好きなテーブルに座り、ゲーム開始
↓
ゲーム終了後も好きなテーブルに移動できる
↓
数ゲーム後に退出
```

こちらは、1予約数ゲームということです。
この方法では沢山のゲームを体験してもらうことができ、様々なゲームでポイントを稼ぐことができます。

（こちらも一方で、客一人にかかる時間が長くなり、人数を回しづらくなるために、来てくださったお客さん全員がカジノを経験できない状態になりうるシステムでした。）

話し合いの結果、最初のフローで進めることになりましたが、少し修正が必要な状態になってしまいました。

***

そんなことがありつつも、修正が終わったところで下校となりました。
準備最終日にテストをするということで、明日の指示を考えながら電車に乗ったのを覚えています。

# 本番前日

本番前日になってしまいました（過酷）

この日はシステム開発以外にもやりたいことがたくさんあったので（そして疲れたので）
軽い新規開発と、テストと修正のみを行いました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/98176f6e-5992-0b82-238e-846ca12c20f7.png)

ついにsurfaceのキーボードにしびれを切らし、自宅のものを持っていきました。

## テスト
カジノ担当がグループの動きを検証する目的からリハーサルが行われたので、
それに付随してシステムも実際に触ってもらいました。

フローの変更もありましたが、大方動いてくれており、軽微なUIのバグやデータベースのエラーのみで済みました。
もしそうでなければ...もう...（絶望）

## モニター爆誕
情報科の特権（？）を利用してテレビを借りれたので
廊下に配置しておきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/51529425-3b1b-02f1-0e3f-09bc06d6cfa0.png)

これでばっちりですね！

***

実は下校前の4時間から爆睡してしまいました。
まあシステムはほぼ完成していますし、本番に備えて...ってことで...

# 本番1日目

ついに本番が訪れてしまいました。
恐ろしいですね。何せ3日で作ったなにもかもハリボテなシステムですから。

## 午前
最初の1時間は私が受付を回しました。
文化祭のスタート時刻が曖昧で、
**「もう入っていい？入るよ？入るからな？」**
と教室前にわずかに人だかりができてたのが面白かったです。

1日目は在校生限定なこともあり、人数は少ないと思いきや、すぐにテーブルが満席。
早くも予約システムの出番となってしまいました。

とはいえ私も校内を回りたかったので、端末の操作方法を担当の人に伝え、
少し待機したのちにその場を離れました。

## 午後
教室に戻るとびっくりしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/b1c14eae-918f-d2f9-7e4f-ffd3530df6b6.png)

**200人越え..？**
正直ここまで人が来るとは思わなかったので、滅茶苦茶困惑しました。
**バグかとおもったもん。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/02a89c52-545c-c6fb-7c51-ba6baffe8ad3.png)

**なんだ68分待ちって。**

:::note info
ちなみに待ち時間の計算式は以下のものでした
```py
(ポーカーを予約した人数//6)*15
+
(ブラックジャックを予約した人数])//4)*10
+
(ルーレットを予約した人数//4)*10
+
どのゲームでもよい人
```
待っている人数を、同時にプレイできる人数で割り、平均プレイ時間を掛けたものです。
:::
計算式がおかしいんでしょうね...

***
というわけで、本番一日目が終了しました。
最後にsqliteに溜まったデータを発表して終わり。
最終的に255人が参加してくれました。わあい。
**中には呼ばれてから30分後に来た**お客さんのデータもありました。
分析するのも面白かったです。

しかし、問題がありました。
もし間違えて呼び出してしまった場合や、呼び出した後に入場したか分からない場合など、
呼び出し時のトラブルが起こるたびに**SQLを書いてデータを直していました。**

SQLを書けるかつシステムのデータベースを理解しているのは私だけなので
**それはもう大変なことになる**のは想像に容易いでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/372b2a42-05fa-0e9b-a2f5-16182dc1c1b1.png)

結果、呼び出し端末には**もはやデータベースそのもの**ともいえる
滅茶苦茶な操作ボタンが追加されました。
**もう全部これだけでいい気がしてきた。**

# 本番2日目

さて、ここまで記事を読んでいただいたみなさん（僅かだと思いますが）
お疲れ様でした。**最終章です。**

この3日間の努力は全てここで発揮されます。

というのも、本番2日目が本当の文化祭といってもいいでしょう（過言）
一般公開、つまりは**高校内外からお客さんがやってきます。**

**それはもうすごい人数です。**

果たして無事にシステムは動くのか...？

## 午前
操作はもう前日の段階でみなさんに覚えてもらっているので、なにか特別なことはしませんでした。
むしろ、例の**実質データベースな操作パネル**がとっても便利でした。
最初からこうすればよかった。

おかげで特にトラブルもなかったようです。

## 午後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/8c357814-b219-d739-c369-32a7ef558374.png)

**うわでた。**

いやまあ、人数が多いのは予想できますが、
**なんだ98分って。**
驚異の1時間半以上。どうして。

また、端末の充電が次々に無くなっていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1927831/7b1e92ca-80d3-5503-2f51-361724bb7070.png)

**ですが大量のモバイルバッテリーでカバーしていました。**
情報科おそるべし。

***

というわけで、大きなトラブルもなく本番2日目も終了しました。

急ごしらえで生まれたシステムですが、普通に動いてくれていたようです。
とても驚いたと同時に、壊れかけのハリボテじゃなく**しっかり動くシステムを作れてしまった**ことを実感しました。

嘘です。多分たまたま無事だっただけで実際は継ぎ接ぎのハリボテです。

最後に恒例のデータ発表会。
最終的に126人が参加してくれました。
ぱっと見だと先日より減ったように感じますが、受付したものの入場していないだけで、
受付システム自体はこれ以上にめっちゃ酷使されました。

# おわりに
ここまで読んでいただきありがとうございました。

この記事は最初に述べた通り一年前に書いたものであり、今見ると
「もっとこんなフレームワーク使ったらよかったのに...」とか
「こうすればトラブルが無かったのに...」とかたくさん思うことがあります。
多分FirebaseとVueとか使えばサーバーレスでイケイケなシステム作れそう。今ならそうする。

でもやけくそで一度こういう経験をしたので、**開発に持久力が付いた**気がします。

さてさて、一年前に書いた、ということは、たった今もうすぐ次の文化祭の準備に取り掛かっております。
果たして今年の私はどのような突貫工事を行うのか、数ヵ月後が楽しみです。

繰り返しますが、こんな長い記事を読んでいただきありがとうございました。
**よければいいねをお願いします！**



https://www.amazon.jp/hz/wishlist/ls/2DJM6RTD8X4FH?ref_=wl_share

（金欠学生なので良ければ...）