フルリモートになったのでVimで出勤する


この記事はfreee APIで業務を楽しく便利にハックしよう! 【PR】 freee Advent Calendar 2020の6日目の記事です。空いていたので代打投稿させていただきました。

フルリモート時代の出勤はVimでする


人事労務freee APIを使って勤怠を取得し、NeoVimのFloatingWindowでカレンダーを作成して表示しています。
ご覧の通りVimとfreeeAPIの相性は抜群。お互いが手を取り合って同じ呼吸をしていますね。

Vim × freeeAPI

新型コロナウィルスによって、フルリモートになった会社も多いのではないでしょうか。

フルリモートにおける出退勤の方法は、freeeやKING OF TIMEなどの勤怠管理システムを利用することがデフォルトだと思います。

ただ、毎朝出勤するためにブラウザを開いて出勤ボタンをポチッとするのは非常に面倒くさい。いち早くVimを起動して仕事に取り掛かりたいのに、なぜボタンを押すためだけにブラウザを開く必要があるのか。

ということでVimから出勤できるようにしました

Vimでカレンダーを表現する

愚直に各日付ごとにNeoVimのFloatingWindow(以後FW)で作成しています。カレンダーなので各日付ごとに線で区切って表示したいところですが、FWには枠線をつけるオプションが存在しません。

そのため枠線用のウィンドウを作成し、同じ位置に日付表示用のウィンドウを設置しています。2つで1つのウィンドウとして見せているという感じですね。

" 日付用ウィンドウの作成
function! s:create_contents_window(config, field) abort
  let config = {'relative': 'editor', 'row': a:config.row + 1, 'col': a:config.col + 2, 'width': a:config.width - 4, 'height': a:config.height - 2, 'style': 'minimal'}
  let buffer = nvim_create_buf(v:false, v:true)
  call nvim_buf_set_lines(buffer, 0, -1, v:true, a:field)
  return nvim_open_win(buffer, v:true, config)
endfunction

" 枠線用ウィンドウの作成
function! s:create_border_window(config) abort
  let width = a:config.width
  let height = a:config.height
  let top = "╭" . repeat("─", width - 2) . "╮"
  let mid = "│" . repeat(" ", width - 2) . "│"
  let bot = "╰" . repeat("─", width - 2) . "╯"
  let lines = [top] + repeat([mid], height - 2) + [bot]
  let buffer = nvim_create_buf(v:false, v:true)
  call nvim_buf_set_lines(buffer, 0, -1, v:true, lines)
  return nvim_open_win(buffer, v:true, a:config)
endfunction

" 2つで1つのウィンドウとしてみせる
function! s:new_window(config, field) abort
  call s:create_border_window(a:config)
  call s:create_contents_window(a:config, a:field)
endfunction

参考: Feature: optional floating window borders

Vimからfreee APIを叩く

シンプルにsystem()関数でcurlを実行しています。

" 2020年12月の勤怠情報を取得
function! s:get_work_record_summaries() abort
  let cmd = 'curl -s -X GET "https://api.freee.co.jp/hr/api/v1/employees/'.g:EMP_ID.'/work_record_summaries/2021/1?company_id='.g:COMPANY_ID.'&work_records=true" -H "accept: application/json" -H "Authorization: Bearer '.g:TOKEN.'"'
  let json = json_decode(system(cmd))
endfunction

freeeのAPIドキュメントからcurlコマンドがコピーできてそのまま使えるのでめちゃ楽です。Swagger万歳ですね。

ENTERでAPIを実行する

今回は「出勤する」もしくは「退勤する」の文字列の上でENTERを押したらfreee APIを実行するようにしています。

ENTERの判定はシンプルにvimスクリプト上にnnoremap <CR>を書くだけですね。

" ENTERで関数を呼ぶ
nnoremap <silent> <CR> :call PostWork()<CR>

" 打刻APIを実行
function! PostWork() abort
  let line = getline('.')
  let action = line == '出勤する' ?
    \ {'type': 'clock_in', 'msg': '出勤しました'} :
    \ {'type': 'clock_out', 'msg': '退勤しました'}
  let cmd = 'curl -s -X POST "https://api.freee.co.jp/hr/api/v1/employees/'.g:EMP_ID.'/time_clocks" -H "accept: application/json" -H "Authorization: Bearer '.g:TOKEN.'" -H "Content-Type: application/json" -d "{ \"company_id\": '.g:COMPANY_ID.', \"type\": \"'.action['type'].'\"}"'
  let json = json_decode(system(cmd))
endfunction

タイムレコーダーの入力は削除できない

人事労務freeeには出勤ボタンをポチッとするAPIは存在するが、入力を削除できない。
一度ポチってしまうとその日は出勤ボタンが押せなくなってしまうため、テストがしずらかった。
回避策は従業員を新たに追加して、その従業員でAPIを実行した。めちゃくちゃ面倒臭い上に、これまた1回しか猶予がないが今回はこれで乗り切った。

終わり

freee APIとVimをコラボさせることで、出勤さえVimで出来るようになりました。
普段触らない技術も間にVimを挟むことで楽しく開発できますね。