ToodledoのログをGASを使ってGoogleスプレッドシートに保存する方法
TODOの管理にはToodledoを利用していまして、実績をIFTTTに連携してEvernoteで、1日1ノートになるように記録していました。 そうする中で2点困っていることがありました。
- Evernoteに連携できる項目が少なく、特に予実管理するために必要な項目(Length:予定作業時間、Timer:実績作業時間)が無いため、Evernote側の記録だけを見ても、各タスクの作業時間がわからなかった。
- 週に1〜2回の頻度で1日複数ノートに別れて記録されてしまい、手でメンテナンスする必要があった。
そこで、ToodledoのAPIを使って、実績を、Googleスプレッドシートに記録することにしました。
実際に行なった事
Toodledo
ToodledoのデータにアクセスするためにAPIを利用します。
APIを利用するためにDeveloper's API Documentation : Register & Stats ✓ Toodledo APIよりアプリケーションを登録し、Client ID、Secretを発行します。
Redirect URLは以下の形式で指定する。 「https://script.google.com/macros/d/{PROJECT KEY}/usercallback」
{PROJECT KEY}はhttps://script.google.com/から確認が出来ます。
Googleスプレッドシート
- ツール->スクリプトエディタよりスクリプトエディタを開く
- OAuth2 ライブラリをインポートする
- エディタのリソース>ライブラリよりライブラリを追加する
- プロジェクトキー:MswhXl8fVhTFUH_Q3UOJbXvxhMjh3Sh48
- コードを書く
var CLIENT_ID = 'XXXXX', // 発行したClient IDを指定 CLIENT_SECRET = 'XXXXX'; // 発行したSecretを指定 function onOpen() { SpreadsheetApp.getUi() .createMenu('Toodledo') .addItem('Authorize', 'openAuthDialog') .addToUi(); } function openAuthDialog() { var toodledoService = getToodledoService(); if (toodledoService.hasAccess()) { Browser.msgBox('Already autherized'); } else { var authorizationUrl = toodledoService.getAuthorizationUrl(); var template = HtmlService.createTemplate( '<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>'); template.authorizationUrl = authorizationUrl; var page = template.evaluate(); SpreadsheetApp.getUi() .showModalDialog(page, 'Authorize'); } } ////////////////////////////////////////////////////////////// function getToodledoService() { // Create a new service with the given name. The name will be used when // persisting the authorized token, so ensure it is unique within the // scope of the property store. return OAuth2.createService('Toodledo') // Set the endpoint URLs, which are the same for all Toodledo services. .setAuthorizationBaseUrl('https://api.toodledo.com/3/account/authorize.php') .setTokenUrl('https://api.toodledo.com/3/account/token.php') // Set the client ID and secret, from Toodledo // https://api.toodledo.com/3/account/doc_register.php .setClientId(CLIENT_ID) .setClientSecret(CLIENT_SECRET) .setScope('basic tasks') // Set the name of the callback function in the script referenced // above that should be invoked to complete the OAuth flow. .setCallbackFunction('authCallback') // Set the property store where authorized tokens should be persisted. .setPropertyStore(PropertiesService.getUserProperties()) } function authCallback(request) { var toodledoService = getToodledoService(); var isAuthorized = toodledoService.handleCallback(request); if (isAuthorized) { return HtmlService.createHtmlOutput('Success! You can close this tab.'); } else { return HtmlService.createHtmlOutput('Denied. You can close this tab'); } } function clearService() { OAuth2.createService('Toodledo') .setPropertyStore(PropertiesService.getUserProperties()) .reset(); } // Reusable function to generate a callback URL, assuming the script has been published as a // web app (necessary to obtain the URL programmatically). If the script has not been published // as a web app, set `var url` in the first line to the URL of your script project (which // cannot be obtained programmatically). function getCallbackURL(callbackFunction) { var url = ScriptApp.getService().getUrl(); // Ends in /exec (for a web app) url = url.slice(0, -4) + 'usercallback?state='; // Change /exec to /usercallback var stateToken = ScriptApp.newStateToken() .withMethod(callbackFunction) .withTimeout(120) .createToken(); Logger.log(url + stateToken); } //////////////////////////////////////////////////// function getCompletedTasks() { var folders = getFolders(); var contexts = getContexts(); var toodledoService = getToodledoService(); var response = UrlFetchApp.fetch('http://api.toodledo.com/3/tasks/get.php?access_token=' + toodledoService.getAccessToken() + '&comp=1&after=' + getAfter() + '&fields=folder,context,goal,location,tag,startdate,duedate,duedatemod,starttime,duetime,remind,repeat,status,star,priority,length,timer,added,note,parent,children,order,meta,previous,attachment,shared,addedby,via,attachments'); var tasks = JSON.parse(response); var values = []; if (tasks.length > 1) { for (var i = 1; i < tasks.length; i++) { var task = tasks[i]; var value = []; value.push(task.id); value.push(task.title); value.push(typeof folders[task.folder] === "undefined" ? "" : folders[task.folder]); value.push(task.tag); value.push(typeof contexts[task.context] === "undefined" ? "" : contexts[task.context]); var startdate = (task.starttime === 0 ? task.startdate : task.starttime); value.push(startdate === 0 ? "" : new Date((startdate - 60 * 60 * 9) * 1000)); // GMT UNIXタイムスタンプ->日付に変換 var duedate = (task.duetime === 0 ? task.duedate : task.duetime); value.push(duedate === 0 ? "" : new Date((duedate - 60 * 60 * 9) * 1000)); // GMT UNIXタイムスタンプ->日付に変換 value.push(new Date((task.completed - 60 * 60 * 9) * 1000)); // GMT UNIXタイムスタンプ->日付に変換 value.push(task.length); value.push(task.timer / 60); // 秒->分に変換 value.push(task.note); values.push(value); } // 最終行に追記 var sheet = SpreadsheetApp.getActiveSheet(); var nextRow = sheet.getLastRow() + 1; sheet.getRange(nextRow, 1, values.length, values[0].length).setValues(values); } } function getFolders() { var toodledoService = getToodledoService(); var response = UrlFetchApp.fetch('http://api.toodledo.com/3/folders/get.php?access_token=' + toodledoService.getAccessToken()); var folders = JSON.parse(response); var map = new Object(); for (var i = 0; i < folders.length; i++) { var folder = folders[i]; map[folder.id] = folder.name; } return map; } function getContexts() { var toodledoService = getToodledoService(); var response = UrlFetchApp.fetch('http://api.toodledo.com/3/contexts/get.php?access_token=' + toodledoService.getAccessToken()); var contexts = JSON.parse(response); var map = new Object(); for (var i = 0; i < contexts.length; i++) { var context = contexts[i]; map[context.id] = context.name; } return map; } // 前日のUNIXタイムスタンプを取得する(翌日深夜にトリガーを起動させることを考慮して) function getAfter() { var now = new Date(); var yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); return Math.round( yesterday.getTime() / 1000 ); }
設定
OAuthの認可
スプレッドシートを開くとToodledoというメニューが表示されるので、Toodledo->Authorizeから認可を行う(初回のみ)。
トリガーの設定
プログラムで前日分を取得するようにしているので、トリガーも合わせて設定します。
私の場合は、時間主導型で日次で深夜に1回実行させるようにしました。
参考
Google Apps ScriptでOAuth2を使う(GitHubのissueを出力) - dackdive's blog