文系のCS/オペマンがGASとかSQLに挑み続けて学んだこと

カンム Advent Calendar 2019 10日目

興味ある方はぜひ他のメンバーの記事もどうぞ


まずは自分の紹介

第一回目なのでまず自分のことを書く。

新卒からずっとCS対応・業務構築/効率化周りで生息してきた。新卒入社は流通系カード会社で、実務部分で与信系全般を経験し、カード営業(利用促進)や経営企画もやったが、結局、マーケツール導入だの新規プロダクトだの、オペ周りに携わった。

8年弱働いた後、代表と自分しかいないベンチャー企業にて、To C向けのプロダクトにおける顧客対応部分のオペレーション構築をする経験をした。この辺はかなり貴重な経験ができたと思っていて、今度改めて書きたいと思う。

そして昨年の3月からバンドルカードを運営するカンムに本格的に参画し、CS/オペ周りの取りまとめとして日々やりたいことをやらせてもらっている。
そんな自分が、強いCS/オペマンとなるべくいま目指していることがある。

1. リスクに強くなって正確かつ迅速にオペ構築できるようになる
2. GASSQLPython等で手元の業務効率化をガシガシできるようになる
3. CS/オペチームをコストセンターでなく目に見えて会社に価値貢献できてる状態にする

他にも一部のスプレDB化やめるためのでっかい業務改善とかもやろうとしてるけれど、直近のなりたい・やりたいは上記。

今日は 2 に掲げてるうちの「GAS」についてやってきたこととか学びを書きたいと思う。(1じゃなくて2。尺的に2。)

本題の前に、業務効率化への想いについて

特に業務効率化には強い想いがあって、よくシャワー浴びてるときとか帰り道に、今日の自分の反省とかなんでこう思ったかとかを考える癖があるのだけれど、何回考えても自分にとっての業務効率化はもう三大欲求くらい当たり前に存在するものなのでは、と思う。

なんというか色んな行為や行動において、一石二鳥とか時短行為的なのが好きで、「AしながらBしてる・最短でCできた」みたいなことやれるといちいち嬉しい。例えば、歯磨きながら頭洗ったり、道曲がるときに壁スレスレの最短で曲がったり、新宿駅西口でいかに止まらずにスイスイ行けるかチャレンジやってたり。
なんか「え、頭も歯もちゃんとやれてなくない?」みたいな雰囲気感じるけど、いいの。そこじゃないの、気になって欲しいところは。他にも早く歩きたすぎて右足二回出してつまづいたりもするし。

実際、仕事となると中途半端なことはできないからちゃんと準備して全力でやりきることは大事にしているんだけど、こう、仕事以外の内なるパワーから取り組んでいる、ってことを伝えたかった。

スクリーンショット 2019-12-07 22.12.18

本題

GASでやってきたことを書く。手元の業務改善として、非エンジニアがどんなプログラムを書いていて、どういう学びを得たかということを伝えて、これから業務効率化に向けてなにかしようとしてる人に対して少しでも参考になれば嬉しみ。
(なお、今回は得た学びを中心に書きたいのでプログラミングの構成とか構文には細かく触れない。それはまた別途。)

 Google Apps Script(GAS)
- Googleの各種アプリであるスプレ、ドキュメント、ドライブ等を外から操作できるプログラミング言語
- 環境構築が不要で即動かせる

^ GASに関しての説明はたくさんの分かりやすい記事が転がっているのでそちらを参考にしてほしい。

自分がGASでやってきたこと

やってきたことはこんな感じ。
これ以外にもチャットツールからgoogleカレンダーに登録するやつとか色々あるけど分かりやすいもの抜粋した。

- 災害通知Bot
- 委託先エスカレシート自動転記
- 自動PDF化→指定フォルダ作成&保存
- 当番お知らせBot
- 問合せ一覧をスプレに自動出力

まだGASに触れたことない人がイメージ掴みやすいように、「災害通知Bot」のソースコードを載せてみる。書き方の一般的なルールも知らない素人のコードなのでアレな部分があったらご容赦願いたい…。
(アドバイスは大変嬉しみmm)

災害情報が更新されたら通知されるBot

災害情報サイトからスクレイピングで災害地域情報を取得。定期実行して
発生件数が更新されたらチャットツールに通知される
(サーバーに負担をかけないよう定期実行回数は制限している。)

スプレで災害情報の発生件数の差異を都度計算し、「差異が発生=更新があった」と判断している。↓↓こんな感じのやつ。

スクリーンショット 2019-12-07 19.05.10

以下が実際のソースコード。
- 前回の発生件数を「前回分」カラムへ移行
- 災害情報・発生件数を取得

function evacuationAlert() {
//前回の発生件数を「前回分」へ移行
 var ss = SpreadsheetApp.openById('//対象スプレのID書く*****').getSheetByName('//シート名書く****');
 var curDate = ss.getRange(2, 1).getValue();
 ss.getRange(1, 1).setValue(curDate);

//災害情報を取得
 const URL = '対象URL書く*****';        
 var key = '*****';
 var payload = 
     {url:URL,
      renderType:'HTML',
      outputAsJson:true};
 payload = JSON.stringify(payload);
 payload = encodeURIComponent(payload);
 var url = 'https://phantomjscloud.com/api/browser/v2/'+ key +'/?request=' + payload;                
 var response = UrlFetchApp.fetch(url);
 var json = JSON.parse(response.getContentText());
 var source = json["content"]["data"];

//発生件数を取得
 var myRegexp = /<p class="navNumItem">([\s\S]*?)<em>/;
 var source = source;
 var x = source.match(myRegexp);
 if(x != null){
   var x = x[1].replace("件中","");
   var x = parseFloat(x);
 }
 else{
   var x = 0;
 };

- 災害地域と避難レベルの文字列を取得する関数を作成
- 取得した文字列から地域名を取得する関数を作成
(上記2つは余分なデータから欲しい部分だけを精査していくための関数というイメージ。)

 //災害地域と避難レベルの文字列を取得する関数
 function check(source, check){
   const error = new Error("エラーメッセージ");
   var source = source
   try {
     var region = source.match(check);
   } catch(error){
     Logger.log('エラー出てる')
   };
   if(region != null){
     var p = region[1].replace("//置き換える内容書く *****"," ");
     var p = p.replace("\">", ":");
     return p
   };
 };
 //取得した文字列から地域名を取得する関数
 function check2(result, array){
   var j = result.search(/[都道府県]/);
   if(j != -1){
     return array.push(result);
   };
 };

- 災害情報を1件ずつスクレイピングしたうえで、作成した関数を使って精査された災害地域情報を配列に追加
ここの正規表現でのパターンマッチングが一番大変だった…。

 //作成した関数を使って地域情報を配列に追加
 var disasterArea = [];
 var c1 = /正規表現書く ******/
 var cr1 = check(c1);
 if(cr1 != null){
   check2(cr1, disasterArea);
 };
 var c2 = /正規表現書く ******/
 var cr2 = check(c2);
 if(cr2 != null){
   check2(cr2, disasterArea);
 };
 var c3 = /正規表現書く ******/
 var cr3 = check(c3);
 if(cr3 != null){
   check2(cr3, disasterArea);
 }; 
 ※……以下略 

- スプレに発生件数をmemo(「今回分」に入れる)
- チャットへPostする関数を作成
- 発生件数に差分が発生した場合にチャットへPost

 //スプレに発生件数を追記
 ss.getRange(2, 1).setValue(x);

 var diff = ss.getRange(3, 1).getValue();
 var slackUrl = '******'; 
 var message = '<!channel>避難情報が更新されたよ!\n' + disasterArea + '\nhttps://*****';
 var username = '災害情報通知';
 var icon = ':zap:';
 
 //前回分と発生件数に差分が発生した場合にSlackへPost
 if(diff != 0){
   slackPost(slackUrl, message, username, icon);
 };
 
 //Slackにpostする関数
 function slackPost(slackUrl, message, username, icon){
   var jsonData =
       {
         "username" : username,
         "icon_emoji": icon,
         "text" : message,
       };
   
   var payload = JSON.stringify(jsonData);
   var params =
       {
         "method" : "post",
         "contentType" : "application/json",
         "payload" : payload,
       };
   
   UrlFetchApp.fetch(slackUrl, params)
 };
};

これを作るだけでもかなり時間を使った気がする。何が時間かかったかというと、やり直しの時間。書き終えて実行してみても、そもそものやりたいこと(要件)が満たされないものになっていたりして、構成を何回も組み直した。心折れかけたけど、そこは例の内なるパワーで乗り切った。

「災害通知Bot」は最近作ったもので、簡単な構文なら参考記事見ないで作れるようになったし、公式ドキュメントを参考にできるようになったので、構文とかで大きく躓いたわけではなかった。
それが以下の学びにつながる話。

GASとかSQLをやり続けていることの学び

①構成を組立ててから取り組むこと大事

これは「作る前にちゃんと構成考えよう」って結構当たり前のことを言ってるんだけど、書けるようになると嬉しくてついドンドン書いていっちゃうので、毎回意識するようにしている。
構文を書けるのは最初すぎる一歩で、やりたいことを実現するのはまた別の力が必要だなって改めて思った。
「GASで何ができるのか」というぼんやり像と、構文(if文とかfor文とか)とかは決まったものなので、ある程度学べばできるようになるんだけど、その先の実用化に向けては何回も試行錯誤した。
上記の例に挙げた、災害通知Botの[災害情報スクレイピング→更新通知]にしても、何をキーに通知をするのかでめちゃくちゃ何回もやり直した。
考えが甘くて、「書きたいやつは書けたけどこれじゃ要件満たせてない。。」状態に何回もなった…。
自分の感覚ではオペを構築するときと似ていて、全体の構成図とか絵を書いたりして要件を自分の中できちんと組み立ててからGASる(=GASを書くこと:社内用語)ようにしている。

②詰まったら細かくデバッグしまくるの大事

エンジニアメンバーからもらった最初のアドバイスがこれだった。プログラムを実行した時にどこで詰まってるかは小さく確認していく。こういう熟練者にとっては当たり前のことも素人はわからなかったので大変勉強になった。これでGASやSQLたちとの距離がグッと縮まったと思う。

③腐らずにやり続ける、そして雑でも良いから作り上げるの大事

根性論だけど「やり続けて作り上げる」経験は大事だと思った。
GASもそうだけど、SQLはなんっかいも挫けそうになったけど繰り返し書くことで積み上がった気がする。(後述する環境に助けられたのももちろん大きい)ある日、「あ、これはあの人が書いたクエリにあったな…」とか「この記事前も見たぞ…」みたいなふうになって、そこからちょいちょい染み付いていった気がする。
加えて、雑でもいいから作り上げないとフィードバック得られないので一個ずつやりきるのも大事。

あと、環境もすごい大事だ思う。
転職して、ふと振り返った時に思うのは、フィードバックを受けられる環境にいることがめちゃくちゃ大事だということ。
業務や仕事の一部を経験しているだけではなかなか全体の影響を自分ごとのフィードバックとして感じられないけど、少人数なので確実に自分のフィードバックになる。更に、成功したときも失敗したときもフィードバックを得るのが速いのですぐ修正できる。
これはカンム特有というわけではなく、少規模なスタートアップ等で共通する環境なのではと思う。

カンムという環境

カンムの環境でいうと、非エンジニアがとてもチャレンジしやすい環境である。
他のメンバーのカンム Advent Calendar の記事でも出たと思うが、カンムには「スパルタンSQL」「スパルタンGAS(通称:スパガ)」「Tech Day」等が催されており、定期的(字面は強制的(笑))に学ぶ時間がある。少人数なこともあるが、非常にエンジニアとの距離が近いので実際は催し外の通常の時間でも聞きやすいし、かつ、エンジニアがみんな優秀なので的確なアドバイスをくれる。
特に自分は「Tech Day」はまさにフィードバックを直でもらえる(すごい褒めてくれる)場なので、かなりモチベーションになっている。人の発表聞くのもすごい勉強になるし。
なんか自分だけやってる寂しい感がなくて、分かんないポイントを他の非エンジニアのメンバーと共有できて、「それなー」とかっていう会話も楽しいし、できたときに一緒に喜んでくれるとことか、なんていうかもう非常にエモい。使い方あってるかな。


まとめ

少々長くなってしまったのでまとめを。

- 自分の場合は、業務効率化への想いは内なるパワーで形成されてる
- 業務効率化するための仲間がGASやSQLといった存在
- 想いの強さと環境のおかげでやり続けられてる
- ①構成の組立て大事、②デバック大事、③やり続ける・作り上げるの大事
- フィードバックを感じられる環境すごくいい
- 直ぐに聞けるメンバーがいる環境すごくいい

ちなみに、今回はSQLのことをあまり書いてないが、SQLはどちらかというと業務上必須な側面が大きい。施策のPDCA等で必要な数値はSQLで取る必要があるので、CS/オペチームのメンバーも含め日別のカード発行数や会員情報取得等の簡単なものならほぼ全員が書ける状態である。

今後は、手元の業務効率化でやれる範囲を広げていきたいと思っているので、Djangoとかの管理画面を色々イジってみたいなと思っている。現状の業務管理画面がDjangoなので、学ぶことでエンジニアとの会話工数を減らしたりもできるのかなと。(一石二鳥的な好きなアレ。)

記事を書くと自分の頭が整理されるので、CS/オペチームのこととか、またなにか書いていきたいなぁと思う。
(ここまで読んでいただいた方、ありがとうございました。)


では例のアレを…

カンムでは一緒に働く仲間を探しています!

色々な職種で募集をしています。ぜひとも興味ある方はご応募ください!
会社紹介資料や募集要項等

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