会社のトイレが空いたかどうか、社内サイトからPCに通知させる

これは [Yahoo! JAPAN 18 新卒 2 Advent Calendar 2018](https://adventar.org/calendars/3419) の15日目の記事です。
Yahoo! JAPAN 2018年度新卒有志によるアドベントカレンダーです。前日の記事は@abekohさんによる[Slackでスニペットを共有するためのVimプラグインつくってみた](https://qiita.com/abekoh/items/707a589dadf571dbc390)です。

----
こんにちは。
突然ですが、皆さんは快適にお花を摘みに行けていますか？
今日は私の職場のトイレの話で押し切ろうと思います。よろしくおねがいします。

## そのトイレは可視化(み)えているか

私の通う紀尾井町のビル、昨年できたこともあってトイレは広くキレイなのですが、
タイミングによっては行列が発生することがあります。
いつ列ができてもおかしくないので、最初の頃は私も幾度となく列に泣かされました。
<br>
しかし今年春、そんなトイレ難民にとって福音となる社内向けWebサイトが登場しました。
それが「`紀尾井町ビルトイレ利用状況`」です。
各トイレの個室の混雑度をリアルタイムに表示してくれるサイトなのです。
<img width="709" alt="スクリーンショット 2018-12-04 16.15.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/201989/0fba5cec-766e-235f-136e-13badc7403dc.png">
サイトイメージ(管理者様に掲載を許可していただきました)


## 情報を元にしたお花摘み戦略
サイトが教えてくれるのは、トイレのドアの開閉センサーをもとにした「空室の数」です。
男子トイレの場合、残り空き部屋数が3以下になった場合にステータスが変わり
<img width="217" alt="スクリーンショット 2018-12-04 16.49.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/201989/ada3977c-669d-bb70-96d4-0da0f9d7869e.png">
表示が変更されます。

空き室が2部屋以下のときの情報が確定するわけですが、並んでいる人は集計に考慮されていません。ここが最大の問題です。

経験則上、確実に入れるのは2部屋以上空いているときです。
1部屋空室では確証がありません。すぐに並んでる人が次に入るかもしれませんし、トイレにいくまでの数十秒で先を越される可能性もあります。大抵は競り負けてしまいます。


そこで、私は普段以下のような評価でトイレに行けるかを見ています。
- 10秒ごとに空き部屋数を確認する
- 空き部屋数をそのままスコア値とし、累計スコアに加算していく（1部屋→1部屋と空いていたら1＋1＝2）
- 累計スコアが3以上になれば空いていると確定する
- ただし満室になった時点で累計スコアを0に戻す

これによって、並ぶ時間をなるべく短くしています。

## トイレに拘束されない生活でありたい

…ですが、これでは困ったことがあります。
トイレが満員のとき、空きが出るまでサイトを表示して眺め続けるのはつらいです。
サイトをチェックするのは別のなにかに任せて、自分は業務に集中したいのです。
<br>

そこで、このサイトをブラウザに自動監視してもらい、空いたらPCに通知して貰うことにしました。

トイレに空室があると確実になると……

こんな通知とか
<img width="360" alt="スクリーンショット 2018-12-06 17.03.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/201989/1b0e7062-1b36-7765-747a-e58c40e907fe.png">

こんな通知が出て、これが出たらだいたい空室があるので颯爽と席を立っています。
<img width="363" alt="スクリーンショット 2018-12-06 17.25.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/201989/7645396d-75da-0e52-abfd-0926342e0aac.png">
べんりー。
## やりかた
ブラウザの検証機能などでソースを見ると、各フロアの情報を表示している要素を見ることができます。
今回の場合、例えば17階の男子トイレのデータは` <td id="17F_male">....</td>`の中に入っていました。
ここのテキストデータを取得すれば空き状況が確認できそうです。
<br>
取得としては、Python3上で画面描画をOFFにしたChromeブラウザを動かし、該当のフロアの文字情報を検索して今座主情報を取得。スコアに変換して計測しています。

- 画面描写しないヘッドレスブラウザ `ChromeDriver`
- 端末からブラウザを自動操作できるテストツール`Selenium`
- ソースの情報をパースしてデータを絞り込める`BeautifulSoup`

この辺をインストールして、該当のフロアのデータを取り、時が来たらAppleScript
社内サイトなのでURLなどの情報は掲載できないのですが、下の感じで取得しています。

```Javascript

import subprocess
import os
import time
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.common.keys import Keys
from datetime import datetime as dt
from time import sleep
from bs4 import BeautifulSoup
 
#階数を入れると該当のフロアの個室ステータスを返す
def get_toilet_status(floor):
    data_now = driver.page_source.encode('utf-8')
    html_now = BeautifulSoup(data_now,'html.parser')
 
#IDから検索
    #タグ内容は取得情報に応じて変える
    targetTag = "#" + str(floor) + "F_male"
    print(html_now.select_one(targetTag).string)
    wcStatus = html_now.select_one(targetTag).string
 
    latest_time = dt.now()
    tstr = latest_time.strftime('%m/%d %H:%M:%S')
    print(tstr)
 
    return wcStatus,latest_time
 
#階数を入れると該当のフロアの空きレベルを返す
# 0…満 1…混雑 2…やや 3…空
def get_toilet_level(floor):
    status, latest_time = get_toilet_status(floor)
 
    if status == '満室':
        return 0, latest_time
    elif status == '混雑':
        return 1, latest_time
    elif status == 'やや混雑':
        return 2, latest_time
    else :
        return 3, latest_time
 
# 10秒おきに空室情報を確認し、空室が十分にありそうなら通知を返す
def announce_toilet_available_single(floor):
    count = 0
    score = 0
    before_state = False
    while count < 120 :
        fb_level, latest_time = get_toilet_level(floor)

        if fb_level == 4 :
            score += 4
        if fb_level == 2 :
            score += 2
        if fb_level == 1 :
            score += 1
        if fb_level == 0 :
            score = 0

        if score >= 4 :
            return "空室があります。出撃可能です"
        if score == 3 :
            return "空室ができました。急ぐことを推奨します"

        count = count + 1
        print('count:',count,'/score:',score)
        sleep(10)
    return "TimeOut"

target_floor = <自分のいるフロア>
 
options = ChromeOptions()
# ヘッドレスモードを有効にする
options.add_argument('--headless')
driver = Chrome(options=options)
driver.get('https://<サイト>.html')
# 描画完了を待つ
time.sleep(2)
 
msg = announce_toilet_available_single(target_floor)
 
os.system("osascript -e 'display notification \"msg: {} \"'".format(msg))
driver.quit()  # ブラウザーを終了する。
```


## ほかにも
この要領で周辺のフロアの混雑情報や、ビル全体の混雑状況を見ることも可能です。
連続して取得したデータをRなどの統計ソフトに食わせれば、このように
<img width="644" alt="スクリーンショット 2018-11-16 13.30.50のコピー.png" src="https://qiita-image-store.s3.amazonaws.com/0/201989/83a78879-c659-9f76-e60d-1ce0d1e3847c.png">

時間やフロアごとの混雑状況を可視化することも可能です（横軸が時間、縦軸はフロアで、色が濃いほど空いています）。
経験上、空いてるフロアや時間帯、曜日などもあるのではないかと思っているので、
データを使って、空き部屋の分析とかできるともっと面白そうですね。

## まとめ
トイレが情報化されていると色々できそうですし、もっと便利にしていきたいものです。
みなさんも試してみて、素敵なトイレライフを過ごしてみてください。
ただ、体調がわるいときは何も考えずに列に並ぶこともお忘れなく。 
ありがとうございました。