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


この記事は株式会社ネクスト(Lifull) Advent Calendar 2016の13日目の記事です。

こんにちは!
Lifullグループのひとつ、株式会社Lifull LiveMatchでひとりエンジニアをやっている@Otakkyです。
やっているサービスもライブマッチといいます。
Wantedlyでエンジニアのアルバイトとか募集してます!(宣伝パート

イントロダクション

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

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

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

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

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

要件と前提

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

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

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

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

できたもの

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

出社時

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

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

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

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

退社時

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

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

集計時

キーバインド: 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として公開するのがむずかしく諦めました。

また、鳴き声はこちらから拝借させていただきました

以下、工夫点です。

ポイント:org-captureとbookmarkをうまく使う

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

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

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

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

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の場所までちょこちょこ移動しないといけなくてダサさがすごいです。
なので

(bookmark-set-position "kintai" (+ 51 (bookmark-get-position "org-capture-last-stored")))

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

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

ポイント:ねこ

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

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あたりを使えば鳴き声どころか好きな音楽自由自在なんですが、できるだけ標準機能だけで構成したかったので選択肢から外れました。セットアップも面倒ですしね。
ちなみにafplayはMac用のコマンドなので、Linuxの方はmpg123に変えれば動くと思います(未確認)。Windowsの場合は・・・・どうするんでしょうね・・・

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

課題

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

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

まとめ

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

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

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

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


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

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


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