GoogleAppScriptで勤怠管理

はじめに
--------------------

### 動機づけ

いちいち出勤・退勤時刻を記録するのが面倒なので、ShellスクリプトとGASを使用して自動化してしまいます。

### 方法

- GAS上にWeb APIを用意し、HTTPリクエストを飛ばすと時刻が記録される口を作ります。
- Mac側でshellスクリプトを定期実行して、職場のWi-Fiに接続していれば、上記APIを叩きに行きます。
- 一日の終わりに記録を集計し、出勤時刻、退勤時刻、滞在時間を計算します。

Macでの定期実行は、Linuxによくあるcronではなくlaunchdを使用するようです。

### 実行環境
- MacOS Sierra 10.12.2

GASスクリプト
--------------------

### ちょっとした準備
スクリプトのプロパティに下記のプロパティを設定します。

- `FILENAME` : 勤怠記録をつけるGoogleスプレッドシートの名前。事前にGoogleドライブにファイルを作成しておく。
- `SHEET_1st` : 集計前の生データを記録しておくシート名。「シート1」でOK
- `SHEET_2nd` : 集計後のデータを記録しておくシート名。「シート2」でOK

Moment.jsを使えるように設定します。外部ライブラリとして導入してもよし、ソース丸コピで突っ込んでもよし。

参考：[日付＆時刻の便利ライブラリ「Moment.js」をGoogle Apps Scriptで使う方法](http://tonari-it.com/gas-moment-js-moment/)

その他

- `summarize`関数に時間ベーストリガーを設定。
- Webアプリとして公開しておく。（下の定期実行シェルにURLを記載する）

### 説明など

- `doGet`関数 : `GET`リクエストを受けた時の処理。受けた時刻をスプレッドシートに`append`しているだけです。
- `summarize`関数 : 時間ベーストリガーで毎日1:00-2:00に実行してあげると、前日の最初〜最後の時刻抽出、出勤時刻・退勤時刻・滞在時刻の計算、計算データのappendを実行します。

```js:main.js
// -----------------------------------------------------------------------------
// GETのハンドラ
// -----------------------------------------------------------------------------
function doGet() {
  try {
    var sp = PropertiesService.getScriptProperties();
    var FILENAME  = sp.getProperty("FILENAME");
    var SHEET_1st = sp.getProperty("SHEET_1st");

    var date = new Date();
    var ss = getSpreadsheetByName(FILENAME);
    var sheet = ss.getSheetByName(SHEET_1st);
    sheet.appendRow([date]);
    return createContent({status: 'OK'});

  } catch(err) {
    return createContent({status: 'NG'});
  }
}


// -----------------------------------------------------------------------------
// スプレッドシート取得メソッド
// -----------------------------------------------------------------------------
function getSpreadsheetByName(filename) {
  var fileIter = DriveApp.getFilesByName(filename);
  if (fileIter.hasNext()) {
    var file = fileIter.next();
    return SpreadsheetApp.open(file);
  } else {
    throw FileError("File Not Found.")
  }
}

// -----------------------------------------------------------------------------
// 返却データの生成
// -----------------------------------------------------------------------------
function createContent(res) {
  return ContentService.createTextOutput(JSON.stringify(res))
                       .setMimeType(ContentService.MimeType.JSON);
}

// -----------------------------------------------------------------------------
// 昨日のデータをサマライズ（1:00-2:00に実行）
// -----------------------------------------------------------------------------
function summarize() {
  var sp = PropertiesService.getScriptProperties();
  var FILENAME = sp.getProperty("FILENAME");
  var SHEET_1st = sp.getProperty("SHEET_1st");
  var SHEET_2nd = sp.getProperty("SHEET_2nd");

  var ss = getSpreadsheetByName(FILENAME);
  var sheet1st = ss.getSheetByName(SHEET_1st);
  var rows = sheet1st.getDataRange().getValues();

  var FORMAT = "YYYY/MM/DD";
  var yesterday = moment().subtract("days", 1).startOf('day');
  rows = rows.filter(function(row) {
    return moment(row[0]).format(FORMAT) == yesterday.format(FORMAT);
  });

  if (rows.length == 0) return;

  var entrDay = moment(rows[0][0]);
  var exitDay = moment(rows[rows.length-1][0]);

  var diff = exitDay.diff(entrDay, "hours", true);

  var sheet2nd = ss.getSheetByName(SHEET_2nd);
  sheet2nd.appendRow([yesterday.toDate(), entrDay.toDate(), exitDay.toDate(), diff, diff-1])

  sheet1st.deleteRows(1, rows.length);
}
```

定期実行シェル
--------------------ー

`airport`コマンドで取得したWi-Fi接続情報を`grep`やら`sed`やらを使用して整形し、接続先のSSID名を抽出します。その後、職場のSSIDに一致した場合は前述のGASの口をcurlで叩きます。

```sh:attrec.sh
#!/bin/bash

SSID=your_office_ssid
GASURL=https://script.google.com/macros/s/your_script_id/exec

AIRPORT=/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport
CURRENT_SSID=`$AIRPORT -I | egrep "^\s+SSID:" | sed -e 's/ *SSID: //g'`

if [ $SSID = $CURRENT_SSID ]; then
  curl $GASURL
fi
```

launchd
--------------------

- Label: 登録する名前。任意。
- ProgramArguments: 実行コマンド等。`$HOME/.attrec/attrec.sh`にシェルが置いてある想定
- StartInterval: 実行間隔。10分おき（600[sec]）の設定。

カスタマイズは、下記あたりを参考に。

参考：[Mac（OS X）ではcronじゃなくてlaunchdでやる - Furudateのブログ](http://furudate.hatenablog.com/entry/2014/11/09/155017)
参考：[Mac OS Xでlaunchdでcronのように定期実行するメモ - launchd.plistの作成とか - tweeeetyのぶろぐ的めも](http://tweeeety.hateblo.jp/entry/2015/01/06/215425)

```xml:attrec.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

  <key>Label</key>
  <string>jp.ryskiwt.attrec</string>

  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>-c</string>
    <string>$HOME/.attrec/attrec.sh</string>
  </array>

  <key>StartInterval</key>
  <integer>600</integer>

</dict>
</plist>
```

インストール
--------------------

シェルの配置とlaunchdへの登録。

```sh-session
mkdir -p $HOME/.attrec
vi $HOME/.attrec/attrec.sh
vi $HOME/Library/LaunchAgents/attrec.plist
Launchctl load $HOME/Library/LaunchAgents/attrec.plist
```

アンインストール
--------------------

launchdからの登録解除とシェルの削除。

```sh-session
launchctl unload $HOME/Library/LaunchAgents/attrec.plist
rm -rf $HOME/.attrec
```