Excel差分検出スキルとCFマッピングJSON自動生成
朝、手修正したExcelファイル(_KKサフィックス付き)とスクリプトの出力を並べて見比べていた。セルの数が多すぎて、目視では差分を拾いきれない。「これ、スクリプトに任せたい」と手が止まったところから今日の作業が始まった。
もう一つの軸は、仕訳データからCF項目の対応表を自動で引き抜く仕組み。全38論点のExcelを横断して、借方・貸方のCF科目をQ番号ごとに整理する。
どちらも「計画を書いてからCodexに叩いてもらう」フローを4往復回して、実装に入る前に設計の穴を埋め切った1日だった。
1. Excel差分検出スキルの設計
背景
eurekapu-nuxt4では、Pythonスクリプトが各論点のExcelファイルを自動生成する。ユーザーが手修正した版(_KKサフィックス)と比較して「何が変わったか」を把握したい。だが手作業では、行の挿入・削除が絡むと前後のセルがずれて追跡できなくなる。
6カテゴリの変更検出
Codexレビューの初回で「セル単位の差分比較では行列の挿入削除を見落とす」と指摘が飛んできた。設計を2次元グリッドベースに書き直し、以下の6カテゴリで変更を拾う方針に落ち着いた。
| カテゴリ | 検出方法 |
|---|---|
| 行挿入 | グリッド行数の増加 + ヘッダ文字列のマッチング |
| 行削除 | グリッド行数の減少 + 欠落行の特定 |
| 列挿入 | 列ヘッダの差分検出 |
| 列削除 | 同上 |
| セル値変更 | 対応セル同士の値比較 |
| 式変更 | openpyxlで数式文字列を直接比較 |
_KKサフィックスがないケースへの対応
全ファイルに_KKが付いているわけではない。同じディレクトリに同名ファイルが複数ある場合、タイムスタンプの新旧で「手修正版」と「スクリプト出力版」を判定するロジックを追加した。
def resolve_pair(directory: Path, base_name: str) -> tuple[Path, Path]:
kk = directory / f"{base_name}_KK.xlsx"
if kk.exists():
return kk, directory / f"{base_name}.xlsx"
# _KKなし: タイムスタンプで新旧判定
candidates = sorted(directory.glob(f"{base_name}*.xlsx"), key=lambda p: p.stat().st_mtime)
return candidates[-1], candidates[0]
Codexが突いた致命的4点
| # | 指摘内容 | 対応 |
|---|---|---|
| 1 | セル単位比較では行列の挿入削除を検出できない | 2次元グリッドベース設計に全面書き直し |
| 2 | ヘッダ行の特定がハードコードされている | 先頭N行のパターンマッチで自動検出に変更 |
| 3 | マージセルの扱いが未定義 | openpyxlのmerged_cellsを展開してから比較 |
| 4 | 出力がstdoutのみで確認しづらい | HTMLレポート + JSON出力を追加 |
4番目は自分でも気づいていなかった。標準出力だけに差分を流していたところ、ユーザー(自分自身)に「どこに出力されてるの?」と指摘されて目が覚めた。
2. CFマッピングJSON/CSVの自動生成
やりたいこと
仕訳のE列(借方CF科目)とJ列(貸方CF科目)を全38論点のExcelから抽出し、Q番号ごとのCF項目対応表をJSON/CSVで吐き出す。リファクタリングで「勘定科目 -> CF区分」のマッピングをハードコードからデータ駆動に切り替えるための下準備。
Codex 4ラウンドの軌跡
計画書v1を書いてCodexに投げたら「実装はまだいい、計画書をまずドキュメントにして」とユーザーに止められた。そこからv4まで4回のイテレーションを回した。
| Version | Codex指摘 | 修正内容 |
|---|---|---|
| v1 | 検証の方向が逆(JSON->Excelではなく、Excel->JSONで検証すべき) | 検証フローを反転 |
| v2 | _EXPLICIT_CF_TYPE_FROM_JSONをマスタdictにすると、論点固有の例外を吸収できない | 論点別オーバーライド層を追加 |
| v3 | ラベル正規化が不十分(全角半角・括弧の揺れ) | unicodedata.normalizeとカスタム正規化関数を導入 |
| v4 | LGTM | -- |
v2の「マスタdict化が危険」という指摘は、手が止まった。38論点のうち3つだけ特殊なCFラベルを使っていて、マスタ辞書で一律に引くと上書きされてしまう。論点IDをキーにしたオーバーライド層を挟むことで、汎用マスタと個別例外を両立させた。
3. 古いExcelファイルの整理
作業の合間に、ディレクトリに積み上がっていた古いExcelファイルを old/ に移動した。
- 移動前: 649ファイル
- 移動後: 410ファイル(_KKと最新日付のみ残す)
- 239ファイルを
old/へ退避
ファイル数が減っただけで、スクリプトの実行時間が体感で縮んだ。globのマッチ対象が減るので当然だが、散らかった部屋を片付けた後のような気分だった。
試行錯誤
| # | テーマ | 試したこと | 結果 | 気づき |
|---|---|---|---|---|
| 1 | 差分レポート出力先 | stdoutのみに出力 | ユーザーに「どこ?」と聞かれる | HTMLレポートとJSONを併用すべき |
| 2 | 計画書v1 | いきなり実装コードを書き始める | ユーザーに止められる | 計画書を先にドキュメント化してレビューに回す |
| 3 | マスタdict一本化 | 全論点共通の辞書で引く設計 | 3論点で例外が壊れる | オーバーライド層を挟む |
| 4 | ラベル比較 | 文字列の完全一致 | 全角半角の揺れで不一致多発 | normalize + カスタム正規化を前段に入れる |
| 5 | 検証方向 | JSON -> Excel方向で検証 | Codexに「逆だ」と指摘される | Excel(原本)-> JSON(生成物)が正しい検証方向 |
今日の学び
- 差分検出はセル単位で比較するとずれる。行列の挿入削除を先に検出してからセルを対応付ける2段階が必要
- 「標準出力に流しておけばいい」は自分だけの感覚。確認しやすい形式(HTML/JSON)で出力する習慣をつける
- Codexレビューを計画段階で回すと、実装前に設計の穴が4つ見つかった。コードを書いてから見つけるより手戻りが圧倒的に少ない
- マスタ辞書の一本化は魅力的だが、例外が3件あるだけで破綻する。汎用層+オーバーライド層の2層構造が安定する
- 「実装はまだいい、まず計画書を書け」というブレーキは正しかった。計画書v1からv4まで4回書き直す過程で、実装時の迷いがほぼ消えた