目次
1.はじめに
2.やろうと思った背景
3.今回やったこと
4.完成イメージ
5.事前準備
6.操作方法
7.スクリプト(コピペでOK)
8.今後の展望、最終的なゴール
1. はじめに
SpreadsheetとTrelloを連動させたい
プロジェクトの管理でSpreadsheetとTrelloを一部連携させた時の考え方とコードをまとめてみました。
特に以下の記事は参考にさせていただいた部分も多く、投稿者様には本当に感謝しています。
任意の行だけ選択してTrelloに転記するという発想、確認ログの表示をお借りしました!
スプレッドシートからTrelloにカード追加する(任意の行だけ選択可能)
本記事の特徴は以下の通りです。
① スプレッドシートに3つ情報を入れれば連携可能(GASのコード触らなくてOK)
② ハードコードを避け、簡単に他のボードに転用可能
では、はりきって参りましょう。
2. やろうと思った背景
Spreadsheetとtrelloでのタスク管理
普段はプロジェクトの管理のためにSpreadsheetでガントチャートを使ってますが、スマホでポチポチしたい人達とSpreadsheetの相性が悪く、Trelloを併用することに。
Trelloを使ってみて、「Trello便利だけどカードを一枚一枚作るのが面倒くさい!!あああ!!あぁhhhgghあああ!!」
と思いながらのたうち回ったため、Spreadsheetで作成したシートを、Trelloに転記可能にすることを決意。
3. 今回やったこと
GASでスクリプト書いて、SpreadsheetからTrelloへの転記を実装
Spreadsheetのセルに情報を3ついれたら、あとは自動でTrelloに転記してくれる様にしました。
一応逆もできます。つまりTrelloからSpreadsheetへの転記です。
ただし色々と不完全なので、コードは載せときますが適宜修正してください。。
(というか、もっと良い実装ありましたら、教えていただきたいです!笑)
4. 完成イメージ
完成イメージは以下GIFで。
Step1:シート2に最小限のTrello情報を入力すると必要な情報が揃う
1-1. UserName, TrelloAPIKey, API Tokenを入力するとボード情報が出力される
1-2. 出力されたボード情報から今回使うボードの情報を選んで入力するとリスト・ラベル情報が出力される
Step2:シート1にタスクをゴリゴリ書く(ここは人力…)
※ガントも生成できるようなspreadsheetに改造していますが、右側画面は気にしないでください。
Step3:ワンクリックでTrelloに転記される!
5. 事前準備
流石に、Spreadsheet とTrelloボードは事前に用意する必要がありますね。笑
僕は以下のようにしています。
① Spreadsheet(2枚のシート)
以下2枚のシートを運用します。軽く、どんな内容なのか見てみましょう。
①-1 シート1枚目の役割
= タスク情報入力欄。ドラッグしている部分にタスクの名前や詳細、期日などを書いていきます。
①-2 シート2枚目の役割
= Trello情報を整理しSetUpする。上3つを入力すれば、後はほぼスクリプトが上手いことやってくれます。
② Trello (1枚のボード)
Trelloのボードを用意しておいて、そこにリスト・ラベルだけ事前に作成しておきます。
▼リストを先に作成(ToDoやDoingなどは皆さんも使いますかね)
▼使うラベルを先に作成:何でもいいのでカード作ってラベルを登録しておきます
6.操作方法(Scriptすっ飛ばしVer.)
一番下にGASのスクリプトを載せております。
そちらをSpreadsheetのエディタにコピーした状態だったら、以下の操作をするだけで転記できます。
① 2枚目のシートに情報を3つ入力
TrelloのAPIキー・TokenID・ユーザー名を入れます。
画像のとおり、緑色のセルに入力してください。
※ 上記3つの情報の取得方法はいろんなところに書いてあるので割愛します。
「GAS」タブ内一番上の「Step1」をクリックする
→ ユーザに紐づくすべてのボード名・ボードIDが直下のセルに出力される
出力されたボード名・IDから、今回使うボードの情報を選び、「今回使うBoard名」「今回使うBoardID」横のセルに転記
「GAS」タブ内二番目の「Step2」をクリック
→ リスト情報とラベル情報が追記されます(嬉しい)
これで、連携への事前準備は完了です。あとはシート1でタスクをつくるだけ。
② 1枚目のシートにタスク情報を入力
ごりごり情報を書いていきます。
同じようなタスクが発生する別のプロジェクトが多ければ、使いまわし出来ますね!(サボりたい)
③ 「GAS」タブから「シート1からTrelloに転記」のボタンをクリック
すると・・・
この様に、Trelloボードにタスクの名前・ラベル・リスト配置・期限が反映されています。
一応、これが今回のゴールです!
7. スクリプト
ちょっとだけまた事前準備(SpreadsheetからGASを開く)
まずは、SpreadsheetでAppsScriptを開いて、エディタを開きコードを書きます。
① Spreadsheet>「拡張機能」>「AppsScript」:AppsScriptが別タブで開く
② 「ファイル」右横の「+」ボタン>「スクリプト」を選択するとエディタが開きます!
コード内容
以下、コメントつきのコードを載せます。(合計180行程度)
コピペでOKです。(おんなじシート数、セル配置ならこれで動きます。)
わからないところや、こうした方がもっとよくね?などありましたら、ぜひ教えて下さい!
追記:
以下のコードをSpreadsheetのGASエディタにすべてコピペしたら、
一番下のonOpenというコードを実行してください。
認証やら何やら出るかもしれませんが承認してください。
その後、Spreadsheetを一度更新してください!
そうするとSpreadsheetのタブ一番右側に「GAS」というタブが現れると思います。
//事前準備:右リンクからApikey, apiトークンを取得、他UserNameをスプレッドシート3枚目の任意のセルに記入 https://trello.com/app-key
//SpreadSheetの情報取得
const ss = SpreadsheetApp.getActiveSpreadsheet(); //スプレッドシート本体
const sheet1 = ss.getSheets()[0]; //シート1
const sheet2 = ss.getSheets()[1]; //シート2
//事前準備で記入済みのSpreadsheetセル情報を取得
function getCellInfo(sheet,cell){ return sheet.getRange(cell).getValue(); }
const userName = getCellInfo(sheet2,"B1"); //sheet2のB1セルに格納されたuserNameを取得
const apiKey = getCellInfo(sheet2, "B2"); //B2のAPIキーを取得
const apiToken = getCellInfo(sheet2,"B3"); //B3のTokenを取得
const boardName = getCellInfo(sheet2,"B4"); //B4の今回使うボードNameを取得
const boardId = getCellInfo(sheet2,"B5"); //B5のボードIDを取得
//nameとid情報取得のために必要になるURLを変数に格納
let boardsInfoUrl = 'https://api.trello.com/1/members/' + userName + '/boards?key=' + apiKey + '&token=' + apiToken + '&fields=name';
let listsUrl = 'https://trello.com/1/boards/' + boardId + '/lists?key=' + apiKey + '&token=' + apiToken + '&fields=name';
let labelsUrl = 'https://trello.com/1/boards/' + boardId + '/labels?key=' + apiKey + '&token=' + apiToken + '&fields=name';
//URLを入れると、連想二次元?配列[{name:'XX', id:'YYYY'}]でname&ID情報を取ってくる(json)
function getNameAndIdByUrl(url) {
let res = UrlFetchApp.fetch(url, {'method':'get'});
let json = JSON.parse(res.getContentText());
return json;
}
//ボード情報
const allBoardsInfo = getNameAndIdByUrl(boardsInfoUrl); //ボード情報取得:アカウントに紐づくボードすべてのnameとIDを取得
//任意のシートの任意のセルにname, id情報を出力(確認用)
function addInfoToCell(info, startRow, sheet, nameCellColumn){
for(let i = 0; i < info.length; i++){ //すべてのname情報の横にid情報を追記していく
sheet.getRange(startRow + i, nameCellColumn).setValue(info[i].name);
sheet.getRange(startRow + i, nameCellColumn+1).setValue(info[i].id);
}
}
// 取得したボード情報をsheet2のセルに追記
function addBoardsInfoTosheet2(){addInfoToCell(allBoardsInfo, 7, sheet2, 2)}
//list・labelの情報を取得:Sheet2のセルに追記(確認用)
function addListsAndLabelsInfoToSheet2(){
const listsInfo = getNameAndIdByUrl(listsUrl); //リスト情報取得:選択したリストnameとID
const labelsInfo = getNameAndIdByUrl(labelsUrl); //ラベル情報取得:選択したボードのラベルnameとID
addInfoToCell(listsInfo, 7, sheet2, 4); //リスト情報
addInfoToCell(labelsInfo, 7, sheet2, 6); //ラベル情報
}
//※振り分けを行う関数: 引数"data"にリスト・ラベルを入れる
// 関数addTrelloCardにて入力されたlist, labelがdataとして格納される。 → array(listsInfo or labelsInfo)のnameを検索 → 対応するIDを返す
function sortListAndLabelByData(data, array){
for(let i = 0; i < array.length; i++){
if(array[i].name == data) return array[i].id;
}
return array[0][1]; //なければ ラベルなら「その他」を張り、リストなら「MileStone」に格納する
}
//スプレッドシートに書いた内容をTrelloCardに反映する
function addTrelloCard(){
let sheet = ss.getActiveSheet();
let upperLeftCell = sheet.getActiveCell() //1枚目のシートで選択した範囲の一番左上のセルを取得
const listsInfo = getNameAndIdByUrl(listsUrl); //リスト情報取得:選択したリストnameとID
const labelsInfo = getNameAndIdByUrl(labelsUrl); //ラベル情報取得:選択したボードのラベルnameとID
let startRow = upperLeftCell.getRow() //開始位置の行を取得
let range = SpreadsheetApp.getActiveRange(); // 選択しているセルの範囲情報を取得
let rows = range.getNumRows(); // 選択した行数(=カードの数)を取得
// 確認ダイアログをスプレッドシート側に作成
let message = '';
let startCardName = sheet.getRange(startRow, 2).getValue();
message += startCardName;
if (rows > 1) {message += ' を含む' + rows + '枚';}
let result = Browser.msgBox('以下のカードを生成します!よろしいですか?', message, Browser.Buttons.OK_CANCEL);
if (result == 'cancel') {return 'キャンセルされました。';}
// カード作成: 選択範囲を抜けるまで処理を繰り返す
for (let i = 0; i < rows; i++) {
let row = startRow + i;
let label = sheet.getRange(row, 1).getValue();
let name = sheet.getRange(row, 2).getValue();
let desc = sheet.getRange(row, 3).getValue();
let list = sheet.getRange(row, 4).getValue();
// let dueComplete = sheet1.getRange(row, 5).getValue();
// let memberName = sheet1.getRange(row, 6).getValue();
// let memberId = sheet1.getRange(row, 7).getValue();
// let urlSource = sheet1.getRange(row, 8).getValue();
let start = sheet.getRange(row, 9).getValue();
let dueDate = sheet.getRange(row, 10).getValue();
let keyUrl = 'https://api.trello.com/1/cards/?key=' + apiKey + '&token=' + apiToken;
// 以下パラメータに対応する変数・関数を入れる
let options = {
'method' : 'post',
'muteHttpExceptions' : true,
'payload' : {
'name' : name,
'desc' : desc,
'due' : dueDate,
'idList': sortListAndLabelByData(list, listsInfo),
'idLabels' : sortListAndLabelByData(label, labelsInfo),
'start' : start,
'urlSource' : '',
}
}
let response = UrlFetchApp.fetch(keyUrl, options);
}
}
// 一応、逆(TrelloからSpreadsheetへ転記)もできる。ちょっと修正必要ですが。
function getCardsInfoFromOneList(listName, listId) {
let url = "https://trello.com/1/lists/" + listId + "/cards?key=" + apiKey + "&token=" + apiToken;
let options = {
'method' : 'get',
"muteHttpExceptions" : true,
"validateHttpsCertificates" : false,
"followRedirects" : false,
}
try {
const res = UrlFetchApp.fetch(url, options);
// Logger.log(res)
} catch(e) { // 例外エラー処理
Logger.log('Error:')
Logger.log(e)
}
const res = UrlFetchApp.fetch(url, options);
let json = JSON.parse(res.getContentText());
Logger.log(json);
let maxRows = json.length;
for(let i = 0; i < maxRows; i++){
let labels = json[i].labels;
let labelName = labels[0].name;
let name = json[i].name;
let desc = json[i].desc;
//listName: 変数で直接指定するので省略
let dueComplete = json[i].dueComplete;
let member = '';
let idMembers = '';
let shortUrl = json[i].shortUrl;
let start = json[i].start;
let due = json[i].due;
// let dateLastActivity = json[i].dateLastActivity; //過去100件を検索する時に使うもの。スプレッドシートへの入力はしない。
let card = [labelName, name, desc, listName, dueComplete,'','', shortUrl, start ,due]; //要素はシート1に記入する内容順になっている
Logger.log(card);
let firstEmptyRow = sheet1.getLastRow()+1;
for(let i = 0; i < card.length; i++){
let row = firstEmptyRow;
sheet1.getRange(row, i+1).setValue(card[i]);
}
}
}
// 上にある関数をすべてのリストに適用し、すべてのリストのカード情報(3Dリスト)をつくる
function getTrelloData(){
const listsInfo = getNameAndIdByUrl(listsUrl); //リスト情報取得:選択したリストnameとID
output = [];
for(let i = 0; i < listsInfo.length; i++){
if(!listsInfo[i].name){break;} //中身が空のリストにあたったら処理を終了
output.push(getCardsInfoFromOneList(listsInfo[i].name, listsInfo[i].id));
}
Logger.log(output);
return output;
}
// メニューバーにカスタムメニューを追加
// 右のURLを参考にonOpen()をトリガーに追加(目覚まし時計アイコンをクリック)
function onOpen() {
let ui = SpreadsheetApp.getUi();
let menu = ui.createMenu("GAS"); //メニュー名
menu.addItem('Step1: すべてのボードname, idをシート3に出力','addBoardsInfoTosheet2') //表示名、スクリプト名
menu.addItem('Step2: 使用ボード決定後、リスト・ラベル情報をシート2に出力','addListsAndLabelsInfoToSheet2') //表示名、スクリプト名
menu.addItem('シート1からTrelloに転記', 'addTrelloCard'); //表示名、スクリプト名
menu.addItem('Trelloからシート1に転記', 'getTrelloData'); //表示名、スクリプト名
menu.addToUi();
}
8. 今後の展望、最終的なゴール
これだけでも僕にとっては便利になったのですが、
せっかくなのでこの先やりたいと思っていることも書いておきます。
今回の記事ではSpreadsheet→Trelloでのカード生成を扱いましたが、
最終的には、同一のスプレッドシートとTrelloボードを一組として、
双方向で情報を共有し合えるようにしたいと思います。
次の記事ネタは、「Trello側で新しいカードを作ったらその情報をSpreadsheetに転記する」とか、「メンバー情報をちゃんと吸い出せるようにする」とか、そのへんにしようかと。まだまだ先は長そうです。笑
少しでもお役に立てたら嬉しいです!
(初投稿緊張した。。)
↧