【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で自動化した際に利用したスクリプトをまとめています。
この記事が気に入ったらサポートをしてみませんか?