👏

GASで仕組みづくりを始めたばかりから ちょっとわかるようになった人🫣

2022/12/08に公開

こんにちは。@lattest です。

この記事は airCloset Advent Calendar 2022 の 09 日目です。
昨年、GASど素人だった人間がGAS素人になって帰ってまいりました🧎‍🧎‍♂️🧎‍

GAS(google Apps Script)と他媒体をつなぐ機能について、少し理解が広がってきたので
今回はスプレッドシート、GASとSlackを使った通知機能について...
と言っても、この3つに関してはAPIなどを駆使して色々な機能実装に関する記事を見かけるので,なにを書くか考えたのですが、素人が最近知って便利だなぁと思った機能について書くことにします。

スプレッドシートって並び順ソートかけたうえにPDF化してSlackに通知送れるんだよー!!!

という実装を書きます。

なぜ、この実装を作ったか?

各個人のptなど、日次での進捗具合をわかりやすく共有することができるためです。

前提条件として

通知用のボットの作成と権限の設定等は事前に行ってあるものとします。

例えば、スプレッドシートがこんな感じだったのを...

こんな風に自動で並び替え

PDF化してSlackへ送信

それぞれどんなことをしているの?

  1. スプレッドシートから範囲選択をする
  2. ソートがかかってたらリセットする
  3. ソートをかける
  4. PDFに加工する
  5. Slackへ飛ばす

以上です!

ソートに関しては、手動でソートをかけるのと基本同じ手順を自動化しています。

1から順に見ていきます

  1. スプレッドシートから範囲選択をする

サクッと、シートの取得

//スプレッドシートのファイル自体をとる(パターン①)
 const ss = SpreadsheetApp.getActiveSpreadsheet();
 //スプレッドシートのファイル自体をとる(パターン②)
 const ss = SpreadsheetApp.openById(sheetId)//sheetIdの例('DH3SuHCjXM0kTs-aaa_S8899')のようなスプレッドシートのURLに組み込まれた羅列;
 
 //シート指定
 const sheet = ss.getSheetByName('シート1名を入れる');

ここでRangeで範囲指定します。
引数に(始まりの行, 始まりの列, 行数, 列数)

let data = sheet.getRange(2, 1, 5, 5);

次に2を見ていきます

  1. ソートがかかってたらリセットする

※何度も自動ソートをかける際、正しくソートをかけるためにフィルターが存在する(ソートがかかってる)場合はフィルターを削除してまっさらな状態にしています。

  • リセットするにはフィルターを取得します
    .getFilter()を使う

  • そして、そのフィルターを削除する
    .remove()これで削除する

実際の使い方

  1. スプレッドシートの範囲内のフィルターを取得→data.getFilter()
  2. 範囲取得されたフィルターに削除をくっつける→data.getFilter().remove()
//フィルターがnull以外(存在する)なら削除するロジック
if(sheet.getFilter() != null) {
  sheet.getFilter().remove();
}

続いて、3を見ていきます

  1. ソートをかける
  • ソートをかけるにはフィルターを作成し、ソートをかける必要があります
    フィルターをかけるには→.createFilter()
    ソートをかけるには→.sort(列の位置, Booleanで決まる昇降順)
//範囲内の列6番目(列は1から始まります)
//true→昇順
//false→降順

data.createFilter().sort(6, true);

ちゃんとソートされています

加工の最後、4を見ていきます

  1. PDFに加工する

PDF化する共通関数を作ってそこに投げてらく効率化します

//特定のスプレッドシートの指定範囲のPDFファイルを作成して、そのファイルインスタンスを返却する関数
//返却されたインスタンスを用いてSlackへの投稿ができる
function createPDFByRange(ss(スプレッドシート), sheetName(シートの名前), rangeStr(PDFしたい範囲)) {

  const ssid = ss.getId();  //スプレッドシートのIDを取得
  const sheet = ss.getSheetByName(sheetName);
  const sheetid = sheet.getSheetId();  //シートIDを取得
  const pdfRange = rangeStr;  //PDF化範囲

  //PDFをエクスポートするURL
  const url = "https://docs.google.com/spreadsheets/d/SSID/export?".replace('SSID', ssid);

  //PDF化オプションを設定
  const opts = {
    exportFormat: 'pdf',
    format: 'pdf',
    size: 'A4',    //出力するサイズ
    portrait: 'true',  //PDFファイルの向き。true:縦向き、false:横向き
    fitw: 'true',  //ページのフィット。true:フィット、false:原寸大
    sheetnames: 'false', //シート名。true:有り、false:無し
    printtitle: 'true', //ドキュメントのタイトル。true:有り、false:無し
    pagenumbers: 'false', //ページ番号。true:有り、false:無し
    gridlines: 'false', //グリッドライン。true:有り、false:無し
    fzr: 'false', //各ページの行見出し。true:含める、false:含めない
    range: pdfRange,
    gid: sheetid
  };

  //オプションを「&」で繋げる
  let url_ext = [];
  for (optName in opts) {
    url_ext.push(optName + '=' + opts[optName]);
  }
  const options = url_ext.join('&');

  //API使用のOAuth認証
  const token = ScriptApp.getOAuthToken();
  const fileName = dayjs.dayjs().format("YYYYMMDD_outputFile") + '.pdf';

  //PDF作成
  return pdf = UrlFetchApp.fetch(url + options, { headers: { 'Authorization': 'Bearer ' + token }, muteHttpExceptions: true }).getBlob().setName(fileName);
}

このfunctioncreatePDFByRangeに引数を渡すとPDFが作成されます
1~3で作ったデータを使ってPDF化してみます

const range = 'A1:F7';//PDF化したい範囲
const pdf = createPDFByRange(ss, 'シートの名前', range);

最後に投稿、5を見ていきます

  1. Slackへ飛ばす

これも、Slackへとばす共通関数を作ってそこに投げてらく効率化します

/**
 * SlackにPDFファイルを投稿する
 * @param {obj} パラメータobj
 * @param {string} channelId:チャンネルID(例:'C0008J000MD')
 * @param {obj} file:投稿する実ファイル
 * @param {string} comment:投稿時のコメント
 */
function postFile({
  channelId,
  file,
  comment
}) {
  const payload = {
    token: 'xox.......',//tokenはそれぞれSlackで設定したものを使ってください
    channels: channelId,
    file,
    comment
  };

  const param = {
    method: 'POST',
    payload
  };

  if (typeof(file) === "Array") {
    param.contentType = 'multipart/form-data';
  }

  // channelにピン留めする
  const url = "https://slack.com/api/files.upload";
  return UrlFetchApp.fetch(url, param);
}

いよいよ完了、Slackへ飛ばしてみる

postFile({
    channelId: "C0000J0J00Z",//チャンネル右クリック→コピー→コピーリンクで出てきます
    file: pdf,
    comment: "学年末試験の総合得点をお知らせします"
  });

振り返ってみて

一つ一つのロジックは徐々に実装を追加していたので気づかなかったのですが
一つにまとめるとたくさんの工程を経て今の形になっているのを感じました。
グループとして最高のUXを目指して、これからもコツコツと改善に努めていきたいと思います。

参照サイト

Discussion