JIRA API を叩いて担当者ごとのストーリーポイントを算出する

アスクルの こたにん (@Kotanin0) です。

アジャイル開発向けプロジェクト管理ツールである JIRA を用いてスクラム開発をすることで、チームの状況を可視化できます。
JIRAには、スプリントの状況を分析するためのレポーティング機能やダッシュボード機能が備わっています。 Dashboard スクラムにおいては 「チームの生産性を高める」 ことが大事な要素のひとつです。
そのためには、これらの分析機能を用いてスプリントの状況を細かく分析する必要があります。

わたしが現在担当しているスクラムチームにおいてもJIRAのレポートやダッシュボードを利用してスプリントの分析をしているのですが、その中でひとつ、どうしても分析したい指標がありました。

「スプリント内でメンバーごとのストーリーポイントがいくつなのか知りたい」

この指標を分析したい理由は、チームがコンポーネントチームではなくフィーチャーチームとして、フラットな関係性であるかを見たかったから。
言い換えると、メンバーごとにタスクが属人化していたり、着手しているタスクのストーリーポイントに差があったりしないかを見たかったから。

分析のためには数字を取りたいです。

JIRA内でなんとか解決したい

まずはJIRA内でなんとか解決したいと思い、実現できる方法を模索してみることにしました。
ダッシュボードを利用して可視化する方法を思いつき、「JIRA dashboard story points」的な検索キーワードでいろいろ調べてみました。

標準では担当者ごとのストーリーポイントを積み上げて可視化する機能はない

調べていく上でわかったことは、JIRA標準では担当者ごとのストーリーポイントを積み上げて可視化する機能はないということ。
ダッシュボードには Two Dimensional Filter Statistics という、2つの軸でマトリックスを表現するgadgetが備わっています。
ですがそのgadgetで指定できる軸に、story points の列が用意されていない のです。
なぜ用意されていないのかまでは調べていませんが、ないものはないので Two Dimensional Filter Statistics で可視化はできなさそうだということがわかりました。
(参考:allow gadgets to show statistics using story points instead of issue counts )

有償のプラグインをインストールすればできそう

Atlassian Marketplace という、Atlassian製品向けのアプリケーションプラットフォーム に、有志が作った有償のプラグインが数多用意されています。
その中に、JIRA内で利用できるプラグインのひとつとして「Status & Progress Report Gadget for Jira」というものがありました。

プラグインのOverviewを読む限りだと、「Count Issues/Story Points by User, Project, Sprint」との記載があるので、メンバーごとのストーリーポイントを可視化できそうです。
ただ、プラグインはお金がかかってしまいます。
お金で解決でもいいんですが、100ユーザだと1ヶ月あたり6,000円です。お安いのかどうなのか。

JIRA API を利用して自作できないか

お金で解決することに二の足を踏んでしまい、辿り着いた先は 「JIRA APIを叩いて自作でなんとかする」 でした。
結果、なんとかなりました。こんな感じで。

{
  "Aさん": 8,
  "Bさん": 18,
  "Cさん": 8,
  "ぬる": 0
}

というわけでここからは、JIRA API を叩いて担当者ごとのストーリーポイントを算出するまでの手順となります。

APIトークンの発行

こちらの手順をもとにAPIトークンを発行します。
発行後、APIトークンは控えておきます。

スプリント内のストーリー一覧を取得するJQLの準備

後ほど登場する Issue Search API で利用するためのJQLを準備します。
JQL(JIRA Query Language) とは、JIRA内で利用できる検索SQLのことです。

JQLの動作確認は、JIRA内の Filters ページにて確認できます。 Filters

今回は、実行中のスプリント内のストーリー一覧を取得するJQLを用意します。

project = XXX AND issuetype = ストーリー AND sprint in openSprints()

project = XXX JIRA内のプロジェクトのエイリアス
issuetype = ストーリー issueがストーリーという属性のもの
sprint in openSprints()現在実行中のスプリント (参考:JIRAで使える関数一覧#openSprints() )

Issue Search API でストーリーを取得

JIRA APIのうち Issue Search API を利用して、ストーリーの一覧を取得します。
Issue Search API ではJQLをリクエストとして渡すことができるので、先程用意したJQLをリクエストに乗せます。

curlコマンドでさくっとAPIを叩いてみましょう。

curl https://xxx.atlassian.net/rest/api/2/search?jql="project%20%3D%20XXXX%20AND%20issuetype%20%3D%20%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AA%E3%83%BC%20AND%20sprint%20in%20openSprints()"
   --user hoge@fuga.com:HOgehoGeFugaFuGA

jqlパラメータに、URLエンコードした文字列を指定します。
--user引数にて、[JIRAのログインメールアドレス] : [APIキー]を指定します。

json内で利用したいフィールドを決定

curlコマンドでAPIを叩くと、ストーリー一覧がjsonレスポンスされます。
Issue Search API のResponsesを参考にしましょう。
今回利用したいのは「担当者」「ストーリーポイント」の2つのフィールドなので、それを探します。

担当者=.fields.assignee.displayName
ストーリーポイント=.fields.customfield_10030

肝となる点は 「ストーリーポイントがカスタムフィールド内にある」 ということです。
customfield_xxxxxxxxxxの部分はお使いのJIRAごとに違うかもしれません。参考程度に。

jqコマンドでjson解析と整形

jsonのどのフィールドを利用すればよいか見当がついたので、jqコマンドを用いて解析と整形を行います。
最終的なかたちとしては「jsonのふたつのフィールドを抽出して、担当者ごとにストーリーポイントを合計する」です。

できあがったjqコマンドを含むワンライナーはこちらです。

curl https://xxx.atlassian.net/rest/api/2/search?jql=(省略)
 | jq '.issues | map({ name: .fields.assignee.displayName, storypoint: .fields.customfield_10030})'
 | sed s/null,/\"ぬる\",/ | sed s/null/0/
 | jq 'reduce .[] as $row ({}; .[$row.name] += $row.storypoint)'

順を追って解説します。

jsonオブジェクトの再整形

jq '.issues | map({ name: .fields.assignee.displayName, storypoint: .fields.customfield_10030})'

jsonから必要なフィールドのみを指定して、jsonオブジェクトを再整形します。
次のような出力になります。

[
  {
    "name": "Aさん",
    "storypoint": 8
  },
  {
    "name": "Bさん",
    "storypoint": 5
  },
  {
    "name": "Cさん",
    "storypoint": 8
  },
  {
    "name": null,
    "storypoint": null
  },
  {
    "name": "Bさん",
    "storypoint": 13
  }
]

担当者とストーリーポイントの項目のみでjsonが再整形されました。

null回避

sed s/null,/\"ぬる\",/ | sed s/null/0/

担当者が割り当てられていない、ストーリーポイントが記載されていないフィールドのnullを回避するためにnullという文字列を適当に置換しておきます。(ここは実装ゴリ押しですごめんなさい)
次のような出力になります。

[
  {
    "name": "Aさん",
    "storypoint": 8
  },
  {
    "name": "Bさん",
    "storypoint": 5
  },
  {
    "name": "Cさん",
    "storypoint": 8
  },
  {
    "name": "ぬる",
    "storypoint": 0
  },
  {
    "name": "Bさん",
    "storypoint": 13
  }
]

nullをぬるにできました。

担当者ごとにストーリーポイントを合計

jq 'reduce .[] as $item ({}; .[$item.name] += $item.storypoint)'

要素ごとに合計を求めます。 jqコマンドのreduceを利用することで、foreachのようにデータを処理していくことができます。
このコマンドだと「name属性ごとにstorypointを合計していく」というループになります。
次のような出力になります。

{
  "Aさん": 8,
  "Bさん": 18,
  "Cさん": 8,
  "ぬる": 0
}

担当者ごとのストーリーポイントが合計されました!

まとめ

JIRA API を叩いて担当者ごとのストーリーポイントを算出できました。
プラグインを入れることもなく、無料でできましたやったね!
ワンライナーで収まったので、分析したいタイミングで叩くとよさそうです。(スプリントが終わる直前とか)
特定のスプリントを分析したい場合は、JQLのスプリントの条件を変えることで取れます。

sprint in openSprints()
↓
sprint = XXXX

スプリント分析して、チームの生産性を高めてベロシティを上げていきましょう!

ASKUL Engineering BLOG

2020 © ASKUL Corporation. All rights reserved.