Emacsで勤怠管理あるいはorg-captureとbookmarkの相性の良さあるいは猫

この記事は[株式会社ネクスト(Lifull) Advent Calendar 2016](http://qiita.com/advent-calendar/2016/lifull)の13日目の記事です。

こんにちは！
Lifullグループのひとつ、[株式会社Lifull LiveMatch](http://www.lifull-livematch.com/)でひとりエンジニアをやっている@Otakkyです。
やっているサービスもライブマッチといいます。
[Wantedlyでエンジニアのアルバイト](https://www.wantedly.com/projects/33454)とか募集してます！（宣伝パート

## イントロダクション
さて、ヘーシャは2016年10月からそれなりの頻度で[Vacancy](http://www.vacancy.jp/)というコワーキングスペースに赴き業務をしています。
約三ヶ月弱通ってみたところ、本社にいくより通勤時間もみじかいし会員用スペースは割と静かで集中できるし外はたまに猫がくつろいでるしで「良い！」って感じです。
近くにお立ち寄りの際はぜひ遊びに来てください。


そんな感じで気ままに働いているのですが、唯一、非常に面倒な問題があります。
それが勤怠管理です。

勤怠管理システムが本社からじゃないとアクセスできないようになっているので、**月末に毎回帰社してせっせと一月分の勤怠を入力しないといけない**んです。
日付と出社時間・退社時間をひとつひとつ確認し、ポチポチとシステムに入力していくその様は賽の河原そのものです。

この毎月の入力作業に加え、そもそも**毎日の勤怠をメモしておかなければならない**という問題もついてきます。
わざわざ勤怠管理用のツールを別途導入するのも馬鹿らしいし、だからといってメモ帳だとぜんぶ手打ちで効率悪いし集計もできないし、だからといってExcelはちょっと仰々しいというかなんていうか**御託はいいからいつも開いてるEmacsで勤怠管理をしたい**。

こんな気持ちになるのは、Emacsユーザーであれば当然ではないでしょうか。
ということでやってみました。

## 要件と前提
今回、僕がEmacsで勤怠管理するにあたって求めた要件をまとめるとこんな感じです。

* Emacsで勤怠管理をする
* 出社ができる
* 退社ができる
* 勤務時間の集計ができる
* できるだけ入力を減らす
* 楽しい


また、下記環境で試してます。

* Emacs 25.1(--with-cocoa --with-imagemagick --with-gnutls)
* Mac OS X El Capitan

## できたもの
こんな感じです。
なお、表示される勤務時間はあくまでデモ用に用意した意図も因果もないデータであり、実際の勤務時間及び私の願望とは異なる部分があります。

### 出社時
![syussya.gif](https://qiita-image-store.s3.amazonaws.com/0/63367/1bff1123-6b84-8c54-8bc8-7270c27804a1.gif)

キーバインド: `C-c c s`

`org-capture`を用いて、出社用のテキストをタスクとして登録しています。
あと、ねこがでます。デモがgifなのでお聴かせできないのが心苦しいですが、鳴き声つきです。

ちなみに、出社するとこんな感じのテキストが保存されます。

```el
* [2016-12-01 Thu]
 CLOCK: [2016-12-01 Thu 13:40]--
```

### 退社時
![taisya.gif](https://qiita-image-store.s3.amazonaws.com/0/63367/8aacc1d0-778e-8405-08b9-1eca20095f10.gif)

キーバインド: `C-x x t`としたあと`C-u C-c !`

出社時の`CLOCK`部分にジャンプしたあと、現在の日時を追加することで退社としてます。
ねこはでません。

### 集計時
![taisya.gif](https://qiita-image-store.s3.amazonaws.com/0/63367/087c9442-e8f7-3b02-fbc2-52b59bda51dc.gif)

キーバインド: `C-x x o`としたあと`C-c C-c`

org-modeの機能である`columnview`を利用し、タスクとして登録されてある勤怠をテーブル化・集計しています。
`columnview`は各タスクにある`CLOCK:[xxx]--[yyy]`の部分をそのタスクにかかった時間としてテーブルのカラムに追加する機能があるので、それを利用しています。
ちなみに、「**YOKU　KITA**」が勤怠の登録時間、「**YOKU HATARAITA**」が実働時間になります(昼休憩の1hをマイナスしたもの)。

どうでしょうか？結構簡単に管理できるようになったと自分では思ってます。
あと、ねこがかわいいですね。
詳細はこちらのgistを参考にしてください。
https://gist.github.com/otakky/4e36755c29c4495ef6bb8e79218c3bde
今回の設定はあくまで`org-mode`を勤怠用にカスタマイズしただけなので、elispとして公開するのがむずかしく諦めました。

また、鳴き声は[こちらから拝借させていただきました](http://taira-komori.jpn.org/animals01.html)。

以下、工夫点です。

## ポイント：org-captureとbookmarkをうまく使う
出社時は`org-capture`のテンプレート機能を使えば簡単にできるんですが、退社時はすでにある出社テキストの適切な位置に挿入しないと集計がうまくいかないので少し手間取りました。
いちいち勤怠管理ファイルを自分で開いて、今日の`CLOCK`の場所をみつけてそこに移動して・・・ってやるの面倒ですからね。

そこで、今回は`org-capture-last-stored`というブックマークを利用してます。
これは`org-capture`でメモを保存したときに自動で登録されるブックマークで、名前の通り**最後にキャプチャで保存した場所を参照できる**ブックマークです。
つまり`org-capture`で出社登録したあとこのブックマークを参照すると、一瞬で今日の出社タスクに移動できます。

ただ、出社以外のキャプチャを使用したら当然このブックマークは上書きされてしまいます。
そこで、このブックマークを出社したときだけ他のブックマークにコピーできれば都合が良くなります。

そのための設定が下記になります。

```el:kintai-hook
(defun kintai-hook ()
  (when (string= "~/org/kintai.org" (bookmark-get-filename "org-capture-last-stored"))
    (bookmark-set-position "kintai" (+ 51 (bookmark-get-position "org-capture-last-stored")))
    (bookmark-set-filename "kintai" (bookmark-get-filename "org-capture-last-stored"))
    (show-cat)))

(add-hook 'org-capture-after-finalize-hook 'kintai-hook)
```
`org-capture-after-finalize-hook`は、`org-capture`でタスクを保存したタイミングで呼び出されるフックです。
このフックを利用し、`org-capture-last-stored`の指すファイル名が勤怠管理ファイルのときのみブックマークをコピーするようにしてやります。
これで他のキャプチャに影響されないブックマークを持つことが可能になります。

また、`org-capture-last-stored`が保存している位置は**キャプチャで登録したメモの先頭位置**になります。そのため、せっかくブックマーク:`kintai`にジャンプしても、その後`CLOCK`の場所までちょこちょこ移動しないといけなくてダサさがすごいです。
なので

```el
(bookmark-set-position "kintai" (+ 51 (bookmark-get-position "org-capture-last-stored")))
```
このように`org-capture-last-stored`からポジションを51だけずらしてやると、ちょうどいい位置にブックマークを設定できます。
マジックナンバーでハードコーディングは最高！

`org-capture-last-stored`を利用すれば、他のキャプチャを利用するときの幅も広がりそうですね。

## ポイント：ねこ
ねこがでるとかわいいので良いですね。
みんな出したいと思いますのでここも少し丁寧に解説します。
ひとまずコードは

```el:show-cat
(defun show-cat ()
  (switch-to-buffer "*Cat*")
  (insert-image-file "~/images/cat.jpg")
  (run-with-timer 0.1 nil '(lambda()
                             (call-process-shell-command "afplay ~/sounds/cat.mp3")))
  (run-with-timer 1.5 nil '(lambda()
                           (kill-buffer "*Cat*"))))
```

こんなかんじです。流れとしては

* ねこ用のバッファをつくって
* 画像を追加して
* 鳴き声を流して
* ねこ用のバッファを消す

となります。
ポイントは、鳴き声部分はシェルコマンドにぶん投げてしまうところです。Emacsは本気を出せば[EMMS](https://www.gnu.org/software/emms/)あたりを使えば鳴き声どころか好きな音楽自由自在なんですが、できるだけ標準機能だけで構成したかったので選択肢から外れました。セットアップも面倒ですしね。
ちなみに`afplay`はMac用のコマンドなので、Linuxの方は`mpg123`に変えれば動くと思います（未確認）。Windowsの場合は・・・・どうするんでしょうね・・・

また、このシェルコマンドを叩く部分は`run-with-timer`を使ってやらないとブロッキングが発生してしまい、ねこが鳴くのを待ったあとに画像が表示されるっていう意味わかんない流れになるので忘れずやっておきましょう[^1]。


## 課題
* 退社時の日時登録の入力をもう少し減らしたい
* 月がかわるごとに先月分をまるっと消すのが少しだるい + ログが消えちゃうのでその辺をgitとか使っていい感じに自動化したい
* 退社時や集計時にもねこをだしたいけどいい感じのhookがない

などなどありますが、現状の状態でも割と実用性はあり重宝してます。


## まとめ
勤怠管理の話のはずだったのに後半はねこの話しかしてませんね。ねこかわいいですね。
さて、お気づきの方もいらっしゃるかもしれませんが、ここまでの機能だと

> ひとつひとつ日付と出社時間・退社時間を確認し、ポチポチとシステムに入力していく

という課題がまったく解消されていません。
じゃあどうしているかというと、今回作成した勤怠管理ファイルをHTMLでエクスポートして、**Selenium IDEからそのデータ読み取って勤怠管理システムに自動で打ち込むようにしてます**。
なんでもEmacsでやろうとするな。

また、ねこは出社するたびにでてくるとうるさいし邪魔なのでださないほうがいいとおもいます。


---
いかがでしたしょうか？
勤怠管理を手軽にメモしたい方や、ねこをEmacs上に出したい人の参考になれば幸いです。

明日は僕の同期エンジニアでもある@hikaoの記事です！お楽しみに！

[^1]: `async-shell-command`でもいいんですが、こっちを使うと**\*Async Shell Command\***というバッファが開いちゃって面倒なのでやめました