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
30
Help us understand the problem. What is going on with this article?
@dexia

Google Apps Scriptで書籍を管理する

More than 3 years have passed since last update.

動機

 最近、シリーズもののライトノベルや漫画を頻繁に購入しているのですが、その量があまりにも増えてきていて買い損ねるケースが増えてきました。

 なので、何らかの手段で買い忘れを通知してくれる簡単なアプリを作れないかなと考えました。

技術選定理由

 最初はAWSのRDSとLambda、それにNode.jsあたりを組み合わせて作ればいいかなと思い、実装していました。

 しかし、Expressを使ってAPIを作るだけで休日を使い果たしてしまい、費用対効果が悪いと感じていました。

 その折に、Google Apps Scriptを使えば、WebサーバーやAPIを作れると聞いたので挑戦してみようと思いました。

仕様

 スプレッドシートにシリーズ物のデータを突っ込んで、予想される発売日(最新刊出版日+出版間隔)を超えたシリーズをGmailで通知するという仕様です。

 出版間隔はこれまでの傾向を見て、自分で判断することとします。

データ設計

実際のデータは下のような感じです。シート名は「series」です。

screenshot.1.jpg

 列の意味は以下の通りです。不要な列があったり、ISBNがないとかちょっと考慮不足はありますが、とりあえず目的を果たしていればいいかという感じです。

列名 意味
id ユニークなキー
name 書籍名
latest 最新刊番号
period 出版期間
subscribe 購読サイン
purchased 最新刊購入日
published 最新刊出版日
author 作者

実装

 スプレッドシートにスクリプトを追加していきます。一応順を追って手順を書きます。

データをオブジェクトの配列として取得する

// テーブルデータの取得
function getTableArr(sheetName){
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName(sheetName);
  var arr2D = sheet.getRange(1,
                             1,
                             sheet.getLastRow(),
                             sheet.getLastColumn()).getValues();
  return arr2D;
}

// テーブルデータをJavaScriptのオブジェクトの配列に変換
function toJSObj(arr2D) {
  var titles = arr2D[0];
  return arr2D.slice(1).map(function(arr) {
    var obj = {};
    for(var i = 0; i < arr.length; i++){
      obj[titles[i]] = arr[i];
    }
    return obj;      
  }); 
}

// シリーズ一覧を取得
function getSeries() {
  var series2D = getTableArr("series");
  var series = toJSObj(series2D);
  return series;
}

 データを取得するのはgetTableArr関数です。シートにはテーブル以外置かないと決めているので、行、列の最大値からRangeを計算して取得しています。

 構造化されていない2次元配列をそのまま扱うのは面倒なので、JavaScriptのオブジェクトに変換しました。それが、toJSObj関数です。

 ここでは1行目のヘッダーをプロパティ名に割り当てて変換しました。

 これでシリーズのデータが取得できました。

フィルタリングする

// おすすめを取得
function getRecommendedSeries(){
  var series = getSeries();
  var now = new Date();
  var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  var recommended = series.filter(function(s) {
    var limit = new Date(s.published.valueOf());
    limit.setMonth(limit.getMonth() + s.period);
    return s.subscribe && (limit.getTime() <= today.getTime());
  });
  return recommended;
}

 ふつうのJavaScriptの操作ですね。

 購読サインが立っていて、かつ予想される発売日を超えているものを取得しています。

 日付は一応コピーを作ってから破壊的な操作(日付の変更)をしていますが、今回はしてもしなくても特に影響はありません。

メールを送信する

// mailを送信
function sendMail(recommeded) {

  var to = "my-email@gmail.com";
  var subject = "推薦書籍の紹介";

  var body = "以下の書籍の購入はお済ですか?\n";
  for(var i = 0; i< recommeded.length; i++){
    body += recommeded[i].name + "\n";
  }

  var from = "my-email@gmail.com";
  var sender = "my-name";

  GmailApp.sendEmail(
    to,
    subject,
    body,
    {
      from: from,
      name: sender
    }
  );
}

// 通知する
function notifyBooks(){
  var recommeded = getRecommendedSeries();
  if(recommeded.length) {
    sendMail(recommeded);
  }
}

 件名、送信者、本文当たりの情報を詰め込んで送るだけで、とてもシンプルな記述でやりたいことができました。

定期実行のトリガーを仕掛ける

 これでnotifyBooks関数に定期実行のトリガーを仕込みます。

 その際に権限周りの認証を行えば、それで完成です。

まとめ

 ここまでの記述で大体2時間くらいで完成しました。

 今回のような小さなシステムであれば、Google App Scriptでさくっと作ってしまうのがとても楽でいいなぁと思いました。

 あとはAmazonのAPIとつなげたり、WebAPIとして公開して他のインターフェースと連携(Slack、Alexa、Lineあたり?)できると面白そうかなぁと思います。

30
Help us understand the problem. What is going on with this article?
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
dexia
動的言語大好き人間です。JavaScript、Lisp、Rubyあたりがマイブームです。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
30
Help us understand the problem. What is going on with this article?