スプレッドシートをGoogle Action Script で操作する

Google Action Script でスプレッドシートを操作するときに知っておくと良さそうなことをまとめてみた。

初級編

  • シート名の先頭に data/ をつけておく
    • どのシートがGASで更新するか、スプレッドシートを見た時にわかるように
    • GASで更新するシートは、基本GASだけがデータを変更する様にして、手入力するシートは分けた方が良い
  • 余計なことはせず、自分自身のファイルを参照する
    • SpreadsheetApp.getActive() を使う
  • GASの実行時間は6分以内じゃないとダメ

セルに書き込む

const sheet = SpreadsheetApp.getActive().getSheetByName('data/hoge');
sheet.getRange(1, 1).setValue('Hello World!');

シート内のデータを消す

const sheet = SpreadsheetApp.getActive().getSheetByName('data/hoge');
sheet.getDataRange().clearContent();

シート内のデータをすべて読み出す

values という変数に2次元配列でデータを取得する

const sheet = SpreadsheetApp.getActive().getSheetByName('data/hoge');
var values = sheet.getDataRange().getValues();

定期実行する

GUIからトリガーを使い、実行したい関数を指定して設定します。 いつ実行されたかわかるように、データを取得するシートの一番上に new Date() の値を書き込みしておくと良い。

const sheet = SpreadsheetApp.getActive().getSheetByName('data/hoge');
sheet.getRange(1, 1, 1, 2).setValues([['更新日', new Date()]]);

中級編

ファイル整理

一つのファイルに色々書くと散らかるので、一つのシートを変更するロジックを一つのスクリプトファイルに書く。

パフォーマンス改善

GASの実行時間は6分以内じゃないとダメなので、外部のHTTPアクセス回数、Google API の呼び出し回数を減らす。 for ループでちまちま sheet.getRange(i, j).setValue(v) とかダメ。

function append(records) {
  const sheet = SpreadsheetApp.getActive().getSheetByName('data/hoge');
  var data = sheet.getDataRange().getValues();

  // 配列結合してまとめて書き込み
  var values = data.concat(records);
  sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
};

HTTPリクエスト

UrlFetchApp.fetch() を使う。

function post(url, payload) {
  const options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization':'Bearer hoge',
      'Accept': 'application/json'
    },
    muteHttpExceptions: true,
    'payload': JSON.stringify(payload)
  };

  return UrlFetchApp.fetch(url, options)
}

上級編

コード管理とデプロイ

Git でコード管理、claspを使いデプロイするのが良さそう。

シークレット情報の取り扱い

GAS 上で、PropertiesServiceクラスを使うと定数データを良い感じに使える。 config/secret.gs.gitignore しておき、 config/secret.gs.tmp でテンプレートをgit にpush しておけば何とかなる。

git から除外しても clasp push の対象になります。

function initialize() {
  var properties = PropertiesService.getScriptProperties();
  properties.setProperties({
    'token': 'hoge'
  });
};

テスト書く

GASはNode.js じゃ無いので、 requireexports できません。 邪魔にならないように module.exports します。

下記は、擬似コード

var Model = function() {};

Model.prototype.fetch = function(url, options) {
  UrlFetchApp.fetch(url, options);
};

if (typeof exports !== 'undefined') { module.exports = Model; }

テストはAVA 使おう。スタブするなら Sinonも。

import test  from 'ava';
import sinon from 'sinon';

var Model = require('../../src/model.gs');

test('Model#fetch', t => {
  var model = new Model();
  sinon.stub(model, 'fetch').returns('OK');

  t.is(model.fetch(), 'OK');
});

知らないとハマる仕様

日付型(シリアル値)

  • スプレッドシートのシリアル値は「1900/1/0」からの経過日数
  • UNIX時間は「1970/1/1」からの経過秒数
// スプレッドシート日付型のシリアル値への変換
function toSerialDate(timestamp) {
  const COEFFICIENT = 24 * 60 * 60 * 1000; //日数とミリ秒を変換する係数
  const DATES_OFFSET = 70 * 365 + 17 + 1 + 1; //「1900/1/0」~「1970/1/1」 (日数)
  const MILLIS_DIFFERENCE = 9 * 60 * 60 * 1000; //UTCとJSTの時差 (ミリ秒)

  return (timestamp + MILLIS_DIFFERENCE) / COEFFICIENT + DATES_OFFSET;
}

API等で、ISO 8601形式で日付が返却される場合は、 Date#parse() で日付型にキャストしてください。

toSerialDate(Date.parse("2020-01-23T01:14:39+00:00"))
// return 43853.42684027778

参考