Google Apps Script + jQuery Mobileでスマホ用簡易タイムレコーダーを作った
最近ブラック企業に関する報道の影響か、自分自身の手帳等にも出社・退社時刻を記録すべきという主張を目にします。
とはいえ正直なところ面倒なので、スマホから記録できる簡易タイムレコーダーを作りました。
使い方
ボタンを押すだけ
仕様
jQuery MobileのクライアントからGETリクエストを送信すると、Google Apps ScriptのWebアプリが受け取ってスプレッドシートに記録し、JSONPで結果を返します。
クライアントはGoogle ドライブのホスティング機能で公開するようにしました。そのためレンタルサーバ等は不要です。
作り方
- 記録先スプレッドシートを準備
- こんな感じの「template」シートを作成
スクリプトはこれを月初めにコピーし、「YYYY/MM」にリネームして記録します。
- E, F, J列の書式を「時間」に
- J1に「=SUM(F:F)」、J2に「=SUM(G:G)」
- こんな感じの「template」シートを作成
- コードを書く(以下参照)
- クライアント一式をGoogle ドライブで「ウェブ上で一般公開」→生成されたホスティングURLにアクセスして使う
コード
サーバー (Google Apps Script)
function doGet(e) { var scriptProperties = PropertiesService.getScriptProperties(); var book = SpreadsheetApp.openById(scriptProperties.getProperty("bookId")); var now = new Date(); var nowStr = Utilities.formatDate(now, "JST", "yyyy/MM/dd HH:mm:ss"); var values = []; var response = {}; switch(e.parameters.mode[0]) { case "checkin": var name = Utilities.formatDate(now, "JST", "yyyy/MM"); var sheet = book.getSheetByName(name); // if sheet does not exist if (!sheet) { var sheet = book.getSheetByName("template").copyTo(book).setName(name); sheet.getRange(1, 1).setValue(name); book.setActiveSheet(sheet); } values[0] = Utilities.formatDate(now, "JST", "yyyy/MM/dd"); values[1] = "=TEXT(R[0]C[-1],\"ddd\")"; values[2] = nowStr; sheet.appendRow(values); response["status"] = "success"; break; case "checkout": var sheet = book.getActiveSheet(); var lastRow = sheet.getLastRow(); values[0] = nowStr; values[1] = "=IF(R[0]C[-1]-R[0]C[-2]>=" + scriptProperties.getProperty("breakLimit") + "/24," + scriptProperties.getProperty("breakHours") + "/24,0)"; values[2] = "=R[0]C[-2]-R[0]C[-3]-R[0]C[-1]"; values[3] = "=IF(R[0]C[-1]-" + scriptProperties.getProperty("workHours") + "/24>0,R[0]C[-1]-" + scriptProperties.getProperty("workHours") + "/24,0)"; sheet.getRange(lastRow, 4, 1, 4).setValues([values]); response["status"] = "success"; break; default: response["status"] = "failed"; } // return jsonp return ContentService.createTextOutput(e.parameters.callback + "(" + JSON.stringify(response) + ")").setMimeType(ContentService.MimeType.JAVASCRIPT); }
スクリプトプロパティ
プロパティ | 値 |
---|---|
bookId | 記録先スプレッドシートのキー |
breakLimit | 休憩時間が付与される最低労働時間(単位:時間) |
breakHours | 休憩時間(単位:時間) |
workHours | 所定労働時間(単位:時間) |
クライアント (jQuery Mobile)
- timeRecorder.js
var end = "終業時刻 (HH:mm)"; var exec = function(event) { if (window.confirm(event.data.name + "を記録しますか?")) { $.mobile.loading("show", { textVisible: true, textonly: false }); $.ajax({ url: "https://script.google.com/macros/s/(スクリプトのキー)/exec?mode=" + event.data.mode, type: "GET", dataType: "jsonp", timeout: 10000, success: function(data, status) { if (data.status == "success") { $.mobile.loading("hide"); alert("記録しました"); } else { $.mobile.loading("hide"); alert("不正なリクエストです"); } }, error: function(data) { $.mobile.loading("hide"); alert("通信エラーが発生しました"); } }); } } $(document).bind("pageinit", function() { var update = function() { var now = moment(); var day = now.format("YYYY/MM/DD"); $("#day").text(day); var current = now.format("HH:mm:ss"); $("#current").text(current); var rest = moment(day + " " + end); rest = moment.utc(rest.diff(now)).format("HH:mm:ss"); $("#rest").text(rest); } update(); setInterval(update, 1000); $("#checkin").on("click", {mode: "checkin", name: "出勤"}, exec); $("#checkout").on("click", {mode: "checkout", name: "退勤"}, exec); });
- index.html
<!DOCTYPE html> <html> <head> <title>タイムレコーダー</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="//code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" /> <script src="//code.jquery.com/jquery-1.9.1.min.js"></script> <script src="//code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js"></script> <script src="timeRecorder.js"></script> </head> <body> <div data-role="page" data-theme="b" id="index"> <div data-role="header"> <h1>タイムレコーダー</h1> </div> <div role="main" class="ui-content"> <table data-role="table" id="data"> <thead> <tr> <th>日付</th> <th>時刻</th> <th>終業まで</th> </tr> </thead> <tbody> <tr> <td><span id="day"></span></td> <td><span id="current"></span></td> <td><span id="rest"></span></td> </tr> </tbody> </table> <a class="ui-btn" href="#" id="checkin">出勤</a> <a class="ui-btn" href="#" id="checkout">退勤</a> </div> </body> </html>
メモ
Google Apps Script
- ScriptProperties/UserPropertiesは廃止されたのでPropertiesServiceを使えとのこと
- しかしPropertiesService.getUserProperties()で取得できるユーザープロパティはスクリプトエディタのUIからは編集できない、一方でスクリプトプロパティは編集できるという仕様 ^1
- doGetの引数に入るparametersは配列になってるのか、[0]をつけないとswitch文でコケる?
- Range#setNumberFormatでセルの書式を設定できるけど、24時間以上が表示できるか微妙な感じ
jQuery Mobile/JavaScript
- $(document).ready()ではなくpageinit()を使えとのこと
- 時刻計算にmoment.jsを使いました