見出し画像

AsanaとGoogleスプレッドシートを連携して、バーンダウンチャートを自動生成する

この記事は、前回投稿したバーンダウンチャートに関する記事の応用編です。

前回は、工数を手動で計算し、Googleスプレッドシートに入力してチャートを作成する方法を紹介しました。ここでは、AsanaとGoogleスプレッドシートを連携して、バーンダウンチャートを自動的に生成する方法を紹介します。

はじめに

こんにちは。株式会社Mobility Technologiesで、タクシーアプリ『GO』の法人向けサービス『GO BUSINESS』のプロダクトマネージャー(PdM)を担当しております。Tannyです。

Asanaの認定アンバサダーでもあり、Asanaの便利な使い方を日々探求しております。

Asanaではダッシュボード機能の中に「バーンアップチャート」を表示する機能があります。が、これだと少しカスタマイズ性が足りないので、私の開発チームでは、Googleスプレッドシートでバーンダウンチャートを作成していました。この方法の場合、残工数をシートに毎日転記する手間が発生します。そこで、Google Apps Script (GAS) を利用して、Asanaの残工数を自動的に連携できるようにしました。

この記事では、連携方法の概要を紹介します。作成する連携フローは以下のようになります。

Google Apps Scriptを利用した連携フロー

はじめに、GASを利用してAsanaからタスク一覧を取得する方法を説明します。次に、取得したタスク一覧からバーンダウンチャートを作成する方法を説明します。


Asanaからタスク一覧を取得する

AsanaではREST APIを提供していて、これを使うことで外部のサービスからタスクのデータを取得したり、タスクの新規作成したりできるようになります。今回はこのAPIをGASで呼び出すことで、Asanaのタスク一覧を取得します。

Asanaのプロジェクトを作成する

まずは、バーンダウンチャートの生成元となるAsanaプロジェクトを作成します。今回は、以下のようなサンプルプロジェクトを用意しました。

サンプル用のAsanaプロジェクト

カスタムフィールドとして「ストーリーポイント(数値)」を作成し、これをバーンダウンチャートに記載する工数としました。

プロジェクトを作成したら、プロジェクトのURLを取得します。URLには以下のような形式でプロジェクトのIDが含まれているので、このIDをメモしておきます。

projectUrl = "https://app.asana.com/0/[project_id]/[project_id]"

アクセストークンを取得する

Asana APIを利用するためのアクセストークンを取得します。Asanaにログインした状態で、以下のURLから開発者コンソールにアクセスします。

URL:Asana Developer Console

画面の指示にしたがって「個人アクセストークン」を取得し、メモしておきます。

個人アクセストークンの新規作成画面

GASの新規プロジェクトを作成する

次に、GASのプロジェクトを作成します。バーンダウンチャートを作成するためのGoogleスプレッドシートのファイルを作成し、「拡張機能」>「Apps Script」をクリックします。

「拡張機能」>「Apps Script」をクリック

プロジェクトが作成できたら、「プロジェクトの設定」>「スクリプト プロパティ」をひらき、先ほど取得したトークンをプロパティとして保存しておきます。

「プロジェクトの設定」>「スクリプト プロパティ」にトークンを保存する

これで事前準備は完了です。

Asana APIでタスク一覧を取得する

ここからは、Asana APIを使ってタスク一覧を取得します。GASの「エディタ」メニューを開いて、コードを書いていきます。

Asana APIの「…/projects/{project_gid}/tasks」を利用すると、プロジェクトに含まれるタスクを全て取得できます。公式ドキュメントを参照して、以下のようなコードを作成しました。

[project_id]の箇所には、最初に取得したプロジェクトのIDを記載します。スクリプトプロパティに記載したトークンは「getProperty('ASANA_API_KEY') 」メソッドで取得しています。

function getAsanaTasks() {

  // AsanaプロジェクトのIDを指定
  const projectGid = "[project_id]"

  var options = {
    'method': 'get',
    'contentType': 'application/json',
    'headers': { 
      'Authorization': 'Bearer ' + PropertiesService.getScriptProperties().getProperty('ASANA_API_KEY')
    }
  }  
  // Asana APIを実行し、タスクを取得する
  var response = UrlFetchApp.fetch(`https://app.asana.com/api/1.0/projects/` + projectGid + `/tasks?opt_fields=name,completed_at,created_at,custom_fields`,options);
  var result = JSON.parse(response);

  Logger.log(result)

//省略
}

以下のようにオプションを指定することで、「タスク名」「完了日時」「作成日時」「カスタムフィールド(ストーリーポイント)」を取得しています。

`/tasks?opt_fields=name,completed_at,created_at,custom_fields`

結果はJSON形式で取得します。このコードを実行すると、以下のようにプロジェクトの全てのタスクを取得できます。

タスクの取得結果

補足
AsanaのAPI Explorerを活用して、APIの呼び出し方法を色々と試すことができます。タスクの他の要素を取得してみたいときなどに活用できます。
https://developers.asana.com/explorer

取得したデータを加工する

Googleスプレッドシートで扱いやすいように、取得したデータを加工します。ここでは、日付の形式をGoogleスプレッドシートに認識される形式に変更しています。タイムゾーンもUTC→JSTに変更しています。

//「Asana APIでタスク一覧を取得する」のコード部分

if(result.data){

  //スプレッドシートに表示するためのデータに加工する
    var filteredTasks = result.data
      .map( task => {
        let completedDateTimeYMD = ""
        let createdDateTimeYMD = ""

        if (task.completed_at != null) {
          let completedDateTime = new Date(task.completed_at)
          completedDateTimeYMD = Utilities.formatDate(completedDateTime, 'JST', 'yyyy-MM-dd HH:mm:ss')
        }
        if (task.created_at != null) {
          let createdDateTime = new Date(task.created_at)
          createdDateTimeYMD = Utilities.formatDate(createdDateTime, 'JST', 'yyyy-MM-dd HH:mm:ss')
        }

        return{
          name : task.name, 
          completedAt : completedDateTimeYMD,
          createdAt : createdDateTimeYMD,
          storyPoint : Number(task.custom_fields[0].display_value)
        }
      })

    Logger.log(filteredTasks)
//省略
}

補足
実業務で利用しているスプリクトは、Asanaタスクの他の項目も取得しているため、ここでもう少し複雑な処理をしています。今回のように取得する項目がシンプルな場合は、データの加工は不要かもしれません。

データをGoogleスプレッドシートに出力する

加工したデータをGoogleスプレッドシートに出力します。ここでは「sample」というシートの、「2行目・10列目(J2)」からデータを出力しています。

if(result.data){

//「取得したデータを加工する」のコード部分

  // 出力先シートを指定
 const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("sample")
   
 //タスクをスプレッドシートに出力する
 filteredTasks.forEach( (tasks, i) => {
    let line = i+2 //ヘッダ1行分の次の行
    let row = 9 // 10列目

    sheet.getRange(line, row+0).setValue(tasks.createdAt)
    sheet.getRange(line, row+1).setValue(tasks.completedAt)
    sheet.getRange(line, row+2).setValue(tasks.name)
    sheet.getRange(line, row+3).setValue(tasks.storyPoint)
  })
}

これでスクリプトを実行すると、sampleシートにタスクの一覧が表示されます。

sampleシート

Asanaの連携部分のコーディングは、これでひとまず完了です。このデータを利用して、Googleスプレットシード側でバーンダウンチャートを作成します。

補足
バーンダウンチャートを作成するための集計後データをGAS側で作成することもできます。しかし、GAS側のコードが複雑になりそうなので、この後のデータ集計はGoogleスプレッドシート側で行うようにしています。

関数の定期実行を設定する

今回作成した関数を定期的に実行するように設定して、最新のタスクをGoogleスプレッドシートに同期するようにします。

「トリガー」メニュー > 「トリガーを追加」から、定期実行のトリガーを設定できます。バーンダウンチャートは1日に1回更新するので、データの取得も1日1回に設定しています。

トリガーの追加ダイアログ

これで、毎朝9時ごろにはAsanaプロジェクトの最新のタスク状況がGoogleスプレッドシートに自動的に同期されるようになりました。

バーンダウンチャートを作成する

自動的に同期されたデータを利用して、バーンダウンチャートを作成します。Googleスプレッドシートを用いたチャートの作成方法は、前回のnote記事に記載していますので、詳しい仕組みは「表計算ソフトを用いたチャートの作り方」の章を参照してください。

上記で紹介した方法では、「残工数」を手入力していましたが、今回はGoogleスプレッドシートのSUMIFS関数を利用して、自動で入力できるようにします。

ベースの表を作る

まずは、ベースとなる表を作ります。以下の例のように、灰色セルの箇所は手入力で記載し、黄色セルの箇所は数式を書いて埋めます。青セルの残工数部分は、これから数式を作ります。

ベースとなる表

この例では、締めタイミングを当日の23:59に設定しています。この日時は集計したいタイミングによって変更してください。

残工数を計算する

残工数の部分は、以下のような考え方で数式を作ります。

当日の残工数
現時点で完了していないタスク(完了日時が空白)の工数
+ 当日より前に完了したタスク(完了日時が当日より前)の工数

この計算式を、以下のようにSUMIFS関数で表現しました。「締めタイミング」の日時を基準にして、毎日の残工数を計算します。(この例では、完了日時を後から手動で変更しています。)

残工数の計算方法

バーンダウンチャートを作成する

必要なデータがそろったので、あとはグラフを挿入してバーンダウンチャートを作成するだけです。

バーンダウンチャートの作成結果

この例では、プロジェクトの期間が全て終了した後の状態を出力しています。プロジェクトの途中では、Asanaのタスクの状態を変更し、GASの関数が定期実行されるたびに、バーンダウンチャートが更新されます。

なお、今回は「作成日時」の項目を利用しませんでした。プロジェクトの途中でタスクが新しく追加される場合は、この項目を利用することで、累積工数の増加をチャートに表現できます。


おわりに

今回は、GASでAsanaからタスク一覧を取得して、自動的にバーンダウンチャートを作成しました。毎日のちょっと面倒な単純作業を自動化することができます。

この記事ではもっともシンプルな方法だけを紹介しましたが、GASとAsana APIを活用すれば色々な機能をチャートに追加することができます。例えば以下のような機能です。

  • 累積工数も表記したバーンダウンチャートを作成する

  • 担当ユーザー別の完了工数を計算して表示する

  • Slack APIを利用して、チャートの更新内容をSlackに通知する

今回の作業で初めてGASを利用してみましたが、非常に簡単にシステム間の連携を実装することができました。まだまだ改良の余地はありそうなので、上記のような機能拡張も試してみたいと思っています。

参考資料


いいなと思ったら応援しよう!