Google Apps Script 事始め Webアプリ編

2016年12月08日
区分
advent-calender
報告者:
kadota

この記事は アクトインディ Advent Calendar 2016 8日目になります。 どうぞよろしくお願いします。

業務でかじっていたGoogle Apps Script (GAS) について軽くまとめました。意外と色々なことができるんですね。
今まで知らなかったWebアプリ化の部分に焦点をあててご紹介します。

概要

G Suite (旧名 Google Apps)を拡張するスクリプトです。Google Sheets, Docs, Forms等のサービスのカスタマイズや自動化、アドオン作成に利用されます。
https://developers.google.com/apps-script/

スクリプトエディタ

Google Apps Script (GAS)用のオンラインエディタです。
https://script.google.com/

Googleドライブからも作成できます。新規>その他>アプリを追加、から検索してGoogle Apps Scriptを追加。
スクリプトエディタはコードを書く環境であり、「プロジェクト」としてファイルをまとめたり各種オプションを設定する場所でもあります。
スクリプトは.gs拡張子でプロジェクトの中で管理されます。

スクリプトの種類

スタンドアローン

Googleドライブに置かれる独立したスクリプトです。後述するバインドスクリプトで無いものです。
https://developers.google.com/apps-script/guides/standalone

以下、最低限動くサンプルです。

コード.gs

function myFunction() {
  Logger.log('test');
}

Google Appsバインド

Sheets, Docs, Forms等のファイルにバインドされたスクリプトです。
https://developers.google.com/apps-script/guides/bound

データファイルのメニューから、ツール>スクリプトエディタを選ぶと出てきます。
getActiveSpreadsheet(), getActiveDocument(), getActiveForm()といった独自の関数が使えてファイルIDをいちいち調べる必要がない、シートにカスタム関数を定義できるなどの特徴があります。

またシンプルトリガーという単純なイベント(onOpen(), onEdit()など)を利用できます。
https://developers.google.com/apps-script/guides/triggers/

コード.gs (Google Sheetsに紐付いている場合)

function getCells() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange().getValues();
  
  for (var i = 0; i < data.length; i++) {
    Logger.log('A列: ' + data[i][0]);
    Logger.log('B列: ' + data[i][1]);
  }
}

Webアプリ

スタンドアローンスクリプトも、バインドスクリプトも、Webアプリとして公開できます。
https://developers.google.com/apps-script/guides/web

公開するにあたって以下の条件があります。

  • doGet()doPost() 関数が定義されていること。
  • その関数が、 HTML service HtmlOutput オブジェクト または Content service TextOutput オブジェクト を返すこと。

Webアプリ公開時に、バージョン番号、スクリプトの実行時ユーザー(スクリプト作成者 or スクリプト実行者)、スクリプトを実行可能なユーザーを指定できます。
スクリプトエディタで「ウェブ アプリケーションとして導入…」から指定します。

コード.gs

function doGet(e) {
  var params = JSON.stringify(e); //入力を受けとり、
  return HtmlService.createHtmlOutput(params); //JSONで出力
}

GASのユーザーインターフェイス

バインドされたスクリプトにはカスタムメニュー、ダイアログ、サイドバーといったUI要素が使用可能です。 スタンドアローンスクリプトでも、画面をHTMLで作り込むことができます。
※どちらのスクリプトでも、用途によっては画面を作らずにスクリプトエディタを実行時インターフェイスとする、またはトリガーを設定する、でも十分な場合も。

UI用にHTMLを扱う場合は、HTML Serviceを利用します。※以前はUI生成用にUi Serviceというものもありましたが現在は廃止されています。
https://developers.google.com/apps-script/reference/html/

実行時モード

以前は、実行時のモード(サンドボックスモード)にNATIVE, EMULATEがありましたが、現在はIFRAMEモード一択です(速度改善、外部JS利用などの柔軟性)。

setSandboxMode(HtmlService.SandboxMode.IFRAME);

IFRAMEの条件:

  • リンクのtarget属性は _top_blank
  • 外部リソースの読み込みは http ではなく https で行う
  • IE9など古いブラウザは対象外。※HTML5のsandbox属性を利用するため

内部リソースの読み込み

スクリプトエディタのプロジェクトにHTMLファイルのみ追加可能です。
テンプレートなどで利用します。

スクリプトでテンプレート用HTMLを読み込む例

  t = HtmlService.createTemplateFromFile('index');

ではJSやCSSはどうしたらいいでしょうか?
この場合はそれぞれのJSやCSSをHTMLのパーツとして用意して、テンプレートにScriptletsというGAS用のテンプレート言語で読み込む形になります(ちょっと強引)。
HTMLの一部なので、中にはstyleタグやscriptタグを入れておく必要があります。

HTMLは スクリプトエディタの ファイル>新規作成>HTML ファイル から登録します。

読み込み用関数の定義

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename)
      .getContent();
}

HTMLにパーツを読み込む

<?!= include('index.css'); ?>
<?!= include('index.js'); ?>

外部リソースの読み込み

テンプレートHTMLにふつうにタグを記述するだけです。要HTTPS。

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

Webアプリのテンプレまとめ

以上をまとめてみるとこのようになります。

コード.gs

function doGet(request) {
  t = HtmlService.createTemplateFromFile('index'); //index.htmlをテンプレートに使う
  t.title = 'タイトルを設定します';
  Logger.log( t.getCode() ); //テンプレートの実際の出力コードを確認できる
  return t.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}

function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename)
      .getContent();
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <title><?= title ?></title>
    <?!= include('index.css'); ?>
  </head>
  <body>
  <h1><?= title ?></h1>
  
  <p id="content">Content ... </p>  
  
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  <?!= include('index.js'); ?>
  </body>
</html>

index.css.html

<style>
body {
  background-color: #f0f0f0;
}
</style>

index.js.html

<script>
$(document).ready(function () {
  $('#content').text( 'Content ... Loaded.' );
});
</script>

文書をデータソースにしてWebアプリを作る

上記の雛形を、文書(例ではスプレッドシート)のバインドスクリプトとして適用する場合はこんなかんじになります。

  1. メニューから、ツール>スクリプトエディタを選ぶ
  2. プロジェクトにHTMLを登録する
  3. コード.gs を下記の例を参考に修正する
  4. テンプレートのどこか (index.js.html等) で data を受け取って処理する
  5. スクリプトエディタで「ウェブ アプリケーションとして導入…」を指定する

コード.gs

function doGet(e) {
  t = HtmlService.createTemplateFromFile('index');
  t.title = 'スプレッドシートをWebアプリ化';
  t.data = JSON.stringify( getCells() );
  return t.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
}

function getCells() {
  //var sheet = SpreadsheetApp.getActiveSheet();
  var id = 'XXXXXXXXXXXXXXXXXXXX'; //Webアプリ化するとIDを自動で取れなかったので調べて記述しておきます
  var sheet = SpreadsheetApp.openById(id);
  var data = sheet.getDataRange().getValues();
  
  return getJSON(data);  
}

//このへん適当です…
function getJSON(data) {
  var json = [];
  
  for (var i = 0; i < data.length; i++) {
    json.push({"a": data[i][0], "b": data[i][1]});
  }
  
  return json;
}

データを受け取る例

  var foo = { data: JSON.parse(<?= data ?>) };
  // このあと受け取ったデータを処理する

後半ざっくりですが、以上です。

Windows10 Anniversary Updateでの失敗記録

2016年10月27日
区分
ヘルプデスク
報告者:
kadota

はじめまして、kadota です。
5月半ばに入社した新参です。社内ヘルプデスクしつつRailsを勉強してます。

ハロウィンの季節ですね。
弊社ブログのデザインも(元から)なんとなくハロウィンっぽい気がしないでもない気がします。

先日、Windows10の生誕祝の更新がありましたが、扱いを間違えて呪いを掛けられてしまったPCがありましたので、反省の記録としてご報告です。

自動更新のたびに失敗するPC

  • 患者:リモートワーカーのちょっと古いノートPC。ASUS U24E Win7モデルで、Win10へ移行済。
  • 症状:アニバーサリー・アップデートの自動更新がかかるたびに失敗して、再起動後に初期状態に戻すはめになっていた。

PCを受け取って、まず Windows Update のコンポーネントを自動的にリセットする を実行。
Windows10 更新アシスタントをDLし、そそくさと、そのまま再度アニバーサリー・アップデートを適用。…これが失敗でした。

いつもより更新が進んで100%までいってから再起動したものの、結局エラーで沈黙。仕方なく強制再起動へ。
今まではWindows回復環境(Windows RE)から初期状態へ戻せたようですが、今回はWindows回復環境は出るも、初期状態に戻すのさえ失敗する状態に。
Windows10なので自動で復元ポイントが刻まれていればよかったのですが、このPCには復元ポイントも無いことがわかり、焦りました…

事前にやっておけば良かったこと

振り返ってみると、アニバーサリー・アップデートを再適用する前に、以下のポイントをおさえるべきでした。

  • 失敗時に備えて、復元ポイントを確認。または修復用イメージを作っておく
  • システム ファイル チェッカーを実行 (SFC /scannow)
  • DISM コマンドを実行 (DISM /Online /Cleanup-image /Restorehealth) ※Windowsイメージの修復 →いきなり修復じゃなく、まず/ScanHealthがいいかも
  • アップデート前にセキュリティソフトを一時無効化、またはクリーンブートで余計な常駐ソフト無しで行ってみる

ちなみに今回は困ったあと、Windows回復環境の「詳細オプション」でコマンドプロンプトを開いて作業してみたのですが、

  • SFC /scannow は、検出後のファイル修復がセーフモードではないので適用できず、セーフモードでの起動もエラーで失敗、で詰まる。
  • chkdsk c: /f は、異常なし
  • 障害になりそうなドライバーを外して起動してみる(DISM /Image:C:\ /Get-Driversで調べて、DISM /Image:C:\ /Remove-Driver /Driver:oem**.infで外す)→改善せず、空振り

という結果でした。

SFC コマンドでシステムファイル修復が出来ていれば、更新もうまくいったかもしれません。ただコマンドプロンプトに入れるのであれば、USB経由等でユーザーデータを救出することはできるようです。

クリーンインストール

結局このPCは、ユーザーデータは破棄でもOKで、OSアップグレード履歴があったので、 Windows10のISO でクリーンインストールしてしまいました。プロダクトキーが空のままでも、問題なくライセンス認証は通りました。

この失敗がどなたかのお役に立てれば幸いです。
以上です。

技師部隊からの
お知らせ

【求人】エンジニア募集しています。

本頁の来客数
八十七万千百七十六名以上(計測停止中)

メンバー一覧

アクトインディ技師部隊員名簿

アクトインディ技師部元隊員

アクトインディへ

カテゴリー

アクトインディ

aaaa