概要
Google Apps Script (GAS)を使って、自分で使うための、作業開始・終了時刻を記録するWebアプリを作成した。

この記事にあるもの
- GASによるWebアプリ作成の流れ
- 作ったアプリのコード
ないもの
- コードの書き方・解説
モチベーション
- 作業時間(開始時刻, 終了時刻)を記録したかった
- 開始時と終了時にボタンを押すだけで記録できるとよい
- スマホで使えると良い
これを満たすのに、勤怠管理システムが使えるかと思い、無料で使える勤怠管理Webサービスを使っていた。しかし、一日に2回以上の勤怠データを登録することができず、自分の使いたい目的では使えないことが分かった。
そのため、自分で使うためのものを作ることにした。
要件
- 「出勤」「退勤」ボタンが表示される(実際には「作業開始」「作業終了」だが長いのでこうした)
- ボタンを押すとその時の時刻およびボタンに対応する「入」「退」のどちらかが記録される
- 時刻の記録だけに特化する。作業時間の計算等は別にプログラムを書いてどうにかする
GASとは
Google Apps Script (GAS)とは、Googleサービス等と組み合わせて使える、Javascriptをベースとしたスクリプト言語で、Googleドライブとかで実行できる。また、GASを使ってWebサービスを開発したり公開したりできる。
実装方針
実際にやって動かしてみる
Apps Scriptの作成
「新規」をクリック*2→「その他」→「Google Apps Script」をクリック
エディタが開くので、「無題のプロジェクト」と書かれている部分をクリックしてプロジェクトの名前を適当に変更する(任意)
実行の流れとしては、コードを変更する→保存する→デプロイする→ページを開いて確認という流れになる。
動作確認
最初にWebアプリを動かしてみる。エディタに次のコードを入力する。
function doGet() { const html = '<!DOCTYPE html><html><head><base target="_top"></head><body><h1>Hello World!</h1></body></html>'; return HtmlService.createHtmlOutput(html); }
デプロイ
保存ボタン「💾」をクリック、あるいはCtrl+Sを押す→「デプロイ」→「新しいデプロイ」をクリック
「種類の選択」の右の歯車「⚙」をクリック→「ウェブアプリ」をクリック
「デプロイ」をクリック
すると、ウェブアプリのURLが表示されるのでコピーして控えておく。後で使う。
ウェブアプリのURLを開くと次のように表示される。
コードを変更した際は、まず保存し、「デプロイ」→「新しいデプロイ」→「デプロイ」を繰り返すと変更が反映される。*3
アプリを動かしてみる
さて、こちらに作ったアプリを用意した。これを動かしていく。
「ファイル」の右の「+」をクリック→「HTML」をクリック
名前を「index」と入力して確定
index.html
のコードを消して次の内容に書き換える:
<!DOCTYPE html> <html> <head> <base target="_top"> <style type="text/css"> button {font-size: 15vw; height: 30vw; width: 45vw;} input {width: 80vw;} </style> </head> <body> <h1>タイムカード</h1> <form action="<?= app_url ?>" method="POST" name="f"> <div> <button type="submit" name="action" value="in" <?= in_disabled ?> >出勤</button> <button type="submit" name="action" value="out" <?= out_disabled ?> >退勤</button> </div> <div>備考: <input name="comment"></input></div> </form> <div>最終: <?= last_time_log ?></div> </body> </html>
コード.gs
の内容を次のコードに書き換える。spread_sheet_id
, sheet_name
の値は先ほど控えた値に変更する。
const spread_sheet_id = "ここにスプレッドシートのIDを入力"; //記録するスプレッドシートのID const sheet_name = "time_log" //スプレッドシートのシートの名前 const app_url = ScriptApp.getService().getUrl(); //このアプリのデプロイURL function getSheet(){ const spreadsheet = SpreadsheetApp.openById(spread_sheet_id); const sheet = spreadsheet.getSheetByName(sheet_name); return sheet; } function getLastRow(){ let sheet = getSheet(); let last_row_idx = sheet.getLastRow(); let values = sheet.getRange(last_row_idx, 1, 1, 3).getValues()[0]; return values; } function addLastRow(time_str, in_out, comment){ let sheet = getSheet(); let last_row_idx = sheet.getLastRow(); sheet.getRange(last_row_idx+1, 1, 1, 3).setValues([[time_str, in_out, comment]]); } function formattedDate(date){ return Utilities.formatDate(date, "Asia/Tokyo", "yyyy/MM/dd HH:mm:ss"); } function doGet(e) { let html = HtmlService.createTemplateFromFile('index'); html.app_url = app_url; let date, in_out, comment; [date, in_out, comment] = getLastRow(); let date_str; if (date instanceof Date) { date_str = formattedDate(date); } else { date_str = date; } html.last_time_log = date_str + " " + in_out + " " + comment; if (in_out == "入"){ //最後が出勤なら退勤のみ表示 html.in_disabled = "disabled"; html.out_disabled = ""; } else { html.in_disabled = ""; html.out_disabled = "disabled"; } return html.evaluate() .addMetaTag('viewport', 'width=device-width, initial-scale=1, user-scalable=no') .setTitle('タイムカード'); } function doPost(e){ let action = e.parameter.action; let now = new Date(); let date_str = formattedDate(now); const action_to_japanese = {"in": "入", "out": "退"}; let in_out = action_to_japanese[action]; if (!in_out) throw new Error('POSTパラメータが異常です'); //不正な呼び出し let comment = e.parameter.comment; addLastRow(date_str, in_out, comment); //書き込み // GETにリダイレクトする return HtmlService.createHtmlOutput( "<script>window.top.location.href='"+ app_url + "';</script>" ); }
入力したら保存し、デプロイをする。
(2021-10-11追記: デプロイURLはScriptApp.getService().getUrl();
で取得できることがわかったのでそのようにコードを変更した)
GAS引っかかりメモ
Googleアカウントを切り替えられない?
エディタを開く際、同時に複数のGoogleアカウントにログインしている場合、メインのアカウント以外に切り替えることができないようで、使いたいアカウント以外をログアウトする必要があるっぽい?
デプロイする前に保存するのを忘れて変更が反映されない
よくある。保存してデプロイし直す。
WebアプリのHTML内のJavaScriptから、ブラウザのアドレスバーにあるWebアプリのURLを取得
無理っぽい。iframe
に入っていてかつクロスドメインになっているため。なぜ…
タイトルやmeta
タグの設定
GASのWebアプリで表示されるページは、二重にiframe
が入ったその中にアプリで設定したHTMLが表示される。そのため、HtmlOutput
を作成する際のHTMLの中でタイトルやスマホ対応のviewport
を指定するmeta
タグを設定しても効果がない。
代わりにHtmlOutput
オブジェクトの.setTitle
メソッド、.addMetaTag
メソッドを呼び出して、それぞれタイトルとメタタグを設定する。メソッドチェーンもできる。
return HtmlService.createTemplateFromFile('index').evaluate() .addMetaTag('viewport', 'width=device-width, initial-scale=1, user-scalable=no') .setTitle('タイムカード');
リダイレクト
window.top.location.href
にリダイレクト先のURLを代入するJavascriptコードを実行するHTMLを返せばいいっぽい。
return HtmlService.createHtmlOutput( "<script>window.top.location.href='"+ app_url + "';</script>" );
参考: google apps script - Automatically Redirecting to a Page - Stack Overflow
(2021-09-03追記: <iframe>
のsandbox
属性にallow-top-navigation-by-user-activation
が指定されているためリダイレクトが動かなくなった。これどうしようかな…。)