2018年3月31日土曜日

Google Apps Scriptで設定ファイルの保存・読込

内容
スクリプトの設定情報やスクリプトの実行状況などを保存する方法としてClass PropertiesServiceが提供されている。しかし、これには少し欠点があるので別の方法を考えてみたのでそれを紹介する。紹介といっても自分用の備忘程度なので細かい説明は書きません。

Class PropertiesServiceについて
データの共有範囲毎にメソッドが用意されている。非常に簡単にデータを保存できるので何かと便利です。後に述べる問題点に該当しなければジャンジャン使いましょう。

メソッド 共有範囲/編集閲覧
getDocumentProperties() すべてのユーザーで共有されるドキュメント(document,spreadsheet,form)のプロパティオブジェクトを取得する。
[T.B.D.]閲覧編集方法不明(調査未実施)
getScriptProperties() すべてのユーザーで共有されるスクリプトのプロパティオブジェクトを取得する。
スクリプトエディタの 「ファイル」>「プロジェクトのプロパティ」>「スクリプトプロパティ」 で閲覧編集可能。
getUserProperties() ユーザー毎でスクリプト毎のプロパティオブジェクトを取得する。
スクリプトエディタの 「ファイル」>「プロジェクトのプロパティ」>「ユーザープロパティ」 で閲覧編集可能。

詳細はClass PropertiesServiceを参照ください。

プロパティの保存方法
var properties = PropertiesService.getUserProperties();
  properties.setProperty("Target File ID","205Ps_mAhEFs.......Ugm1m");

プロパティの参照方法
var properties = PropertiesService.getUserProperties();
  var TargetFileId = properties.getProperty("Target File ID");

問題点
  • 複数のスクリプトで共有することができない。
  • 保存できるサイズに制限があり、実験的に9217文字以上を指定すると関数が失敗する事がわかっている。
    • 9216文字までと確定している場合には問題にならないが、不定長の場合には注意が必要。

解決策
ファイルにプロパティを保存できるようにします。用意している関数は以下の4つ。
  • getProperty(key)
  • setProperty(key,value)
  • deleteProperties()
  • deletePropertu(key)
使い方の例は以下の通り。
function myFunction() {
  // スクリプトを起動した回数をプロパティから取得
  var count = getProperty("count");
  if( count != null )
  {
    count++;
  }
  else
  {
    count=0;
  }
  // プロパティに値を保存
  setProperty("count",count);
  setProperty("hoge","123");
  setProperty("hogehoge","456");
  if( count > 1 )
  {
    // 指定したプロパティを削除
    deleteProperty("hogehoge");
  }
  if( count > 4 )
  {
    // すべてのプロパティを削除
    deleteProperties();
  }
}

上記を実行するとマイドライブの下に、[スクリプト名]_[スクリプト実行ユーザーメールアドレス]という名前のファイルが生成され、そこにプロパティが保存されている。 いかが一回スクリプトを実行したときに生成されるファイルの内容。
{"count":0,"hoge":"123","hogehoge":"456"}
実行するたびにスクリプトの内容が変化するので内容を確認しながら実行すると動作がわかると思う。

最後にコード
// 使い方
//  var properties = getProperty("count");
//  setProperty("count",12345);
//  deleteProperties();
//  deleteProperty("count");

// プロパティのファイル
var PropertiesFile=null;

// プロパティを取得する。
function getProperty(key)
{
  try{
    return JSON.parse(getPropertiesFile().getBlob().getDataAsString())[key];
  }
  catch(e)
  {
    return null;
  }
}

// プロパティをセットする。
function setProperty(key,obj)
{
  try{
    // エラーに備えてからのオブジェクトを生成
    var property = {};
    // ファイルからテキスト読み込み
    var text =  getPropertiesFile().getBlob().getDataAsString();
    // オブジェクト生成
    property =  JSON.parse(text);
  }
  finally
  {
    // 指定されたプロパティを追加
    property[key] = obj;
    // 最終的にオブジェクトをファイルに保存
    getPropertiesFile().setContent(JSON.stringify(property));
  }
}

// すべてのプロパティを削除する。
function deleteProperties()
{
  // PropertiesServiceに保存してあるファイルIDを取得
  var key = "PropertiesFileID";
  var id = PropertiesService.getUserProperties().getProperty(key);
  // PropertiesServiceを削除
  PropertiesService.getUserProperties().deleteProperty(key);

  try{
    if( PropertiesFile==null )
    {
      if( id != null ){
        DriveApp.getFileById(id).setTrashed(true);
      }
    }
    else
    {
      PropertiesFile.setTrashed(true);
    }
  }
  finally{}
}

// プロパティを削除する。
function deleteProperty(key)
{
  // エラーに備えてからのオブジェクト生成
  var property = {};
  try{
    // ファイルにアクセス
    var text =  getPropertiesFile().getBlob().getDataAsString();
    // ファイルの内容からオブジェクト生成
    property =  JSON.parse(text);
    // 指定されたプロパティ削除
    delete property[key];
  }
  finally
  {
    // 最終的にオブジェクトをファイルに保存
    getPropertiesFile().setContent(JSON.stringify(property));
  }  
}


// プロパティファイルを取得する。
function getPropertiesFile()
{
  if( PropertiesFile!=null )
  {
    return PropertiesFile;
  }
  else
  {
    var userProperties =  PropertiesService.getUserProperties();
    var id = userProperties.getProperty("PropertiesFileID");
    try{
      if( id!=null )
      {
        PropertiesFile = DriveApp.getFileById(id);
        if( PropertiesFile.isTrashed() )
        {
          PropertiesFile=null;;
        }
      }
    }
    catch(e)
    {
      PropertiesFile=null;
    }
    if( PropertiesFile==null )
    {
      // ロパティを保存するファイル名を決める
      // 必要に応じて変更する。
      // 複数のスクリプトで共有する場合はここを同じ名前にする。
      var name = DriveApp.getFolderById(ScriptApp.getScriptId()).getName()
                   + "_" + Session.getActiveUser().getEmail();
      PropertiesFile =  DriveApp.createFile(name,"{}",MimeType.PLAIN_TEXT);
      userProperties.setProperty("PropertiesFileID",PropertiesFile.getId());
    }
    return PropertiesFile;                             
  }
}


コードの説明は省略。。。
気が向いたら解説を追記します。

0 件のコメント:

コメントを投稿

質問、要望、指摘など書いていただいてもよいですが、対応できるとは限りませんのでご了承ください。私に対するものも含め他の人を嫌な気分になるようなコメントは避けてください。