読者です 読者をやめる 読者になる 読者になる

moai's blog

ソフトウェア開発にまつわる内容を記載します。

Gmailを検索してサマリーを自分のメールアドレスに送るスクリプト

なにこれ

Gmailを検索してサマリーを自分のメールアドレスに送るGoogle App Script

スクリプトの事前手順

事前手順とかは、こちらをご覧ください。 blog.ryamazaki.com

スクリプト本体

function engKintaiMailGrep() {
  var yesterday = new Date();
  // ここをよしなな検索条件にすると適当に取ってこれます。
  var threads = GmailApp.search('subject:勤怠 newer:' + getYesterdayDateString());
  // Logger.log(threads.length);
  var offPeopleSubjects = threads.map(function(thread) {
    var firstMessage = thread.getMessages()[0];
    var subject = firstMessage.getSubject();
    // Logger.log(subject);
    var offWordInTitle = subject.match(/休み|やすみ|欠勤|全休/);
    var offWordInBody = firstMessage.getPlainBody().match(/休み|やすみ|欠勤|全休/);
    // Logger.log(offWordInTitle);
    // Logger.log(offWordInBody);
    var is_off = offWordInTitle || offWordInBody
    return is_off ? subject : null
  });
  var compactedOffPeopleSubjects = offPeopleSubjects.filter( function(subject){ return subject } );
  // Logger.log(compactedOffPeopleSubjects);
  var compactedOffPeopleSubjectsJoinsNewline = compactedOffPeopleSubjects.join("\n");
  // Logger.log(compactedOffPeopleSubjectsJoinsNewline)
  // 今回は誰かのメールに通知するために、最後にmailを送る処理を書いてます。
  GmailApp.sendEmail("hogehoge@hogehoge.com", "今日のお休み", compactedOffPeopleSubjectsJoinsNewline);
}


/**
  昨日の日付を取ってくる
**/
function getYesterdayDateString(){
  var today = new Date();
  var yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  return yesterday.getFullYear() + '/' + (yesterday.getMonth() + 1) + '/' + yesterday.getDate();
}

定期実行手順

定期実行手順とかも、こちらをご覧ください。 blog.ryamazaki.com

G Suiteで作ったテンプレートファイルを定期的に特定のフォルダにコピーするスクリプト

なにこれ?

タイトルの通りのスクリプトを作ってみる記事。

困っていたこと

ざっくりいうと

G Suiteとか業務で使っているとテンプレートファイルを作って、それをコピーして使うようになるじゃないですか。 ただ、そうすると毎回、ファイルを右クリックして、コピーしてファイル名書き換えてとかするわけですよ。 めんどくさい。

具体的に言うと

  1. こうして(テンプレートファイルを右クリックしてコピーして) f:id:ryamazaki:20170128160538p:plain
  2. こうして(右クリックして、ファイル名書き換えて) f:id:ryamazaki:20170128160551p:plain
  3. こうする(はぁ、やっとほしいもんが得られた) f:id:ryamazaki:20170128160602p:plain 作業がめんどくさかったわけです。

そのスクリプト

スクリプトの設置方法

事前手順

Googe App Scriptのアプリは事前に追加しておいてください。

  1. 『アプリを追加』から追加する f:id:ryamazaki:20170128161504p:plain

  2. Google App Scriptを接続しておく

f:id:ryamazaki:20170128161559p:plain

手順

そこのディレクトリに行って、ファイル追加と同じ要領で追加してください。 (そこのディレクトリにいく必要はホントは無いけど、運用的にはそこディレクトリにおいておくのが後々のわかりやすさから親切だと思います。)

  1. 新規ボタンからスクリプトを選択して、作成 f:id:ryamazaki:20170128162005p:plain
  2. スクリプトファイルが作成される f:id:ryamazaki:20170128162230p:plain

スクリプト本体

スクリプト作り方

できたスクリプトファイルの中に、以下をそのままコピペします。 templateのファイルIDと、フォルダのIDは置き換える必要があるので、注意してください。

function copyFileFromTemplate() {
  // template Docを取ってくる
  //   (参考)DocのfileIdは一番最後のやつ。下の例では、 hogehogehogehogehogehogehoge がDocのfileId
  //         https://docs.google.com/document/d/hogehogehogehogehogehogehoge/edit
  var templateDocFile = DriveApp.getFileById('hogehogehogehogehogehogehoge');
  
  // ファイルをコピーして、フォルダに格納する
  //   (参考)IDは一番最後のやつ。下の例では fugafugafugafugafugafugafuga がID
  //         https://drive.google.com/drive/folders/fugafugafugafugafugafugafuga
  var weeklyNoteFolder = DriveApp.getFolderById('fugafugafugafugafugafugafuga');
  templateDocFile.makeCopy(getWeeklyNoteTitle, weeklyNoteFolder);
}

/*
  weeklyのファイルを作成する
*/
function getWeeklyNoteTitle(){
  var date = new Date();
  var y = date.getFullYear();
  var m = date.getMonth() + 1;
  var d = date.getDate();
  var w = date.getDay();

  m = ('0' + m).slice(-2);
  d = ('0' + d).slice(-2);

  // フォーマット整形済みの文字列を戻り値にする
  return y  + m +  d + ' weekly';
}

スクリプトの動作確認

上の再生マークっぽいものをポチッとすれば、実行できます。 f:id:ryamazaki:20170128162636p:plain

定期実行の設定方法

最後に定期実行を設定しておきましょう。 どうせ毎週やるんだから、忘れないように定期実行させておきましょう。

  1. すべてのトリガーからトリガー一覧を見つけて f:id:ryamazaki:20170128163007p:plain
  2. トリガーを追加して f:id:ryamazaki:20170128163014p:plain
  3. よしなに定期実行を設定する f:id:ryamazaki:20170128163023p:plain

やったね

これで、一つ創造的な時間が増えたぞ。

振る舞いを切り出して共通化するときの指針

なにこれ

振る舞いを切り出して共通化する時、どのようなケースの場合は共通化すればいいんだろうかと悩んできました。 なんとなくこうすれば良いのではと思っていることがあるので書いてみたものです。

背景

完全に同じmethodだけど、2クラスぐらいなら抽象化しないままそのまま取り扱ったほうが別クラスなので別に扱ったほうが後々の変更に便利そう。 なので、別に切り出すほどではないような少し似ているくらいなら放置しようかという気持ちになっている。 しかし、同じものは何回も書きたくないのでDRYの法則は守りたい。 そうやってモヤモヤしていたものを最近はこういう指針でやっていこうと思ったので、書いておくことにした。

こういう方針でやればいいんじゃないか?

これを満たすときには共通化すればいいと思う。

  • 記述している振る舞いが本質的に同じものである。
  • 3つ以上同じ処理を書くことが容易に想像がつく。

記述しているものが本質的に同じもの

これは高確率で抽象化しても良いと思う。例えば、本と野菜にどちらも消費税込みの小売価格を計算するメソッドが有ったら、 抽象化して、親クラスを作るなりすればいいと思う。

class Book

  def price
    100
  end

  def retail_price
    self.price * 1.08
  end

end

class Vegetable

  def price
    100
  end

  def retail_price
    self.price * 1.08
  end

end

でも、たまたま振る舞いは同じだけど、本質的には違うものは抽象化したらアカンと思う。 例えば、小売価格を計算するBookと信託手数料を計算するInvestmentTrustクラスは同じ* 1.08するのかもしれない。 だけど、こいつらは抽象化したらあかん。 下で言うと、retail_price と trust_commisionをまとめるようなものです。

class Book

  def price
    100
  end

  def retail_price
    self.price * 1.08
  end

end

class InvestmentTrust

  def price
    100
  end

  def trust_commission
    self.price * 1.08
  end

end

3つ以上同じ処理を書くことが容易に想像がつく

3つ以上同じ処理を書くことが容易に想像がつく場合も抽象化しても良いと思う。 逆に言うと2つくらいなら放置しておく。 2つくらいなら時期がきたら誰かが抽象化してくれると思うし、2つくらいなら複雑性も上がらないと思うから。

下の例で、かつ物を一般向けに販売するシステムなんかを作っていた場合、その小売価格を計算することなんてたくさんあるだろうから すぐに親クラスを作って抽象化すればいいと思う。

class Book

  def price
    100
  end

  def retail_price
    self.price * 1.08
  end

end

class Vegetable

  def price
    100
  end

  def retail_price
    self.price * 1.08
  end

end

でも、上の例でも、かつ物を卸のシステムを作っていて、でも特別に本と野菜だけは特別に小売する場合なら 少し待ってもいいと思う。 時期がきたら特別扱いしなくても良くなると思う。 Rubyなんかで作っている場合は変更も容易だし、気がついたときに変更すればいいと思います。

ただ、異論も認めます。

SICPやるときの参考にした記事とか

なにこれ?

SICPやろうかなと思って、環境整えるの準備したことを書いていきます。

SCIPって何さ

これのことです。プログラミングの教科書です。 ざっくりとした概要についてはwikiの方が詳しいのでそちらをどうぞ。 英文の方がいいとか聞きますが、私は日本語で読めたほうが楽なので、日本語で読んでいます。

ズバリ

これです。 書いているうちに、ズバリな記事を見つけてしまったので、それの紹介で終わります。

その他

ズバリな記事に行くまでに参考にした記事を幾つか書いておきます。