TVer Technologies TechBlog

TVer Technologies TechBlog

HAROiD での Google Apps Script の活用など

どうも @muddydixon です。

HAROiD では番組と連動したデータ放送企画の裏で動くシステムの運用をしています。 例えば、某朝の番組での投票企画や某おいしいもののメニューの値段を当てる企画やざぶとんがもらえたり、取られたりする番組の企画などに携わっています。

テレビは全国5000万世帯、テレビのネット結線率が30%を超えている時代ですので、視聴率が20%の番組になると番組の時間中ずっと負荷が来るわけですからそれなりの規模のマシン台数やスペックを用意します。 しかし、番組が終わるとそれらのシステムは不要になるのでさっさと廃棄/縮退してコストを下げるようにしています。

HAROiD ではこのスケールアウト・アップ/スケールイン・ダウンの仕組みを API を叩くだけで構築できるようにしています。 これは全くよくわからないのですが「小諸そばひとりアドベントカレンダー」を継続している@toritoriの尽力の賜物です。

こんなところで語られたりもしています

YAPC2014

前置きが長くなりました

API とはいえ設定を変更した API コールはいつでもヒューマンエラーを導く原因となりますのでシステム化したいところです。 Slack も検討しましたが、これも「忘れてしまう」というヒューマンエラーになります。

そこで HAROiD では「Google Calendar」と同期する仕組みを採用しています。

Google Calendar に予定を入れるとその予定にしたがって、拡大され・縮退されます。

f:id:tt-daichi:20200713211156p:plain

当然スケールの過程は Slack に通知されてくるのでおかしなことがあれば気が付きますし、便利ですね。 Slack!

f:id:tt-daichi:20200713211150p:plain

システム全体の構築と縮退/廃棄を Google Calendar と Slack で連携させてやっているところは少ないと思ったので紹介させていただきました。

おまけ

私には子供が2人いて、似た家庭はあるかと思いますが、宿題や勉強があまり好きではないようです。 掛け算の練習はしてほしい!ということで下記のような GAS を作っておきましたので似たようなお子様をお持ちのご家庭ではご利用ください。

習い事などもあるので毎日は難しいですが、それなりにはやるようになった結果、今は使わなくなりました!

f:id:tt-daichi:20200713211145p:plain

var LOG_FILE_ID="スプレッドシートを作り、IDを設定しておくとログを記録できて便利";
function Log(fileId){
  if(fileId){
    this.logFile = SpreadsheetApp.openById(fileId);
  }else{
    this.logFile = null;
  }
}
Log.prototype = {
  log: function log(text) {
    if(this.logFile){
      this.logFile.appendRow([Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/M/d H:m:s'), text]);
    }
    Logger.log(text);
  }
};

function main(){
  const DRILL_DIR = "ディレクトリのID";
  const drillDir = DriveApp.getFolderById(DRILL_DIR);
  
  const dt = Moment.moment().format("YYYYMMDDHH");
  const drillName = "drill-" + dt;
  
  const drillSS = SpreadsheetApp.create(drillName, 13, 12);
  // 九九で埋める
  createMultipleTable(drillSS, 6);

  // ファイルを移動させる
  const fs = DriveApp.getFilesByName(drillSS.getName());
  if(fs.hasNext()){
    const f = fs.next();
    drillDir.addFile(f);
    DriveApp.removeFile(f);
  }    
}

function getRange(start, end, step){
  if(typeof end === "undefined"){
    end = start;
    start = 0;
    step = 1;
  }else if(typeof step === "undefined"){
    step = 1;
  }
  
  const ret = [];
  for(var i = start; i < end; i += step){
    ret.push(i);
  }
  return ret;
}

function createMultipleTable(ss, num){
  const serifs = [
    "なんか子供のテンションがあがるメッセージ"
  ];
  const img = DriveApp.getFilesByName("子供のテンションがあがる画像.png").next().getBlob();
  const xs = getRange(1, 10);
  const x = [];
  while(xs.length){
    x.push(xs.splice(0|Math.random() * xs.length, 1));
  }
  const ys = getRange(1, 10);
  const y = [];
  while(ys.length){
    y.push(ys.splice(0|Math.random() * ys.length, 1));
  }
  const s = ss.getSheets()[0];
  s.getRange("B2").setValue("かけざん");
  s.getRange("C3:K3").setValues([x]);
  s.getRange("B4:B12").setValues(y.map(function(j){ return [j];}));
  s.getRange("B3:K12").setBorder(true, true, true, true, true, true);
  const size = getRange(10).map(function(){ return getRange(10).map(function(){ return 26;}); });
  s.getRange("B3:K12").setFontSizes(size);
  for(var i = 1; i < 12; i++){
    s.setColumnWidth(i, 50);
  }
  s.setColumnWidth(12, 300);
  s.getRange("L5").setValue(serifs[0|Math.random() * serifs.length]);
  s.getRange("L6:L12").merge();
  s.insertImage(img, 12, 6);
}

We are Hiring!

HAROiD ではヒューマンエラーをなくしたいエンジニアとか、定常ではなく突発の負荷に耐えうるシステムを作りたいエンジニアとか、データを解析したいエンジニアを募集しています!

Wantedly