GitHub Issue に締切と優先度と親子関係を導入してタスク管理を行う試み

# はじめに
私は GitHub が好きなので何でも GitHub で済ませようと目論むのですが、今回は Issue を使ったタスク管理ができないかに挑戦してみました。

## タスク管理？元から可能だと思うけど？
はい。Open/Closed ステータスやら `- [ ] hogehoge` 表記による TODO リストなど、最低限の機能は揃っているのでタスク管理自体は可能です。実際、個人のちょっとした TODO を管理する程度なら十分機能してくれます。

しかし、いまいち物足りない、使いづらいところもあると思います。たとえばタスク管理には定番の以下属性がありません。

- 締切を設定できない
- 優先度を設定できない
- タスクの親子関係を設定できない

ふと、上記の属性を実現できないか試してみたくなったので、試してみました。

## Issue に締切やら優先度やらを導入するってどういうこと？
Issue には「タイトル」「本文」「ステータス(Open or Closed)」「ラベル」といった属性が多数存在します。が、ここには締切も優先度も親子関係も存在しません。これらも属性として実現できないか、というのが今回の試みであり、それを「導入する」と表現しています。

以下は締切、優先度、親子関係のそれぞれについて、実現方法や使い心地についてまとめていきます。

# 最初にまとめ
最初に実現方法とサンプルを一言でまとめてみます。

- [サンプルリポジトリはこちら](https://github.com/stakiran/taskmanagement_with_issues)
- 実現方法
  - 締切 → Issue に「YYYY/MM/DD という名前の Milestone」を設定する
  - 優先度 → Issue に「優先度: 低」みたいな名前の Label を設定する
  - 親子関係 → 子 Issue から 親 Issue を参照する（と親から子の一覧が見える）

これだけで大体どんな使い心地になるか想像が付く人もいるかもしれませんが、以降で詳しく見ていきます。

# 締切

## 実現方法
**Milestone** を使います。

締切が 2017/12/25 なら、Issue には **2017/12/25 という名前の Milestone** を付与します。

ここで「え？締切日はタスク次第だよね？いちいち YYYY/MM/DD 名の Milestone をつくるの？」という疑問が浮かびますが、答えは YES です。

とはいえ毎度手動でつくるのはだるいですし、（複数人で運用していた場合は）作成者によってフォーマットに乱れが生じるかもしれないので、**GitHub API で最初にまとめて作ってしまう** のが良いでしょう。

## サンプル
[サンプルリポジトリをつくってみました](https://github.com/stakiran/taskmanagement_with_issues/milestones)。

2017/10/31 から一年分（計365個）の Milestoneをつくっています。これなら向こう一年以内なら締切日がいつであっても漏れなく選択肢があります。

## 使い心地
良い点は :o: で、悪い点は :x: で箇条書きします。

- :o: YYYY-MM-DD の Milestone を見れば当該日締切のタスクが全部見える（他に何が残っているかが一目瞭然）
- :x: 最初に Milestone を作るのがだるい（ [GitHubAPI](https://developer.github.com/v3/issues/milestones) を使いました）
- 【Issue 上で Milestone を設定するとき】
  - :x: Issue 上で締切を設定する時、Milestone を付与するわけだが、絶対日付でしか選べない（一週間後など相対で選べない）、カレンダーから選択できないなど地味に不便
  - :x: Milestone 選択時のフィルタが「17 11」で2017-11にヒットしない（いちいち「-」も打たないといけない）
- 【[Milestone 一覧](https://github.com/stakiran/taskmanagement_with_issues/milestones)】
  - :x: 365件の Milestone が全て表示されるので重い...
  - :x: Milestone を CRUD した時に一覧に再描画が走るが（365件全部表示されてると）再読込が重い...
  - :o: 締切の近い順などソート方法がいくらかあるので便利
  - :x: ~~デフォのソート順が Recently Updated なのが使いづらい（Closest due dateにしてほしい）~~ [専用URLから見れば可能](https://github.com/stakiran/taskmanagement_with_issues/milestones?direction=asc&sort=due_date&state=open)
      - こういうオプションですね → `?direction=asc&sort=due_date`

## コード例
Python 2.x と requests ライブラリで書きました。GitHub API を使う部分はラップしています（汚い上に長いので載せません  :sweat: ）。Python だと日付時刻計算等も楽に行えるからいいですね。

コード:

```python
# coding: utf-8

import argparse
import datetime

# GitHub API は api モジュールでラップしているとする
import api

parser = argparse.ArgumentParser()
parser.add_argument('-u', '--username', required=True)
parser.add_argument('-r', '--reponame', required=True)
args = parser.parse_args()
username = args.username
reponame = args.reponame

def create_milestone(datestr, timestr='00:00:00'):
    """ @param datestr A string, but format must be YYYY-MM-DD.
    @param timestr A string, but format must be HH:MM:SS. """
    title = datestr
    description = ''
    duedate = datestr
    duetime = timestr
    due = '{:}T{:}Z'.format(duedate, duetime)
    # 指定したユーザー名の指定リポジトリに
    # 指定タイトル/本文/締切日で milestone を作成する.
    r = api.create_milestone(username, reponame, title, description, due)

    return r

# 現在日付から一年分の milestone を作る.
dt = datetime.datetime.now()
day_adder = datetime.timedelta(days=1)
total = 365
for i in range(total):
    dt = dt + day_adder
    datestr = dt.strftime('%Y-%m-%d')
    timestr = dt.strftime('%H:%M:%S')
    print '[{:}/{:}] {:}...'.format(i+1, total, datestr),
    r = create_milestone(datestr, timestr)
    # milestone 作成成功時は 201、
    # 既に同名が存在してて作れなかった場合は 441(うろ覚え)が返ってくる.
    print 'Status {:}.'.format(r.status_code)
```

使い方:

```
$ python milestone_creator.py -u stakiran -r taskmanagement_with_issues
```

# 優先度

## 実現方法
**Label** を使います。

実現方法は単純で、**「優先度: 高」「優先度: 中」「優先度: 低」といった名前の Label をつくる** だけです。

## サンプル
上記と同じリポジトリですが [Issues ・ stakiran/taskmanagement_with_issues](https://github.com/stakiran/taskmanagement_with_issues/issues) サンプルです。

## 使い心地
Label を使うだけなのでシンプルで使いやすいです。Label はフィルタもできるので、優先度低のタスクを一覧表示する、なんてこともできます。

しかしあくまで Label でしかないので、Label 以上のことはできません。

# タスクの親子関係

## 実現方法
多少無理矢理な実現となりますが、Issue の **Reference** 機能を使います。

Issue #1 の中で `#2` というキーワードを書くと、Issue #2 側では「Issue #1 から参照されたよ(This was referenced)」という情報が表示されます。（これを便宜上 Reference と呼ぶことにしますが）Reference を使うと、擬似的に親子関係を実現できなくもないです。

![github_parent_child_issue_reference.jpg](https://qiita-image-store.s3.amazonaws.com/0/191170/54953934-a64f-5d8e-5af8-5835eb1f76a5.jpeg)

運用のイメージとしては、

- 「子タスク Issue」内に「親タスク Issue」の番号を `#123` 形式で書く
- 親タスクからは「小タスクから参照された」と表示される
- 全ての小タスクから親タスクを参照させれば、親タスクからは自分の子タスク全部が見える
  - 見えるのはタスク名とステータス(Open or Closed)だけですが

このような具合になるでしょうか。

## サンプル
[親タスクのサンプルです](https://github.com/stakiran/taskmanagement_with_issues/issues/6)。

## 使い勝手
- 親子関係設定時
  - :x: 子タスク作成時にいちいち親タスクを指定するのがだるい＆指定を忘れがち
- 運用時
  - :x: [表示反映]小タスクを Close しても親タスク側の表示は反映されない（F5で再読込が必要）
  - :x: [進捗]親タスクからは進捗（小タスク全12件中6件が完了、進捗50%みたいな情報）が見えない
  - :x: [並び順]親タスク上で子タスクの並びが「参照された順」で固定される
- ポテンシャル
  - :x: 孫関係（親→子→孫）を作った時に親から孫が見えない

ただ小タスクが一覧表示されてるだけなので正直言っていまいちですね :sweat: 

# おわりに
以上、半分ネタみたいなものですが、Issue に締切と優先度と親子関係を導入してみました。~~良い暇つぶしになりました~~ タスク管理の一手法として試してみると役に立つのではないでしょうか。