Emacs&Org-modeで始めるタスク管理と作業時間計測

# はじめに
私は，大学時代からEmacs & Org-modeでタスクを管理してきました．一方，作業時間は管理していませんでした．
社会人になってからもタスクのみを管理していましたが，以下の理由から作業時間も管理する必要性を感じていました．

+ 理由1: お客さまとの契約毎，社内作業分類毎に作業時間を計上したい
+ 理由2: 作業時間実績を把握し，作業時間見積精度を向上したい

そんな折，自身のタスク管理方法を紹介する機会が生まれ，(新型コロナ感染防止を理由に延期されておりますが．)
この機会に必要性を感じていた作業時間管理を実現し，作業時間管理方法も一緒に説明しよう！
ということで，今年度からEmacs & Org-modeでタスク管理に加え，作業時間管理を始めました．
本記事を執筆した2020/04/22現在時点で，かれこれ1か月ほど運用しています．

本記事は，私のEmacs & Org-modeでのタスク管理，および，作業時間計測方法を紹介します．
また，上記を実現するElisp設定を紹介します．

# こんな方に読んでほしい
+ タスクを忘れて人に迷惑をかけた経験がある．タスクを忘れないよう，管理したい．
+ 作業時間の記録を付けているが，記憶ベースで記録している．実態に即して記録したい．
+ 社外に情報が出るため，Webシステムでのタスク管理や作業時間計測はできない．ローカルファイルで記録したい．

# 前提知識
+ Emacsの環境構築方法
+ elispの読み方
+ Emacsによくあるショートカットキーの読み方(`C-`, `SPC`...)

# 運用
## 考え方
### タスク状態
+ 未完了状態群
  + TODO: 登録されたばかりのタスク．
  + NEXT: 現在進行中，または，仕掛中のタスク．直近着手すべき．
  + WAITING: 他人の作業待ちのタスク．待ち内容を記載する．
  + HOLD: 自分が諸事情で延期中のタスク．延期理由を記載する．
+ _完了状態群_
  + _DONE_: 完了したタスク．
  + _CANCELLED_: 実施不要と判断したタスク．判断理由を記載する．
  + _PHONE_: 計時用タスク．電話したことを示す．
  + _MEETING_: 計時用タスク．会議・ちょっとした会話を示す．

### ファイル・タグ構成
Org-modeには，ファイル内の全項目にタグをつける設定(`#+FILETAGS:`)があります．
そこで，タグ毎にファイルを分割し，タスクを管理しています．

| ファイル名                  | タグ                                  | 概要                                                                         |
|-----------------------------|---------------------------------------|------------------------------------------------------------------------------|
| refile.org                  | `REFILE`                              | Org-captureの記録項目を一時格納する．                                        |
| OwnCompany.org              | `OwnCompany`,`@office`                | 社内タスクを格納する．                                                       |
| CustomerName-SystemName.org | `CustomerName`,`SystemName`,`@office` | CustomerNameというお客さまのSystemNameというシステム関連のタスクを格納する． |
| clock-table.org             | `ETC`                                 | 作業時間集計用ファイル．                                                     |

## 作業流れ
### 概要
業務の流れになぞらえて，Emacs & Org-modeによるタスク管理と作業時間計測流れを説明します．
以降の説明ではスクリーンショットとアニメーションgifを用いています．
これらは私用環境で撮影しており，[ファイル・タグ構成](#ファイル・タグ構成)と構成が異なる点に注意してください．

### 週次レビュー(週初め営業日の場合)
#### 作業開始
`F12 SPC w`を押下し，週次アジェンダを呼び出します．
![wrrklyAgemda.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/5e9aea61-93ae-8157-fe3e-30a20d291818.gif)
週次アジェンダ上で，**Weekly Task Review**にカーソルを合わせ，`I`を押下すると，Org-mode上の作業時間計測が始まります．
現在の作業時間計測対象タスク，および，その作業時間累計は図中赤丸で確認できます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/eb96d11a-3be6-2c3f-b024-ef98a9ce9295.png)
ちなみに，作業時間計測停止時は，`C-c C-x C-o`を押下します．
作業時間計測停止時，タスクにカーソルを合わせる必要はありません．

#### 週次のタスク実施日設定・見直し
週次アジェンダには，現在週に実施予定日，または，締切日があるタスクが表示されます．
週次アジェンダの各タスクを見て，実施予定日，または，締切日を再設定していきます．
タスクにカーソルを合わせ，`C-c C-s`で予定日，`C-c C-d`で〆切日を入力できます．
以下のように，上記ショートカットを入力するとカレンダが表示されます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/007293e0-285d-ad00-fdf8-ef5bf0daa026.png)
この状態で，`Shift-カーソルキー`入力により，カレンダから日付を選択します．`Enter`で確定です．

私は，タスクの実施日予定，または，締切日を明確にできない場合，
翌週月曜に実施予定日を設定し，翌週の週次レビューで当該タスクを棚卸しします．
これにより，永続的なタスク放置を防止しています．

#### 週次の未分類タスク分類
以下のように，**リファイル待ち**ヘッダには未分類タスク一覧が表示されます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/83f4f443-8e45-05a2-d19a-ea818ce1d0ba.png)
まず，分類前にタスクにカーソルを合わせ`TAB`キーで実施予定日，または，締切日が設定されているかを確認します．
確認時に未設定の場合，実施予定日，または，締切日を設定します．
これは，実施予定日と締切日を未設定のまま分類されたタスクは，
日次アジェンダ，および，週次アジェンダで表示されず，棚卸しされなくなってしまうためです．

以下では，`TAB`での確認時，未設定であったため，実施予定日を04/20に設定しています．
![ConfirmSchedule.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/c08a15c8-9700-84b8-de90-d1921f4d07bf.gif)

実施予定日，または，締切日を設定後，タスクをお客さまとの契約，または，社内作業分類に分類します．
タスク分類は，タスクにカーソルを合わせて`C-c C-w`で可能です．
以下のアニメーションgifでは，**VRChatのワールドをUnity2018対応する**タスクを**VRChat**の**World**に分類しています．
![Refile.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/c92ac08c-2f45-608a-d7c7-1cdb5ed87e0b.gif)
なお，私は，インクリメンタル検索インタフェースに[Helm](https://github.com/emacs-helm/helm)を使っています．
Helmでは，上記アニメーションgifのようにファイル名の一部，あるいは，分類のキーワードから分類先を簡単に検索できます．

#### 作業終了
週次アジェンダ上で，**Weekly Task Review**にカーソルを合わせ，
`t`を押下するとタスク状態一覧とショートカットキーが表示されます．
作業終了時は，`d`を押下します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/722590ad-6071-a195-b322-6b169a52e346.png)

`d`の押下時，Org-modeは週次レビューが作業時間計測対象であること，および，
作業時間計測対象が完了状態になったことを検知し，週次レビューの作業時間計測を停止します．

### 日次レビュー
#### コミュニケーションツールからのタスク収集
メーラやチャットツールからタスクをOrg-captureで収集します．
Org-captureは，事前指定したテンプレートで情報を記録する機能です．
私は，Org-captureを`C-c c`に設定しています．`C-c c`を押下すると，以下の画面が表示されます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/2918110b-3144-8815-2633-e60ba73d9421.png)
この画面では，各テンプレートのショートカットキーと，名前が表示されます．
例えば，`t`を押下すると，通常のタスク記録テンプレートが呼び出せます．
同様に，`r`を押下すると，メール返信テンプレートが呼び出せます．
`t`を押下すると，以下の画面が表示されます．
**TODO**の右側に自動でカーソルが移動するため，タスク名だけを入力し，`C-c C-c`で保存します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/31843cdb-5eaf-131a-3cda-a9ff535b5fe4.png)

#### 日次のタスク実施日設定・見直し
`F12 SPC`を押下して日次アジェンダを表示し，タスクの実施予定日，締切日を再確認します．
対象スコープの相違を除き，[週次のタスク実施日設定・見直し](#週次のタスク実施日設定・見直し)と同じ運用です．

#### 日次の未分類タスク分類
[週次の未分類タスク分類](#週次の未分類タスク分類)同様，**リファイル待ち**に表示されるタスクを分類します．
[コミュニケーションツールからのタスク収集](#コミュニケーションツールからのタスク収集)で登録したタスクも**リファイル待ち**に表示されます．
私は，このタイミングで実施予定日，または，締切日を計画します．

#### 1日の作業実施順序計画
[日次の未分類タスク分類](#日次の未分類タスク分類)までの作業により，今日やるべきタスクがOrg-modeファイル群に書き込まれた状態です．
この状態で，日次アジェンダで`r`を押下し，日時アジェンダを更新します．
すると，今日の日付の下に今日やるべきタスクが一覧されます．
ここで，以下のアニメーションgifように，タスクにカーソルを合わせ，
`Alt-Up`または，`Alt-Down`でタスクを動かし，表示順序を作業実施順序と合致するよう整列します．
![Move.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/a3dc213c-61a5-0751-384c-d607267175df.gif)

このように表示順序を作業実施順序と合致するよう整列する理由は，
精神的コストが大きい次作業の選択を日中にしないためです．
日中は，整列したタスクを上から順に取り掛かれば，次作業の選択が不要となります．

#### 前営業日の作業時間確認
弊社では，1日単位でお客さまとの契約毎の工数を記録します．
そこで，私は契約毎の工数をclock table機能で自動計上しています．
任意のOrg-modeファイルに以下を記載し，`#+BEGIN:`の行で`C-c C-c`を押下すると，
agenda対象ファイルを眺め，昨日の作業時間を集計します．

```
#+BEGIN: clocktable :maxlevel 1 :block yesterday :scope agenda :fileskip0 t
#+END
```

集計後イメージは，以下の通りです．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/06ce31cb-b516-4f36-7ec1-d844813b79b7.png)

設定により，様々な作業時間集計が可能です．
詳しくは，[公式ドキュメント](https://orgmode.org/manual/The-clock-table.html)を参照ください．

### 作業実施時
#### 作業の開始
[作業開始](#作業開始)でも述べたとおり，
作業開始時はアジェンダのタスクにカーソルを合わせ，`I`で作業時間を計測します．
また，アジェンダのタスクにカーソルを合わせ`t n`と押下してタスクを**NEXT**状態にします．
これにより，タスク中断時，仕掛中であるとわかるようにします．

#### 作業のメモ
Org-modeでは，タスク内にメモができます．
アジェンダのタスクにカーソルを合わせ`Tab`を押下するとタスクが展開されます．自由にメモしましょう．
![Tab.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/38bc8f5a-565a-e8b6-8cc5-5907a18dccfc.gif)

#### 作業の中断
作業時間の計測を，`C-c C-x C-o`で停止します．
さらに，タスクを本日中に再開できない場合，`t`を押下してタスクを**WAITING**，または，**HOLD**にしておきます．
この時，以下のような画面が表示されるので，中断理由を記録しておきます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/2440852e-f7e9-eada-76f3-c83ec9b63a96.png)

また，中断理由記録後は，`C-c C-s`で中断理由が解消されたか棚卸予定日を設定し，
`C-c C-d`でタスク中断の許容日付を設定します．

この作業により，棚卸予定日になると日次アジェンダにタスクが表示され，確実な棚卸しが可能です．
また，タスクを`TAB`で確認すると，タスク中断の許容日付が明確になっており，
「これって今お客さまをフォローしなきゃいけないんだっけ？」と悩む必要もありません．

### ちょっとした会議，同僚との会話，お客さまからの電話
ちょっとした会議，同僚から話かけられる，お客さまからの電話で作業が中断されることがあります．
皆さんもそんな経験は，1度や2度ではないですよね．
そんな時は，**現在のタスクの計時を終了せず(重要)**，`C-c c`を押下し，Org-captureを起動します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/3751e32d-20ea-0e3b-9bde-72b480b1cf50.png)
ここで，`m`や`p`を押下すると，**MEETING**や**PHONE**のタスクが生成されます．
この時，自動的に生成タスクの作業時間が計測開始されます．
ちょっとした会議，同僚との会話，お客さまからの電話が終わったら，`C-c C-c`でタスクを記録します．
すると，生成タスクの作業時間が計測終了したうえで，**中断したタスクの計時を再開します**．その様子が，以下です．
![clock.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27879/93ab5a0c-7ada-d905-05f8-4a4bebde0e61.gif)

これで，突然の割込み作業時間計測もばっちりですよ．(まだ慣れてなくてたまにOrg-captureを忘れますが...)

# Elisp
## 概要
[運用](#運用)の実現に必要なElisp設定を抜粋し，記載します．

## Org-modeディレクトリ，ファイル変数の定義
```elisp:10-org.el
;; Dropbox導入環境では，Dropboxを優先的に山椒するよう設定．
(setq org-directory "~/Documents/org/")
(when (file-exists-p "~/Dropbox/org/")
  (setq org-directory "~/Dropbox/org/"))
(setq org-default-notes-file (concat org-directory "refile.org"))
```

## TODO状態定義設定
```elisp:10-org.el
;; "|"から右に記載した状態は，完了状態として扱われる．
;; 状態名後の"()"内に状態遷移についての設定を記載する．
;; 1文字目: C-c C-t 押下時に当該状態を選択するためのショートカット．
;; "@": 当該状態に遷移した時，アノテーションの記載を強制する．
;; /!: 当該状態に遷移した時，当該状態への遷移時刻を自動記録する．
(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
              (sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING"))))
```

## TODO状態遷移時のタグ付け指定
```elisp:10-org.el
;; t付きのtupleは，当該状態に遷移した時，そのタグを付与することを示す。
;; ここで付与したタグを活用し，agendaを表示する．
(setq org-todo-state-tags-triggers
      (quote (("CANCELLED" ("CANCELLED" . t))
              ("WAITING" ("WAITING" . t))
              ("HOLD" ("WAITING") ("HOLD" . t))
              (done ("WAITING") ("HOLD"))
              ("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
              ("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
              ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))
```

## agenda設定
```elisp:10-org.el
;; デフォルトで1日のビューとする。
(setq org-agenda-span 'day)

;; agenda 表示の対象ファイル
;; 計時の集計対象にもなる．
(setq org-agenda-files (list org-directory))

;; Custom agenda command definitions
;; tags-todoは、タグ/TODO状態で検索条件を指定。!はDONEでないタスクに検索対象を限定する。
(setq org-agenda-custom-commands
      (quote (("N" "Notes: メモ一覧" tags "NOTE"
               ((org-agenda-overriding-header "Notes")
                (org-tags-match-list-sublevels t)))
              ("h" "Habits: 習慣タスク一覧" tags-todo "STYLE=\"habit\""
               ((org-agenda-overriding-header "Habits")
                (org-agenda-sorting-strategy
                 '(todo-state-down effort-up category-keep))))
              (" " "Agenda: 予定表"
               ((agenda "" nil)
                (tags "REFILE"
                      ((org-agenda-overriding-header "リファイル待ち")
                       (org-tags-match-list-sublevels nil)))
                (tags-todo "-CANCELLED/!NEXT"
                           ((org-agenda-overriding-header "NEXTタスク")
                            (org-tags-match-list-sublevels t)
                            (org-agenda-sorting-strategy
                             '(todo-state-down effort-up category-keep))))
                (tags-todo "-HOLD-WAITING-CANCELLED/!-NEXT"
                           ((org-agenda-overriding-header "処理待ち(NEXT候補)")
                            (org-tags-match-list-sublevels 'indented)
                            (org-agenda-sorting-strategy
                             '(category-keep))))
                (tags-todo "-CANCELLED+WAITING|HOLD/!"
                           ((org-agenda-overriding-header "他者作業待ち・延期中")
                            (org-tags-match-list-sublevels nil)))
               nil)))))
```

## キーバインド設定
```elisp:00-keybind.el
;; 頻繁に実行するagendaは1キーで起動するように定義します．
(global-set-key (kbd "<f12>") 'org-agenda)
```

# おわりに
本記事では，Emacs & Org-modeでのタスク管理方法，および，作業時間計測方法を紹介しました．
また，上記を実現するElisp設定を紹介しました．
これを機に，EmacsやOrg-modeでのタスク管理，および，作業時間計測に興味を持ってもらえたら幸いです．
皆さんも，EmacsやOrg-modeに限らず，思い思いの方法で確実なタスク管理と作業時間計測を実現しましょう！
そして，その情報を共有してお互いのタスク管理方法と作業時間計測方法をよりよくしましょう！

# 参考文献
+ [Org Mode - Organize Your Life In Plain Text!](http://doc.norang.ca/org-mode.html) : Elisp設定・運用方法の参考となったサイト
+ [okada-takuya/.emacs.d](https://github.com/okada-takuya/.emacs.d) : 私が使っているEmacsの設定です．
  + [inits/10-org.el](https://github.com/okada-takuya/.emacs.d/blob/master/inits/10-org.el) : Org-mode関連の設定
+ [EmacsとOrg-modeで簡単TODO管理のススメ](http://www.swlab.cs.okayama-u.ac.jp/lab/nom/articles/okada-20141207-184858) : 在学中にやっていたEmacsとOrg-modeでのタスク管理について説明する記事です．