WekanAPIとjqを使ってカードから作業時間を抽出してみた話

## はじめに

かんばん方式タスク管理ツール「[Wekan](https://wekan.github.io/)」を用いてタスク管理をしている。
様々なAPIも提供されてるようなので、それらを使ってカードから情報を抽出してみる。
![wekan-markdown.png](https://qiita-image-store.s3.amazonaws.com/0/127161/5c091192-ee2d-7ec4-22a3-3813d376ed82.png)


## 環境

- クライアントPC：Windows10 Pro
- コマンド：Git Bash

## WekanのAPIを呼び出す

Wekanが[REST API](https://github.com/wekan/wekan/wiki/REST-API)を提供してくれているのでそれを使う。

![wekan-rest.png](https://qiita-image-store.s3.amazonaws.com/0/127161/d7c3b51f-a8ac-d2d4-9616-5821c6ae1df3.png)


できることを要約すると、以下の通り。

　- ログイン処理
　- ユーザ一覧、ログイン済みユーザ一覧
　- ユーザの登録・有効化・無効化・情報取得
　- カードの登録・削除・更新　などが挙げられる

公式にも記載されてるが、まだまだ未完成状態のよう。

### アクセストークンの取得

ログイン状態でないとカードを見れないため、事前にトークンを発行する。
※`${ServerName}`、`${email}`、`${password}`は適宜入力すること

| HTTP Method | Url            | Short Description                                            |
| ----------- | -------------- | :----------------------------------------------------------- |
| `POST`      | `/users/login` | [Authenticate with the REST API.](https://github.com/wekan/wekan/wiki/REST-API#login) |

```bash
$ curl http://${ServerName}/users/login \
  -d "email=${email}&password=${password}" -s
{"id":"FtDartLCavnm2NbFC","token":"YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw","tokenExpires":"2019-03-17T01:28:38.347Z"}
```

これ以降の処理ではヘッダにトークン情報をセットしてカード情報を取得する。

### swinlaneidの取得

対象のスイムレーン一覧を取得する。※かんばんの各行のidに該当する箇所

| HTTP Method | Url                              | Short Description                                            |
| ----------- | -------------------------------- | :----------------------------------------------------------- |
| `GET`       | `/api/boards/:boardId/swimlanes` | [REST API Swimlanes](https://github.com/wekan/wekan/wiki/REST-API-Swimlanes) |

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \ 
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes -s 
[{"_id":"qHg6qGGShDPmgdrN4","title":"tasks"},{"_id":"sSjG6txBwCGcSviPW","title":"event"},{"_id":"AYcP8gYYDpK2niCxX","title":"backlog"},{"_id":"w53X5PSWDt28dzxxN","title":"sprint"}]
```

### カードの取得

上記で取得したスイムレーン内のカード一覧を取得する。※特定の行に含まれる全てのカード

| HTTP Method | Url                                                | Short Description                                            |
| ----------- | -------------------------------------------------- | :----------------------------------------------------------- |
| `GET`       | `/api/boards/:boardId/swimlanes/:swimlaneId/cards` | [REST-API-Cards](https://github.com/wekan/wekan/wiki/REST-API-Cards) |

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards -s
[{"_id":"FRDjZf9FQcZ4ayXip","title":"Java11検証:2h","description":"","listId":"uK7Thp97rGdtEXQvC"},{"_id":"EmXHtfv7d3y2FQkkM","title":"UnitTest:3h","description":"","listId":"uK7Thp97rGdtEXQvC"},]
```

## jqツールを使用

返却されたJSON形式データはコンソールに一行で表示されるため見づらい。

そのため、文字列の整形やデータの抽出機能を持つツール「[jq](https://stedolan.github.io/jq/)」を使う。

### インストール

公式のダウンロードからインストールしようとしたが、exeが実行できないので別の方法で試す。

以下のサイトを参考に、別の方法としてWindowsのパッケージマネージャChocolateyで入れる。

[jsonを楽に扱えるjqコマンドを導入(Mac, Windows)](https://y-mattu.hatenablog.com/entry/2016/01/19/211118)

確認OK

```
$ chocolatey list jq
Chocolatey v0.10.11
jq 1.5
```

### JSONを整形して出力

パイプで`jq .`をつなぐ。

```bash

$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards -s | jq .
[
  {
    "_id": "FRDjZf9FQcZ4ayXip",
    "title": "Java11検証:2h",
    "description": "",
    "listId": "uK7Thp97rGdtEXQvC"
  },
  {
    "_id": "n7dHW6KsDMGaRjkTA",
    "title": "UnitTest:3h",
    "description": "",
    "listId": "uK7Thp97rGdtEXQvC"
  }
]
```

## jqツールの活用

`:`以降に`2h`のように作業時間を記載しているので、すべてのカードの合計値を計算してみる。

### リスト内のカードで絞り込み

タスクのステータスが「TODO」「DOING」「DONE」に分かれているため、

`select`関数を用いてリストIDで「TODO」に絞り込む。※かんばんの各行のidに該当する箇所

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards -s \
| jq '.[] | select(.listId == "uK7Thp97rGdtEXQvC" or.listId == "JCsuKobA4wBL8jvmb")'
{
  "_id": "FRDjZf9FQcZ4ayXip",
  "title": "Java11検証:2h",
  "description": "",
  "listId": "uK7Thp97rGdtEXQvC"
}
{
  "_id": "n7dHW6KsDMGaRjkTA",
  "title": "UnitTest:3h",
  "description": "",
  "listId": "uK7Thp97rGdtEXQvC"
}

```

### カードの中身で絞り込み

カード内のタイトルのみが欲しいのでキー(title)で絞り込む。

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards | \
 jq '.[] | select(.listId == "uK7Thp97rGdtEXQvC" or.listId == "JCsuKobA4wBL8jvmb") | .title'
"Java11検証:2h"
"UnitTest:3h"
```

### 余計なダブルクオーテーションを削除する

オプション`-r`を付与する。

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards -s | \
jq '.[] | select(.listId == "uK7Thp97rGdtEXQvC" or.listId == "JCsuKobA4wBL8jvmb") | .title' -r
Java11検証:2h
UnitTest:3h
```

### 作業時間のみを抽出する

`split`で`:`より後ろの文字列を取得し、`gsub`で`h`を削除する。

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http:/${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards  -s | \
jq -c '.[] | select(.listId == "uK7Thp97rGdtEXQvC" or.listId == "JCsuKobA4wBL8jvmb") | .title | split(":")[1] | gsub("h";"") | tonumber' -r
2
3
```

### 作業時間を配列にする

数値形式の配列でないと合計値が算出できないため、`tonumber`で数値変換かつ`[]`で囲む。

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards  -s | \
jq -c '[.[] | select(.listId == "uK7Thp97rGdtEXQvC" or.listId == "JCsuKobA4wBL8jvmb") | .title | split(":")[1] | gsub("h";"") | tonumber]' -r
[2,3]
```

### 合計を算出する

`add`で配列内の数値の合計値を求める。

```bash
$ curl -X GET -H "Authorization: Bearer YLerKsBaQMP3MwX7iLRioWKHXbewQ-e9jyPLcFbL1Nw" \
http://${ServerName}/api/boards/pMBEMbm2KfrR2P73n/swimlanes/w53X5PSWDt28dzxxN/cards -s | \
jq -c '[.[] | select(.listId == "uK7Thp97rGdtEXQvC" or.listId == "JCsuKobA4wBL8jvmb") | \
.title | split(":")[1] | gsub("h";"") | tonumber] | add' -r
5
```

## 参考

- [jqを活用してAPIレスポンス等から欲しい情報だけを抽出する](https://dev.classmethod.jp/tool/road-to-jq-master-novice/)
- [jq コマンドを使う日常のご紹介](https://qiita.com/takeshinoda@github/items/2dec7a72930ec1f658af)
- [jqコマンドで覚えておきたい使い方17個](https://orebibou.com/2016/05/jq%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A6%9A%E3%81%88%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E4%BD%BF%E3%81%84%E6%96%B917%E5%80%8B/#i-2)
- [jq Manual](https://stedolan.github.io/jq/manual/)


[^1]: ※見積もり時間を管理できる別のツールを使用したほうが得策だが、今回は既存のツールで頑張る！