Notionで管理しているチケットのバーンダウンチャートを表示する
株式会社IVRy (アイブリー)のエンジニアのkinashiです。
IVRyではエンジニアの人数が増えてきたこともあり、昨年の11月からスクラム開発を導入しました。
Notionの ボードビュー を使って進捗を管理しています。
Notionは便利なツールですが、バーンダウンチャートを表示する機能は今のところありません。
デイリースクラムでバーンダウンチャートを確認したかったので、Notion API と Google Apps Script(以下GAS) を使って Googleスプレッドシート にバーンダウンチャートを表示してみました。
開発に使ったツール
"devDependencies": {
"@types/google-apps-script": "^1.0.57",
"rome": "^11.0.0",
"typescript": "^4.9.4"
}
今回は TypeScript で書いたコードを tsc でコンパイルし、出力されたコードを GAS のスクリプトエディタにコピペするまごころあふれる方法でコードを反映しました。
romeでlintとformatをしています。
clasp を使うとコマンドでデプロイできるようになるみたいなので機会があれば試してみたいです。
データの取得
グラフを表示するために下記のデータを Notion API から取得します。
- 現在のスプリントのDevチームのストーリーポイントの合計
- 現在のスプリントのDevチームの完了したストーリーポイントの合計
今回はスプリント名を指定してデータを取得したかったので Query API を使用しました。
fetchData
データ取得に必要なアクセストークンやデータベースIDなどはスクリプトのプロパティとして登録しておいて、 PropertiesService を使って読み込んでいます。
データ取得後に開発メンバーでフィルターしたデータを返却しています。
getPoint
取得したデータからポイントを算出します。
const fetchData = (sprintName: string) => {
const token = PropertiesService.getScriptProperties().getProperty('TOKEN')
const databaseId = PropertiesService.getScriptProperties().getProperty('DATABASE_ID')
const payload = JSON.stringify({
filter: {
property: 'Sprint',
select: {
equals: sprintName,
},
},
})
const options = {
headers: {
Authorization: `Bearer ${token}`,
'Notion-Version': '2022-06-28',
},
contentType: 'application/json',
method: 'post' as const,
payload,
}
const data = JSON.parse(
UrlFetchApp.fetch(`https://api.notion.com/v1/databases/${databaseId}/query`, options).getContentText(),
) as Data
const members = PropertiesService.getScriptProperties().getProperty('MEMBERS')?.split(',') ?? []
return data.results.filter((result) =>
result.properties.Assign.people?.some((people) => members.includes(people.person.email)),
)
}
const getPoint = (data: ReturnType<typeof fetchData>) => data.reduce(
(result, { properties }) => {
const point = properties['Story Point'].number ?? 0
const completed = properties.Status.select?.name === 'Completed' ? point : 0
return {
all: result.all + point,
completed: result.completed + completed,
}
},
{
all: 0,
completed: 0,
},
)
初期値を出力する
ストーリーポイントは1日に1回計測することにし、次のような値を出力するようにしました。
日付と休日はスプリントの分だけ入力しておいて、理想線表示用の項目を埋めます。
日付 | 休日 | 理想 | 実績 |
---|---|---|---|
start | 100 | 100 | |
1/13 | 80 | ||
1/14 | ○ | 80 | |
1/15 | ○ | 80 | |
1/16 | 60 | ||
1/17 | 40 | ||
1/18 | 20 | ||
1/19 | 0 |
init
初期化関数をスプレッドシートのメニューに登録して実行できるようにしておきます。
onOpen
onOpen という関数の中にメニュー追加処理を書くことでスプレッドシートを開いたときにメニューが追加されます。
const init = () => {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const sprintName = sheet.getRange(SPRINT_NAME_RANGE).getValue() as string
const data = fetchData(sprintName)
const { all: allPoint } = getPoint(data)
// スプリント初めのSPを入力
sheet.getRange(`${IDEAL_COLUMN}${START_ROW}:${ACTUAL_COLUMN}${START_ROW}`).setValues([[allPoint, allPoint]])
const lastRow = sheet.getLastRow()
const holidaysCont = sheet
.getRange(`${HOLIDAY_COLUMN}${START_ROW}:${HOLIDAY_COLUMN}${lastRow}`)
.getValues()
.flat()
.filter((value) => !!value).length
const workDaysCount = lastRow - START_ROW - holidaysCont
for (let row = START_ROW + 1; row <= lastRow; row++) {
const isHoliday = sheet.getRange(`${HOLIDAY_COLUMN}${row}`).getValue() !== ''
const dayBeforeValue = sheet.getRange(`${IDEAL_COLUMN}${row - 1}`).getValue()
// 休日の次の日は理想線のポイントを据え置き
const value = isHoliday ? dayBeforeValue : dayBeforeValue - allPoint / workDaysCount
sheet.getRange(`${IDEAL_COLUMN}${row}`).setValue(value)
}
}
const onOpen = () => {
SpreadsheetApp.getActiveSpreadsheet().addMenu('バーンダウン設定', [
{ name: '初期値入力', functionName: 'init' },
// 手動でも実行できるようにしておく
{ name: '現在の実績を入力', functionName: 'setActualValue' },
])
}
日毎の値を出力する
あとは実績の値を出力すれば終わりです!
GASのトリガーを設定し、1日1回実行されるようにします。
const setActualValue = () => {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const sprintName = sheet.getRange(SPRINT_NAME_RANGE).getValue() as string
const data = fetchData(sprintName)
const { all, completed } = getPoint(data)
const lastRow =
START_ROW + sheet.getRange(`${ACTUAL_COLUMN}${START_ROW}:${ACTUAL_COLUMN}20`).getValues().filter(String).length
sheet.getRange(`${ACTUAL_COLUMN}${lastRow}`).setValue(all - completed)
}
まとめ
Notion API と GAS を使うことでバーンダウンチャートを表示することができました。
少しの遊び心で日々の改善ができたのは良かったです。
今回書いたコードはGitHubに置いてあるので全体が見たい方はこちらでご確認ください。
最後に、IVRyでは一緒に働く仲間を絶賛募集中です!
カジュアル面談もできるので、興味がある方はお気軽にご応募ください!
Discussion