多くの人（？）が通る道、保育園のアルバム委員の作業をGASで効率化した話

# 背景
保育園の役員や謝恩会の委員、アルバム制作など、多くの人が経験することとと思います。
その中で、アルバム制作の写真集めを、GASを使って効率化した話を書きます。
エンジニアの仕事の効率化ではないことを最初に謝っておきます。ごめんなさい。
長女(9)の時に苦労して、長男(7)の時に効率化した話です。
GASのコードは、初めてGASを書いた状態なので燦々たる状態ですが、現状リファクタリングニーズがないのでこのまま供養します。

# 課題
子供達の保育園では、卒園アルバム制作が保護者のタスクとなっています。
例年、下記の内容でアルバム制作を進めています。
1. 業者選定
2. 分担（デザイン、撮影、データ）
3. 分担に分かれて作業
4. 納品
5. 配布  

この中で、データ担当になり、写真データを集めて管理していた話です。
年にもよりますが、長女の時は、3000枚程度の写真を集めていました
何時間もかけて集めた写真をスキャンしていました、、、、

「各自、スキャンして送ってもらうのが良いが、メールで送ってもらったのを分類するのも大変そう、、、、どうせなら色々と自動でできないかしら。」
と思ったということで、長男の時には、いろんな条件で特定のドライブに移すものを作りました

# 使うもの
- Googleメールアドレス
    - 画像メールの受け子
- Googleドライブ
    - 画像の格納先
- GoogleAppScript
    - いろいろ（後述）
- GoogleSpreadsheet
    - いろいろの設定（後述）

# GAS + SpreadSheetで行ったこと
- GAS
    - 新着メールからの複数画像ファイルをSpreadSheetで指定したファイルへの移動
        - メールタイトルとフォルダを対応付け
        - ファイル→メールのトレーサビリティを確保するため、メールの日時をファイル名に付与（ファイルサイズが小さい等の返信をすることがあるので結構重要）  
    - SpreadSheetで指定したファイル名への変更
        - 連番_〇〇ちゃん、というprefixを付けて、ソートできるようにした
        
```js:新着メールからの複数画像ファイルをSpreadSheetで指定したファイルへの移動.gs

function gmailFileUploader(){
  
  //スプレッドシートを読み込む
  var spreadsheets = SpreadsheetApp.openById(SPREADSHEET_ID);
  var spreadsheet = spreadsheets.getSheetByName("フォルダ");
  
  //二次元配列に
  var data = spreadsheet.getRange(1,1,spreadsheet.getLastRow(),2).getValues();
  
  //タイトル名の配列と、ファイル名の配列にする
  folderArray = [];
  titleArray = [];
  for(var i = 0; i < data.length ; i++){
    titleArray.push(data[i][0]);
    folderArray.push(data[i][1]);
  }
  
  var Threads = GmailApp.getInboxThreads();　
  for(var i=0;i<100;i++){　
    if (!Threads[i]){return;}//メールがなければ抜ける
    var status = Threads[i].isUnread();
    var status_save = false;
    if(status==true){
      var subject = Threads[i].getMessages()[0].getSubject();
      for (var k = 0;k < titleArray.length;k++){
        if(data[k] != null && subject==titleArray[k]){//タイトルは0列目に
          for(var i_msg = 0; i_msg < Threads[i].getMessageCount(); i_msg++){
            var msg = Threads[i].getMessages()[i_msg];
            //既読だったら処理を飛ばして次のmsgへ
            if(msg.isUnread() == false){ continue; }
            var attachments = Threads[i].getMessages()[i_msg].getAttachments({includeInlineImages:true});
            var mailfrom = remove_dot(Threads[i].getMessages()[i_msg].getFrom());　
            var folders = DriveApp.getFoldersByName(folderArray[k]);//フォルダは1列目に
            while(folders.hasNext()){
              var folder = folders.next();
              for(var j = 0;j<attachments.length;j++){
                var data = DriveApp.createFile(attachments[j]);
                //ファイル名に時間を入れる
                var new_file_name = Utilities.formatDate(msg.getDate(),"Asia/Tokyo","yyyyMMdd-HHmmss") + mailfrom + "_" + attachments[j].getName();
                data.setName(new_file_name);
                folder.addFile(data); 
                status_save=true;
              }
            }
            if(status_save){
              msg.markRead();//保存終了したら既読にする
            }
          }
          continue;//一致するタイトルが見つかったら次のループへ
        }
      }
    }
  }  
} 
//あとでメールアドレスから名前に変換するため、メールアドレスの＠以前のドットを抜いた状態でファイル名につける
function remove_dot(mail_from){
  
  var reg = /<.*@.*>/g;
  var ret = "";
  
  if(mail_from.length > 0 && mail_from.match(reg) != null){
    ret = mail_from.match(reg)[0];
  }else{
    ret = mail_from; 
  }
  ret = ret.split("@")[0].replace(/@*/g,"").split('<').join('').split('.').join('');
  
  return ret;
}
```

```js:SpreadSheetで指定したファイル名への変更.gs
function myFunction() {
  //スプレッドシートを読み込む
  var spreadsheets = SpreadsheetApp.openById(SPREADSHEET＿ID);
  var spreadsheet = spreadsheets.getSheetByName("よみかえ");
  
  //エリアのサイズを取得
  var rowSize = spreadsheet.getDataRange().getHeight();
  var colSize = spreadsheet.getDataRange().getWidth();
  
  //jpegのみ収集
  var files = DriveApp.getFilesByType(MimeType.JPEG);
  
  var froms = spreadsheet.getRange(1, 1, rowSize, 1).getValues();
  var tos = spreadsheet.getRange(1, 2, rowSize, 1).getValues();
  //ファイル全てに対してループ
  
  var count = 0;
  while (files.hasNext()) {
    var file = files.next();
    
    for(var i = 1; i < rowSize ; i++){
      var from = froms[i]
      var name = tos[i]
     
      //ファイル名に指定文字列が含まれる、かつ、読み替え後文字列が存在すれば読み替える
      if(file.getName().indexOf(from) > 0 && name.length > 0){
        var fname = file.getName();
        file.setName(name + fname.replace(from,""));
        Logger.log(fname);
        count++;
        //読み替えたら次ループへ
        continue;
      }
    }
  }
  Logger.log(count);
}
```

- SpreadSheet※個人情報満載なのでつけられません
    - タイトルによる格納先一覧
        - フォルダと、メールタイトルの対応付け
    - メールアドレスによる自動ファイル名付け一覧
        - 送信元メールアドレス（＠以前のみ、ドットは削除）と、つけるprefixの対応付け



# 運用
- 写真は、専用のメールアドレスにファイル添付で送ってもらうこととする
- 「2歳児クラス」等の学年や、「5才遠足」等のイベントごとに、メールのタイトルと氏名等を含めたメールを作成するURL・QRコードを作成して一覧配布
    - 同時にLINEでも○歳は、タイトル「○才」で送ってね、という周知
    - （※ページの都合で、「5歳クラス」と「5歳遠足」というMECEじゃない分け方で困りそうな感じなのは私のせいじゃない）

# 振り返り等々
- タイトル指定はなかなか難しい（半角全角の表記ゆれ等）
- メールでデータを送る、が慣れていない人も
- そもそもスキャンしたくない人も多くいた（そういう人は、スキャンしてまで送りたくない、となって枚数が減ってしまう。写真集めをタスクとしている身としては、目的を満たせないかなり大きな問題）
- スキャン環境がない人の分は現物を受け取って実施した。以前と比べると現物受け取り枚数は減ったので、スキャンの負担をある程度分散できたように思う
- この仕組みを使っても使わなくても、スキャン画像の荒さなどの問題がでる
- 学年間違えて送ったりは目検で救済
- 複数人から同じ写真が送られてくることがあり、皆が気に入って買っている写真があるのもあったが、人毎に分かれてしまうので、似た写真がわかるといいなと思った

# 感想
- GASを当時初めて使ったけれど、便利だと思った、勉強になった！ここから私のGAS生活が始まった、、、！
- 機械学習用の画像データを集めを複数人で行うときに便利かもと思ったり
- 個人情報だらけのため、スプレッドシートが出せなくてすみません

# 今後
- もう一人、次男(3)の分の仕事が残っています
- この仕組みをまた使うかは分からないが、次回使うなら、画像サイズでの自動返信を入れたい、、、。「画像サイズが小さいので、担当まで現物を渡してください」のような感じで、、、、
- GoogleFormの方が簡単かもしれないとちょっと思っています
- LINEでのコミュニケーションを、Slackに出来たら、まだまだ色々と自動化できそうだな〜と思ったり。（進行スケジュールとか）
- まあ細かい効率化はありますが、結局コミュニケーションを効率化することが一番、、、、
- 4人目の予定はありません。たぶん。