Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

GAS(Google Apps Script)の操作方法とタスク管理アプリの作成を学んでみた

More than 1 year has passed since last update.

GASとは

Google Apps Script(GAS)とは、Googleが提供するサーバーサイド・スクリプト環境で、Googleのあらゆるサービスを統合処理できます。
公式サイトには、たくさんのアイコンが載っており、これらの機能はすべて統合処理できるってことですね。
Googleフォームに記入した内容をGooglespreadSheetやGoogleカレンダーに反映させたり、gmailに送信したりできます。
また、他のアプリのAPIを使って連携させることもできます。
僕の職場では、Chatworkとの連携が流行っています。すぎょい。

こちらのサイトには、GASでできることがいくつか書いてあります。

そんなGASはテキストエディタや環境が必要ないので、プログラミングをやったことない人でも簡単にできます。
学習もそこまで苦ではありません。

ただ、実行に制限がある、外部向けの本格的な業務サービスには向かない、といった面もあります。
メールの送信は1日100通までで、スクリプトの実行時間は1回6分以内です。
ちょっとした社内の業務改善には使えます。
最終的には、システムでやるのが1番良いですね。

GASを使ってみる

それでは、GASを使ってみましょう。
初学者御用達のドットインストールで学習していきます。安定の分かりやすさ。
こちらの動画を参考に、シートとスクリプトを用意しましょう。

ログ

GASは言語的には、JavaScriptで書かれます。
しかし、GASのサーバー上で動くので、ブラウザの部品は動かせません。
例えば、ブラウザにアラートを出すalert()は実行できません。
このように、言語的にはJSですが、使えない命令がいくつかあります。

ただ、代用することはできます。alert()の代わりには、Loggerを使えます。

function myFunction() {
  Logger.log('ログを出すぞぉぉぉ');
}

関数myFunctionを実行し、ログを確認します。
Ctrl+Enter、もしくは「表示→ログ」でログを確認できます。

GASの勉強_ドットインストール_ログ.png

プログラムを組んでいて値などを確かめたい場合によく使います。

自分で関数を作ってみる

スプレッドシートには、元々たくさんの関数が用意されています。
「挿入」→「関数」から、SUMやAVERAGEを使えますね。
この関数ですが、自分で作ることもできます。

こんな関数を作ってみます。

function getResult(input) {
  if (input >= 80){
    return 'PASS';
  } else {
    return 'FAIL';
  }
}

シートに記入された数字が80以上ならPASSを表示させ、80未満ならFAILを出します。
ただ、数字が多くなると、少し面倒になります。
このままだと、1個のセルごとに関数を反映させなければいけません。(セルを選択して右下のとこをピーッと引っ張ればいいのですが。)
そこで、map関数を使えば、楽になります。

function getResult(input) {
  if(input.map){
    return input.map(getResult);
  } else {
    return input >= 80 ? 'PASS' : 'FAIL';
  }
}

こんな感じで「A1:A5」と、まとめてセルを選択できます。

GASの勉強_ドットインストール_map関数.png

データの数が多い時は、map関数を使えば楽ですね。

スクリプトからデータを追加する

関数だけでなく、データも追加できます。
例えば、以下のコード。

function initSheet() {
  var sheet = SpreadsheetApp.getActiveSheet(); //スプレッドシートへのアクセス
  var names = ['牛肉', '豚肉', '鶏肉',''];
  var i;

  sheet.clear(); //シートをクリアにする

  for (i = 1; i <= 100; i++) {
    sheet.getRange(i, 1).setValue(names[Math.floor(Math.random() * names.length)]);
    sheet.getRange(i, 2).setValue(Math.floor(Math.random() * 101));
  }
  sheet.getRange(40, 3).setValue('肉祭り').setBackground('skyblue');
}

for文のループ処理のところを整理すると、、、
getRange(i,1)でセルを取得する。(i行目1列目)
setValue()でセルに値をセットする。
Math.floor()で数字の小数点以下を無くして整数にする。
Math.random()で0~1の乱数を作る。
names.length()で文字列namesの長さを取得する。
setBackgroundで色を指定できます。
となります。
ちなみに、Math.floorは、Mathオブジェクトのfloor関数(メソッド)ですね。

GASの勉強_ドットインストール_肉祭り.png

こんな感じで自動で値を割り振ってくれます。

コードを高速化

先程のコードですが、getRange()setValue()の処理をセルの個数分やってしまっています。
わざわざセルを読み取るのは重い処理だそうです。
そのため、先程のループ処理でi <= 1000として値を1000個にすると、処理に2秒くらいかかります。
そこで、以下のようにコードを書き換えます。

function initSheet() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var names = ['牛肉', '豚肉', '鶏肉',''];
  var i;
  var startTime = new Date();  //処理の開始時間
  var scores = [];  //多次元配列を用意

  sheet.clear();

  for (i = 1; i <= 1000; i++) {
    scores.push([
      names[Math.floor(Math.random() * names.length)],
      Math.floor(Math.random() * 101)
    ]);
  }

  sheet.getRange(1, 1, 1000, 2).setValues(scores);

  Logger.log(new Date() - startTime);
}

for文のループのところで先にデータを作り、配列の要素に入れ込む処理をしています。
最後に、1行目1列目から1000行×2列の値を取得し、配列scoreに入れます。
また、処理を終了した時間から開始した時間を引いて、処理時間を計算しています。

すると、処理時間が0.042秒に縮まりました!
処理速度はログ(Ctrl+Enter)で確認できます。(単位はミリ秒)
このように、書き込む処理を1度にすることで、処理がかなり高速化します。

判定処理を実装してみる

先程のコードに判定処理を実装してみます。(データの数は面倒なので、先ほどのコードの1000個のデータは10個にしておきます。)

function showResults() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var scores = [];
  var i;
  var results = [];

  scores = sheet.getRange(1, 2, sheet.getLastRow(), 1).getValues();

  for (i = 0; i < scores.length; i++) {
    results.push([scores[i] >= 80 ? 'PASS' : 'FAIL']);
  }
  sheet.getRange(1, 3, Results.length,1).setValues(results);
}

配列を渡すので、resultsの部分は[]でくくります。
最後の行まではgetLastrow()で表します。
ここでは最後の行は10行目までなので、1行2列目から10行×1列の値を取得し、配列scoresにセットしています。

こうして、判定処理を実装できましたが、毎回スクリプトから実行するのは面倒です。
そのため、シート中のボタンから判定できるようにします。
ボタンにスクリプトを割り当てるのですが、詳しいやり方はドットインストールの講座を見てください。
ボタン作って、右クリックで「:」を選択して「スクリプトを割り当て」で割り当てるだけです!

GASの勉強_ドットインストール_シート初期化確認.png

また、初期化する際に確認するボタンを表示するには、関数initSheet()に以下を追加します。

  if (Browser.msgBox('シートの初期化', '実行していいですか?', Browser.Buttons.OK_CANCEL) === 'cancel') {
    return;
  }

こうして、シートに判定処理機能を追加できました。

タスク管理アプリを作る

完成例はこちら

この講座の最終目標である、タスク管理アプリを作っていきます。

順序は下記の通り。

①シート・フォーム・スクリプトを用意する
まず、スプレッドシートの「ツール」⇒「フォームを作成」から、タスクを書いてもらうフォームを作ります。
その後、「ツール」⇒「スクリプトエディタ」から、スクリプトを開きます。

②Webページで追加したタスクを公開する
「公開」「→Webアプリケーションとして導入」から公開の設定をします。

③HTMLファイルを表示
公開したWebページに、HTMLを反映させるには以下のようにします。

function doGet() {
  return HtmlService.createHtmlOutput('<h1>タスク</h1>');
}

ただ、直接コードを記入するのはめんどくさいので、HTMLファイルをリンクさせます。

function doGet() {
  var template = HtmlService.createTemplateFromFile('index');

  template.title = 'タスク管理アプリ';
  template.tasks = getTasks();

  return template.evaluate();
}

⑤フォームへのリンクを作る
GASで公開されるWebページはセキュリティ上の問題から「iframe(インラインフレーム)」内で展開されています。

*iframeとは
HTMLタグのひとつで、文書内にフレーム(枠)を作り、その中に別のテキストや画像などのコンテンツを配置することができます。
これを利用した、「クリックジャッキング」というWeb攻撃が脅威となっています。
あるページ上に、iframeで作った害悪ページ(CSSで透明にしている)を重ね、ボタンなどをクリックさせるのです。

*クリックジャッキング
Webブラウザを悪用して、ユーザーに不利益をもたらすセキュリティ上の攻撃手法の一つです。
ボタンやリンクなどを透明で見えない状態にして、通常のWebページの上にかぶせてしまいます。そして、クリックさせることで、コンピュータの利用者や企業、官公庁などに危害を加えます。

*参考文献
クリックジャッキングとは

そこで、以下のコードで制限をかけています。

<iframe sandbox=""></iframe>

Qiita_iframe.png

詳しくは👇より。
インラインフレーム内のコンテンツに制限をかける
こうして制限をかけることで、クリックジャッキングを防いでいるのです。

このような制限がかかっているので、iframeの外のフォームに跳ぶには、HTMLファイルで以下のように書く必要があります。

<p><a href="https://docs.google.com/forms/..." target="_blank">タスク追加用フォームへ</a></p>

⑥メール送信処理を実装〜完成
後は、メール送信処理を実装して、バージョンを上げれば完成です。
バージョンを上げないと、公開用のページには反映されません。(開発用のページには常に反映されますが。)
トリガーあたりをいじれば、毎朝タスク内容をメールで送るようにもできます。

完成コード(例)

cord.js
function doGet() {
  var template = HtmlService.createTemplateFromFile('index');

  template.title = 'タスク管理アプリ';
  template.tasks = getTasks();

  return template.evaluate();
}

function getTasks(){
  var sheet = SpreadsheetApp.getActiveSheet();
  return sheet.getRange(2, 2, sheet.getLastRow() - 1, 1).getValues();
}

function sendReport(){
  var to = 'kokubu.miraidenshi@gmail.com';
  var subject = 'タスク一覧';
  var url = 'https://script.google.com/macros/s/AKfycbw08TyaVISA4rDOLC1ecOxYUp2peYS26KAaunjJb27gNnP2o0I/exec';
  var body = getTasks().join('\n') + '\n\n' +url;

  MailApp.sendEmail(to, subject, body);
}
index.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1><?= title ?></h1>
    <p><a href="https://docs.google.com/forms/...">
    タスク追加用フォームへ</a></p>
    <ul>
      <? for(var i = 0; i < tasks.length; i++){ ?>
        <li><?= tasks[i] ?></li>
      <? } ?>
    </ul>
  </body>
</html>
yinawekuky
Python(Django) ,JS(React, Vue, D3, Gatsby)でのWebアプリ開発をやっています。 GAS, SeleniumでのRPA構築も時々やります。 座右の銘:「Happy, Kind, Crazy」
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away