LoginSignup
6
3

More than 1 year has passed since last update.

Asana+GAS+DataPortalで子供の夏休みの宿題の進捗を可視化する

Last updated at Posted at 2021-07-26

はじめに

僕に似て夏休みの宿題をサボる傾向にあるようなので、この形質を遺伝させるのはまずいと思い、バーンダウンチャートもどきを作りました。

こんな感じ
image.png

アーキテクチャ

asanaのタスクをGASでsheetに書いてdataportalでグラフ化する流れです。
asanaのPortfolioという機能でサクッとバーンダウンチャートを作成できるっぽいんですが、有料なので自分で書くのです。

image.png

やったこと

大変ポイント(ストーリーポイント)をつける

最初に子供と話し合ってこんな感じでポイントをつけました。
進捗を定量化することが目的なので割とテキトウに付けています。

Asanaにタスクを作る

決めた単位だけブワーッと作ります。うちの場合は一旦スプレッドシートでcsvを作ってasanaにimportしました。

中身のdescriptionはこんな感じにします。

point: 1は先ほど決めたポイントです。これは後でGASでparseしてゴニョゴニョします。

GASを書く

これをスケジュール実行(1hくらい?)で仕掛けておきます。


コード内容
// main.gas

const token = "asana token"
const groupId = "asana project group id"
const spreadSheetId = "集計用のスプレッドシートのid"
const start =  new Date("2021-07-21") // 夏休み初日
const end = new Date("2021-08-31") // 夏休み最終日
const allPoint = 295 // 大変ポイントの総計

function exec() {
  const asana = new Asana(token)
  const spreadSheet = new SpreadSheet(spreadSheetId, "list")

  const list = asana.getTasks(groupId)
  const headers = Object.keys(list[0])
  spreadSheet.apply(headers, list)

  const list2 = _summarize(list, start, end, allPoint)
  const spreadSheet2 = new SpreadSheet(spreadSheetId, "summary")
  spreadSheet2.apply(Object.keys(list2[0]), list2)
}

function _summarize(list, start, end, allPoint) {
  const range = __dateRange(start, end)
  const expected = allPoint / range.length
  const dates = range.map(date => {
    const dateStr = date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate()
    return {
      dateStr: dateStr,
      point: 0,
      expected: expected,
    }
  })
  return list.reduce((ac, val) => {
    if (!val.completed) {
      return ac
    }
    const date = new Date(val.completed_at)
    const dateStr = date.getFullYear() + "/" + (date.getMonth() + 1) + "/" + date.getDate()
    const di = ac.findIndex(v => v.dateStr === dateStr)
    if (di < 0) {
      ac.push({
        dateStr: dateStr,
        point: val.point,
        expected: expected,
      })
    } else {
      ac[di].point += val.point
    }
    return ac
  }, dates)
}

function __dateRange(startDate, endDate, steps = 1) {
  const dateArray = []
  const currentDate = startDate
  while (currentDate <= endDate) {
    dateArray.push(new Date(currentDate.getTime()))
    currentDate.setUTCDate(currentDate.getUTCDate() + steps)
  }
  return dateArray
}

//asana.gas

class Asana {
  constructor(token) {
    this.token = token
  }

  getTasks(groupId) {
    const options = {
      'method': 'get',
      'contentType': 'application/json',
      'headers': {
        'Authorization': 'Bearer ' + this.token,
      },
    }
    const params = [
      "name", "modified_at", "completed", "completed_at", "html_notes"
    ]
    const url = Utilities.formatString(
      `https://app.asana.com/api/1.0/projects/%s/tasks?opt_fields=%s`, groupId, params.join(","))
    const res = UrlFetchApp.fetch(url, options)
    if (res.getResponseCode() !== 200) {
      const errStr = Utilities.formatString("invalid status code!: %d, %s", res.getResponseCode, res.getContentText)
      throw new Error(errStr)
    }
    const data = JSON.parse(res).data

    const list = data.map(val => {
      const html = val.html_notes
      const point = parseInt(Parser.data(html).from("point:</strong> ").to("</body>").build())
      const completed = val.completed === "TRUE" ? true : false
      return {...val, point: point}
    })

    return list
  }
}

//spreadsheet.gas

class SpreadSheet {
  constructor(id, sheetName) {
    this.spreadSheet = SpreadsheetApp.openById(id)
    this.sheetNameData = sheetName
  }
  apply(headers, list) {
    const sheet = this.spreadSheet.getSheetByName(this.sheetNameData)
    const range = sheet.getRange(1, 1, 1, headers.length)
    range.setValues([headers])
    const list2 = list.map(obj => {
      return Object.values(obj)
    })
    sheet.getRange(2, 1, list2.length, headers.length).setValues(list2)
  }
}

Dataportalでグラフを作る

下記のような感じで集計されたカラムのうちdateStrをディメンジョンにして、
point,point,expected,expectedと2つずつカラムをフィールドに加えます。
さらに同じ種類のカラムのうち一方だけをスタイル累計にチェックをするとバーンダウンチャートぽくなります。

image.png

このグラフだと目標累計の線にポイント累計が追いつくように進捗していけばいいことがわかります。
可視化できてよかったですね!
image.png

最後に

はっきり言って僕がAsana apiを触りたかったので作ったんですが、うちでは「これだけ頑張ればこのくらいさぼれるよ」と教えるために使っています。決して子供へプレッシャーを与える目的で使ってはいけないなあと思いました。

以上

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3