見出し画像

【GAS】Google Apps Script 活用事例 HRMOS経由で届いたメールを取得して、書類選考や面接の合否結果をスプレッドシートに自動反映させるスクリプト

前に書いた記事で、HRMOSの応募者情報をシートに書き出す事が出来たので、ついでに選考結果も自動で反映出来ないかなと思って、ロジックを考え、やっと自動化する事が出来ました。

このスクリプトが、きちんと動く前提

1. Google Workspace(旧 G Suite)を使っている。
2. HRMOSを使っている。

Office系では、残念ながらGASは使えません.....。

スクリプトは、こんな感じ

/*
 * 1DAY選考の書類選考のNG評価の回数をカウント、ステータス更新を自動化する。
 * 書類選考は、2人の面接官から付くNG評価が2回以上になるかを数える。
 * 合格は、その逆で、NG以外が2回あったら、合格とする。
 */

function reflectStatusNG() {
 const spreadsheet     = SpreadsheetApp.getActiveSpreadsheet();
 const targetSheet     = spreadsheet.getSheetByName('応募者管理表');
 const values          = targetSheet.getDataRange().getValues();
 const headerRow       = values[1];
 
 const nameColIdx     = headerRow.indexOf('氏名');
 const statusColIdx   = headerRow.indexOf('選考ステータス');
 const dateColIdx     = headerRow.indexOf('登録日');
 const typeColIdx     = headerRow.indexOf('選考タイプ');
 
 let newValues = [];
 let count     = 0;       
 
 const targetDate  = new Date();
 targetDate.setMonth(targetDate.getMonth() -1);
 
 //console.log(headerRow);
 console.log(targetDate);
 
 for(let i = 0; i < values.length; i++){
   
   if(values[i][statusColIdx] === 'close'){continue}
   if(values[i][typeColIdx].includes('1DAY') === false){continue}
   
   //1か月以内に応募があった人のみ対象とする。
   if(targetDate < values[i][dateColIdx] === true && values[i][nameColIdx] !== ''){
     const person = values[i][nameColIdx];
     const query  = person + '様(HRMOS求人ページ)の書類選考が評価されました';
     
     //対象が何人いるかを把握するための変数
     count += 1;
     
     //対象者を1人ずつ検索して、1次元配列で返す
     const array  = getNG_Mail_(query, person);
     newValues.push(array);
   }//if
 }//for
 
 console.log(`集計対象 ${count}`);
 console.log(newValues);
 
 //書き込みを開始する。
 setScreeningStatus_(targetSheet, newValues);
}//end



/*
 * Gmailから、書類選考が評価されました というキーワードで検索
 * 検索結果を2次元配列で返す
 * @param {string} {string} Gmailの検索ワード、 応募者名 
 *
 */
 
function getNG_Mail_(query, person) {
 const threads     = GmailApp.search(query, 0, 2);
 let arrayMessages = [];
 let count         = 0;
 
 console.log(threads);
 
 //スレッドを取得する
 for (const thread of threads){
   const messages = thread.getMessages();
   
   for(const message of messages){
     const subject  = message.getSubject();
     
     //NGの回数をカウントする。
     if(subject.includes('NG') === true){
       count += 1;
       console.log(subject, count);
     }else if(subject.includes('NG') === false){
       count -= 1;
       console.log(subject, count);
     }
     
     //NG回数が2以上、書類不合格が確定したら.....配列に追加
     if(count === 2){
     
       //ステータス欄に書類不合格と入力、合否列に不合格と入力するための配列を作成
       arrayMessages.push(person ,'書類不合格', '不合格');
       count = 0;//初期化
       
     }else if(count === -2){
       arrayMessages.push(person ,'1次選考参加待ち', '合格');
       count = 0;//初期化
     }//else if
   }//for
 }//for
 return arrayMessages
}//end



/*
 * 検索結果として返ってきた2次元配列を、順番にシートに書き込む関数
 * 最初の関数に書くと、処理が複雑でわかりにくくなるため、分離
 * @param {object} {string} 書き出す対象のシートオブジェクト、 Gmailの検索結果の2次元配列
 *
 */
 
function setScreeningStatus_(sheet, values) {
 const originalValues = sheet.getDataRange().getValues();
 const headerRow      = originalValues[1];
 const targetIndex    = headerRow.indexOf('氏名');
 const array          = generateArray_(originalValues, targetIndex);
 
 const targetColumn   = targetIndex + 1;//getRange用
 const statusColumn   = headerRow.indexOf('ステータス') + 1;
 const resultColumn   = headerRow.indexOf('合否') + 1;
 const isActiveColumn = headerRow.indexOf('Active') + 1;
 const startRow       = 2;
 
 console.log(array);
 
 
 for(let i = 0; i < values.length; i++){
   
   //配列の中身 [ '野比のび太', '1次選考参加待ち', '合格' ]
   const person  = values[i][0];
   const status  = values[i][1];
   const result  = values[i][2];
   
   //初期化
   let targetRow = startRow;
   
   console.log(person);
   
   
   if(array.indexOf(person) !== -1){
     targetRow  += array.indexOf(person);
     
     //ステータス 書類不合格か1次選考参加待ちかを入力する
     const range = sheet.getRange(targetRow, isActiveColumn);
     
     //不合格の場合はcloseに変更
     if(result === '合格'){
       range.setValue('active');
     }else {
       range.setValue('close');
     }
     
     //書き込みスタート 
     sheet.getRange(targetRow, statusColumn).setValue(status);
     sheet.getRange(targetRow, resultColumn).activate().setValue(result);
     
     console.log(`${person}, ${range.getA1Notation()}`);
   }
 }//for
}//end



//空白行をすべて省いた上で、1次元配列を生成する。
function generateArray_(values, column){
return values.map(record => record[column]).filter(value => value);
}

解説

reflectStatusNG         →  実行するメインの関数
getNG_Mail_            →  応募者の書類選考結果をGmailから取得する関数
setScreeningStatus_ →  シートに書き込む関数

※getNG_Mail_ リーダブルコードの原則に倣うと統一して書いた方が良いと思うものの、NgとかIdと書くよりもNGとかIDと書いてくれた方が、自分にとっては読みやすいので、あえて書いたのですが、みなさんはどうでしょうか?

NgMail、読みにくい....利用している環境がGmailだとなおさらね。getResultMailとか悪くないかもしれませんね。

const headerRow  = values[1];
const nameColIdx = headerRow.indexOf('氏名');

この部分は、見出し行から、特定の列を検索するための記述です。毎回スクリプト実行時に、列を検索して、配列番号を返すので、列の挿入などがあった際にも、スクリプトを変更しなくて済むという理由で追記しています。

const targetDate  = new Date();
targetDate.setMonth(targetDate.getMonth() -1);

 
 for(let i = 0; i < values.length; i++){
   
   if(values[i][statusColIdx] === 'close'){continue}
   if(values[i][typeColIdx].includes('1DAY') === false){continue}
   
   //具体的な処理
 }

本日の日付から1ヶ月前の1DAY選考会を対象に絞るための記述です。1ヶ月に1回くらいの頻度で行っているため、前回の1DAY選考会の結果を上書きしないようにするために行っています。

選考が不合格になり、終了した際は、必ずcloseに変更するという運用ルールで行っています。万が一、closeに変更し忘れていた場合でも、1ヶ月以内かつclose以外を対象にする事で、最新の1DAY選考会のみに絞る事が出来ます。

for(const message of messages){
     const subject  = message.getSubject();
     
     //NGの回数をカウントする。
     if(subject.includes('NG') === true){
       count += 1;
       console.log(subject, count);
     }else if(subject.includes('NG') === false){
       count -= 1;
       console.log(subject, count);
     }
     
     //NG回数が2以上、書類不合格が確定したら.....配列に追加
     if(count === 2){
       arrayMessages.push(person ,'書類不合格', '不合格');
       count = 0;
     }else if(count === -2){
       arrayMessages.push(person ,'1次選考参加待ち', '合格');
       count = 0;
     }
    }//for

ここでは、HRMOSから送られてくるメールの件名にNGが含まれているかを調べています。勤め先の場合は、書類選考の面接官は2人いるため、2人が、NGをつけたら、不合格。2人がNG以外をつけたら、合格。

2や-2に達したら、次の応募者の評価情報を検索するために、カウントを0に初期化します。これがないと、評価が正しく取得出来ず、間違った結果が書き込まれてしまいます。

評価が割れた場合は、何も起こりません。

人事業務をGASで自動化した際に利用したスクリプトをまとめています。


この記事が気に入ったらサポートをしてみませんか?