Googleドライブのファイル整理作業を効率化したくて、スプレッドシート上で指定したフォルダ内のファイルのリネームと移動を行うことができるツールをGASで制作しました。
この記事では、ツールの全体像とスクリプトの構成についてまとめておきます。主にクラス化をがんばってスッキリしたスクリプトが書けてよかったという話。
ツールの全体像
今回のツールでGASで扱う対象となるのは、特定のドライブのフォルダとスプレッドシートです。
ドライブのフォルダ
整理したいpdf資料が特定のフォルダ(ここをソースフォルダと呼んでいます)にズラーっと並んでおり、さらにファイルを分類するための移動先フォルダをいくつか用意しておきます。
今回のツール制作の動機は、スキャンに掛けた資料データがソースフォルダに自動で溜まっていくので、それを整理しやすくしたいということでした。
これまではファイル1つ1つに対して名前の変更とドラッグ&ドロップで移動していたのですが、頻繁に発生する作業なのでこれをなんとか効率化したくて、次のようなスプレッドシートを使って編集する方法をとることにしました。
スプレッドシート
スプレッドシートには、「編集シート」「ファイル情報一覧」「分類先フォルダ一覧」の3つのシートを用意します。
編集シート
ツールのメインとなるシートです。このシート上で、変更したいファイル名の入力や移動先フォルダ選択を行います。
実際の主な利用の流れはこんな感じです。
- 右上の「ファイルをロードする」ボタンをクリックと、ドライブのソースフォルダからファイル一覧を読み込む
- スプレッドシート上で変更したい名前・名前に付与したい情報・分類先フォルダ名を指定する
- 右上の「ファイル名を変更する」「ファイルを整理する」ボタンをどちらかクリックすると、ボタンに応じて一括で処理を行う
ファイル情報一覧シート
ファイル名に付与するための情報が入ったシートです。
編集シートのD〜F列(「補足1」「補足2」「種別」)のプルダウンリストの要素として使います。ファイル名によく使う団体名や取引先など、表記揺れをなくしたくて作りました。
このシートに手打ちで追加していっても良いですし、GASでリネーム処理したものが自動で追加されるようにもしています。
分類先フォルダ一覧シート
ソースフォルダの中の分類先フォルダ名とIDの一覧が入ったシートです。
編集シートのH列(「分類先フォルダ」)のプルダウンリストの要素として使います。
このフォルダ情報の一覧はGASで更新できるようにしています。
スクリプトの構成
スクリプト全体はこちらのGitHubに。GASはスプレッドシートのコンテナバインドスクリプトとして作成しています。
https://github.com/massa-potato/tool-work-drive-arrangiment/tree/dev
グローバル変数やクラス化については、今年の頭にノンプロ研のモブプロで学んだこと、えとうさんやかにみそさんのLTなどで学んだことを取り入れて、すごくメンテナンスが楽で使い回しのきく構成になりました。ありがとうございました!(最近のはまったく追えてないし、解釈が正しいかはわかりませんが)
グローバル変数を1箇所にまとめた
シート情報やドライブ情報などを、global.js
にまとめています。
変更される可能性のあるシート名やドライブIDなどはここにまとまってるので、スクリプトのあちこちを触らずここで変更すればOKです。関数やクラス内のマジックナンバーも一掃できました。
const DRIVE = Object.freeze({ SRC_FOLDER: { ID:'<ドライブのフォルダIDを入力>', URL:'<ドライブのフォルダURLを入力>' } }); const SHEET = Object.freeze({ EDIT: {NAME: '編集シート', HEADER_ROWS: 5}, FILES_INFO: {NAME: 'ファイル情報一覧', HEADER_ROWS: 1}, FOLDERS_INFO: {NAME: '分類先フォルダ一覧', HEADER_ROWS: 1} }); const COLUMN = Object.freeze({ EDIT: { SRC_NAME: {COL: 'A', NO: 1, IDX: 0, NAME: '変更前ファイル名'}, DATE: {COL: 'B', NO: 2, IDX: 1, NAME: '日付'}, NAME: {COL: 'C', NO: 3, IDX: 2, NAME: 'ファイル名'}, SUFFIX_1: {COL: 'D', NO: 4, IDX: 3, NAME: '補足1'}, SUFFIX_2: {COL: 'E', NO: 5, IDX: 4, NAME: '補足2'}, PREFIX: {COL: 'F', NO: 6, IDX: 5, NAME: '種別'}, DEL: {FOL: 'G', NO: 7, IDX: 6, NAME: '削除'}, DEST: {COL: 'H', NO: 8, IDX: 7, NAME: '分類先フォルダ'}, URL: {COL: 'I', NO: 9, IDX: 8, NAME: 'ファイルURL'}, ID: {COL: 'J', NO: 10, IDX: 9, NAME: 'ファイルID'} }, FILES_INFO: { // (省略) }, FOLDERS_INFO: { // (省略) } });
中でも、シートの列名や列の順番はスクリプトを書きながら使い勝手を見て変更しちゃうことが多いので、このようにCOLUMN
オブジェクトとしてまとめておくことで特に便利になりました。
COLUMN
のプロパティはシートごと、列ごとに入れ子になってます。一番内側のプロパティについて、NO
はSheet.getRange()
などで列番を指定する際の順番、IDX
は配列で扱う時のインデックス番号(NO - 1
)を表しています。ちょっと冗長に感じますが、それぞれここで用意しておいた方が処理の中身がスッキリします。
1機能につき1つの関数とした
シートのボタンに応じて4つの関数に分けました。分すぎてもメンテナンスしづらいと感じて、1機能につき1つの1つの関数&1つのファイル、といった感じです。
func01_loadFileRecords.js
:ドライブからファイル一覧を読み込んでシートに書き出す機能func02_renameFiles.js
:シート上で編集したファイル名を変更する機能func03_assortFiles.js
:シート上で編集したファイル名の変更&ファイル移動をまとめて行う機能func11_loadFolders.js
:ドライブからフォルダ一覧を読み込んでシートに書き出す機能
また、個別の処理は関数の中に書いたり関数分割して作るのではなく、クラスにまとめています。
たとえば、ひとつめのドライブからファイル一覧を読み込んでシートに書き出すためのloadFileRecords()
関数はこんな中身です。
function loadFileRecords(check = true) { // 確認プロンプトの表示 if(check === true) { const res = Ui.showYesNoAlert('確認', 'ファイル一覧を更新しますか?'); if(res === false) return; } // ドライブからファイル一覧レコードを取得 const records = new DriveSrcFolder().generateFileRecords(); // 編集シートのヘッダー行以下にファイル一覧レコードを書き込み const sheet = new SheetEdit(); sheet.clearRecords(); sheet.writeRecords(records); // 編集シートのフォーマットを設定 sheet.setFormat(); }
関数を作り始める前にコメントでやりたい処理を順次で書いておくことで、処理の流れが追いやすくスッキリ書けます。
クラスの分類をしっかり考えた
クラスは6つ作っていますが、大きく4つの分類に分かれています。
- UIに関するクラス(ここでは確認用のプロンプト)
cls01_Ui.js
- アプリ操作に関するクラス(ここではドライブ操作)
11_DriveSrcFolderクラス
- データベースを操作するクラス(ここではシート)
21_SheetEdit
22_SheetFilesinfo
23_SheetFoldersInfo
- 細かい処理をするためのクラス(ここでは形式の決まった二次元配列に対してのいろいろな処理)
31_FileRecords
クラスの分け方について、完全に理解できているわけではなくだいぶ感覚が入っています。
- 「UIに関するクラス」と「アプリ操作に関するクラス」は、汎用的に使える機能をそれぞれまとめておくと良さそう
- 「データベースを操作するクラス」は、シートごとに作ればとりえあえずOKそう
- 「細かい処理をするためのクラス」は、システムの中で頻繁に扱う型があればそれごとにデータと処理をまとめておくようなイメージが良さそう
4つ目の分類がドメインクラスにあたるのかな、という認識です。このツールは編集シート上に入力されたデータを起点に動いているので、そのデータ(型は二次元配列)とさまざまな処理をFileRecords
クラスとして1箇所にまとめています。
実際に書いているともう一つクラスを作った方が良さそうでしたが、逆にメンテナンス性が悪くなりそうでやめました。ツールを拡張していって扱うデータ型が増えれば、またクラスを追加すればよいと思っています。
クラス内の1つ1つのメソッドは、目的ごとに短くポンポンとデータを返す・処理を行うようなイメージで作っています。シートを扱うクラスは共通のメソッドが存在するので、こういうのを継承を使って書けるともっとスッキリするんですかねー。
おわりに
かなり実用的なツールができました!がっつりGASを書けて楽しかったです。
ファイルの削除機能など急がないので後回しにしている機能があるので、また必要になったら手を加えていきます。