ふり返りのグループ分けをGASで自動化する
こんにちは!アルダグラムでエンジニアをしている sukechannnn です。
アルダグラムの開発チームはスクラム開発をしており、プラクティスの一環として定期的にKPTを使った振り返りを行っています。
振り返りは KANNA のチャット機能を使って行っています。ユーザーに最も使われる機能のドッグフーディングも兼ねており、定期的に改善案が出てきます。
そんな振り返りですが、開発チームの人数が増えてきたため、毎回ランダムで2つのグループに分けて行うことになりました。
そこで GAS を使ってグループ分けを自動化してみたので、その方法を紹介してみます。
成果物
以下のような仕様で動くようにしました。
- 開発チームメンバー(エンジニア + デザイナー + PdM)を毎回ランダムに2つのチームに分ける
- 開発チームメンバーはスプレッドシートで管理する
- 隔週で金曜日に行われる振り返りの前に、自動でグループ分けの結果をSlack通知する
金曜日にGASが動くと、以下のようにグループ分けの結果がSlack通知されます。
作り方
Google Apps Script の実装
まずは Google Drive から GAS を追加します。会社の Drive でも動きます。
コード.gs に以下のコードを追加します。
// グループ名です。
const groupNames = ['グループA', 'グループB']
const groupNum = groupNames.length;
// 与えられた配列をランダムな順番にします
const shuffle = (array) => {
for (let i = array.length - 1; i >= 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
// メインの通知処理です
const doPost = () => {
// スプレッドシートのURLから取得できるIDです
const spreadSheetById = SpreadsheetApp.openById("xxxxx")
// 上記スプレッドシートに「開発チームメンバー」というシートを作っておいて、そこにグループ分けしたいメンバーの名前を記入します
// 以下で「開発チームメンバー」シートを引っ張ってこれます
const sheetByName = spreadSheetById.getSheetByName("開発チームメンバー")
const developers = sheetByName.getRange(1, 1, sheetByName.getLastRow(), sheetByName.getLastColumn()).getValues()
const shuffledDevelopers = shuffle(developers)
const numPerTeam = Math.ceil(developers.length / groupNum)
let groups = []
for (let i = 0; i < groupNum; i++) {
const order = i + 1
const start = numPerTeam * (order - 1)
const end = numPerTeam * order
if(groupNum === order) {
groups.push(shuffledDevelopers.slice(start, end))
} else {
groups.push(shuffledDevelopers.slice(start, end))
}
}
const groupMessages = groups.map((group, i) => {
const joined = group.map((member, i) => {
return `${i + 1}.${member}`
}).join(' > ')
return `${groupNames[i]}: ${joined}`
})
groupMessages.unshift("今日の振り返りグループは、\n")
groupMessages.push("\nです!")
const message = groupMessages.join("\n")
// Slack に通知するメッセージの内容をログで確認
Logger.log(message)
// Slack から取得したトークンを入れる
const token = "xxxx";
// ライブラリから導入したSlackAppを定義し、トークンを設定する
const slackApp = SlackApp.create(token);
const channelId = "#通知先のチャンネル";
slackApp.postMessage(channelId, message);
// HTTPレスポンスを返す(利用しないので空でOK)
return ContentService.createTextOutput("");
}
GAS で使っている SlackApp
ライブラリは、以下のスクリプトIDで検索して追加できます。
1on93YOYfSmV92R5q59NpKmsyWIQD8qnoLYk-gkQBI92C58SPyA2x1-bq
このライブラリを使うことで、簡単に Slack への通知を実装できます。
スプレッドシートの準備
スプレッドシートは以下のように準備しておきます。
スプレッドシートに「開発チームメンバー」というシートを作り、そこにグループ分けしたいメンバーの名前を記入します。
Slack ボットの準備
Slackボットを用意してトークンを取得する必要があります。Slackボットの作り方は、以下の記事が詳しいです。
OAuth Tokens for Your Workspace
が GAS に登録するトークンです。
※実行結果を先に確認したい場合は、このステップをスキップしても大丈夫です。
実行してみる
GAS で実行する関数を doPost にして、実行ボタンを押してみてください。毎回ランダムなグループ分けがされたメッセージがログに表示され、指定したSlackチャンネルにメッセージが飛ぶはずです。
※Slackボットの作成を飛ばした場合は、slackApp.postMessage(channelId, message);
の行をコメントアウトしてください。
Slack と連携する
Slack ボットを作成したら、Slack からスラッシュコマンドで実行できるようにしてみます。
右上のデプロイボタンから「新しいデプロイ」をクリックして、「ウェブアプリ」としてデプロイします。そうするとGASを実行するためのURLが得られるので、これをSlackボットで使います。
作成したSlackボットに Slash Commands を追加します。左のメニューから Slash Commands を選んで「Create New Command」します。
ここで、先程作成したGASのURLを貼り付けます。また、Slashコマンドの名前などを記入します。
今回は /lookback-group
コマンドにしました。
これを実行すると、冒頭でお見せした Slack メッセージが表示されます。
スケジュール実行してみる
最後に、スケジュール実行を設定してみます。
私達は振り返りを隔週で金曜日に行っているため、そのスケジュールで振り返りの前にSlackに通知するようにしてみます。
まずは以下のコードを doPost() 関数と同じファイルに追加します。
GAS では「隔週で実行」ということができないので、Googleカレンダーに「振り返り」という予定がある時のみ実行するようにしました。
// Googleカレンダーから予定を取得し「振り返り」という予定が入ってる場合のみ doPost 関数(振り分け通知処理)を実行する
const lookBackTrigger = () => {
let date = new Date();
const calendar = CalendarApp.getCalendarById("example@mail.com");
const events = calendar.getEventsForDay(date);
const isLookBackDay = events.some(event => (event.getTitle() === '振り返り'))
Logger.log(isLookBackDay)
if (isLookBackDay) {
doPost()
}
}
そして、左のメニューからトリガーを管理するページに移動します。
右下に「トリガーを追加」ボタンがあるのでそこからトリガーを追加し、実行する関数やどのタイミングで実行するかを選びます。ここでは毎週金曜日の朝に実行するようにしています。
これで設定が完了しました。
試しに実行してみたい場合は「時間ベースのトリガーのタイプを選択」で直近の日時を選択すると、自動でトリガーされるのが確認できると思います。
株式会社アルダグラムのTech Blogです。 世界中のノンデスクワーク業界における現場の生産性アップを実現する現場DXサービス「KANNA」を開発しています。 採用情報はこちら herp.careers/v1/aldagram0508/
Discussion