【GAS】Togglの時間データを抽出するプログラムを作る

Python・GAS
ワタシ

ども、らーにゃです。今回はGASの活用についてまとめます。

身の回りのことをもっと自動化できないものか?

仕事でもプライベートでも忙しくなるほどに自動化の必要性・重要性は増していると思います。

例えば、時間管理については個人的にTogglを使って計測しているのですが

「あ、入れ忘れていた…」

ということが頻発しており、入力を簡易化して入力漏れがないようにできないかと考えることが多々あります。

そんな中、最近GAS(Google Apps Script)を勉強している中で

『これを活用することで入力作業が変わる!』

と思えるものが出来たので、備忘録的に下記に記載します。

自動化やGASのプログラミングに興味があるけど、どんなことなら出来るのか…と思っている初心者の方のご参考なれば幸いです。(‘ω’)ノ

今回の論点整理

今回取り扱うのはTogglの時間計測プログラムです。

具体的にはTogglというサービスで計測した結果をAPIを参照して集計するようにして、下記のようなアウトプットを出します。

ビジュアルがひどいので改良予定…

上記を実現するために、下記を記載していきます。

具体的には下記の3点について触れていきます。

今回の論点
  • Toggl×GASで出来ること
  • 自動計測のログラム
  • 自作プログラムへの拡張

順に触れていきます。

Google Apps Scriptの活用ポイント

Toggl × GAS で出来ること

Togglとは何か

Togglとは時間トラッキングサービスの一種です。

調べてみると時間トラッキングサービスは色々あります。

その中でも、Togglはシェアも高く誰でも感覚的につかいやすいというコメントが目立ちます。

■ご参考リンク(タイムトラッキングサービスの例)

結構、有名企業でも導入されているっぽいので、サービス品質の安心感があります!

Togglホームページより抜粋

で、これらのトラッキングサービスで何ができるのか?

と考えると、基本的な機能は大きく以下の2つだったりします。

  • その都度、時間を記録する
  • 記録した時間を振り返る

TogglはとにかくUIがシンプルで使いやすく、後述する連携プログラムを組むために必要な外部APIも公開できます。

今回はこのソフトを使って、自動計測をしていきます。

具体的な使い方や登録の流れは下記にまとめます。

Toggl APIの活用

さて、上記であげたTogglですが、何が良いかというとそのトラッキングのしやすさだけでなく、APIを公開して各種分析が出来る仕様にしているという点もあります。

まさにオープンイノベーションを意図するような立てつけになっており、APIを開放して自分のデータを客観的に見て分析・判断できるような形になっています。

出来ることは下記に記載がされています。

■ご参考リンク(Toggl APIに関して)

上記は英語で分かりにくいため、超ザックリというと、GASなどを使って大きくは以下の2点が出来ます。

  • 自動で時間計測を開始(入力)する
  • 集計された時間を自動で取り出して記載する

以前、前者についてはまとめました。

■ご参考リンク(GAS×Togglで音声入力で時間計測するプログラム作り)

そのため、今回は後者についてまとめていきます。

基本的にはコピペで実行できるものになっているはずです。

ワタシ

自分のデータを自分で分析するというのは面白いですね。

Toggl API参照のプログラム

さて、では実際にTogglのAPIをどう参照して、どうプログラムに活かすかを考えます。

基本的には下記のプログラムをコピペして頂ければ使えるはずです。

プログラムのコード

まずはGoogle Driveからスプレッドシートを作成し、下記のような形でタイトルと1行目を記入します。

尚、1行目に記載する項目は、計測したいTogglのプロジェクト名になります。

その後に「ツール」と「スクリプトエディタ」をクリックし、下記のコードをパッと張り付けて必要箇所のみ変更します。

var CACHE_KEY          = 'toggl_exporter:lastmodify_datetime';
var TIME_OFFSET        = 9 * 60 * 60; // JST
var TOGGL_API_HOSTNAME = 'https://api.track.toggl.com';
var TOGGL_BASIC_AUTH   = '★自身のTogglのAPIトークン:api_token';
var authData = Utilities.base64Encode(TOGGL_BASIC_AUTH);
var ss = SpreadsheetApp.openById("★自身のスプレッドシートの該当ID")
var sheet = ss.getSheetByName("データ")
var lastRow = sheet.getLastRow();
var lastCol = sheet.getLastColumn();
var p_names = sheet.getRange(1,2,1,lastCol-1).getValues();
var arr = []
var now = new Date();
var date = new Date(now.getFullYear(),now.getMonth(),now.getDate());
date.setDate(date.getDate()-1);
var date_gt = date.getTime();

function watch() {
  try {
    var time_entries = getTimeEntries(date_gt);

    for(var i = 0;i < time_entries.length;i++){
      var start_time = time_entries[i]["start"];
      var month1 = Number(start_time.substr(5,2))
      var time1 = Number(start_time.substr(11,2))
      var start_time_jpn = new Date(start_time.substr(0,4),month1-1,start_time.substr(8,2),time1+9,start_time.substr(14,2),start_time.substr(17,2))
      var start_time_gt = start_time_jpn.getTime();
      var start_time_d = new Date(start_time_jpn.getFullYear(),start_time_jpn.getMonth(),start_time_jpn.getDate())
      var start_time_d_gt = start_time_d.getTime();
      if(date_gt == start_time_d_gt){
        var pid = time_entries[i]["pid"]
        var p_name = getProjectName(pid,authData)
        var stop_time = time_entries[i]["stop"];
        var month2 = Number(stop_time.substr(5,2))
        var time2 = Number(stop_time.substr(11,2))
        var stop_time_jpn = new Date(stop_time.substr(0,4),month2-1,stop_time.substr(8,2),time2+9,stop_time.substr(14,2),stop_time.substr(17,2))
        var stop_time_gt = stop_time_jpn.getTime();
        //var minutes = Math.ceil((stop_time_gt - start_time_gt)/(1000*60))
        var minutes = (stop_time_gt - start_time_gt)/(1000*60)
        //var minutes = (stop_time_gt - start_time_gt)/(1000)
        var flg = false;
        if(arr.length !== 0){
          for(var j = 0;j < arr.length;j++){
            if(p_name == arr[j][0]){
              flg = true
              break;
            }
          }
          if(flg == true){
            arr[j].push(minutes)
          }else{
            arr.push([p_name,minutes])
          }
        }else{
          arr.push([p_name,minutes])
        }
      }
    }

    var arr2 = [];
    for(var i = 0;i < arr.length;i++){
      var min = 0;
      if(arr[i].length > 2){
        for(var j = 1;j < arr[i].length;j++){
          min = min + arr[i][j];
        }
        arr2.push([arr[i][0],min])
      }else{
        arr2.push([arr[i][0],arr[i][1]])
      }
    }

    var arr3 = [];
    arr3.push(date)
   var flg = false
   var data = ""
    for(var k = 0;k < p_names[0].length;k++){
    for(var j = 0;j < arr2.length;j++){
    if(arr2[j][0] == p_names[0][k]){
    flg = true
    data = arr2[j][1]
    break;
  }else{
  flg = false
  }
  }
    if(flg == true){
    arr3.push(data)
    }else{
    arr3.push("")
  }
  }
    sheet.getRange(lastRow+1,1,1,arr3.length).setValues([arr3])
  }
  catch (e) {
    Logger.log(e);
  }
}

function getTimeEntries(unix_timestamp) {
  var uri = TOGGL_API_HOSTNAME + '/api/v8/time_entries' + '?' + 'start=' + unix_timestamp;
  var response = UrlFetchApp.fetch(
    uri,
    {
      'method' : 'GET',
      'headers' : { "Authorization" : " Basic " + authData},
      'muteHttpExceptions': true
    }
  );
  try {
    return JSON.parse(response);
  }
  catch (e) {
    Logger.log([unix_timestamp, e]);
  }
}

function getWorkspaceId(authData){
  var currentUrl = "https://www.toggl.com/api/v8/workspaces";
  var options = {
    'headers' : {"Authorization" : "Basic " + authData}
  }
  var response = UrlFetchApp.fetch(currentUrl, options);
  var json = JSON.parse(response.getContentText());
  return json[0].id;
}

function getProjectName(pid,authData){
  var workspace_id = getWorkspaceId(authData)
  var currentUrl = "https://www.toggl.com/api/v8/workspaces/" + workspace_id + "/projects";
  var options = {
    'headers' : {"Authorization" : "Basic " + authData}
  }
  var response = UrlFetchApp.fetch(currentUrl, options);
  var json = JSON.parse(response.getContentText());
  for(var i = 0;i < json.length;i++){
    if(pid == json[i].id){
      var p_name = json[i].name
    }
  }
  return p_name;
}

上記をコピペしていただいた上で変更する点は下記の2点のみです。

修正する箇所

「★自身のTogglのAPIトークン」の部分を変更

「★自身のスプレッドシートの該当ID」の部分を変更

ちなみに、前者のTogglのAPIトークンはTogglにログイン頂き、下記の流れで確認ができます。

Toggl APIトークンの参照の仕方
  1. Togglにログイン
  2. 左下のプロフィールからprofile settingをクリック
  3. 下にスクロールしてAPI Tokenをクリック

また、後者のスプレッドシートのIDは下記のように確認します。

スプレッドシートのIDの参照の仕方
  1. Googleスプレッドシートを開く
  2. URLを確認して該当各所を確認する
    ( “dos.google.com/spreadsheets/d/~~~/edit#gid=0″ とあるアドレスの~~~の箇所が該当IDになります)

それぞれをコードにコピペして貼り付ければそれでOKです。

ワタシ

予備知識なくともコピペでいいので簡単にできちゃいます。

プログラムの実行

あとはこれを実際に動くか試して、良い感じであれば繰り返しをするのみです。

具体的には下記の保存マークを押した後「▷実行」マークをクリックしてみてください。

これでエラーがでなければ、自動送信がなされます

途中Googleのアカウントを確認する画面に推移しますが、ここは気にせず進めていきましょう。

「権限を確認」をクリックすると「このアプリは確認されていません」と警告が出てきてビビりますが問題ないです。

冷静に、左下の詳細をクリックして○○に移動をクリックしましょう。

この画面ではじめてプログラム利用の承認ができるので、許可をしておきましょう。

ここまで出来たらほぼ完了です。

これを修正すると下記のような形でスプレッドシートに記載がなされます。

これが確認出来れば成功です。

他の項目は下記のように設定ができるようです。

あとはこれを毎日、起動して、スプレッドシートの下に追加するプログラムにします。

今度は左側にある時計ボタンをクリックし、毎日更新される設定にうつります。

詳細は以下の通りです。

トリガーを作成というボタンを押した後

『時間主導型』、『日付ベースのタイマー』、『好きな時間帯』を選択します。

ここまで設定して完成です。これで毎日、プログラムが指定時間に実行ボタンを押してくれる設定になりました。

コードの記載を含めコピペすれば3分くらいで終わる作業かと思います。

あとは、指定のスプレッドシートを参照してBIツールでビジュアル化すればダッシュボードの完成です。

BIツール(Google Data Portal)は下記でまとめています。

ツマ

定例的な作業などは全部こういうプログラムに置き換えて見える化したいね

まとめ

今回ポイントしてまとめてきたのは以下の2点です。

抑えたいポイント
  • Toggl×GASで出来ること
    ▷普段の身の回りの時間計測の入力と集計の作業を簡易化・自動化できる
  • 自動計測のログラム
    ▷Toggl APIを参照してトラッキングした計測時間を収集できる

今回は一例を出しましたが、GASを扱うことが出来ると、効率化・自動化をさせることは無限大です。

そしてこのGASの何と言っても素晴らしいのが環境不要!そしてしかもタダ!ということです。

仕事でつかえる部分があるのは勿論のこと、プライベートでも利活用できるオトク技がいっぱいなため、非エンジニアの人でもぜひトライしてみてください!

とにかく私のような初心者の人が手軽に試して面白い!と感じて飽きる前に習慣化できるように、今まで作ったコードは公開していくのでコピペして利用・参考にして下さい。

自身の今のスキルや状況に応じて、次のステップアップに向けた動きを付けて一緒に自動化・効率化の技術を上げていきましょう!(‘ω’)ノ

>>Udemy:GASのおすすめ講座

ご精読頂きありがとうございました!
m(_ _)m