2014年7月7日月曜日

[GAS] CalendarApp、Calendarのパフォーマンス比較


GoogleカレンダーもGoogle Apps Scriptで操作できますが、
その方法には2通りあります。
CalendarAppとCalendarですが、ここにもパフォーマンスの差があります。

下図の様なカレンダーで指定期間内の3つのイベントを取得する処理を考えます。

まずはCalendarAppから。

function func01() {
  var start = new Date(2014, 7 - 1, 7, 0, 0, 0);
  var end = new Date(2014, 7 - 1, 11, 23, 59, 59);
  var calendar = CalendarApp.getCalendarById
                   ("ooerh9mbu1v5htghbnio67hetk@group.calendar.google.com");
  var events = calendar.getEvents(start, end);
  for (var i = 0; i < events.length; i++) {
    Logger.log(events[i].getTitle());
  }
}
実行時間はこんな感じ。
1.8秒くらい。
3件のイベントを取得するごときの処理にしては
時間がかかり過ぎかな、と思います。

次にCalendar。
function func02() {
  var start = new Date(2014, 7 - 1, 7, 0, 0, 0);
  var end = new Date(2014, 7 - 1, 11, 23, 59, 59);
  var calendar = Calendar.Events.list
                   ("ooerh9mbu1v5htghbnio67hetk@group.calendar.google.com", 
                   {timaMin: start.toISOString(), timeMax: end.toISOString()});
  var events = calendar.items;
  for (var i = 0; i < events.length; i++) {
    Logger.log(events[i].summary);
  }
}
こちらの実行時間は、
0.34秒。5倍くらい速いですね。
カレンダーの数や、イベントの数が増えれば当然その差は開いていきます。

ただし、Calendarを使用するには設定が2つ必要です。
1つ目は[リソース] > [Googleの拡張サービス] からCalendarAPIをONにすること。
2つ目は上図のダイアログからGoogleデベロッパーコンソールを開き、
Calendar APIをONにすること。

この2つの設定で使用可能となります。

制限がもう1点。

1日に10万リクエストまでしか使えません。
そのため、それなりの規模の業務でバンバン使う、ということになると
難しいかもしれません。

逆にそこそこの規模であれば有効だと思います。



拍手する にほんブログ村 IT技術ブログ プログラム・プログラマーへ
にほんブログ村

プログラマー ブログランキングへ

2014年7月4日金曜日

[GAS] Google Apps ScriptでjQueryを使う


Google Apps ScriptでもjQueryを使った画面制御を行うことができます。

下図の様な簡単な画面を作ります。
AのテキストボックスとBのテキストボックスに数字を入力して
「足し算」ボタンをクリックすると結果を表示する画面です。
※Javascriptだけで完結できる内容ですが、あえてGoogle Apps Scriptとの通信を発生させます。
Googleドライブからスクリプトを作成します。
そして最初に画面表示用のHTMLを作成します。

[ファイル] > [新規作成] > [HTMLファイル]とクリックしていきます。

内容はこんな感じ。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
  // 画面初期表示
  $(function() {
    $('#btn').click(function(e) {
      // サーバ上のadd関数を呼び出し、成功した場合はonAddSuccess関数にコールバックする
      google.script.run.withSuccessHandler(onAddSuccess).add(this.parentNode);
    }); 
    function onAddSuccess(res) {
      $('#answer').text(res);
    }
  });
</script>
<form style="margin: 50px;">
  A<input type="text" name="a"/>
  B<input type="text" name="b"/>
  <input type="button" id="btn" value="足し算"/>
  <div id="answer"></div>
</form>

続いてGoogle Apps Scriptのコードは以下のとおり。
// 初期表示
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index"); 
}
// 足し算の結果を返却する
function add(param) {
  var a = parseInt(param.a);
  var b = parseInt(param.b);
  var c = a + b;
  return a + " + " + b + " = " + c;
}

[公開] > [ウェブアプリケーションとして導入]から画面を表示します。
そしてAとBに何か数字を入力します。


そして、「足し算」ボタンをクリックすると・・・

正しくGoogle Apps ScriptとjQueryの連携ができました。
拍手する にほんブログ村 IT技術ブログ プログラム・プログラマーへ
にほんブログ村

プログラマー ブログランキングへ

2014年7月1日火曜日

[GAS] DriveApp、DocsListのパフォーマンス比較


DriveAppとDocsListはどちらもGoogleドライブ上のファイルを扱う事ができます。
似たような事ができますが、パフォーマンスに結構違いがあるようです。

まずはDriveAppでファイルの参照を取得する処理を300回繰り返してみます。
function func01() {
  for (var i = 0; i < 300; i++) {
    DriveApp.getFileById("0B_GnH793KcV7cDZMaHgzdmlXeUE");
  }
}
単純にファイルIDからファイルを取得するのみの処理です。

結果は
40秒ちょっとです。

続いてDocsListでファイルの参照を取得する処理を300回繰り返してみます。
function func02() {
  for (var i = 0; i < 300; i++) {
    DocsList.getFileById("0B_GnH793KcV7cDZMaHgzdmlXeUE");
  }
}
こちらも実行してみます。

結果は
152秒・・・圧倒的にDriveAppの方が速いです。
ファイル参照の取得のみの比較ですので確実ではありませんし、
Googleのサーバの混み具合によって前後があるかもしれませんが、
基本的にDriveAppの方が速い印象があります。

DocsListはExperimentalマークが付いていますので、
要件的にどちらを使っても良い場合は
今のところDriveAppを使った方が良さそうです。

拍手する にほんブログ村 IT技術ブログ プログラム・プログラマーへ
にほんブログ村

プログラマー ブログランキングへ

2014年6月30日月曜日

[GAS] onEditで行の背景色変更


開発現場で作業をしていると
よく課題管理表というものを作成します。
Excelが使われることが多いですが、
完了した課題は行の背景色を変えて
完了したことをわかりやすくすることが多いです。

Excelを使っていれば「条件付き書式」を使って
「解決日に値が入ったら行の背景色をグレーにする」
などといった設定を簡単に行う事が出来ます。

しかし、残念ながらスプレッドシートにはその機能はありません。

代わりにGoogle Apps Scriptを書く必要があります。

↓の様なスプレッドシートがあったとします。
解決日に何か入力するとその行の背景色をグレーに設定する処理を作ります。

function onEdit(param) {
  var range = param.range;
  if (range.getColumn() != 6) {
    // 解決日の列以外の場合は処理を抜ける。
    return;
  }
  var sheet = SpreadsheetApp.getActiveSheet();
  if (range.getValue() == "") {
    // 解決日が空白になった場合は背景色を白にする。
    sheet.getRange(range.getRow(), 1, 1, 6).setBackground("#FFFFFF");
  } else {
    // 解決日が空白以外になった場合は背景色をつける。
    sheet.getRange(range.getRow(), 1, 1, 6).setBackground("#C0C0C0");
  }
}

試しに№3の解決日に日付を入れてみると

こんな感じで背景色が変わります。

ただし、スクリプトが走るため背景色が変わるまで若干のタイムラグがあります。

「ん?あれ?スクリプトがおかしくなったか?」

と思うくらい遅い場合もありますのでご注意を。
拍手する にほんブログ村 IT技術ブログ プログラム・プログラマーへ
にほんブログ村

プログラマー ブログランキングへ

2014年6月27日金曜日

[GAS] onEditでの注意点


今日もGoogle Apps Scriptのお話です。
onEditファンクションはスプレッドシートのセルが更新されるたびに呼ばれます。
とても便利ですが、注意すべき点があります。
例えば以下の様なスプレッドシートがあるとします。
B1列にはファイルIDが書かれています。
そのファイルIDの示すドキュメントはこちら。

このドキュメントのファイル名を取得してB2セルに書き込む処理を考えます。
普通に書くとこんな感じです。
function func01() {
  var range = SpreadsheetApp.getActiveSheet().getRange(1, 2);
  // セルに記載された文字をIDとしてドライブからファイルを読み込む。
  var file = DriveApp.getFileById(range.getValue());
  // ファイル名をB2セルへ書き込む。
  SpreadsheetApp.getActiveSheet().getRange(2, 2).setValue(file.getName());
}
結果は
こんな感じ。想定通りです。

続いて本題のonEditを使った処理です。
B1セルにファイルIDを記述した、というイベントを拾ってファイル名を取得、書き込みを実行します。
function onEdit(param) {
  // 編集されたセルの参照を取得する。
  var range = param.range;
  if (range.getRow() != 1 && range.getColumn() != 2) {
    // B1のセル以外が編集された場合は処理を抜ける。
    return;
  }
  // セルに記載された文字をIDとしてドライブからファイルを読み込む。
  var file = DriveApp.getFileById(range.getValue());
  // ファイル名をB2セルへ書き込む。
  SpreadsheetApp.getActiveSheet().getRange(2, 2).setValue(file.getName());
}
結果は
書き変わりません・・・
ロジック的には間違ってないはずですが・・・
実行ログを見てみると・・・
権限が無い、というエラーが発生しています。
これは私の憶測ですが、onEditでバンバン更新されて
バンバンGoogleドライブにアクセスされたらたまらん!
というGoogleの中の人の防御策ではないかと思っています。
私がGoogleの中の人だったら

「読めなくすりゃいんじゃね?onEditでドライブなんか読みに来んなよ。迷惑なんだよ。」

と思うと思いますので仕方ないですかね。
拍手する にほんブログ村 IT技術ブログ プログラム・プログラマーへ
にほんブログ村

プログラマー ブログランキングへ

2014年6月26日木曜日

[GAS] スプレッドシートの値参照、値設定のパフォーマンス


Google Apps Scriptを使ってGoogleスプレッドシートの内容を編集する、という場面がよくあります。
しかし、ここでExcelVBAの様に素直に書くと、ちょっと痛い目を見る事があります。

例えば↓のようにスプレッドシートのA列の値とB列の値を加算してC列に結果を入れる処理を考えます。
※100行あるとします。 あ、関数書きゃいいだろ、ってのはここでは無しね(・∀・)

普通に書くとこんな感じになると思います。
function func01() {
  var sheet = SpreadsheetApp.getActiveSheet();
  for (var row = 1; row <= 100; row++) {
    // C列に設定する値を計算(A列の値+B列の値)
    var answer = sheet.getRange(row, 1).getValue() 
               + sheet.getRange(row, 2).getValue();
    // 計算結果をC列に設定
    sheet.getRange(row, 3).setValue(answer);
  }
}
これで実行すると当然結果は正しいものとなりますが・・・
たった100行処理するのに18秒以上・・・(´・ω・`)Excelノホウガイイヨネ・・・

原因は
・sheet.getRange(row, 1).getValue()
・sheet.getRange(row, 2).getValue()
・sheet.getRange(row, 3).setValue(answer)
をそれぞれ100回呼び出してしまっていることです。
これらは全て実行されるたびにスプレッドシートとのやり取りが発生するため、
パフォーマンスに影響が出てしまいます。

問題点を改善したソースがこちら。

function func02() {
  var sheet = SpreadsheetApp.getActiveSheet();
  // 100行2列の値を一気に取得
  var values = sheet.getRange(1, 1, 100, 2).getValues();
  // 行配列を定義
  var rows = [];
  for (var row = 0; row < 100; row++) {
    // 列配列を定義
    var cols = [];
    // C列に設定する値を計算(A列の値+B列の値)
    var answer = values[row][0] + values[row][1];
    // 列配列に計算結果を設定
    cols.push(answer);
    // 行配列に列配列を設定
    rows.push(cols);
  }
  // 計算結果をC列に設定
  sheet.getRange(1, 3, 100, 1).setValues(rows);
}
そして実行結果のログがこちら。
圧倒的じゃないか、我が軍は・・・

同じ行を処理するのにたった0.3秒ちょい。
これはスプレッドシートの参照を全値取得、全値設定の2回のみにしているからです。

スプレッドシートのGoogle Apps Scriptでパフォーマンスに悩んでいる方は
まずスプレッドシートの参照回数を見直せないかどうかを検討してみてはいかがでしょうか。


拍手する にほんブログ村 IT技術ブログ プログラム・プログラマーへ
にほんブログ村

プログラマー ブログランキングへ