Day 176|T-sort稼働CSVの取り込み完了。通し実行と“毎朝ジョブ”を本線化する

こんにちは、こんです🦊
100日チャレンジ2回目のWeek 11 / Day 5(通算 Day 176)です。

Day 175で返品CSV→BigQueryのストリーミング挿入まで通し、今日はT-sort稼働CSV(tsort_logs)の取り込み実装と、全体結合(main実行)+自動実行トリガーまで仕上げました。Week設計でいう「Day 5:全体結合と自動化」のゴール到達です。


🎯 今日のゴール

  • Box上のT-sort稼働CSV → box_raw_data.tsort_logs へ型変換+insertAllで投入

  • 通し関数 main() を整備(未処理抽出→取得→変換→挿入→記録)

  • GASの時間主導トリガー毎朝8–9時の自動運転を有効化(暫定)


🔧 実装ログ

1) T-sort稼働CSV → tsort_logs 取り込み

Day 173で確定したスキーマ(started_at TIMESTAMP, line STRING, status STRING, error_code STRING, duration_sec INT64)に合わせて、ヘッダ正規化&型変換を追加。

function castTsort(row) {
  return {
    started_at: new Date(row.started_at).toISOString(),  // TIMESTAMP
    line: String(row.line||'').trim(),
    status: String(row.status||'').trim(),
    error_code: String(row.error_code||'').trim(),
    duration_sec: Number(row.duration_sec||0)
  };
}
  • files/{file_id}/content でCSV本文取得 → Utilities.parseCsv() → 1行ずつ castTsort() → BigQuery.Tabledata.insertAll へ(返品CSVと同じ仕立て)。

  • ヘッダ揺れ(Started At / started_at)は小文字化+記号除去の正規化で吸収。

2) 全体結合 main()(通し)

Boxの複数フォルダを監視し、未処理file_idのみを処理→DLQ(失敗行)退避処理済み記録までをワンショット化。

function main() {
  const targets = [
    { folderId: getProp('BOX_FOLDER_RETURNS'), table:'ec_returns',  caster: castReturns },
    { folderId: getProp('BOX_FOLDER_TSORT'),   table:'tsort_logs',  caster: castTsort   }
  ];
  let summary = {files:0, rowsOK:0, rowsNG:0};

  for (const t of targets) {
    const files = listNewCsvFiles(t.folderId);              // Day174で実装
    for (const f of files) {
      const text = fetchCsvText(f.id);                      // Day175で実装
      const rows = csvToObjects(text);                      // header→obj配列
      const casted = rows.map(t.caster);
      const errs = insertRowsToBQ('box_raw_data', t.table, casted);
      markProcessed(f.id);                                   // 冪等
      handleInsertErrors(errs, casted, t.table);            // DLQへ退避
      summary.files++; summary.rowsOK += casted.length - errs.length; summary.rowsNG += errs.length;
    }
  }
  logSummary(summary);
}

方針は「小さく速く」:千行程度/ファイルは1ショット、それ以上は分割(1,000〜5,000行/バッチ)でinsertAll安定化。

3) 毎朝の自動運転(トリガー)

GASの時間主導型トリガーで main() を8–9時帯に実行。まずは1日1回から開始(頻度は運用後に調整)。

function setupTriggerDaily() {
  // 既存トリガー掃除は省略
  ScriptApp.newTrigger('main')
    .timeBased().atHour(8).everyDays(1).create();
}
  • 失敗時はLogger+管理シートへ記録(どのファイルのどの段階で失敗か追跡可能に)。


🧪 スモーク結果

  • T-sort CSV(数十行)→ tsort_logs へ全件成功

  • 一部の空値(duration_sec空)が0に正規化され、Looker計算上も問題なし(来週、NULL/0方針を正式決定)

  • main() 実行で返品+T-sort両方が1回で処理され、処理済みIDもしっかり記録


🧭 運用メモ(初期)

  • 未処理判定はfile_id基準:重複挿入を根元から排除。

  • DLQ(dead-letter queue):失敗行はシートに退避→次回バッチで修正→再送の逃し道を確保。

  • “毎朝まわる”価値を最短で実現し、Day 6のLookerプロトタイプへつなぐ。


💡 今日の学び

  • 通しの1本化が最強のデバッグ:段階テストよりもmainで通し、どこで詰まるかを最速で掴むのが早い。

  • 列名揺れは“入口で吸収”:後段(BQ/Looker)に影響させないのが保守コスト最小。

  • 頻度はあとから上げる:まずは毎朝1回で信頼性を固め、必要なら複数回差分検知強化へ。


🔭 明日の予定(Day 177|Week 11 Day 6)

  • Looker Studioでのプロトタイプ可視化(返品推移/SKU別/理由別、T-sort時間帯稼働・エラー構成)

  • 共有用テンプレ(期間フィルタ、拠点/ライン切替)の初版作成

  • データ型の最終擦り合わせ(計算フィールド・日付扱い)


✍️ まとめ

Day 176は、T-sort稼働データの取り込み完了全体の自動運転までを仕上げ、“置けば朝に溜まる”状態に一歩前進。明日はLookerで“見ればわかる1枚”を形にします。
この1週間で目指す「プロトタイプ共有→次週の改善タネ作り」への道筋が、いよいよ見えてきました。


#100日チャレンジ #Week11 #Day176 #DX実験室
#BoxDataWeaver #BoxAPI #BigQuery #GoogleAppsScript #データパイプライン #自動化 #業務改善

いいなと思ったら応援しよう!