Google Spread Sheet + Apps Script で紙の「速報」をWEBの「超速報」にした

スイマーによるスプリント系スポーツのための ~~表計算~~ 改め日曜プログラミングシリーズ第3弾。
じわじわと表計算じゃなくなってきてしまった。あとそろそろ日曜プログラミングの域も出つつある（ジャブジャブつぎ込まれる土日）

[前回記事](https://qiita.com/westdepth/items/2d52fc063bacc22495dc)では Google スプレッドシートにタイムを入力する際に時間判定されるように小細工をする話を書きましたが、今回はこれがそもそも何を目的にしていたかという話です。

# 速報とは
私の観測範囲においては、大会や記録会に行くとSEIKOとかのタイマーで計測された記録が印刷されて会場に張り出されるようになっていて、これを「速報」と呼びます。
機会があってイベントの運営側に参加することになり、運用の改善を試みたというのが今回の発端。

# 現状の整理
超速報導入前の速報作成順序は下記の通り。
1. スポーツタイマーが自動で計時し、レシートに印字する。（スタート合図のピストルとプールの壁に吊るすゴール・ラップ用タッチ板がレシートプリンタとセットになっている）（ソフトタッチなどのタイマー動作不良に備えて手動計時員もいる）
2. このレシートを入力担当者が手元のノートPCでExcelに入力する。
3. 入力した値は主催者が長年こねくり回した数式を経由して出力用のシートに表示される。
4. これを会場に持ち込んだプリンタで印刷して、手の空いた人が速報コーナーに貼りに行く。
5. するとそこに出番の終わった選手がスマホを携えて群れを成してやってきて、自分の記録を写メっていく。

……正直Excelファイルが使われているかどうかは主催次第だと思うのだが、システムを丸ごと借りると結構な金額がかかるらしい（伝聞）。そんなわけで私の参加した大会はExcelだった。


#問題点
ざっと流れを見ただけでもお分かりの通り、

- 入力が面倒
- 印刷が面倒
- 貼りに行くのが面倒
- 速報コーナーに行くのが面倒
- 行ったら行ったでまだ自分の記録が掲示されていなかったら二度手間
- 最終レースだったらクールダウンしてシャワー浴びてる間に速報が撤収されかねない
- うっかり自分の記録を撮影し忘れると主催者に問い合わせでもしない限り永遠に自分の記録が分からなくなる

運営側も出場側もこれだけ面倒なんですね。しかも昨今はこれに加えて、

- 速報コーナーにめっちゃ人が来て密

というのも加わる。もうこれだけでイベント主催者としてはWEB対応待ったなしでしょう。ということで、本部PCの入力内容をWEBで公開できる仕組みを作ることになった。


# システム構成

DB：Google Spread Sheet（以下GSS）
(+ Google Apps Script（以下GAS） で Web API 化）
WEB公開：Github Pages
WEBフレームワーク：Vue.js
※レース結果はもともと最終的にPDF化してWEBで公開されることになっているので、アクセスを参加者のみに限るような認証・制限は実装しません。

# 大まかな仕組み

### 1. タイマーのレシートを見たまま入力したら、 Query 等を使用して JSON の元ネタとして成型する Google Spread Sheet を作成する
まず、レシートの印字内容は以下の通り。

```
ヘッダ
（日付・プログラム順・泳法・距離・男女・組etc.）

              50M LAP
P  L  TURN     TIME
1  5   1S       32.55
2  4   1S       33.39
3  3   1S       36.22

              100M GOAL
P  L  TURN     TIME
1  5   2S     1:05.48 G
2  4   2S     1:10.72 G
3  3   2S     1:15.50 G
```

ヘッダは無視するとして、Pは周回での着順、Lはレーン、TURNは何回目のタッチか。TIMEは言わずもがな。
また、最終ラップではタイムのあとにGがついているがこれも無視です。
速報を作るのに必要なのは泳法, 距離, 組, L, TURN, TIMEの3つ。
見直しの際の利便性を考慮して、レシートとできるだけ同じフォーマットにしつつ、後工程の加工がめんどくさくないように作ってみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311217/c90839ff-5a72-ee87-af19-0ac1189dccdb.png)
こんな感じですね。
（この大会では男女混合で組分けを行うので男女の項目はありません。また、「個人」の項目が見えているのはもともと個人種目・リレー種目を専用のフラグで管理しようとした名残。結局泳法に吸収させたので今は使ってません）

| 列 | データ内容 |
|:-:|:-:|
|A:D列   |ヘッダ情報   |
|E:G列   |レーン・周回情報   | 
|H列   |ラップ距離（計算値）   | 
|I列   |TIME（計算値）   |  

#### 計算値について
ラップ距離は短水路で1周回当たり50をかけているだけ。後の加工を考えたときに「1回目の周回距離」が25だったり50だったりすると処理が面倒だし、参加者への表示時には周回の距離は使わない（順番に並べるだけ）ので、25M競技の場合でもTURN1の距離は内部的に50として処理されます。
また、分未満・分越えのタイムの扱いをそろえるため、[前回記事](https://qiita.com/westdepth/items/2d52fc063bacc22495dc)の数式をI列に仕込んでいます。あれだけ苦労したのにほんっっとに一部分でしかない。

あと、GSS 上にいわゆるエントリー情報のマスタデータを用意していて、泳法・距離・組・レーンをキーに泳者の氏名・年齢・チーム名などが取得できるようにしてます。このマスタデータと上記入力データを左外部結合でくっつけて（実際にはVLOOKUP）ようやく JSON 元データの完成。

### 2. Google Spread Sheet を参照して JSON を吐き出す Google Apps Script を作成し、WEBアプリケーションとして公開する
1で作成した元データを、泳法・距離・組（つまりレース）ごとにネストされた1つのオブジェクトとして返す doGet 関数を GAS 上に作成した。
母語がVBAなので、 ```if(hoge=fuga)``` とやらかしたりしながらもなんとかなった。この部分の開発でだいぶjsの文法に慣れた気がする。

### 3. Vue.js で作成したWEBサイトをGithub Pages で公開し、データソースとして上記 GAS から JSON を受け取るようにする
これが今回一番の時間喰い虫。なんといってもほぼ初めてのjsで、しかもフレームワーク。
会場でアクセスしてもらうわけだからスマホに寄せたデザインで、個人種目とリレーはデータセットを分けて、などとやっていたら瞬く間に時間が削られていった。あと method 呼ぶのに ```this```を忘れるのは本当に何とかしたい。
おそらく Vue.js に慣れている人には屁でもないだろうシンプルな、参照だけのサイトなのに、学習コストの上乗せがすごかったのでお手軽開発の蜜を吸うには至らなかった。先生の次回作にご期待ください（屍）。

### 4. WEBサイトのURLを印刷して会場に張り付けまくる
ここまで来たらもはやウイニングラン。当日会場入りして主催が持ち込んだプリンタでQRコードを大量に印刷し、設営担当者に張り付けをお願いしたら私は本部に自分の巣を作るお楽しみタイムです。
PC、モバイルモニタ、 **外付けテンキー** を並べてひとしきりご満悦したった。昔から運動会の放送係とかでテント脇にいるのが好きでしたね。

# 産業革命～そして歯車へ
さて、嬉々として持ち込んだ道具の中に外付けテンキーがあるわけですが。
こいつとそれにつながれた私こそが今回の仕組みのボトルネックです。
いやもう見りゃわかるんだけど、これ結局人が入力することからは解放されてないので。
最初に掲げていた現状の問題点のうち、一番最初の課題↓は解消されなかったわけです。
> - **入力が面倒**

結果、私は一日中PCに張り付いてレシートの数字を打ち込み続けるシステムの一部と化しました。自分が出る大会だったら棄権しているところだった危ない危ない。
これは早期に解消しないといつまでたってもこの大会では泳げないことになる、せっかく参加チームと顔つないでもらったのに。

ということでもう一つ技術をかませる必要があるという結論になりました。そう、みんな大好き**OCR**。レシートをパシャっと撮って入力の手間を減らしてしまえ。
これについては分量的に分けたいので次の記事に譲ることにします。稼働させた大会も別のだし。